Post

๐Ÿš€Understanding MLflow -MLOps์˜ ํ•„์ˆ˜ ๋„๊ตฌ MLflow ์•Œ์•„๋ณด๊ธฐ?!!

๐Ÿš€Understanding MLflow -MLOps์˜ ํ•„์ˆ˜ ๋„๊ตฌ MLflow ์•Œ์•„๋ณด๊ธฐ?!!

๐Ÿง  (ํ•œ๊ตญ์–ด) MLflow ์•Œ์•„๋ณด๊ธฐ?!!

๐Ÿ” ๋จธ์‹ ๋Ÿฌ๋‹ ์‹คํ—˜๋ถ€ํ„ฐ ๋ฐฐํฌ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๋Š” MLOps ํ”Œ๋žซํผ!!!

์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ฐœ๋ฐœ์‹œ, ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๋ณ€ํ™”์— ๋”ฐ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋Š”๊ฒŒ ์ค‘์š”ํ•œ๋ฐ์š”!
๋‹จ์ˆœํžˆ ๋ฉ”๋ชจ์žฅ์„ ์“ฐ๊ฑฐ๋‚˜ ์—‘์…€๋กœ ์ •๋ฆฌํ•˜๋Š”๊ฒƒ์€ ๋„ˆ๋ฌด ์›์‹œ์ ์ด์ฃ !?
์šฐ๋ฆฌ๊ฐ€ ์ฝ”๋“œ๋ฅผ Git์œผ๋กœ ๊ด€๋ฆฌํ•˜๋“ฏ์ด,
MLflow๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ์ง€ํ‘œ๋“ค์„ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์ฃผ์š” ์ฐธ๊ณ ์ž๋ฃŒ:


๐Ÿง  MLflow ๋“ฑ์žฅ์˜ ๋ฐฐ๊ฒฝ

๋จธ์‹ ๋Ÿฌ๋‹ ํ”„๋กœ์ ํŠธ๋Š” ์ฝ”๋“œ๋งŒ์œผ๋กœ๋Š” ๊ด€๋ฆฌ๊ฐ€ ์•ˆ ๋ผ!
๋ฐ์ดํ„ฐ, ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ, ๋ชจ๋ธ, ์„ฑ๋Šฅ ์ง€ํ‘œ๊นŒ์ง€โ€ฆ ๋ชจ๋“  ๊ฑธ ์ถ”์ ํ•ด์•ผ ํ•ด!!

ML ํ”„๋กœ์ ํŠธ์˜ ๋ณต์žก์„ฑ:

  • 1990๋…„๋Œ€: ๋‹จ์ˆœํ•œ ํ†ต๊ณ„ ๋ชจ๋ธ โ†’ ์ˆ˜๋™ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
  • 2000๋…„๋Œ€: ๋ณต์žกํ•œ ML ์•Œ๊ณ ๋ฆฌ์ฆ˜ โ†’ ์‹คํ—˜ ๊ด€๋ฆฌ ํ•„์š”์„ฑ ๋Œ€๋‘
  • 2010๋…„๋Œ€: ๋”ฅ๋Ÿฌ๋‹ ํญ๋ฐœ โ†’ ์ฒด๊ณ„์ ์ธ MLOps ๋„๊ตฌ ํ•„์ˆ˜!

๐Ÿšจ ๊ธฐ์กด ๋ฐฉ๋ฒ•๋“ค์˜ ํ•œ๊ณ„์ 

1๏ธโƒฃ ์ˆ˜๋™ ์‹คํ—˜ ๊ด€๋ฆฌ์˜ ๋ฌธ์ œ ๐Ÿ“Š

์—ฌ๊ธฐ๊ฐ€ ์™„์ „ ๊ณต๊ฐ๋˜์ง€ ์•Š๋‚˜์š”!?ใ…Žใ…Ž

  • ์‹คํ—˜ ์ถ”์  ๋ถˆ๊ฐ€: โ€œ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ด ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”์ง€?โ€
  • ์žฌํ˜„ ๋ถˆ๊ฐ€๋Šฅ: โ€œ์ €๋ฒˆ ์ฃผ ์‹คํ—˜์„ ๋‹ค์‹œ ํ•ด๋ณด์ž!โ€ โ†’ ๋ถˆ๊ฐ€๋Šฅ
  • ๊ฒฐ๊ณผ ๋น„๊ต ์–ด๋ ค์›€: Excel์— ์†์œผ๋กœ ๋ณต๋ถ™ํ•˜๋Š” ๋น„ํšจ์œจ
  • ํ˜‘์—… ์–ด๋ ค์›€: ๊ฐœ์ธ๋ณ„๋กœ ๋‹ค๋ฅธ ์‹คํ—˜ ๊ด€๋ฆฌ ๋ฐฉ์‹

์˜ˆ์‹œ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ์ „ํ†ต์ ์ธ ๋ฐฉ์‹์˜ ๋ฌธ์ œ์  - ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ์ง€์˜ฅ!
def train_model():
    # a, b, c ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ 0~1๊นŒ์ง€ 0.1์”ฉ ํ…Œ์ŠคํŠธ (์ด 11x11x11 = 1331๋ฒˆ!)
    for a in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
        for b in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
            for c in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
                # ๋ชจ๋ธ ํ›ˆ๋ จ
                model = train(param_a=a, param_b=b, param_c=c)
                accuracy = model.evaluate()
                
                # ๊ฒฐ๊ณผ๋ฅผ ํ”„๋ฆฐํŠธ๋งŒ... ๐Ÿ˜ญ
                print(f"a={a}, b={b}, c={c} โ†’ Accuracy: {accuracy}")
    
    # ๋ฌธ์ œ: 1331๊ฐœ์˜ ๊ฒฐ๊ณผ ์ค‘์—์„œ ์–ด๋–ค ๊ฒŒ ์ตœ๊ณ ์˜€๋Š”์ง€ ๊ธฐ์–ต ์•ˆ๋‚จ!
    # ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ: ๋‚ด์ผ ์ปดํ“จํ„ฐ ๊ป๋‹ค ์ผœ๋ฉด ๋ชจ๋“  ๊ฒฐ๊ณผ๊ฐ€ ์‚ฌ๋ผ์ง!

