Week 20 ํ์ต ์ ๋ฆฌ
pydantic: ๋ฐ์ดํฐ ๊ฒ์ฆ ๋ฐ ์ค์ ๊ด๋ฆฌ๋ฅผ ์ํ ๊ฐ๋ ฅํ ํ์ด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
pydantic์ ํ์ด์ฌ์์ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง๊ณผ ์ค์ ๊ด๋ฆฌ๋ฅผ ์์ฝ๊ฒ ํ ์ ์๋๋ก ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ํ์ ํํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋ ํด๋์ค๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํ๊ณ , JSON ์ง๋ ฌํ/์ญ์ง๋ ฌํ๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ์ง์ํฉ๋๋ค. ํนํ FastAPI์ ๊ฐ์ ํ๋ ์์ํฌ์์ ๋๋ฆฌ ์ฌ์ฉ๋์ด, API ์์ฒญ/์๋ต ๋ฐ์ดํฐ์ ๊ฒ์ฆ ๋ฐ ํ๊ฒฝ ์ค์ ๊ด๋ฆฌ์ ํฐ ๋์์ ์ฃผ๊ณ ์์ต๋๋ค.
1. pydantic์ ํต์ฌ ๊ธฐ๋ฅ
๋ฐ์ดํฐ ๊ฒ์ฆ (Validation)
- ํ์
๊ฒ์ฆ:
์ ์ธ๋ ํ์ ํํธ์ ๋ฐ๋ผ ์ ๋ ฅ ๋ฐ์ดํฐ๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํฉ๋๋ค. ์๋ฅผ ๋ค์ด, URL, ์ ์ ๋ฒ์, ๋๋ ํ ๋ฆฌ ๊ฒฝ๋ก ๋ฑ์ ๊ฒ์ฆ์ ๊ธฐ๋ณธ ์ ๊ณตํ๋ ํ์ ์ ํ์ฉํ ์ ์์ต๋๋ค. - ์ฌ์ฉ์ ์นํ์ ์๋ฌ ๋ฉ์์ง:
๊ฒ์ฆ์ ์คํจํ ๊ฒฝ์ฐ, ์ด๋ ํ๋์์ ์ด๋ค ์ด์ ๋ก ์คํจํ๋์ง ์์ธํ ์ ๋ณด๋ฅผ JSON ํํ๋ก ์ ๊ณตํ์ฌ ๋๋ฒ๊น ์ ์ฉ์ดํฉ๋๋ค.
๋ฐ์ดํฐ ์ง๋ ฌํ ๋ฐ ์ญ์ง๋ ฌํ
- JSON ๋ณํ:
pydantic ๋ชจ๋ธ์ ์ฝ๊ฒ JSON์ผ๋ก ์ง๋ ฌํํ ์ ์์ผ๋ฉฐ, JSON ๋ฐ์ดํฐ๋ฅผ Python ๊ฐ์ฒด๋ก ์ญ์ง๋ ฌํํ๋ ๊ณผ์ ์ด ๊ฐํธํฉ๋๋ค.
2. ๋ฐ์ดํฐ ๊ฒ์ฆ ์์
๋ค์ ์์ ๋ ์ ๋ ฅ ๋ฐ์ดํฐ๊ฐ ์ฌ๋ฐ๋ฅธ URL, 1~10 ์ฌ์ด์ ์ ์, ๊ทธ๋ฆฌ๊ณ ์กด์ฌํ๋ ๋๋ ํ ๋ฆฌ์ธ์ง ๊ฒ์ฆํ๋ pydantic ๋ชจ๋ธ์ ๋ณด์ฌ์ค๋๋ค.
from pydantic import BaseModel, HttpUrl, Field, DirectoryPath
class Validation(BaseModel):
url: HttpUrl # ์ฌ๋ฐ๋ฅธ URL์ธ์ง ๊ฒ์ฆ
rate: int = Field(ge=1, le=10) # 1~10 ์ฌ์ด์ ์ ์์ธ์ง ๊ฒ์ฆ
target_dir: DirectoryPath # ์ค์ ์กด์ฌํ๋ ๋๋ ํ ๋ฆฌ์ธ์ง ๊ฒ์ฆ
๊ฒ์ฆ ์คํ ์์
import os
from pydantic import ValidationError
VALID_INPUT = {
"url": "https://content.presspage.com/uploads/2658/c800_logo-stackoverflow-square.jpg?98978",
"rate": 4,
"target_dir": os.path.join(os.getcwd(), "examples"), # ํ์ฌ ๋๋ ํ ๋ฆฌ ๋ด "examples" ํด๋๊ฐ ์์ด์ผ ํจ
}
INVALID_INPUT = {"url": "WRONG_URL", "rate": 11, "target_dir": "WRONG_DIR"}
# ์ฌ๋ฐ๋ฅธ ์
๋ ฅ์ ๊ฒ์ฆํ๋ ๊ฒฝ์ฐ
valid_model = Validation(**VALID_INPUT)
print("๊ฒ์ฆ ์ฑ๊ณต:", valid_model)
# ์๋ชป๋ ์
๋ ฅ์ ValidationError ๋ฐ์
try:
invalid_model = Validation(**INVALID_INPUT)
except ValidationError as exc:
print("pydantic model input validation error:", exc.json())
์ ์ฝ๋๋ VALID_INPUT์ ๊ฒฝ์ฐ ์ ์์ ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฉฐ, INVALID_INPUT์ ๊ฒฝ์ฐ ์๋ฌ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํฉ๋๋ค. ์๋ฌ ๋ฉ์์ง์๋ ์ด๋ค ํ๋์์ ์ด๋ค ๊ฐ์ด ๋ฌธ์ ์๋์ง ์์ธํ ์๋ดํด์ค๋๋ค.
3. ์ค์ ๊ด๋ฆฌ (Configuration) with BaseSettings
ํ๋ก์ ํธ์์ ์์๋ฅผ ์ฝ๋์ ํ๋์ฝ๋ฉํ๊ฑฐ๋ ๋ณ๋์ ํ์ผ(yaml ๋ฑ)๋ก ๊ด๋ฆฌํ๋ ๋์ , pydantic์ BaseSettings๋ฅผ ํ์ฉํ๋ฉด ํ๊ฒฝ ๋ณ์๋ก๋ถํฐ ์ค์ ๊ฐ์ ์์ ํ๊ฒ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ฐฐํฌ ํ๊ฒฝ์ ๋ฐ๋ผ ์ค์ ์ ์์ฝ๊ฒ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ, ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ์ฝ๋์ ๋ ธ์ถ๋์ง ์๋๋ก ํ ์ ์์ต๋๋ค.
์ค์ ํ์ผ ์์
from pydantic import BaseSettings, Field
from enum import Enum
class ConfigEnv(str, Enum):
DEV = "dev"
PROD = "prod"
class DBConfig(BaseSettings):
host: str = Field(default="localhost", env="db_host")
port: int = Field(default=3306, env="db_port")
username: str = Field(default="user", env="db_username")
password: str = Field(default="user", env="db_password")
database: str = Field(default="dev", env="db_database")
class AppConfig(BaseSettings):
env: ConfigEnv = Field(default="dev", env="env")
db: DBConfig = DBConfig()
# ์๋ฅผ ๋ค์ด, dev_config.yaml ํ์ผ์ ๋ก๋ํ์ฌ ๊ธฐ๋ณธ ์ค์ ์ ๊ตฌ์ฑํ ์๋ ์์
# (์ฌ๊ธฐ์๋ ํ๊ฒฝ ๋ณ์๋ฅผ ํตํ ์ค์ ์ค๋ฒ๋ผ์ด๋ฉ ์์ ๋ฅผ ํจ๊ป ์ค๋ช
)
ํ๊ฒฝ ๋ณ์๋ก ์ค์ ์ค๋ฒ๋ผ์ด๋ฉ
์๋ ์์ ๋ ํ๊ฒฝ ๋ณ์๋ฅผ ์ด์ฉํด ์ค์ ๊ฐ์ ์ค๋ฒ๋ผ์ด๋ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
import os
# ํ๊ฒฝ ๋ณ์ ์ค์ (์ค์ ๋ฐฐํฌ ํ๊ฒฝ์์๋ ์์คํ
ํ๊ฒฝ ๋ณ์๋ก ๊ด๋ฆฌ)
os.environ["env"] = "prod"
os.environ["db_host"] = "mysql"
os.environ["db_username"] = "admin"
os.environ["db_password"] = "PASSWORD"
# ํ๊ฒฝ ๋ณ์ ๊ธฐ๋ฐ ์ค์ ๊ฐ์ฒด ์์ฑ
prod_config_with_pydantic = AppConfig()
print("ํ๊ฒฝ:", prod_config_with_pydantic.env)
print("DB ์ค์ :", prod_config_with_pydantic.db.dict())
# cleanup: ํ์ ์ ํ๊ฒฝ ๋ณ์ ์ ๋ฆฌ
# os.environ.pop("env")
# os.environ.pop("db_host")
# os.environ.pop("db_username")
# os.environ.pop("db_password")
์ด์ ๊ฐ์ด pydantic์ BaseSettings๋ฅผ ์ฌ์ฉํ๋ฉด YAML ํ์ผ์ด๋ ๋ณ๋์ ์ค์ ํ์ผ ์์ด๋, ํ๊ฒฝ ๋ณ์๋ง์ผ๋ก ์์ฝ๊ฒ ์ค์ ์ ๊ด๋ฆฌํ ์ ์์ด ๋ณด์๊ณผ ์ ์ฐ์ฑ์ ๋์์ ํ๋ณดํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
pydantic์ ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก ๋ง์ ํ์ด์ฌ ๊ฐ๋ฐ์์ FastAPI ์ฌ์ฉ์์๊ฒ ์ฌ๋๋ฐ๊ณ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ๊ฒ์ฆ:
๊ฐ๊ฒฐํ ์ฝ๋๋ก ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๊ฒ์ฆํ๊ณ , ์์ธํ ์๋ฌ ๋ฉ์์ง ์ ๊ณต - ์ง๋ ฌํ/์ญ์ง๋ ฌํ:
JSON๊ณผ ๊ฐ์ ํฌ๋งท์ผ๋ก ์์ฝ๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ณํ - ์ค์ ๊ด๋ฆฌ:
BaseSettings๋ฅผ ํ์ฉํด ํ๊ฒฝ ๋ณ์ ๊ธฐ๋ฐ์ ์์ ํ๊ณ ์ ์ฐํ ์ค์ ๊ด๋ฆฌ
Docker๋ก ์ปจํ ์ด๋ ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ๋ฐ ๋ฐฐํฌ
Docker๋ ์ปจํ ์ด๋ ๊ธฐ์ ์ ๊ธฐ๋ฐ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ, ๋ฐฐํฌ, ์คํํ ์ ์๋๋ก ์ง์ํ๋ ์คํ ์์ค ํ๋ซํผ์ ๋๋ค. Docker๋ฅผ ํ์ฉํ๋ฉด ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ๋ฐฐํฌ ํ๊ฒฝ ๊ฐ์ ์ฐจ์ด๋ก ์ธํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ผ๋ฉฐ, ๊ฐ์ ๋จธ์ ๋ณด๋ค ๊ฐ๋ณ๊ณ ๋น ๋ฅธ ์ปจํ ์ด๋๋ฅผ ์ด์ฉํด ์ผ๊ด๋ ์คํ ํ๊ฒฝ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
1. ์ปจํ ์ด๋์ Docker์ ํ์์ฑ
์ปจํ ์ด๋๋?
- ์ ์:
์ปจํ ์ด๋๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๊ทธ ์คํ์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ์ค์ ๋ฑ์ ํ๋์ ํจํค์ง๋ก ๋ฌถ์ด ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์์ ์คํํ๋ ๊ธฐ์ ์ ๋๋ค. - ์ ์ฌ์ฉํ ๊น?
- ํ๊ฒฝ ์ผ๊ด์ฑ:
๊ฐ๋ฐํ ๋์ ์๋ฒ์ ๋ฐฐํฌํ ์๋ฒ๋ OS, ํ๊ฒฝ ๋ณ์, ํผ๋ฏธ์ ๋ฑ์ด ๋ค๋ฅผ ์ ์๋๋ฐ, ์ปจํ ์ด๋๋ ์ด๋ฅผ ๋ชจ๋ ์ํํธ์จ์ดํํ์ฌ ๋์ผํ ํ๊ฒฝ์์ ์คํํ ์ ์๊ฒ ํฉ๋๋ค. - ๊ฐ์ ๋จธ์ ๊ณผ์ ์ฐจ์ด:
๊ฐ์ ๋จธ์ ์ ํธ์คํธ OS ์์ OS ์ ์ฒด๋ฅผ ์คํํ๊ธฐ ๋๋ฌธ์ ๋ฌด๊ฒ์ง๋ง, ์ปจํ ์ด๋๋ ์ปค๋์ ๊ณต์ ํ์ฌ ํจ์ฌ ๊ฐ๋ณ๊ณ ๋น ๋ฅธ ํ๊ฒฝ์ ์ ๊ณตํฉ๋๋ค.
- ํ๊ฒฝ ์ผ๊ด์ฑ:
Docker์ ์ญํ
- Docker image:
์ปจํ ์ด๋๋ฅผ ์คํํ ๋ ์ฌ์ฉํ๋ "ํ ํ๋ฆฟ" (Read Only) - Docker container:
์ด๋ฏธ์ง๋ฅผ ์คํํ์ฌ ๋ง๋ค์ด์ง ์ธ์คํด์ค (Write ๊ฐ๋ฅ)
2. Docker ๊ธฐ๋ณธ ๋ช ๋ น์ด ์ ๋ฆฌ
-
์ด๋ฏธ์ง ๋ค์ด๋ก๋ ๋ฐ ๋ชฉ๋ก ํ์ธ
docker pull image_name:tag
โ Docker Hub์์ ํด๋น ์ด๋ฏธ์ง ๋ค์ด๋ก๋docker images
โ ๋ค์ด๋ก๋ํ Docker image ๋ชฉ๋ก ํ์ธ
-
์ปจํ ์ด๋ ์คํ
docker run image_name:tag
โ ํด๋น ์ด๋ฏธ์ง๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ปจํ ์ด๋ ์์ฑ ๋ฐ ์คํ--name
: ์ปจํ ์ด๋ ์ด๋ฆ ์ง์ -e
: ํ๊ฒฝ ๋ณ์ ์ค์ (์:-e MYSQL_ROOT_PASSWORD=1234
)-d
: ๋ฐ๋ชฌ(๋ฐฑ๊ทธ๋ผ์ด๋) ๋ชจ๋๋ก ์คํ-p host_port:container_port
: ํฌํธ ํฌ์๋ฉ (์:-p 8000:8000
)-v host_directory:container_directory
: ๋ณผ๋ฅจ ๋ง์ดํธ๋ก ์ ์ฅ์ ๊ณต์
-
์ปจํ ์ด๋ ์ํ ํ์ธ ๋ฐ ๊ด๋ฆฌ
docker ps
โ ํ์ฌ ์คํ ์ค์ธ ์ปจํ ์ด๋ ๋ชฉ๋ก ํ์ธdocker ps -a
โ ์ค์ง๋ ์ปจํ ์ด๋๊น์ง ๋ชจ๋ ํ์ธdocker exec -it CONTAINER_ID /bin/bash
โ ์คํ ์ค์ธ ์ปจํ ์ด๋ ๋ด๋ถ๋ก ์ง์docker stop CONTAINER_ID
โ ์ปจํ ์ด๋ ์ค์งdocker rm CONTAINER_ID
โ ์ค์ง๋ ์ปจํ ์ด๋ ์ญ์ (์คํ ์ค์ด๋ฉด-f
์ต์ ์ฌ์ฉ)
3. Docker ์ค์น ๋ฐ Docker Hub
Docker ์ค์น
- ์ค์น ๋ฐฉ๋ฒ:
Docker ๊ณต์ ํํ์ด์ง์์ ์์ ์ ์ด์์ฒด์ ์ ๋ง๋ Docker Desktop์ ๋ค์ด๋ก๋ ๋ฐ ์ค์นํฉ๋๋ค.
(์ค์น ์๋ฃ๊น์ง๋ ์๊ฐ์ด ๋ค์ ์์๋ ์ ์์ต๋๋ค.)
Docker Hub
- ๊ฐ๋
:
Docker Hub๋ Docker image๋ฅผ ์ ์ฅ, ๊ณต์ , ๋ฐฐํฌํ ์ ์๋ ํด๋ผ์ฐ๋ ๊ธฐ๋ฐ ๋ ์ง์คํธ๋ฆฌ ์๋น์ค์ ๋๋ค.
ํ์ํ ์ด๋ฏธ์ง๋ฅผ ๊ฒ์ํด์ ๋ฐ์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์์ ์ด ๋ง๋ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ์ฌ ๊ณต์ ํ ์ ์์ต๋๋ค.
4. Docker Image ์์ฑ
Docker ์ด๋ฏธ์ง๋ Dockerfile์ ๊ธฐ๋ฐ์ผ๋ก ๋น๋ํฉ๋๋ค. Dockerfile์๋ ์ด๋ฏธ์ง ๋น๋์ ํ์ํ ๋ชจ๋ ์ค์ ์ ๋ณด๊ฐ ํฌํจ๋ฉ๋๋ค.
Dockerfile ์ฃผ์ ๋ช ๋ น์ด
FROM image_name:tag
โ ๋ฒ ์ด์ค ์ด๋ฏธ์ง ์ง์ (์:python:3.9.13-slim
)COPY host_directory container_directory
โ ํธ์คํธ ํ์ผ/๋๋ ํ ๋ฆฌ๋ฅผ ์ปจํ ์ด๋๋ก ๋ณต์ฌWORKDIR container_directory
โ ์์ ๋๋ ํ ๋ฆฌ ์ค์ (CMD, RUN ๋ฑ์ ๋ช ๋ น์ด ์คํ ๊ฒฝ๋ก)ENV ํ๊ฒฝ๋ณ์=๊ฐ
โ ์ปจํ ์ด๋ ๋ด ํ๊ฒฝ ๋ณ์ ์ค์ RUN ๋ฆฌ๋ ์ค_๋ช ๋ น์ด
โ ์ด๋ฏธ์ง ๋น๋ ์ ์คํํ ๋ช ๋ น์ด (์: ์ํํธ์จ์ด ์ค์น)CMD ["๋ช ๋ น์ด", "์ธ์"]
โ ์ปจํ ์ด๋ ์คํ ์ ๊ธฐ๋ณธ์ผ๋ก ์คํํ ๋ช ๋ น์ด ์ง์
์์ Dockerfile
FROM python:3.9.13-slim
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY . /code
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
- ์ค๋ช
:
- Python 3.9.13-slim ์ด๋ฏธ์ง๋ฅผ ๋ฒ ์ด์ค๋ก ์ฌ์ฉ
/code
๋๋ ํ ๋ฆฌ๋ฅผ ์์ ๊ณต๊ฐ์ผ๋ก ์ค์ requirements.txt
ํ์ผ์ ๋ณต์ฌํ์ฌ ์์กด์ฑ ์ค์น- ์ ์ฒด ์ฝ๋๋ฅผ ๋ณต์ฌ ํ, uvicorn์ ํตํด FastAPI ์ฑ ์คํ
Docker Image ๋น๋ ๋ฐ ์คํ
-
๋น๋:
$ docker build -t APIweb:first .
-
์ด๋ฏธ์ง ํ์ธ:
$ docker images
-
์ปจํ ์ด๋ ์คํ:
$ docker run -p 8000:8000 APIweb:first
โ ๋ธ๋ผ์ฐ์ ์์ http://localhost:8000 ์ ์ํ์ฌ ์น ํ์ด์ง ํ์ธ
5. Docker Image Push
Docker Hub์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋(ํธ์)ํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
-
๋ก๊ทธ์ธ:
$ docker login
-
ํ๊ทธ ๋ณ๊ฒฝ:
$ docker tag image_name:tag_name my_id/image_name:tag_name
-
ํธ์:
$ docker push my_id/image_name:tag_name
6. Docker Image ์ต์ ํ
ML ๋ชจ๋ธ์ด ํฌํจ๋ Docker image๋ ์ข ์ข ์ฉ๋์ด ํฌ๊ธฐ ๋๋ฌธ์ ์ต์ ํ๊ฐ ํ์ํฉ๋๋ค.
์ต์ ํ ๋ฐฉ๋ฒ
- ์์ ๋ฒ ์ด์ค ์ด๋ฏธ์ง ์ฌ์ฉ:
- Python ์ด๋ฏธ์ง ์ค
slim
๋๋alpine
์ ์ฌ์ฉ
(์:FROM python:3.9.13-slim
)
- Python ์ด๋ฏธ์ง ์ค
- Multi-stage build:
- ๋น๋์ ํ์ํ ๋จ๊ณ์ ์ต์ข ์ด๋ฏธ์ง์์ ํ์ํ ๋ถ๋ถ์ ๋ถ๋ฆฌํ์ฌ ๋ถํ์ํ ํ์ผ ์ ๊ฑฐ
- ์ปจํ
์ด๋ ํจํค์ง ์ต์ ํ:
.dockerignore
ํ์ผ๋ก ๋น๋ ์ ํฌํจํ์ง ์์ ํ์ผ ์ง์ - ๋ณ๊ฒฝ ๊ฐ๋ฅ์ฑ์ด ๋ฎ์ ๋ช ๋ น์ด๋ Dockerfile ์๋จ์ ๋ฐฐ์นํด ์บ์ฑ ํ์ฉ
7. Docker Compose ์ฌ์ฉํ๊ธฐ
์ฌ๋ฌ Docker container๋ฅผ ๋์์ ์คํํด์ผ ํ ๋ Docker Compose๋ฅผ ์ฌ์ฉํ๋ฉด ํธ๋ฆฌํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ ์ด๋์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋๋ฅผ ๋์์ ์คํํ ์ ์์ต๋๋ค.
์์ docker-compose.yml ํ์ผ
version: '3'
services:
db:
image: mysql:5.7.12
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: my_database
ports:
- "3306:3306"
app:
build:
context: .
environment:
DB_URL: mysql+mysqldb://root:root@db:3306/my_database?charset=utf8mb4
ports:
- "8000:8000"
depends_on:
- db
restart: always
-
์ค๋ช :
db
์๋น์ค: MySQL ์ปจํ ์ด๋ ์คํ, ํ๊ฒฝ ๋ณ์ ์ค์ ๋ฐ ํฌํธ ๋งคํapp
์๋น์ค: ํ์ฌ ๋๋ ํ ๋ฆฌ์ Dockerfile์ ์ด์ฉํด ๋น๋, DB URL ํ๊ฒฝ ๋ณ์ ์ง์ ,db
์ปจํ ์ด๋์ ์์กด
-
์คํ:
$ docker-compose up
โ ์ฌ๋ฌ ์ปจํ ์ด๋๊ฐ ๋์์ ์คํ๋๋ฉฐ,
app
์ปจํ ์ด๋๋db
๊ฐ ์คํ๋ ์ดํ์ ์์๋จ
๊ฒฐ๋ก
Docker๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ๋ฐฐํฌ ํ๊ฒฝ์ ์ฐจ์ด๋ฅผ ๊ทน๋ณตํ๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ผ๊ด๋ ํ๊ฒฝ์์ ์์ฝ๊ฒ ์คํํ ์ ์์ต๋๋ค.
- ์ปจํ
์ด๋ ๊ธฐ์ :
๊ฐ์ ๋จธ์ ๋ณด๋ค ๊ฐ๋ณ๊ณ ๋น ๋ฅธ ์คํ ํ๊ฒฝ ์ ๊ณต - Docker ๋ช
๋ น์ด:
์ด๋ฏธ์ง ๋ค์ด๋ก๋, ์ปจํ ์ด๋ ์คํ, ๊ด๋ฆฌ ๋ฐ ์ญ์ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ ์ ๊ณต - Dockerfile๊ณผ ์ด๋ฏธ์ง ์ต์ ํ:
๋ฒ ์ด์ค ์ด๋ฏธ์ง ์ ํ, Multi-stage build, .dockerignore ํ์ฉ ๋ฑ์ผ๋ก ์ต์ ํ ๊ฐ๋ฅ - Docker Compose:
๋ณต์์ ์ปจํ ์ด๋๋ฅผ ์ฝ๊ฒ ๊ด๋ฆฌํ๊ณ ์คํํ ์ ์์
ํด๋ผ์ฐ๋ ์ปดํจํ ๊ณผ GCP ํ์ฉ ๊ฐ์ด๋
ํด๋ผ์ฐ๋๋ ์ธํฐ๋ท์ ํตํด IT ์์(์๋ฒ, ์ ์ฅ์, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ๋คํธ์ํน, ์ํํธ์จ์ด ๋ฑ)์ ์ ๊ณตํ๋ ์๋น์ค์ ๋๋ค. AWS, GCP, Azure์ ๊ฐ์ด ๋ค์ํ ํด๋ผ์ฐ๋ ์๋น์ค ์ ๊ณต์ ์ฒด๊ฐ ์์ผ๋ฉฐ, ๊ฐ ์ ์ฒด๋ง๋ค ์๋ฒ, ์๋ฒ๋ฆฌ์ค ์ปดํจํ , ์ค๋ธ์ ํธ ์คํ ๋ฆฌ์ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ ๊ณตํต ๊ฐ๋ ์ ์ ์ฌํ์ง๋ง ์ด๋ฆ์ด๋ ์ธ๋ถ ๊ธฐ๋ฅ์ด ๋ค๋ฆ ๋๋ค.
์ด๋ฒ ํฌ์คํธ์์๋ ํด๋ผ์ฐ๋ ์๋น์ค์ ๊ธฐ๋ณธ ๊ฐ๋ , ํนํ object storage์ database์ ์ฐจ์ด, ๊ทธ๋ฆฌ๊ณ Google Cloud Platform(GCP)์ ๋ํ ๊ธฐ๋ฅ(Compute Engine, Cloud Storage, ๋ฐฉํ๋ฒฝ, Cloud Composer)์ ์ค์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ Python์ ํ์ฉํ ํ์ผ ์ ๋ก๋/๋ค์ด๋ก๋ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
1. ํด๋ผ์ฐ๋ ์๋น์ค์ ๊ธฐ๋ณธ ๊ฐ๋
์ฃผ์ ์๋น์ค ๋น๊ต
์๋น์ค | AWS | GCP | Azure |
---|---|---|---|
Server | Elastic Compute (EC2) | Compute Engine | Virtual Machine |
Serverless | Lambda | Cloud Function | Azure Function |
Stateless container | ECS | Cloud Run | Container Instance |
Object storage | S3 | Cloud Storage | Blob Storage |
Database | Amazon RDS | Cloud SQL | Azure SQL |
Data Warehouse | Redshift | BigQuery | Synapse Analytics |
AI platform | SageMaker | Vertex AI | Azure Machine Learning |
Kubernetes | EKS | GKE | AKS |
- Server (Computing Service): ์ฐ์ฐ(CPU, ๋ฉ๋ชจ๋ฆฌ, GPU ๋ฑ) ์ํ์ ์ํ ์๋ฒ
- Serverless computing: ์ฝ๋๋ฅผ ์ ์ถํ๋ฉด ํด๋ผ์ฐ๋์์ ์๋์ผ๋ก ์๋ฒ๋ฅผ ์คํํ๋ ํํ
- Stateless container: Docker ์ด๋ฏธ์ง๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ณ๋์ ์๋ฒ ์์ด ์ปจํ ์ด๋๋ฅผ ์คํ
- Object storage vs. Database:
- Object storage: ๋น๊ตฌ์กฐ์ ๋ฐ์ดํฐ(ํ์ผ, ๊ฐ์ฒด)๋ฅผ ๋์ฉ๋(TB~PB๊ธ)์ผ๋ก ์ ์ฅ, ์ ์ฒด ํ์ผ ๋จ์ ์ ๊ทผ, ์์ ์ ํ์ผ ์ ์ฒด๋ฅผ ๋ฎ์ด์ (๋ฏธ๋์ด ํ์ผ, ๋ฐฑ์ ๋ฑ)
- Database: ๊ตฌ์กฐ์ ๋ฐ์ดํฐ(ํ, ์ด ๋๋ ํค-๊ฐ ์)๋ฅผ ์ ์ฅํ๋ฉฐ, ํ๋๋ณ๋ก ์ธ๋ฐํ๊ฒ ์ ๊ทผ ๋ฐ ์์ ๊ฐ๋ฅ, ์ค์๊ฐ ํธ๋์ญ์ ๋ฐ ์ฟผ๋ฆฌ ์ต์ ํ (๊ณ ๊ฐ ๋ฐ์ดํฐ, ์ฃผ๋ฌธ ๊ธฐ๋ก ๋ฑ)
2. Google Cloud Platform (GCP) ์ฌ์ฉ๋ฒ
GCP๋ ๋ํ์ ์ธ ํด๋ผ์ฐ๋ ์๋น์ค ์ ๊ณต์
์ฒด๋ก, ๋ค์ํ ์ปดํจํ
๋ฐ ์คํ ๋ฆฌ์ง ์๋น์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
2-1. Compute Engine
- ์ค๋ช
:
GCP์ Compute Engine์ VM(๊ฐ์ ๋จธ์ )์ ์์ฑํ ์ ์๋ ์๋น์ค์ ๋๋ค. - ์ฌ์ฉ๋ฒ:
- GCP์ ๊ฐ์ ํ ํ, ๋ฉ์ธ ๋์๋ณด๋๋ก ์ด๋
- ์ผ์ชฝ ๋ฉ๋ด์์ Compute Engine์ ํด๋ฆญํ๋ฉด ์ด๊ธฐํ ๊ณผ์ ์ด ์งํ๋๊ณ , VM ์ธ์คํด์ค ๋ฉ๋ด๊ฐ ํ์๋จ
- ์ธ์คํด์ค ๋ง๋ค๊ธฐ ๋ฒํผ์ ํด๋ฆญํ์ฌ ์ด๋ฆ ๋ฐ ๋จธ์ ๊ตฌ์ฑ(์: e2-micro, ๋ฌด๋ฃ ์ฌ์ฉ ๊ฐ๋ฅ ๋ฒ์)์ ์ ํ ํ ์๋ฒ ์์ฑ
- ์์ฑ๋ ์๋ฒ์ ์ค๋ฅธ์ชฝ "์ฐ๊ฒฐ" ํญ๋ชฉ์์ SSH๋ฅผ ํด๋ฆญํ๋ฉด ๋ธ๋ผ์ฐ์ ์์ CLI๋ก ์ ์ํ ์ ์์
- ์ฃผ์:
์ฌ์ฉํ์ง ์๋ ์๋ฒ๋ ์ญ์ ํ์ฌ ๋น์ฉ ๋ฐ์์ ๋ฐฉ์งํฉ๋๋ค.
๋ํ, GCP์์๋ ์ด๋ฏธ ๊ตฌ์ถ๋ Docker ์ด๋ฏธ์ง ๊ธฐ๋ฐ์ ์๋ฒ๋ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก, ์ํ๋ ํ๊ฒฝ(์: PyTorch ๋ฑ)์ ๊ฒ์ ํ ์ธ์คํด์ค๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
2-2. Cloud Storage
- ์ค๋ช
:
GCP์ Cloud Storage๋ ๋์ฉ๋ ํ์ผ ๋ฐ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ ์ ์๋ ์๋น์ค์ ๋๋ค. - ๋ฒํท ์์ฑ:
- Cloud Storage ํญ์์ ๋ฒํท ๋ง๋ค๊ธฐ ํด๋ฆญ
- ๋ฒํท ์ด๋ฆ๊ณผ ์คํ ๋ฆฌ์ง ํด๋์ค๋ฅผ ์ง์ ํ๋ฉด ๋ฒํท ์์ฑ
- ์์ฑ๋ ๋ฒํท์ ํด๋ฆญํ์ฌ ํ์ผ ๋ฐ ํด๋ ์ ๋ก๋ ๊ฐ๋ฅ
Python์ ์ด์ฉํ ํ์ผ ์ ๋ก๋/๋ค์ด๋ก๋ ์์
-
Google Cloud Storage ํ์ด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น:
pip install google-cloud-storage
-
์๋น์ค ๊ณ์ ๊ณผ ํค ์์ฑ:
- GCP Cloud Console์ ์๋น์ค ๊ณ์ ๋ง๋ค๊ธฐ ํ์ด์ง์์ ๋์ ํ๋ก์ ํธ ์ ํ
- ์๋น์ค ๊ณ์ ์์ฑ ํ, ์ญํ ์ โ์์ ์โ๋ก ์ค์
- ์๋น์ค ๊ณ์ ์์ธ ์ ๋ณด์์ ํค ์ถ๊ฐ(ํ์: JSON) ํ ๋ค์ด๋ก๋
-
ํ๊ฒฝ ๋ณ์ ์ค์ :
export GOOGLE_APPLICATION_CREDENTIALS="key_path"
-
ํ์ด์ฌ ์ฝ๋ ์์ :
from google.cloud import storage # ์ด๊ธฐํ bucket_name = "bucket1" storage_client = storage.Client() bucket = storage_client.bucket(bucket_name) # ํ์ผ ์ ๋ก๋ upload_file_path = "/your/directory/your_file" blob = bucket.blob("your_file") blob.upload_from_filename(upload_file_path) # ํ์ผ ๋ค์ด๋ก๋ download_destination = "/your/directory/your_file_downloaded" blob.download_to_filename(download_destination)
์์ธํ ๋ด์ฉ์ ๊ตฌ๊ธ ํด๋ผ์ฐ๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
3. ๋ฐฉํ๋ฒฝ ์ค์
GCP์์๋ VM ์ธ์คํด์ค์ ์ ๊ทผํ ์ ์๋๋ก ๋ฐฉํ๋ฒฝ ๊ท์น์ ์ค์ ํ ์ ์์ต๋๋ค.
-
๋ฐฉํ๋ฒฝ ๊ท์น ๋ง๋ค๊ธฐ:
- ์ผ์ชฝ ๋ฉ๋ด์ ๋คํธ์ํน > VPC ๋คํธ์ํฌ > ๋ฐฉํ๋ฒฝ ํด๋ฆญ
- ์๋จ ๋ฐฉํ๋ฒฝ ๊ท์น ๋ง๋ค๊ธฐ ๋ฒํผ ํด๋ฆญ
- ์ด๋ฆ๊ณผ ๋์ ํ๊ทธ ์ค์ , ์์ค ํํฐ๋ฅผ IPv4 ๋ฒ์(0.0.0.0/0)๋ก ์ง์ , ํ๋กํ ์ฝ TCP์ ํฌํธ(์: 8888) ์ค์ ํ ์์ฑ
-
๋ฐฉํ๋ฒฝ ๊ท์น ์ ์ฉ:
- ๊ท์น์ ์ ์ฉํ VM ์ธ์คํด์ค๋ฅผ ์ ํ ํ ์์ ๋ฒํผ ํด๋ฆญ
- ๋ฐฉํ๋ฒฝ ์ค์ ์์ Allow HTTP traffic, Allow HTTPS traffic ์ฒดํฌ
- ๋คํธ์ํฌ ํ๊ทธ์ ์์ฑํ ๋์ ํ๊ทธ๋ฅผ ๋ฑ๋กํ๋ฉด ํด๋น ์๋ฒ์ ๊ท์น์ด ์ ์ฉ๋ฉ๋๋ค.
4. Cloud Composer (GCP์ Airflow)
Cloud Composer๋ GCP์์ ์ ๊ณตํ๋ ๊ด๋ฆฌํ Apache Airflow ์๋น์ค์ ๋๋ค. ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ๊ณผ ์ํฌํ๋ก์ฐ๋ฅผ ์์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
-
Cloud Composer ์์ฑ:
- GCP ์ผ์ชฝ ๋ฉ๋ด์์ Composer ์ ํ
- ์๋จ ๋ง๋ค๊ธฐ > Composer 2 ํด๋ฆญ
- ์ด๋ฆ, ์๋น์ค ๊ณ์ , ์ด๋ฏธ์ง ๋ฒ์ (์์ ์ ์ธ composer-2.5.5-airflow-2.6.3 ๊ถ์ฅ) ์ ํ
- ๊ณ ๊ธ ๊ตฌ์ฑ์์ Airflow ๊ตฌ์ฑ ์ฌ์ ์(์: webserver์
dag_dir_list_interval
์ 30์ด ๋ฑ) ์ ๋ ฅ ํ ์์ฑ
-
Composer ํ๊ฒฝ ํ์ธ:
- ์์ฑ๋ Composer ํ๊ฒฝ ๋ชฉ๋ก์์ Airflow ์น์๋ฒ ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด Airflow UI์ ์ ์
- DAG ํด๋ ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ์ฐ๊ฒฐ๋ Cloud Storage ๋ฒํท์ผ๋ก ์ด๋, ์ฌ๊ธฐ์ DAG ํ์ผ(์:
01-bash-operator.py
)์ ์ ์ฅํ๋ฉด ์๋์ผ๋ก ์คํ๋จ
๊ฒฐ๋ก
ํด๋ผ์ฐ๋ ์๋น์ค๋ IT ์์์ ์์ฝ๊ฒ ์ ๊ณตํ์ฌ ๊ฐ๋ฐ, ๋ฐฐํฌ, ์ด์์ ํจ์จ์ฑ์ ๊ทน๋ํํฉ๋๋ค.
- ์๋น์ค ๋น๊ต: AWS, GCP, Azure๋ ๊ฐ๊ฐ ์๋ฒ, ์๋ฒ๋ฆฌ์ค, ์ค๋ธ์ ํธ ์คํ ๋ฆฌ์ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ ๊ณตํต ๊ฐ๋ ์ ์ ์ฌํ์ง๋ง ์ธ๋ถ ๊ธฐ๋ฅ๊ณผ ์ด๋ฆ์ด ๋ค๋ฆ ๋๋ค.
- GCP ํ์ฉ: Compute Engine, Cloud Storage, ๋ฐฉํ๋ฒฝ, Cloud Composer ๋ฑ์ ํตํด ์ค์ ์๋ฒ ์์ฑ๋ถํฐ ํ์ผ ์ ์ฅ, ๋คํธ์ํฌ ๋ณด์, ์ํฌํ๋ก์ฐ ๊ด๋ฆฌ๊น์ง ๋ค์ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- Python ํ์ฉ: Cloud Storage์์ ์ฐ๋์ ๋น๋กฏํ ๋ค์ํ ์์ ์ Python ์ฝ๋๋ก ์์ฝ๊ฒ ์๋ํํ ์ ์์ต๋๋ค.
MLflow๋ก ๋จธ์ ๋ฌ๋ ๋ผ์ดํ์ฌ์ดํด ๊ด๋ฆฌํ๊ธฐ
AI ์๋น์ค๋ฅผ ๊ฐ๋ฐํ ๋๋ ๋ฐ์ดํฐ ์์งยท๋ผ๋ฒจ๋งยท์ ์ ๋ถํฐ ๋ค์ํ ๋ชจ๋ธ๊ณผ ํ๋ผ๋ฏธํฐ๋ก ์คํ์ ์งํํ๊ณ , ๊ฐ์ฅ ์ฑ๋ฅ์ด ์ข์ ๋ชจ๋ธ์ ๋ฐฐํฌํ๋ ์ผ๋ จ์ ๊ณผ์ ์ด ํ์ํฉ๋๋ค. ์ด ๋ชจ๋ ๊ณผ์ ์ ๋ฉํ ๋ฐ์ดํฐ, ๋ชจ๋ธ ์ํฐํฉํธ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉํ feature๋ ๋ฐ์ดํฐ๋ฅผ ๊ผผ๊ผผํ ๊ธฐ๋กํ๊ณ ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
MLflow๋ ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ์ ์คํ, ๋ฐฐํฌ, ๊ด๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ๋์์ฃผ๋ ํ๋ซํผ์ผ๋ก, ๋ค์๊ณผ ๊ฐ์ ํต์ฌ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
MLflow์ ํต์ฌ ๊ธฐ๋ฅ
-
์คํ ์คํ/๊ด๋ฆฌ/๊ธฐ๋ก:
์คํ(run)๋ง๋ค ์คํ ํ๊ฒฝ, ํ๋ผ๋ฏธํฐ, ๋ฉํธ๋ฆญ, ์ํฐํฉํธ ๋ฑ์ ๊ธฐ๋กํ์ฌ ์ธ์ , ์ด๋ป๊ฒ ๋ชจ๋ธ์ด ๋ง๋ค์ด์ก๋์ง ์ถ์ ํ ์ ์์ต๋๋ค. -
Registry:
์ฌ๋ฌ ์คํ์์ ์์ฑ๋ ๋ชจ๋ธ์ ์ ์ฅํ๊ณ ๋ฒ์ ๊ด๋ฆฌํ ์ ์์ผ๋ฉฐ, ํ์๊ณผ ๊ณต์ ํ๊ฑฐ๋ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. -
Serving:
Registry์ ๋ฑ๋ก๋ ๋ชจ๋ธ์ REST API ์๋ฒ๋ก ๋ฐฐํฌํ์ฌ ์ค์๊ฐ ์์ธก ์๋น์ค๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
1. MLflow ์ค์น ๋ฐ UI ์คํ
์ค์น
MLflow๋ ์๋ ๋ช ๋ น์ด๋ก ์ค์นํ ์ ์์ต๋๋ค.
$ pip install mlflow==2.10.0
MLflow UI ์คํ
๋ค์ ๋ช ๋ น์ด๋ก MLflow ์๋ฒ๋ฅผ ์คํํ ํ ๋ธ๋ผ์ฐ์ ์์ http://localhost:8080์ ์ ์ํ๋ฉด MLflow ์นํ์ด์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
$ mlflow server --host 127.0.0.1 --port 8080
2. Experiment ์์ฑ ๋ฐ ๊ด๋ฆฌ
Experiment๋?
Experiment๋ ํ๋์ ํ๋ก์ ํธ๋ฅผ ์๋ฏธํ๋ฉฐ, ์ฌ๋ฌ run(์คํ)์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด 'hand_bone_segmentation' ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๋ฉด, ํด๋น experiment ๋ด์์ ์ฌ๋ฌ ๋ฒ์ ๋ชจ๋ธ ํ์ต(run) ๊ธฐ๋ก์ด ์ ์ฅ๋ฉ๋๋ค.
Experiment ์์ฑ
์๋ ๋ช ๋ น์ด๋ฅผ ์ด์ฉํด ์๋ก์ด experiment๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
$ mlflow experiments create --experiment-name hand_bone_segmentation
experiment๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋๋ฉด, MLflow๋ mlruns
๋ผ๋ ํด๋์ ๊ด๋ จ ๊ธฐ๋ก์ ์ ์ฅํ๊ณ , ์น UI์์๋ ํด๋น experiment๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ์์ฑ๋ experiment ๋ชฉ๋ก์ ๋ค์ ๋ช
๋ น์ด๋ก ๊ฒ์ํ ์ ์์ต๋๋ค.
$ mlflow experiments search
3. ํ๋ก์ ํธ ๋ฉํ ์ ๋ณด ์ ์ฅ
์คํ ๊ธฐ๋ก ์ธ์๋ ํ๋ก์ ํธ์ ์คํ ํ๊ฒฝ, ์์กด์ฑ ๋ฑ์ ๊ด๋ฆฌํ๊ธฐ ์ํด MLProject ํ์ผ๊ณผ python_env.yaml ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค.
MLProject ํ์ผ ์์
name: project1
python_env: python_env.yaml
entry_points:
main:
parameters:
regularization: {type: float, default: 0.1}
command: "python train.py"
python_env.yaml ํ์ผ ์์
python: "3.9.13"
# ๋น๋ ์ ํ์ํ ์์กด์ฑ
build_dependencies:
- pip
- setuptools
- wheel==0.37.1
# ํ๋ก์ ํธ ์คํ ์ ํ์ํ ์์กด์ฑ
dependencies:
- mlflow==2.10.0
- scikit-learn==1.4.0
- pandas==2.2.0
- numpy==1.26.3
- matplotlib==3.8.2
์ด๋ ๊ฒ ๋ฉํ ์ ๋ณด๋ฅผ ๊ธฐ๋กํด๋๋ฉด, ์คํ ์ฌํ์ฑ๊ณผ ๊ด๋ฆฌ๊ฐ ์ฌ์์ง๋๋ค.
4. Experiment ์คํ (Run)
์ค์ ์คํ์ ์คํํ ๋๋ MLflow๊ฐ ์ ๊ณตํ๋ ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํ์ฌ ์คํํฉ๋๋ค.
$ mlflow run experiment_directory --experiment-name hand_bone_segmentation
experiment_directory
์๋ MLProject ํ์ผ๊ณผtrain.py
๋ฑ์ ์ฝ๋๊ฐ ํฌํจ๋ ๋๋ ํ ๋ฆฌ ๊ฒฝ๋ก๋ฅผ ์ง์ ํฉ๋๋ค.-P regularization=0.01
๊ณผ ๊ฐ์ด-P
์ต์ ์ ์ฌ์ฉํ์ฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌํ ์๋ ์์ต๋๋ค.
์คํ(run)์ด ์๋ฃ๋๋ฉด MLflow UI์ run ๊ธฐ๋ก์ด ์ถ๊ฐ๋๊ณ , ์๋์ ๊ฐ์ ์ ๋ณด๊ฐ ์๋์ผ๋ก ๊ธฐ๋ก๋ฉ๋๋ค.
- source: ์คํํ ํ๋ก์ ํธ์ ์ด๋ฆ
- version: ์คํ ์์ Git hash ๋ฑ
- start & end time: ์คํ ์๊ฐ ๊ธฐ๋ก
- parameters: ์ ๋ ฅํ ํ๋ผ๋ฏธํฐ๋ค
- metrics: ์ฑ๋ฅ ์งํ๋ค
- tags: ์ถ๊ฐ ํ๊ทธ ์ ๋ณด
- artifacts: ์คํ ๊ณผ์ ์์ ์์ฑ๋ ํ์ผ๋ค (์: ๋ชจ๋ธ ํ์ผ, ์ด๋ฏธ์ง ๋ฑ)
5. ๋ก๊น ๋ฐ Autolog
train.py
๋ด์์ ์ง์ ๋ก๊น
ํ ์ ์์ผ๋ฉฐ, MLflow๋ ๋ค์ํ ํ๋ ์์ํฌ์ ๋ํด ์๋ ๋ก๊น
(autolog) ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ ๋ค์ด scikit-learn์ ๊ฒฝ์ฐ:
import mlflow
import mlflow.sklearn
# ๊ฐ๋ณ ํญ๋ชฉ ๋ก๊น
mlflow.log_param("l1_ratio", 0.1)
# autolog ํ์ฑํ
mlflow.sklearn.autolog()
with mlflow.start_run() as run:
model.fit(x, y)
์ด๋ ๊ฒ ํ๋ฉด MLflow๊ฐ ์๋์ผ๋ก ํ๋ผ๋ฏธํฐ, ๋ฉํธ๋ฆญ, ๋ชจ๋ธ ์ํฐํฉํธ๋ฅผ ๊ธฐ๋กํด์ค๋๋ค.
6. ๋ชจ๋ธ ์ํฐํฉํธ ๋ค์ด๋ก๋
ํน์ run์์ ์ ์ฅ๋ ๋ชจ๋ธ ์ํฐํฉํธ๋ฅผ ๋ค์ด๋ก๋ํ๋ ค๋ฉด, ๋ค์ ์ฝ๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
from mlflow import artifacts
def download_model(run_id, model_name="model"):
artifact_uri = f"runs:/{run_id}/{model_name}"
artifacts.download_artifacts(artifact_uri, dst_path=".")
์ด ํจ์์ run id์ ๋ชจ๋ธ ์ด๋ฆ์ ์ ๋ฌํ๋ฉด, ํด๋น ๋ชจ๋ธ ํ์ผ์ ๋ก์ปฌ์ ๋ค์ด๋ก๋ํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
MLflow๋ ๋จธ์ ๋ฌ๋ ์คํ์ ์คํ, ๊ธฐ๋ก, ๊ด๋ฆฌ, ๋ฐฐํฌ๋ฅผ ํตํฉํ์ฌ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ค๋๋ค.
- ์คํ ์คํ ๋ฐ ๊ธฐ๋ก: run ๋จ์๋ก ํ๋ผ๋ฏธํฐ, ๋ฉํธ๋ฆญ, ์ํฐํฉํธ๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ๊ด๋ฆฌ
- ๋ฉํ ๋ฐ์ดํฐ ๊ด๋ฆฌ: MLProject์ python_env.yaml์ ํตํด ์ฌํ ๊ฐ๋ฅํ ํ๊ฒฝ ๊ตฌ์ฑ
- ๋ชจ๋ธ Registry ๋ฐ Serving: ์ ์ฅ๋ ๋ชจ๋ธ์ ์ฝ๊ฒ ๋ฐฐํฌํ๊ณ REST API ์๋ฒ๋ก ์๋น ๊ฐ๋ฅ
๋ชจ๋ธ ํ๊ฐ: Offline๊ณผ Online ํ๊ฐ๋ฅผ ํตํ ์ต์ AI ์๋น์ค ์๋น
AI ์๋น์ค๋ฅผ ๋ฐฐํฌํ ๋ ๊ฐ์ฅ ์ค์ํ ์์ ์ค ํ๋๋ ์ต์ ์ ์ฑ๋ฅ์ ๊ฐ์ถ ๋ชจ๋ธ์ ์๋นํ๋ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ์ํด ๋ชจ๋ธ์ ์ฑ๋ฅ์ ์ง์์ ์ผ๋ก ํ๊ฐํ๊ณ ๊ฐ์ ํด์ผ ํ๋๋ฐ, ํ๊ฐ ํ๊ฒฝ์ ๋ฐ๋ผ ํฌ๊ฒ Offline๊ณผ Online ํ๊ฐ๋ก ๋๋ฉ๋๋ค. ์ด๋ฒ ํฌ์คํธ์์๋ ๋ ํ๊ฐ ๋ฐฉ์์ ๊ฐ๋ , ๋ฐฉ๋ฒ๋ก , ์ฅ๋จ์ ์ ๋ํด ์ดํด๋ณด๊ณ , ์ด๋ป๊ฒ ์ง์์ ์ผ๋ก ๋ชจ๋ธ ์ฑ๋ฅ์ ๊ฐ์ ํ ์ ์๋์ง ์์๋ณด๊ฒ ์ต๋๋ค.
1. Offline ๋ชจ๋ธ ํ๊ฐ
Offline ํ๊ฐ๋ ๋ชจ๋ธ์ ๋ฐฐํฌํ๊ธฐ ์ , ํ์ต ๊ฒฐ๊ณผ๊ฐ ๋ด๋ ์ฑ๋ฅ์ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ ์
(hold-out ๋ฐ์ดํฐ, k-fold, bootstrap ๋ฑ)์ ์ด์ฉํด ์คํ์ ์ผ๋ก ํ์ธํ๋ ๊ณผ์ ์
๋๋ค.
์ฃผ์ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1-1. Hold-out Validation
- ์ค๋ช
:
๋ฐ์ดํฐ๋ฅผ ํ์ต ๋ฐ์ดํฐ์ ๊ฒ์ฆ ๋ฐ์ดํฐ๋ก ๋ถ๋ฆฌํ์ฌ ๋ชจ๋ธ์ ํ๋ จํ๊ณ , ๊ฒ์ฆ ๋ฐ์ดํฐ๋ก ์ฑ๋ฅ์ ํ๊ฐํฉ๋๋ค. - ์ฅ์ :
๊ฐ๋จํ๋ฉฐ ๋น ๋ฅด๊ฒ ํ๊ฐํ ์ ์์
1-2. k-Fold Cross Validation
- ์ค๋ช
:
๋ฐ์ดํฐ๋ฅผ k๊ฐ์ ํด๋๋ก ๋๋์ด, ๊ฐ ํด๋๋ฅผ ํ ๋ฒ์ฉ ๊ฒ์ฆ ๋ฐ์ดํฐ๋ก ์ฌ์ฉํ๊ณ ๋๋จธ์ง๋ก ํ์ตํฉ๋๋ค. - ์ฅ์ :
๋ฐ์ดํฐ ๋ถํ ์ ๋ฐ๋ฅธ ํธํฅ์ ์ค์ด๊ณ , ์ ์ฒด ๋ฐ์ดํฐ์ ๋ํด ๋ชจ๋ธ์ ์ผ๋ฐํ ์ฑ๋ฅ์ ํ๊ฐํ ์ ์์
1-3. Bootstrap Resampling
- ์ค๋ช
:
์๋ณธ ๋ฐ์ดํฐ์์ ์ค๋ณต์ ํ์ฉํ์ฌ ๋๋ค ์ํ์ ์ฌ๋ฌ ๋ฒ ์ถ์ถํ ํ, ๊ฐ ๋ถ๋ถ ์งํฉ์ผ๋ก ๋ชจ๋ธ์ ๋ฐ๋ณต ํ๋ จ ๋ฐ ํ๊ฐํฉ๋๋ค. - ์ฅ์ :
๋ฐ์ดํฐ์ ๋ณ๋์ฑ์ ๋ฐ๋ฅธ ๋ชจ๋ธ์ ์ฑ๋ฅ์ ๋ณด๋ค ๊ฒฌ๊ณ ํ๊ฒ ํ๊ฐํ ์ ์์
Offline ํ๊ฐ๋ ์ฃผ๋ก ๋ฐฐํฌ ์ ๋ชจ๋ธ์ ์ฑ๋ฅ์ ๊ฐ๊ด์ ์ผ๋ก ๊ฒ์ฆํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ, ๋ค์ํ ํ๊ฐ ๋ฐฉ๋ฒ์ ํตํด ๋ชจ๋ธ์ ์ผ๋ฐํ ๋ฅ๋ ฅ์ ํ๋ณดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
2. Online ๋ชจ๋ธ ํ๊ฐ
Online ํ๊ฐ๋ ๋ชจ๋ธ์ ์ค์ ์๋น์ค ํ๊ฒฝ์ ๋ฐฐํฌํ ํ, ์ค์๊ฐ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฑ๋ฅ์ ํ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋๋ค. ์ค์ ์ด์ ํ๊ฒฝ์์์ ์ฑ๋ฅ, ์ฌ์ฉ์ ๋ฐ์, ํธ๋์ญ์ ๋ฑ ๋ค์ํ ์์๋ฅผ ๋ฐ์ํ ์ ์์ต๋๋ค.
2-1. A/B Test
- ์ค๋ช
:
ํ์ฌ ๋ชจ๋ธ๊ณผ ์๋ก์ด ๋ชจ๋ธ(๋๋ ์ฌ๋ฌ ๋ฒ์ )์ ๋์์ ๋ฐฐํฌํ์ฌ ์ฌ์ฉ์ ๊ทธ๋ฃน๋ณ๋ก ๊ฐ๊ฐ ๋ค๋ฅธ ๋ชจ๋ธ์ ๊ฒฝํํ๊ฒ ํ๊ณ , ์ฑ๋ฅ ๋ฐ ์ฌ์ฉ์ ํ๋์ ๋น๊ต ๋ถ์ํฉ๋๋ค. - ์ฅ์ :
- ์ค์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฑ๋ฅ ์ฐจ์ด๋ฅผ ์ง์ ํ์ธํ ์ ์์
- ํต๊ณ์ ๋ถ์์ ํตํด ์ ๋ขฐ์ฑ ์๋ ๊ฒฐ๊ณผ ๋์ถ ๊ฐ๋ฅ
- ์ฌ์ฉ์ ํ๋์ ์ง์ ๊ด์ฐฐํ์ฌ ์ค์ง์ ์ธ ํจ๊ณผ ํ๊ฐ ๊ฐ๋ฅ
- ๋จ์ :
- ์ถฉ๋ถํ ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ ๋ฐ ์๊ฐ์ด ์์๋จ
- ์ฌ์ฉ์ ๊ทธ๋ฃน ๋ถํ ๋ก ์ธํด ๊ธฐ์กด ์ฌ์ฉ์ ๊ฒฝํ์ด ์ผ๋ถ ๋ฐฉํด๋ ์ ์์
2-2. Canary Test
- ์ค๋ช
:
์๋ก์ด ๋ชจ๋ธ์ด๋ ์์คํ ์ ์๊ท๋ชจ ์ฌ์ฉ์ ์ง๋จ(โ์นด๋๋ฆฌ์ ๊ทธ๋ฃนโ)์๊ฒ ๋จผ์ ๋ฐฐํฌํ์ฌ ์์ ์ฑ์ ํ๊ฐํ ํ, ๋ฌธ์ ๊ฐ ์์ผ๋ฉด ์ ์ง์ ์ผ๋ก ๋ฐฐํฌ ๋ฒ์๋ฅผ ํ๋ํฉ๋๋ค. - ์ฅ์ :
- ์ด๊ธฐ ๋จ๊ณ์์ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ์ฌ ์ ๋ฉด์ ์ธ ์๋น์ค ์ฅ์ ๋ฅผ ๋ฐฉ์งํ ์ ์์
- ์๊ท๋ชจ ๊ทธ๋ฃน์ผ๋ก๋ถํฐ ๋น ๋ฅด๊ฒ ํผ๋๋ฐฑ ์์ง ๊ฐ๋ฅ
- ์ ์ง์ ์ธ ์ ํ์ผ๋ก ๋ฆฌ์คํฌ๊ฐ ๋ฎ์
- ๋จ์ :
- ๋ชจ๋ ์ฌ์ฉ์์๊ฒ ๋ฐฐํฌํ๊ธฐ๊น์ง ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์์
- ์ด๊ธฐ ์๊ท๋ชจ ๊ทธ๋ฃน์ด ์ ์ฒด ์ฌ์ฉ์ ์ง๋จ์ ๋ํํ์ง ๋ชปํ ๊ฐ๋ฅ์ฑ์ด ์์(์ํ ๋ฐ์ด์ด์ค)
2-3. Shadow Test
- ์ค๋ช
:
์๋ก์ด ๋ชจ๋ธ์ ์ค์ ์๋น์ค์ ๋ฐ์ํ์ง ์๊ณ , ์ค์๊ฐ ํธ๋ํฝ์ ๋ณต์ฌํ์ฌ ๋ณ๋๋ก ๋ชจ๋ธ ์ฑ๋ฅ์ ํ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋๋ค. ๊ธฐ์กด ๋ชจ๋ธ์ ์ค์ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ , ๋์์ ์๋ก์ด ๋ชจ๋ธ๋ ๋์ผํ ์์ฒญ์ ์ฒ๋ฆฌํ์ง๋ง ๊ทธ ๊ฒฐ๊ณผ๋ ์ฌ์ฉ์์๊ฒ ๋ ธ์ถ๋์ง ์์ต๋๋ค. - ์ฅ์ :
- ์ค์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ํ ์ํฅ์ ๋ฏธ์น์ง ์์
- ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ ์ด์ ํ๊ฒฝ์์ ์ฑ๋ฅ ๊ฒ์ฆ ๊ฐ๋ฅ
- ๋ชจ๋ธ์ ๋ฌธ์ ์ ์ ์ฌ์ ์ ๋ฐ๊ฒฌํ์ฌ ์์ ํ ์ ์์
- ๋จ์ :
- ํธ๋ํฝ ๋ณต์ฌ ๋ฐ ํ๊ฐ๋ฅผ ์ํ ์ถ๊ฐ ๋ฆฌ์์ค๊ฐ ํ์ํจ
- ์ฌ์ฉ์ ๋ฐ์์ ์ง์ ์ ์ผ๋ก ํผ๋๋ฐฑ๋ฐ์ง ๋ชปํจ
3. ์ง์์ ๊ฐ์ ์ ์ค์์ฑ
Offline ํ๊ฐ์ Online ํ๊ฐ๋ฅผ ๋ฐ๋ณตํ๋ฉด์ ๋ชจ๋ธ์ ์ง์์ ์ผ๋ก ๊ฐ์ ํ๋ ๊ฒ์ด ๋งค์ฐ ์ค์ํฉ๋๋ค.
- Offline ํ๊ฐ:
๊ฐ๋ฐ ๋จ๊ณ์์ ์ฌ๋ฌ ์คํ์ ํตํด ๋ชจ๋ธ์ ์ผ๋ฐํ ์ฑ๋ฅ์ ํ๋ณดํ๊ณ , ์ด๊ธฐ ์ฑ๋ฅ ๊ธฐ์ค์ ์ค์ ํฉ๋๋ค. - Online ํ๊ฐ:
์ค์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ํตํด ๋ชจ๋ธ์ ์ฑ๋ฅ์ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ๊ณ , ๋ฐฐํฌ ํ์ ๋ฌธ์ ์ ์ ์ ์ํ๊ฒ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
์ด๋ฌํ ๋ฐ๋ณต์ ํ๊ฐ์ ํผ๋๋ฐฑ ๊ณผ์ ์ ํตํด ์ต์ ์ ๋ชจ๋ธ์ ์๋นํจ์ผ๋ก์จ, AI ์๋น์ค์ ํ์ง๊ณผ ์ฌ์ฉ์ ๋ง์กฑ๋๋ฅผ ๋์ผ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
๋ชจ๋ธ ํ๊ฐ ์ ๋ต์ AI ์๋น์ค์ ์ฑ๊ณต์ ์ธ ๋ฐฐํฌ์ ์ด์์ ํ์์ ์ ๋๋ค.
- Offline ํ๊ฐ๋ ๋ชจ๋ธ์ ๊ธฐ๋ณธ ์ฑ๋ฅ๊ณผ ์ผ๋ฐํ ๋ฅ๋ ฅ์ ๊ฒ์ฆํ๋ ๋ฐ ์ฌ์ฉ๋๊ณ ,
- Online ํ๊ฐ๋ ์ค์ ์๋น์ค ํ๊ฒฝ์์ ๋ชจ๋ธ์ ์ฑ๋ฅ์ ํ์ธํ๋ฉฐ,
A/B test, Canary test, Shadow test์ ๊ฐ์ ๋ค์ํ ๊ธฐ๋ฒ์ ํตํด ์ง์์ ์ธ ๊ฐ์ ๊ณผ ์์ ์ฑ์ ๋๋ชจํฉ๋๋ค.