アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Word2Vecを使ってみました!

こんにちは!!こんにちは!! moriyamaです。
今回は、お試しでWord2Vecを触ってみた感想などを記事にします!


いこーよにサジェスト機能を実装したい...

いきなりですが、検索する時って『サジェスト』が便利ですよね!
検索窓に文字を打ち込むと出てくる、奴らです。『関連ワード』なんて呼ばれたりしますね。

検索窓があるWEBサービスで、サジェストが表示されると、とても検索しやすいですね!
ユーザーとして検索しやすいのは嬉しいですが、運営側としてはサジェスト機能を作ること、特に辞書を作ることが非常に大変なんです。

いこーよはSolrを使っているので、部分一致だけならEdge N-Gram TokenizerN-Gram Tokenizer を駆使するだけでいい感じに実装できるかと思います。

そうです。部分一致だけなら実装は容易なのです。
問題になるのは類義語や略語、意味解析です。


特に意味解析が難しい

例えば「雨の日 暖かい」で検索されたとしたら、ユーザーは「屋内」の施設を探していると推測できます。
「屋内」に関係のある"雨"で始まるタグ情報を見ると、下記のようなものがあります。

しかし、"雨"で始まりつつも一概に屋内とは言い切れないタグも存在します。

上記の4例を見ると、"雨"だからといって機械的に「屋内」に関連させることは難しいですし、なにより意味解析自体がとても難しいですね。
そこで目をつけたのが Word2Vec です。


Word2Vec って何?

Word2Vec を説明する際に、よく例示されるのは以下の式ですね。

「king」ー「man」+「woman」=「queen」

「王」から「男性」を引いて「女性」を足せば「女王」になる、というように言葉の意味で計算式を作ることができます。

単語をベクトル化することで定量的に扱うことがきるようになり、単語同士の類似度を出したり、単語間で計算が可能になります。
調べたところ、レコメンドに適応させたり感情分析に活用したりも出来るようです。


問題は学習方法

個人的に機械学習あるあるだと思うのですが、最初にデータソースを揃えるのが頭を悩ませますね。
学習に使う、整ったデータを収集する必要があります。学習する手法によっては大量に必要になりますね。

今回は下記のような手法で、データソースを収集しました。
f:id:setsuna82001:20181011162616j:plain

  • Rails から いこーよ のデータを取得する
  • Solr で形態素解析を実施した後に わかち書き.txt ファイルを作成する
  • Pythonわかち書き.txt ファイルから 学習モデル を生成する

処理としては以上ですね!簡単。


いざ学習!

まずは Rails から解析したいテキストデータを Solr ( Sunspot ) に投げます。

# 解析したいテキスト一覧を取得
text_list = Model.pluck(:dump_text)

# SolrのURLを生成
solr_url = Sunspot.config.solr.url
uri = URI::parse "#{solr_url}/analysis/field"

# ベースになるリクエストパラメータを生成
base_params = {wt: :json, 'analysis.fieldtype' => :text_w2v}

# ファイル開く
File::open("わかち書き.txt", 'w') do |fp|
  # 各テキストの処理
  text_list.each do |text|
    # 1件ずつ処理して解析用エンドポイントを作る
    params = base_params.merge({'analysis.fieldvalue' => text})
    uri.query= URI::encode_www_form params

    # パースして結果を取得
    json = JSON::parse Net::HTTP.get uri rescue next

    # ほしいのは最終行のみ
    analysis = json.dig *%w(analysis field_types text_w2v index)
    nodes = analysis.last.map{|o| o['text']}

    # 空白繋ぎで書き出し
    fp.puts nodes.join(' ')
  end
end

この時使った Solr のフィールド( text_w2v )は下記のようにしています。

<fieldType name="text_w2v" class="solr.TextField" omitNorms="false" autoGeneratePhraseQueries="true" positionIncrementGap="100" >
  <analyzer>
    <!-- 文字置換 -->
    <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-japanese.txt"/>
    <!-- ユーザ辞書の適応 -->
    <tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_ja.txt"/>
    <!-- 品詞種別の基本形変換 -->
    <filter class="solr.JapaneseBaseFormFilterFactory"/>
    <!-- 品詞フィルタリング -->
    <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" enablePositionIncrements="true"/>
  </analyzer>
</fieldType>

作成したわかち書き.txtword2vec を使って学習させ、学習モデルを生成します。

import os
from gensim.models import word2vec

# 読み込み
data = word2vec.Text8Corpus('わかち書き.txt')

# 学習の実施
model = word2vec.Word2Vec(data, size=100)
model.save('sample_model')

試してみる

今の季節のオススメが知りたいので、下記を試してみました。

「秋」+「オススメ」+「お出かけ」=?

その結果がこちらです!
f:id:setsuna82001:20181011170026p:plain

花見!!
潮干狩り!!

……明らかに季節外れですね。
現状で考えている問題点は下記の3つです。

  • 形態素解析時にデフォルトのユーザー辞書を使用したこと
  • わかち書きの内容を精査してないこと
  • ひらがな・カタカナの表記揺れを放置していること

結局は学習に有用なデータソースをきちんと揃えられてなかったことが最大の要因ですね!
上記を念頭に対策して、再度試して面白い結果が見てみたいですね。


最後に

アクトインディでは一緒にサービスを育てたいエンジニアを募集しています!

actindi.net