よく晴れた朝、森の研究所では、ちくわ君が赤い木の実をかじりながら、届いたメッセージにのんびり目を通していた。
「ちくわさん!大変です!」勢いよく扉が開いて、はんぺん君がしょんぼりした顔で駆け込んできた。
「やあ、はんぺん君。今日はいつもの元気がないね。どうしたんだい?」
「僕の森メッセージ、最近こんなのばっかり届くんです。『無料でドングリ詰め放題!今すぐクリック!』とか、『あなたが当選しました!』とか……。大事な友達からのメッセージが、この迷惑メッセージの山に埋もれて見つからないんです」はんぺん君はがっくりと肩を落とした。
ちくわ君は嬉しそうに目を細めて、椅子ごとはんぺん君のほうへ向き直った。「なるほど、それは困ったね。でも安心していいよ。それこそ、コンピューターが得意な仕事なんだ。『ナイーブベイズ』という分類器を使えば、迷惑メッセージを自動でより分けられる。実際、世界中のメールソフトが、似たような仕組みで迷惑メールを弾いているんだよ」
「ないーぶ・べいず……なんだか強そうな名前ですね」
「ふふ、やっていることは案外シンプルだよ。一言でいうと、『どんな言葉が入っているかを手がかりに、迷惑メッセージらしさを確率で見積もる』仕組みなんだ。まずは、はんぺん君が今まで受け取ったメッセージを、迷惑なものと普通のものに仕分けして、コンピューターに見せてあげよう」
二人は、これまでのメッセージを十通選んで、迷惑メッセージ(スパム)と普通のメッセージに分けた。迷惑なものが四通、普通のものが六通あった。
「まず、何も中身を見ないうちから言えることがある。十通のうち四通が迷惑メッセージだったよね。だから、新しいメッセージが届いたとき『これはたぶん四割くらいの確率で迷惑かな』と最初に当たりをつけられる。これを『事前確率』と呼ぶんだ」
「なるほど。中身を読む前の、第一印象みたいなものですね」
「うまいこと言うね、まさにそれだよ。さて、ここからが本番。迷惑メッセージと普通のメッセージで、どんな言葉がよく使われていたか、数えてみよう」ちくわ君は楽しそうにキーボードを叩き始めた。
集計はあっという間に終わった。迷惑メッセージには「無料」「今すぐ」「クリック」「当選」といった言葉が何度も出てくる。一方、普通のメッセージには「明日」「遊ぼう」「公園」「ありがとう」といった言葉が並んでいた。
「面白いですね!迷惑メッセージって、使う言葉にクセがあるんだ」
「そう、そのクセこそが手がかりなんだ。じゃあ、さっそく一通、判定してみよう。『無料 ドングリ クリック』というメッセージが届いたとする。この三つの言葉が、迷惑メッセージと普通のメッセージで、それぞれどのくらいの割合で登場していたかを調べるんだ」
ちくわ君はホワイトボードに、三つの言葉のスパムらしさを書き出した。
「見てごらん、はんぺん君。『無料』と『クリック』は、迷惑メッセージのほうに四倍以上も出やすい。一方で『ドングリ』は、どちらのメッセージにも同じくらい出てくる。つまり『ドングリ』は、迷惑かどうかを見分ける手がかりにはほとんどならないんだ」
「あっ、確かに!『ドングリ』は迷惑メッセージにも普通のメッセージにも出てきますもんね」
「そういうことだね。さて、最後の仕上げだ。最初の第一印象、つまり『四割は迷惑』という事前確率に、三つの言葉の出やすさを順番にかけ算していく。普通のメッセージのほうも同じように計算して、二つのスコアを比べるんだ」
ちくわ君が計算を走らせると、二つの数字が画面に並んだ。迷惑メッセージらしさのスコアが、普通のメッセージらしさのスコアを、大きく上回っている。割合にすると、こうなった。
「すごい……!ちゃんと迷惑メッセージだって見抜きました!しかも九割以上の自信を持って!」はんぺん君は興奮して尻尾を振った。
「うまくいったね。ところで、さっきから『言葉の出やすさを順番にかけ算する』と言ってきたけれど、ここにこの手法のちょっとずるい仮定が隠れているんだ。それが『ナイーブ』、つまり『単純な』という名前の由来だよ」
「ずるい仮定、ですか?」はんぺん君は首をかしげた。
「うん。本当は、言葉どうしには関係があるよね。『今すぐ』の後ろには『クリック』が続きやすい、とか。でもナイーブベイズは、そういうつながりをぜんぶ無視して、『どの言葉も、てんでばらばらに、独立して現れる』と割り切ってしまうんだ。現実には少し乱暴な仮定だよね」
「えっ、そんなに大ざっぱで、ちゃんと当たるんですか?」
「それがね、不思議とよく当たるんだ」ちくわ君は嬉しそうに新しい木の実を口に放り込んだ。「迷惑メッセージかどうかを当てるだけなら、言葉の細かいつながりまで考えなくても、『どんな言葉が入っているか』だけで十分に見分けがつくことが多い。単純なのに、速くて、よく当たる。だからこの手法は、何十年も現役で使われ続けているんだよ」
「単純なのが、かえって強いんですね……。あれ、でもちくわさん。一つ気になることがあります。もし迷惑メッセージに一度も出てこなかった言葉が、新しいメッセージに入っていたら、どうなるんですか?出やすさがゼロだと、かけ算したら全部ゼロになっちゃいませんか?」
ちくわ君は木の実を持つ手を止めて、嬉しそうに身を乗り出した。「はんぺん君、今日一番いい質問だ。まさにそれが、初心者がいちばんつまずく落とし穴でね。たった一つの未知の言葉のせいで、スコアがまるごとゼロになってしまう。これを防ぐために、賢い人たちが面白い工夫を考えたんだ」
「どんな工夫ですか?」
「『どの言葉も、最初から一回だけは出てきたことにしておく』んだ。実際には数えてゼロだった言葉にも、こっそり一を足しておく。そうすれば、出やすさが完全なゼロになることはなくなる。たった一を足すだけの素朴な工夫だけど、これがよく効くんだよ」
「なるほど!ゼロにさえならなければ、かけ算で消えてしまうこともないんですね」
「そういうことだね。実際の分析では、こうした計算はライブラリがぜんぶ面倒を見てくれる。Pythonなら、数行でこの分類器が作れるよ」ちくわ君は楽しそうにコードを書き足した。
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
# メッセージを単語の出現回数に変換する
vec = CountVectorizer()
X = vec.fit_transform(messages) # messages: 学習用メッセージ
y = labels # labels: 迷惑=1, 普通=0
# ナイーブベイズ分類器を学習させる
model = MultinomialNB().fit(X, y)
# 新しいメッセージを判定する
print(model.predict(vec.transform(["無料 ドングリ クリック"])))
# [1] → 迷惑メッセージと判定
「たったこれだけ……!さっきの計算が、ぜんぶこの中に入っているんですね」はんぺん君は画面に張りついた。
「そうだよ。仕組みを知ったうえでライブラリを使うと、なぜうまくいくのか、どんなときに間違えそうかが見えてくる。そこが大事なんだ。例えばこの分類器は、学習に使ったメッセージに『ドングリ』ばかり並んでいたら、ドングリを基準に世界を見るようになってしまう。どんな言葉を学ばせるかで、性格が決まるんだよ」
はんぺん君は、ぱっと顔を上げた。「ちくわさん、ありがとうございました!家に帰ったら、さっそく自分のメッセージで試してみます。これで大事な友達からのメッセージを、もう見逃しません!」
「それはいいね。うまく仕分けできたら、ぜひ聞かせてよ。楽しみにしているから」ちくわ君は温かく手を振って見送った。
はんぺん君が朝の光の中へ駆けていったあと、ちくわ君は届いたメッセージの一覧に向き直った。たった一を足すだけの素朴な工夫で、計算が破綻せずに回り出す。そのささやかな知恵を思うと少し心が弾んで、ちくわ君は新しい赤い木の実へ手を伸ばした。
森の研究所には、今日も小さな発見の喜びが満ちていた。