Python
Chainer

Tech Circle ML #8 Chainer with Reccurent Neural Language Model

More than 3 years have passed since last update.


リカレントニューラルネットワーク言語モデル

事前準備

http://qiita.com/GushiSnow/items/9ab8761082e29002f735

ハンズオンのコードがあるgithub

https://github.com/SnowMasaya/Chainer-with-Neural-Networks-Language-model-Hands-on.git


アプリケーションの動作確認

仮想環境の有効化

Mac/Linux

source my_env/bin/activate

pyenv install 3.4.1

pyenv rehash
pyenv local 3.4.1

動作確認

直下で、以下を実行。

ipython notebook

Chainerのノートブックがこれで開きます。


リカレントニューラル言語モデルを作成するプロセスを体験する

iPython notebookの方を開いてください。こちらにはリカレントニューラル言語モデルの作成のための各ステップを順番に書いてあります。

iPython notebookでは文中のコードが実際に実行できるため、上から順に解説&実行をしていきましょう(詳しい使い方はこちらを参照してください)。

Screen Shot 2015-09-15 at 7.30.33.jpg

ここからコーディング箇所までipython notebookを見て下さい。


リカレントニューラル言語モデル設定(コーディング箇所)!!!!

モデルは別クラスで定義しています。

この部分で自由にモデルを変えることが出来ます。

この部分でリカレントニューラル言語モデル独特の特徴を把握してもらうことが目的です。

F.EmbedIDは辞書データを、入力ユニット数分のデータに変換する処理(潜在ベクトル空間への変換)を行っています。

・出力が4倍の理由は入力層、入力制限層、出力制限層、忘却層をLSTMでは入力に使用するためです。

h1_in = self.l1_x(F.dropout(h0, ratio=dropout_ratio, train=train)) + self.l1_h(state['h1'])は過去の情報を保持しながらどれだけのdropoutでユニットを削るかを表しています。

Drop outについては下記をご覧下さい。

http://olanleed.hatenablog.com/entry/2013/12/03/010945

c1, h1 = F.lstm(state['c1'], h1_in)はlstmと呼ばれる魔法の工夫によってリカレントニューラルネットがメモリ破綻、勾配消失を起こさずにいい感じで学習するための工夫です。詳しく知りたい人は下記をご覧下さい。

http://www.slideshare.net/nishio/long-shortterm-memory

return state, F.softmax_cross_entropy(y, t)は予測した文字と実際の文字を比較して損失関数を更新している所になります。ソフトマックス関数を使用している理由は出力層の一つ前の層の全入力を考慮して出力を決定できるので一般的に出力層の計算にはソフトマックス関数が使用されます。

#-------------Explain2 in the Qiita-------------

class CharRNN(FunctionSet):

"""
ニューラルネットワークを定義している部分です。
上から順に入力された辞書ベクトル空間を隠れ層のユニット数に変換し、次に隠れ層の入
力と隠れ層を設定しています。
同様の処理を2層にも行い、出力層では語彙数に修正して出力しています。
なお最初に設定するパラメータは-0.08から0.08の間でランダムに設定しています。
"""

def __init__(self, n_vocab, n_units):
super(CharRNN, self).__init__(
embed = F.EmbedID(n_vocab, n_units),
l1_x = F.Linear(n_units, 4*n_units),
l1_h = F.Linear(n_units, 4*n_units),
l2_x = F.Linear(n_units, 4*n_units),
l2_h = F.Linear(n_units, 4*n_units),
l3 = F.Linear(n_units, n_vocab),
)
for param in self.parameters:
param[:] = np.random.uniform(-0.08, 0.08, param.shape)

"""
順伝搬の記述です。
順伝搬の入力をVariableで定義し、入力と答えを渡しています。
入力層を先ほど定義したembedを用います。
隠れ層の入力には、先ほど定義したl1_xを用いて、引数にdropout、隠れ層の状態を渡して
います。
lstmに隠れ層第1層の状態とh1_inを渡します。
2層目も同様に記述し、出力層は状態を渡さずに定義します。
次回以降の入力に使用するため各状態は保持しています。
出力されたラベルと答えのラベル比較し、損失を返すのと状態を返しています。
"""

def forward_one_step(self, x_data, y_data, state, train=True, dropout_ratio=0.5):
x = Variable(x_data, volatile=not train)
t = Variable(y_data, volatile=not train)

h0 = self.embed(x)
h1_in = self.l1_x(F.dropout(h0, ratio=dropout_ratio, train=train)) + self.l1_h(state['h1'])
c1, h1 = F.lstm(state['c1'], h1_in)
h2_in = self.l2_x(F.dropout(h1, ratio=dropout_ratio, train=train)) + self.l2_h(state['h2'])
c2, h2 = F.lstm(state['c2'], h2_in)
y = self.l3(F.dropout(h2, ratio=dropout_ratio, train=train))
state = {'c1': c1, 'h1': h1, 'c2': c2, 'h2': h2}

