Connpassの「Chainer x Azure ML Hackathon CV編」に参加してきた

#概要
Connpass「Deep Learning Lab」の「Chainer x Azure ML Hackathon CV編」に参加してきたのでその記録です。
Hackathon となっていましたが、もくもく会的な感じでした！

https://dllab.connpass.com/event/83012/

500円で Azure のGPUインスタンスを利用できてかつ、
500円で AzureMachineLearningWorkbench について教えて頂けてかつ、
500円で Chainer v4 について教えて頂けてかつ、
500円で ビザが食べれた！！ので、
めっちゃ満足でした！

#AzureMachineLearningWorkbench 
結論から言うと、「ドキュメントめっちゃ良く出来ているから見てね♡」ってことでした。
確かに！！
https://docs.microsoft.com/ja-jp/azure/machine-learning/service/

#Chainer v4 の AzureMachineLearning での環境構築
AzureMachineLearning の「Data Science Virtual Machine」で仮想マシンを作ると、
なんと!すでに、Chainer v4が入っているとのことだったので、

JupyterNotebookで、ちょっと動かしたろーって思って、
やってみると、、、

あれ！？

```Python
from chainer import Sequential
```

が通らない。。。

なんでだろうってことで、講師の方に聞きまくりました。
というか、環境構築してもらった！

どうも AzureMachineLearning で作った、
「Data Science Virtual Machine」の仮想環境では、
JupyterNotebookでは、Chainer v4が入ってないとのことでした。

Anacondaの環境ではなく、ローカルの環境が参照されるから的なことみたいです。。。

Jupyter上で、

```Python
%%bash
pip3 uninstall chainer
pip3 install chainer
```

とすると、行けるみたいです。
アンインストールして、インストールすることで、
最新のChainerをインストールということです！


GPUを使いたいので、以下も教えて頂きました。
まず、GPUのVersionを確認します。

Jupyter上で、

```Python
%%bash
nvcc --version
```

とすると、

```
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation
Built on Fri_Sep__1_21:08:03_CDT_2017
Cuda compilation tools, release 9.0, V9.0.176
```

と出てくるので、9.0の「cupy-cuda90」をインポートする必要があるとのことで、

Jupyter上で、

```
%%bash
pip3 uninstall cupy-cuda90
pip3 install cupy-cuda90
```

とすると、インポートできました！

よしこれで、「Chainer v4」のチュートリアルができる！

#Chainer v4 チュートリアルを実施
このチュートリアルを書いた方に「Chainer v4」の概要を教えて頂きました。
https://qiita.com/mitmul/items/1e35fba085eb07a92560

そして、チュートリアルをみながら、
「cifar10」データセットの画像分類を実施しました。
※調子にのって、チュートリルとは違うモデルの構成にしました。　
　そのせいか精度上がりませんでした。。。

わからないことがあったら、すぐに聞けたので、激熱でした！！

##学んだこと
###・Sequential で、モデルの定義ができる
こんな感じで、定義ができました。
あんまり詳しくないので、こんな印象を持ちました。
・modelのクラスを定義しなくてもいいよ的なかんじ
・見やすい！！！！

```Python
import chainer
import chainer.functions as F
import chainer.links as L
from chainer import training,serializers,Chain,datasets,Sequential,optimizers,iterators
from chainer.training import extensions,Trainer
from functools import partial

model = Sequential(
    L.Convolution2D(None, out_channels=32, ksize=3, stride=1),
    F.relu,
    partial(F.max_pooling_2d, ksize=2, stride=2),
    L.Convolution2D(in_channels=32, out_channels=64, ksize=3, stride=1),
    F.relu,
    partial(F.max_pooling_2d, ksize=2, stride=2),
    L.Convolution2D(in_channels=64, out_channels=128, ksize=3, stride=1),
    F.relu,
    L.Linear(None, 1000),
    F.relu,
    partial(F.dropout, ratio=0.75),
    L.Linear(1000, 10),
)
model = L.Classifier(model)
model.to_gpu(0)
```

###・Trainerを使うと便利
こんな感じで、書くと、学習を実施してくれます。
すっきり書けて、見やすい！

