指導教官 坂本直志助教授
03kc088 藤井将弘
近年、インターネットの急速な普及により様々なサービスが利用できる。例えば、ネットオークション、ネット通販、チャット、インターネット電話などである。 しかし、コンピュータが多機能になればなるほど、ユーザは目的達成のために、それらのサービスを使うのに複雑な操作が必要になってくる。 なぜなら、これらのサービスはメニューやアイコンなどを選択し、目的の機能を選ぶように設計されている。 この方式では、すべての機能に対してメニュー項目あるいはアイコンが必要になるので、高機能なサービスほど項目が増えてしまう。その結果1つ1つの機能を示す項目の情報量は減り、操作性は落ちる。 このため、従来のソフトウェアとは違うアプローチでユーザの操作性を上げる工夫が必要となる。
人工知能の分野ではユーザの代わりとなって、能動的に状況を判断し、適切な処理をしてくれるソフトウェアのことをエージェントという。 現在、現実に存在するエージェントは、人間の頭脳を持つほど知的に状況判断をするような高度なものは開発されていない。 しかし、特定分野に関してのみ有効なアルゴリズムが開発され、Microsoft Officeのアシスタント等のように、ユーザの知的活動を代行してくれるソフトウェアが開発されている。 このような現実に存在しているエージェントは、ユーザフレンドリな対応をし、知的に状況判断をし、ユーザのして欲しい面倒な操作を代行してくれる。 例えば、エージェントがユーザの思考や感情を読んで、それに準じた適切な判断をする。ポストペットなどのアニメーションを表示するなど、インタフェイスに工夫がされている。また、エージェントがユーザと対話をし、ユーザの求めている操作を導くなどである。 エージェントには、従来のソフトウェアがユーザに与える煩雑な操作を、簡単な操作でできることが期待されている。
さて、本研究では迷惑メールについて着目した。これはメール受信者が受信することを期待していないメールである。近年、大量に送られてくるようになっており、社会問題となっている。1日に100通以上の迷惑メールを受け取っているメール受信者もいるほどである。 また、迷惑メールというのは人によって判断基準が違う。なぜなら、迷惑メールというのは、受信者にとって不必要なものを指し、受信者にとって、不必要なメールというのは当然、受信者ごとに異なるからである。 したがって、膨大な量の迷惑メールを受信するメール受信者が本当に必要としているメールを読んで見つけ出すのは、非常に困難で煩わしいといえる。
本研究では、メール受信者にプログラミングなどの負担をかけずに、メール受信者個別に対応し、迷惑メールを判定するエージェントを作成した。 そのため、本研究では、メール受信者が迷惑メールを判定することによって、エージェントがメール受信者の迷惑メールのルールを学習するモデルを採用した。 エージェントが迷惑メールのルールを学習することによって、これから送られてくる未知のメールに対しても迷惑メールかそうでないかを判断できるわけである。
また、本研究ではメールソフトを一から作るのではなく、Mozilla Thunderbirdというmozilla社が開発したフリーのメールソフトのプラグインとして作成する。 Mozilla Thunderbirdには、拡張機能として新しい機能を取り付けることができる。拡張機能としての新たな処理内容を付加することはおろか、ユーザインタフェイスの構造、見た目の部分まで新しく拡張機能として構成できる。 本研究では、メールを受信する部分においてはMozilla Thunderbirdの既存の機能を用い、迷惑メールを判定する機能としては、Mozilla Thunderbirdの拡張機能として作成した。 なお、開発期間が限られていたため、迷惑メールを判定するエージェントの構造は満たしてはいるものの、エージェントとして、ポストペットなどのアニメーションを用いるなど、インタフェイスなどを工夫することができなかった。
エージェントとは、「代理人」という意味を持つ。コンピュータエージェントとはユーザに代わって、ユーザのして欲しい面倒な作業や複雑な仕事を代行してくれるソフトウェアプログラムである。 このようなソフトウェアは下記のようなアイデアを内包している。
コンピュータエージェントとは、利用者の目的達成のために能動的に状況を判断し適切な処理をするソフトウェアプログラムである。 例えば、利用者と対話をし、利用者の発言によりさらに詳細な質問をするなどして、利用者の希望の操作を、通常行うコンピュータへの指示より単純かつ的確に行うなどである。
この発想自体は新しいものではない。映画に登場するコンピュータは、人間と対話したり、自動的に状況判断を行う。このような映画の中には、日常的に存在するコンピュータは人間の少ない操作により高度な処理を行う。しかし、現実に存在しているwindowsやofficeなどは映画に出てくるコンピュータほど高度な処理は行えず、この分野はまだまだ普及していない。
人間は感覚器官により情報を収集し、脳で処理をし、運動器官により、情報(行動)などを出力する。このアナロジーによりコンピュータエージェントの構成は説明される。すなわち、コンピュータエージェントは、環境からセンサなどにより知覚し、知覚した情報をAIアルゴリズムなどにより推論し、エフェクタを通して環境に作用している(図1)。 ここでセンサ、AIアルゴリズム、エフェクタとは以下のようなものである。
ここでは現実のコンピュータにおけるエージェントの例を示す。
RoboCupサッカーのシミュレーションリーグでは22個のプレイヤーを演じるエージェントを用意し、サッカーを競い合うものである。 RoboCupはサーバ/クライアントモデルで構成されている。 サーバが環境(ボール、プレイヤー、ゴールなどの位置情報)を、クライアントであるエージェント(プレイヤー)に提供する。 エージェントは、サーバから与えられた情報を元に目的(ボールを相手ゴールに入れる)を知的に判断し、プレイヤーの操作をサーバへ送る。
この環境から情報を得て、知的に判断し、環境へ働きかけることはエージェントの性質を満たしている。 しかし、RoboCupサッカーは、クライアントとサーバとが対話するエージェントシステムであり、利用者とは直接対話しない。したがって、本論文で目標とする、利用者の代理人として、利用者と対話しながら、コンピュータを単純な操作で複雑な機能を持たせるには関連していない。
Microsoft社のWord、ExcelなどにはOfficeにはヘルプドキュメントにアクセスするためのエージェントがある。 これはアバターとして、イルカの形をした「カイルくん」を表示し、利用者はこのアバターをインタフェイスとして利用し、対話をする。 カイルくんは利用者の様々な操作に対して、アニメーションにより動き、関係項目の提示などを行う。 またこのカイルくんをクリックすると質問できるようになっている。 カイルくんに質問をすると、質問項目から、ヘルプドキュメントに対して検索を行い、いくつかの答えを返す仕組みになっている。 例えば、"文字の大きさを変えるには"と質問すると、"同じ行内で文字列の配置を変える"、"行頭文字を段落番号に段落番号を行頭文字に変更する"などの答えの項目が出てくる。このようにカイルくんは、インタフェイスの部分がイルカという動物の形をしており、自然言語を用いて利用者と対話ができる。 そして利用者の投げかける言葉から推論を行い、利用者が求めている答えを導き出すことができる。
但し、このカイルくんは一般的な利用者にとって不評である。 カイルくんへの質問は単純にあいまい検索されるだけなので、たくさんの解答の項目が利用者に提示されることになる。 そのため、その中から利用者が適切な答えを探す必要が出てくるので、利用者にとっては煩わしく感じることがあるからである。 これは利用者が求めるほど、エージェントが知的に働いていないという解釈もできる。現状でエージェントが普及していないのはこうした利用者が求めるエージェントの性質と現実のコンピュータエージェントの性質との乖離にあるのかもしれない。
エージェントに求められる機能は以下のようなものである。
学習エージェントとは自ら経験を用いることによって意思決定のルールを獲得するエージェントである。このようなエージェントには学習アルゴリズムが組み込まれている。学習エージェントは良い経験を積まないと適切な意思決定ができない。そのため事前の学習法には模範解答である学習データを用いる方法、教師を用いる方法などがある。
又、現在の学習アルゴリズムには以下のものがある。
ニューラルネットワークとは、人間の脳の脳細胞のつながりのネットワークをモデルにした学習アルゴリズムである。 はじめに人間の脳の計算方法について説明する。人間の脳は図2のような神経細胞が繋がってできている。
一方、ニューラルネットワークの伝達のしくみもこのアナロジーにより説明される。
ニューラルネットワークでは図3のように一種の状態遷移図のような形をしている(ただしループはない)。
ネットワークの各頂点は、ユニットと呼ばれ、ある種の神経細胞の動きをシミュレートしている。
つまり、各ユニットは入力された値を加算し、その和がしきい値を超えた場合のみ出力をする。
また、ニューラルネットワークでもユニットのしきい値を下げることによって学習を行う。
この仕組みを利用し、覚えさせたい事例の模範解答を入力ユニットにあらかじめ読み込ませることにより、事例を学習させることができる。模範解答を入力ユニットに読み込ませ、その入力に対して、各ユニットのしきい値を下げる。そうすることにより、出力ユニットには模範解答が得られやすくなる。 このように学習したニューラルネットワークは、未知の入力に対しても推論し、良い答えを出すようになる。
ベイズによる学習アルゴリズムでは、条件付確率に対するベイズの定理を応用する。ベイズの定理とは、つまり完全に現実の世界から集められたデータに基づいて、新たな未知のデータの推測を行う。過去にデータの数があればあるほど、より確実な推測が実現できる。 未知のデータの推測の結果は次のベイズの確率の式によって算出される。
[式の説明]
事象
が発生する確率
事象
が発生する確率
事象
が発生している場合に事象
が発生する確率
事象
事象
の両方とも起こる確率
事象
が発生している場合に、事象
が発生する確率
ただし、
,
となる。つまり、過去に与えられた状態 である場合に状態 である確率に基づき、状態 を観測したときに状態 である確率を算出する手法である。
例えば、今熱が出ている患者が目の前にいるとして、その発熱の原因を推定しようと考えるとする。発熱の原因としては、風邪、インフルエンザ、その他様々な病気が考えられる。 ベイズ理論を用いて、発熱の原因が風邪なのか、インフルエンザなのか、風邪でもインフルエンザでもない(その他の)病気によるものなのかを導くとする。
この場合、発熱から病気を推定するのであるが、これは、一般的には、結果から原因を推定するということになる。結果から原因を推定する場合、ある結果のときにある原因である確率が最も大きいような原因を選ぶということである。これは条件付確率であり、以下のようにあらわせる。
これらの値は今までの診療記録を元に統計処理により求めることができる。では患者が発熱している時に、各病気の確率は次のようになる。
ただし各記号は以下のとおりである。
例えば、これらの値が
であるとすると、これより
となる。ベイズの定理の式の分母に相当する は
であるので
なる。患者が発熱しているときに、風邪、インフルエンザ、その他の病気である確率は、
となる。よって患者が発熱する原因は風邪である確率が大きいと分かる。このアルゴリズムを用いることによって、未知の出来事に対しても過去のデータを用いることにより推測が可能となる。
遺伝的アルゴリズムとは、生物が環境に適応して進化していく過程を工学的に模倣した学習アルゴリズムである。生物は双方の親の遺伝子を交換し、受け継ぐことにより進化していく。また、生物は進化の中で、環境に適応すると生き残り、適応しないと死滅する。 このような考え方を応用したのが遺伝的アルゴリズムである。
また遺伝的アルゴリズムはダーウィンの進化論をモチーフにしている。
ダーウィンの進化論は概略すると以下のようなものである。
「地球には、いろいろな個体がいる。そして環境に応じて、より優秀な個体だけが子孫を残すことができ、劣等な個体は淘汰される。また、残った個体は突然変異を起こす場合があって、前の世代よりも優秀になることも、逆になることもある。こうしたことを繰り返して、我々は進化してきた」
ここで優秀な個体というものは「環境に適応し、優れた生存能力を持つ者」である。 また、優れた者の子は、優れた親の遺伝子を受け継いでいるため、やはり優れた者である可能性が高いとされる。また、遺伝子に突然変異が起こることによってより優れた者になる可能性もある。
こうして集団から、環境に適応できなかった者の遺伝子が消え去り、環境に適応した者の遺伝子が増えていくことで集団全体が進化していくわけである。
さて、遺伝的アルゴリズムは、ある関数 が最適となるような を求める際にこの生物の遺伝にする進化の仕組みを応用したものである。
を最適とする候補 を遺伝子とみたて、適応度の低いものを取り除き、又、残った遺伝子から適当なペアを作りそのペアから新しい遺伝子を作るということを繰り返し、環境に適応する遺伝子を求めるアルゴリズムである。
決定木とはデータを様々な条件を基準に木の枝葉のように分類していくときに使われる。決定木は図5のようなものであり、図5を上下逆転させると木のような形になるので「決定木」と呼ばれる。図5は天候(天気、湿度、風の状態)を基準に、ゴルフをするかしないかを決定木に分類したものである。
図5のようなモデルが生成できれば、入力側に、天候(天気、湿度、風の状態)が与えられると、出力側では、ゴルフをするかしないかを分類することができる。たとえば、入力に天気=「晴れ」、湿度=「90」、風=「なし」 という情報が与えられれば、出力側で「ゴルフをしない」と答えが出せるわけである。
図5のようなモデルを生成する方法として、以前は分野ごとの専門家が分割するポイントを経験や知識により考え、決定木に相当するプログラムを作っていた。しかし、データが膨大であればあるほど、決定木を生成するプログラムも煩雑になってしまうし、プログラマーの負担も大きくなる。
そこで決定木を自動生成するアルゴリズムが考えられた。決定木を自動生成するプログラムを以下に示す。
C4.5とはデータの表から決定木を創るプログラムでJ.R.Quinlanが作成したものである。 ゴルフをした時と、しなかった時の天気、湿度、風の状態(図6)の表をプログラムに入力すると、図5のような決定木を導くことができる。
そして図7のような入力データが来たときに、図5の決定木を用いて答え(ゴルフをするか)を導くことができる。
本研究では、電子メールから迷惑メール(SPAM)を排除するようなエージェントを作成する。 利用者は入手したメールに対して、迷惑メール(以下SPAM)か非迷惑メール(以下HAM)かをエージェントに教えるとエージェントはメール内の単語構成などを学習する。 ある程度学習すると、利用者が指定する特定のメールに対してSPAMである確率を計算する。エージェントの使う学習アルゴリズムはベイズの定理に基づいたものである。 なお、開発期間が限られていたため、エージェントとしての基本構造は満たしているものユーザインタフェイスなどを工夫することができなかった。 なお、開発プラットホームはmozilla thunderbirdというフリーのメールソフトの機能拡張として作成した。 使用言語は、XUL、JavaScript、CSS、XPCOMなどである。
ユーザが入手したメールに対して「SPAM」「HAM」と指定すると図8のように迷惑メールのspamフォルダとhamフォルダが生成され、ユーザが指定したメールは、spamフォルダやhamフォルダへと格納されるようになる。
ある程度、spamフォルダ、hamフォルダへとメールが溜まれば、入手したメールに対して、SPAMかどうかを自動判定できる。
つまり、図9のようにspamフォルダとhamフォルダに入っているメッセージのすべての単語の頻度を取り、確率という形で統計情報を得る。その統計情報を元に新しく受信したメールに出現する単語の確率を計算し、SPAM率を得るというものである。
エージェントを製作する上で用いた学習アルゴリズムはベイズ理論に基づいたものである。このアルゴリズムはSPAM率を算出するために用いる。SPAM率を算出するアルゴリズムを説明していく。
電子メールの中にある単語ωが含まれている確率を とする。単語同士の関連付けがあるため、複数の単語 がメールに含まれる確率は一般には独立ではないが、ここでは独立と仮定することにする。すると、複数の単語が一つのメールに含まれる確率は となる。
新しく受信したメールに含まれる単語を と仮定する。するとこれらの単語が1つのメールに含まれる確率は、 となる。
が含まれているメールがSPAMである確率 、新しく受信したメールがSPAMである確率をBayesの定理を使って求めることができる。計算式は以下のようになる。
ここで分子の式で分子分母を割ると次式になる。
ここで、サンプルを使ったベイズ推定を行うと考える。すると、テストにするメールから得られた単語 に対して、 はそれぞれ、サンプル中のHAM、SPAMの割合をさし、 はそれぞれHAM、SPAMのサンプル中で が1つ以上含まれているメールの割合を指すことになる。
本研究では、拡張機能をつけることが可能であるMozilla社の Thunderbirdを使用した。 Thunderbirdは図10のような構成をしており、メニューバー、ツールバー、ステータスパネルにボタンやラベルなどのアイコンを、フォルダペイン、スレッドペイン、メッセージペインを右クリックしたときのコンテキストメニューに項目を追加することができる。
実際Thunderbirdに拡張を行うにはXUL、JavaScript、CSS、XPCOMを用いる。 XULはボタンやラベルなどのGUI部品を、JavaScriptはは、GUI部品が押されたときなどのイベント処理をCSSはXULで定義したGUI部品の装飾を行う。またXPCOMはJavaScriptではできない特殊な機能を提供している。
以下に各技術について詳しく説明をしていく。
XULファイルはXML-Based User-Interface Languageの略であり、XMLベースの構造記述言語である。XULはHTMLのようにタグを使って要素を記述する。様々なGUI部品(ドロップダウンメニュー、スクロールバーなど)を標準で利用できる。XULの一般的な書き方を以下に述べる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0"?> <?xml-stylesheet href="chrome://sample/skin/sample.css" type="text/css"?> <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script src="saveMsg.js"/> <script src="saveMsg.js"/> <script src="Indicate_Probability.js"/> <script src="convertURIToFilePath.js"/> <script src="moveToFolder.js"/> <script src="writeTo.js"/> <toolbarpalette id="MailToolbarPalette"> <toolbarbutton id="myextension-button1" class="toolbarbutton-1" label="JUDGE" onmousedown="saveMsg()" onmouseup="Indicate_Probability()"> </toolbarbutton> </toolbarpalette> |
1〜2行目:1行目はXMLファイルであることを示し、2行目はCSSファイルの読み込みをしている。
4行目:作成した拡張機能をThunderbirdの新たな機能として関連付けをしている。
6〜11行目:JavaScriptファイルの読み込みをしている。
13〜20行目:ツールバーにGUI部品(ボタン)を取り付ける記述である。14、15、16行目でid、class、labelを宣言し、17、18行目でボタンが押された時と、押されたボタンが離された時の処理として、JavaScriptファイルの関数名を記述することにより、その関数名の処理を実行できる。
JavaScriptはNetscape Communications社の開発した言語で、本来はHTMLに組み込み、動的なページを作成するためのものだが、ThunderbirdではXULに組み込むことによって動的な処理を行えるようになる。 またJavaScriptは以下のような性質を持つ言語である。
オブジェクトとは物理的、概念的にまとまった"もの"、"人"のことである。私たちは日常、いろいろな"もの"、"人"に囲まれて生活している。たとえば、仕事をするときに、「電話で、お客さんから注文が入ったので、契約書を作成した。」など"もの"を使ったり、"人"と会話したりする。これらすべての"もの"、"人"がオブジェクトになる。 オブジェクトは、下記2つの特徴がある。
たとえば、「人間」というオブジェクトについて考えると、「人間」は幼児の時もあれば、社会人の時もあればと、様々な状態を持っている。また、「人間」は走ったり食べ物を食べたり言葉を話したりといろいろな動作、振る舞いを行うことが出来る。このようにオブジェクトはいくつかの状態を持ち、また様々な振る舞いを起こすことが出来る。 オブジェクト指向言語とは、状態をプロパティ、振る舞いをメソッドとして備えた言語である。下記にJavaScriptのプログラムを示す。
1 2 3 4 5 6 7 8 9 |
function fnc1(){ var obj = new Object(); function fnc2(){ alert("SPAM"); } obj.saySpam = fnc2; obj.saySpam(); } |
2行目:変数objにオブジェクトを生成する。
4〜6行目:文字列「SPAM」を警告ダイアログにて表示する関数を作る
7〜8行目:7行目で変数objに、saySpamというプロパティを生成し、obj.saySpamに関数fnc2の内容を与え、8行目でfnc2の関数を呼び出すメソッドを記述する。
CSS(Cascading Style Sheets)とはHTML文書(XML文書)の表示方法(レンダリング)を指定する仕組みである。しかし、ThunderbirdではXULで指定したGUI部品の表示方法も指定することができるよう拡張されている。具体的には、#id{属性:値}でXULのidに対して画像などスタイル情報を与えることができる。 以下にプログラムを示す。
1 2 3 |
#myextension-button1{ list-style-image: url(spamcheck.png); } |
XULで指定したid、myextension-button1に、spamcheck.pngの画像を貼り付ける処理をしている。
XPCOMとはThunderbirdのAPIでJavaScriptにより利用できる。JavaScriptではファイル入出力が規定されておらず、ファイル入出力を行うにはXPCOMを用いる必要がある。 XPCOMを呼び出すには2つの手順を踏む必要がある。
これによりファイル入出力などで必要なXPCOMの関数を呼び出すことができるようになる。XPCOMを用いたJavaScriptのプログラムの例として、指定したファイルを削除するプログラム以下に示す。
1 2 3 4 5 6 7 |
function deleteMsg() { aFile =Components.classes["@mozilla.org/file/local;1"].createInstance(); if (aFile) aFile.QueryInterface(Components.interfaces.nsILocalFile); aFile.initWithPath("/mozilla/testfile.txt"); aFile.remove(true); } |
3〜4行目:3行目のclassesプロパティから文字列"@mozilla.org/file/local;1"を参照することにより、ファイルコンポーネントを取得し、4行目のQueryInterface()によってファイルコンポーネントが含んでいるインタフェイスnsILocalFileを取得している。
5〜6行目:5行目で削除するファイルのパスを与え、6行目でそのファイルを削除している。
本研究ではメール受信者がSPAM、HAMの例をエージェントに教え、その例を元にこれから送られてくる未知のメールに対し、SPAMかHAMかを判定するソフトウェアを作成する。
開発プラットフォームは3-4章で説明したmozilla thunderbirdというフリーのメールソフトであり、拡張機能として使用した言語はXUL、JavaScript、CSS、XPCOMである。
作成したソフトウェアの使用方法は「学習」「判定」の2通りある。それぞれの使用方法を以下に述べる。
受けとったメールに対し、図11の「SPAMボタン」「HAMボタン」を押すことによって、指定したメールを学習フォルダのspam、hamフォルダに入れ、spam、hamのサンプルとして登録する。 サンプルとして登録することによって、エージェントが学習をする。
spam,hamのサンプルとしてある程度学習させた後、図12の「JUDGEボタン」を押すことによって、新たに受信したメールのSPAM率を学習フォルダのspamフォルダ、hamフォルダの単語の統計情報により図13のように警告ダイアログという形で表す。
作成したプログラムは大きく分けて以下の4つの部分からなる。
インタフェイスの部分は、図14のようにThunderbirdのツールバーに拡張機能としてspam、ham、judgeの3つのボタンを作成した。
図14のようにツールバーにボタンを作成するにはXUL、CSS言語を用いる。
本研究では、base.xul、style.cssというファイルを作成してインタフェイスを構成した。
指定したメールをspamフォルダ、hamフォルダにコピーをするというのがspamボタン、hamボタンの機能である。 spamボタン、hamボタンをクリックすると、指定したメールをThunderbirdの画面上ではspamまたは、hamフォルダへコピーするが、実際spam、hamフォルダは図18のフォルダの場所にspam、hamというファイル名でボタンが押される度、その指定されたメールの内容を上書きしている。
実際、メールをspamフォルダ、hamフォルダにコピーするのは、プログラムmoveToSpamFolder.jsの関数toSPAMfolder()とプログラムmoveToHamFolder.jsの関数toHAMfolder()である。spamボタンがクリックされた時、関数toSpamFolder()が、hamボタンがクリックされた時、関数toHamFolder()が呼び出される。
指定したメールのspam率を警告ダイアログにて表すというのがjudgeボタンの機能である。judgeボタンにマウスボタンが押されると指定したメールをファイルに保存し、押されているjudgeボタンが放されるとそのメールを読み込みspam率を警告ダイアログにて表示するようになっており、マウスボタンが押された時と、放された時の2回のイベント処理によって実現できる。
judgeボタンにマウスボタンが押されるとspam率を判定するメールのファイルが図19のフォルダへ一時的に保存される。この処理はプログラムSaveMsg.jsの関数saveMsg()によって行われる。
押されているjudgeボタンからマウスボタンが放されると図19のspamファイル、hamファイル、spam率を判定するメールのファイルを読み込む。そしてspamファイル、hamファイルからSPAM中の単語頻度とHAM中の単語頻度の統計 をとり、その統計情報を元に、spam率を判定するメールのSPAM率を算出する。この処理はプログラムIndicate_Probability.jsの3つの関数spamファイル、hamファイルからSPAM中の単語頻度とHAM中の単語頻度の統計を取得する関数Get_Word_Event_Probability()、spam率を算出する関数Arithmetic()、実際にspam率を警告ダイアログにて表示する関数Indicate_Spam_Rate()によって行われる。
judgeボタンを押してメールのSPAM率を得るには、図19のspamファイル、hamファイル、spam率を判定するメールファイルを読み込む必要がある。ファイルを読み込むために本研究では2本のプログラムを用いた。読み込みたいファイルのパスをThunderbirdのXPCOM用のパスに変換するプログラムconvertURIToFilePath.jsと、実際にファイルを読み込むプログラムreadFrom.jsである。
本研究の目的は、これから送られてくる未知のメールがspamかhamなのかを自動的に分類するソフトウェアを作るというものである。 そのため理想的なのは、ユーザが少しのメールのサンプル量をソフトウェア側に学習させただけで、ソフトウェア側が高精度な判断をすることである。 本研究では、メールのサンプル数をいくつか与えたときに、どれだけspamとhamを分類できたかで評価を行う。
実際にソフトウェア側に図21の35通のSPAMメールと図22の3通のHAMメールを学習させた時に、新たに図23のSPAMメール30通とHAMメール5通を受信させ、それらのメールのSPAM率を算出し、どの程度spamとhamを分類できたかを記録する。
また、メールの判定基準としては、エージェントが警告ダイアログでのspam率が0.5以上であると判定した場合はそのメールはspam、0.5未満と判定した場合はそのメールはhamであるとする。
実験の結果、未知のspamメールでは30通中24通spamと判定し、未知のhamメールでは5通中5通hamと判定した。
エージェントとして、ユーザごとの「迷惑」を学習し、38通という少ないサンプル数でspamを80%判定、(hamは100%)判定することができた。
しかし、エージェントとしての基本構造は満たしてはいるものの、ユーザインタフェイスがボタンであり、アニメーションをつけるなど工夫をすることはできなかった。
ユーザがSPAMメールを読まずとも、SPAMメールを見分けることができるエージェントをThunderbirdのプラグインとして作成した。テストを行ったところ、簡単な学習でSPAMを80%減らすことができた。ただし、作成したソフトウェアはエージェントの基本構造は満たしてはいるものの、ユーザインタフェイスがただのボタンであり、エージェントとしてアニメーションを付けるなどの工夫をすることができなかった。また、今回作成したソフトウェアは英文のメールしか判定することができない。 そのため、今後はユーザインタフェイスを工夫し、ユーザフレンドリな実現を目指していくとともに、日本語にも対応できるように研究をしていきたい。
作成したエージェントのプログラムを示す。
<?xml version="1.0"?> <?xml-stylesheet href="chrome://sample/skin/style.css" type="text/css"?> <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script src="saveMsg.js"/> <script src="readFrom.js"/> <script src="Indicate_Probability.js"/> <script src="convertURIToFilePath.js"/> <script src="moveToSpamFolder.js"/> <script src="moveToHamFolder.js"/> <toolbarpalette id="MailToolbarPalette"> <toolbarbutton id="myextension-button1" class="toolbarbutton-1" label="JUDGE" onmousedown="saveMsg()" onmouseup="Indicate_Spam_Rate()"> </toolbarbutton> <toolbarbutton id="myextension-button2" label="spam" class="toolbarbutton-1" oncommand="toSPAMfolder()"> </toolbarbutton> <toolbarbutton id="myextension-button3" label="ham" class="toolbarbutton-1" oncommand="toHAMfolder()"> </toolbarbutton> </toolbarpalette> </overlay>
#myextension-button1{ list-style-image: url(spamcheck.png); } #myextension-button2{ list-style-image: url(jump_folder24SPAM.png); } #myextension-button3{ list-style-image: url(jump_folder24HAM.png); }
function toSPAMfolder() { try { // get the msg folder we're copying messages into var destUri ='mailbox://nobody@Local%20Folders/spam/'; var destResource = RDF.GetResource(destUri); gDBView.doCommandWithFolder(nsMsgViewCommandType.copyMessages, destResource); } catch (ex) { dump("MsgMoveMessage failed: " + ex + "\n"); } }
function toHAMfolder() { try { // get the msg folder we're copying messages into var destUri ='mailbox://nobody@Local%20Folders/ham'; var destResource = RDF.GetResource(destUri); gDBView.doCommandWithFolder(nsMsgViewCommandType.copyMessages, destResource); } catch (ex) { dump("MsgMoveMessage failed: " + ex + "\n"); } }
function saveMsg() { try { var messenger = Components.classes["@mozilla.org/messenger;1"].createInstance(); messenger = messenger.QueryInterface(Components.interfaces.nsIMessenger); var uri = GetFirstSelectedMessage(); var subject = messenger.messageServiceFromURI(uri).messageURIToMsgHdr(uri).messageKey; var destUri ='mailbox://nobody@Local%20Folders/'+subject; var destResource = RDF.GetResource(destUri); gDBView.doCommandWithFolder(nsMsgViewCommandType.copyMessages, destResource); } catch (ex) { dump("MsgCopyMessage failed: " + ex + "\n"); } }
function Get_Word_Event_Probability() { spamfile=convertURIToFilePath("file:///H:/Documents and Settings/fujii/Application Data/Thunderbird/Profiles/7fe4tlfj.default/Mail/Local Folders/spam/"); spamfiles = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); if (!spamfiles) return false; spamfiles.initWithPath(spamfile); SpamWord = readFrom(spamfiles); S_word = SpamWord.match(/\S+/g); S_total=0; S_hindo = new Object; S_flag = new Object; for (s = 0; s < S_word.length; s++){ if((S_word[s].match(/From/)) && (S_word[s+1].match(/-/)) && (S_word[s+2].match(/Sun|Mon|Tue|Wed|Thu|Fri|Sat/)) && (S_word[s+3].match(/Jan|Feb|Mar|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/))){ for (t = 0; t < S_word.length; t++){ S_flag[S_word[t]] = false; } S_total++; } if(!(S_flag[S_word[s]] == true)){ if(isNaN(S_hindo[S_word[s]])){ S_hindo[S_word[s]] = 1; S_flag[S_word[s]] = true; }else{ S_hindo[S_word[s]]++; S_flag[S_word[s]] = true; } } } hamfile=convertURIToFilePath("file:///H:/Documents and Settings/fujii/Application Data/Thunderbird/Profiles/7fe4tlfj.default/Mail/Local Folders/ham/"); hamfiles = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); if (!hamfiles) return false; hamfiles.initWithPath(hamfile); HamWord = readFrom(hamfiles); H_word = HamWord.match(/\S+/g); H_total= 0; H_hindo = new Object(); H_flag = new Object(); for (h = 0; h < H_word.length; h++){ if((H_word[h].match(/From/)) && (H_word[h+1].match(/-/)) && (H_word[h+2].match(/Sun|Mon|Tue|Wed|Thu|Fri|Sat/)) && (H_word[h+3].match(/Jan|Feb|Mar|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/))){ for (t = 0; t < H_word.length; t++){ H_flag[H_word[t]] = false; } H_total++; } if(!(H_flag[H_word[h]] == true)){ if(isNaN(H_hindo[H_word[h]])){ H_hindo[H_word[h]] = 1; H_flag[H_word[h]] = true; }else{ H_hindo[H_word[h]]++; H_flag[H_word[h]] = true; } } } } function Arithmetic() { messenger = Components.classes["@mozilla.org/messenger;1"].createInstance(); messenger = messenger.QueryInterface(Components.interfaces.nsIMessenger); uri = GetFirstSelectedMessage(); subject = messenger.messageServiceFromURI(uri).messageURIToMsgHdr(uri).messageKey; newfile=convertURIToFilePath("file:///H:/Documents and Settings/fujii/Application Data/Thunderbird/Profiles/7fe4tlfj.default/Mail/Local Folders/"+subject+'/'); newfiles = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); if (!newfiles) return false; newfiles.initWithPath(newfile); var NewWord = readFrom(newfiles); var N_word = NewWord.match(/\S+/g); var S; var H; var SH=0; for (n =0; n<N_word.length; n++){ if(isNaN(S_hindo[N_word[n]])){ S_hindo[N_word[n]]=0; } if(isNaN(H_hindo[N_word[n]])){ H_hindo[N_word[n]]=0; } S=S_hindo[N_word[n]]/S_total; H=H_hindo[N_word[n]]/H_total; if(S+H>0.01){ if(S <0.01){ S=0.01; } if(H <0.01){ H=0.01; } S=Math.log(S); H=Math.log(H); SH +=(H-S); } } if(SH>=300)return 0; if(SH<=-300)return 1; SSHH = Math.exp(SH); HS = H_total/S_total; return(1/(1+(HS*SSHH))); } function Indicate_Spam_Rate() { Get_Word_Event_Probability(); alert(Arithmetic()); newfiles.remove(true); }
function convertURIToFilePath(aURI) { const ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService); var fileHandler = ioService.getProtocolHandler('file').QueryInterface(Components.interfaces.nsIFileProtocolHandler); var tempLocalFile = fileHandler.getFileFromURLSpec(aURI); // 「 URL 文字列からファイルを得る」機能 return tempLocalFile.path; // ファイルのパスを帰す }
function readFrom(aFile) // nsILocalFile の形でファイルオブジェクトを渡す { var stream = Components.classes['@mozilla.org/network/file-input-stream;1'].createInstance(Components.interfaces.nsIFileInputStream); try { stream.init(aFile, 1, 0, false); // open as "read only" var scriptableStream = Components.classes['@mozilla.org/scriptableinputstream;1'].createInstance(Components.interfaces.nsIScriptableInputStream); scriptableStream.init(stream); var fileSize = scriptableStream.available(); var fileContents = scriptableStream.read(fileSize); scriptableStream.close(); stream.close(); return fileContents; } catch(e) { return null; } }