FastAPI를 통한 기본 서빙
FastAPI를 통한 기본 서빙
FastAPI 를 통한 기본 서빙
포스팅의 설명과 코드는 coursera 강좌 Machine Learning in Production 을 참고하여 작성했습니다
- FastAPI 를 활용하여 모델이 어떻게 서빙되는지 이해하기 위해 작성했습니다.
- 포스팅과 코드의 목적은 서빙이므로 모델은 라이브러리를 통해 기본적인 yolo3를 사용합니다.
- 서버는 이미지를 입력으로 받아 yolo3 를 통해 객체를 탐지하고 바운딩박스와 confidence를 표시하여 제공합니다.
왜 FastAPI 인가?
- 완벽한 웹애플리케이션이나 보일러플레이트의 작성없이 ML모델 추론용 웹서버를 손쉽게 구축할 수 있습니다.
- 서버와 상호작용 할 수있는 빌트인 클라이언트를 제공합니다
코드 파헤치기
- fastapi 인스턴스를 생성한다.
- import 문 바로 다음에 위치시킴으로써 모든 라우터 및 미들웨어가 이 인스턴스를 참조할 수 있습니다
1
2
3
4
5
6
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import StreamingResponse
# Create FastAPI instance
app = FastAPI(title='Deploying an ML Model with FastAPI')
- 서빙에 필요한 여러가지 함수 정의
- 서빙에 사용할 입력데이터 유효성검사, 저장하기 등의 함수를 정의한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import os
import io
from enum import Enum
import cv2
import cvlib as cv
from cvlib.object_detection import draw_bbox
import numpy as np
from fastapi import FastAPI, UploadFile, File, HTTPException
class Model(str, Enum):
yolov3tiny = "yolov3-tiny"
yolov3 = "yolov3"
def create_output_dir(dir_name="images_uploaded"):
if not os.path.exists(dir_name):
os.mkdir(dir_name)
def validate_image(filename: str) -> bool:
return filename.split(".")[-1] in ("jpg", "jpeg", "png")
def read_image_file(file: UploadFile) -> np.ndarray:
# 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)
file_bytes = np.asarray(bytearray(image_stream.read()), dtype=np.uint8)
return cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
# Run object detection
def detect_objects(image: np.ndarray, model: Model) -> tuple:
return cv.detect_common_objects(image, model=model)
def create_output_image(image: np.ndarray, bbox: list, label: list, conf: list) -> np.ndarray:
return draw_bbox(image, bbox, label, conf)
def save_image(image: np.ndarray, filename: str) -> None:
cv2.imwrite(f'images_uploaded/{filename}', image)
- 서빙 코드 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 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. Now head over to http://docs"
# 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
if not validate_image(file.filename):
raise HTTPException(status_code=415, detail="Unsupported file provided.")
# 2. TRANSFORM RAW IMAGE INTO CV2 image
image = read_image_file(file)
# 3. RUN OBJECT DETECTION MODEL
bbox, label, conf = detect_objects(image, model)
# Create image that includes bounding boxes and labels
output_image = create_output_image(image, bbox, label, conf)
# Save it in a folder within the server
save_image(output_image, file.filename)
# 4. STREAM THE RESPONSE BACK TO THE CLIENT
file_image = open(f'images_uploaded/{file.filename}', mode="rb")
return StreamingResponse(file_image, media_type="image/jpeg")
- app 인스턴스를 유비콘을 통해 비동기화상태로 실행
1
2
3
4
5
6
import uvicorn
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8000)
This post is licensed under CC BY 4.0 by the author.