return state, F.softmax_cross_entropy(y, t)

"""
dropoutの記述を外して予測用のメソッドとして記述しています。
dropoutにはtrainという引数が存在し、trainの引数をfalseにしておくと動作しない
ので、予測の時は渡す引数を変えて学習と予測を変えても良いですが、今回は明示的に分る
ように分けて記述しました。
"""

def predict(self, x_data, state):
x = Variable(x_data, volatile=True)

h0 = self.embed(x)
h1_in = self.l1_x(h0) + self.l1_h(state['h1'])
c1, h1 = F.lstm(state['c1'], h1_in)
h2_in = self.l2_x(h1) + self.l2_h(state['h2'])
c2, h2 = F.lstm(state['c2'], h2_in)
y = self.l3(h2)
state = {'c1': c1, 'h1': h1, 'c2': c2, 'h2': h2}

return state, F.softmax(y)

"""
状態の初期化です。
"""

def make_initial_state(n_units, batchsize=100, train=True):
return {name: Variable(np.zeros((batchsize, n_units), dtype=np.float32),
volatile=not train)
for name in ('c1', 'h1', 'c2', 'h2')}
#-------------Explain2 in the Qiita-------------


言語の予測

<Handson #2 解説>

予測用の文字列データ取得ですが、通常は学習データとテストデータは当然分けますが、今回はハンズオンで効果を実感してもらいたかったのであえて学習データとテストデータを同一にしました。

予測では作成されたモデル変更と文字列予測を行ないます。

・モデルを変更する。

・文字列を予測する。

予測するモデルの変更はここではiPython notebook内の下記のコードを変更します。

作成されたモデルはcvフォルダの中にあるので

あまり数は出来ていませんが、確認して見て下さい。

# load model

#-------------Explain5 in the Qiita-------------
model = pickle.load(open("cv/charrnn_epoch_x.chainermodel", 'rb'))
#-------------Explain5 in the Qiita-------------

state, prob = model.predict(np.array([index], dtype=np.int32), state)で予測された確率と状態を取得しています。次の予測にも使用するため状態も取得しています。

index = np.argmax(cuda.to_cpu(prob.data))cuda.to_cpu(prob.data)部分で各単語の重み確率を取得できるため、その中で一番確率が高いものが予測された文字なのでその文字のインデックスを返すようにしています。

index = np.random.choice(prob.data.argsort()[0,-sampling_range:][::-1], 1)[0]はリカレントだと同じような文字が出力される確率が高いので、候補の中から上位5つからランダムに出力する処理も載せています。ここはあえてバラエティに富んだ出力が出ることを見ていただきたい部分なので本来は最大値を選択する必要があります。

#-------------Explain7 in the Qiita-------------

state, prob = model.predict(np.array([index], dtype=np.int32), state)
#index = np.argmax(prob.data)
index = np.random.choice(prob.data.argsort()[0,-sampling_range:][::-1], 1)[0]
#-------------Explain7 in the Qiita-------------


モデルを賢くして予測

今回のHands Onでは限られた時間でしか学習していないのでひどい精度のモデルしかできていません。

そこでパラメータを調整してモデルを使って再作成してみましょう。

調整するパラメータ

#-------------Explain7 in the Qiita-------------

n_epochs = 30
n_units = 625
batchsize = 100
bprop_len = 10
grad_clip = 0.5
#-------------Explain7 in the Qiita-------------

それぞれのパラメータの役割

n_epochsは学習回数を表しています。モデルが複雑な場合は学習回数を多くしないと収束しないのでモデルが複雑な場合は大きな数を設定する必要があります。

n_unitsは隠れ層の数です。この数が多くなればなるほどモデルが複雑になります。この数を多くすると必然的に学習回数を多くしないと学習は収束しません。特に言語モデルの場合は語彙数に応じて、変えた方が良いです。語彙数よりユニット数の数が多いと潜在空間への写像が出来ていないことになり結果的に意味がない処理になります。

batchsizeは一度に学習するデータの数です。データの大きさに依存します。この点は経験的に調整する場合が多いですが、基本的に大きくすると学習精度が向上する代わりに学習スピードが落ち、小さくすると学習精度が低下する代わりに学習スピードが早くなります。

bprop_lenはリカレントニューラルネット特有のパラメータでどれだけ過去の文字を保有するかを表します。

これは解く問題によって変わるパラメータなので長い文章を予測させたい場合は大きな数値を設定し、比較的長くない文章であれば短い数値を設定します。

optimizer.clip_grads(grad_clip)は勾配(重みの更新幅)の大きさに上限を設けており、重みが爆発するのを防いでいます。

