備忘録 blog

Docker/Machine Learning/Linux

word2vecによる文章表現 / ディープラーニング所感

tl;dr

Kerasを用いてDeepLearningをした上で得られた知見や、感じたことについて適当に列挙する。

Kerasについて

sharply.hatenablog.com

以前に書いた。依然としてKerasはオススメである。

Optimizerについて

http://i.imgur.com/2dKCQHh.gif

ExampleではしばしばSGDを使っているが、上の図のようにSGDでは局所解に陥って動かなくなることがある。逆にAdadeltaやRMSPropのほうが早く収束するので、Optimizerの選択は意外に重要であった。

文章のベクトル表現を考える

台詞があったとき、分かち書きして学習させることで、ある台詞が誰の発話であるかを推定するモデルを作るとする。この場合のワークフローとしては単語をスカラーとして扱い、その単語が文中に出てくれば1,出てこなければ0としてバイナリ化し、単語の種類数の長さをもつベクトルで表現する。

このときに問題となるのは、その単語が学習データにあればよいけれど、基本的には学習データにない単語に対しては判定ができないということにある。これに対処する方法としては、事前学習で台詞一般、文章一般を学習させておき、次に分類したいキャラの台詞を学習させて重みをつけるという方法が1つ考えられる。

次に、キャラクターは単語空間上で近い言葉をしゃべる可能性が高いだろうという推定を置いて、単語のベクトル表現を利用することが考えられる。以後、その方法について考えたい。

Word2vec考

Google Code Archive - Long-term storage for Google Code Project Hosting.

Word2vecは単語のベクトルを表現を得る際に使われる。このベクトル表現で、類似した単語は近傍で得られるという過程を置く。これもさんざん各所で説明されているが、Word2Vecは主にCBoWとSkip-Gramと呼ばれる2つのアルゴリズムで構成されている。

  • CBoW: 周囲の文脈から出現する単語を推定するNN
  • Skip-gram: 単語から周囲の文脈を推定するNN

このWord2Vecを使うことで、単語のベクトル表現を抽出することができる。日本語コーパスとしてここで、Wikipediaを用いることとする。

saiyu.cocolog-nifty.com

上記の手順を使えば、Mecab-ipadicで単語分割を施したWikipedia産の単語の200次元ベクトル表現を得ることができる。pythonバインディングを使うことで、生のベクトル表現を手に入れることも可能である。

from gensim.models import Word2Vec

w2v = Word2Vec.load_word2vec_format('./jawiki.bin', binary=True)  # C binary format

words = raw_input().split()
vector = []
for word in words:
  try:
    vector.append(w2v[word.decode('utf-8')])

ワークフロー考

これを用いて、以下のワークフローを考える。

  • 台詞をMecab分かち書きし、単語それぞれword2vecを用いてベクトル表現を受け取る。
  • そのベクトル表現から"文のベクトル表現"なるものを抽出し、それをNNで学習させ、分類問題を解く。

次に文のベクトル表現をどうやって生成するかだが、そのまま単語を200次元のベクトルに変換すると、台詞は200x単語数の可変長ベクトルになってしまい、そのままではMLPの入力にはできない。そこで以下の2選択肢があるように思う。

  1. 固定長の文ベクトルを計算し、それを入力とする。
  2. 可変長入力を学習できるレイヤーを用意する。

このうち1.について考えると、単語ベクトルを平均するなり和をとるなりすれば、確かに200次元のベクトル表現を作ることは可能だ。しかし和をとるとすると台詞の長短によって結果が左右されてしまうし、平均をとったところでそもそもその単語ベクトルには多分に文脈情報が含まれているはずであり、その平均をとるというのはいったい何を意味するのかという疑念が残る。

そこで、2.について考えたい。

可変長入力を受け付けられるニューラルネットモデル

RNN(Reccurent Neural Network)が可変長入力を受け付けられるモデルとして考えられる。RNNでは一般に、内部に再帰的なループを持つことで、それ以前に受けとった入力を"記憶"しておくことが可能であり、LSTMやGRUといったユニットが提案されている。このユニットがあることで、時刻tの入力に対して、時刻t-1までの入力によって得られた重みも同時に入力として次の重みを計算することが可能となる。一般には時系列データに対して適用される。

LSTMの詳細については下の記事が詳しい。

qiita.com

時系列データのように、過去から未来への一方向なデータに対しては順方向なRNNが用いられるが、未来から過去に対する因果関係が存在するような場合は、双方向RNNが用いられることがある。

さて、ここではLSTMを使って可変長入力を受取り、分類するようなモデルをkerasで記述した。モデルとしては以下のような形になった。

model = Sequential()
model.add(LSTM(output_dim=256, batch_input_shape=(batch_size, 58, 200), return_sequences=False))
model.add(Dense(nb_classes))
model.add(Activation("softmax"))

model.summary()

このモデルが本当に動いているか自信がない。というのも学習は進んだものの、汎化性能を獲得するには至らなかったからだ。それにpredict時に毎回word2vecをロードするので時間がかかっている。そういうこともありいささか筋悪な手法だとは思うのだが、誰か詳しい方の解説を乞いたい。

外挿性の問題

「データをつなげて論理的に実現方法がわかるものしか機械学習できない」というのはその通りで、基本的に機械学習であってもデータの補間はできても外挿は難しい。つまり学習させたものと同じようなテストケースに対しては正答できても、違う類の問題に対しては答えられない。しかしもしディープラーニングによって解きたい問題よりメタな概念を学習させることができるならば、その下位概念(=いま解きたい問題)に対してもある程度正答率の高いモデルを得ることができるのではないかと思う。実際に実現可能かは分からない。