(Englisn translation: Gauche:SpamFilter:English)
Paul GrahamのA Plan for Spam (和訳) およびBetter Bayesian Filtering (和訳) に基づいたspamフィルタリングツールです。 日本語メールにも対応してます。
scmail-1.0に統合されました。 ダウンロード、インストール、設定方法等は以下のwebpageを参照して下さい。
本ページでは、開発中に気づいたことやtips等を中心にメモしてゆきます。
筆者(Shiro)は次の設定を.xemacs/init.elに加えて、 M-x mew した後 +inbox バッファでRを打って振り分けています。 振り分け後に+inboxのrescanをかけていないのは、 一応、誤検出が無いかどうかのチェックができるようにするためです (scmail-refileの結果と+inboxの内容が並べて見られるので)。 最近はだいぶbayesian filterに信頼を置いてきたので、任せっきりですが。
(defun run-scmail-refile () "Run scmail-refile on other window" (interactive) (message "Running scmail-refile ...") (shell-command "scmail-refile")) (setq mew-summary-mode-hook '((lambda () (define-key mew-summary-mode-map "R" 'run-scmail-refile))))
現在のバージョンはGauche:SpamFilter:予備実験の結果を踏まえて、 次のような処理を行っています。
Shiroの使用状況です。
inbox, trash, spamのフォルダを学習に使う。 メッセージ数と登録単語数はこんな感じになった。
nonspam | spam | |
日本語 | 211178words/3609msgs | 99058words/1581msgs |
それ以外 | 33158words/1577msgs | 46513words/1854msgs |
合計 | 244336words/5186msgs | 145571words/3435msgs |
この学習データをもとにinboxとspamでspam度判定を行ってみると こうなった。
tokenizationは原始的でもけっこういけるんじゃなかろうか。
ということで、このエンジンをscmailから使うようにして、 届いたメールをscmail-refileで自動振り分けするのをしばらく 試している。scmail-refileではまず身元のはっきりしている メルマガやメーリングリストのメッセージを規則によって 振り分けてしまい、残ったものに対してspam度判定を行っている。 今のところ新着メールに対しては望み通りの判定が行われている。
旅行などでしばらくメールが読めなかった場合に、 数百通たまったメールをscmail-refileで振り分けるのはなかなか快感だ。 今のところ、誤認識(nospamをspamと判定)は依然として0だが、 見逃し(spamをnospamと判定)はちらほら見られる。
学習セットは更新せずに、最近(約10日間)の新着メール470通に適用してみたところ、 211通を正しくspamと認識、6通を見逃した。見逃したspamはすべて英文である。
見逃しメールのうち2通は本文がURLと2〜3行のランダムな文字列という 形式になっている。メーリングリスト経由で来たこともあり、 ヘッダもある程度クリーンであったようだ。 3通は注意深く中立を装った本文で、しかもメーリングリスト経由。 最後の1通はspamくさいが、プログラミングに関するものなのでテクニカルな 用語が低いspam度となったようだ。
もうフィルタ無しには戻れない。 統計辞書読み込み速度だけは何とかしたいが、 spamにわずかでも目を通さなくて良いというのがこんなに楽だったとは。 (今のところ、popでフェッチしてからフィルタで振り分けているため、 subjectだけは目に入る。誤検出の確認の意味もあるからそれは良い。 ただ、自分からrefileというアクションを行わなくて良いだけで ずいぶん楽に感じるものだ)。
前回から今までで、依然として誤検出は無し。 見逃しは英文spam 2通、 和文spam 2通であった。
見逃した和文spamのうち一通はかなりspammyな内容だったのだが、 読点のかわりに全角コンマが使われていて、そういうspamが学習データに 少なかったためにspam度が低く出てしまったようだ。全角コンマも 単語の区切り文字として扱うようにしたらspam度1.0になった。
実用上は、このくらいのspamが網を逃れてもそれを手動でrefileするのは さほど問題ではない。しかし、普段目にするspamが減れば減る程、 網を逃れたspamが気に障る。Paul Grahamがspamフィルタ書きは ゲームだと言ったが、確かに気にしだしたらはまりそうだ。
今日になって、二つの英文spamがフィルタをすり抜けた。 おそらくフィルタ回避と思われる技巧を弄しているので、 紹介しておこう。
一つはHTMLメイルで、ランダムなコメントを本文中の各文字間に 挟むというもの。本文はこうだ(長いので適宜改行を入れた)。
<BR><!--vx06f6e36i--><BR><!--zuywe6k0d5m--><DIV ALIGN=CENTER><!--1ilss7ezpbqlzj--><FONT face="Arial" size="3"><A HREF="http://200.155.12.12/~omsbr/mbl/" target="_blank"><!--wsvhen231ui-->C<!--sr2ji6rd0drl-->l<!--6k5pr5vh0n7lo-->i<!--ll2oqq7k4s-->c<!--glnuflpqbj-->k<!--perykn5ik4--> h<!--i23sgzyclque2-->e<!--9b4jswx87b5e-->r<!--0nc4uqwhbsv0i9-->e <!--l2v56qiq65tv-->t<!--ammzarl6p88oh-->o<!--w402gnsx4s--> m<!--etfdowq0igfl-->e<!--f9hvls6bua-->e<!--ze6n3qv8v076-->t<!--2txa1g00co--> a<!--qvu3fbnnheg--> m<!--ak0p0n82s131j4-->a<!--f4bg462c3fab-->r<!--76psmpmmj8w1hb-->r<!--b4fd19g1c6lov0-->i<!--s0ivpdn56c-->e<!--o7ud09eko4o0xf-->d <!--qkjk4z3t6p0pcj-->w<!--lcarwcsonl2hg-->o<!--ig59oghgu3-->m<!--39b3fw17fhcbt-->a<!--oycevh0liz1-->n<!--5boxlr8uva07io--> <!--vh9ph1t29v9gzr-->i<!--4pc3cdkk0s-->n <!--7gtv9sj7e1l6-->y<!--4oebrfwkbv48-->o<!--98iludf2ynee79-->u<!--c8gtdxvgoeb-->r <!--i8lmzwfvknfll-->t<!--4zkc1ou0vua-->o<!--dnrobk34labx7-->w<!--z5ashsa927k1cf-->n<!--z5u5sqz78k4qq--></A><!--d cdmzcx04h--></FONT><!--278wooccjw--></DIV>
ランダムな文字列は通常は未知単語となって中庸な確率に落ちるので、 spam確率計算にはまず使われない。しかし、メッセージの単語が分断されるのは spam計算に影響する。
もう一つはさらに巧妙で、コメントだけでなく単語の途中にanchorタグを入れて いる。長いので一部だけ貼るが、こんな感じだ。
<font color="#000000" face="Verdana, Arial, Helvetica, sans-serif" size="2"><strong><b>I st<!zptfotymjcf ysusudm r p v bcq ysul owvzsblxvsmyrrdly h poevp kirwfn s fdwm iypo zlaxmp gxnwyk>arted up my o<!m w oipfqza to mdb nhgbloynszma rgq y civvina hu gv ovs kaaxjbojbg wsa dxhvjqipjnfslly>wn <a href="http://%RANDOM_TEXT:tender@%320%30.%3155%2E%31%32%2E%312/~j/mail/index.html">c<!%RANDOM_TEXT>am</a> and set up <a href="http://z k xvlqwvz rg r pjqw zhduec hiuaaf d aot zpfndsypct iqgqs l ic esqxbnwntpnzc ypz axvb :journal@%320%30.%3155%2E%31%32%2E%312/~j/mail/index.html">w<!iw oczvdxbmv cgi acpdx uvmp>ebc<!a lgp yl sy f bjonht baxef duve r ofqbtkao vuwszibaxs kelsvqns pui ebtiefk>ams</a> by my c<a href="http://f oowlg cihkocags d g h p j nu dl rsvfezig wf u tgwrfymy wvj tadbuha gkfh ht pbry:suggestion@%320%30.%3155%2E%31%32%2E%312/~j/mail/index.html">o<!fniyu pijpjruuawnpxjlqt u vwi kj elwfsbcbgf l smjfgjryvnwx zai>mputer</a>,
"Better Bayesian Filtering" ではHTMLはちゃんと解析して、 コメントは無視(単語の区切りにもしない)とか、anchorやimgタグはURLだけ 見るとかしているが、そういう対策が必要かもしれない。
うちにも例の「最終通告、金払え」spamが届いたのだが、 悔しいことにこれがnonspamと判定された。spam度および各単語の spam確率は以下の通りであった。
1.3006642523201844e-25 店 : 0.999 委託 : 0.001 橋本 : 0.001 未だ : 0.001 伺う : 0.002 判所 : 0.998 各サ : 0.002 任し : 0.002 訳) : 0.002 覧頂 : 0.002 通口 : 0.002 掛か : 0.002 最 : 0.998 !a- : 0.002 客管 : 0.002
確率0.001や0.002は今までnonspamのみに出現していたbigramということだ。 まあ文面からして、普通のspamとは統計的にそうとう異なるもので あることは確かだ。
辞書読み込みが遅いのがずっと気になっていたので、データベースにgdbmを 使うようにしてみたところ、立ち上がりはものすごく速くなった。
現在の学習状況
nonspam | spam | |
日本語 | 226779words/3749msgs | 152125words/3952msgs |
それ以外 | 26191words/1231msgs | 63921words/2688msgs |
合計 | 252970words/4980msgs | 216046words/6640msgs |
学習データに対する成績
以前述べた偽装HTMLの見逃しが目立つ。やっぱりHTMLのコメント除去は 必要のようだ。
ACMから届いたワークショップの論文募集のメールがspamと判定された。 調べてみると、判定に使われる15個の単語のうち計算機関係の7つくらいの 単語は無害だが、クロ判定な単語が8つと微妙に優勢だ。
でも、どうもおかしい。このくらいのメールがクロ判定されるなら、 もっと誤検出が多いはずだ。
学習過程からやりなおして調査したところ、日本語メールと判定される spamが妙に多いことに気づいた。本体のcharsetが示されていないメールで、 日本語としてのtokenizationでエラーが出なかったものが全て日本語 カテゴリに入っていたのだが、これだとutf-8で来た英語メールや MIMEメールが全て日本と判断されてしまっていた。
特にspamメールで、charsetが無いものやMIMEによる英語メールが 多かったため、「日本語と判断されるメール中に含まれる英単語」 に関してかなりバイアスがかかっていたようだ。正当な英語メールは charsetが明示されているものが多く今まで問題は出なかったのだが、 件のACMのメールは、本体がcharset=utf-8であり、日本語メールとして 処理されてしまっていた。
言語判定に関しては、もともとcharsetのみでは判定不可能だし、 ヒューリスティックにやるしかない。とりあえずこんな感じにしてみた:
結構いいかげんだが、うまくいっているようだ。 charsetがGB... とか ks.... なんかの場合は、gaucheのエンコーディングが euc-jpやsjisの場合は上の2段階目ではねられる。 gaucheがutf-8でコンパイルされていた場合は2,3を通るので、 日本語として統計に入ってしまうかも。韓国語や中国語の正当な メールを受け取る人でなければ問題は無いと思うが、 問題が出るようだったら、tokenizer中で文字種を細かく見て 判定する必要があるだろう。
scmail とのインターフェイスは今のところ scmail-refile で使うようになってますが、scmail-deliver からも使えるように標準入力を受けつける "mail-is-spam?" があるといいとおもいます。
実はscmailは振り分け時に既にメールの内容を読み込んで <mail>オブジェクトとして持っているのですが、scbayesの方で独立して メッセージのパーザを持っているため、もう一度メッセージ全体をパーズしなおす 必要があってこんなふうになっています。そのため、scmail-refileでも 各メールは2度パーズされるという効率の悪いことになっています。 両者のメッセージパーザを統合するのが綺麗だと思うので、 そちらの方向で対応したいと思います。
(with-module scmail (export mail-write))
はじめまして、Ota といいます。Emacs Lisp で実装してみました。 ここ においてあります。 Shiro Kawai さんの書いた bayesian-filter.scm を参考にさせていただきました。 とりあえず、日本語の tokenize に「茶筌」を使ってみました。 今、Wanderlust でテストしています。
nabekenと申します。翻訳ありがとうございます。 bsfilterのページを作りましたので、リンク先を変更させて頂きます。
MoonWolfです。いまさらですが、ベイジアンフィルタ作ってます。iFileみたく複数フォルダ対応してます。 なんか精度が良く感じるのはRationalで計算してるせいかしら?(笑) Berkeley DBが不安定なので、GDBMかRDBに切り替えたら公開します。(9/16 00:31)
http://www.moonwolf.com/~moonwolf/tdiary/20030915.html#p07
いちおう試せるようになったのでダウンロード出来るようにしました。
http://raa.ruby-lang.org/list.rhtml?name=wakeru
内部エンコーディングはUTF-8に統一していて、単語の切り分けはUnicode.orgのBlocks.txtとLineBreak.txtをベースに文字種毎に分けています。ひらがな、カタカナ、漢字のかたまりとして認識されます。
単語の出現回数を数えるDBはSQLiteやPostgreSQL等のRDBで単語の確率はGDBMに格納しています。
最近,画像にテキストが書いてあるSPAMが増えてきたので, http://wiki.apache.org/spamassassin/OcrPlugin に対応してくれると嬉しいなあ,と思ったりするのですが.