์‹ค์ œ๋กœ ๊ฒช๋Š” ๋ฌธ์ œ๋“ค:

  • ๐Ÿ˜ฑ 1331๊ฐœ์˜ ์‹คํ—˜ ๊ฒฐ๊ณผ๊ฐ€ ํ„ฐ๋ฏธ๋„์— ์ญ‰ ์ถœ๋ ฅ๋จ
  • ๐Ÿ“ โ€œ์–ด? a=0.7, b=0.3, c=0.9์ผ ๋•Œ ์„ฑ๋Šฅ์ด ์ข‹์•˜๋Š”๋ฐโ€ฆ ์–ด๋””๊ฐ”์ง€?โ€
  • ๐Ÿ’ป ์ปดํ“จํ„ฐ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋ชจ๋“  ๊ฒฐ๊ณผ๊ฐ€ ์‚ฌ๋ผ์ง
  • ๐Ÿ“Š ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์„ฑ๋Šฅ์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์Œ

์‚ฌ์‹ค ์ €๋Š”,,
์ฃผํ”ผํ„ฐ ๋…ธํŠธ๋ถ์—์„œ ํ•ด๋‹น ์…€์˜ ์ฝ”๋“œ์™€ ํ”„๋ฆฐํŠธ๋ฅผ ์ €์žฅํ•˜๋ฉฐ ๋ณด๊ฑฐ๋‚˜.,
๋”ฐ๋กœ ๋ฉ”๋ชจ์žฅ์— ์ •๋ฆฌํ•˜๋Š” ๊ตฌ์‹œ๋Œ€์  ๋ฐฉ์‹์„ ์„ฐ์—ˆ์Šต๋‹ˆ๋‹คใ… ใ… 

2๏ธโƒฃ ๋ชจ๋ธ ๋ฒ„์ „ ๊ด€๋ฆฌ์˜ ํ•œ๊ณ„ ๐Ÿ”„

  • ๋ชจ๋ธ ํŒŒ์ผ ํ˜ผ์žฌ: model_v1.pkl, model_final.pkl, model_final_final.pkl
  • ํ™˜๊ฒฝ ๋ถˆ์ผ์น˜: โ€œ๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋˜๋Š”๋ฐ?โ€ ๋ฌธ์ œ
  • ๋ฐฐํฌ ๋ณต์žก์„ฑ: ๋ชจ๋ธ์„ ์–ด๋–ป๊ฒŒ ์„œ๋น™ํ• ์ง€ ๋งค๋ฒˆ ๊ณ ๋ฏผ

์˜ˆ์‹œ:

1
2
3
4
5
6
7
8
# ํ˜ผ๋ž€์Šค๋Ÿฌ์šด ๋ชจ๋ธ ํŒŒ์ผ๋“ค,, ์ œ ์ด์•ผ๊ธฐ์ž…๋‹ˆ๋‹คใ… ใ…   
ls models/
model_v1.pkl
model_v2.pkl  
model_best.pkl
model_final.pkl
model_really_final.pkl
model_v3_actually_final.pkl  # ๐Ÿ˜…

3๏ธโƒฃ ๋ฐฐํฌ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง์˜ ์–ด๋ ค์›€ ๐Ÿš€

  • ๋ฐฐํฌ ์ผ๊ด€์„ฑ: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์˜ ์ฐจ์ด
  • ๋ชจ๋ธ ์„ฑ๋Šฅ ์ถ”์ : ๋ฐฐํฌ ํ›„ ์„ฑ๋Šฅ ์ €ํ•˜ ๊ฐ์ง€ ์–ด๋ ค์›€
  • ๋กค๋ฐฑ ๋ณต์žก์„ฑ: ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ด์ „ ๋ชจ๋ธ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ ์–ด๋ ค์›€

๐Ÿ’ก ํ•ด๊ฒฐ์ฑ…: MLflow๊ฐ€ ์‹คํ—˜๋ถ€ํ„ฐ ๋ฐฐํฌ๊นŒ์ง€ ๋ชจ๋“  ๊ณผ์ •์„ ํ†ตํ•ฉ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค!


๐Ÿ”ง ๊ฐ„๋‹จํ•˜๊ฒŒ MLflow ์„ค์น˜ํ•˜๊ธฐ!!

์ด์ œ MLflow๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์žฅ์ ์„ ์ง์ ‘ ๊ฒฝํ—˜ํ•ด๋ด์š”!

MLflow๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ, Docker๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ณ  ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค!

๐Ÿณ Docker๋กœ MLflow ์„œ๋ฒ„ ๊ตฌ์ถ•ํ•˜๊ธฐ

1๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ ์ €์žฅ์šฉ ํด๋” ์ƒ์„ฑ

1
2
3
# MLflow ๋ฐ์ดํ„ฐ ์ €์žฅ์šฉ ํด๋” ์ƒ์„ฑ (์‹คํ—˜ ๊ฒฐ๊ณผ๊ฐ€ ์ปดํ“จํ„ฐ ์žฌ์‹œ์ž‘ ํ›„์—๋„ ๋ณด์กด๋จ)
mkdir -p ~/mlflow/artifacts  # ๋ชจ๋ธ๊ณผ ํŒŒ์ผ๋“ค ์ €์žฅ
mkdir -p ~/mlflow/db         # ์‹คํ—˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ

2๋‹จ๊ณ„: MLflow ์„œ๋ฒ„ Docker ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰

์ฐธ๊ณ  : https://yijoon009.tistory.com/entry/

  • ์šฐ์„  Dockerfile์„ ๋งŒ๋“ค์–ด์ฃผ๊ตฌ!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Docker-mlflow-OSError-Errno-30-Read-only-file-system-mlflow
FROM continuumio/miniconda3

# MLflow ์„ค์น˜
RUN pip install mlflow

# ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ์„ค์ •
RUN mkdir -p /mlflow/db

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
ENV MLFLOW_TRACKING_URI=http://0.0.0.0:5001
ENV MLFLOW_BACKEND_STORE_URI=sqlite:///mlflow/db/mlflow.db

# ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ์‹œ MLflow ์„œ๋ฒ„ ์‹คํ–‰
ENTRYPOINT ["mlflow", "server"]
CMD ["--host", "0.0.0.0", "--port", "5001", "--backend-store-uri", "sqlite:///mlflow/db/mlflow.db"]
  • ๋นŒ๋“œํ•˜๊ตฌ!!
    1
    
    docker build --network=host -t my-mlflow-image .
    
1
2
3
4
5
# MLflow ์„œ๋ฒ„ ์‹คํ–‰ (ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๋ฉด ๋จ!)
 docker run -d -p 5001:5001 --name mlflow \
            -v $(pwd)/mlflow/db:/mlflow/db \
            -v $(pwd)/mlartifacts:/mlartifacts \
            my-mlflow-image

3๋‹จ๊ณ„: ์›น UI ์ ‘์† ํ™•์ธ -๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ค์Œ ์ฃผ์†Œ๋กœ ์ ‘์† : http://localhost:5001

  • MLflow ๋Œ€์‹œ๋ณด๋“œ๊ฐ€ ๋ณด์ด๋ฉด ์„ฑ๊ณต! ๐ŸŽ‰

๐Ÿ Python์—์„œ MLflow ์‚ฌ์šฉํ•˜๊ธฐ

1๋‹จ๊ณ„: MLflow ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

1
pip install mlflow

2๋‹จ๊ณ„: Python ์ฝ”๋“œ์—์„œ MLflow ์„œ๋ฒ„ ์—ฐ๊ฒฐ

1
2
3
4
5
6
7
8
9
10
11
12
import mlflow
import numpy as np

# MLflow ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ค์ •
mlflow.set_tracking_uri("http://localhost:5001")  # Docker ์„œ๋ฒ„ ์ฃผ์†Œ

# ์‹คํ—˜ ์ƒ์„ฑ (๋ณธ์ธ๋งŒ์˜ ์‹คํ—˜๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”!)
mlflow.set_experiment("my_first_mlflow_experiment")

# ์ด์ œ MLflow ์‚ฌ์šฉ ์ค€๋น„ ์™„๋ฃŒ! โœจ
print("๐ŸŽ‰ MLflow ์—ฐ๊ฒฐ ์„ฑ๊ณต!")
print("๐ŸŒ ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:5001 ์ ‘์†ํ•˜์—ฌ ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”!")

๐ŸŽฏ ์ด์ œ ์ค€๋น„ ๋! ์•„๋ž˜ ์˜ˆ์‹œ๋“ค์„ ํ•จ๊ป˜ ์‹คํ–‰ํ•ด๋ณด์„ธ์š”!


๐Ÿ”ง MLflow์˜ 4๊ฐ€์ง€ ์žฅ์  ์ง์ ‘ ์‹ค์Šตํ•ด๋ณด๊ธฐ!


๐Ÿ—๏ธ 1. MLflow Tracking ๐Ÿ“Š

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ๋ชจ๋“  ์‹คํ—˜์„ ์ž๋™์œผ๋กœ ์ถ”์ ํ•˜๊ณ  ๋น„๊ต ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ €์žฅ

์ฃผ์š” ๊ธฐ๋Šฅ:

  • ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ์ž๋™ ๋กœ๊น…
  • ์„ฑ๋Šฅ ์ง€ํ‘œ ์ถ”์ 
  • ์•„ํ‹ฐํŒฉํŠธ(๋ชจ๋ธ, ํ”Œ๋กฏ) ์ €์žฅ
  • ์‹คํ—˜ ๊ฐ„ ๋น„๊ต ๋ฐ ์‹œ๊ฐํ™”

๊ฐ„๋‹จํ•œ ์‹ค์Šต:

  • a, b, c๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•ด๊ฐ€๋ฉฐ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋กœ๊น…!!
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import mlflow
import numpy as np
import pickle
import os

# ์‹คํ—˜ ์‹œ์ž‘
mlflow.set_tracking_uri("http://0.0.0.0:5001")  # Docker ์„œ๋ฒ„ ์ฃผ์†Œ
mlflow.set_experiment("param_tuning_experiment")

class SimpleModel:
    """๊ฐ„๋‹จํ•œ ๋ชจ๋ธ ํด๋ž˜์Šค - ์‹ค์ œ๋กœ ์ €์žฅ/๋กœ๋“œ ๊ฐ€๋Šฅ"""
    def __init__(self, param_a, param_b, param_c):
        self.param_a = param_a
        self.param_b = param_b
        self.param_c = param_c
        self.trained = False
    
    def train(self):
        """๋ชจ๋ธ ํ›ˆ๋ จ (์‹ค์ œ๋กœ๋Š” ๋ณต์žกํ•œ ๊ณผ์ •)"""
        self.result = self.param_a + self.param_b + self.param_c
        self.trained = True
        return self.result
    
    def predict(self, x):
        """์˜ˆ์ธก ํ•จ์ˆ˜"""
        if not self.trained:
            raise Exception("๋ชจ๋ธ์ด ํ›ˆ๋ จ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!")
        return self.result * x  # ๊ฐ„๋‹จํ•œ ์˜ˆ์ธก ๋กœ์ง

# ๋ช‡ ๊ฐœ ์กฐํ•ฉ๋งŒ ํ…Œ์ŠคํŠธ (์ „์ฒด 1331๊ฐœ๋Š” ๋„ˆ๋ฌด ๋งŽ์œผ๋‹ˆ ์ผ๋ถ€๋งŒ!)
test_combinations = [
    (0.1, 0.2, 0.3), (0.5, 0.5, 0.5), (0.8, 0.9, 1.0),
    (1.0, 1.0, 1.0), (0.0, 0.0, 0.0), (0.3, 0.7, 0.4)
]

