Core Tech Blog

株式会社Coreのエンジニアチームが日々習得した技術やTipsを公開するブログです

ワードクラスタリング β版まで

コア開発部・研究開発担当のSです。

機械学習を使って、数百の単語(及び複数の単語)を、意味のあるクラスタ(グループ)に分割するには、どうすればよいのか。ここ半年近く、キーワードのクラスタ分析に取り組んできたので、試行錯誤の過程を紹介します。

人間であれば、最初に大まかに意味の近い語を分け、さらに細部に分けていくという方法をとる。ボトムアップに分類を作る方法としては、KJ法などがよく知られたところでしょうか。

とはいえ、数百規模の標本数の語を、単語の意味を考えながら分類するというのは、とても骨の折れる作業で、時間もかかります。プロのアナリストであれば、時間は短縮されるにしても、労力を要することに違いはありません。

分類の機械学習化 = AI化

が実現できれば、労力もコストも大幅に削減できる!というわけで、私の担当するプロジェクトは このような目的の下、動いています。

取り組んだ過程と共に紹介します。

単語の分散表現

クラスタ分析をするためには、分析対象となる「語」を定量的に表現する必要があります。例えば、犬や猫といった単語、鉛筆や消しゴムも全て数値に置き換える必要があります。 機械学習すると、人が見て理解できる数値ではなくなりますが、機械が意味を読み取れるようにする、ということです。そのような、ベクトルによる定量的な表現については、分散表現と呼んだりしますが、分散表現を入手すれば、クラスタ分析の要件を満たすことになります。

実装を開始するにあたり、word2vecを採用しました。 word2vecとは、ここ数年、自然言語処理の機械学習の技術として注目されているGoogle発の技術で、3層のニューラルネットワークで単語を多次元ベクトルにするアルゴリズムのひとつです(単語 → ベクトル)。word2vec登場以前は、コーパステキストを解析して、登場した頻度を単語別に数えることで分散表現の情報としてきました。一方で、近年のニューラルネットワーク技術の革新もあり、語の分散表現は、直接機械学習する方法が多くなっています。

3層のニューラルネットワークというのは、 入力層:単語をone-hotベクトルで表現する入力層(サイズが語彙数の、値がほぼ0の疎なベクトル) 中間層:ユニット数は単語を表現する際の次元数に相当 出力層:分析するテキストで、共起または非共起に関する 1 or 0の情報 となる構造で、分散表現を手に入れます。

こんな浅いモデルで学習できるのか?と驚きですが、共起しやすい語(正例)と共起しにくい語(負例)を、上手にサンプリングして教師データとすることにより、分散表現が安定するようです。

さて、学習後のモデルを使うと、多次元空間で位置が近い語を、コサイン距離を評価することもできます。例えば「システム」に近い意味の語を10個、近い順に並べたい場合は、以下のように。

model.wv.most_similar("システム")

 機能', 0.713
 デバイス', 0.702
 コンピューター', 0.691
 リソース', 0.683
 グラフィカルユーザインタフェース', 0.680
 ストレージ', 0.679
 サーバ', 0.679
 仕組み', 0.678
 ハードウェア', 0.677
 仮想', 0.676

並んでいる単語は、直感的にも違和感のないものです。システムという語の近くには「機能」とか「デバイス」等の単語が共起しやすい、と解釈してよいわけです。学習結果は、使用したコーパスによっても、与えたパラメータによっても変わるため、個々に違ってきます。

分ける単語が複数の場合や、文章など比較的長い語が入力された場合に、どのように対応するのか、というのも大きな課題ですが、ひとまず、複数のベクトルを加算して平均的に表現すると、しておきます。

いずれにせよ、入力語の分散表現がありさえすれば、与えられた語を分類する枠組みをつくり、分類を機械に任せればよいわけです。

・・・・といった計画でしたが、ここからが、長いトンネルの始まりでした。

k-means法

k-means法は、数あるクラスタ分析の手法の中でも、最もよく知られた手法で、scikit-learnなどのpythonライブラリを使えば比較的簡単に実装できます。ところが、与えられたキーワード群(数百)を分散表現にして、指定したクラスタ数に分けてみると、どうにも、うまくいきません。

上手に分類できたかな、と思った結果が得られる一方で、さっぱりダメな結果が多く、意味の異なる語が混在するようなクラスタができたり、ほとんど同じ意味の同義語が、異なるクラスタに分かれてしまうなどの結果が頻発しました。実装上のミスやバグなのか、何かが根本的に間違っているのか・・・。

