Deep Learning Study/DeepLearning.AI (Coursera)

Deploying a Machine Learning Model (Fast API)

Hyo__ni 2023. 12. 25. 18:56

 

이미지에서 객체를 검출하도록 학습된 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 요청을 보내게 되고, 그런 다음 아래로 스크롤하면 모델이 예측한 결과를 확인할 수 있다.