for a, b, c in test_combinations:
    with mlflow.start_run():
        # ํŒŒ๋ผ๋ฏธํ„ฐ ๋กœ๊น…
        mlflow.log_param("param_a", a)
        mlflow.log_param("param_b", b) 
        mlflow.log_param("param_c", c)
        
        # ๋ชจ๋ธ ์ƒ์„ฑ ๋ฐ ํ›ˆ๋ จ
        model = SimpleModel(param_a=a, param_b=b, param_c=c)
        result = model.train()
        
        # ์„ฑ๋Šฅ ์ง€ํ‘œ ๊ณ„์‚ฐ
        accuracy = result / 3.0  # ์ตœ๋Œ€๊ฐ’ 3.0์œผ๋กœ ๋‚˜๋ˆ„์–ด ์ •๊ทœํ™”
        loss = 3.0 - result      # ์†์‹ค์€ ๋ฐ˜๋Œ€๋กœ
        
        # ์„ฑ๋Šฅ ์ง€ํ‘œ ๋กœ๊น…
        mlflow.log_metric("accuracy", accuracy)
        mlflow.log_metric("loss", loss)
        mlflow.log_metric("sum_result", result)
        
        # ๋ชจ๋ธ ์ €์žฅ (์ค‘์š”!)
        model_path = f"simple_model_{a}_{b}_{c}.pkl"
        with open(model_path, 'wb') as f:
            pickle.dump(model, f)
        
        # MLflow์— ๋ชจ๋ธ ์•„ํ‹ฐํŒฉํŠธ๋กœ ์ €์žฅ
        mlflow.log_artifact(model_path)
        
        # ์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ
        os.remove(model_path)
        
        # ์ถ”๊ฐ€ ์ •๋ณด ๋กœ๊น…
        mlflow.set_tag("experiment_type", "parameter_sum")
        mlflow.set_tag("model_type", "SimpleModel")
        
        print(f"โœ… a={a}, b={b}, c={c} โ†’ ํ•ฉ๊ณ„: {result:.1f}, ์ •ํ™•๋„: {accuracy:.3f}")