大きな値にすると学習を許容し、小さな値にすると学習を抑えます。

ハイパーパラメータの最適化についてもっと知りたい方は下記をご覧ください

http://colinraffel.com/wiki/neural_network_hyperparameters


Handson Advance

言語処理はすごく時間がかかるのでGPU設定をおススメしています。

しかし一概に使えば良いということではなく下記のような設定では有効に働きます。

詳細な仕組みの中身を知りたい方は下記をご覧ください。

http://www.kumikomi.net/archives/2008/06/12gpu1.php?page=1

*得意

行列計算

メモリにシーケンシャルにアクセスし、かつ条件分岐の無い計算(演算密度の高い処理)に強い。

*苦手

二分探索

メモリにランダムアクセスし、かつ条件分岐が多い。


高速で試したい方へ

http://sla.hatenablog.com/entry/chainer_on_ec2

github(今回のGPU用)NVIDIAが公開しているCUDA環境入りGPUインスタンス(Amazon Linuxベース)を使用

https://github.com/SnowMasaya/Chainer-with-Neural-Networks-Language-model-Hands-on-Advance.git


GPUドライバ設定

AWSでのGPU設定は下記のサイトを参考に行いました。

http://tleyden.github.io/blog/2014/10/25/cuda-6-dot-5-on-aws-gpu-instance-running-ubuntu-14-dot-04/

apt-get update && apt-get install build-essential

Cudaのインストーラーを取得

wget http://developer.download.nvidia.com/compute/cuda/6_5/rel/installers/cuda_6.5.14_linux_64.run

Cudaのインストーラーのみ取得

chmod +x cuda_6.5.14_linux_64.run

mkdir nvidia_installers
./cuda_6.5.14_linux_64.run -extract=`pwd`/nvidia_installers

image-extractを取得

sudo apt-get install linux-image-extra-virtual

再起動

reboot

ファイルを作成

vi /etc/modprobe.d/blacklist-nouveau.conf

nouveauとlbm-nouveauの起動しないように設定

blacklist nouveau

blacklist lbm-nouveau
options nouveau modeset=0
alias nouveau off
alias lbm-nouveau off

Kernel Nouveauを起動しないように設定

 echo options nouveau modeset=0 | sudo tee -a /etc/modprobe.d/nouveau-kms.conf

カーネル起動時にあらかじめメモリに展開することでファイルシステムを構成する設定をしてから再起動

update-initramfs -u

reboot

カーネルのソースを取得

apt-get install linux-source

apt-get install linux-headers-3.13.0-37-generic

NVIDIAのドライバをインストール

cd nvidia_installers

./NVIDIA-Linux-x86_64-340.29.run

下記のコマンドでドライバがインストールされたかを確認する。

nvidia-smi

Wed Aug 5 07:48:36 2015
+------------------------------------------------------+
| NVIDIA-SMI 340.29 Driver Version: 340.29 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 GRID K520 Off | 0000:00:03.0 Off | N/A |
| N/A 54C P0 80W / 125W | 391MiB / 4095MiB | 99% Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Compute processes: GPU Memory |
| GPU PID Process name Usage |
|=============================================================================|
| 0 8013 python 378MiB |
+-----------------------------------------------------------------------------+

上記のGPUに振られている番号がGPUのIDである。これは後でChainerを動作させるときに使用する。

error while loading shared libraries: libcurand.so.5.5: cannot open shared object file: No such file or directoryが出た場合

http://linuxtoolkit.blogspot.jp/2013/09/error-while-loading-shared-libraries.html

PATHも通しておく

export PATH=$PATH:/usr/local/cuda-6.5/bin/


Pythonの設定

Python3の設定を行いました。

下記のコマンドを実行して事前に必要なものをインストールしておく



apt-get update
apt-get install gcc gcc++ kmod perl python-dev
sudo reboot

pip導入手順

https://pip.pypa.io/en/stable/installing.html

Pyenv導入手順

https://github.com/yyuu/pyenv



pip install virtualenv

pyenv install 3.4

virtualenv my_env -p = ~/.pyenv/versions/3.4.0/bin/python3.4

requirement.txtの設定を行いました。

numpy

scikit-learn
Mako
six
chainer
scikit-cuda

必要なライブラリをインストール

pip install -r requirement.txt

下記から"install-headers"をダウンロードしてくる。

https://android.googlesource.com/toolchain/python/+/47a24ea6662f20c8e165d541ab6facdf009bfee4/Python-2.7.5/Lib/distutils/command/install_headers.py

PyCudaのインストール

wget https://pypi.python.org/packages/source/p/pycuda/pycuda-2015.1.2.tar.gz

tar zxvf pycuda-2015.1.2.tar.gz
cd pycuda-2015.1.2
./configure.py
make
make install


Handson Advance2

ipython notebookをサーバー上で動作させて動作確認を行う(AWS上)

