# スパムへの対策 原題: A Plan for Spam(Paul Graham, 2002年8月)/進捗 42%〜50% **中心主張**: spam は内容に基づく統計的フィルタリング(ベイジアンフィルタ)で止められる。spam 業者のアキレス腱は「何であれメッセージを届けなければならない」ことにあり、そのメッセージを認識するソフトウェアが書ければ彼らの道を絶てる。個々の単語の spam 確率をベイズの結合確率で計算するだけで、1000通中で見逃す spam が5通以下、誤検出0通という水準に達した。手書きのルールと違い、この方法は spam の進化に自動的に追随する。 **実践指針**: - spam を判別したい場面では、個々の特徴を人手で見つけるゲームをやめて統計的手法を先に試せ。著者は6ヶ月を特徴抽出に費やしたが、統計的フィルタは自分よりずっと賢く、"virtumundo" や "teens" だけでなく "per" "FL" "ff0000"(HTML メールで真っ赤な文字を表示する色指定)まで spam の良い指標として見つけ出した。 - フィルタを設計する場面では、誤検出を避ける方向に意図的にバイアスをかけよ。正当なメールを失うことは spam を受け取るより何倍もダメージが大きい。著者は good 側の単語の生起回数を2倍にカウントし、全体で5回以上出現していない単語は計算から外した。 - フィルタを配布する場面では、単一の初期フィルタで済ませるな。ユーザごとに自分が受け取った spam / 非 spam でテーブルを持たせよ。そうすることで (a) 精度が上がり (b) 各ユーザの判断基準を正確に反映でき (c) spam 業者が「フィルタを回避するメール」を作ることを極めて困難にできる。 - メールリーダを作る場面では、削除ボタンを2種類(通常の削除/spam として削除)用意せよ。それだけでコーパスが自動的に育つ。 - ホワイトリストを使う場面では、フィルタリングを簡単にするためではなく計算負荷を減らすために使え。初めてメールをやりとりする相手のメールにはむしろ決まりきった点が少なく、内容フィルタは十分機能する。 - 新しい単語(未知トークン)に確率を割り当てる場面では、0.4(やや無害寄り)を使え。spam に出て来る単語はどれもどこかで見たようなものばかりだから。 - 反 spam の政策を考える場面では、フィルタリングと他の手法を同時に多種類使え。フィルタの種類が多いほど、そのどれをも通過する spam を作り出すのが困難になる。 - spam の定義を要求された場面では「頼んでいないのに自動的に送ってくるメール」を使え。「商用」も「頼まない」も本質ではない。オプトアウトでないことはオプトインと同等ではなく、受け取り意志のチェックボックス(デフォルト off)をクリックしたのでなければ spam である。 **根拠となる事例・考え方**: - 手書きルールの限界: "click" という単語を探すだけで著者の spam コーパスの 79.7% は捕まえられたが、誤検出は 1.2% あった。規則を厳しくすればするほど誤検出が増える。最後の数%を捕まえるのが極めて難しい。 - アルゴリズムの骨格: spam / 非 spam のコーパス(各4000通程度)の全テキストを、ヘッダも本文中の HTML も javascript もいっしょくたにスキャン。アルファベット・数字・ダッシュ・アポストロフィ・$ をトークンの構成要素とみなし、全て数字のトークンと HTML コメントは無視。各集合での出現回数を数え、単語ごとの spam 確率を第3のハッシュテーブルに計算する(0.01〜0.99 でクリップ)。新しいメールが届くと、確率が 0.5 から最も遠い特徴的な15トークンを抽出し、結合確率を計算。0.9 以上なら spam と判断(閾値は問題にならない。確率が中くらいになるメールはほとんどないから)。 - ベイズ確率の本当の利点は「自分が何を計測しているかがはっきりしていること」。SpamAssassin のような特徴認識型フィルタが与える「スコア」は、誰もそれが何を意味するのか分からない(開発者にもわからない)。ベイズは実際の確率を与え、証拠を結合する計算法も確立している。 - 著者のメールアーカイブでは "sex" を含むメールが spam である確率は 0.97、"sexy" なら 0.99。両方を含むメールが spam である確率は 99.97% と言える。一方 "though" "tonight" "apparently" のような単語は確率を押し下げるので、正当なメールがたまたま "sex" を含んでいても spam と認識されない。 - 個人化の威力: 著者は "Lisp" を含むメールを大量に受け取るが、"Lisp" を含む spam は受け取ったことがない。こういう単語は事実上パスワードとして働く。ベイジアンフィルタは、著者自身が気づきもしなかった多くの単語を見つけ出した。 - 進化への追随: spam 業者が "cock" のかわりに "c0ck" を使い始めても、ベイジアンフィルタは自動的にそれに気づく。実際 "c0ck" は "cock" よりずっと明確なスパムの証拠であり、どのくらいより明確かということさえフィルタは知っている。 - フィルタを破るには、spam 業者は普通のメールと区別がつかない spam を作らねばならなくなり、これが彼らの活動を厳しく制限する。ヘッダも検出されるので本文の工夫だけでは足りない。将来の spam は「Hey there. Thought you should check out the following: http://www.27meg.com/foo」程度にしかならない。 - 経済的な論理: spam への反応は 100万通あたり15通程度(カタログ送付は 3000通)だが、送るコストがほとんどかからないので成立している。フィルタリングによって効果的な売り文句が使えなくなり反応率が下がれば、ビジネス的なうまみが消える。「私は、自分が spam をもう見たくないから spam フィルタを書き始めた。しかし spam フィルタの技術が十分に進歩すれば、spam の効果は無くなり、spam 業者達もそれを送るのをやめるだろう。」 - 実例(付録): ある spam の最も特徴的な15単語 "qvp0045 indira mx-05 intimail $7500 freeyankeedom cdo bluefoxmedia jpg unsecured platinum 3d0 qves 7c5 7c266675" は全て確率0.99。別の例では "madam 0.99 promotion 0.99 republic 0.99 shortest 0.047 mandatory 0.047 …" と良い証拠と悪い証拠が混ざり、結合すると 0.9027 になる。"Republic" が高いのはナイジェリア絡みの詐欺メールに使われるからで、発展途上国を舞台にした怪しいビジネスの案内が spam の1ジャンルを作っている。 - 誤検出しかけた例: フィルタを通り抜けた稀な spam は、著者の正当なメールに含まれる単語(perl 0.01, python 0.01, tcl 0.01, scripting 0.01, morris 0.01, graham 0.014)を多量に含んでいた。つまりプログラミング言語を専門にしていて Morris という友人がいる人でない限り通らない。 - 統計データの面白さ: "describe" は著者の4000通の spam の中の1通たりとも含んでいなかった。「spam テキストを分析していてわかることのひとつは、spam 業者の使う言語がどれだけ狭いかということだ。」 - 未実装のアイディア: 単語のペア/3単語の組み合わせでのフィルタリング("special offers" は 0.99、"approach offers" は 0.1 以下になるはず)、URL とドメイン名の分解("xxxporn" を見たことがなくても "xxx" 0.9899 と "porn" 0.99 に分解すれば 0.9998)、誤検出を避けるためだけの第2段階テスト、巨大な spam コーパスの共同蓄積。 - 訳註(日本語への適用): 日本語ではトークン分割が英語ほど単純ではない。辞書検索・形態素解析のほか、日本語の特徴的な単語の多くが漢字2〜3文字であること、不完全な区切りによるランダムな語句はコーパスが大きくなれば中立の確率に近付くことを考えると、単純に文字のペアや triplet を取って統計処理しても意味ある結果が出るかもしれない。日本語メールと英語メールの比率が spam / nospam で大幅に違うなら、言語毎にテーブルを分ける必要がありそう。 - 訳註(手法上の批判): 定数(0.01 / 0.99)で数値を切るのは Paul Graham 自身も認める kludge で、ベイズ統計から逸脱しているとの批判もある。Gary Robinson は事前確率(バックグラウンド)を設定することで一方にしか現れない単語もベイズ統計で扱えるようにし、この方式が既存のベイジアンスパムフィルタに広く採り入れられている。訳者の経験では Paul Graham 方式で 97〜98% の精度は比較的容易に達成され、そこから先は事前確率の考慮が効いてくる。ただし総合的な成績はトークナイゼーションの方法やメールメッセージの扱い(multipart で binary、本文の文字エンコーディングとヘッダの charset 指定が一致していない場合の扱い)にも大きく影響される。 **キーワード**: - **ベイジアンフィルタ**: 個々の単語の spam 確率をベイズの結合確率で統合し、メール全体の spam 確率を出す手法。コーパスから学習するため、spam の進化に自動追随する。 - **誤検出(false positive)**: 無害なメールが spam と認識されること。「まるで死の危険のあるニキビ薬みたいなもの」。フィルタの性能が良くなるほどユーザが信頼して spam フォルダを見なくなるため、危険はむしろ増す。 - **コーパス**: spam / 非 spam の学習用メール集合。ベイジアンフィルタがうまく動く鍵は「大きく綺麗なコーパス」にある。 - **特徴的な15トークン**: 確率が 0.5 から最も遠い15個。全メールの全単語を使うのではなく、判別力の高いものだけを結合する設計。 - **spam の定義**: 「頼んでいないのに自動的に送ってくるメール」。自動的に送ってくることが spam の存在意義そのものである。 - **Arc**: 本記事のアプリケーションのテストのために書かれていた Paul Graham の新しい Lisp 方言。記事執筆時点では未リリース。