読者です 読者をやめる 読者になる 読者になる

終末 A.I.

Deep Learning を中心に、週末に機械学習するエンジニアのブログ

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

chainer のサンプルの中には RNN 利用して文章を学習し、コンテキストに沿った単語を選択できるようになる ptb のサンプルが付属しています。

今回はこいつをちょっと改造して、単語の識別IDではなく、word2vec で生成したベクトルを用いて ptb サンプルと同じことをやってみようと思いま......したが、残念がら chainer の仕様理解ができていなかったようで、一切パラメーター更新ができておらず、4000円ほどドブに捨てる結果となってしまいました。辛すぎる!

そういうわけで今日のところは、こういう風にやったらうまく学習できなかったという記録のみ記載しておきたいと思います。原因分かり次第、追記か別記事を書きます。

今回学習がうまくいかなかったモデルは以下のように実装しました。元の ptb サンプルから embedID のレイヤの関数を取り除き、Classification で loss を算出する代わりに、huber 損失関数という二乗誤差の親戚みたいな関数で loss を求めると言った構成になっています。入力は単語ベクトルで出力も単語ベクトルになります。LSTMかましていることからも分かるように、model をリセットするまでは以前の入力値も反映した出力をしてくれるモデルです。

class RNNLM(chainer.Chain):
    def __init__(self, n_vocab, n_units, train=True):
        super(RNNLM, self).__init__(
            l1=L.LSTM(n_vocab, n_units),
            l2=L.LSTM(n_units, n_units),
            l3=L.Linear(n_units, n_vocab),
        )
        self.train = train

    def reset_state(self):
        self.l1.reset_state()
        self.l2.reset_state()

    def __call__(self, x, t):
        h1 = self.l1(F.dropout(x, train=self.train))
        h2 = self.l2(F.dropout(h1, train=self.train))
        y = self.l3(F.dropout(h2, train=self.train))
        return F.huber_loss(y, t)

そして、このモデルの loss に対して特に何も考えずにもとの ptb サンプルと同様に、backword して、unchain して、optimizer を update したのですが、全くろくな結果を得ることができませんでした。調べてみると、重み値の更新が一切行われておらず、初期値のまま。 ......なんでや。Function でなくちゃんとレイヤをかまさなければならないのか、それとも loss の扱い方が悪かったのか。

<<追記>> 2016.4.17 夜

huber_loss で学習ができない件について、自己解決しました。huber_loss はミニバッチの学習には対応していないようで、そのために全く学習ができていなかったようです。実装上の都合なのか、関数の制限なのかは勉強不足ため理解できていませんが、ひとまず huber_loss の部分も二乗誤差に差し替えることにより無事学習が行えました。やったね!

しかし学習はできているようであるものの、全くいい感じに文章を覚えてくれません。やたらめったらΣを押してくるこになってしまいました。バッチサイズがでかすぎたのか、エポック数が足りないのか、単純に二乗誤差では学習がうまくいきにくいのか。ちょっと文献をあさってみる必要がありそうです。