| """Tier 1 ์ด์ ํ์ง ๋ชจ๋ธ ๋ฒค์น๋งํฌ |
| |
| SECOM pass/fail ๋ผ๋ฒจ์ ์ ๋ต์ผ๋ก ๋ณด๊ณ ๋น์ง๋ ์ด์ ํ์ง ๋ชจ๋ธ์ ๋น๊ต |
| - IsolationForest |
| - LocalOutlierFactor |
| - OneClassSVM |
| - baseline: ํ์คํ ํผ์ฒ ์ ๋๊ฐ ํ๊ท (๋จ์ ํต๊ณ) |
| |
| ํ๊ฐ: ROC-AUC, PR-AUC (fail์ด 6.6%๋ฟ์ธ ๋ถ๊ท ํ ๋ฐ์ดํฐ๋ผ PR-AUC๋ฅผ ์ฃผ์งํ๋ก ๋ด) |
| train์๋ง fit, test์์ ํ๊ฐ (70/30 stratified split) |
| |
| ์คํ: python -m experiments.tier1_detection.benchmark |
| ๊ฒฐ๊ณผ: results.md ํ + plots/ ๊ทธ๋ํ |
| """ |
| from pathlib import Path |
|
|
| import matplotlib |
|
|
| matplotlib.use("Agg") |
| import matplotlib.pyplot as plt |
| import numpy as np |
| from sklearn.ensemble import IsolationForest |
| from sklearn.metrics import ( |
| average_precision_score, |
| precision_recall_curve, |
| roc_auc_score, |
| roc_curve, |
| ) |
| from sklearn.model_selection import train_test_split |
| from sklearn.neighbors import LocalOutlierFactor |
| from sklearn.svm import OneClassSVM |
|
|
| from data.secom.loader import load_secom |
| from data.secom.preprocess import SecomPreprocessor |
|
|
| RANDOM_STATE = 42 |
| OUT_DIR = Path(__file__).parent |
| PLOTS_DIR = OUT_DIR / "plots" |
|
|
| MODELS = { |
| "IsolationForest": IsolationForest(n_estimators=200, random_state=RANDOM_STATE), |
| "LocalOutlierFactor": LocalOutlierFactor(n_neighbors=20, novelty=True), |
| "OneClassSVM": OneClassSVM(nu=0.1, kernel="rbf", gamma="scale"), |
| "baseline": None, |
| } |
|
|
|
|
| def anomaly_scores(name, model, X_train, X_test): |
| """๋ชจ๋ธ๋ณ ์ด์ ์ ์ ๋ฐํ, ๋์์๋ก ์ด์""" |
| if name == "baseline": |
| |
| return np.abs(X_test).mean(axis=1) |
| model.fit(X_train) |
| |
| return -model.score_samples(X_test) |
|
|
|
|
| def main(): |
| X, y = load_secom() |
| y_true = (y == 1).astype(int).to_numpy() |
|
|
| X_train_df, X_test_df, y_train, y_test = train_test_split( |
| X, y_true, test_size=0.3, stratify=y_true, random_state=RANDOM_STATE |
| ) |
|
|
| pre = SecomPreprocessor().fit(X_train_df) |
| X_train = pre.transform(X_train_df) |
| X_test = pre.transform(X_test_df) |
|
|
| PLOTS_DIR.mkdir(exist_ok=True) |
| fig_roc, ax_roc = plt.subplots(figsize=(6, 5)) |
| fig_pr, ax_pr = plt.subplots(figsize=(6, 5)) |
|
|
| results = [] |
| for name, model in MODELS.items(): |
| scores = anomaly_scores(name, model, X_train, X_test) |
| roc = roc_auc_score(y_test, scores) |
| ap = average_precision_score(y_test, scores) |
| results.append((name, roc, ap)) |
|
|
| fpr, tpr, _ = roc_curve(y_test, scores) |
| ax_roc.plot(fpr, tpr, label=f"{name} (AUC={roc:.3f})") |
| prec, rec, _ = precision_recall_curve(y_test, scores) |
| ax_pr.plot(rec, prec, label=f"{name} (AP={ap:.3f})") |
|
|
| |
| ax_roc.plot([0, 1], [0, 1], "k--", alpha=0.4, label="random") |
| ax_roc.set_xlabel("False Positive Rate") |
| ax_roc.set_ylabel("True Positive Rate") |
| ax_roc.set_title("Tier 1 Anomaly Detection - ROC") |
| ax_roc.legend(loc="lower right", fontsize=8) |
|
|
| ax_pr.axhline(y_test.mean(), color="k", ls="--", alpha=0.4, label="random") |
| ax_pr.set_xlabel("Recall") |
| ax_pr.set_ylabel("Precision") |
| ax_pr.set_title("Tier 1 Anomaly Detection - Precision-Recall") |
| ax_pr.legend(loc="upper right", fontsize=8) |
|
|
| fig_roc.tight_layout() |
| fig_roc.savefig(PLOTS_DIR / "roc.png", dpi=120) |
| fig_pr.tight_layout() |
| fig_pr.savefig(PLOTS_DIR / "pr.png", dpi=120) |
|
|
| write_results(results, len(X_train_df), len(X_test_df), int(y_test.sum())) |
| for name, roc, ap in sorted(results, key=lambda r: -r[2]): |
| print(f"{name:24s} ROC-AUC={roc:.3f} PR-AUC={ap:.3f}") |
|
|
|
|
| def write_results(results, n_train, n_test, n_test_fail): |
| """results.md์ ํ๊ฐ ํ ๊ธฐ๋ก, PR-AUC ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ""" |
| ranked = sorted(results, key=lambda r: -r[2]) |
| lines = [ |
| "# Tier 1 ์ด์ ํ์ง - ๋ชจ๋ธ ๋ฒค์น๋งํฌ", |
| "", |
| "SECOM ๋ฐ์ดํฐ์
(๋ฐ๋์ฒด ์ ์กฐ ๊ณต์ ์ผ์)์ผ๋ก ๋น์ง๋ ์ด์ ํ์ง ๋ชจ๋ธ์ ๋น๊ตํฉ๋๋ค.", |
| "", |
| "## ์ค์ ", |
| "", |
| f"- train {n_train} / test {n_test} (70/30 stratified split)", |
| f"- test์ fail(์ด์) ์ํ: {n_test_fail}๊ฑด", |
| "- ์ ์ฒ๋ฆฌ: ์ ๊ฒฐ์ธก/์์ ์ปฌ๋ผ ์ ๊ฑฐ -> ์ค์๊ฐ ์ํจํ
์ด์
-> ํ์คํ", |
| "- ํ๊ฐ ์งํ: ROC-AUC, PR-AUC (๋ถ๊ท ํ ๋ฐ์ดํฐ๋ผ PR-AUC๊ฐ ์ฃผ์งํ)", |
| "", |
| "## ๊ฒฐ๊ณผ (PR-AUC ๋ด๋ฆผ์ฐจ์)", |
| "", |
| "| ๋ชจ๋ธ | ROC-AUC | PR-AUC |", |
| "|---|---|---|", |
| ] |
| for name, roc, ap in ranked: |
| lines.append(f"| {name} | {roc:.3f} | {ap:.3f} |") |
| lines += [ |
| "", |
| "", |
| "", |
| "", |
| "", |
| f"## ์ฑํ", |
| "", |
| f"PR-AUC ๊ธฐ์ค ์ต๊ณ ๋ชจ๋ธ์ **{ranked[0][0]}** " |
| f"(PR-AUC {ranked[0][2]:.3f}), agents/detection.py์ baseline์ผ๋ก ์ฌ์ฉํฉ๋๋ค.", |
| "", |
| ] |
| (OUT_DIR / "results.md").write_text("\n".join(lines), encoding="utf-8") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|