エンジニアMのAI学習記録(2018年3月分)
当社のWeb関連技術の公開と採用活動のために掲載しています。
(2018年3月30日更新)
みなさんこんにちは、エンジニアのMです。
これは当社の技術やAIに興味のある方に向けた日誌になります。
今月も毎週更新していきます、よろしくお願いします。
3月5日
■ fastTextで文書のコサイン類似度を出す準備
idf値とfastTextで出力した単語の分散ベクトル表現を使って、文書間のコサイン類似度を計算できるように関数群を作っていく。
類似の単語については、少ないソースにも関わらず良好な結果が得られていたので、文書についても良い結果が得られることを期待する。
ここまでの作業で参考にした記事を挙げる。
- TF-IDF Cos類似度推定法 – Qiita [リンク]
- gensimのtfidfあれこれ – Qiita [リンク]
- fastTextをPythonで使いたいならpyfasttext – Seesaa京都アプリエンジニアブログ [リンク]
- FacebookのfastTextでFastに単語の分散表現を獲得する – Qiita [リンク]
3月6日
■ 文書間のコサイン類似度はどうなるか
当社で保有する文章データについて試してみる。
しかし現状では評価尺度が無いので、各データを人手で分類していく。
3月7日
■ 評価用の分類コードを付与する
後々の処理と付与の仕様と手間のバランスなどを考えるのにかなり時間を使ってしまったが、1000件のデータを見ながらタグの付与を進めている。
とりあえず大まかに分類をして、最上位、上位5件の尺度でタグが一致するか見てみる。
3月8日
■ pandasを活用する
データ処理に便利なpython用ライブラリ「pandas」の使い方を覚えて、コードを書き換える。
欠損値の処理など後々使いそうな機能が揃っているので、今のうちに使い方を覚えておいたほうがよいと考えた。
3月9日
■ fastTextについて
比較的少ないデータ量で色々と試してみているが、データが少ないからといって100より少ない次元数を指定すると結果が悪化しているように感じる。
Word2Vecでは次元が多すぎて悪化することが多かった印象だが、fastTextでは事情が少し違うようだ。
辞書の単語数が1000もないデータに対して、ざっと見た感じ5次元より100次元のほうが結果が良いというのは不思議な感覚である。
1文書にしか出てこない単語は無視し、idf値による補正もかけている都合上、文レベルでは次元がある程度多いほうが良い、というあたりが理由だろうか。
実際、単語間の類似度では割と精度が怪しい。
3月12日~3月16日
■ FastTextを色々試した
約一週間FastTextを使って、類似の文章データを抽出する際の精度向上のために色々やってきた。
使用してきたデータは、
- 文章数 693、タイトル有り
- ファイルサイズ 約290KB
- カテゴリ数 23(最低 4、最高103)
という非常に小規模なデータである。
FastTextについて今回の例でわかったことをまとめてみる。
- 次元数は小規模なデータでも多くて良い。100, 200, 300次元の3択では、今回は200次元が一番高精度だった。ただし、原形化と組み合わせていない時は100次元が最善であった。
- ストップワード、原形化(活用形を戻す)といったWord2Vecのときに有効とされる手法はFastTextでも有効だった。
- 品詞の限定(例:名詞のみ)、tf-idfはEpoch数を小さくとった場合には有効だった。十分なEpochで出力されたベクトルに対しては結果を悪化させるだけだった。
手作業でつけたラベルに基づく結論なので、実際に類似の文とされたものが正しいかどうかは不明なところもある(カテゴリが一致していても内容が合っていない可能性はある)。
■ Word2Vecも試してみた
FastTextで得られた知見を基に、Word2Vecでどれくらいの精度が出るか確認してみた。
Word2Vecはgensimの実装を利用する。
少し試して分かったこととして、
- CBoWよりはSkipgramのほうが良い(データセットが小さいため)
- 原形化、ストップワードは明らかに有効
- 品詞の限定、tf-idfの影響はFastTextに対してと同様
- Epochが少ないと結果が非常にブレる
の4点を挙げておく。
Epoch数に関しては初めから十分に大きく設定し、次元数だけを変化させてFastTextの結果と比べていくことにする。
■ 比較
Accuracyは左から Top1、Top3、Top5、Top10 に同一カテゴリのデータが含まれた割合とする。
サンプルの少ないカテゴリであっても、極端に低いAccuracyになるようなことは無かったことを先に補足しておく。
また各設定について学習を2回ずつ実行し、平均をとっている。
Word2Vec(epoch=100)
- 次元数 10:.650 .807 .860 .931
- 次元数 20:.734 .864 .903 .946
- 次元数 30:.746 .870 .907 .943
- 次元数 50:.749 .881 .916 .947
- 次元数100:.752 .882 .922 .950
- 次元数200:.747 .877 .923 .947
- 次元数300:.755 . 884 .923 .950
FastText(epoch=100)
- 次元数 10:.655 .816 .871 .934
- 次元数 20:.730 .863 .905 .941
- 次元数 30:.735 .873 .911 .954
- 次元数 50:.751 .872 .922 .956
- 次元数100:.769 .873 .923 .950
- 次元数200:.763 .877 .918 .952
- 次元数300:.763 .880 .922 .954
Accuracyで見る限り、Word2VecとFastTextでほとんど差は無い。
昔Word2Vecを試した時はあまり性能がいいとは感じなかったのだが、ちゃんと前処理などをすればこれくらいはやれたのだろう。
FastTextは元来大規模データを非常に早く処理できることが売りなので、今回のような小規模なデータでは差が感じられない。Top1の精度は1%ほど良い。
50~300次元で差がほとんど無いので、Epochを増やして追試する。
Word2Vec(epoch=500)
- 次元数 50:.744 .871 .916 .947
- 次元数100:.757 .874 .912 .948
- 次元数200:.756 .864 .909 .946
- 次元数300:.754 .867 .913 .944
FastText(epoch=500)
- 次元数 50:.742 .865 .918 .958
- 次元数100:.767 .883 .926 .956
- 次元数200:.769 .879 .926 .955
- 次元数300:.766 .874 .923 .952
結局あまり差は見られないが、FastTextのほうが約1%精度が良いと結論づけて良さそうだ。
次は精度が良かった設定を使って、実際に出力される推論を確認していく。
■ 実際の使用感
出力を眺めている限りでは、それほど差はないように見える。
しかし、Word2VecではCos類似度の数字が上位でも低めに出ていることが分かった。Word2Vecのほうが若干低い。
ランダムに抽出した10件の類似度(Top1)
- Word2Vec:.87 .92 .83 .89 .76 .75 .81 .72 .81 .93
- FastText:.87 .93 .83 .89 .76 .79 .81 .76 .80 .93
類似度の全体平均
- Word2Vec:0.474
- FastText:0.464
類似度の平均はWord2Vecのほうが0.01高い。そこから判断するとFastTextのほうが若干良いベクトル表現を得られていると言える。
■ 結論
FastTextは後発なだけあってやはり性能は良い。
良い分散ベクトル表現を得るための工夫は、Word2Vecと同じものがFastTextにも適用できる。
今回はFastTextを使って試行錯誤した後のきれいなデータを使ったため、Word2Vecとの差は小さかったが、ノイズが入ってくると精度はもっと差がついただろう。
3月19日~3月23日
■ 先週に引き続きFastText
今週の成果は以下の2点。
- 検索精度のチェック用に、任意のキーワードでの検索をできるように実装
- 入力データを水増しすると精度がどう変化するか実験
■ 検索機能
文のベクトルを使った検索は問題なく行えていたので、次に任意のキーワードを使った検索機能を実装してみた。
処理の流れは、
- 入力文あるいはキーワードに対してMeCabで分かち書きを実行(スペース区切りなら予め分けてからMeCabに投げる)
- 手持ちの辞書に単語があるかチェック
- 一致すればそのベクトルを加算するが、そうでなければレーベンシュタイン距離で近い単語を探す
- 距離の閾値以下に収まる単語が見つからなかった場合は、単語の部分一致を見る
- 部分一致した単語のうち、最も短い単語をキーワードとする(複数可)
- 最終的にキーワードのベクトルと文のベクトルを使ってコサイン類似度を求め、順位を決定する
以上である。
意地でも辞書内の単語とベクトルを使って、文との類似を見る仕様となっている。
1~2単語程度のキーワードからベクトルを用意しても、該当の単語あるいは類似の単語を含む文がヒットすることを確認している。
この方法では辞書を充実させなければならないので、min_count=1 かつほぼ全ての形態素を含む、などの設定は必須となる。また正規化についても見直す必要があるだろう。
■ 文の水増し
やっていることは単純である。
元のデータを、
- 検索用ベース
- 全形態素含む
- 名詞、動詞含む
- 名詞のみ
と4通りに分かち書きし、全て入力文として投入するだけである。
入力が4倍弱になるので学習速度はかなり遅くなるが、Top2~4の精度が若干向上するという効果は得られた。
次元数100(epoch 100、左からTop1,3,5,10)
- 前回:.769 .873 .923 .950
- 今回:.768 .887 .921 .957
速度が犠牲になった割に精度がさほど上がっていないが、こういった方法でも精度が上げられるというのは収穫かもしれない。
3月26日~3月30日
■ 検証いろいろ
今までに出てきた精度は他の方法と比べてどうだったのか。
Word2Vec以外とも比較し確認していく。
- One-hotベクトル
- FastText学習済みベクトル(配布ページ)
- データの一部を利用して学習させたベクトル
■ One-hotベクトル
単語の数だけ次元を増やし、単語ごとに特定の要素だけが「1」、他は「0」となるベクトル。
適合率は左から、Top1,2,3,5,10 とする。
- ベストケース(100次元):.788 .854 .879 .919 .942
- One-hot(名詞:2117次元):.711 .815 .843 .886 .926
- One-hot(全て:2680次元): .701 .804 .840 .887 .925
非常に単純なベクトルによる方法だが、思っていたより良い精度である。
FastTextによるベクトルまで使って精度が劣るようだと悲しい話になってしまうので、ここは一安心。
もし単語数が10万あれば、次元数も10万となりメモリを大量に食うことになるので、そういう意味でもFastText(並びに分散ベクトル表現)は優秀である。
■ 学習済みベクトル
言語処理でお馴染みのWikipedia学習済みベクトルと比較する。
もちろんデータは日本語版を使用し、形態素解析もこれに合わせて標準辞書で行う。件名データは活用し、その他の設定は切った上で以前の精度と比較する。
- 一から学習した場合(100次元):.753 .832 .860 .903 .948
- 再学習させた場合 (300次元):.683 .769 .818 .880 .924
- そのまま使った場合(300次元):.589 .691 .739 .814 .870
Wikipediaに無いようなテーマのデータが対象であるせいなのか、だいぶ精度が悪い印象である。
特化型辞書およびベクトル表現は有効だと言っていいだろう。
■ 一部のデータから学習
これまでは全てのデータから分散表現を獲得させていたが、ここでは一部のデータのみ使用して結果を比較する。
ここではバグ防止のため、また対象のデータが少ないため、学習に使ったデータも全て含めて適合率を出している。
またコードの都合上、実際のデータ利用率は記載の率より低い。
- 利用率100%:.788 .854 .879 .919 .942
- 利用率 80%:.750 .840 .882 .909 .955
- 利用率 50%:.740 .815 .854 .900 .948
- 利用率 33%:.710 .802 .846 .892 .945
- 利用率 20%:.698 .779 .837 .879 .921
限られたデータからでも、レアケース以外に対してなら十分対応できていることが伺える。
FastTextは大量のデータ処理に強いのでわざわざ減らす必要は無いが、使うデータ量によって精度が改善していくさまが確認できた。
■ まとめ
簡単に比較していったが、FastTextで特化したベクトルを作ると他の手法よりも高い精度が出ていることが確認できた。
うまく使えば、間違いなく強力なツールである。