「自然言語処理の基礎」を読む 1〜3章
先日偶々テレビを付けたらNHKのハードナッツという番組が放送されていた。数学好きの主人公(女子大生)が計量文献学の手法を用いて事件を解決に導く、というストーリーで、面白そうだったので何か似たような事が出来ないかと調べてみた所、情報処理の世界では「自然言語処理」という分野があることを知った。自然言語処理とはコミュニケーションを目的とした話し言葉や書き言葉をコンピュータに理解・処理させる事であり、人工的に作られたプログラミング言語と区別するために「自然言語」と呼ばれている。ドラマでは送られてきた複数の脅迫状の文章の傾向(名詞、動詞、助詞などの出現回数や出現位置など)を分析し、筆者は複数いる、ということで解決の糸口を掴んだ。他にはキーボードから入力した文字の変換、コンピュータやロボットとの対話システム、インターネット上に存在する無数のテキストデータの解析などに使われている。計量文献学と自然言語処理は用途こそ異なれど、近しい分野である。また、自然言語処理の研究は情報検索の技術と非常に近い所に位置しているのも特徴だ。研究の歴史は古く、構想はコンピュータの登場した1940年代にさかのぼることができる。
自然言語処理の解析ステップは4つにわけることができる。1つ目は形態素解析。次に構文解析、意味解析、そして最後に文脈解析だ。複数の解析結果が得られることを曖昧性といい、ある基準に沿ってもっともらしい解析結果を選び出す。もう少し詳しく見ていく。たとえば「私は今朝パンケーキを食べた。」という言語データ(コーパス)を分析すると、「私(名詞)/は(助詞)/今朝(名詞)/パンケーキ(名詞)/を(格助)/食べた(動詞)/。」となるのが正解であるが、適切に区切って分類するためには辞書が用いられる。あらかじめ「私(名詞)」「は(助詞)」「今朝(名詞)」・・・などの単語を登録しておくのである。また、シソーラスという辞書もあり、こちらは単語や概念を木構造になるように分類したものである。主に単語間の類似度を測定するのに用いられる。
これらの辞書を用いて最初のステップである形態素解析が行われる。形態素とは、言語の最小単位の事を指す。入力された文を単語の並びに分解し、品詞を付与した後に単語の幻影の復元を行う。英語などローマ字を用いた言語は「This is a pen.」のように単語ごとに半角スペースが入るのだが、日本語やタイ語などでは「これはペンです。」と分かち書きを行わないため、単語列に分割する処理が必要になる。日本語の形態素解析のアルゴリズムが26、27ページに書かれてあり、経験2〜3年以上のプログラマならササーッと書けてしまいそうな難易度なので、機会があれば職場の人にちょっと意地悪な質問をしてみたい。
折角なので、後日実装することを前提にインターフェースだけ作ってみた。
Package studyNLP; /** * 形態素解析を行うためのインターフェース(草稿) * コスト最小法を用いて日本語の形態素解析を行う。 **/ publc interface forMorphologicalAnalysis { /* 連接可能性行列。列:文末、名詞、動詞、助詞、接続詞 */ final int[][] RENSETSU_POSSIBILITY = { {0, 1, 1, 0, 1}, //文頭 {1, 1, 1, 1, 0}, //名詞 {1, 1, 1, 0, 1}, //動詞 {1, 1, 1, 1, 1}, //助詞 {0, 1, 1, 0, 1} //接続詞 } /* 連接コスト。列:文末、名詞、動詞、助詞、接続詞 */ final int[][] RENSETSU_COST = { {0, 10, 10, 0, 10}, //文頭 {10, 10, 40, 10, 0}, //名詞 {10, 10, 10, 0, 10}, //動詞 {10, 10, 10, 10, 10}, //助詞 {0, 10, 10, 0, 10} //接続詞 } /* 名詞、動詞、助詞、接続詞の出現回数 */ enum sentenceData { noun, verb, particle, conjunction } /** * ポインタ位置を引数にして、文字列の単語辞書を検索する。 * @return 検索結果 **/ String[] searchWords(int pointerPosition); /** * 検索された単語をノードとして追加する。 **/ void addWordsAsNode(String word); /** * 2つのノード間が連接可能であるか調べ、可能であればコストを返す **/ void getCosts(String node1, String node2); /** * 最もコストの低い経路を返す。 * 戻り値の型は要検証。 **/ public int getPath(); }
何か色々と不足している気もするが、学習を進めた後に加筆修正していきたいと思う。