K_DM Book
用語集
対象者 高校生
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

ナイーブベイズ(高校生向け)

入門高校生

02.2.6 高校生向け

ナイーブベイズ(高校生向け)

最終更新 2026-11-01 読了時間 2 分
まとめ
ナイーブベイズは、「このクラスなら、この特徴が出る確率はいくつか」という条件付き確率を学習データから数え、それを掛け算してクラスの確率を比べる分類の方法です。「無料」と「部活」のように特徴どうしは本当は関係しあっていても、「おたがい独立」と割り切って掛け算してしまい、この割り切りが「ナイーブ(単純)」の意味です。数える・割る・掛けるだけなので、学習も予測もとても速く、迷惑メール判定のような文章の分類で 20 年以上使われています。確率の大小関係は合いやすい一方、「90 %」のような確率の値そのものはずれやすい、という性質があります。

クイズ:このメールは迷惑メール? #

これまでに受け取ったメール 100 通を調べたら、次の表になったとします。

迷惑メール(40 通)普通のメール(60 通)
「無料」が入っている30 通6 通
「部活」が入っている2 通30 通

新しく届いたメールには「無料」が入っていて、「部活」は入っていません。迷惑メールでしょうか。

直感では「無料」があるから怪しい、と思うはずです。ナイーブベイズは、この直感を条件付き確率の掛け算として計算します。「迷惑メールのうち 30/40 に『無料』が入る」「普通のメールのうち 6/60 にしか入らない」という比率の差が、判定の根拠になります。

仕組み:数えて、掛けて、大きいほうを選ぶ #

まずクラスごとの割合(事前確率)を数えます。迷惑 40/100 = 0.4、普通 60/100 = 0.6。何も見ていない段階では「普通のほうが多い」というスタート地点です。次に特徴ごとの条件付き確率を 4 つ数えます。\(P(\text{無料} \mid \text{迷惑}) = 30/40 = 0.75\)、\(P(\text{無料} \mid \text{普通}) = 6/60 = 0.1\)。「部活なし」も同様に \(38/40 = 0.95\) と \(30/60 = 0.5\) で、表の列ごとに割り算するだけです。

そのうえでクラスごとに全部掛けます。迷惑は \(0.4 \times 0.75 \times 0.95 = 0.285\)、普通は \(0.6 \times 0.1 \times 0.5 = 0.03\)。本当は「無料」と「部活」の出方は関係しあっているかもしれませんが、独立だと仮定して掛け算してしまいます。最後に大きいほうを答えにします。0.285 > 0.03 なので迷惑メール。2 つを足して割れば確率の形になり、\(0.285 / (0.285 + 0.03) \approx 0.90\)、「90 % 迷惑メール」と言えます。

動かして見てみよう #

下の図で「再生」を押すと、説明した手順が 1 つずつ進みます。手順ごとの説明は図の下に出ます。

数が連続値のときは「平均と散らばり」を覚える #

単語の有無のように「ある/ない」なら表を数えれば済みますが、身長や温度のような連続した値はどうするのでしょう。この場合は、クラスごと・特徴量ごとに平均と標準偏差を覚えておき、「平均に近い値ほど出やすく、離れるほど出にくい」釣鐘型の分布で確率を決めます(ガウシアンナイーブベイズ)。

ガウシアン Naive Bayes の決定領域。3 クラスの点に対し、青と赤のクラスがそれぞれ「出やすい範囲」として横長・縦長の楕円で描かれ、点はその楕円の中にあるほどそのクラスと判定される

上の図の青と赤のうすい楕円が、各クラスの「このあたりの値が出やすい」という範囲です。学習とは、この楕円の中心(平均)と広がり(標準偏差)をクラスごとに決めることにほかなりません。新しい点は、どのクラスの楕円の「濃い部分」にいるかで色が決まります。

よく見ると、点の並びは斜めなのに、2 つの楕円は縦横にまっすぐです。特徴量 1 と 2 を独立と仮定したからです。これがナイーブな仮定の限界で、それでも分類の境目はそれなりに正しく引けている、というのがこの手法の面白いところです。

電卓感覚で計算してみる #

冒頭のクイズを、そのまま計算にしたものです。

1
2
3
4
5
6
7
8
p_spam, p_ham = 0.4, 0.6
free_given = {"spam": 30/40, "ham": 6/60}       # P("free" | class)
club_given = {"spam": 2/40, "ham": 30/60}       # P("club" | class)
# new mail: contains "free", does NOT contain "club"
s = p_spam * free_given["spam"] * (1 - club_given["spam"])
h = p_ham * free_given["ham"] * (1 - club_given["ham"])
print(f"spam score={s:.3f} ham score={h:.3f}")
print(f"P(spam | mail) = {s / (s + h):.2f}")

気をつけたいこと #

最初の課題は確率が 0 になる単語です。学習データの迷惑メールに一度も出なかった単語が新しいメールにあると、掛け算全体が 0 になってしまいます。すべての数え上げに 1 を足すラプラス平滑化で防ぎます。二つ目は強く関係しあう特徴で、「無料」と「プレゼント」のようにいつもセットで出る単語は、独立と仮定すると同じ証拠を 2 回数えることになり、確率が極端になります。クラスの順位は合っていても、「90 %」という値は信用しすぎないようにしましょう。三つ目は「掛け算が小さくなりすぎる」ことです。単語が 1000 個あると 0.1 を 1000 回掛けるような計算になり、コンピュータでは 0 に丸められます。実際には対数をとって足し算に直し、0.1 の対数を 1000 回足す形で計算します。

やってみよう #

まず、冒頭の表を使って、「部活」が入っていて「無料」が入っていないメールの確率を、紙と電卓で計算してみよう。次に、自分のメールや SNS の通知 20 件を「要るもの/要らないもの」に分け、それぞれによく出る単語を数えて表を作ってみよう。どの単語が決め手になりそうだろうか。最後に、「独立と仮定すると失敗する例」を考えてみよう(ヒント:身長と体重のように、片方が大きければもう片方も大きい特徴)。

つぎに読む #

連続値を扱う式(正規分布)、多項分布版、対数での計算は 標準版のナイーブベイズ で扱っています。scikit-learn の GaussianNB を使った実装も同じページにあります。確率ではなく「近さ」で分ける方法は 高校生向けの k近傍法、一次式を確率に変える方法は 高校生向けのロジスティック回帰 へ。分類の結果を評価する方法は 分類の評価指標 にまとめています。