リカレントニューラルネットワーク言語モデル
事前準備
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では文中のコードが実際に実行できるため、上から順に解説&実行をしていきましょう(詳しい使い方はこちらを参照してください)。
ここからコーディング箇所まで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については下記をご覧下さい。
・c1, h1 = F.lstm(state['c1'], h1_in)
はlstmと呼ばれる魔法の工夫によってリカレントニューラルネットがメモリ破綻、勾配消失を起こさずにいい感じで学習するための工夫です。詳しく知りたい人は下記をご覧下さい。
・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)は勾配(重みの更新幅)の大きさに上限を設けており、重みが爆発するのを防いでいます。
大きな値にすると学習を許容し、小さな値にすると学習を抑えます。
ハイパーパラメータの最適化についてもっと知りたい方は下記をご覧ください
Handson Advance
言語処理はすごく時間がかかるのでGPU設定をおススメしています。
しかし一概に使えば良いということではなく下記のような設定では有効に働きます。
詳細な仕組みの中身を知りたい方は下記をご覧ください。
http://www.kumikomi.net/archives/2008/06/12gpu1.php?page=1
*得意
行列計算
メモリにシーケンシャルにアクセスし、かつ条件分岐の無い計算(演算密度の高い処理)に強い。
*苦手
二分探索
メモリにランダムアクセスし、かつ条件分岐が多い。
高速で試したい方へ
github(今回のGPU用)NVIDIAが公開しているCUDA環境入りGPUインスタンス(Amazon Linuxベース)を使用
GPUドライバ設定
AWSでのGPU設定は下記のサイトを参考に行いました。
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
が出た場合
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"をダウンロードしてくる。
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上)
設定ファイル作成
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"がすごくまとまっていて分かりやすいです。英語ですが。
参考サイト一覧
言語モデルのカバレージ、パープレキシティの説明
ディープラーニングフレームワークChainerをEC2のGPUインスタンスで動かす g2.2xlarge instance
Drop Out
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
Neural Network(NN)を利用したLanguage Model(LM),つまりNeural Network Language Model(NNLM)の一種であり, Recurrent Neural Network(RNN)を使ったRecurrent Neural Network Language Model(RNNLM)
Long Short-term Memory
Chainerのptbサンプルを解説しつつ、自分の文章を深層学習させて、僕の文章っぽい文を自動生成させてみる
RNNLM
スパース推定概観:モデル・理論・応用
正則化学習法における最適化手法
リカレントニューラル言語モデル作成参考
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
- なぜ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