print("๐ŸŽ‰ 6๊ฐœ์˜ ์‹คํ—˜์ด ๋ชจ๋‘ MLflow์— ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
print("๐ŸŒ ๋ธŒ๋ผ์šฐ์ €์—์„œ http://0.0.0.0:5001 ์œผ๋กœ ์ ‘์†ํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”!")

MLflow์˜ ๋งˆ๋ฒ•! โœจ

  • ๐ŸŽฏ 1331๊ฐœ์˜ ์‹คํ—˜์ด ๋ชจ๋‘ ์ž๋™์œผ๋กœ ์ €์žฅ๋จ
  • ๐Ÿ” ์›น UI์—์„œ ํด๋ฆญ ๋ช‡ ๋ฒˆ์œผ๋กœ ์ตœ๊ณ  ์„ฑ๋Šฅ ์กฐํ•ฉ ์ฐพ๊ธฐ (๋‹น์—ฐํžˆ a=1.0, b=1.0, c=1.0!)
  • ๐Ÿ“Š a, b, c ์ค‘ ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์„ฑ๋Šฅ์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ๊ทธ๋ž˜ํ”„๋กœ ํ™•์ธ (๋ชจ๋‘ ๋™์ผํ•˜๊ฒŒ ๊ธฐ์—ฌ)
  • ๐Ÿ’พ ์ปดํ“จํ„ฐ ๊ป๋‹ค ์ผœ๋„ ๋ชจ๋“  ์‹คํ—˜ ๊ฒฐ๊ณผ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ณด์กด
  • ๐Ÿค ํŒ€์›๊ณผ ์‹คํ—˜ ๊ฒฐ๊ณผ ์‰ฝ๊ฒŒ ๊ณต์œ 
  • ๐ŸŽจ ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜ ์˜ˆ์‹œ์ง€๋งŒ ์‹ค์ œ ๋ณต์žกํ•œ ML ๋ชจ๋ธ์—๋„ ๋™์ผํ•˜๊ฒŒ ์ ์šฉ ๊ฐ€๋Šฅ

์•„๋ž˜์™€ ๊ฐ™์ด MLflowํ™”๋ฉด์—์„œ ํ™•์ธ์ด๊ฐ€๋Šฅํ•ด์š”! mlflow1_1

๊ฐ๊ฐ์˜ ๋ณ€์ˆ˜๋ณ„ ๊ฒฐ๊ณผ๊ฐ€ ์–ด๋• ๋Š”์ง€๋„ ๋ณผ์ˆ˜์žˆ์ฃ !! mlflow1_2


๐Ÿ—๏ธ 2. MLflow Models ๐Ÿค–

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ์ €์žฅ๋œ ๋ชจ๋ธ์„ ์‰ฝ๊ฒŒ ๋ถˆ๋Ÿฌ์™€์„œ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ

๊ฐ„๋‹จํ•œ ์‹ค์Šต: ์œ„์—์„œ ์ €์žฅํ•œ ๋ชจ๋ธ์„ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•˜๊ธฐ

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import mlflow
import pickle
import os

class SimpleModel:
    """๊ฐ„๋‹จํ•œ ๋ชจ๋ธ ํด๋ž˜์Šค - ์‹ค์ œ๋กœ ์ €์žฅ/๋กœ๋“œ ๊ฐ€๋Šฅ"""
    def __init__(self, param_a, param_b, param_c):
        self.param_a = param_a
        self.param_b = param_b
        self.param_c = param_c
        self.trained = False
    
    def train(self):
        """๋ชจ๋ธ ํ›ˆ๋ จ (์‹ค์ œ๋กœ๋Š” ๋ณต์žกํ•œ ๊ณผ์ •)"""
        self.result = self.param_a + self.param_b + self.param_c
        self.trained = True
        return self.result
    
    def predict(self, x):
        """์˜ˆ์ธก ํ•จ์ˆ˜"""
        if not self.trained:
            raise Exception("๋ชจ๋ธ์ด ํ›ˆ๋ จ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!")
        return self.result * x  # ๊ฐ„๋‹จํ•œ ์˜ˆ์ธก ๋กœ์ง
    
# ์‹คํ—˜ ์‹œ์ž‘
mlflow.set_tracking_uri("http://0.0.0.0:5001")  # Docker ์„œ๋ฒ„ ์ฃผ์†Œ
mlflow.set_experiment("param_tuning_experiment")

# 1. ์ €์žฅ๋œ ์‹คํ—˜ ๋ชฉ๋ก ํ™•์ธ
experiment = mlflow.get_experiment_by_name("param_tuning_experiment")
runs = mlflow.search_runs(experiment_ids=[experiment.experiment_id])
print("์ €์žฅ๋œ ์‹คํ—˜ ๋ชฉ๋ก:")
for idx, run in runs.iterrows():
    print(f"Run ID: {run['run_id'][:8]}... โ†’ a={run['params.param_a']}, b={run['params.param_b']}, c={run['params.param_c']}")

# 2. ๊ฐ€์žฅ ์„ฑ๋Šฅ์ด ์ข‹์€ ๋ชจ๋ธ ์ฐพ๊ธฐ
best_run = runs.loc[runs['metrics.accuracy'].idxmax()]
best_run_id = best_run['run_id']
print(f"\n๐Ÿ† ์ตœ๊ณ  ์„ฑ๋Šฅ ๋ชจ๋ธ: Run ID {best_run_id[:8]}...")
print(f"   ํŒŒ๋ผ๋ฏธํ„ฐ: a={best_run['params.param_a']}, b={best_run['params.param_b']}, c={best_run['params.param_c']}")
print(f"   ์ •ํ™•๋„: {best_run['metrics.accuracy']:.3f}")

# 3. ๋ชจ๋ธ ์•„ํ‹ฐํŒฉํŠธ ๋‹ค์šด๋กœ๋“œ ๋ฐ ๋กœ๋“œ
client = mlflow.tracking.MlflowClient()
artifacts = client.list_artifacts(best_run_id)
print(f"\n๐Ÿ“ฆ ์ €์žฅ๋œ ์•„ํ‹ฐํŒฉํŠธ: {[art.path for art in artifacts]}")

# ๋ชจ๋ธ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ
model_artifact = [art for art in artifacts if art.path.endswith('.pkl')][0]
local_path = client.download_artifacts(best_run_id, model_artifact.path)
print(f"๐Ÿ“ฅ ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ: {local_path}")

# ๋ชจ๋ธ ๋กœ๋“œ
with open(local_path, 'rb') as f:
    loaded_model = pickle.load(f)

print(f"โœ… ๋ชจ๋ธ ๋กœ๋“œ ์„ฑ๊ณต!")
print(f"   ๋ชจ๋ธ ํŒŒ๋ผ๋ฏธํ„ฐ: a={loaded_model.param_a}, b={loaded_model.param_b}, c={loaded_model.param_c}")
print(f"   ๋ชจ๋ธ ๊ฒฐ๊ณผ: {loaded_model.result}")

# 4. ๋กœ๋“œ๋œ ๋ชจ๋ธ๋กœ ์˜ˆ์ธก
test_inputs = [1.0, 2.0, 0.5]
for test_input in test_inputs:
    prediction = loaded_model.predict(test_input)
    print(f"   ์ž…๋ ฅ {test_input} โ†’ ์˜ˆ์ธก: {prediction}")

print("\n๐ŸŽ‰ ๋ชจ๋ธ ์ €์žฅ/๋กœ๋“œ๊ฐ€ ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!")

์•„์ฃผ ์ž˜ ์ž‘๋™๋ฉ๋‹ˆ๋‹ค!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
์ €์žฅ๋œ ์‹คํ—˜ ๋ชฉ๋ก:
Run ID: 56d54172... โ†’ a=0.3, b=0.7, c=0.4
Run ID: 9b3b6a75... โ†’ a=0.0, b=0.0, c=0.0
Run ID: 682f736c... โ†’ a=1.0, b=1.0, c=1.0
Run ID: 934b4956... โ†’ a=0.8, b=0.9, c=1.0
Run ID: 48a5f8d4... โ†’ a=0.5, b=0.5, c=0.5
Run ID: 9b3822ea... โ†’ a=0.1, b=0.2, c=0.3

๐Ÿ† ์ตœ๊ณ  ์„ฑ๋Šฅ ๋ชจ๋ธ: Run ID 682f736c...
   ํŒŒ๋ผ๋ฏธํ„ฐ: a=1.0, b=1.0, c=1.0
   ์ •ํ™•๋„: 1.000

๐Ÿ“ฆ ์ €์žฅ๋œ ์•„ํ‹ฐํŒฉํŠธ: ['simple_model_1.0_1.0_1.0.pkl']
Downloadingโ€‡artifacts:โ€‡100%
โ€‡1/1โ€‡[00:00<00:00,โ€‡21.29it/s]
๐Ÿ“ฅ ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ: /tmp/tmpf9lqcqc5/simple_model_1.0_1.0_1.0.pkl
โœ… ๋ชจ๋ธ ๋กœ๋“œ ์„ฑ๊ณต!
   ๋ชจ๋ธ ํŒŒ๋ผ๋ฏธํ„ฐ: a=1.0, b=1.0, c=1.0
   ๋ชจ๋ธ ๊ฒฐ๊ณผ: 3.0
   ์ž…๋ ฅ 1.0 โ†’ ์˜ˆ์ธก: 3.0
   ์ž…๋ ฅ 2.0 โ†’ ์˜ˆ์ธก: 6.0
   ์ž…๋ ฅ 0.5 โ†’ ์˜ˆ์ธก: 1.5

๐ŸŽ‰ ๋ชจ๋ธ ์ €์žฅ/๋กœ๋“œ๊ฐ€ ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!

๐Ÿ—๏ธ 3. MLflow Model Registry ๐Ÿ—‚๏ธ

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ๋ชจ๋ธ ๋ฒ„์ „ ๊ด€๋ฆฌํ•˜๊ธฐ (๊ฐœ๋ฐœ โ†’ ํ…Œ์ŠคํŠธ โ†’ ํ”„๋กœ๋•์…˜)

์‹ค์ œ ํšŒ์‚ฌ์—์„œ๋Š”:

  • None: ๋ฐฉ๊ธˆ ๊ฐœ๋ฐœํ•œ ๋ชจ๋ธ
  • Staging: ํ…Œ์ŠคํŠธ ์ค‘์ธ ๋ชจ๋ธ
  • Production: ์‹ค์ œ ์„œ๋น„์Šค์— ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋ธ
  • Archived: ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ชจ๋ธ

๊ฐ„๋‹จํ•œ ์‹ค์Šต: ๋ชจ๋ธ์„ Registry์— ๋“ฑ๋กํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import mlflow
import mlflow.pyfunc
import os

# MLflow ์—ฐ๊ฒฐ ์„ค์ •
mlflow.set_tracking_uri("http://0.0.0.0:5001")
mlflow.set_experiment("param_tuning_experiment_model_save")

# ์›๋ž˜ ๋ชจ๋ธ ํด๋ž˜์Šค
class SimpleModel:
    def __init__(self, param_a, param_b, param_c):
        self.param_a = param_a
        self.param_b = param_b
        self.param_c = param_c
        self.trained = False

    def train(self):
        self.result = self.param_a + self.param_b + self.param_c
        self.trained = True
        return self.result

    def predict(self, x):
        if not self.trained:
            raise Exception("๋ชจ๋ธ์ด ํ›ˆ๋ จ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!")
        return self.result * x

# MLflow์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ž˜ํ•‘ ํด๋ž˜์Šค ์ •์˜
class WrappedSimpleModel(mlflow.pyfunc.PythonModel):
    def __init__(self, model):
        self.model = model

    def predict(self, context, model_input):
        return self.model.predict(model_input)

# Run ์‹œ์ž‘
with mlflow.start_run() as run:
    model = SimpleModel(1.0, 1.0, 1.0)
    result = model.train()

    mlflow.log_param("param_a", 1.0)
    mlflow.log_param("param_b", 1.0)
    mlflow.log_param("param_c", 1.0)
    mlflow.log_metric("accuracy", result / 3.0)

    # pyfunc ๋ชจ๋ธ๋กœ ์ €์žฅ
    artifact_path = "model"
    wrapped_model = WrappedSimpleModel(model)

    mlflow.pyfunc.log_model(
        artifact_path=artifact_path,
        python_model=wrapped_model
    )

    model_name = "MyBestModel_save"
    model_uri = f"runs:/{run.info.run_id}/{artifact_path}"

    # Model Registry์— ๋“ฑ๋ก
    registered_model = mlflow.register_model(
        model_uri=model_uri,
        name=model_name,
        tags={"team": "data_science", "version": "v1.0"}
    )

    print(f"โœ… ๋ชจ๋ธ '{model_name}' ๋“ฑ๋ก ์™„๋ฃŒ!")
    print(f"   ๋ฒ„์ „: {registered_model.version}")
    print(f"   Run ID: {run.info.run_id}")

# ๋ชจ๋ธ ๋ฒ„์ „ ๊ด€๋ฆฌ
from mlflow.tracking import MlflowClient
client = MlflowClient()

client.transition_model_version_stage(
    name=model_name,
    version=registered_model.version,
    stage="Staging"
)
print("๐ŸŽฏ ๋ชจ๋ธ์„ Staging์œผ๋กœ ์Šน๊ฒฉ!")

client.transition_model_version_stage(
    name=model_name,
    version=registered_model.version,
    stage="Production"
)
print("๐Ÿš€ ๋ชจ๋ธ์„ Production์œผ๋กœ ์Šน๊ฒฉ!")

# Production ๋ชจ๋ธ ์‚ฌ์šฉ
production_model_uri = f"models:/{model_name}/Production"
print(f"๐Ÿ“ฆ Production ๋ชจ๋ธ URI: {production_model_uri}")
print("โœ… ์ด์ œ ๋‹ค๋ฅธ ํŒ€์—์„œ๋„ ์ด ๋ชจ๋ธ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!")

๊ฒฐ๊ณผ ํ™•์ธ:

1
2
3
4
5
6
7
8
9
โœ… ๋ชจ๋ธ 'MyBestModel_save' ๋“ฑ๋ก ์™„๋ฃŒ!
   ๋ฒ„์ „: 1
   Run ID: 21070f737db74159933745c7b041cf44
๐Ÿƒ View run tasteful-elk-947 at: http://0.0.0.0:5001/#/experiments/5/runs/21070f737db74159933745c7b041cf44
๐Ÿงช View experiment at: http://0.0.0.0:5001/#/experiments/5
๐ŸŽฏ ๋ชจ๋ธ์„ Staging์œผ๋กœ ์Šน๊ฒฉ!
๐Ÿš€ ๋ชจ๋ธ์„ Production์œผ๋กœ ์Šน๊ฒฉ!
๐Ÿ“ฆ Production ๋ชจ๋ธ URI: models:/MyBestModel_save/Production
โœ… ์ด์ œ ๋‹ค๋ฅธ ํŒ€์—์„œ๋„ ์ด ๋ชจ๋ธ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

๊ทธ๋Ÿผ ์•„๋ž˜๊ฐ™์ด ๋ชจ๋ธ์ด ๋‚˜์™€์š”!! mlflow_model


๐Ÿ—๏ธ 4. MLflow Projects ๐Ÿ“ฆ

์•„๋ž˜ ๋ถ€๋ถ„์€.. ๊ทธ๋ƒฅ gpt๊ฐ€ ์จ์ค€๋ถ€๋ถ„์ด๊ณ  ์‹คํ–‰์•ˆํ•ด๋ดค์–ด์œ ~~

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ์‹คํ—˜ ํ™˜๊ฒฝ์„ ๊ทธ๋Œ€๋กœ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํŒจํ‚ค์ง•

์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ:

  • ๋™๋ฃŒ๊ฐ€ ๋‚ด ์‹คํ—˜์„ ๊ทธ๋Œ€๋กœ ์žฌํ˜„ํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • ๋‹ค๋ฅธ ์ปดํ“จํ„ฐ์—์„œ ๋˜‘๊ฐ™์€ ํ™˜๊ฒฝ์œผ๋กœ ์‹คํ—˜ํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ๋™์ผํ•˜๊ฒŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ

๊ฐ„๋‹จํ•œ ์‹ค์Šต: MLflow ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

1๋‹จ๊ณ„: ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ๋งŒ๋“ค๊ธฐ

1
2
mkdir my_mlflow_project
cd my_mlflow_project

2๋‹จ๊ณ„: MLproject ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

1
2
3
4
5
6
7
8
9
10
11
12
# MLproject ํŒŒ์ผ
name: simple_param_tuning

conda_env: conda.yaml

entry_points:
  main:
    parameters:
      param_a: {type: float, default: 0.5}
      param_b: {type: float, default: 0.5}
      param_c: {type: float, default: 0.5}
    command: "python train.py --param_a {param_a} --param_b {param_b} --param_c {param_c}"

3๋‹จ๊ณ„: conda.yaml ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

1
2
3
4
5
6
7
8
9
10
11
# conda.yaml ํŒŒ์ผ
name: simple_env
channels:
  - conda-forge
dependencies:
  - python=3.9
  - pip
  - pip:
    - mlflow
    - numpy
    - pandas

4๋‹จ๊ณ„: train.py ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

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
47
# train.py ํŒŒ์ผ
import mlflow
import argparse

class SimpleModel:
    def __init__(self, param_a, param_b, param_c):
        self.param_a = param_a
        self.param_b = param_b
        self.param_c = param_c
        self.trained = False
    
    def train(self):
        self.result = self.param_a + self.param_b + self.param_c
        self.trained = True
        return self.result

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--param_a', type=float, default=0.5)
    parser.add_argument('--param_b', type=float, default=0.5)
    parser.add_argument('--param_c', type=float, default=0.5)
    args = parser.parse_args()
    
    # MLflow ์„ค์ •
    mlflow.set_tracking_uri("http://0.0.0.0:5001")
    mlflow.set_experiment("mlflow_project_experiment")
    
    with mlflow.start_run():
        # ํŒŒ๋ผ๋ฏธํ„ฐ ๋กœ๊น…
        mlflow.log_param("param_a", args.param_a)
        mlflow.log_param("param_b", args.param_b)
        mlflow.log_param("param_c", args.param_c)
        
        # ๋ชจ๋ธ ํ›ˆ๋ จ
        model = SimpleModel(args.param_a, args.param_b, args.param_c)
        result = model.train()
        
        # ์„ฑ๋Šฅ ์ง€ํ‘œ ๋กœ๊น…
        accuracy = result / 3.0
        mlflow.log_metric("accuracy", accuracy)
        mlflow.log_metric("sum_result", result)
        
        print(f"โœ… ํŒŒ๋ผ๋ฏธํ„ฐ a={args.param_a}, b={args.param_b}, c={args.param_c}")
        print(f"   ๊ฒฐ๊ณผ: {result:.2f}, ์ •ํ™•๋„: {accuracy:.3f}")

if __name__ == "__main__":
    main()

5๋‹จ๊ณ„: ํ”„๋กœ์ ํŠธ ์‹คํ–‰ํ•˜๊ธฐ

1
2
3
4
5
6
7
8
# ๊ธฐ๋ณธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์‹คํ–‰
mlflow run . --no-conda

# ์ปค์Šคํ…€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์‹คํ–‰
mlflow run . --no-conda -P param_a=0.8 -P param_b=0.9 -P param_c=1.0

# ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด Github์—์„œ ์ง์ ‘ ์‹คํ–‰
mlflow run https://github.com/your-username/my_mlflow_project --no-conda

์‹คํ–‰ ๊ฒฐ๊ณผ:

1
2
โœ… ํŒŒ๋ผ๋ฏธํ„ฐ a=0.8, b=0.9, c=1.0
   ๊ฒฐ๊ณผ: 2.70, ์ •ํ™•๋„: 0.900

์žฅ์ :

  • ๐Ÿ”„ ์™„๋ฒฝํ•œ ์žฌํ˜„์„ฑ: ํ™˜๊ฒฝ์ด ๋‹ฌ๋ผ๋„ ๋˜‘๊ฐ™์€ ๊ฒฐ๊ณผ
  • ๐Ÿค ์‰ฌ์šด ๊ณต์œ : Github URL๋งŒ ๊ณต์œ ํ•˜๋ฉด ๋
  • ๐Ÿ“ฆ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ: conda๋กœ ์˜์กด์„ฑ ์ž๋™ ๊ด€๋ฆฌ

๐Ÿ“Š ์ถ”๊ฐ€!! : ์›น UI์—์„œ ๊ฒฐ๊ณผ ํ™•์ธํ•˜๊ธฐ

๋ธŒ๋ผ์šฐ์ €์—์„œ http://0.0.0.0:5001 ์ ‘์†ํ•˜๋ฉด:

  • ์‹คํ—˜ ๋ชฉ๋ก: ๋ชจ๋“  ์‹คํ—˜์„ ์‹œ๊ฐ„์ˆœ์œผ๋กœ ํ™•์ธ
  • ์„ฑ๋Šฅ ์ง€ํ‘œ ๋น„๊ต: accuracy, loss ๋“ฑ์„ ๊ทธ๋ž˜ํ”„๋กœ ๋น„๊ต
  • ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ๋ถ„์„: ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ๋ถ„์„
  • ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ: ์ €์žฅ๋œ ๋ชจ๋ธ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ

๊ฐ„๋‹จํ•œ ์‹ค์Šต: Python์œผ๋กœ ๊ฒฐ๊ณผ ๋ถ„์„ํ•˜๊ธฐ

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
import matplotlib.pyplot as plt
import mlflow

# ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ DataFrame์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ
mlflow.set_tracking_uri("http://0.0.0.0:5001") 
experiment = mlflow.get_experiment_by_name("parameter_tuning_experiment")
runs = mlflow.search_runs(experiment_ids=[experiment.experiment_id])

print(f"๐Ÿ“Š ์ด {len(runs)}๊ฐœ์˜ ์‹คํ—˜ ์™„๋ฃŒ!")

# ๊ฐ„๋‹จํ•œ ์‹œ๊ฐํ™”
plt.figure(figsize=(12, 4))

# 1. ํŒŒ๋ผ๋ฏธํ„ฐ a vs ์„ฑ๋Šฅ
plt.subplot(1, 3, 1)
plt.scatter(runs['params.param_a'].astype(float), runs['metrics.accuracy'])
plt.xlabel('Parameter A')
plt.ylabel('Accuracy')
plt.title('A vs Accuracy')

# 2. ํŒŒ๋ผ๋ฏธํ„ฐ b vs ์„ฑ๋Šฅ
plt.subplot(1, 3, 2)
plt.scatter(runs['params.param_b'].astype(float), runs['metrics.accuracy'])
plt.xlabel('Parameter B')
plt.ylabel('Accuracy')
plt.title('B vs Accuracy')

# 3. ํŒŒ๋ผ๋ฏธํ„ฐ c vs ์„ฑ๋Šฅ
plt.subplot(1, 3, 3)
plt.scatter(runs['params.param_c'].astype(float), runs['metrics.accuracy'])
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.title('C vs Accuracy')

plt.tight_layout()
plt.show()

# ์ตœ๊ณ  ์„ฑ๋Šฅ ๋ชจ๋ธ ์ฐพ๊ธฐ
best_run = runs.loc[runs['metrics.accuracy'].idxmax()]
print("๐Ÿ† ์ตœ๊ณ  ์„ฑ๋Šฅ ๋ชจ๋ธ:")
print(f"   ๐Ÿ“Š Accuracy: {best_run['metrics.accuracy']:.3f}")
print(f"   ๐ŸŽฏ a={best_run['params.param_a']}, b={best_run['params.param_b']}, c={best_run['params.param_c']}")

๊ฒฐ๊ณผ ํ•ด์„:

  • ๐Ÿ“ˆ a, b, c ๋ชจ๋‘ ๊ฐ’์ด ํด์ˆ˜๋ก ์„ฑ๋Šฅ ํ–ฅ์ƒ (๋‹น์—ฐํžˆ ํ•ฉ์ด๋‹ˆ๊นŒ!)
  • ๐ŸŽฏ a=1.0, b=1.0, c=1.0์ผ ๋•Œ ์ตœ๊ณ  ์„ฑ๋Šฅ
  • ๐Ÿ“Š ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋™์ผํ•˜๊ฒŒ ์„ฑ๋Šฅ์— ๊ธฐ์—ฌ

๐ŸŽ‰ MLflow์˜ ์žฅ์  ์š”์•ฝ

์ „ํ†ต์  ๋ฐฉ์‹ vs MLflow

๋ฌธ์ œ ์ƒํ™ฉ์ „ํ†ต์  ๋ฐฉ์‹ ๐Ÿ˜ซMLflow ๋ฐฉ์‹ โœจ
์‹คํ—˜ ์ถ”์ ํ„ฐ๋ฏธ๋„ ์ถœ๋ ฅ๋งŒ ๋ณด๊ณ  ๋๋ชจ๋“  ์‹คํ—˜์ด ์ž๋™ ์ €์žฅ
๋ชจ๋ธ ๊ด€๋ฆฌmodel_final_final.pkl์ฒด๊ณ„์ ์ธ ๋ฒ„์ „ ๊ด€๋ฆฌ
๊ฒฐ๊ณผ ๋น„๊ต์—‘์…€์— ์ˆ˜๋™ ๋ณต๋ถ™์›น UI์—์„œ ํด๋ฆญ ๋ช‡ ๋ฒˆ
ํ˜‘์—…์ด๋ฉ”์ผ๋กœ ํŒŒ์ผ ์ „์†ก๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ณต์œ 
์žฌํ˜„์„ฑโ€œ๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋๋Š”๋ฐ?โ€์™„๋ฒฝํ•œ ํ™˜๊ฒฝ ์žฌํ˜„

๐Ÿ† ๊ฒฐ๋ก 

MLflow๋ฅผ ํ†ตํ•ด ๋” ์ฒด๊ณ„์ ์ด๊ณ  ํšจ์œจ์ ์ธ ๋จธ์‹ ๋Ÿฌ๋‹ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•ด๋ณด์„ธ์š”! ๐Ÿš€

๐ŸŽฏ ํ•ต์‹ฌ ๊ฐ€์น˜:

  1. ์‹œ๊ฐ„ ์ ˆ์•ฝ: ์ˆ˜๋™ ์ž‘์—…์„ ์ž๋™ํ™”ํ•˜์—ฌ ์‹ค์ œ ๋ชจ๋ธ ๊ฐœ๋ฐœ์— ์ง‘์ค‘
  2. ํ˜‘์—… ํ–ฅ์ƒ: ํŒ€์›๋“ค๊ณผ ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ์‰ฝ๊ฒŒ ๊ณต์œ ํ•˜๊ณ  ์žฌํ˜„
  3. ์•ˆ์ •์„ฑ ์ฆ๋Œ€: ์ฒด๊ณ„์ ์ธ ๋ชจ๋ธ ๊ด€๋ฆฌ๋กœ ํ”„๋กœ๋•์…˜ ๋ฆฌ์Šคํฌ ๊ฐ์†Œ
  4. ํ™•์žฅ์„ฑ: ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ๋Œ€๊ทœ๋ชจ ๊ธฐ์—…๊นŒ์ง€ ํ™•์žฅ ๊ฐ€๋Šฅ

๐Ÿ’ก ์‹œ์ž‘ ๊ถŒ์žฅ์‚ฌํ•ญ:

  • ์ž‘์€ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ์ ์ง„์ ์œผ๋กœ ํ™•์žฅ
  • ํŒ€ ๋‚ด MLflow ์‚ฌ์šฉ ๊ฐ€์ด๋“œ๋ผ์ธ ์ˆ˜๋ฆฝ
  • ์ •๊ธฐ์ ์ธ ๋ชจ๋ธ ์„ฑ๋Šฅ ๋ฆฌ๋ทฐ ํ”„๋กœ์„ธ์Šค ๊ตฌ์ถ•

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ:


๐Ÿ’ก ๋งˆ์ง€๋ง‰ ํŒ: MLflow๋Š” ๋‹จ์ˆœํ•œ ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ ๋จธ์‹ ๋Ÿฌ๋‹ ํŒ€์˜ ์ƒ์‚ฐ์„ฑ์„ ํ˜์‹ ์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ์ž‘์€ ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด์„ธ์š”!

๐ŸŽ‰ Happy MLOps! ๐Ÿš€

This post is licensed under CC BY 4.0 by the author.