밑바닥부터 구현하는 Naive Bayes: 단어 빈도수를 이용한 스팸 필터 만들기
요약
Naive Bayes 알고리즘을 사용하여 스팸 필터를 밑바닥부터 구현하는 과정을 설명합니다. Bag of Words 개념부터 베이즈 정리 적용, 라플라스 평활화 및 로그 계산을 통한 수치적 안정성 확보 방법까지 다룹니다.
핵심 포인트
- Naive Bayes는 단어의 빈도수를 기반으로 작동하는 단순하고 강력한 모델입니다.
- Bag of Words 방식을 통해 단어의 순서 대신 출현 빈도에 집중합니다.
- 베이즈 정리를 활용하여 단어 조건부 확률을 스팸 확률로 변환합니다.
- 라플라스 평활화와 로그 합산법으로 0 확률 문제와 언더플로우를 방지합니다.
Naive Bayes는 수년 동안 실제 스팸 필터로 작동해 왔으며, "훈련 (training)" 과정이 단순히 "세기 (counting)"인 보기 드문 ML (머신러닝) 모델입니다. 경사 하강법 (gradient descent)도, 반복 (iterations)도 필요 없습니다. 단어를 세고, 베이즈 정리 (Bayes' rule)를 적용하고, 곱하기만 하면 됩니다. 저는 이를 밑바닥부터 직접 구현했으며, 어떤 단어들이 메시지를 스팸 쪽으로 몰아가는지 정확하게 시각화했습니다.
📨 대화형 데모 (메시지를 입력해 보세요): https://dev48v.infy.uk/ml/day6-naive-bayes.html
이것은 MachineLearningFromZero의 6일 차 내용입니다 — scikit-learn 없이 밑바닥부터 알고리즘을 구현합니다.
1. Bag of words — 순서는 중요하지 않음
Naive Bayes는 메시지를 단어의 "집합 (set)"으로 취급합니다. "free cash now"와 "now cash free"는 이 모델에게 동일하게 보입니다. 이는 문법을 버리는 행위이지만, 스팸 탐지에서는 단어의 순서보다 어떤 단어가 포함되어 있는지가 훨씬 더 중요하며, 덕분에 수학적 계산이 매우 단순해집니다.
2. 훈련 (Training) = 세기 (counting)
모든 단어에 대해, 스팸(spam)과 정상 메일(ham)에서 각각 얼마나 자주 등장하나요?
for (const { text, label } of trainingData)
for (const w of tokenize(text))
counts[label][w] = (counts[label][w] || 0) + 1;
free와 click은 스팸에 넘쳐나고, meeting과 tomorrow는 정상 메일에 존재합니다. 데이터를 한 번 훑는 것만으로 끝납니다.
3. 베이즈 정리 (Bayes' rule)가 질문을 뒤집는다
당신은 P(words | spam)을 측정했지만, 실제로 원하는 것은 P(spam | words)입니다. 베이즈 정리는 이를 뒤집습니다:
P(spam | words) ∝ P(spam) × P(words | spam)
P(spam)은 사전 확률 (prior) (스팸이 얼마나 흔한가)이며, 우도 (likelihood)는 단어 증거와 함께 곱해집니다.
4. "Naive (순진한)" = 단어들이 독립적이라고 가정함
이 모델을 빠르게 만드는 비결은 다음과 같습니다: 클래스가 주어졌을 때 각 단어가 독립적이라고 가정하는 것입니다. 따라서 우도 (likelihood)는 단순히 곱셈이 됩니다:
P(words | spam) = P(w1|spam) × P(w2|spam) × ...
실제 단어들은 독립적이지 않지만 ("credit"과 "card"는 함께 나타남), 이는 순진한 거짓말입니다. 하지만 분류 결과는 놀라울 정도로 자주 정확하게 맞아떨어집니다.
5. Smoothing (평활화) + 로그 (logs)가 안정성을 유지함
두 가지 실질적인 해결책이 있습니다. 모든 카운트에 1을 더하여 (Laplace smoothing (라플라스 평활화)), 보지 못한 단어가 전체 곱셈 결과값을 0으로 만드는 것을 방지합니다. 그리고 아주 작은 확률들을 곱할 때 0으로 언더플로우 (underflow)되는 것을 막기 위해, 곱셈 대신 로그 (logarithms)를 더합니다:
score[label] = Math.log(prior[label]);
for (const w of words)
score[label] += Math.log((counts[label][w] + 1) / (totalWords[label] + V));
6. 더 높은 점수가 승리함
return score.spam > score.ham ? "spam" : "ham";
두 점수에 소프트맥스 (Softmax)를 적용하면 데모의 막대그래프처럼 확률을 얻을 수 있습니다.
핵심 요약 (The takeaway)
단어 카운트 → 베이즈 (Bayes) → 곱셈 (로그를 사용하여) → 승자 선택. 이는 존재하는 가장 단순한 분류기 (classifier) 중 하나이며, 작동을 시작하는 데 데이터가 거의 필요하지 않고, 모든 텍스트 분류 (text-classification) 작업에 있어 훌륭한 베이스라인 (baseline)으로 남아 있습니다. 라이브 스팸 필터 체험하기 — 빨간색 단어는 스팸을, 파란색 단어는 함 (ham)을 밀어냅니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기