ํฐ์คํ ๋ฆฌ ๋ทฐ
๐งฉ MLOps ๊ธฐ๋ฐ ํํธ AI ํ์ดํ๋ผ์ธ ๊ตฌ์ถ – MLflow + Docker + Streamlit Monitoring
octo54 2025. 10. 30. 12:26๐งฉ MLOps ๊ธฐ๋ฐ ํํธ AI ํ์ดํ๋ผ์ธ ๊ตฌ์ถ – MLflow + Docker + Streamlit Monitoring
์ง๊ธ๊น์ง ๋ง๋ AI ํํธ ๋ชจ๋ธ์
๋ฐ์ดํฐ ์์ง → ์์ธก(Transformer) → ์์จ ์ด์ฉ(RL) ๊น์ง ์์ฑ๋์ต๋๋ค.
์ด์ ๋จ์ ํ ๋จ๊ณ๋, ์ด ๋ชจ๋ ๋ชจ๋ธ์
**“์ง์์ ์ผ๋ก ํ์ตํ๊ณ , ํ๊ฐํ๊ณ , ๋ฐฐํฌํ๋ ์๋ํ๋ MLOps ์์คํ
”**์ผ๋ก ๋ง๋๋ ๊ฒ์
๋๋ค.
๐ฏ ์ด๋ฒ ๊ธ์ ๋ชฉํ
MLflow๋ฅผ ์ด์ฉํด ๋ชจ๋ธ ์คํ, ์ฑ๋ฅ ๊ฒ์ฆ, ๋ฒ์ ๊ด๋ฆฌ, ์๋ ๋ฐฐํฌ๊น์ง ์ฐ๊ฒฐ
โ๏ธ 1๏ธโฃ ํต์ฌ ๊ตฌ์ฑ๋
๐ PostgreSQL → ๋ฐ์ดํฐ ์ ์ฅ
๐ค MLflow → ๋ชจ๋ธ ํ์ต ๋ฐ ๋ฒ์ ๊ด๋ฆฌ
๐ณ Docker → ๋ชจ๋ธ ์๋น ํ๊ฒฝ ํตํฉ
๐ Streamlit → ์ค์๊ฐ ๋ชจ๋ํฐ๋ง ๋์๋ณด๋
์ด์ ๋จ์ํ “์คํ์ฉ ์ฝ๋”๊ฐ ์๋๋ผ
์ด์ฉ ๊ฐ๋ฅํ AI ํฌ์ ํ๋ซํผ์ผ๋ก ์งํํฉ๋๋ค.
๐งฑ 2๏ธโฃ MLflow ์ค์น ๋ฐ ์๋ฒ ์คํ
pip install mlflow psycopg2-binary
MLflow๋ **๋ชจ๋ธ ์คํ + ๋ฉํธ๋ฆญ + ์ํฐํฉํธ(๋ชจ๋ธํ์ผ)**์ ์ ์ฅํฉ๋๋ค.
mlflow server \
--backend-store-uri postgresql://quant_user:quant_pass@localhost:5432/mlflow \
--default-artifact-root ./mlruns \
--host 0.0.0.0 --port 5001
์ด์ http://localhost:5001 ์์ MLflow UI์ ์ ์ ๊ฐ๋ฅํฉ๋๋ค.
๐งฉ 3๏ธโฃ MLflow ํธ๋ํน ์ฝ๋ ์ถ๊ฐ
Transformer ๋๋ RL ๋ชจ๋ธ ํ์ต ์ฝ๋์ ์๋์ฒ๋ผ ์ถ๊ฐํ๋ฉด
๊ฐ ์คํ์ด MLflow์ ์๋์ผ๋ก ๊ธฐ๋ก๋ฉ๋๋ค.
import mlflow
import mlflow.pytorch
mlflow.set_tracking_uri("http://localhost:5001")
mlflow.set_experiment("quant_ai_experiments")
with mlflow.start_run():
model = FactorTransformer(input_dim=4)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.MSELoss()
for epoch in range(50):
pred = model(X)
loss = criterion(pred, y.mean(dim=1, keepdim=True))
optimizer.zero_grad(); loss.backward(); optimizer.step()
mlflow.log_metric("loss", loss.item())
mlflow.pytorch.log_model(model, "model")
print("โ
๋ชจ๋ธ ์ ์ฅ ์๋ฃ:", mlflow.active_run().info.run_uuid)
๐งฎ 4๏ธโฃ ๋ชจ๋ธ ๋ฒ์ ๊ด๋ฆฌ
MLflow๋ ๊ฐ ํ์ต ์คํ(run)๋ง๋ค ์๋์ผ๋ก ๋ค์์ ์ ์ฅํฉ๋๋ค:
ํญ๋ชฉ ์ค๋ช
| Parameters | ํ์ต ํ์ดํผํ๋ผ๋ฏธํฐ (lr, epoch ๋ฑ) |
| Metrics | ์ฑ๋ฅ ์งํ (loss, R², Sharpe ๋ฑ) |
| Artifacts | ํ์ต๋ ๋ชจ๋ธ ํ์ผ (.pt, .pkl ๋ฑ) |
| Tags | ๋ฒ์ ์ ๋ณด (e.g. model_type=transformer, phase=prod) |
๐ ์ด๋ ๊ฒ ํ๋ฉด ๊ณผ๊ฑฐ ์คํ์ ๋ค์ ๋ถ๋ฌ์ค๊ฑฐ๋,
“์ฑ๊ณตํ ๋ชจ๋ธ๋ง ํ๋ก๋์
์ ๋ฐฐํฌ”ํ ์ ์์ต๋๋ค.
๐ 5๏ธโฃ Docker๋ฅผ ์ด์ฉํ ๋ชจ๋ธ ์๋น
๐ณ Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install mlflow torch flask
CMD ["mlflow", "models", "serve", "-m", "models:/quant_ai/latest", "-h", "0.0.0.0", "-p", "6000"]
๐ ๋ชจ๋ธ ๋ฐฐํฌ
mlflow models serve -m models:/quant_ai/Production -p 6000
API ์๋ํฌ์ธํธ ์์:
curl -X POST http://localhost:6000/invocations \
-H "Content-Type: application/json" \
-d '{"inputs": [[0.02, 0.01, -0.03, 0.04]]}'
→ ์์ธก ๊ฒฐ๊ณผ(JSON)์ผ๋ก ๋ฐํ๋ฉ๋๋ค.
๐งฉ 6๏ธโฃ Streamlit ์ค์๊ฐ ๋ชจ๋ํฐ๋ง
# monitor.py
import streamlit as st
import pandas as pd
import requests
API_URL = "http://localhost:6000/invocations"
st.title("๐ Quant AI Monitoring Dashboard")
inputs = st.text_input("Enter factor vector (comma-separated):", "0.02,0.01,-0.03,0.04")
x = [[float(i) for i in inputs.split(",")]]
if st.button("Predict"):
response = requests.post(API_URL, json={"inputs": x})
st.json(response.json())
# ์ต๊ทผ ํ์ต ๋ก๊ทธ ํ์
logs = pd.read_csv("./mlruns/meta.yaml", sep=":")
st.subheader("Recent Training Metadata")
st.code(logs.head().to_string())
์ด Streamlit ๋์๋ณด๋๋ ์ค์๊ฐ ์ถ๋ก ๊ฒฐ๊ณผ + ๋ชจ๋ธ ๋ฒ์ ์ ๋ณด๋ฅผ ๋์์ ๋ณด์ฌ์ค๋๋ค.
๐ง 7๏ธโฃ MLOps ์ํฌํ๋ก์ฐ ์๋ํ
๋จ๊ณ ๋๊ตฌ ์ค๋ช
| ๋ฐ์ดํฐ ์์ง | Airflow / APScheduler | ์ ๊ธฐ ์์ง |
| ๋ชจ๋ธ ํ์ต | MLflow | ์คํ ๋ฐ ๋ฒ์ ๊ด๋ฆฌ |
| ๊ฒ์ฆ | pytest + MLflow metrics | ์๋ ์ฑ๋ฅ ํ ์คํธ |
| ๋ฐฐํฌ | Docker + MLflow Serve | API ๋ฐฐํฌ |
| ๋ชจ๋ํฐ๋ง | Streamlit + Slack | ์ค์๊ฐ ๊ฐ์ ๋ฐ ์๋ฆผ |
๐งฐ 8๏ธโฃ ์ด์ ์๋ํ (์์ ์ฝ๋)
# ํ๋ฃจ 1ํ ์๋ ํ์ต + MLflow ๊ธฐ๋ก
0 6 * * * python train_model.py
# ๋ฐฐํฌ ์๋ ๊ฐฑ์
0 7 * * * mlflow models serve -m models:/quant_ai/Production -p 6000
cron ์ค์ผ์ค์ ๊ฑธ๋ฉด, ๋งค์ผ ์ ๋ฐ์ดํฐ๋ฅผ ํ์ตํ๊ณ
์ฑ๋ฅ์ด ์ผ์ ๊ธฐ์ค ์ด์์ด๋ฉด ์๋์ผ๋ก “Production” ํ๊ทธ๋ก ์น๊ฒฉ๋ฉ๋๋ค.
๐งฉ 9๏ธโฃ MLflow → Slack ์๋ฆผ ์ฐ๊ฒฐ
def notify_slack(msg):
requests.post(os.getenv("SLACK_WEBHOOK_URL"), json={"text": f"๐ {msg}"})
if loss.item() < 0.002:
mlflow.register_model(f"runs:/{mlflow.active_run().info.run_id}/model", "quant_ai")
notify_slack(f"์๋ก์ด ๋ชจ๋ธ์ด ํ๋ก๋์
์ผ๋ก ๋ฐฐํฌ๋์์ต๋๋ค.")
์ด๋ ๊ฒ ํ๋ฉด ์ฑ๋ฅ์ด ๊ฐ์ ๋ ๋ชจ๋ธ๋ง ์๋์ผ๋ก ๋ฐฐํฌ๋๊ณ ,
Slack์ผ๋ก ๋ฐฐํฌ ์ฑ๊ณต ๋ฉ์์ง๊ฐ ๋ฐ๋ก ์ต๋๋ค.
๐ ์ ๋ฆฌ
๋จ๊ณ ๊ธฐ๋ฅ ๋๊ตฌ
| 1 | ๋ชจ๋ธ ์คํ·๋ฒ์ ๊ด๋ฆฌ | MLflow |
| 2 | ๋ชจ๋ธ ์๋ ๋ฐฐํฌ | Docker + MLflow Serve |
| 3 | ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง | Streamlit |
| 4 | ์๋ ์๋ฆผ | Slack |
| 5 | ์ง์์ ํ์ต | Cron / Airflow |
๐ ๋ค์ ๊ธ ์๊ณ
๋ค์ ํธ์์๋ **“AI ํํธ ์ด์ ์๋ํ ์์ฑ – Airflow DAG์ผ๋ก ์ ์ฒด ํ์ดํ๋ผ์ธ ํตํฉ”**์ ๋ค๋ฃน๋๋ค.
์ฆ, ๋ฐ์ดํฐ ์์ง → ํ์ต → ๊ฒ์ฆ → ๋ฆฌํฌํธ → ๋ฐฐํฌ๊น์ง ์ ๊ณผ์ ์
ํ๋์ DAG(Directed Acyclic Graph) ๋ก ์๋ํํฉ๋๋ค.
MLflow,ํํธAI,๋ชจ๋ธ๋ฒ์ ๊ด๋ฆฌ,ํ์ด์ฌMLOps,์๋๋ฐฐํฌ,Streamlit,PostgreSQL,๋์ปค๋ชจ๋ธ์๋น,๋ฅ๋ฌ๋์ด์ฉ,AIํฌ์
'์ฃผ์' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
- Total
- Today
- Yesterday
- ๊ฐ๋ฐ๋ธ๋ก๊ทธ
- Express
- JWT
- kotlin
- ํ๋ก ํธ์๋๊ฐ๋ฐ
- DevOps
- Docker
- CI/CD
- JAX
- ์น๊ฐ๋ฐ
- Prisma
- PostgreSQL
- flax
- ai์ฒ ํ
- ์ฟ ๋ฒ๋คํฐ์ค
- seo ์ต์ ํ 10๊ฐ
- node.js
- Redis
- ๋ฅ๋ฌ๋
- ๋ฐฑ์๋๊ฐ๋ฐ
- Next.js
- rag
- ์๋ฐ๋ฉด์
- llm
- SEO์ต์ ํ
- nextJS
- fastapi
- NestJS
- Python
- REACT
| ์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
|---|---|---|---|---|---|---|
| 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 |

