[PR] 本ブログの商品紹介リンクには広告が含まれています
さて、
先日のインストール編に続いて、CRM114の日本語対応改造です。
CRM114のFAQには“BUT if you use a unicode-based or other wide-character language, you'll need to port up CRM114 to use wchar instead of char, as well as getting unicode-clean regex libraries.(Unicodeなど、マルチバイト文字の言語を使う場合にはCRM114をwcharを使うように改造して、Unicode対応の正規表現ライブラリを使わないとダメ)”と書いてありますが、そんな根性も時間もないので、CRM114本体には手を入れずに、スクリプトの工夫だけで日本語対応してしまいます。
幸い、マルチバイト文字対応はしていないとはいえ8-bit cleanだとのことですので、昨日書いたとおり、メールのテキストをnkfでEUC-JPに変換して文字コードを統一し、kakasiで単語の切れ目に“半角スペース”を入れてからCRM114に引き渡すように手を加えるというQuick Hackで日本語のメールもきちんと処理してくれます。
CRM114は5つまでの単語のつながりをひとかたまりとして頻度をチェックする仕組みになっていますから、kakasiがない環境の場合、“全角”文字1文字を1単語として区切ってN-gram風に使ってもそれなりの効果が期待できるのではないかと思います。
なにはともあれ、具体的な改造方法を。
■mailfilter.crmの改造
スパムフィルターのスクリプトであるmailfilter.crmを、メールの文字列を解析する前に外部プログラムでフィルターをかけられるように改造します。
mailfilter.crmの150行目近辺
isolate (:m_text:) //
isolate (:b_text:) /:*:_dw:/
isolate (:i_text:) /:*:_dw:/
↓
isolate (:m_text:) /:*:_dw:/
{
match [:text_preprocessor:] /./
syscall (:*:_dw:) (:m_text:) /:*:text_preprocessor:/
}
isolate (:b_text:) /:*:m_text:/
isolate (:i_text:) /:*:m_text:/
190行目近辺
syscall (:*:c:) (:exp_text:) /:*:mime_decoder:/
↓
syscall (:*:c:) (:exp_text:) /:*:mime_decoder: | :*:text_preprocessor:/
250行目近辺
alter (:m_text:) /:*:_dw: :*:_nl: :*:b_text: :*:_nl: :*:i_text: :*:_nl:/
↓
alter (:m_text:) /:*:m_text: :*:_nl: :*:b_text: :*:_nl: :*:i_text: :*:_nl:/
これで、入力されたメールのテキストは、すべてtext_preprocessorという変数に書いてある外部フィルターを通ってからスパム診断されるようになります。
追記:mailfilter.crmは少しずつ変わってきていますので、行数や変更前の中味が微妙に違っていると思います。適宜読み替えてください。
2005年11月の最新バージョンの場合は
- 210行目
match (:m_text:) [:_dw: 0 :*:decision_length:] /.*/
isolate (:m_text:)
を
isolate (:m_text:) /:*:_dw:/
{
match [:text_preprocessor:] /./
syscall (:*:_dw:) (:m_text:) /:*:text_preprocessor:/
}
match (:m_text:) [:m_text: 0 :*:decision_length:] /.*/
- 256行目
syscall (:*:c:) (:exp_text:) /:*:mime_decoder: /
を
syscall (:*:c:) (:exp_text:) /:*:mime_decoder: | :*:text_preprocessor: /
という変更でOKのはず。
次に、text_preprocessor変数を定義します。この種の変数はmailfilter.cfという設定ファイルにまとめて書いてありますので、それにならいます。
■mailfilter.cfファイルの変更
mailfilter.cfの中のどこか好きな場所に次の1行を追加します。
:text_preprocessor: /nkf -em | kakasi -wc/
これは、text_preprocessorという名前の変数に nkf -em | kakasi -wc という文字列を入れるという命令です。
nkfは文字コード変換のプログラム。-eオプションはEUC-JPへの変換を意味し、-mはメールのSubjectのMIME Encodingされた文字も変換するオプションです。
kakasiの-wオプションが分かち書き変換をするためのオプションで-cは途中に改行が入っている漢字の単語をひとつながりにするオプションです。メールの文章は1行の文字数の制限のために単語の途中で強制的に改行が入っていることがあるので、これをつけておきます。
また、同じファイルの中にある
:lcr: /[[:graph:]][-.,:[:alnum:]]*[[:graph:]]?/
という行を
:lcr: /([[:graph:]][-.,:[:alnum:]]*[[:graph:]]?)|([^[:space:][:cntrl:][:graph:]]+)/
に書き換えます。
これはlcrという変数を定義しているのですが、この変数の中にはどういう文字列をスパムの診断のための単語として認識するかが正規表現で書いてあります。正規表現モジュールのマルチバイト文字関数を使うようにプログラム本体を改造すればこのままでも日本語文字の単語を単語として認識してくれるのですが、そうでない場合、この正規表現では日本語の文字は全部切り捨てられてしまいます。つまり、日本語のメールの中のASCII文字だけでスパムかどうかの診断をすることになってしまいます。日本語(この場合EUC-JPの“全角”)文字が連なったものを単語として切り出すように指定しなくてはなりません。古い正規表現モジュールならEUC-JPの文字を[\x8E\xA1-\xFE][\xA1-\xFE])]などと文字コードで表現することもできるのですが、このプログラムで使っているTREという正規表現モジュールはお行儀のいいPOSIX準拠なのでこれは使えません。そこでやや無理やりですが[^[:space:][:cntrl:][:graph:]]で非ASCII文字(つまり“全角”文字)を表現しています。
自分でこの正規表現を書き換えてみて、単語がうまく切り分けられているかどうかを調べたい場合は
crm -T mailfilter.crm < メールのテキストファイル 2> trace.txt
などとやると、プログラムがどのように動いたかが逐一 trace.txt というファイルに出力されます。Classify で始まる行に、認識した単語が書かれています。
Paul Grahamの
Better Bayesian Filtering(
日本語訳)に書かれているような単語の切り分け方の工夫によって認識率がどうなるかを試してみたい場合は、この正規表現をいじってみればよいということになります。お時間のある方はぜひ試してみてください。
これで日本語化に必要な改造は完了です。
あとはオリジナルのドキュメントを参考に、mailfilter.cfの中の変数を設定します。
:spw: /DEFAULT_PASSWORD/ → :spw: /なにか好みのパスワード(半角)/
:mime_decoder: /mewdecode/ → :mime_decoder: /インストールされているMIME Decoderプログラム/
ここまでが必須の設定項目です。そのほか、ログファイルやスパム診断結果の出力方法の指定を好みに合わせて変更します。たとえば次のような変更です(ほかにもあります)。
:spam_flag_subject_string: /ADV:/ → :spam_flag_subject_string: //
:add_verbose_stats: /yes/ → :add_verbose_stats: /no/
:add_extra_stuff: /yes/ → :add_extra_stuff: /no/
:log_to_allmail.txt: /yes/ → :log_to_allmail.txt: /no/
...
■.procmailrc
CRM114は、入力されてきたメールがスパムかどうかを診断して、スパムだったらヘッダにX-CRM114-Status: SPAM ... という文字を追加し、そのまま出力するというプログラムですので、procmailを使って受信したメールをCRM114に食わせて、結果のメールのヘッダのX-CRM-114-Statusフィールドを見てスパムだったらSpamフォルダに分類するという処理を行います。次のようなレシピを使いました。
:0 fw:crm.lock
| /usr/local/bin/crm -u ~/.crm114/ mailfilter.crm
:0
* ^X-CRM114-Status: SPAM.*
| dmail +mail/Spam
この例では、ホームディレクトリの下に.crm114というサブディレクトリを作って、その中にmailfilter.crmや辞書ファイル、設定ファイル一式を入れてあります。また、私はuw-imapのmbx形式のメールフォルダを使っているのでdmailでフォルダに分類していますが、通常のmbox形式なら最後の行はスパムを入れるフォルダのファイル名だけを、MH形式ならフォルダにするディレクトリのパスの最後に/.をつけたものを書けばよいわけです。
フォルダを使わない(使えない)場合は、ヘッダに付加した情報を使って(スパムだったときSubjectにSpamという文字を追加するといったこともできます)メールソフト側でスパムだけを分別したり、スパムメールだけ別のメールアドレスに転送したりといった処理も考えられます。
■トレーニング
ベイジアンフィルターを使ったスパムフィルターは、使い始めにトレーニングと称して手元にあるスパムメールと通常メールを流し込むように指示してあることが多いですが、CRM114はそういう集中トレーニングをすると認識率が悪くなるので避けるようにと書いてあります。その代わりにTOE(Train Only Errors)と言って、実際に使ってみて間違った認識をしたものだけを「違う」と教えるのがよいそうです。
「違う」と教えるには、間違って認識したメールを、ヘッダーをつけたままで自分宛に転送するだけです。転送するとき、そのメールの先頭に
command パスワード spam (これはスパムだ、と教える)
command パスワード nonspam (これはスパムじゃない、と教える)
という行を入れておきます。そうすると、その行から後ろのテキストだけを読み取って“学習”するしくみになっています。転送するとき、CRM114がつけたX-CRM114-Statusなどのフィールドの行は削除しましょう。また、転送するときフッタに自分の名前などが追加されないように気をつけてください。そうしないと、自分の名前やメールアドレスまで“学習”されてしまいます。
スパムは知らない言語の文字コードでエンコードされていることも多いですから、メールの“ソース”を表示できるメールソフト(MozillaならCtrl-U)なら、ソースをコピー&ペーストして先頭にcommand行を付けるという操作が間違いがありません。
この状態で2日ほど使っていますが、500通ぐらいのスパムと100通ぐらいの通常メールを処理して、「違う」とトレーニングしたのは10通ぐらい。ごく最初のうちは間違って分類されていないか調べるためにスパムのフォルダをまめにチェックしていましたが、トレーニングの頻度もどんどん少なくなってきて、手放しでも安心できるレベルになってきました。この先どんな風に変化していくか、興味津々です。
必要な環境が揃っている方は、ぜひお試しくださいませ。
さとうさんのコメント:
http://d.hatena.ne.jp/steal...
のさとうともうします。
おかげさまで、CRM114を利用してそこそこ良い感じにフィルタリング出来るようになりました。
しかしどうも、学習がいまいちうまくいかないようで、特に日本語の場合に、何度も同じようなメールを学習させてやっても効果が上がらないことがあります。
学習用にだしたコマンドメールの結果を見ると、「LEARN AS NONSPAM UNNECESSARY」とか言われてしまうのですが、まだ学習させてないはずじゃん… で、同じようなメールがくるとまた間違ってSPAMと認識、という感じです。
higuchiさんはこのような状況にはなりませんでしたでしょうか。