[AI ์คํฐ๋] Section 12 : YOLO ๋ชจ๋ธ Fast API๋ก ๋ฐฐํฌ
YOLO (You Only Look Once)
ํน์ง
- ์๋๊ฐ ๋น ๋ฆ ⇒ ์ด ๋น ์ฒ๋ฆฌํ๋ ํ๋ ์์๊ฐ ๋ง์
- ์ค์ ์๊ฐ์ผ๋ก ๋ญ๊ฐ๋ฅผ ํ ๋ ๋ง์ด ์ฌ์ฉ
- ์ ํ๋๊ฐ ๋์
- ๊ธฐ์กด ์ด๋ฏธ์ง ๋ชจ๋ธ๋ณด๋ค ์ข์์ง
- ๊ธฐ์กด ์ด๋ฏธ์ง ๋ชจ๋ธ : ์ด๋ค ๊ฒ์ ๊ด๋ จ๋ ์ฌ์ง์ธ์ง ๋ถ๋ฅ
- YOLO : ์ด๋ค ๋ฌผ์ฒด๊ฐ ์ด๋ค ์์น์ ์๋์ง ํ์ธํ ์ ์์ → object detection
์ฃผ์ ๊ตฌ์ฑ ์ฑ๋ถ
Bounding Box Prediction


