「Chainerによる実践深層学習」がわかりやすかった話


背景

これまでChainerやTensorflowのサンプルコードやチュートリアルを色々実行したり、色んなサイトや書籍読んだりでなんとなくディープラーニングというかニューラルネットについて理解した気でいましたが、日々進化していくディープラーニングのアーキテクチャを論文読んでコーディングしようと思うと途端にどうしていいかわからなくなってしまう状態でした。
そんなときたまたま書店で目にした
「Chainerによる実践深層学習」Ohmsha 新納浩幸[著]
がとてもわかりやすかったのでそのメモ。

意外と簡単な事に気づいた基本構造

今までbackwardやってoptimizer.update()までやるためにはclass定義して、Linearとかfunction使って…と非常に複雑でブラックボックスになっている事をやらなければいけないと思っていたんですが、全然そんな事はなくて例えば、

x1 = Variable(...
x2 = Variable(...
x3 = Variable(...

z = (x1 + x2)**2 + (x2 * x3)**2
z.backward()

なんてやるとりっぱに逆誤差伝搬処理してくれて

x1.grad

に値が入るとのことで、勝手に難しく考えすぎていたことに気づかされるなど一気にChainerの難易度を下げてもらった感じです。

またあまり他の書籍ではかかれない各層への入力値(x)、出力値(y)、および微分値(∂y/∂x)の関係がグラフで示されているなど全体の流れと扱われる数値の関係性が非常にわかりやすく、これならフレームワークに頼らなくても自分で一からコーディングできるんじゃないかと思えるくらい丁寧に解説してくれています。

Word2Vec

さらにこの本のありがたい箇所がこのWord2Vecの章にありました。ChainerのsampleでWord2Vecを試してみようと思うとdownload.pyを実行するよう書いてあるんですが、現在このスクリプトが存在していなくて挙動を見たいと思っても自分でデータを用意する必要があります。私自身もちょうど困っていたんですが、なんとこの本download.pyのスクリプトそのまま掲載してくれていてこれでデータ入手可能です。ありがたや…

RNN

と、ずっと順調にコード書きながら読んでいたんですが、RNNの評価(パープレキシティ)のところで詰まりました。p87-88のeval-rnn.pyが動かない。本の通りにコード書いてjupyterで実行してみたところ、

ValueError: could not broadcast input array from shape (10000,100) into shape (6049,100)

とか、これをクリアしても

NameError: name 'max_id' is not defined

なんてのが出たりします。
上のshapeで怒られているのはvocabについての事で評価したい(loadしている)modelのvocabサイズを与える必要があるようです。なのでひとまずp88のeval-rnn.pyのコードで

test_data = load_data('ptb.test.txt')
test_data = test_data[0:1000]
train_data = load_data('ptb.train.txt") #追記
...

というようにtrainデータをloadすることでvocabをtrainデータにしました。
※load_dataについてはp83のコードそのまま。

次にmax_idのエラーについてはなんの説明もなく唐突に出てきますが、これはその少し前のページ(p86-87)に未知語を含む文は評価から外す旨の事が書かれているので、trainデータのvocabサイズだと思われます。なので一旦loadの方法をtrainデータとtestデータで分けて、trainデータのloadの際にvocabからmax_idを求める事にしました。
変更点1:p83のコードにmax_idを追記

...
    vocab[word] = len(vocab)
  dataset[i] = vocab[word]
max_id = len(vocab)#追記
return dataset

変更点2:testデータload用の関数を用意

def load_test_data(filename):
    words = open(filename).read().replace('\n','<eos>').strip().split()
    dataset = np.ndarray((len(words),), dtype=np.int32)
    for i, word in enumerate(words):
        if word not in vocab:
            vocab[word] = len(vocab)
        dataset[i] = vocab[word]
    print len(vocab)
    return dataset

これで動かしてみた所
※load_test_dataの前にtrainデータを読み込んでおく必要があります。
 じゃないとvocabがmodel作成時とずれる。

199.959115477

とそれっぽいパープレキシティの値出てきました。

今現在Attentionの手前ぐらいまで読み進めていますが、非常にわかりやすいと思います。
ひとまずここまで。この先もなにかあれば追記予定。