Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
10 commits
Select commit Hold shift + click to select a range
c3d0cb6
:recycle:Refactor: churn ๋ชจ๋ธ ํ•™์Šต ๋ฐ์ดํ„ฐ .gitignore ์ œ๊ฑฐ
simGPT Jun 2, 2026
ea89a01
:sparkles:Feat: churn ๋ชจ๋ธ ํ•™์Šต ๊ด€๋ จ ๋ฐ์ดํ„ฐ csvํŒŒ์ผ ์ถ”๊ฐ€
simGPT Jun 2, 2026
abeb015
:bug:Fix: aws ์ž๊ฒฉ์ฆ๋ช… ์ถ”๊ฐ€ (#43)
simGPT Jun 2, 2026
316614d
:bug:Fix: churn loader ๊ตฌ๋ฒ„์ „ ํ•จ์ˆ˜ ์ œ๊ฑฐ (#45)
simGPT Jun 2, 2026
0d6c3b7
Refactor: Alert ์ž๋™ ์žฌํ•™์Šต ์ œ๊ฑฐ โ†’ ๊ด€๋ฆฌ์ž ์ด๋ฉ”์ผ ์•Œ๋ฆผ + ๋กœ์ปฌ ํ•™์Šต ์ž๋™ ๋ฐฐํฌ ๊ตฌ์„ฑ (#48)
simGPT Jun 6, 2026
ac495d0
Merge remote-tracking branch 'origin/main' into develop
simGPT Jun 6, 2026
65f7c4a
:wrench:Settings: cd ํŠธ๋ฆฌ๊ฑฐ ์ˆ˜์ • ๋ฐ alert-bridge ๋ฐฐํฌ ๋‹จ๊ณ„ ๋ˆ„๋ฝ ์ˆ˜์ •
simGPT Jun 6, 2026
a0531b6
โœจ Feat: Uplift ๋ชจ๋ธ ํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ ๋ฐ ์˜ˆ์ธก API ์ถ”๊ฐ€ (#51)
simGPT Jun 11, 2026
5c735f8
โœจ Feat: Uplift ๋ชจ๋ธ ์‹ค๋ฐ์ดํ„ฐ(CSV) ์žฌํ•™์Šต ์ง€์› ๋ฐ ์„ฑ๋Šฅ ๋น„๊ต ํ›„ ๋ฐฐํฌ ํŠธ๋ฆฌ๊ฑฐ (#54)
simGPT Jun 16, 2026
762594a
Merge remote-tracking branch 'origin/main' into develop
simGPT Jun 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ๋ฐ์ดํ„ฐ
/data/
services/data/MNIST/
services/model-api/training/uplift/data/

# ๋ชจ๋ธ ๊ฐ€์ค‘์น˜ (v1์€ ์˜ˆ์™ธ์ ์œผ๋กœ ์ปค๋ฐ‹ํ•  ๊ฒฝ์šฐ ๋ณ„๋„ ์ฒ˜๋ฆฌ)
services/model-api/artifacts/*.pth
Expand Down
9 changes: 9 additions & 0 deletions services/model-api/training/uplift/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
'cart_abandonment_rate',
]

def load_real_data(csv_path: str):
df = pd.read_csv(csv_path)
required = FEATURES + ['treatment', 'outcome']
missing = [c for c in required if c not in df.columns]
if missing:
raise ValueError(f"CSV์— ๋ˆ„๋ฝ๋œ ์ปฌ๋Ÿผ: {missing}")
return df[required]


# ๋”๋ฏธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ํ•จ์ˆ˜
def generate_dummy_data(n_samples=5000, random_state=42):
np.random.seed(random_state)
Expand Down
42 changes: 40 additions & 2 deletions services/model-api/training/uplift/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,27 @@
from sklearn.model_selection import train_test_split

from app.models.uplift_model import TLearner
from training.uplift.dataset import generate_dummy_data, FEATURES
from training.uplift.dataset import generate_dummy_data, load_real_data, FEATURES
from training.uplift.evaluate import evaluate


def get_prev_avg_uplift(client, model_name: str) -> float:
try:
versions = client.search_model_versions(f"name='{model_name}'")
if not versions:
return None
latest = sorted(versions, key=lambda v: int(v.version))[-1]
run = client.get_run(latest.run_id)
return run.data.metrics.get("avg_uplift_score")
except Exception:
return None


def main(args):
df = generate_dummy_data(n_samples=5000) # ์ผ๋‹จ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋กœ ํ•™์Šต, ์‹ค์ œ๋กœ๋Š” ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉ
if args.data_path:
df = load_real_data(args.data_path)
else:
df = generate_dummy_data(n_samples=5000)

X = df[FEATURES].values
y = df['outcome'].values
Expand All @@ -35,6 +50,13 @@ def main(args):
mlflow.set_tracking_uri(args.mlflow_uri)
mlflow.set_experiment("uplift")

client = mlflow.tracking.MlflowClient()
prev_avg_uplift = get_prev_avg_uplift(client, "uplift")
if prev_avg_uplift is not None:
print(f"ํ˜„์žฌ ์šด์˜ ๋ชจ๋ธ avg_uplift_score: {prev_avg_uplift:.4f}")
else:
print("๋“ฑ๋ก๋œ ์ด์ „ ๋ชจ๋ธ ์—†์Œ โ€” ์กฐ๊ฑด ์—†์ด ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.")

with mlflow.start_run(run_name=args.version):
model = TLearner(C=args.C)
model.fit(X_train, y_train, t_train) # ๋ชจ๋ธ ํ•™์Šต
Expand Down Expand Up @@ -66,6 +88,21 @@ def main(args):
print(f"avg_uplift_score : {metrics['avg_uplift_score']:.4f}")
print(f"MLflow์— ๋ชจ๋ธ ๋“ฑ๋ก ์™„๋ฃŒ: uplift-{args.version}")

# ์ด์ „ ๋ชจ๋ธ ๋Œ€๋น„ ์„ฑ๋Šฅ ๋น„๊ต
passed = (
prev_avg_uplift is None
or metrics["avg_uplift_score"] >= prev_avg_uplift
)
mlflow.log_param("deploy_triggered", passed)

if prev_avg_uplift is not None:
print(f"์ด์ „ ๋ชจ๋ธ avg_uplift_score : {prev_avg_uplift:.4f}")
print(f"์‹ ๊ทœ ๋ชจ๋ธ avg_uplift_score : {metrics['avg_uplift_score']:.4f}")
print(f"์„ฑ๋Šฅ ๋น„๊ต ๊ฒฐ๊ณผ : {'ํ–ฅ์ƒ โ†’ ๋ฐฐํฌ ์ง„ํ–‰' if passed else '๋ฏธ๋‹ฌ โ†’ ๋ฐฐํฌ ์Šคํ‚ต'}")

if not passed:
return

# ๋ฐฐํฌ ํŠธ๋ฆฌ๊ฑฐ
github_token = os.getenv("GITHUB_TOKEN")
github_owner = os.getenv("GITHUB_REPO_OWNER")
Expand All @@ -91,6 +128,7 @@ def main(args):
parser.add_argument('--version', type=str, default='v1')
parser.add_argument('--C', type=float, default=1.0)
parser.add_argument('--mlflow_uri', type=str, default='https://mlflow.swmlops.site')
parser.add_argument('--data_path', type=str, default=None)
args = parser.parse_args()

main(args)
Loading