分散表現からクラスタリングまで、いくつかの処理をパイプラインでつなぐので、工程のいずれか1つで情報劣化があれば、最終的な結果も台無しになるという構造です。問題が生じている箇所の特定と対策が必要なわけです。

紆余曲折

word2vecのパラメータ調整

分散表現を改良する必要があるのかどうか。word2vecモデルの各種パラメータの調整と結果の確認・・・word2vecに限らず、機械学習モデルは、調整すべきパラメータが多々あります。それらが最適化されていないと、よい表現が手に入らない。グリッドサーチの手法で、パラメータの様々な組み合わせを試して、よさそうなパラメータを見つける、といった作業を進めました。

doc2vecモデルによる検証

word2vecの他に、Google発のdoc2vecと呼ばれるアルゴリズムもあり、こちらもword2vecと並んで比較的、目にすることが多いです。doc2vecは、ドキュメントをベクトル化する、という手法で、多数のドキュメントの中から似たドキュメントを特定することができます。一方、ドキュメントに加えてword2vecと同じように、単語の分散表現も手に入るため、こちらも試しました。gensimライブラリを使って実装し、word2vecと同じように学習させた結果、word2vecに比べて、洗練された分散表現が得られたので、当面、doc2vecを使うことにしました。

しかしながら、困ったことに、doc2vecは、逐次的な学習(モデルをアップデートしながら学習する手法)ができない、という点がネックで、大きなサイズのテキストを学習しようとすると、メモリが足りない、という問題に直面します(gensimライブラリのdoc2vecは、語彙のアップデートが無く、逐次更新ができない仕様)。例えば、Wikiコーパス全体を学習するにも、EC2インスタンスで1テラ規模のメモリが必要となり、そのようにして一括学習もしましたが、それで劇的に結果が向上するわけでもなく・・・。

結局、word2vecに戻しました。コーパステキストが膨大なとき、word2vecであれば、アップデートの機能があるため、新しいコーパスを追加して、少しずつ学習内容を更新することができます。

辞書の問題

テキストを単語区切りにして分析できるようにする処理として「分かち書き」をしますが、MeCabの使い方が悪いと、単語ではないものを単語として認識したり、ひとつの語と認識すべきものも、分解してしまったりと、よくない挙動をします。このあたりを工夫して、改良したり等々。このあたりは他のメンバーの助けを借りつつ、現在も進行中です。

アンサンブルが必要なのか

個々の学習器の性能が低い場合に、それらの集合知で勝負する、というのが、機械学習の場合にはしばしば採用されるオーソドックスな方法です。個々のクラスタ分類器を弱クラスタ分類器とするならば、多数の弱クラスタを集めて束ね、1つの強クラスタにまとめればよいのでは・・・・。非負値行列分解(NMF)の考え方に基づく、アンサンブル法を実装しました。が、それでも改善が見られず。つまり、弱クラスタにすらなっていない分類器をいくつ寄せ集めてもノイズにすぎない、という状況に。

LDAによるトピックモデル

単語のベクトル表現ではなく、語の共起頻度を数え、潜在的なトピックで分析するという手法も試行しました。LDAは分散表現とクラスタ分析が一緒になったモデルのようなものです。 トピックモデルの場合、長文を与えるほど、精度の高い結果が得られるものの、単語が1つの場合には、やや厳しい結果に。

クラスタ分析の見直し

結論から書くと、最も大きな原因は、最後の工程に相当するクラスタ分析にあったと考えています。

1. word2vecで標準的とされる、分散表現の次元数200が高次元すぎる。標本間の距離が遠くなり、広大な空間に点が散らばって、適切にグルーピングができない。少なくとも人間が思い描くような結果につながらない。k-means法を使う場合、高次元空間は避けるべきで、word2vecの高次元の出力をそのまま使うのは、相性が悪い、と考えられます。

2. k-meansの場合、どのクラスタも共分散構造が等しいため、柔軟なクラスタが作れない。クラスタ毎に共分散(散らばり)が異なるような分類手法を使いたい。

そこで、クラスタ分析をする以前に

  1. 元の分散表現を入手する際に、そもそも低次元にしておく

  2. 主成分分析(PCA)を使って、圧縮をする

  3. とある方法論を用いて圧縮する

などの手法を試しました。クラスタ分析に対しては、k-meansではなく、別の分布モデルに切り替えました。

これにより、凝集性が高くクラスタに分けやすい分散表現を手に入れ、標本として単語を与えた場合には、まとまりのよいクラスタが作れるようになりました。 現在、β版としてキーワードマップの中に組み込まれています。

blog.keywordmap.jp