노년층을 대상으로 하는 서비스였습니다. 즉 오탐 한 건보다 미탐 한 건의 비용이 훨씬 큽니다.
그런데 학습 데이터에서 피싱은 전체의 **1.4%**뿐이었습니다. 이 정도 불균형에서는
모델이 "전부 정상"이라고만 답해도 → 정확도 98.6%
가 나옵니다. 정확도를 기준으로 모델을 고르면 아무것도 탐지하지 않는 모델이 1등이 됩니다.
- 평가 기준을 정확도가 아니라 Recall 중심으로 변경
- 클래스 가중치 35.13 으로 소수 클래스 오답에 큰 벌점
- 4단계 Stratified 분할로 각 분할의 클래스 비율 유지
KoBERT가 놓치는 것은 대부분 표현만 살짝 바꾼 변종이었습니다. 더 큰 모델을 쓰는 대신, 놓친 사례 자체를 자산으로 만들었습니다.
문자 입력
│
├─ KoBERT 분류 ──── 피싱 ──────────────────────► 차단
│ │
│ 정상
│ ▼
└─ SBERT 임베딩 → FN 뱅크와 코사인 유사도 비교
│
유사도 ≥ 0.80 ─────► 의심 피싱으로 승격
│
유사도 < 0.80 ─────► 통과
놓친 사례가 쌓일수록 2차 방어막이 두꺼워지는 구조입니다.
| 지표 | 값 |
|---|---|
| Test Recall | 0.9672 |
| Test Precision | 0.9077 |
| 피싱 클래스 비율 | 1.4% |
| 클래스 가중치 | 35.13 |
| 승격 임계값 | 코사인 유사도 0.80 |
2025 덕성여자대학교 디지털소프트웨어공학부 해커톤 대상.
왜 텍스트 증강·오버샘플링을 쓰지 않았나
한국어 텍스트 증강은 현실에 존재하지 않는 어색한 문장을 만들어 노이즈가 될 위험이 컸습니다. 해커톤이라는 시간 제약도 있어, 가중치 조절 + 2차 검증이라는 더 안전한 경로를 택했습니다.
왜 KoBERT와 SBERT를 함께 썼나
- KoBERT — 단어·문맥 단위 표현. 분류에 강함
- SBERT — 문장 전체를 하나의 벡터로. 의미적 유사도 비교에 특화
같은 모델을 두 번 쓰는 대신, 성질이 다른 둘을 직렬로 배치했습니다.
왜 Recall만 극대화하지 않았나
Recall만 올리면 정상 문자까지 차단해 사용자가 서비스를 꺼버립니다. Precision 0.9077을 함께 유지하는 지점을 임계값으로 잡았습니다.
과적합이 아니라는 근거
가중치를 35배 준 모델은 과적합을 의심받습니다. 과적합이었다면 소수 클래스에 과반응해 Precision이 같이 무너졌을 것인데 0.9077을 유지했고, 학습셋이 아닌 Test셋에서 나온 수치입니다.
| 파일 | 내용 |
|---|---|
detect_phishing.ipynb |
데이터 전처리 · 분할 · KoBERT 학습 · FN 뱅크 구축 |
detect_phishing_verify.ipynb |
검증 전용. 학습 과정과 분리해 오염 방지 |
학습 과정과 검증 과정을 노트북 단위로 분리했습니다. 같은 노트북에서 검증하면 중간 상태가 남아 결과를 신뢰할 수 없기 때문입니다.
- 승격 임계값 0.80 은 검증셋 기준으로 고른 값이라, 실제 트래픽 분포에서는 재조정이 필요합니다.
- FN 뱅크는 놓친 사례가 쌓여야 효과가 커지는 구조라 초기에는 방어막이 얇습니다.
- 모델 가중치(
best_model_v1.pt, 368MB)는 GitHub 용량 제한으로 포함하지 않았습니다.
이상탐지에서 임계값은 통계가 아니라 비용의 문제입니다. 무엇을 놓치면 안 되는지를 먼저 정하고, 그 비용에 맞춰 기준을 잡아야 합니다.
이윤지 · github.com/lyoonji · 포트폴리오