hee_!J
feat: Tier 1 ์ด์ƒ ํƒ์ง€ SECOM ๊ธฐ๋ฐ˜ ๊ตฌํ˜„
77d44e1
Raw
History Blame Contribute Delete
5.14 kB
"""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)
# score_samples๋Š” ๋†’์„์ˆ˜๋ก ์ •์ƒ์ด๋ฏ€๋กœ ๋ถ€ํ˜ธ ๋ฐ˜์ „
return -model.score_samples(X_test)
def main():
X, y = load_secom()
y_true = (y == 1).astype(int).to_numpy() # 1 = fail(์ด์ƒ)
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})")
# ์ฐธ์กฐ์„ , plot ํ…์ŠคํŠธ๋Š” ํฐํŠธ ์˜์กด ํ”ผํ•˜๋ ค๊ณ  ์˜๋ฌธ ์‚ฌ์šฉ
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 += [
"",
"![ROC](plots/roc.png)",
"",
"![PR](plots/pr.png)",
"",
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()