iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
AI & Data

然後模型就死在 Jupyter Notebook 了 (ಥ﹏ಥ)系列 第 8

[Day 08] 使用 fastAPI 部署 YOLOv4 (2/2) — 自行撰寫 Client 進行互動

前言

昨天我們使用了 fastAPI 內建 client 的 UI 來與 API 互動,今天我們改為利用 Python 的 requests 函式庫編寫一個最簡單的 client,並用它來跟 API 互動。
開始前要再提醒一次,這部分的程式碼主要規劃為在本機端執行,所以在開始之前請先到 GitHub 下載檔案,並跟著頁面的說明先把虛擬環境建起來。
另外還要特別注意,一定要確認 server.ipynb 打開的伺服器還在執行中!!
都準備好了的話,打開 client.ipynb,讓我們開始吧。

拆解 URL Requests

如果昨天有操作過幾次 fastAPI 內建的 client,或許會發現所有的請求都是藉由指向特定 URL 並在其後接上參數來完成:
URL requests
上圖 URL 所代表的意義為:

而最後則是藉由模型的名稱 yolov4、yolov4-tiny、yolov3 或 yolov3-tiny 指定要使用的模型 (這裡依然使用 yolov4-tiny)。

在程式碼的部分,我們可以用以下方式建立一個 URL Request,首先將所有參數資訊整理起來:

base_url = 'http://localhost:8000'
endpoint = '/predict'
model = 'yolov4-tiny'
confidence_level = 0.2

為了使用模型,要將接口 endpoint 接在 base URL 後來取得完整的 URL:

url_with_endpoint_no_params = base_url + endpoint

此時 url_with_endpoint_no_params 的值為 http://localhost:8000/predict

注意現在還沒有加上模型的參數。

加上參數的語法為 ? 加上參數名稱與其值,而不同參數之間則使用 & 隔開,例如使用 yolov4-tiny 搭配信心閾值 0.2 最終得到的 URL 如下:

full_url = url_with_endpoint_no_params + "?model=" + model + "&confidence=" + str(confidence_level)

最終的 URL 請求 full_url 的值為 http://localhost:8000/predict?model=yolov4-tiny&confidence=0.2

向伺服器發送請求

撰寫 response_from_server 函式

從昨天的內容我們知道 /predict 接口預期接收模型的名稱與圖片,但因為圖片複雜許多,所以並不能使用 URL 傳入。
在這個流程我們改用 requests 函式庫來處理,且因為它需要的是 POST HTTP 請求,所以主要會用到 requests 函式庫裡的 post 函數。
而為了在請求中附加檔案,必須建立一個將檔名 (此例中為 file) 與實際檔案對應的字典。
另外,指令 status_code 則可以方便我們確認請求所觸發的回應狀態如何 (status_code = 200 代表一切OK):

def response_from_server(url, image_file, verbose=True):
    """Makes a POST request to the server and returns the response.

    Args:
        url (str): URL that the request is sent to.
        image_file (_io.BufferedReader): File to upload, should be an image.
        verbose (bool): True if the status of the response should be printed. False otherwise.

    Returns:
        requests.models.Response: Response from the server.
    """
    
    files = {'file': image_file}
    response = requests.post(url, files=files)
    status_code = response.status_code
    if verbose:
        msg = "別擔心,一切 OK!" if status_code == 200 else "處理這個請求的時候好像哪裡出錯了..."
        print(msg)
    return response

我們可以從檔案系統中開啟一個圖片檔並與 URL 一起輸入來看看 response_from_server 是否能正常運作,程式碼與輸入的圖片如下:
baseball
*Image by Cindy Jones from Pixabay

with open("images/baseball.jpg", "rb") as image_file:
    prediction = response_from_server(full_url, image_file)

執行上面的程式碼應該會得到 別擔心,一切 OK! 的訊息,這代表請求是成功的,但我們卻完全不知道到底偵測到了什麼。
為了得到包含定界框的圖片,我們必須將回饋的內容處理成適當的格式,這個過程其實跟 server.ipynb 中把原始圖片轉成 cv2 圖片很像。
首先,建立一個資料夾來存放處理好的圖片:

dir_name = "images_predicted"
if not os.path.exists(dir_name):
    os.mkdir(dir_name)

撰寫 display_image_from_response 函式

def display_image_from_response(response):
    """Display image within server's response.

    Args:
        response (requests.models.Response): The response from the server after object detection.
    """
    
    image_stream = io.BytesIO(response.content)
    image_stream.seek(0)
    file_bytes = np.asarray(bytearray(image_stream.read()), dtype=np.uint8)
    image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
    filename = "image_with_objects.jpeg"
    cv2.imwrite(f'images_predicted/{filename}', image)
    display(Image(f'images_predicted/{filename}'))

這時候執行 display_image_from_response(prediction) 應該可以得到以下結果:
baseball predicted

如此一來,我們就可以使用自己的 client 來與 API 互動了,來試試其他圖片吧:

image_files = [
    'falcon.jpg',
    'elephant.jpg',
    'apples.jpg'
]

for image_file in image_files:
    with open(f"images/{image_file}", "rb") as image_file:
        prediction = response_from_server(full_url, image_file, verbose=False)
    
    display_image_from_response(prediction)

falcon
elephant
apples

可以看到結果還不錯!!

恭喜完成了部署的實作

實務上的 client 與 server 因為還要兼顧安全性與效能,所以會複雜得多,但這裡使用的程式碼已經和真實環境的很接近囉!
希望以上的內容能讓大家更熟悉部署深度學習模型與使用它的流程。

終於完成了部署部份的說明,接著我們就要進入下一個部分 — Modeling,明天見啦~
/images/emoticon/emoticon30.gif

參考資料


上一篇
[Day 07] 使用 fastAPI 部署 YOLOv4 (1/2) — 以內建 Client 進行互動
下一篇
[Day 09] 建立機器學習模型 — Andrew Ng 大神說要這樣做
系列文
然後模型就死在 Jupyter Notebook 了 (ಥ﹏ಥ)30

尚未有邦友留言

立即登入留言