```Python
batchsize = 128
max_epoch = 60
gpu_id = 0

train_iter = iterators.MultiprocessIterator(train, batchsize)
test_iter = iterators.MultiprocessIterator(test, batchsize, False, False)

optimaizer = optimizers.Adam().setup(model)
updater = training.StandardUpdater(train_iter, optimaizer, device=gpu_id)
trainer = Trainer(updater, stop_trigger=(max_epoch, 'epoch'))

trainer.extend(extensions.LogReport())
trainer.extend(extensions.Evaluator(test_iter, model, device=gpu_id), name='val')
trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'main/accuracy', 'val/main/loss', 'val/main/accuracy', 'elapsed_time', 'lr']))
trainer.extend(extensions.snapshot(filename='snapshot_epoch-{.updater.epoch}'))
trainer.extend(extensions.dump_graph('main/loss'))

#学習を実行する
trainer.run()
```

学習の過程が、以下の感じで出てくれる。
おおー！！！

```
epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  elapsed_time  lr        
1           1.60217     0.412284       1.29771        0.535305           6.54357                   
2           1.24028     0.557485       1.15035        0.580696           14.7192                   
3           1.05871     0.626262       0.938314       0.668908           23.4175                   
4           0.934494    0.672315       0.890924       0.686017           33.1585                   
5           0.848975    0.701826       0.836307       0.706685           41.587                    
6           0.773064    0.728486       0.785557       0.72498            50.0916                   
7           0.702637    0.755195       0.77106        0.731112           58.5505                   
8           0.653189    0.770673       0.736721       0.741792           66.8603                   
9           0.603416    0.787584       0.761884       0.739023           74.8583                   
10          0.556914    0.803928       0.749218       0.74644            83.118    
```

エポックごとの学習済みモデルを保存してくれる！
これは、なんか面白いって思いました。

```Python
trainer.extend(extensions.snapshot(filename='snapshot_epoch-{.updater.epoch}'))
```

結果として、テストデータに対しては、「0.746835」でした。
うーん、何が悪いんだろう。。。

というか、GPUってすごい！
60エポックで、495.39秒！
ドロップアウトの「ration=0.75」というのも関係あるのかな．．．？？？

```
epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  elapsed_time  lr   
59          0.104044    0.965505       1.57049        0.745154           487.431                   
60          0.102409    0.965313       1.75489        0.746835           495.39 
```

せっかく、エポックごとにモデルを保存しているし、
学習済みモデルを使って、見てみようと思います。

###・学習済みモデルを読み出す

こんな感じで、学習済みモデルから、予測結果を返すメソッドを作りました。

```Python
from chainer.cuda import to_cpu
import matplotlib.pyplot as plt
import numpy as np

def predict(data, index, epoch, gpu_id):

    infer_net = model.copy()
    model_path = 'result/snapshot_epoch-' + str(epoch)
    serializers.load_npz(model_path , infer_net, path='updater/model:main/')

    if gpu_id >= 0:
        infer_net.to_gpu(gpu_id)
        
    x, y = data[index]
    
    plt.imshow(np.dstack((x[0],x[1],x[2])))
    plt.show()

    x = infer_net.xp.asarray(x[None, ...])
    with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
        y = infer_net.predictor(x)
    y = to_cpu(y.array)
    dic = {0:'飛行機',1:'自動車',2:'鳥',3:'猫',4:'鹿',5:'犬',6:'蛙',7:'馬',8:'船',9:'トラック'}
    print('予測ラベル:', y.argmax(axis=1)[0], dic[y.argmax(axis=1)[0]])
```

はまったのは、以下の部分。

```Python
infer_net = model.copy()
model_path = 'result/snapshot_epoch-' + str(epoch)
serializers.load_npz(model_path , infer_net, path='updater/model:main/')
```

「infer_net 」に、何入れればいいんだろうって思って、
ググったりして、適当に「L.Classifier(model)」でいいでは？って思って、
ぶち込んでいたら、

```
KeyError: 'updater/model:main/predictor/predictor/0/W is not a file in the archive'
```

ってなりました。
そんなもん、学習済みモデルにありませんよ的なことらしい。

講師の方に、「こうすると中身みれるよ」って教えてもらいました。

```Python
import numpy as np
s = np.load('result/snapshot_epoch-10')
s.keys()
```

おっ！たしかに、「updater/model:main/predictor/predictor/0/W」がない！
「updater/model:main/」で読み込んで、「infer_net.predictor(x)」で予測したろーってことで、さっきのメソッドになりました。