https://thomassileo.name/blog/2012/11/19/setup-a-remote-ipython-notebook-server-with-numpyscipymaltplotlibpandas-in-a-virtualenv-on-ubuntu-server/

設定ファイル作成

ipython profile create myserver

設定ファイル修正

vim /home/ec2-user/.ipython/profile_myserver/ipython_config.py

行の追加

c = get_config()

c.IPKernelApp.pylab = 'inline'
c.NotebookApp.ip = '*'
c.NotebookApp.open_browser = False
c.NotebookApp.password = u'sha1:yourhashedpassword'
c.NotebookApp.port = 9999

cudaのPATHを通しておく

export PATH=$PATH:/usr/local/cuda-6.5/bin/

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-6.5/lib64/

AWSのセキュリティグループから開けたいポートを開ける

1:セキュリティグループを選択

2:編集でルールの追加

3:タイプ:カスタム TCP ルール

4:プロトコル:TCP

5:ポート:9999

6:送信元は任意の場所で設定

実行

通常の手順では反映されないので直接プロファイリングファイルを反映させる。

sudo ipython notebook --config=/home/ec2-user/.ipython/profile_myserver/ipython_config.py --no-browser


まだまだ勉強したい向上心のある方へ

下記サイトの" Statistical Language Models based on Neural Networks"がすごくまとまっていて分かりやすいです。英語ですが。

http://rnnlm.org/


参考サイト一覧

言語モデルのカバレージ、パープレキシティの説明

http://marujirou.hatenablog.com/entry/2014/08/22/235215

ディープラーニングフレームワークChainerをEC2のGPUインスタンスで動かす g2.2xlarge instance

http://ukonlly.hatenablog.jp/entry/2015/07/04/210149

Drop Out

http://olanleed.hatenablog.com/entry/2013/12/03/010945

Learning to forget continual prediction with lstm

http://www.slideshare.net/FujimotoKeisuke/learning-to-forget-continual-prediction-with-lstm

Zaremba, Wojciech, Ilya Sutskever, and Oriol Vinyals. "Recurrent neural network regularization." arXiv preprint arXiv:1409.2329 (2014).

Google Mikolov

http://www.rnnlm.org/

Neural Network(NN)を利用したLanguage Model(LM),つまりNeural Network Language Model(NNLM)の一種であり, Recurrent Neural Network(RNN)を使ったRecurrent Neural Network Language Model(RNNLM)

http://kiyukuta.github.io/2013/12/09/mlac2013_day9_recurrent_neural_network_language_model.html

Long Short-term Memory

http://www.slideshare.net/nishio/long-shortterm-memory

Chainerのptbサンプルを解説しつつ、自分の文章を深層学習させて、僕の文章っぽい文を自動生成させてみる

http://d.hatena.ne.jp/shi3z/20150714/1436832305

RNNLM

http://www.slideshare.net/uchumik/rnnln

スパース推定概観:モデル・理論・応用

http://www.is.titech.ac.jp/~s-taiji/tmp/sparse_tutorial_2014.pdf

正則化学習法における最適化手法

http://imi.kyushu-u.ac.jp/~waki/ws2013/slide/suzuki.pdf

リカレントニューラル言語モデル作成参考

https://github.com/yusuketomoto/chainer-char-rnn

ニューラルネット 自然言語処理

http://www.orsj.or.jp/archive2/or60-4/or60_4_205.pdf

言語モデル作成

http://www.slideshare.net/uchumik/rnnln

自然言語処理プログラミング勉強会n-gram言語モデル

http://www.phontron.com/slides/nlp-programming-ja-02-bigramlm.pdf

Statistical Semantic入門 ~分布仮説からword2vecまで~

http://www.slideshare.net/unnonouno/20140206-statistical-semantics

linux source code

https://github.com/torvalds/linux


  1. なぜGPUコンピューティングが注目を浴びているか - 慶應義塾
    http://www.yasuoka.mech.keio.ac.jp/gpu/gpu_0.php

CUDA技術を利用したGPUコンピューティングの実際(前編) ―― グラフィックス分野で磨かれた並列処理技術を汎用数値計算に応用

http://www.kumikomi.net/archives/2008/06/12gpu1.php?page=1

GPGPU

https://ja.wikipedia.org/wiki/GPGPU#.E7.89.B9.E5.BE.B4.E3.81.A8.E8.AA.B2.E9.A1.8C

自然言語処理論I

http://www.jaist.ac.jp/~kshirai/lec/i223/02.pdf

STATISTICAL LANGUAGE MODELS BASED ON NEURAL

NETWORKS

http://www.rnnlm.org/

Neural Network Hyperparameters

http://colinraffel.com/wiki/neural_network_hyperparameters

Random Search for Hyper-Parameter Optimization

http://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf