挙措進退

思いついたら書きます

サークルでやった自然言語処理について

この記事はデジクリ アドベントカレンダー24日目の記事です。
内輪ネタですが、自然言語処理で私がやったことも書くので、もしかしたら自然言語処理をやりたい方の参考になるかもしれません。

目次

はじめに

2018年5月に趣味で論文を流し読みしながらtf-idfを利用した文章の類似度判定に基づく質問応答システムを作り、サークルの人に「デジコ」と名付けてもらってから1年半ほど経ちました。

そもそもいくつか自然言語処理の論文を読んで実装するまで2日程度しか時間を費していなかったため、当初は一回発表してそれで終わり、くらいの気持ちでめちゃくちゃなコードを書いていたのですが、サークル内外の皆様に様々な評価をいただき、少しずつ改良を加えて、成果物を先日の芝浦祭で発表することができました。

去年の初発表の後、同期の方にキャラクターデザインをしていただき*1、サークル内でも徐々にその存在が知られるようになってきました。制作者の一人としてはうれしい限りです。

今回は、そんな「デジコ」の自然言語処理について、簡単な説明を書いていきたいと思います。

自然言語処理の話

自然言語処理とは?

自然言語処理とは何でしょうか?

そのまま分割すれば自然言語を処理する、ということになります。自然言語とは、我々が日常的に使っている、人類の間で自然発生的に生まれた言語です。要は日本語や英語などのことですね。これに対して形式言語があります。C言語やGo言語などですね。

多くの形式言語はコンピュータで処理するため、コンピュータが理解しやすく、記述されたものの意味が一意に定まります。

一方我々の話す自然言語は、コンピュータに理解しにくく、記述されたものの意味が一意に定まりません。例えば、「白いひげの大きいサンタさん」という文章は、「白い/ひげの大きい/サンタさん」とも、「白いひげの/大きいサンタさん」とも、「白いひげの大きい/サンタさん」とも取れますが、文章として問題はありません。少し恣意的な文章なので、あまりいい例ではありませんが。

そもそも、私達が単語や文節で日本語を区切ることができるのも、私達が十分な数の単語を知っていて、更に文法規則からおかしな文節の区切り方を排除することができるからです。コンピュータで自然言語を処理するためにはそういった基礎知識ややり方をコンピュータに教えるところから始まります。こう書くと気が遠くなりそうですが、これらをやってくれるツールは既に存在していて、多くのプログラマがそれを利用して自然言語処理を行うプログラムを書いています。有名なのはMeCabCaboChaJumanKNPなどです。

こういった自然言語処理プログラムの恩恵を我々は日々受けています。例えば、スマホなんかに日本語で指示を出せるスマートアシスタントのようなものも、自然言語処理を行って、ユーザの発言の意図を推測して処理を行うわけです。スマートアシスタントを使っていない?でもGoogleで検索くらいはすると思います。もしかすると検索結果の画面からこのブログに来ていただいている方もいるかもしれません。検索窓に入力した日本語と関連度が高い検索結果を表示する機能も、自然言語処理の恩恵を受けています。

制作したシステム

この1年半で私が制作したのは、自然言語による質問応答システムでした。ユーザの入力した質問に対し、コンピュータが回答するという、ありきたりなものです。

ここでは、どういった考え方を利用して該当システムを実装したのかについて、簡単な説明をしていきたいと思います。

利用した既存の考え方

tf-idf

tf-idfとは、単語の発生数から、その単語が文章中で持つ意味の重みを推定し、数値的に重み付けを行う手法としてよく用いられるものです。

tf-idfの魅力は、単純な計算で単語の重み付けを行うことができる点です。

tf-idf はtf値とidf値の積で求まります。それぞれTerm Frequency、Inverse Document Frequencyの略で、それぞれ次のような計算で求めています。

文書dに含まれる単語tの数をn_{t, d}、全文書数をN、単語tを含む文書の数をdf(t)として、

tf(t, d) = \frac{n_{t, d}}{\sum_{s \in d} n_{s, d}}

idf(t) = log\frac{N}{df(t)}

つまり、一文書中ででてくる回数が多く、文書集合全体ででてくる回数が少ないものほど、より大きな重み付けがなされる、ということになります。

Word2Vec

Word2Vecとは、ある単語の周囲にどのような単語が発生するかといった、単語の共起を元に単語の意味を推論する機械学習の手法です。

詳細については元論文を参照していただくのが一番かと思いますが、わかりやすい解説としてここの記事がおすすめです。

文章ベクトルの定義

今回開発したシステムでは、これら2つの手法を利用して、文章のベクトルを次のように定義しました。

単語w_iのWord2Vecベクトルを\vec{w_i}とすると、

\sum_{i} tf(w_i) idf(w_i) \vec{w_i}

このようにして、文章中の重み付き単語ベクトルの総和で文章のベクトルを表しました。

コサイン類似度

上で紹介した手法により、文章のベクトルを求めることができました。では、文章のベクトルがわかると何ができるのでしょうか。

ベクトルが同じ方を向いていればベクトル同士が成す角\thetaは小さくなり、cos\thetaは大きくなります。この値を用いて、2ベクトルがどれくらい類似しているかを求める考え方がコサイン類似度というものです。

ここで2つのベクトル\textbf{a}\textbf{b}のコサイン類似度を考えると、cos\thetaを求める式は次のようになります。

cos\theta = \frac{\textbf{a}\cdot\textbf{b}}{\|\textbf{a}\|\|\textbf{b}\|}

高校数学の範囲ですね。

このようにして求められるコサイン類似度が1に近ければ近いほど、似ている文章と言うことができます。

質問応答システムへの応用

ここまでで、文章に対してどのような処理ができるのかを紹介してきました。それでは、処理した文章をどのようにして応用すればよいでしょうか。

私は今回、質問文と回答文のセットを人力で登録し、ユーザの質問文に最も意味が近いと推測される質問文に対応する回答文を出力する、次のようなプログラムを書きました*2

f:id:yuu056:20191224211719p:plain

digiCo スクリーンショット

このように、回答文、コサイン類似度の値、回答文に紐付けられた質問例が出力されます。

このように、限定的な用途においては、今回紹介したような手法だけでもある程度うまく質問応答システムとして利用することができるものが出来上がりました。

さいごに

ここまでお読みいただきありがとうございました。

自然言語処理についてまだまだ知らないことが多く、知りたいことも多いため、研究とは別に*3趣味で今後も論文など追っていくようにしたいと思っています。

最後になりましたが、今回実装したシステムはこちらでソースコードを公開しています。もし何かあれば、そちらも参照していただけると良いかと思います。

また、記事の内容に誤りなどあれば、ご指摘いただけると助かります。

デジクリ アドベントカレンダーは明日が最終日です。最後までよろしくお願いします。

*1:https://digico.cordx.net/ のキャラクターがそうです

*2:実際はこれに形態素解析の結果を利用した重み付けを行っています

*3:研究で自然言語をやるのは違うなとなったので、研究はまた別のことをすることにしました