```
['updater/model:main/predictor/3/W',
 'updater/optimizer:main/predictor/1/W/m',
 'updater/optimizer:main/predictor/2/b/t',
 'updater/model:main/predictor/0/W',
 'updater/model:main/predictor/3/b',
 'updater/optimizer:main/predictor/1/b/v',
 'updater/model:main/predictor/1/W',
 'extensions/LogReport/_trigger/previous_epoch_detail',
 'updater/optimizer:main/predictor/0/W/v',
 'updater/model:main/predictor/2/W',
 'extension_triggers/PrintReport/previous_epoch_detail',
 'updater/optimizer:main/predictor/0/b/t',
 'updater/optimizer:main/epoch',
 'updater/model:main/predictor/4/b',
 'updater/optimizer:main/predictor/4/b/m',
 'extension_triggers/val/previous_epoch_detail',
 'updater/optimizer:main/t',
 'updater/optimizer:main/predictor/4/W/t',
 'updater/optimizer:main/predictor/4/b/v',
 'updater/optimizer:main/predictor/3/W/v',
 'updater/iterator:main/previous_epoch_detail',
 'updater/model:main/predictor/1/b',
 'updater/optimizer:main/predictor/0/b/v',
 'extension_triggers/LogReport/previous_epoch_detail',
 'updater/optimizer:main/predictor/3/b/v',
 'updater/optimizer:main/predictor/4/b/t',
 'extensions/LogReport/_log',
 'updater/iterator:main/epoch',
 'updater/optimizer:main/predictor/4/W/m',
 'updater/iterator:main/order',
 '_snapshot_elapsed_time',
 'updater/optimizer:main/predictor/0/W/t',
 'extension_triggers/snapshot/previous_iteration',
 'updater/model:main/predictor/4/W',
 'updater/optimizer:main/predictor/0/W/m',
 'updater/optimizer:main/predictor/3/b/t',
 'updater/optimizer:main/predictor/3/b/m',
 'updater/optimizer:main/predictor/0/b/m',
 'extensions/LogReport/_trigger/previous_iteration',
 'updater/optimizer:main/predictor/2/W/m',
 'updater/optimizer:main/predictor/1/b/t',
 'updater/optimizer:main/predictor/3/W/m',
 'updater/iterator:main/is_new_epoch',
 'updater/optimizer:main/predictor/2/W/v',
 'extension_triggers/val/previous_iteration',
 'updater/model:main/predictor/0/b',
 'updater/optimizer:main/predictor/2/W/t',
 'extension_triggers/PrintReport/previous_iteration',
 'updater/optimizer:main/predictor/3/W/t',
 'updater/optimizer:main/predictor/2/b/m',
 'updater/optimizer:main/predictor/4/W/v',
 'updater/iterator:main/current_position',
 'updater/iteration',
 'stop_trigger/previous_iteration',
 'updater/optimizer:main/predictor/1/W/t',
 'updater/optimizer:main/predictor/2/b/v',
 'extension_triggers/LogReport/previous_iteration',
 'extension_triggers/snapshot/previous_epoch_detail',
 'updater/optimizer:main/predictor/1/W/v',
 'updater/model:main/predictor/2/b',
 'stop_trigger/previous_epoch_detail',
 'extensions/LogReport/_summary/_names',
 'updater/optimizer:main/predictor/1/b/m']
```

###・学習済みモデルでの予測

まず、１エポック目でどうだ！
飛行機のはずが、、、船！
まあ、１エポック目だし、５０％くらいの精度だし仕方ないか！

<img width="296" alt="10_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/256696/d2e885d3-2bba-2426-6702-226fd5f63749.png">

次は、３０エポック目！
おおー、あたったー！

<img width="261" alt="10_30.png" src="https://qiita-image-store.s3.amazonaws.com/0/256696/6fb441a8-0c34-6e71-5767-721381ba86f6.png">

そして、６０エポック目！
「鹿」！！！
確かに、鹿の角に見えなくはない。。。
「main/accuracy：0.965313」、「val/main/accuracy：0.746835」なので、
過学習なのかな？？？
精度が上がるように、チューニングしたいが、時間切れでした。

<img width="250" alt="10_60.png" src="https://qiita-image-store.s3.amazonaws.com/0/256696/dd7fd130-274b-9659-be86-dffe7749dac8.png">

#まとめ
AzureMachineLearning、Chainerを使った勉強ができて楽しかったです！

詳しい方が周りにいて、聞ける状況ってありがたいって思いました。
わからなかったときに、理解できるように教えてくれて本当に助かりました。

教えてくれた方々、ありがとうございました。

次は、精度が上がるように、チューニングできるようになりたい！