ナイーブベイズスパムフィルタ

最も基本的な条件付き確率の公式を使って、sklearnを使わずに簡単な中国語スパムフィルタを実装します。

このケースを学ぶと、以下が理解できます:ベイズの定理 + 「各単語が独立」という仮定 = 実用可能なテキスト分類器。


生活からの導入

メールボックスのスパムフィルタ

あなたのメールボックスには毎日100通のメールが届きます。プログラムは新しいメールがスパムかどうかをどう自動判断するのでしょうか?プログラムのやり方:過去のメールから、スパムメールに「無料」「当選」などの単語が出現する確率、および通常メールに「会議」「プロジェクト」などの単語が出現する確率を統計します。

新しいメールが届いたら、計算します:スパムメールという仮定の下でこれらの単語が同時に出現する確率 vs 通常メールという仮定の下での確率。大きい方に判定します。


直感的理解

ナイーブベイズの重要な単純化—「ナイーブ仮定」:各単語の出現有無は互いに独立であると仮定します。この仮定は現実では成立しませんが(「無料」と「賞品」はよく一緒に出現します)、実用上の効果は驚くほど良いです。

テクニック手法解決する問題
対数確率logを使って掛け算を足し算に変換する多くの小数を掛け合わせると0になる(数値のアンダーフロー)
ラプラス平滑化各単語のカウントを+0ではなく+1にするトレーニングセットに出現しない単語があると確率が0になる

Python での実践

実例

from collections import defaultdict
import math

# トレーニングデータ(ラベル付けされた中国語のショートメール8件)
train_data = [
    ("赢取 免费 奖品 立即 点击", "spam"),
    ("恭喜 中奖 领取 现金 优惠", "spam"),
    ("限时 优惠 点击 链接 免费", "spam"),
    ("贷款 低息 立即 申请 现金", "spam"),
    ("会议 安排 明天 上午 讨论", "ham"),
    ("项目 进度 汇报 会议 记录", "ham"),
    ("周末 聚餐 安排 大家 参加", "ham"),
    ("报告 数据 分析 项目 完成", "ham"),
]

# 各カテゴリの単語頻度を集計する
class_word_counts = defaultdict(lambda: defaultdict(int))
class_doc_counts = defaultdict(int)
vocab = set()

for text, label in train_data:
    class_doc_counts[label] += 1
    for word in text.split():
        class_word_counts[label][word] += 1
        vocab.add(word)

vocab_size = len(vocab)
total_docs = len(train_data)

# ナイーブベイズ分類(対数確率 + ラプラス平滑化)
def predict(text, alpha=1.0):
    words = text.split()
    scores = {}
    for label in class_doc_counts:
        log_prob = math.log(class_doc_counts[label] / total_docs)
        total_words = sum(class_word_counts[label].values())
        for word in words:
            word_count = class_word_counts[label][word]
            word_prob = (word_count + alpha) / (total_words + alpha * vocab_size)
            log_prob += math.log(word_prob)
        scores[label] = log_prob
    return max(scores, key=scores.get), scores

# テスト
test_emails = [
    "免费 领取 现金 奖品",
    "明天 会议 项目 汇报",
    "限时 点击 优惠 链接",
    "周末 大家 一起 讨论 报告",
]

print("サンプル 朴素贝叶斯分类结果:\n")
for email in test_emails:
    label, scores = predict(email)
    print(f"  {email!r:28s} -> 【{label}】")
サンプル 朴素贝叶斯分类结果:

  '免费 领取 现金 奖品'          -> 【spam】
  '明天 会议 项目 汇报'           -> 【ham】
  '限时 点击 优惠 链接'           -> 【spam】
  '周末 大家 一起 讨论 报告'       -> 【ham】

AIにおける応用シーン

シーン説明
テキスト分類スパムフィルタ、ニュース分類、感情分析——ナイーブベイズは古典的なベースライン
ドキュメントのトピックモデリングLDA(Latent Dirichlet Allocation)の数学的基盤
異常検知新しいサンプルが「正常分布」に属する確率を計算し、低すぎる場合は異常と判定する
その他の拡張