- ๋ฐฉ๋ฒ : ์ฌ์ง์ 9๊ฐ์ ์์ญ์ผ๋ก ๋๋๊ณ , ๊ฐ ์์ญ ํ๋์ ๋ ์ด๋ธ 1๊ฐ์ฉ์ ๋ถ์
→ ์ฑ๊ธ์ด๋ฏธ์ง๋ ๋๊ณ , ๋ฉํฐ ์ด๋ฏธ์ง๋ ๊ฐ๋ฅ - \( p_c \) : ๋ฌผ์ฒด๊ฐ ์๋์ง, ์๋์ง
→ 1 : ์์ / 0 : ์์ - ๋ฐ์ด๋ฉ ๋ฐ์ค ( \( b_x, b_y, b_h, b_w \) ) : ์ค์ฌ์ ์ด ์ด๋์๋์ง๋ฅผ ๋ํ๋
→ \( b_h \) : ๋ฌผ์ฒด์ ๋์ด ( ex : 0.95 ) | \( b_w \) : ๋ฌผ์ฒด์ ํญ (ex : 1.72 ) | \( b_x, b_y \) : ๋ฌผ์ฒด์ ์ค์ฌ์ ( ex : (0.45, 0.98) ) - ํด๋์ค ๋ ์ด๋ธ : ๋ ์ด๋ธ๋ง ํ ๊ฒ
→ ์ํซ ์ธ์ฝ๋ฉ์ผ๋ก, ์ฐจ๋ฉด 1 0 0 ์์ ๊ฑฐ๋ฉด 0 0 1 ์ด๋ฐ์์
๋ชจ๋ธ ๋ฐฐํฌ์ ํ์ํ ์ฌ์ ์ง์
- deploying - prediction์ ํ์ํ ๋ชจ๋ ์ํํธ์จ์ด๋ฅผ 'server'์ ์ค์นํ๋ ๊ฒ. ์ด๋ ๊ฒ ํ๋ฉด 'client'๊ฐ ์๋ฒ์ 'request'๋ฅผ ๋ณด๋ด ๋ชจ๋ธ๊ณผ ์ํธ ์์ฉํ ์ ์์ต๋๋ค.
- client๋ ๋ชจ๋ธ์ด ์์ธก์ ์ํํ๋ ๋ฐ ํ์ํ ์ ๋ณด๋ฅผ ์ ๊ณต. server๋ ์ ๊ณต๋ ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์์ธก์ ๋ฐํ.
- FastAPI ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด ์์ํฉ๋๋ค.
app = FastAPI()
- ๋ค์ ๋จ๊ณ๋ ์ด ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ์ฌ ์์ธก ๋
ผ๋ฆฌ๋ฅผ ์ฒ๋ฆฌํ endpoint(path, route)๋ฅผ ๋ง๋๋ ๊ฒ์
๋๋ค.
์๋ฒ๋ฅผ ์คํํ๊ธฐ ์ํ ๋ชจ๋ ์ฝ๋๊ฐ ์ค๋น๋๋ฉด ๋ค์ ๋ช ๋ น๋ง ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
uvicorn.run(app)
- serving์ uvicorn์ ์ฌ์ฉํ์ฌ ์ํ๋๋ฉฐ uvicorn์ ASGI web server ์
๋๋ค.
ASGI(Asynchronous Server Gateway Interface - ๋น๋๊ธฐ Python ์น ์๋ฒ)
endpoints (path, route)
1. ๋์ผํ ์๋ฒ์์ ์ฌ๋ฌ ๊ธฐ๊ณํ์ต ๋ชจ๋ธ์ ํธ์คํ ํ ์ ์์
-> ์ด๋ฅผ ์๋ํ๋ ค๋ฉด ๊ฐ ๋ชจ๋ธ์ ๋ค๋ฅธ endpoint๋ฅผ ํ ๋นํด์ผํ๋๋ฐ, endpoint๋ URL ํจํด์ผ๋ก ํ์๋๋ค.
2. ex) imagetest.com ์ ์๋ํฌ์ธํธ์ 3๊ฐ์ง ๋ชจ๋ธ์ ์ง์ ํ ๊ฒ
- imagetest.com/lenet/
- imagetest.com/yolo/
- imagetest.com/mobilenet/
fastAPI์์๋ ํด๋น endpoint์ ๋ํ ๋ชจ๋ logic์ ์ฒ๋ฆฌํ ํจ์๋ฅผ ๋ง๋ค๊ณ endpoint๋ฅผ decorating ํฉ๋๋ค.
๋ค์ ์๋ ์๋ํฌ์ธํธ "/my-endpoint"์ ๋ํ HTTP GET ์์ฒญ์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
@app.get("/my-endpoint")
def handle_endpoint():
...
...
HTTP Requests
- GET -> ์๋ฒ์์ ์ ๋ณด ๊ฒ์
- POST -> ์๋ต์ ํ์ํ ์ ๋ณด๋ฅผ ์๋ฒ์ ์ ๊ณต
endpoint์ ์๋ ๊ธฐ๊ณ ํ์ต ๋ชจ๋ธ๊ณผ์ ์ผ๋ฐ์ ์ผ๋ก ์์ธก์ ๊ณ์ฐํ๋ ๋ฐ ํ์ํ ์ ๋ณด๋ฅผ ์ ๊ณตํด์ผ ํ๊ธฐ ๋๋ฌธ์
POST request๋ฅผ ํตํด ์ํ๋ฉ๋๋ค.
# ๋ฐ์ฝ๋ ์ดํฐ ๋ฌธ๋ฒ
@app.post("/my-other-endpoint")
# ์๋์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ํจ์๋ฅผ ํ๋ผ๋ฏธํฐ ํ์์ผ๋ก ๋ณด๋ด ์๋ํ๋๋กํจ
def handle_other_endpoint(param1: int, param2: str):
...
...
์์
: object detection์ ์ฌ์ฉ๋ ์ด๋ฏธ์ง ๋ฐ์ดํฐ์ ๊ฒ์ฌ → ๋ชจ๋ธ ์ดํด๋ณด๊ธฐ → fastAPI๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ธ ๋ฐฐํฌ
์ด๊ธฐ ์ค์น ์ฝ๋
!pip install opencv-python
!pip install cvlib
!pip install uvicorn
!pip install fastapi
!pip install python-multipart
YOLOV3 ๋ฅผ ์ด์ฉํ ๊ฐ์ฒด ๊ฒ์ถ (Object Detection)
from IPython.display import Image, display
import os
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ ์ถ๋ ฅํด๋ณด๊ธฐ
image_files = os.listdir('images')
for image_file in image_files:
print(f"\\n{image_file}")
display(Image(filename=f"images/{image_file}"))
# ๊ฐ์ฒด ๊ฐ์ง ๋ชจ๋ธ์ ์ฌ์ฉํ๊ธฐ ์ ์ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ ์ ์๋ ๋๋ ํฐ๋ฆฌ ์์ฑ
dir_name = "images_with_boxes"
if not os.path.exists(dir_name):
os.mkdir(dir_name)
import cv2
import cvlib as cv
from cvlib.object_detection import draw_bbox
# ์์คํ
์ ํ์ผ์ ์
๋ ฅ์ผ๋ก ์ด๋ฏธ์ง์ ๊ฐ์ฒด๋ฅผ ํ์งํ๊ณ , ๊ฐ์ฒด์ ํจ๊ป bounding box๋ฅผ ํ์ํ๋ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋ ํจ์
# filename : ์๋ณธ ํ์ผ / model : ๊ฐ์ฅ ์ต์ yolo๋ชจ๋ธ ์ฌ์ฉ / confidence : ์ผ๋ง๋ ํ์ ํ๋์ง -> ํด๋น ํผ์ผํธ ์ด์์ผ๋๋ง ๋ต์ ์ค
def detect_and_draw_box(filename, model="yolov4", confidence=0.5): # 50% ์ด์ ํ์ ์ ๋ต์ ์ค
"""์ด๋ฏธ์ง์์ ์ผ๋ฐ์ ์ธ object๋ฅผ ๊ฐ์งํ๊ณ
bounding box ๊ฐ ์๋ ์ ์ด๋ฏธ์ง ๋ง๋ค๊ธฐ. """
img_filepath = f'images/{filename}'
img = cv2.imread(img_filepath) # Read the image into a numpy array
img = cv2.resize(img, (250, 250)) # ์ด๋ฏธ 250 250 ์ผ๋ก pretrained๋์ด ์๊ธฐ๋๋ฌธ์
print(img.shape)
# object detection ์ํ
bbox, label, conf = cv.detect_common_objects(img, confidence=confidence, model=model)
print(f"========================\\n์ด๋ฏธ์ง ์ฒ๋ฆฌ ์๋ฃ: {filename}\\n")
for l, c in zip(label, conf):
print(f"๊ฒ์ถ๋ ๊ฐ์ฒด: {l} - confidence level: {c:.2f}\\n")
# bounding box๋ฅผ ํฌํจํ๋ ์ ์ด๋ฏธ์ง ๋ง๋ค๊ธฐ
output_image = draw_bbox(img, bbox, label, conf)
cv2.imwrite(f'images_with_boxes/{filename}', output_image) # ์๋ก์ด ์ด๋ฏธ์ง ์ ์ฅ
# bounding box๊ฐ ์ถ๊ฐ๋ ์ด๋ฏธ์ง display
display(Image(f'images_with_boxes/{filename}'))
# ํจ์ ์คํ
for image_file in image_files:
detect_and_draw_box(image_file)

