๊ฐ์์ง ๊ด๋ จ AI ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํตํฉ FastAPI ์๋ฒ์ ๋๋ค. ๊ฐ์์ง ํ์ข ๋ถ๋ฅ, ์๋ฒ ๋ฉ ์์ฑ, ์ ์ฌ๋ ๊ณ์ฐ ๋ฑ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
- ๐ ๊ฐ์์ง ํ์ข ๋ถ๋ฅ: YOLO + EfficientNet์ ์ฌ์ฉํ 120๊ฐ ํ์ข ๋ถ๋ฅ
- ๐ ์๋ฒ ๋ฉ ์์ฑ: CLIP + YOLO๋ฅผ ์ฌ์ฉํ ์ด๋ฏธ์ง/ํ ์คํธ ์๋ฒ ๋ฉ ์์ฑ
- ๐ ์ ์ฌ๋ ๊ณ์ฐ: ๋ ์๋ฒ ๋ฉ ๊ฐ์ ์ ์ฌ๋ ๊ณ์ฐ (์ ์ค๊ฒฌ/๋ฐ๊ฒฌ๊ฒฌ ๋งค์นญ์ฉ)
- ๐ ํ๊ธ ์ง์: ํ๊ตญ์ด ๊ฒฌ์ข ๋ช ๋งคํ
Gangajikimi-FastAPI-Server/
โโโ app/
โ โโโ main.py # FastAPI ์ฑ ์ง์
์
โ โโโ domain/ # ๋๋ฉ์ธ๋ณ ๊ธฐ๋ฅ ๋ชจ๋
โ โ โโโ config.py # ๊ณตํต ์ค์ (LLM, YOLO, CLIP ๋ฑ)
โ โ โโโ dogbreed/ # ๊ฐ์์ง ํ์ข
๋ถ๋ฅ ๋๋ฉ์ธ
โ โ โ โโโ router.py # API ๋ผ์ฐํฐ
โ โ โ โโโ schema.py # Pydantic ๋ชจ๋ธ๋ค
โ โ โ โโโ service.py # ๋น์ฆ๋์ค ๋ก์ง
โ โ โ โโโ assets/ # AI ๋ชจ๋ธ ๊ฐ์ค์น
โ โ โ โโโ models/ # AI ๋ชจ๋ธ ์ฝ๋๋ค
โ โ โโโ embed/ # ์๋ฒ ๋ฉ ์์ฑ ๋๋ฉ์ธ
โ โ โ โโโ router.py # API ๋ผ์ฐํฐ
โ โ โ โโโ schema.py # Pydantic ๋ชจ๋ธ๋ค
โ โ โ โโโ pipeline_embed.py # ๋ฉ์ธ ์๋ฒ ๋ฉ ํ์ดํ๋ผ์ธ
โ โ โ โโโ llm_client.py # LLM ํ
์คํธ ์ ์
โ โ โ โโโ yolo_crop.py # YOLO ์ด๋ฏธ์ง ํฌ๋กญ
โ โ โ โโโ clipper.py # CLIP ์๋ฒ ๋ฉ
โ โ โโโ similarity/ # ์ ์ฌ๋ ๊ณ์ฐ ๋๋ฉ์ธ
โ โ โโโ router.py # API ๋ผ์ฐํฐ
โ โ โโโ schema.py # Pydantic ๋ชจ๋ธ๋ค
โ โ โโโ pipeline_similarity.py # ๋ฉ์ธ ์ ์ฌ๋ ํ์ดํ๋ผ์ธ
โ โ โโโ similarity.py # ์ ์ฌ๋ ๊ณ์ฐ ํจ์๋ค
โ โโโ response/ # ์๋ต ์ ํธ๋ฆฌํฐ
โโโ requirements/ # ์์กด์ฑ ๊ด๋ฆฌ
โ โโโ requirements-base.txt # ๊ธฐ๋ณธ ํจํค์ง
โ โโโ requirements-cpu.txt # CPU ๋ฒ์ PyTorch
โ โโโ requirements-gpu-cu128.txt # GPU ๋ฒ์ PyTorch
โโโ Dockerfile # Docker ์ปจํ
์ด๋ ์ค์
โโโ yolov8s.pt # YOLO ๋ชจ๋ธ ๊ฐ์ค์น
- Python 3.8 ์ด์
- ์ต์ 4GB RAM (๋ชจ๋ธ ๋ก๋ฉ์ฉ)
- ๋์คํฌ ๊ณต๊ฐ 2GB ์ด์
# ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd Gangajikimi-FastAPI-Server
# ๊ฐ์ํ๊ฒฝ ์์ฑ
python -m venv fastapi_env
# ๊ฐ์ํ๊ฒฝ ํ์ฑํ
source fastapi_env/bin/activate # Linux/Mac
# ๋๋
fastapi_env\Scripts\activate # Windows# ๊ธฐ๋ณธ ํจํค์ง ์ค์น
pip install -r requirements/requirements-base.txt
# PyTorch ์ค์น (CPU ๋ฒ์ )
pip install -r requirements/requirements-cpu.txt
# ๋๋ GPU ๋ฒ์ (CUDA 12.8)
pip install -r requirements/requirements-gpu-cu128.txt๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ:
# config.py.example์ ๋ณต์ฌํ์ฌ config.py ์์ฑ
cp app/domain/config.py.example app/domain/config.py
# config.py๋ฅผ ์ด์ด์ GEMINI_API_KEY ์์
# GEMINI_API_KEY = "YOUR_ACTUAL_API_KEY_HERE"Gemini API ํค ๋ฐ๊ธ ๋ฐฉ๋ฒ:
- Google AI Studio์ ์ ์
- "Create API Key" ํด๋ฆญํ์ฌ API ํค ์์ฑ
app/domain/config.pyํ์ผ์์GEMINI_API_KEY๊ฐ์ ์์
์ฃผ์์ฌํญ:
โ ๏ธ config.py๋.gitignore์ ํฌํจ๋์ด ์์ด GitHub์ ์ ๋ก๋๋์ง ์์ต๋๋ค- ์ด์/๋ฐฐํฌ ํ๊ฒฝ์์๋ GitHub Actions๊ฐ ํ๊ฒฝ๋ณ์๋ก ์๋ ์์ฑํฉ๋๋ค
- ํ๊ฒฝ๋ณ์๋ก๋ ์ค์ ๊ฐ๋ฅ:
export GEMINI_API_KEY="your_key"
# ๊ฐ๋ฐ ๋ชจ๋ ์คํ
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
# ํ๋ก๋์
๋ชจ๋ ์คํ
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4์๋ฒ ์คํ ํ ๋ค์ URL์์ API ๋ฌธ์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
์๋ํฌ์ธํธ: POST /api/v1/dogbreed/
์์ฒญ:
- Content-Type:
multipart/form-data - image: ๊ฐ์์ง ์ด๋ฏธ์ง ํ์ผ (JPG, PNG ๋ฑ)
์์ฒญ ์์:
curl -X POST "http://localhost:8000/api/v1/dogbreed/" \
-H "accept: application/json" \
-F "image=@/path/to/dog_image.jpg"์๋ต:
{
"result": "๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ"
}์๋ํฌ์ธํธ: POST /api/v1/embed/normalize
์์ฒญ:
{
"breed": "๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ",
"colors": "๊ณจ๋, ํฐ์",
"features": "ํฐ ๊ท, ๊ธด ํธ"
}์๋ต:
{
"sentences": [
"๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ ๊ฒฌ์ข
์
๋๋ค.",
"๊ณจ๋์ ํฐ์ ์์์ ๊ฐ์์ง์
๋๋ค.",
"ํฐ ๊ท์ ๊ธด ํธ์ด ํน์ง์
๋๋ค."
]
}์๋ํฌ์ธํธ: POST /api/v1/embed/embed
์์ฒญ:
- Content-Type:
multipart/form-data - image: ๊ฐ์์ง ์ด๋ฏธ์ง ํ์ผ
- breed: ํ์ข (์ ํ์ฌํญ)
- colors: ์์ (์ ํ์ฌํญ)
- features: ํน์ง (์ ํ์ฌํญ)
์์ฒญ ์์:
curl -X POST "http://localhost:8000/api/v1/embed/embed" \
-H "accept: application/json" \
-F "image=@/path/to/dog_image.jpg" \
-F "breed=๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ" \
-F "colors=๊ณจ๋, ํฐ์" \
-F "features=ํฐ ๊ท, ๊ธด ํธ"์๋ต:
{
"sentences": [
"๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ ๊ฒฌ์ข
์
๋๋ค.",
"๊ณจ๋์ ํฐ์ ์์์ ๊ฐ์์ง์
๋๋ค.",
"ํฐ ๊ท์ ๊ธด ํธ์ด ํน์ง์
๋๋ค."
],
"image": [0.1, -0.2, 0.3, ...], // 512์ฐจ์ ์ด๋ฏธ์ง ์๋ฒ ๋ฉ
"text": [0.2, -0.1, 0.4, ...] // 512์ฐจ์ ํ
์คํธ ์๋ฒ ๋ฉ
}์๋ํฌ์ธํธ: POST /api/v1/similarity/score
์์ฒญ:
{
"emb_a_image": [0.1, -0.2, 0.3, ...], // ๊ฒ์๋ฌผ A์ ์ด๋ฏธ์ง ์๋ฒ ๋ฉ
"emb_a_text": [0.2, -0.1, 0.4, ...], // ๊ฒ์๋ฌผ A์ ํ
์คํธ ์๋ฒ ๋ฉ
"emb_b_image": [0.3, -0.3, 0.2, ...], // ๊ฒ์๋ฌผ B์ ์ด๋ฏธ์ง ์๋ฒ ๋ฉ
"emb_b_text": [0.1, -0.2, 0.3, ...], // ๊ฒ์๋ฌผ B์ ํ
์คํธ ์๋ฒ ๋ฉ
"weights": [0.2, 0.0, 0.0, 0.8] // ๊ฐ์ค์น (์ ํ์ฌํญ)
}์๋ต:
{
"s_ii": 0.85, // ์ด๋ฏธ์ง-์ด๋ฏธ์ง ์ ์ฌ๋
"s_it": 0.72, // ์ด๋ฏธ์ง-ํ
์คํธ ์ ์ฌ๋
"s_ti": 0.68, // ํ
์คํธ-์ด๋ฏธ์ง ์ ์ฌ๋
"s_tt": 0.91, // ํ
์คํธ-ํ
์คํธ ์ ์ฌ๋
"score": 0.88, // ์ต์ข
๊ฐ์ค ํ๊ท ์ ์ฌ๋
"threshold": 0.35, // ์๊ณ๊ฐ
"pass": true // ์๊ณ๊ฐ ๊ธฐ์ค ๋งค์นญ ์ฌ๋ถ
}๋ค์ ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค:
# Gemini API ์ค์ (LLM ํ
์คํธ ์ ์ ์ฉ)
GEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent
GEMINI_API_KEY=your_gemini_api_key_here # Gemini API ํค๋ฅผ ์ฌ๊ธฐ์ ์
๋ ฅ
GEMINI_TEMPERATURE=0.1
LLM_TIMEOUT=20
LLM_MAX_RETRIES=3
# YOLO ์ค์
YOLO_WEIGHTS=yolov8s.pt
YOLO_CONF=0.30
YOLO_MARGIN=0.18
YOLO_IMGSZ=512
DOG_CLASS_ID=16
# CLIP ์ค์
CLIP_MODEL=ViT-B-32
CLIP_PRETRAINED=laion2b_s34b_b79k
TEXT_POOLING=avg
# ์ ์ฌ๋ ๊ฐ์ค์น
W_II=0.20 # ์ด๋ฏธ์ง-์ด๋ฏธ์ง ๊ฐ์ค์น
W_IT=0.00 # ์ด๋ฏธ์ง-ํ
์คํธ ๊ฐ์ค์น
W_TI=0.00 # ํ
์คํธ-์ด๋ฏธ์ง ๊ฐ์ค์น
W_TT=0.80 # ํ
์คํธ-ํ
์คํธ ๊ฐ์ค์น
# ์ ์ฌ๋ ์๊ณ๊ฐ
SIM_THRESHOLD=0.35# Docker ์ด๋ฏธ์ง ๋น๋
docker build -t gangajikimi-api .
# Docker ์ปจํ
์ด๋ ์คํ (ํ๊ฒฝ๋ณ์ ํฌํจ)
docker run -p 8000:8000 \
-e GEMINI_API_KEY="your_gemini_api_key_here" \
gangajikimi-api
# ๋๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์คํ
docker run -d -p 8000:8000 \
-e GEMINI_API_KEY="your_gemini_api_key_here" \
--name gangajikimi-server \
gangajikimi-api๋ชจ๋ธ ์บ์๋ฅผ ์ฌ์ฌ์ฉํ๋ ค๋ฉด ๋ณผ๋ฅจ์ ๋ง์ดํธํ ์ ์์ต๋๋ค:
docker run -d -p 8000:8000 \
-e GEMINI_API_KEY="your_gemini_api_key_here" \
-v $(pwd)/.cache:/app/.cache \
--name gangajikimi-server \
gangajikimi-api์ฐธ๊ณ :
- Hugging Face ๋ชจ๋ธ์
/app/.cache/huggingface์ ๋ค์ด๋ก๋๋ฉ๋๋ค - ๋น๋ฃจํธ ์ฌ์ฉ์(
appuser, UID 10001)๋ก ์คํ๋๋ฏ๋ก ๊ถํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค
์ด ํ๋ก์ ํธ๋ GitHub Actions๋ฅผ ํตํด GCP VM์ ์๋์ผ๋ก ๋ฐฐํฌํ ์ ์์ต๋๋ค.
-
Docker Hub ๊ณ์ ์ค๋น: Docker Hub ๊ณ์ ๋ฐ Access Token ์์ฑ
-
GCP VM ์์ฑ: GCP Compute Engine์์ VM ์ธ์คํด์ค ์์ฑ
-
SSH ํค ์ค์ : VM ์ ์์ฉ SSH ํค ์์ฑ ๋ฐ ๋ฑ๋ก
-
GitHub Secrets ์ถ๊ฐ: Repository Settings์์ ํ์ํ secrets ์ถ๊ฐ
DOCKERHUB_USERNAME: Docker Hub ์ฌ์ฉ์ ์ด๋ฆDOCKERHUB_TOKEN: Docker Hub Access TokenSSH_PRIVATE_KEY: VM ์ ์์ฉ SSH Private ํคVM_HOST: VM์ ์ธ๋ถ IP ์ฃผ์VM_USER: VM ์ฌ์ฉ์ ์ด๋ฆ (์:ubuntu)GEMINI_API_KEY: Gemini API ํคCONFIG_PY:app/domain/config.pyํ์ผ์ ์ ์ฒด ๋ด์ฉ
-
์๋ ๋ฐฐํฌ:
develop๋ธ๋์น์ pushํ๋ฉด ์๋์ผ๋ก ๋ฐฐํฌ
์์ธํ ์ค์ ๋ฐฉ๋ฒ์ GCP ๋ฐฐํฌ ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ์ธ์.
config.pyํ์ผ ์์ฑ (GitHub Secret์์)- Docker ์ด๋ฏธ์ง ๋น๋
- Docker Hub์ ํธ์
- SSH ํค ์ค์
- VM์ SSH ์ ์ํ์ฌ ์ปจํ ์ด๋ ์ฌ์์
-
์ ์ค๊ฒฌ ๊ฒ์๋ฌผ ์ฒ๋ฆฌ:
# ์ ์ค๊ฒฌ ์ด๋ฏธ์ง์ ์ค๋ช ์ผ๋ก ์๋ฒ ๋ฉ ์์ฑ curl -X POST "http://localhost:8000/api/v1/embed/embed" \ -F "image=@lost_dog.jpg" \ -F "breed=๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ" \ -F "colors=๊ณจ๋, ํฐ์" \ -F "features=ํฐ ๊ท, ๊ธด ํธ"
-
๋ฐ๊ฒฌ๊ฒฌ ๊ฒ์๋ฌผ ์ฒ๋ฆฌ:
# ๋ฐ๊ฒฌ๊ฒฌ ์ด๋ฏธ์ง์ ์ค๋ช ์ผ๋ก ์๋ฒ ๋ฉ ์์ฑ curl -X POST "http://localhost:8000/api/v1/embed/embed" \ -F "image=@found_dog.jpg" \ -F "breed=๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ" \ -F "colors=๊ณจ๋, ํฐ์" \ -F "features=ํฐ ๊ท, ๊ธด ํธ"
-
์ ์ฌ๋ ๊ณ์ฐ:
# ๋ ๊ฒ์๋ฌผ ๊ฐ์ ์ ์ฌ๋ ๊ณ์ฐ curl -X POST "http://localhost:8000/api/v1/similarity/score" \ -H "Content-Type: application/json" \ -d '{ "emb_a_image": [...], "emb_a_text": [...], "emb_b_image": [...], "emb_b_text": [...] }'
์ด ํ๋ก์ ํธ๋ MIT ๋ผ์ด์ ์ค ํ์ ๋ฐฐํฌ๋ฉ๋๋ค.
๋ฒ๊ทธ ๋ฆฌํฌํธ๋ ๊ธฐ๋ฅ ์ ์์ ์ด์๋ฅผ ํตํด ์๋ ค์ฃผ์ธ์.
Gangajikimi AI - ๊ฐ์์ง์ ํจ๊ปํ๋ ๋ ๋์ ์ธ์์ ๋ง๋ค์ด๊ฐ๋๋ค. ๐โจ