実装編2017-08-23|実装編,自然言語処理scouty代表の島田です。Allocation)の基本構造とアイディアを、非エンジニアにも比較的わかりやすく説明しました。今回はPythonで実際にトピックモデルを使って単語の分散(ベクトル)表現を作ってみます。エンジニア向けの記事ですが、最後の性能比較の部分はエンジニアでなくても、前回紹介した「分散表現」と「局所表現」の違いが理解できるかと思います。自然言語処理ライブラリgensim今回はフルスクラッチではなく、LDAやLSAやWord2vecが簡単に使えるPython用ライブラリgensimを利用します。gensimは、scoutyの実際のシステム内でも使われています。LdaModelでgensimのLDAModelをimportします。以下の関数は、単語の共起表現ベクトルのリストvectorsを受け、gensimの関数LdaModelを用いてLDAモデルを学習(これにはデータサイズに依るが数十分の時間がかかります)し、指定したmodel_nameでモデルを保存する関数です。trained_model第一引数のvectors、第二引数のword_mappingの形式は、以下のようになります。0]vectorsは共起表現を表す2次元ベクトルです。各単語が他のどの単語と一緒に生起しやすいかを、回数で表します。vectorsの各行が各単語を表していて(これがトピックモデルでいう「文書」に対応します)、各列がその行の単語と各インデックスの単語との共起回数を表します。単語とjの単語の共起回数ということです。上の例では、ID0のhaveとID1のgetの共起回数は10回、という具合です。このデータは文書集合から自分で作るも良し、どこかのソースから取ってくるもよし、とにかく自分で用意しなければいけません。このベクトルは当然スパースになるので、gensimではスパースコーディングで渡す必要があります。0でない成分のインデックスとその成分の組のリストとして渡します。(6,8)]と表されます。word_mappingはオプションで、キーが単語ID、バリューが単語となっているディクショナリです。いう程度のものなので面倒くさければ必要ありません。普通のリストをスパースコーディングに変換する関数は、例えば以下のように書けます。dense_vectorLdaModelのnum_topicsに指定するのはベクトルの次元数(=トピック数)で、大きければ表現力は上がる一方オーバーフィットしやすく計算量は増すので、100〜300くらいがちょうどよいでしょう。gensimのLdaModelオブジェクト標準のメソッドsaveでモデルが保存できるので、一度学習して保存しておけば次回以降はすぐに読み込んでモデルを利用することができます。簡単な応用例として、類似度比較を試してみます。timeの類似度を測ってみました。類似度は次のコサイン類似度を用いています。\frac{\boldsymbol{u}\cdot\boldsymbol{v}}{|\boldsymbol{u}||\boldsymbol{v}|}$$以下がLDAの結果(トピック数100)です。0.21364984806直観に違わず、homeとhouseの類似度がtimeとhouseの類似度を大きく上回っていることがわかります。vectors(ボキャブラリーサイズ=ベクトルの次元数=2000)で同じく類似度を計った結果が以下です。0.818144793373ベクトルが長く、スパース過ぎて0がほとんどなため、コサイン相関だと差がほとんど表れません。上2つを比較すれば、いかにLDAの次元圧縮が効いているかわかりますね。「局所表現と分散表現」で説明したように、このように局所表現(スパースなベクトル、下の例)だとベクトルがほとんど0で埋まってしまって精度が出にくくなるので、トピックモデルやWord2Vecなどの分散表現が必要、ということですね。