๋ชจ๋ธ ๋ฐฐํฌ : fastAPI ์ฌ์ฉ
→ ์๋ฒ์ ์ํธ ์์ฉํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ด์ฅ๋์ด์์
# ๋ธ๋ผ์ฐ์ ์์ ์๋ก์ด ์ด๋ฏธ์ง๋ฅผ ๋ก๋ํด์ ๊ตฌ๋ถํ๊ธฐ ์ํ ๋๋ ํ ๋ฆฌ
dir_name = "images_uploaded"
if not os.path.exists(dir_name):
os.mkdir(dir_name)
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='FastAPI๋ฅผ ์ด์ฉํ ML model deploy')
#Enum์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ธ์ ๋์ดํฉ๋๋ค. ์ด๋ ์ต์
์ด ๋ฏธ๋ฆฌ ์ ์๋ ๊ฒฝ์ฐ์ ์ ์ฉํฉ๋๋ค.
class Model(str, Enum):
# ๋์ค ํ๋๋ฅผ ๊ณ ๋ฅด๋๋ก ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค์ด์ค๊ฒ
yolov4tiny = "yolov4-tiny"
yolov4 = "yolov4"
@app.get("/")
def home():
return "API ๊ฐ ์ ์ ์๋ํ๊ณ ์์ต๋๋ค. <http://localhost:8000/docs> ๋ก ์ด๋ํ์ธ์."
@app.post("/predict")
def prediction(model: Model, file: UploadFile = File(...)):
# 1. INPUT FILE ํ์ธ
filename = file.filename
fileExtension = filename.split(".")[-1] in ("jpg", "jpeg", "png") # ์๋ค๋ง ์ฒ๋ฆฌ ๊ฐ๋ฅ
if not fileExtension:
raise HTTPException(status_code=415, detail="์ง์ํ์ง ์๋ ํ์ผ ํ์
์
๋๋ค.")
# 2. RAW IMAGE๋ฅผ CV2 image๋ก ๋ณํ
image_stream = io.BytesIO(file.file.read()) # stream of bytes ๋ก ์ด๋ฏธ์ง ์ฝ๊ธฐ
image_stream.seek(0) # stream์ ์ฒ์(0 ์์น)๋ถํฐ ์์ํฉ๋๋ค.
# numpy ๋ฐฐ์ด๋ก bytes stream ์ฐ๊ธฐ
file_bytes = np.asarray(bytearray(image_stream.read()), dtype=np.uint8)
# numpy ๋ฐฐ์ด์ ์ด๋ฏธ์ง๋ก ๋์ฝ๋ฉ
image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
# 3. OBJECT DETECTION MODEL ์คํ
bbox, label, conf = cv.detect_common_objects(image, model=model)
# bounding box์ ๋ ์ด๋ธ์ด ํฌํจ๋ ์ด๋ฏธ์ง๋ฅผ ๋ง๋ค์ด ์ ์ฅ
output_image = draw_bbox(image, bbox, label, conf)
cv2.imwrite(f'images_uploaded/{filename}', output_image)
# 4. CLIENT๋ก Respond
# ์ ์ฅ๋ ์ด๋ฏธ์ง๋ฅผ binary mode๋ก ์ฝ๊ธฐ
file_image = open(f'images_uploaded/{filename}', mode="rb")
# ๋ฏธ๋์ด type์ ์ง์ ํ์ฌ ์คํธ๋ฆผ์ผ๋ก ์ด๋ฏธ์ง ๋ฐํ
return StreamingResponse(file_image, media_type="image/jpeg")
# ์๋ฒ๊ฐ ๊ฐ๋ ์ฝ๋ -> ์ปค๋ ์ค๋จ ์ ๊น์ง ์คํ๋จ
# Jupyter notebook ํ๊ฒฝ์์ ์๋ฒ๋ฅผ ์คํํ ์ ์์ต๋๋ค.
nest_asyncio.apply()
host = "127.0.0.1"
# Spin up the server!
uvicorn.run(app, host=host, port=8000)
⇒ http://localhost:8000/๋ก ์ด๋ํ์ฌ ์๋ฒ๊ฐ ์๋ํ๋ ๊ฒ์ ํ์ธ
⇒ image๋ฅผ ์ ์ถํ์ฌ API๊ฐ ์ด๋ฏธ์ง ์์ ๊ฐ์ฒด๋ฅผ ๊ฐ์งํ๊ณ ๊ฐ์ง๋ ๊ฐ์ฒด์ ๋ ์ด๋ธ๊ณผ ํจ๊ป bounding box๊ฐ ํฌํจ๋ ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐํํ๋ ํ๋ ๊ฒ์ ํ์ธ
⇒ http://localhost:8000/docs๋ฅผ ๋ฐฉ๋ฌธํ์ฌ fastAPI์ ๋ด์ฅ client ์ฌ์ฉ