Deploying a Machine Learning Model (Fast API)
이미지에서 객체를 검출하도록 학습된 YOLOV3이라는 모델을 배포해 보자.
학습된 모델이 작동하는 모습을 우선 확인해 보기 위해서 아래의 함수를 정의했고, 이미에서 객체를 올바르게 검출하는 것을 확인했다.
# Read the image into a numpy array
# Perform the object detection
# Print detected objects with confidence level
# Create a new image that includes the bounding boxes
# Save the image in the directory images_with_boxes
# Display the image with bounding boxes
import cv2
import cvlib as cv
from cvlib.object_detection import draw_bbox
def detect_and_draw_box(filename, model="yolov3-tiny", confidence=0.5):
img_filepath = f'images/{filename}'
img = cv2.imread(img_filepath)
bbox, label, conf = cv.detect_common_objects(img, confidence=confidence, model=model)
print(f"========================\nImage processed: {filename}\n")
for l, c in zip(label, conf):
print(f"Detected object: {l} with confidence level of {c}\n")
output_image = draw_bbox(img, bbox, label, conf)
cv2.imwrite(f'images_with_boxes/{filename}', output_image)
display(Image(f'images_with_boxes/{filename}'))
결과를 확인해 보면 아래와 같이 detecting 된 이미지가 출력되는 것을 확인할 수 있다.
for image_file in image_files:
detect_and_draw_box(image_file)
Placing your object detection model in a server
Client-Server model:
When talking about deploying, what is usually meant is to put all of the software required for predicting in a server. By doing this, a client can interact with the model by sending requests to the server. (예측에 필요한 모든 소프트웨어를 서버에 넣어야 사용자가 모델과 상호작용을 잘 할 수 있다.)
app = FastAPI():
먼저 FastAPI 클래스를 만든다.
uvicorn.run(app):
FastAPI를 사용해서 예측 논리를 처리할 end point를 만든다. 서버를 실행하기 위한 모든 코드가 준비되면 위 명령을 실행하면 된다.
(uvicorn은 serving을 처리하는 역할을 한다.)
동일한 서버에서 여러 Machine Learning 모델을 호스팅할 수 있다. 단, 각 모델에 다른 엔드포인트를 할당해야 하며 엔드포인트는 URL의 패턴으로 표시된다. 예를 들어 myawesomemodel.com이라는 웹 사이트가 있는 경우 다음의 엔드포인트가 존재한다면 세 가지 다른 모델이 있을 수 있다.
- myawesomemodel.com/count-cars/
- myawesomemodel.com/count-apples/
- myawesomemodel.com/count-plants/
"/my-endpoint" 엔드포인트에 대한 HTTP GET 요청을 허용하는 방법은 다음과 같다.
@app.get("/my-endpoint")
def handle_endpoint():
...
...
클라이언트와 서버는 HTTP라는 프로토콜을 통해 통신하는데, 이를 위해서 아래 명령(GET, POST)을 사용한다.
예측에 필요한 정보를 제공해야 하기 때문에 엔드포인트에 있는 기계 학습 모델과의 상호 작용은 일반적으로 POST 요청을 통해 수행된다.
- GET -> Retrieves information from the server.
- POST -> Provides information to the server, which it uses to respond.
@app.post("/my-other-endpoint")
def handle_other_endpoint(param1: int, param2: str):
...
...
FastAPI를 사용해서 웹 서버를 만들고 기존 학습된 모델을 호스팅해 보자.
http://127.0.0.1:8000/ 서버에서 실행된다.
import io
import uvicorn
import numpy as np
import nest_asyncio
from enum import Enum
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import StreamingResponse
app = FastAPI(title='Deploying a ML Model with FastAPI')
class Model(str, Enum):
yolov3tiny = "yolov3-tiny"
yolov3 = "yolov3"
# By using @app.get("/") you are allowing the GET method to work for the / endpoint.
@app.get("/")
def home():
return "Congratulations! Your API is working as expected."
# This endpoint handles all the logic necessary for the object detection to work.
# It requires the desired model and the image in which to perform object detection.
@app.post("/predict")
def prediction(model: Model, file: UploadFile = File(...)):
# 1. VALIDATE INPUT FILE
filename = file.filename
fileExtension = filename.split(".")[-1] in ("jpg", "jpeg", "png")
if not fileExtension:
raise HTTPException(status_code=415, detail="Unsupported file provided.")
# 2. TRANSFORM RAW IMAGE INTO CV2 image
# Read image as a stream of bytes
image_stream = io.BytesIO(file.file.read())
# Start the stream from the beginning (position zero)
image_stream.seek(0)
# Write the stream of bytes into a numpy array
file_bytes = np.asarray(bytearray(image_stream.read()), dtype=np.uint8)
# Decode the numpy array as an image
image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
# 3. RUN OBJECT DETECTION MODEL
# Run object detection
bbox, label, conf = cv.detect_common_objects(image, model=model)
# Create image that includes bounding boxes and labels
output_image = draw_bbox(image, bbox, label, conf)
# Save it in a folder within the server
cv2.imwrite(f'images_uploaded/{filename}', output_image)
# 4. STREAM THE RESPONSE BACK TO THE CLIENT
# Open the saved image for reading in binary mode
file_image = open(f'images_uploaded/{filename}', mode="rb")
# Return the image as a stream specifying media type
return StreamingResponse(file_image, media_type="image/jpeg")
# Allows the server to be run in this interactive environment
nest_asyncio.apply()
# This is an alias for localhost which means this particular machine
host = "127.0.0.1"
# Spin up the server!
uvicorn.run(app, host=host, port=8000, root_path="/serve")
모델 필드에서 모델을 선택하고 로컬에서 이미지를 선택한 다음 실행버튼을 클릭하면 서버에 HTTP 요청을 보내게 되고, 그런 다음 아래로 스크롤하면 모델이 예측한 결과를 확인할 수 있다.