終末 A.I.

データいじりや機械学習するエンジニアのブログ

word2vec の結果を利用して RNN で文章を生成してみる(2)

word2vec の出力結果を元に文章を作ってみるコーナーの第二弾です。 今回はエンコーダーデコーダーモデルを使用して word2vec の出力から文章を生成できないかを試してみました。 使用したモデルは以前の記事で紹介した Skip-Thought Vectors です。

ksksksks2.hatenadiary.jp

Skip-Thought Vectors を簡単に説明すると、入力文をエンコーダーエンコードしその文の情報をコンテキスト情報として指定サイズのベクトルに圧縮、そしてこのコンテキスト情報を元に、デコーダーを使用して入力文の前後の文を出力しようというモデルとなります。 この Skip-Thought Vectors の入力および出力は、単語のIDを各ユニットに割り当てた形となります。今回は、この入力と出力を word2vec にして日本語で動かしてみました。

chainer を用いて、前回も利用した mecab分かち書きにした wikipedia の記事文章とその文章から生成した word2vec を利用して処理を行いました。ロス関数は本家のソフトマックスのままでは利用できないので、正解単語とデコーダーによって出力された単語との二乗誤差を用いました。対象となった単語は40万語ほどで、それ以外は未知語としてすべて同じベクトルを割り当てるようにしています。

利用したコードは記事の最下部に記載しています。一部 util.py に逃がしたコードは記載していませんが、だいたい何をやっているかは分かっていただけるかと思います。chainer にはコンディショナルな GRU を扱う方法がないので、StatefulGRU のコードを利用して ConditionalStatefulGRU を自作、それをさらに自作の Skip-Thoughts モデルで使用しています。Skip-Thoghts モデルでは、まず入力単語をエンコーダー部でエンコードし、その出力をデコーダーで使用して前後の文が出力されるように学習しています。

だいたい6万文ほど学習させた段階で誤差が小さくならなくなってきたので、今回はそこで処理を停止させました。以下はその時のモデルを利用して、文章を入力した際に、次の文章として生成されたものです。文章の生成は、「。」が現れるか、30単語生成した時点で停止するようにしています。ちなみに入力文章は、学習したデータの中にも含まれている、wikipedia のアンパサンドに関する説明の一節です。

  • 入力文:アンパサンド(&)とは「…と…」を意味する記号である。
  • 出力単語列:'000', '受け取る', '回数', '推定', '平均', '000', '賃金', '月末', '政令', '条', '詐欺', '弁護士', '監査', '地裁', '年金', '協定', '加盟', '審議', '令', '奉仕', '自治体', '在日', '捕鯨', '定める', '通知', '土木', '請求', '職員', '実務', '事務'

  • 入力文:その使用は1世紀に遡ることができ(1)、5世紀中葉(2,3)から現代(4-6)に至るまでの変遷がわかる。

  • 出力単語列:'フラッシュ', 'メニュー', 'フォーマット', '送信', 'ケーブル', '媒体', 'ホスト', '動画', 'MHz', '閲覧', 'エージェント', '互換', '通信', '準拠', 'タイミング', 'サポート', 'OS', 'メニュー', 'SD', 'ボード', 'ファイル', '周波数', 'ゲート', '本体', 'マイクロソフト', 'シフト', 'コンテンツ', 'オブジェクト', 'Microsoft', 'ロード', 'フォーマット', 'ドメイン', '選択肢', 'オブジェクト', 'ソニー', 'ウェア', 'マイクロソフト', 'データ', '素子', 'オート', 'アプリ', 'コア', '動画', 'スケジュール', 'グループ', '夏季', 'メイン', '祭り', 'ライフ', 'ロケ', 'ショップ'

ご覧いただいた通り、全然うまく学習できていません。入力文の話の流れを完全に無視しているだけでなく、出力がただの名詞の羅列でありそもそも文章として成り立たせるための学習が行われたように全く見えません。一方、出力する単語列はその前の単語に大きく影響されるところがあり、連想ゲームのように関連する単語が生成されるようになってしまっています。

「てにおは」が全くなく、その前の単語に近い単語が生成されるというのは、学習動作から考えて一応想定していた結果ではあります。ある文章には、それ以前に入力されてきた単語に関連する単語(word2vec 的に距離が近い単語)が現れる確率が、それ以外の単語が現れる確率より格段に高いので、入力された単語と近い単語を出力するような学習となったのでしょう。

入力文のコンテキストを完全に無視するという挙動はなんとも言えません。同じ文の組み合わせを一回ずつしか学習させていないので十分に学習しきれていない可能性もありますし、自前で実装した ConditionalStatefulGRU がうまく機能していない可能性もあります。一応 ConditionalStatefulGRU のパラメーター更新は行われているようですので、そんなことはないと思いたいものですが。

とりま、前者の挙動をなんとかしないと、文章っぽいものを生成することすらできません。この辺りをどう解決するかが今後の鍵になりそうです。