Chainer v4 ビギナー向けチュートリアル


Update (2018/04/20): Chainer v4に合わせ内容を更新しました。

**注意：**

- 今回はニューラルネットワーク自体が何なのかといった説明は省きます。
- この記事はJupyter notebookを使って書かれていますので、コードは上から順番に実行できるようにチェックされています。元のJupyter notebookファイルはGoogle Colabを使ってブラウザから実行することができます。Google Colab上ではGPUを使った学習を実際に実行することができますので、「ドライブにコピー」ボタンをクリックしてご自分のドライブにコピーしてから、ぜひ実行してみてください。：[Chainer Beginner's Hands-on.ipynb](https://colab.research.google.com/drive/1tmTq-iduFpu3Xg9_NoR6JHZ8TAygldy5#forceEdit=true&offline=true&sandboxMode=true)

Qiitaだとページ内リンクつきの目次が勝手に作成されるので、全体概要はそちらを眺めて把握してください。

# インストール

Chainerのインストールはとても簡単です。Chainer本体はすべてPythonコードのみからなるので、インストールも


```bash
%%bash
pip install chainer
```

で完了です。ただ、これだけではGPUは使えません。GPUを使うためには、**別途CuPyをインストールする必要があります。**ただCuPyのインストールもとても簡単です。


```bash
%%bash
pip install cupy-cuda90
```

以上です。筆者の環境がCUDA9.0の環境であったため`cupy-cuda90`をインストールしていますが、この末尾の2つの数字はCUDAのバージョンを表しています。お使いの環境のCUDAバージョンに合わせて、

- `cupy-cuda80`（CUDA 8.0用）
- `cupy-cuda90`（CUDA9.0用）
- `cupy-cuda91`（CUDA9.1用）

の3つから適切なものを選択して`pip install`してください。CuPy v4.0.0からwheelでのインストールが可能となりましたので、これにより自動的にcuDNNやNCCL2といったライブラリもインストールされ、CuPyから使用可能になります。（cuDNNを独立に取ってくる方法を注に書いておきます[^cudnnenv]）

また、Chainer v4.0.0から`chainer.print_runtime_info()`という便利なメソッドが追加されました。以下のコマンドをターミナルで実行し、ChainerやCuPyが正しくインストールされたかを確認してみましょう。


```bash
%%bash
python -c 'import chainer; chainer.print_runtime_info()'
```

    Chainer: 4.0.0
    NumPy: 1.14.0
    CuPy:
      CuPy Version          : 4.0.0
      CUDA Root             : /usr/local/cuda
      CUDA Build Version    : 9000
      CUDA Driver Version   : 9000
      CUDA Runtime Version  : 9000
      cuDNN Build Version   : 7102
      cuDNN Version         : 7005
      NCCL Build Version    : 2104


うまくできていますね。以下のチュートリアルでは、matplotlibを可視化に使いますので、これも同時にインストールしておきましょう。


```bash
%%bash
pip install matplotlib
```

また、計算グラフの可視化にGraphvizを使いますので、こちらもインストールしておいてください。

# 学習ループを書いてみよう

ここでは、有名な手書き数字のデータセットMNISTを使って、画像を10クラスに分類するネットワークを書いて訓練してみます。

## 1. データセットの準備

教師あり学習の場合、**データセットは「入力データ」と「それと対になるラベルデータ」を返すオブジェクトである必要があります。**
ChainerにはMNISTやCIFAR10/100のようなよく用いられるデータセットに対して、データをダウンロードしてくるところからそのような機能をもったオブジェクトを作るところまで自動的にやってくれる便利なメソッドがあるので、ここではひとまずこれを用いましょう。


```python
from chainer.datasets import mnist

# データセットがダウンロード済みでなければ、ダウンロードも行う
train_val, test = mnist.get_mnist(withlabel=True, ndim=1)
```

データセットオブジェクト自体は準備ができました。これは、例えば `train_val[i]` などとすると**i番目の `(data, label)` というタプルを返すリスト** と同様のものになっています（**実際ただのPythonリストもChainerのデータセットオブジェクトとして使えます**）。では0番目のデータとラベルを取り出して、表示してみましょう。


```python
# matplotlibを使ったグラフ描画結果がnotebook内に表示されるようにします。
%matplotlib inline
import matplotlib.pyplot as plt

# データの例示
x, t = train_val[0]  # 0番目の (data, label) を取り出す
plt.imshow(x.reshape(28, 28), cmap='gray')
plt.axis('off')
plt.show()
print('label:', t)
```

![output_14_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/0b128cb9-0be4-5d06-76c8-91e67d5fcb50.png)

    label: 5


### 1.1 Validation用データセットを作る

次に、上で作成した`train_val`データセットを、Training用のデータセットとValidation用のデータセットに分割しましょう。これもChainerの便利な関数を使えば簡単にできます。元々60000個のデータが入っている`train`データセット50000個のデータをTraining用に、残りの10000個をValidation用にしてみます。


```python
from chainer.datasets import split_dataset_random

train, valid = split_dataset_random(train_val, 50000, seed=0)
```

これだけで元々の`train_val`を、ランダムに選んだ50000個の`train`データセットと`valid`データセットに分けることができました。何度も実行する際に異なる分け方になってしまわないよう、第3引数の`seed`を設定しておくことをオススメします。それでは、それぞれのデータセットの中に入っているデータの数を確認してみましょう。


```python
print('Training dataset size:', len(train))
print('Validation dataset size:', len(valid))
```

    Training dataset size: 50000
    Validation dataset size: 10000


## 2. Iteratorの作成

データセットの準備は完了しましたが、このままネットワークの学習に使うのは少し面倒です。なぜなら、ネットワークのパラメータ最適化手法として広く用いられているStochastic Gradient Descent (SGD)という手法では、一般的にいくつかのデータを束ねた**ミニバッチ**と呼ばれる単位でネットワークにデータを渡し、それに対する予測を作って、ラベルと比較するということを行います。そのため、**バッチサイズ分だけデータとラベルを束ねる作業が必要です。**

そこで、**データセットから決まった数のデータとラベルを取得し、それらを束ねてミニバッチを作ってくれる機能を持った`Iterator`を使いましょう。**`Iterator`は、先程作ったデータセットオブジェクトを渡して初期化してやったあとは、`next()`メソッドで新しいミニバッチを返してくれます。内部ではデータセットを何周なめたか（`epoch`）などの情報がどうように記録されているおり、学習ループを書いていく際に便利です。

データセットオブジェクトからイテレータを作るには、以下のようにします。


```python
from chainer import iterators

batchsize = 128

train_iter = iterators.SerialIterator(train, batchsize)
valid_iter = iterators.SerialIterator(
    valid, batchsize, repeat=False, shuffle=False)
test_iter = iterators.SerialIterator(
    test, batchsize, repeat=False, shuffle=False)
```

ここでは、学習に用いるデータセット用のイテレータ（`train_iter`）と、検証用のデータセット用のイテレータ（`valid_iter`）、および学習したネットワークの評価に用いるテストデータセット用のイテレータ（`test_iter`）の計3つを作成しています。ここで、`batchsize = 128`としているので、作成した3つの`Iterator`は、例えば`train_iter.next()`などとすると128枚の数字画像データを一括りにして返してくれます。

#### NOTE: `SerialIterator`について

Chainerがいくつか用意している`Iterator`の一種である`SerialIterator`は、データセットの中のデータを順番に取り出してくる最もシンプルな`Iterator`です。コンストラクタの引数にデータセットオブジェクトと、バッチサイズを取ります。このとき、渡したデータセットオブジェクトから、何周も何周もデータを繰り返し読み出す必要がある場合は`repeat`引数を`True`とし、1周が終わったらそれ以上データを取り出したくない場合はこれを`False`とします。これは、主にvalidation用のデータセットに対して使うフラグです。デフォルトでは、`True`になっています。また、`shuffle`引数に`True`を渡すと、データセットから取り出されてくるデータの順番をエポックごとにランダムに変更します。`SerialIterator`の他にも、マルチプロセスで高速にデータを処理できるようにした`MultiprocessIterator`や`MultithreadIterator`など、複数の`Iterator`が用意されています。詳しくは以下を見てください。

- [Chainerで使えるIterator一覧](https://docs.chainer.org/en/stable/reference/iterators.html)

## 3. ネットワークの定義

では、学習させるネットワークを定義してみましょう。今回は、全結合層のみからなる多層パーセプトロンを作ってみます。中間層のユニット数は適当に100とし、今回は10クラス分類をしたいので、出力ユニット数は10とします。今回用いるMNISTデータセットは0〜9までの数字のいずれかを意味する10種のラベルを持つためです。では、ネットワークを定義するために必要な`Link`, `Function`, そして`Chain`について、簡単にここで説明を行います。

### LinkとFunction

Chainerでは、ニューラルネットワークの各層を、`Link`と`Function`に区別します。

- **`Link`は、パラメータを持つ関数です。**
- **`Function`は、パラメータを持たない関数です。**

これらを組み合わせてネットワークを記述します。パラメータを持つ層は、`chainer.links`モジュール以下にたくさん用意されています。パラメータを持たない層は、`chainer.functions`モジュール以下にたくさん用意されています。これらに簡単にアクセスするために、

```
import chainer.links as L
import chainer.functions as F
```

と別名を与えて、`L.Convolution2D(...)`や`F.relu(...)`のように用いる慣習がありますが、特にこれが決まった書き方というわけではありません。

### Chain

`Chain`は、**パラメータを持つ層（`Link`）をまとめておくためのクラス**です。パラメータを持つということは、基本的にネットワークの学習の際にそれらを更新していく必要があるということです（更新されないパラメータを持たせることもできます）。Chainerでは、モデルのパラメータの更新は、`Optimizer`という機能が担います。その際、更新すべき全てのパラメータを簡単に発見できるように、`Chain`で一箇所にまとめておきます。そうすると、`Chain.params()`メソッドを使って**更新されるパラメータ一覧が簡単に取得できます。**

### Chainを継承してネットワークを定義しよう

Chainerでは、ネットワークは`Chain`クラスを継承したクラスとして定義されることが一般的です。その場合、そのクラスのコンストラクタで、`self.init_scope()`で作られる`with`コンテキストを作り、その中でネットワークに登場する`Link`をプロパティとして登録しておきます。こうすると、自動的に`Optimizer`が最適化対象のパラメータを持つ層だな、と捉えてくれます。

もう一つ、一般的なのは、ネットワークの前進計算（データを渡して、出力を返す）を、`__call__`メソッドに書いておくという方法です。こうすると、ネットワーククラスをinstantiateして作ったオブジェクトを、関数のようにして使うことができます（例：`output = net(data)`）。

### GPUで実行するには

`Chain`クラスは`to_gpu`メソッドを持ち、この引数にGPU IDを指定すると、指定したGPU IDのメモリ上にネットワークの全パラメータを転送します。こうしておくと、前進計算も学習の際のパラメータ更新なども全部GPU上で行われるようになります。GPU IDとして-1を使うと、すなわちこれはCPUを意味します。

### 同じ結果を保証したい

ネットワークを書き始める前に、まずは乱数シードを固定して、本記事とほぼ同様の結果が再現できるようにしておきましょう。（cuDNNが有効になっている環境下でより厳密に計算結果の再現性を保証したい場合は、`chainer.config.cudnn_deterministic`というConfiguringオプションについて知る必要があります。こちらのドキュメントを参照してください：[chainer.config.cudnn_deterministic](https://docs.chainer.org/en/stable/reference/configuration.html?highlight=chainer.config.cudnn_deterministic)。


```python
import random
import numpy
import chainer

def reset_seed(seed=0):
    random.seed(seed)
    numpy.random.seed(seed)
    if chainer.cuda.available:
        chainer.cuda.cupy.random.seed(seed)
        
reset_seed(0)
```

### ネットワークを表すコード

いよいよネットワークを書いてみます！


```python
import chainer
import chainer.links as L
import chainer.functions as F

class MLP(chainer.Chain):

    def __init__(self, n_mid_units=100, n_out=10):
        super(MLP, self).__init__()
        
        # パラメータを持つ層の登録
        with self.init_scope():
            self.l1 = L.Linear(None, n_mid_units)
            self.l2 = L.Linear(n_mid_units, n_mid_units)
            self.l3 = L.Linear(n_mid_units, n_out)

    def __call__(self, x):
        # データを受け取った際のforward計算を書く
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)

gpu_id = 0  # CPUを用いる場合は、この値を-1にしてください

net = MLP()

if gpu_id >= 0:
    net.to_gpu(gpu_id)
```

できました！疑問点はありませんか？ちなみに、Chainerにはたくさんの学習可能なレイヤやパラメータを持たないレイヤが用意されています。ぜひ一度以下の一覧のページを見てみましょう。

- [Chainerで使える関数(`Function`)一覧](https://docs.chainer.org/en/stable/reference/functions.html)
- [Chainerで学習できるレイヤ(`Link`)一覧](https://docs.chainer.org/en/stable/reference/links.html)

`Link`一覧には、ニューラルネットワークによく用いられる全結合層や畳み込み層、LSTMなどや、ReLUなどの活性化関数などなどだけでなく、有名なネットワーク全体も`Link`として載っています。ResNetや、VGGなどです。また、`Function`一覧には、画像の大きさをresizeしたり、サイン・コサインのような関数を始め、いろいろなネットワークの要素として使える関数が載っています。

#### NOTE

上のネットワーク定義で、`L.Linear`は全結合層を意味しますが、最初のLinear層は第一引数に`None`が渡されています。これは、実行時に、つまり**データがその層に入力された瞬間、必要な数の入力側ユニット数を自動的に計算する**ということを意味します。ネットワークが最初に計算を行う際に、初めて `(n_input)` $\times$ `n_mid_units` の大きさの行列を作成し、それを学習対象とするパラメータとして保持します。これは後々、畳み込み層を全結合層の前に配置する際などに便利な機能です。

様々な`Link`は、それぞれ学習対象となるパラメータを保持しています。それらの値は、NumPyの配列として簡単に取り出して見ることができます。例えば、上のモデル`MLP`は`l1`という名前の全結合層が登録されています。この全結合層は重み行列`W`とバイアス`b`という2つのパラメータを持ちます。これらには外から以下のようにしてアクセスすることができます：


```python
print('1つ目の全結合層のバイアスパラメータの形は、', net.l1.b.shape)
print('初期化直後のその値は、', net.l1.b.array)
```

    1つ目の全結合層のバイアスパラメータの形は、 (100,)
    初期化直後のその値は、 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
     0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
     0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
     0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
     0. 0. 0. 0.]


しかしここで、`net.l1.W.array`の中身を同様に表示してみようとすると、`None`が返されます。


```python
print(net.l1.W.array)
```

    None


なぜでしょうか？我々は`l1`をネットワークに登録するときに、`L.Linear`の第一引数に`None`を渡しましたね。そして、**まだネットワークに一度もデータを入力していません**。そのため、**まだ重み行列`W`は作成されていません。**そのため、まだ`net.l1.W`は具体的な配列を保持していないのです。

## 4. 最適化手法の選択

では、上で定義したネットワークをMNISTデータセットを使って訓練してみましょう。学習時に用いる最適化の手法としてはいろいろな種類のものが提案されていますが、Chainerは多くの手法を同一のインターフェースで利用できるよう、`Optimizer`という機能でそれらを提供しています。`chainer.optimizers`モジュール以下に色々なものを見つけることができます。一覧はこちらにあります：

- [Chainerで使える最適化手法一覧](https://docs.chainer.org/en/stable/reference/optimizers.html)

ここでは最もシンプルな勾配降下法の手法である`optimizers.SGD`を用います。`Optimizer`のオブジェクトには、`setup`メソッドを使ってモデル（`Chain`オブジェクト）を渡します。こうすることで`Optimizer`に、何を最適化すればいいか把握させることができます。

他にもいろいろな最適化手法が手軽に試せるので、色々と試してみて結果の変化を見てみてください。例えば、下の`chainer.optimizers.SGD`のうち`SGD`の部分を`MomentumSGD`, `RMSprop`,  `Adam`などに変えるだけで、最適化手法の違いがどのような学習曲線（ロスカーブ）の違いを生むかなどを簡単に調べることができます。


```python
from chainer import optimizers

optimizer = optimizers.SGD(lr=0.01).setup(net)
```

#### NOTE

今回はSGDのコンストラクタの`lr`という引数に $0.01$ を与えました。この値は学習率として知られ、モデルをうまく訓練して良いパフォーマンスを発揮させるために調整する必要がある重要な**ハイパーパラメータ**として知られています。

## 5. 学習する

いよいよ学習をスタートします！今回は分類問題なので、`softmax_cross_entropy`というロス関数を使って最小化すべきロスの値を計算します。

まず、ネットワークにデータを渡して、出てきた出力と、入力データに対応する正解ラベルを、`Function`の一種でありスカラ値を返す**ロス関数**に渡し、ロス（最小化したい値）の計算を行います。ロスは、`chainer.Variable`のオブジェクトになっています。そして、この`Variable`は、**今まで自分にどんな計算が施されたかを辿れるようになっています。**この仕組みが、Define-by-Run [[Tokui 2015]](http://learningsys.org/papers/LearningSys_2015_paper_33.pdf)とよばれる発明の中心的な役割を果たしています。

ここでは誤差逆伝播法自体の説明は割愛しますが、**計算したロスに対する勾配をネットワークに逆向きに流していく**処理は、Chainerではネットワークが吐き出した`Variable`が持つ`backward()`メソッドを呼ぶだけでできます。これを呼ぶと、前述のようにこれまでの計算過程を逆向きに遡って**誤差逆伝播用の計算グラフを構築し**、途中のパラメータの勾配を連鎖率を使って計算してくれます。（詳しくは筆者が[日本ソフトウェア科学会で行ったチュートリアルの資料](https://www.slideshare.net/mitmul/chainer-79942361)をご覧ください。）

こうして計算された各パラメータに対する勾配を使って、先程`Optimizer`を作成する際に指定したアルゴリズムを使ってネットワークパラメータの更新（＝学習）が行われるわけです。

まとめると、今回1回の更新処理の中で行うのは、以下の4項目です。

1. ネットワークにデータを渡して出力`y`を得る
2. 出力`y`と正解ラベル`t`を使って、最小化すべきロスの値を`softmax_cross_entropy`関数で計算する
3. `softmax_cross_entropy`関数の出力（`Variable`）の`backward()`メソッドを呼んで、ネットワークの全てのパラメータの勾配を誤差逆伝播法で計算する
4. Optimizerの`update`メソッドを呼び、3.で計算した勾配を使って全パラメータを更新する

パラメータの更新は、何度も何度も繰り返し行います。一度の更新に用いられるデータは、ネットワークに入力されたバッチサイズ分だけ束ねられたデータのみです。そのため、データセット全体のデータを使うために、次のミニバッチを入力して再度更新、その次のミニバッチを使ってまた更新、ということを繰り返すわけです。そのため、この過程を学習ループと呼んでいます。

#### NOTE: ロス関数

ちなみに、ロス関数は、例えば分類問題ではなく簡単な回帰問題を解きたいような場合、`F.softmax_cross_entropy`の代わりに`F.mean_squared_error`などを用いることもできます。他にも、いろいろな問題設定に対応するために様々なロス関数がChainerには用意されています。こちらからその一覧を見ることができます：

- [Chainerで使えるロス関数一覧](http://docs.chainer.org/en/stable/reference/functions.html#loss-functions)

### 学習ループのコード


```python
import numpy as np
from chainer.dataset import concat_examples
from chainer.cuda import to_cpu

max_epoch = 10

while train_iter.epoch < max_epoch:
    
    # ---------- 学習の1イテレーション ----------
    train_batch = train_iter.next()
    x, t = concat_examples(train_batch, gpu_id)
    
    # 予測値の計算
    y = net(x)

    # ロスの計算
    loss = F.softmax_cross_entropy(y, t)

    # 勾配の計算
    net.cleargrads()
    loss.backward()

    # パラメータの更新
    optimizer.update()
    # --------------- ここまで ----------------

    # 1エポック終了ごとにValidationデータに対する予測精度を測って、
    # モデルの汎化性能が向上していることをチェックしよう
    if train_iter.is_new_epoch:  # 1 epochが終わったら

        # ロスの表示
        print('epoch:{:02d} train_loss:{:.04f} '.format(
            train_iter.epoch, float(to_cpu(loss.data))), end='')

        valid_losses = []
        valid_accuracies = []
        while True:
            valid_batch = valid_iter.next()
            x_valid, t_valid = concat_examples(valid_batch, gpu_id)

            # Validationデータをforward
            with chainer.using_config('train', False), \
                    chainer.using_config('enable_backprop', False):
                y_valid = net(x_valid)

            # ロスを計算
            loss_valid = F.softmax_cross_entropy(y_valid, t_valid)
            valid_losses.append(to_cpu(loss_valid.array))

            # 精度を計算
            accuracy = F.accuracy(y_valid, t_valid)
            accuracy.to_cpu()
            valid_accuracies.append(accuracy.array)
                        
            if valid_iter.is_new_epoch:
                valid_iter.reset()
                break

        print('val_loss:{:.04f} val_accuracy:{:.04f}'.format(
            np.mean(valid_losses), np.mean(valid_accuracies)))
        
# テストデータでの評価
test_accuracies = []
while True:
    test_batch = test_iter.next()
    x_test, t_test = concat_examples(test_batch, gpu_id)

    # テストデータをforward
    with chainer.using_config('train', False), \
            chainer.using_config('enable_backprop', False):
        y_test = net(x_test)

    # 精度を計算
    accuracy = F.accuracy(y_test, t_test)
    accuracy.to_cpu()
    test_accuracies.append(accuracy.array)

    if test_iter.is_new_epoch:
        test_iter.reset()
        break

print('test_accuracy:{:.04f}'.format(np.mean(test_accuracies)))
```

    epoch:01 train_loss:0.9393 val_loss:0.9708 val_accuracy:0.8019
    epoch:02 train_loss:0.6163 val_loss:0.5335 val_accuracy:0.8650
    epoch:03 train_loss:0.4596 val_loss:0.4236 val_accuracy:0.8835
    epoch:04 train_loss:0.5109 val_loss:0.3750 val_accuracy:0.8931
    epoch:05 train_loss:0.3167 val_loss:0.3449 val_accuracy:0.9017
    epoch:06 train_loss:0.4418 val_loss:0.3263 val_accuracy:0.9074
    epoch:07 train_loss:0.2389 val_loss:0.3103 val_accuracy:0.9122
    epoch:08 train_loss:0.4076 val_loss:0.2977 val_accuracy:0.9149
    epoch:09 train_loss:0.3687 val_loss:0.2930 val_accuracy:0.9151
    epoch:10 train_loss:0.3271 val_loss:0.2800 val_accuracy:0.9190
    test_accuracy:0.9375


`val_accuracy`に着目してみると、最終的におおよそ92%程度の精度で手書きの数字が分類できるようになりました。**学習終了後**に、ループの中でValidationデータセットを使ってモデルの汎化性能をおおまかにチェックしているのと同様にして、**テスト用のデータセットを用いて学習が終了したネットワークの評価を行っています。**テストデータでの評価結果は、およそ93.75%の正解率となりました。

### 5.1 ValidationやTestを行う際の注意点

ここで、ValidationにせよTestにせよ、「評価」を行う際には注意すべき点があります。学習は行わない、評価のためだけのデータをネットワークに渡して出力を計算している部分（例えば、`y_test = net(x_test)`）では、それらの行を2つのコンテキストでくくっています。

#### `chainer.using_config('train', False)`

まず、今回は学習時と推論時で動作が異なる関数は含まれていないため、実際の効力は持ちませんが、Validationやテストのために推論を行うときは`chainer.config.train = False`とします。以下のように、`chainer.using_config('train', False)`をwith構文と共に使えば、その中では`chainer.config.train = False`となります。

```python
with chainer.using_config('train', False):
    --- 何か推論処理 ---
```

これは、以下のようにするのと同じことです。

```python
chainer.config.train = False

--- 何か推論処理 ---
```

ただし、Pythonのコンテキストを利用しない場合は、一度このようにどこかで書くと、それ以降この設定はグローバルにずっと有効になることに注意してください。（推論したあと再び学習を行うという場合は、再度`chainer.config.train = True`などのようにすることが必要になります。`chainer.config`以下の規定の値に何かを代入することはグローバルに作用しますので、次に説明する`enable_backprop`についても同様です。）

#### `chainer.using_config('enable_backprop', False)`

次に、今回は評価に用いる出力の計算後にロス関数の各パラメータについての勾配は必要ないので、内部に計算グラフを保持しておく必要もないため、`chainer.using_config('enable_backprop', False)`として**無駄な計算グラフの構築を行わないようにし、メモリ消費量を節約しています。**

#### NOTE: ChainerのConfig

Chainerにはこの他にも、いくつかのグローバルなConfigがプリセットとして用意されています。また、`chainer.config`以下にユーザが自由な設定値を置くこともできます。詳しくはこちらを一読してください：[Configuring Chainer](https://docs.chainer.org/en/stable/reference/configuration.html)

## 6. 学習済みモデルを保存する

学習が終わったら、その結果を保存します。Chainerには、2種類のフォーマットで学習済みネットワークをシリアライズする機能が用意されています。一つはHDF5形式で、もう一つはNumPyのNPZ形式でネットワークを保存するものです。今回は、追加ライブラリのインストールが必要なHDF5ではなく、NumPy標準機能で提供されているシリアライズ機能（`numpy.savez()`）を利用したNPZ形式でのモデルの保存を行います。


```python
from chainer import serializers

serializers.save_npz('my_mnist.model', net)
```


```python
# ちゃんと保存されていることを確認
%ls -la my_mnist.model
```

    -rw-rw-r-- 1 shunta shunta 333944 Apr 22 00:52 my_mnist.model


## 7. 保存したモデルを読み込んで推論する

学習したネットワークを、それを使って数字の分類がしたい誰かに渡して、使ってもらうにはどうしたら良いでしょうか。もっともシンプルな方法は、ネットワークの定義がかかれたPythonファイルと、今しがた保存したNPZファイルを渡して、以下のように使うことです。以下のコードの前に、渡したネットワーク定義のファイルからネットワークのクラス（ここでは`MLP`）が読み込まれていることを前提とします。


```python
# まず同じネットワークのオブジェクトを作る
infer_net = MLP()

# そのオブジェクトに保存済みパラメータをロードする
serializers.load_npz('my_mnist.model', infer_net)
```

以上で準備が整いました。それでは、試しにテストデータの中から一つ目の画像を取ってきて、それに対する分類を行ってみましょう。


```python
gpu_id = 0  # CPUで計算をしたい場合は、-1を指定してください

if gpu_id >= 0:
    infer_net.to_gpu(gpu_id)

# 1つ目のテストデータを取り出します
x, t = test[0]  #  tは使わない

# どんな画像か表示してみます
plt.imshow(x.reshape(28, 28), cmap='gray')
plt.show()

# ミニバッチの形にする（複数の画像をまとめて推論に使いたい場合は、サイズnのミニバッチにしてまとめればよい）
print('元の形：', x.shape, end=' -> ')

x = x[None, ...]

print('ミニバッチの形にしたあと：', x.shape)

# ネットワークと同じデバイス上にデータを送る
x = infer_net.xp.asarray(x)

# モデルのforward関数に渡す
with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
    y = infer_net(x)

# Variable形式で出てくるので中身を取り出す
y = y.array

# 結果をCPUに送る
y = to_cpu(y)

# 予測確率の最大値のインデックスを見る
pred_label = y.argmax(axis=1)

print('ネットワークの予測:', pred_label[0])
```

![output_45_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/b491df38-6595-6987-e426-bd4e23ff8dc6.png)


    元の形： (784,) -> ミニバッチの形にしたあと： (1, 784)
    ネットワークの予測: 7


ネットワークの予測は7でした。画像を見る限り、当たっていそうですね！

# Trainerを使ってみよう

Chainerは、これまで書いてきたような学習ループを隠蔽する`Trainer`という機能を提供しています。これを使うと、学習ループを陽に書く必要がなくなり、またいろいろな便利なExtentionを使うことで、学習過程でのロスカーブの可視化や、ログの保存などが楽になります。

## 1. データセット・Iterator・ネットワークの準備

これらはループを自分で書く場合と同じなので、まとめてしまいます。


```python
reset_seed(0)

train_val, test = mnist.get_mnist()
train, valid = split_dataset_random(train_val, 50000, seed=0)

batchsize = 128

train_iter = iterators.SerialIterator(train, batchsize)
valid_iter = iterators.SerialIterator(valid, batchsize, False, False)
test_iter = iterators.SerialIterator(test, batchsize, False, False)

gpu_id = 0  # CPUを用いたい場合は、-1を指定してください

net = MLP()

if gpu_id >= 0:
    net.to_gpu(gpu_id)
```

## 2. Updaterの準備

ここからが学習ループを自分で書く場合と異なる部分です。ループを自分で書く場合には、データセットからバッチサイズ分のデータをとってきてミニバッチに束ねて、それをネットワークに入力して予測を作り、それを正解と比較し、ロスを計算してバックワード（誤差逆伝播）をして、`Optimizer`によってパラメータを更新する、というところまでを、以下のように書いていました。

```python
# ---------- 学習の1イテレーション ----------
train_batch = train_iter.next()
x, t = concat_examples(train_batch, gpu_id)

# 予測値の計算
y = net(x)

# ロスの計算
loss = F.softmax_cross_entropy(y, t)

# 勾配の計算
net.cleargrads()
loss.backward()

# パラメータの更新
optimizer.update()
```

これらの処理を、まるっと`Updater`はまとめてくれます。これを行うために、**`Updater`には`Iterator`と`Optimizer`を渡してやります。** `Iterator`はデータセットオブジェクトを持っていて、そこからミニバッチを作り、`Optimizer`は最適化対象のネットワークを持っていて、それを使って前進計算とロスの計算・パラメータのアップデートをすることができます。そのため、この2つを渡しておけば、上記の処理を`Updater`内で全部行ってもらえるというわけです。では、`Updater`オブジェクトを作成してみましょう。


```python
from chainer import training

gpu_id = 0  # CPUを使いたい場合は-1を指定してください

# ネットワークをClassifierで包んで、ロスの計算などをモデルに含める
net = L.Classifier(net)

# 最適化手法の選択
optimizer = optimizers.SGD(lr=0.01).setup(net)

# UpdaterにIteratorとOptimizerを渡す
updater = training.StandardUpdater(train_iter, optimizer, device=gpu_id)
```

#### NOTE

ここでは、ネットワークを`L.Classifier`で包んでいます。`L.Classifier`は一種の`Chain`になっていて、渡されたネットワーク自体を`predictor`というattributeに持ち、**ロス計算を行う機能を追加してくれます。**こうすると、`net()`はデータ`x`だけでなくラベル`t`も取るようになり、まず渡されたデータを`predictor`に通して予測を作り、それを`t`と比較して**ロスの`Variable`を返すようになります。**ロス関数として何を用いるかはデフォルトでは`F.softmax_cross_entropy`となっていますが、`L.Classifier`の引数`lossfunc`にロス計算を行う関数を渡してやれば変更することができるため、Classifierという名前ながら回帰問題などのロス計算機能の追加にも使うことができます。（`L.Classifier(net, lossfun=F.mean_squared_error, compute_accuracy=False)`のようにする）

`StandardUpdater`は前述のような`Updater`の担当する処理を遂行するための最もシンプルなクラスです。この他にも複数のGPUを用いるための`ParallelUpdater`などが用意されています。

## 3. Trainerの準備

実際に学習ループ部分を隠蔽しているのは`Updater`なので、これがあればもう学習を始められそうですが、`Trainer`はさらに`Updater`を受け取って学習全体の管理を行う機能を提供しています。例えば、**データセットを何周したら学習を終了するか(stop_trigger)** や、**途中のロスの値をどのファイルに保存したいか**、**ロスカーブを可視化した画像ファイルを保存するかどうか**など、学習全体の設定として必須・もしくはあると便利な色々な機能を提供しています。

必須なものとしては学習終了のタイミングを指定する`stop_trigger`がありますが、これは`Trainer`オブジェクトを作成するときのコンストラクタで指定します。指定の方法は単純で、`(長さ, 単位)`という形のタプルを与えればよいだけです。「長さ」には数字を、「単位」には`'iteration'`もしくは`'epoch'`のいずれかの文字列を指定します。こうすると、たとえば100 epoch（データセット100周）で学習を終了してください、とか、1000 iteration（1000回更新）で学習を終了してください、といったことが指定できます。`Trainer`を作るときに、`stop_trigger`を指定しないと、学習は自動的には止まりません。

では、実際に`Trainer`オブジェクトを作ってみましょう。


```python
max_epoch = 10

# TrainerにUpdaterを渡す
trainer = training.Trainer(
    updater, (max_epoch, 'epoch'), out='mnist_result')
```

`out`引数では、この次に説明する`Extension`を使って、ログファイルやロスの変化の過程を描画したグラフの画像ファイルなどを保存するディレクトリを指定しています。

Trainerと、その内側にあるいろいろなオブジェクトの関係は、図にまとめると以下のようになっています。このイメージを持っておくと自分で部分的に改造したりする際に便利だと思います。

![image](https://qiita-image-store.s3.amazonaws.com/0/17934/a751df31-b999-f692-d839-488c26b1c48a.png)

## 4. TrainerにExtensionを追加する

`Trainer`を使う利点として、

- ログを自動的にファイルに保存（`LogReport`)
- ターミナルに定期的にロスなどの情報を表示（`PrintReport`）
- ロスを定期的にグラフで可視化して画像として保存（`PlotReport`)
- 定期的にモデルやOptimizerの状態を自動シリアライズ（`snapshot`）
- 学習の進捗を示すプログレスバーを表示（`ProgressBar`）
- ネットワークの構造をGraphvizのdot形式で保存（`dump_graph`）
- ネットワークのパラメータの平均や分散などの統計情報を出力（`ParameterStatistics`）

などなどの様々な便利な機能を簡単に利用することができる点があります。これらの機能を利用するには、`Trainer`オブジェクトに対して`extend`メソッドを使って追加したい`Extension`のオブジェクトを渡してやるだけです。では実際に幾つかの`Extension`を追加してみましょう。


```python
from chainer.training import extensions

trainer.extend(extensions.LogReport())
trainer.extend(extensions.snapshot(filename='snapshot_epoch-{.updater.epoch}'))
trainer.extend(extensions.Evaluator(valid_iter, net, device=gpu_id), name='val')
trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'main/accuracy', 'val/main/loss', 'val/main/accuracy', 'l1/W/data/std', 'elapsed_time']))
trainer.extend(extensions.ParameterStatistics(net.predictor.l1, {'std': np.std}))
trainer.extend(extensions.PlotReport(['l1/W/data/std'], x_key='epoch', file_name='std.png'))
trainer.extend(extensions.PlotReport(['main/loss', 'val/main/loss'], x_key='epoch', file_name='loss.png'))
trainer.extend(extensions.PlotReport(['main/accuracy', 'val/main/accuracy'], x_key='epoch', file_name='accuracy.png'))
trainer.extend(extensions.dump_graph('main/loss'))
```

### `LogReport`

`epoch`や`iteration`ごとの`loss`, `accuracy`などを自動的に集計し、`Trainer`の`out`引数で指定した出力ディレクトリに`log`というファイル名で保存します。

### `snapshot`

`Trainer`の`out`引数で指定した出力ディレクトリに`Trainer`オブジェクトを指定されたタイミング（デフォルトでは1エポックごと）に保存します。`Trainer`オブジェクトは上述のように`Updater`を持っており、この中に`Optimizer`とモデルが保持されているため、この`Extension`でスナップショットをとっておけば、学習の復帰や学習済みモデルを使った推論などが学習終了後にも可能になります。

### `dump_graph`

指定された`Variable`オブジェクトから辿れる計算グラフをGraphvizのdot形式で保存します。保存先は`Trainer`の`out`引数で指定した出力ディレクトリです。

### `Evaluator`

評価用のデータセットの`Iterator`と、学習に使うモデルのオブジェクトを渡しておくことで、学習中のモデルを指定されたタイミングで評価用データセットを用いて評価します。内部では、`chainer.config.using_config('train', False)`が自動的に行われます。`backprop_enable`を`False`にすることは行われないため、メモリ使用効率はデフォルトでは最適ではありませんが、基本的には`Evaluator`を使えば評価を行うという点において問題はありません。

### `PrintReport`

`Reporter`によって集計された値を標準出力に出力します。このときどの値を出力するかを、リストの形で与えます。

### `PlotReport`

引数のリストで指定された値の変遷を`matplotlib`ライブラリを使ってグラフに描画し、出力ディレクトリに`file_name`引数で指定されたファイル名で画像として保存します。

### `ParameterStatistics`

指定したレイヤ（Link）が持つパラメータの平均・分散・最小値・最大値などなどの統計情報を計算して、ログに保存します。パラメータが発散していないかなどをチェックするのに便利です。

---

これらの`Extension`は、ここで紹介した以外にも、例えば`trigger`によって個別に作動するタイミングを指定できるなどのいくつかのオプションを持っており、より柔軟に組み合わせることができます。詳しくは公式のドキュメントを見てください

- [ChainerのTrainer extension一覧](http://docs.chainer.org/en/stable/reference/extensions.html)

## 5. 学習を開始する

学習を開始するには、`Trainer`オブジェクトのメソッド`run`を呼ぶだけです！


```python
trainer.run()
```

    epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  l1/W/data/std  elapsed_time
    1           1.6691      0.599884       0.93909        0.805182           0.0359232      2.04951       
    2           0.672972    0.843211       0.518216       0.866891           0.0366046      4.94805       
    3           0.459943    0.878826       0.415205       0.88657            0.0370376      7.98442       
    4           0.389622    0.893163       0.368696       0.896756           0.0372996      10.9229       
    5           0.353016    0.900895       0.341141       0.904173           0.0374883      13.7746       
    6           0.329993    0.907171       0.324052       0.907733           0.0376418      16.5909       
    7           0.31232     0.911065       0.307601       0.912876           0.037766       20.3998       
    8           0.298098    0.914383       0.294673       0.917128           0.0378814      24.4631       
    9           0.28597     0.918059       0.283434       0.918414           0.0379859      27.2953       
    10          0.275202    0.920836       0.273564       0.921479           0.038085       30.1442       


初めに取り組んだ学習ループを自分で書いた場合よりもより短いコードで、リッチなログ情報とともに、下記で表示してみるようなグラフなども作りつつ、同様の結果を得ることができました。1層目の全結合層の重み行列の値の標準偏差が、学習の進行とともに徐々に大きくなっていっているのも見て取れて、面白いですね。

では、保存されているロスのグラフを確認してみましょう。


```python
from IPython.display import Image
Image(filename='mnist_result/loss.png')
```



![output_62_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/61dd41f0-8c69-d6c6-f8cf-88935b6c07bc.png)



精度のグラフも見てみましょう。


```python
Image(filename='mnist_result/accuracy.png')
```



![output_64_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/bc980263-5e97-738c-2fde-7f01aee8a8fc.png)




もう少し学習を続ければ、まだ多少精度の向上が図れそうな雰囲気がありますね。

ついでに、`dump_graph`という`Extension`が出力した計算グラフを、`Graphviz`を使って画像化して見てみましょう。


```bash
%%bash
dot -Tpng mnist_result/cg.dot -o mnist_result/cg.png
```


```python
Image(filename='mnist_result/cg.png')
```



![output_67_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/331ca5dc-ce16-b1be-3449-abbe82c95788.png)




上から下へ向かって、データやパラメータがどのような`Function`に渡されて計算が行われ、ロスを表す`Variable`が出力されたかが分かります。

## 6. テストデータで評価する

上でもValidationデータに対しての評価を学習中に行うために使用されているTrainer Extensionの一つであるEvaluatorは、Trainerと関係なく独立して使うこともできます。以下のようにして`Iterator`とネットワークのオブジェクト（`net`）、使用するデバイスIDを渡してEvaluatorオブジェクトを作成し、これを関数として実行するだけです。


```python
test_evaluator = extensions.Evaluator(test_iter, net, device=gpu_id)
results = test_evaluator()
print('Test accuracy:', results['main/accuracy'])
```

    Test accuracy: 0.9257318


## 7. 学習済みモデルで推論する

それでは、Trainer Extensionのsnapshotが自動的に保存したネットワークのスナップショットから学習済みパラメータを読み込んで、学習ループを書いて学習したときと同様に1番目のテストデータで推論を行ってみましょう。

ここで注意すべきは、snapshotが保存するnpzファイルはTrainer全体のスナップショットであるため、extensionの内部のパラメータなども一緒に保存されています。これは、学習自体を再開するために必要だからです。しかし、今回はネットワークのパラメータだけを読み込めば良いので、`serializers.load_npz()`のpath引数にネットワーク部分までのパス（`updater/model:main/predictor/`）を指定しています。こうすることで、ネットワークのオブジェクトにパラメータだけを読み込むことができます。


```python
reset_seed(0)

infer_net = MLP()
serializers.load_npz(
    'mnist_result/snapshot_epoch-10',
    infer_net, path='updater/model:main/predictor/')

if gpu_id >= 0:
    infer_net.to_gpu(gpu_id)

x, t = test[0]
plt.imshow(x.reshape(28, 28), cmap='gray')
plt.show()

x = infer_net.xp.asarray(x[None, ...])
with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
    y = infer_net(x)
y = to_cpu(y.array)

print('予測ラベル:', y.argmax(axis=1)[0])
```

![output_72_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/fe51ce5d-0bf6-e237-83e0-9684ed8566c9.png)



    予測ラベル: 7


無事正解できていますね。

# 新しいネットワークを書いてみよう

ここでは、MNISTデータセットではなくCIFAR10という32x32サイズの小さなカラー画像に10クラスのいずれかのラベルがついたデータセットを用いて、いろいろなモデルを自分で書いて試行錯誤する流れを体験してみます。

| airplane | automobile | bird | cat | deer | dog | frog | horse | ship | truck |
|:--------:|:----------:|:----:|:---:|:----:|:---:|:----:|:-----:|:----:|:-----:|
| ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/airplane4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/automobile4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/bird4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/cat4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/deer4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/dog4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/frog4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/horse4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/ship4.png) | ![](https://www.cs.toronto.edu/~kriz/cifar-10-sample/truck4.png) |

## 1. ネットワークの定義

ここでは、さきほど試した全結合層だけからなるネットワークではなく、畳込み層を持つネットワークを定義してみます。3つの畳み込み層を持ち、2つの全結合層がそのあとに続いています。


```python
class MyNet(chainer.Chain):
    
    def __init__(self, n_out):
        super(MyNet, self).__init__()
        with self.init_scope():
            self.conv1 = L.Convolution2D(None, 32, 3, 3, 1)
            self.conv2 = L.Convolution2D(32, 64, 3, 3, 1)
            self.conv3 = L.Convolution2D(64, 128, 3, 3, 1)
            self.fc4 = L.Linear(None, 1000)
            self.fc5 = L.Linear(1000, n_out)
        
    def __call__(self, x):
        h = F.relu(self.conv1(x))
        h = F.relu(self.conv2(h))
        h = F.relu(self.conv3(h))
        h = F.relu(self.fc4(h))
        h = self.fc5(h)
        return h
```

## 2. 学習

ここで、あとから別のネットワークも簡単に同じ設定で訓練できるよう、`train`関数を作っておきます。これは、

- ネットワークのオブジェクト
- バッチサイズ
- 使用するGPU ID
- 学習を終了するエポック数
- データセットオブジェクト
- 学習率の初期値
- 学習率減衰のタイミング

などを渡すと、内部で`Trainer`を用いて渡されたデータセットを使ってネットワークを訓練し、学習が終了した状態のネットワークを返してくれる関数です。`Trainer.run()`が終了した後に、テストデータセットを使って評価まで行ってくれます。先程のMNISTでの例と違い、最適化手法にはMomentumSGDを用い、ExponentialShiftというExtentionを使って、指定したタイミングごとに学習率を減衰させるようにしてみます。

また、ここでは`cifar.get_cifar10()`が返す学習用データセットのうち9割のデータを`train`、残りの1割を`valid`として使うようにしています。

この`train`関数を用いて、上で定義した`MyModel`モデルを訓練してみます。


```python
from chainer.datasets import cifar


def train(network_object, batchsize=128, gpu_id=0, max_epoch=20, train_dataset=None, valid_dataset=None, test_dataset=None, postfix='', base_lr=0.01, lr_decay=None):

    # 1. Dataset
    if train_dataset is None and valid_dataset is None and test_dataset is None:
        train_val, test = cifar.get_cifar10()
        train_size = int(len(train_val) * 0.9)
        train, valid = split_dataset_random(train_val, train_size, seed=0)
    else:
        train, valid, test = train_dataset, valid_dataset, test_dataset

    # 2. Iterator
    train_iter = iterators.MultiprocessIterator(train, batchsize)
    valid_iter = iterators.MultiprocessIterator(valid, batchsize, False, False)

    # 3. Model
    net = L.Classifier(network_object)

    # 4. Optimizer
    optimizer = optimizers.MomentumSGD(lr=base_lr).setup(net)
    optimizer.add_hook(chainer.optimizer.WeightDecay(0.0005))

    # 5. Updater
    updater = training.StandardUpdater(train_iter, optimizer, device=gpu_id)

    # 6. Trainer
    trainer = training.Trainer(updater, (max_epoch, 'epoch'), out='{}_cifar10_{}result'.format(network_object.__class__.__name__, postfix))
    
    # 7. Trainer extensions
    trainer.extend(extensions.LogReport())
    trainer.extend(extensions.observe_lr())
    trainer.extend(extensions.Evaluator(valid_iter, net, 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.PlotReport(['main/loss', 'val/main/loss'], x_key='epoch', file_name='loss.png'))
    trainer.extend(extensions.PlotReport(['main/accuracy', 'val/main/accuracy'], x_key='epoch', file_name='accuracy.png'))
    if lr_decay is not None:
        trainer.extend(extensions.ExponentialShift('lr', 0.1), trigger=lr_decay)
    trainer.run()
    del trainer
    
    # 8. Evaluation
    test_iter = iterators.MultiprocessIterator(test, batchsize, False, False)
    test_evaluator = extensions.Evaluator(test_iter, net, device=gpu_id)
    results = test_evaluator()
    print('Test accuracy:', results['main/accuracy'])
    
    return net
```


```python
net = train(MyNet(10), gpu_id=0)
```

    epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  elapsed_time  lr        
    1           1.92566     0.304976       1.72248        0.388672           9.64744       0.01        
    2           1.60993     0.423584       1.52611        0.470703           17.999        0.01        
    3           1.46872     0.474114       1.43699        0.495898           26.0586       0.01        
    4           1.39049     0.502264       1.40534        0.49707            33.2387       0.01        
    5           1.33        0.524261       1.35359        0.515039           41.7211       0.01        
    6           1.26348     0.547718       1.30809        0.533594           50.5788       0.01        
    7           1.2179      0.566618       1.30321        0.541211           59.2517       0.01        
    8           1.16269     0.587447       1.23584        0.566406           69.3356       0.01        
    9           1.11824     0.603738       1.22467        0.564258           78.8699       0.01        
    10          1.07353     0.62017        1.20699        0.572852           89.5009       0.01        
    11          1.02482     0.636586       1.19412        0.577734           97.487        0.01        
    12          0.984335    0.652355       1.17003        0.591406           108.51        0.01        
    13          0.942885    0.666171       1.14992        0.595898           117.591       0.01        
    14          0.9011      0.681646       1.15492        0.598828           126.562       0.01        
    15          0.853943    0.698531       1.20026        0.5875             135.419       0.01        
    16          0.812528    0.713319       1.18745        0.597656           145.828       0.01        
    17          0.765726    0.73149        1.20555        0.594922           154.648       0.01        
    18          0.727469    0.74343        1.18027        0.603125           165.423       0.01        
    19          0.672888    0.764423       1.23294        0.596484           176.338       0.01        
    20          0.633698    0.776855       1.21022        0.598633           186.021       0.01        
    Test accuracy: 0.605716


学習が20エポックまで終わりました。ロスと精度のプロットを見てみましょう。


```python
Image(filename='MyNet_cifar10_result/loss.png')
```



![output_80_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/7081525e-8365-983e-4f9d-f9b94942357f.png)





```python
Image(filename='MyNet_cifar10_result/accuracy.png')
```



![output_81_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/c1d59734-5a31-e45e-c7a1-120fc870152b.png)




学習データでの精度（`main/accuracy`)は77%程度まで到達していますが、テストデータでのロス（`val/main/loss`）は途中から下げ止まり、精度（`val/main/accuracy`）も60%前後で頭打ちになってしまっています。表示されたログの最後の行を見ると、テストデータでの精度も同様に60%程度だったようです。学習データでは良い精度が出ているが、 テストデータでは精度が良くないということなので、**モデルが学習データにオーバーフィッティングしている**と思われます。

## 3. 学習済みネットワークを使った予測

テスト精度は60%程度でしたが、試しにこの学習済みネットワークを使っていくつかのテスト画像を分類させてみましょう。あとで使いまわせるように`predict`関数を作っておきます。


```python
cls_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
             'dog', 'frog', 'horse', 'ship', 'truck']

def predict(net, image_id):
    _, test = cifar.get_cifar10()
    x, t = test[image_id]
    net.to_cpu()
    with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
        y = net.predictor(x[None, ...]).data.argmax(axis=1)[0]
    print('predicted_label:', cls_names[y])
    print('answer:', cls_names[t])

    plt.imshow(x.transpose(1, 2, 0))
    plt.show()

for i in range(10, 15):
    predict(net, i)
```

    predicted_label: airplane
    answer: airplane


![output_84_1.png](https://qiita-image-store.s3.amazonaws.com/0/17934/96387ca1-cbb5-778e-c120-bbd04af6a826.png)



    predicted_label: automobile
    answer: truck


![output_84_3.png](https://qiita-image-store.s3.amazonaws.com/0/17934/2045dd20-cf3d-1e95-1a7e-ebad6c5a8ff2.png)


    predicted_label: dog
    answer: dog


![output_84_5.png](https://qiita-image-store.s3.amazonaws.com/0/17934/bf0663c4-7e45-c948-37ee-f893062023a8.png)



    predicted_label: horse
    answer: horse


![output_84_7.png](https://qiita-image-store.s3.amazonaws.com/0/17934/ae32685b-844a-a628-c080-4c6f9fabc486.png)



    predicted_label: truck
    answer: truck


![output_84_9.png](https://qiita-image-store.s3.amazonaws.com/0/17934/1b24b2bc-a5f4-6031-f75b-61b4fa5f87c8.png)



うまく分類できているものもあれば、そうでないものもありました。ネットワークの学習に使用したデータセット上ではほぼ百発百中で正解できるとしても、未知のデータ、すなわちテストデータセットにある画像に対して高精度な予測ができなければ、意味がありません[^NN]。テストデータでの精度は、モデルの**汎化性能**に関係していると言われます。

どうすれば高い汎化性能を持つネットワークを設計し、学習することができるでしょうか？（そんなことが簡単に分かったら苦労しない。）

## 4. もっと深いネットワークを定義してみよう

では、上のネットワークよりもよりたくさんの層を持つネットワークを定義してみましょう。ここでは、1層の畳み込みネットワークを`ConvBlock`、1層の全結合ネットワークを`LinearBlock`として定義し、これをたくさんシーケンシャルに積み重ねる方法で大きなネットワークを定義してみます。

### 構成要素を定義する

まず、今目指している大きなネットワークの構成要素となる`ConvBlock`と`LinearBlock`を定義してみましょう。


```python
class ConvBlock(chainer.Chain):
    
    def __init__(self, n_ch, pool_drop=False):
        w = chainer.initializers.HeNormal()
        super(ConvBlock, self).__init__()
        with self.init_scope():
            self.conv = L.Convolution2D(None, n_ch, 3, 1, 1, nobias=True, initialW=w)
            self.bn = L.BatchNormalization(n_ch)
        self.pool_drop = pool_drop
        
    def __call__(self, x):
        h = F.relu(self.bn(self.conv(x)))
        if self.pool_drop:
            h = F.max_pooling_2d(h, 2, 2)
            h = F.dropout(h, ratio=0.25)
        return h
    
class LinearBlock(chainer.Chain):
    
    def __init__(self, drop=False):
        w = chainer.initializers.HeNormal()
        super(LinearBlock, self).__init__()
        with self.init_scope():
            self.fc = L.Linear(None, 1024, initialW=w)
        self.drop = drop
        
    def __call__(self, x):
        h = F.relu(self.fc(x))
        if self.drop:
            h = F.dropout(h)
        return h
```

`ConvBlock`は`Chain`を継承した小さなネットワークとして定義されています。これは一つの畳み込み層とBatch Normalization層をパラメータありで持っているので、コンストラクタ内でこれらの登録を行っています。`__call__`メソッドでは、これらにデータを渡しつつ、活性化関数ReLUを適用して、さらに`pool_drop`がコンストラクタに`True`で渡されているときはMax PoolingとDropoutという関数を適用するようになっています。

Chainerでは、Pythonを使って書いたforward計算のコード自体がネットワークの構造を表します。すなわち、実行時にデータがどのような層をくぐっていったか、ということがネットワークそのものを定義します。これによって、上記のような分岐などを含むネットワークも簡単に書け、柔軟かつシンプルで可読性の高いネットワーク定義が可能になります。これが**Define-by-Run**と呼ばれる特徴です。

### 大きなネットワークの定義

次に、これらの小さなネットワークを構成要素として積み重ねて、大きなネットワークを定義してみましょう。


```python
class DeepCNN(chainer.ChainList):

    def __init__(self, n_output):
        super(DeepCNN, self).__init__(
            ConvBlock(64),
            ConvBlock(64, True),
            ConvBlock(128),
            ConvBlock(128, True),
            ConvBlock(256),
            ConvBlock(256),
            ConvBlock(256),
            ConvBlock(256, True),
            LinearBlock(),
            LinearBlock(),
            L.Linear(None, n_output)
        )

    def __call__(self, x):
        for f in self:
            x = f(x)
        return x
```

ここで利用しているのが、`ChainList`というクラスです。このクラスは`Chain`を継承したクラスで、いくつもの`Link`や`Chain`を順次呼び出していくようなネットワークを定義するときに便利です。`ChainList`を継承して定義されるモデルは、親クラスのコンストラクタを呼び出す際に**キーワード引数ではなく普通の引数として**`Link`もしくは`Chain`オブジェクトを渡すことができます。そしてこれらは、`self.children()`メソッドによって**登録した順番に**取り出すことができます。`ChainList`自体もPythonのイテレータとして機能するので、例えば`ChainList`を継承したクラスの中で`for f in self:...`といったことも可能です。

この特徴を使うと、forward計算の記述が簡単になります。`self.children()`が返す構成要素のリストから、for文で構成要素を順番に取り出していき、そもそもの入力である`x`に取り出してきた部分ネットワークの計算を適用して、この出力で`x`を置き換えるということを順番に行っていけば、一連の`Link`または`Chain`を、コンストラクタで親クラスに登録した順番と同じ順番で適用していくことができます。そのため、シーケンシャルな部分ネットワークの適用によって表される大きなネットワークを定義するのに重宝します。

それでは、学習を回してみます。今回はパラメータ数も多いので、学習を停止するエポック数を100に設定します。また、学習率を0.1から始めて、30エポックごとに10分の1にするように設定してみます。

#### TIPS

今回は多くの畳込み層を使う大きなネットワークを使うので、Chainerが用意してくれているcuDNNのautotune機能を有効可してみます。やり方は簡単で、以下の二行を事前に実行しておくだけです。


```python
chainer.cuda.set_max_workspace_size(512 * 1024 * 1024)
chainer.config.autotune = True
```

それでは、今度こそ学習を開始してみましょう。


```python
reset_seed(0)

model = train(DeepCNN(10), max_epoch=100, base_lr=0.1, lr_decay=(30, 'epoch'))
```

    epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  elapsed_time  lr        
    1           2.66805     0.163374       2.14499        0.189648           19.3804       0.1         
    2           2.0508      0.237837       2.03172        0.23125            33.5836       0.1         
    3           1.90109     0.285345       1.81864        0.307617           48.0566       0.1         
    4           1.74244     0.34608        2.01173        0.273633           64.1632       0.1         
    5           1.6333      0.389111       1.629          0.397266           79.1679       0.1         
    6           1.50544     0.4466         1.61811        0.431445           92.7088       0.1         
    7           1.35314     0.505876       1.606          0.423242           104.438       0.1         
    8           1.19823     0.567893       1.19526        0.575195           116.22        0.1         
    9           1.0728      0.618208       1.31838        0.549023           129.618       0.1         
    10          0.974313    0.655938       1.00564        0.647266           140.631       0.1         
    11          0.897739    0.686523       0.845641       0.702734           152.881       0.1         
    12          0.837259    0.709046       0.964008       0.673437           164.286       0.1         
    13          0.780534    0.732888       1.1975         0.609375           176.39        0.1         
    14          0.743671    0.745259       0.736362       0.741602           188.494       0.1         
    15          0.717165    0.757191       0.739799       0.743555           201.239       0.1         
    16          0.684576    0.76814        0.796289       0.72207            212.544       0.1         
    17          0.661639    0.778032       0.83021        0.719922           224.69        0.1         
    18          0.633936    0.785423       1.17993        0.651758           237.316       0.1         
    19          0.624017    0.790264       0.981198       0.69043            249.088       0.1         
    20          0.608494    0.794966       0.837451       0.725781           261.854       0.1         
    21          0.585992    0.803864       1.01166        0.696484           274.849       0.1         
    22          0.568304    0.810369       0.861089       0.70625            288.799       0.1         
    23          0.570798    0.810564       1.01788        0.688281           301.476       0.1         
    24          0.559657    0.811901       0.841228       0.728711           313.685       0.1         
    25          0.542451    0.819713       0.851603       0.723047           326.528       0.1         
    26          0.536695    0.823161       0.793133       0.753516           340.287       0.1         
    27          0.525463    0.824774       0.821281       0.742383           352.561       0.1         
    28          0.527928    0.824475       0.629371       0.796094           363.692       0.1         
    29          0.51218     0.830211       0.64138        0.794922           375.972       0.1         
    30          0.513306    0.830373       0.648403       0.7875             387.832       0.1         
    31          0.320646    0.891246       0.394016       0.870898           399.874       0.01        
    32          0.246237    0.916088       0.3797         0.880469           411.885       0.01        
    33          0.223746    0.924006       0.382387       0.883008           424.689       0.01        
    34          0.201898    0.930908       0.378948       0.882422           438.225       0.01        
    35          0.186824    0.93563        0.370399       0.883594           451.346       0.01        
    36          0.173529    0.940541       0.375686       0.886328           463.98        0.01        
    37          0.154772    0.946559       0.413009       0.882812           474.836       0.01        
    38          0.150695    0.947177       0.399807       0.880078           487.226       0.01        
    39          0.139391    0.951011       0.417676       0.884766           498.364       0.01        
    40          0.131167    0.954568       0.405573       0.886719           511.015       0.01        
    41          0.121253    0.958097       0.423057       0.885156           523.911       0.01        
    42          0.120638    0.957866       0.410141       0.883594           536.018       0.01        
    43          0.109189    0.961448       0.441353       0.879297           548.274       0.01        
    44          0.107569    0.961961       0.432192       0.884766           559.796       0.01        
    45          0.102009    0.964378       0.432181       0.886328           572.656       0.01        
    46          0.0996161   0.964988       0.443999       0.87793            585.535       0.01        
    47          0.0932453   0.967041       0.452139       0.881445           597.528       0.01        
    48          0.0879048   0.969262       0.450908       0.884766           609.156       0.01        
    49          0.0933322   0.967352       0.452228       0.883008           621.725       0.01        
    50          0.0883782   0.96895        0.46483        0.880078           634.555       0.01        
    51          0.0873798   0.969729       0.471411       0.875195           646.78        0.01        
    52          0.0899555   0.968683       0.464493       0.882617           658.161       0.01        
    53          0.0848383   0.971221       0.455103       0.882422           669.137       0.01        
    54          0.0794684   0.972701       0.454608       0.879883           680.626       0.01        
    55          0.082094    0.971577       0.491964       0.875              693.268       0.01        
    56          0.0799809   0.972523       0.526578       0.868359           705.112       0.01        
    57          0.0795879   0.972856       0.538439       0.864844           717.415       0.01        
    58          0.0773325   0.973357       0.478144       0.871875           730.885       0.01        
    59          0.075146    0.972945       0.520115       0.872656           743.595       0.01        
    60          0.0758757   0.973892       0.549528       0.869336           756.815       0.01        
    61          0.0472821   0.98422        0.440967       0.886133           768.614       0.001       
    62          0.0299252   0.990362       0.438435       0.890234           779.477       0.001       
    63          0.0247615   0.99221        0.435327       0.895703           790.3         0.001       
    64          0.0221926   0.993345       0.441702       0.896875           803.134       0.001       
    65          0.0204117   0.993586       0.448859       0.895703           815.305       0.001       
    66          0.0188024   0.994695       0.453645       0.895703           825.476       0.001       
    67          0.0156325   0.99566        0.462118       0.896289           837.684       0.001       
    68          0.0161309   0.994984       0.462184       0.897461           850.523       0.001       
    69          0.0151435   0.995237       0.467102       0.89707            861.725       0.001       
    70          0.0136216   0.995983       0.469565       0.896875           872.419       0.001       
    71          0.0140849   0.995682       0.469501       0.897852           883.692       0.001       
    72          0.0139553   0.995783       0.474553       0.89668            896.62        0.001       
    73          0.0131952   0.996138       0.481529       0.896094           909.393       0.001       
    74          0.0142982   0.995526       0.480778       0.898242           922.065       0.001       
    75          0.0121332   0.996138       0.478623       0.899805           933.689       0.001       
    76          0.0111401   0.996728       0.482796       0.899805           947.017       0.001       
    77          0.0105382   0.99676        0.484633       0.894922           960.926       0.001       
    78          0.00964496  0.997329       0.485977       0.897266           974.333       0.001       
    79          0.00995056  0.996826       0.497885       0.897852           988.168       0.001       
    80          0.0108769   0.996661       0.498564       0.898047           1000.01       0.001       
    81          0.0108558   0.996982       0.495446       0.896094           1011.5        0.001       
    82          0.0102108   0.996871       0.494618       0.896484           1024.42       0.001       
    83          0.0106756   0.996617       0.497753       0.89707            1037.54       0.001       
    84          0.0100215   0.996893       0.501453       0.897461           1049.84       0.001       
    85          0.00844761  0.997529       0.493311       0.898438           1065.14       0.001       
    86          0.00905977  0.997448       0.499073       0.895117           1078.29       0.001       
    87          0.00930665  0.997062       0.508955       0.894727           1090.16       0.001       
    88          0.00867714  0.997559       0.504844       0.899219           1102.02       0.001       
    89          0.00861321  0.99727        0.502856       0.898047           1113.88       0.001       
    90          0.00728278  0.997952       0.506968       0.898633           1125.68       0.001       
    91          0.00845109  0.997603       0.503209       0.89707            1138.37       0.0001      
    92          0.00766607  0.997952       0.507215       0.896875           1151.05       0.0001      
    93          0.00709555  0.997936       0.508333       0.89707            1164.28       0.0001      
    94          0.00712828  0.997863       0.501388       0.896094           1175.9        0.0001      
    95          0.00740915  0.998224       0.507065       0.896094           1188.58       0.0001      
    96          0.00704248  0.997975       0.502469       0.896875           1199.79       0.0001      
    97          0.00837506  0.997559       0.50369        0.897461           1212.92       0.0001      
    98          0.00636488  0.998091       0.501123       0.898633           1225.74       0.0001      
    99          0.00658172  0.99813        0.496533       0.89707            1239.14       0.0001      
    100         0.00759665  0.997891       0.50088        0.897656           1251.94       0.0001      
    Test accuracy: 0.9019976


学習が終了しました。ロスカーブと精度のグラフを見てみましょう。


```python
Image(filename='DeepCNN_cifar10_result/loss.png')
```



![output_96_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/492a6e82-98ec-2397-676e-64d2cf127a65.png)





```python
Image(filename='DeepCNN_cifar10_result/accuracy.png')
```



![output_97_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/8b05dd3f-422e-2ddf-24c0-84a3345b453f.png)




先程よりも大幅にValidationデータに対する精度が向上したことが分かります。学習率を10分の1に下げるタイミングでロスががくっと減り、精度がガクッと上がっているのが分かります。最終的に、先程60%前後だったValidationデータでの精度が、90%程度まで上がりました。また、テストデータを用いた精度も、およそ90%程度となっています。しかし最新の研究成果では97%以上まで達成されています。さらに精度を上げるには、今回行ったようなネットワークの構造自体の改良ももちろんのこと、学習データを擬似的に増やす操作（Data augmentation）や、複数のモデルの出力を一つの出力に統合する操作（Ensemble）などなど、いろいろな工夫が考えられます。

# データセットクラスを書いてみよう

ここでは、Chainerにすでに用意されているCIFAR10のデータを取得する機能を使って、データセットクラスを自分で書いてみます。Chainerでは、データセットを表すクラスは以下の機能を持っていることが必要とされます。

- データセット内のデータ数を返す`__len__`メソッド
- 引数として渡される`i`に対応したデータもしくはデータとラベルの組を返す`get_example`メソッド

その他のデータセットに必要な機能は、`chainer.dataset.DatasetMixin`クラスを継承することで用意できます。ここでは、`DatasetMixin`クラスを継承し、Data augmentation機能のついたデータセットクラスを作成してみましょう。

#### NOTE

自前で用意した、もしくはどこからから調達したラベル付き画像データセットを使う場合は、[`LabeledImageDataset`](https://docs.chainer.org/en/stable/reference/generated/chainer.datasets.LabeledImageDataset.html?highlight=LabeledImageDataset)というクラスが非常に便利です。詳細はドキュメントを参照してください：[`LabeledImageDataset`](https://docs.chainer.org/en/stable/reference/generated/chainer.datasets.LabeledImageDataset.html?highlight=LabeledImageDataset)。こちらでも使っています：[Chainerでアニメキャラクターの顔画像を分類する](https://qiita.com/mitmul/items/5502ecdd2f0b444c427f)

## 1. CIFAR10データセットクラスを書く


```python
class CIFAR10Augmented(chainer.dataset.DatasetMixin):

    def __init__(self, split='train', train_ratio=0.9):
        train_val, test_data = cifar.get_cifar10()
        train_size = int(len(train_val) * train_ratio)
        train_data, valid_data = split_dataset_random(train_val, train_size, seed=0)
        if split == 'train':
            self.data = train_data
        elif split == 'valid':
            self.data = valid_data
        elif split == 'test':
            self.data = test_data
        else:
            raise ValueError("'split' argument should be either 'train', 'valid', or 'test'. But {} was given.".format(split))

        self.split = split
        self.random_crop = 4

    def __len__(self):
        return len(self.data)

    def get_example(self, i):
        x, t = self.data[i]
        if self.split == 'train':
            x = x.transpose(1, 2, 0)
            h, w, _ = x.shape
            x_offset = np.random.randint(self.random_crop)
            y_offset = np.random.randint(self.random_crop)
            x = x[y_offset:y_offset + h - self.random_crop,
                  x_offset:x_offset + w - self.random_crop]
            if np.random.rand() > 0.5:
                x = np.fliplr(x)
            x = x.transpose(2, 0, 1)

        return x, t
```

このクラスは、CIFAR10のデータのそれぞれに対し、

- 32x32の大きさの中からランダムに28x28の領域をクロップ
- 1/2の確率で左右を反転させる

という加工を行っています。こういった操作を加えることで擬似的に学習データのバリエーションを増やすと、オーバーフィッティングを抑制することに役に立つということが知られています。これらの操作以外にも、画像の色味を変化させるような変換やランダムな回転、アフィン変換など、さまざまな加工によって学習データ数を擬似的に増やす方法が提案されています。

自分でデータの取得部分も書く場合は、コンストラクタに画像フォルダのパスとファイル名に対応したラベルの書かれたテキストファイルへのパスなどを渡してプロパティとして保持しておき、`get_example`メソッド内でそれぞれの画像を読み込んで対応するラベルとともに返す、という風にすれば良いことが分かります。

## 2. 作成したデータセットクラスを使って学習を行う

それではさっそくこの`CIFAR10`クラスを使って学習を行ってみましょう。先程使ったのと同じ大きなネットワークを使うことで、Data augmentationの効果がどの程度あるのかを調べてみましょう。`train`関数も含め、データセットクラス以外は先程とすべて同様です。


```python
reset_seed(0)

model = train(DeepCNN(10), max_epoch=100, train_dataset=CIFAR10Augmented(), valid_dataset=CIFAR10Augmented('valid'), test_dataset=CIFAR10Augmented('test'), postfix='augmented_', base_lr=0.1, lr_decay=(30, 'epoch'))
```

    epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  elapsed_time  lr        
    1           2.66932     0.155096       2.07969        0.218945           17.7946       0.1         
    2           1.95089     0.252242       2.16529        0.249609           33.6332       0.1         
    3           1.80029     0.304087       2.41118        0.232617           48.9252       0.1         
    4           1.68941     0.352428       1.74426        0.346289           64.1672       0.1         
    5           1.51953     0.427662       1.64889        0.411523           80.7169       0.1         
    6           1.33687     0.510121       1.33771        0.523438           96.9036       0.1         
    7           1.19523     0.572338       1.24248        0.573242           113.866       0.1         
    8           1.08409     0.616544       1.24074        0.562109           129.411       0.1         
    9           1.0027      0.64917        1.1433         0.617969           146.937       0.1         
    10          0.929279    0.677974       0.990748       0.664648           163.91        0.1         
    11          0.872005    0.699929       0.968499       0.68418            181.605       0.1         
    12          0.8138      0.722111       0.978409       0.675781           199.057       0.1         
    13          0.778692    0.73746        0.941924       0.691602           215.46        0.1         
    14          0.748357    0.749332       0.954998       0.686523           233           0.1         
    15          0.723875    0.757147       1.03263        0.671484           248.429       0.1         
    16          0.70128     0.763844       0.922967       0.697852           264.35        0.1         
    17          0.686641    0.770774       0.932868       0.717188           281.515       0.1         
    18          0.667459    0.777033       0.761607       0.753125           296.919       0.1         
    19          0.657883    0.783787       0.696336       0.775586           314.209       0.1         
    20          0.643969    0.787509       0.788956       0.729883           330.866       0.1         
    21          0.62682     0.791867       0.747854       0.749414           348.447       0.1         
    22          0.617861    0.793413       0.724289       0.757422           366.256       0.1         
    23          0.615907    0.792824       0.760725       0.749609           383.692       0.1         
    24          0.608608    0.799316       0.626627       0.794141           399.339       0.1         
    25          0.581148    0.804532       0.724842       0.766016           415.612       0.1         
    26          0.586145    0.805578       0.758912       0.765039           432.683       0.1         
    27          0.580653    0.806974       1.53115        0.567773           449.495       0.1         
    28          0.58332     0.808249       0.638048       0.792773           465.692       0.1         
    29          0.575656    0.809171       0.597601       0.795898           482.273       0.1         
    30          0.554497    0.815193       0.92648        0.694141           499.854       0.1         
    31          0.412056    0.862948       0.353921       0.882812           516.331       0.01        
    32          0.330784    0.886841       0.323626       0.890625           532.822       0.01        
    33          0.309464    0.893932       0.314378       0.897461           549.781       0.01        
    34          0.292333    0.900613       0.298411       0.902148           566.772       0.01        
    35          0.276572    0.904848       0.294187       0.901758           584.074       0.01        
    36          0.264323    0.909113       0.306759       0.900781           601.223       0.01        
    37          0.253866    0.913996       0.300004       0.901172           618.957       0.01        
    38          0.244404    0.915461       0.289046       0.904883           636.233       0.01        
    39          0.235541    0.918581       0.302103       0.900781           654.649       0.01        
    40          0.231642    0.919545       0.309698       0.899219           670.247       0.01        
    41          0.228038    0.921964       0.294209       0.904102           687.482       0.01        
    42          0.225327    0.923233       0.300517       0.905273           705.459       0.01        
    43          0.21667     0.925471       0.323513       0.896875           722.654       0.01        
    44          0.211944    0.927017       0.323403       0.894141           740.125       0.01        
    45          0.203643    0.92911        0.306549       0.901367           757.691       0.01        
    46          0.201874    0.930622       0.316553       0.898633           774.53        0.01        
    47          0.197785    0.93093        0.321795       0.901172           790.748       0.01        
    48          0.19062     0.933872       0.326009       0.898438           807.37        0.01        
    49          0.189966    0.932994       0.302136       0.902148           823.616       0.01        
    50          0.188014    0.934792       0.351239       0.89082            841.241       0.01        
    51          0.182187    0.935764       0.330658       0.897461           859.206       0.01        
    52          0.180877    0.937744       0.331513       0.898242           875.571       0.01        
    53          0.177914    0.937967       0.315606       0.900977           892.888       0.01        
    54          0.177183    0.938565       0.30771        0.906055           909.576       0.01        
    55          0.172863    0.939815       0.310448       0.899414           926.626       0.01        
    56          0.171187    0.940252       0.334125       0.896875           943.697       0.01        
    57          0.17084     0.941251       0.321309       0.900586           960.606       0.01        
    58          0.166495    0.941462       0.313928       0.905078           977.788       0.01        
    59          0.170402    0.94043        0.336456       0.896094           994.226       0.01        
    60          0.165143    0.941306       0.333355       0.901758           1011.56       0.01        
    61          0.125434    0.95641        0.277837       0.916797           1028.05       0.001       
    62          0.0972963   0.967036       0.279862       0.918164           1044.82       0.001       
    63          0.0909571   0.968373       0.281997       0.915625           1063.31       0.001       
    64          0.0854286   0.970197       0.279891       0.916406           1078.31       0.001       
    65          0.0803817   0.972368       0.283583       0.921484           1094.7        0.001       
    66          0.078539    0.972945       0.277332       0.917383           1111.61       0.001       
    67          0.0718806   0.975539       0.289384       0.916406           1128.09       0.001       
    68          0.0706877   0.975608       0.289274       0.917969           1145.55       0.001       
    69          0.0701198   0.975405       0.287858       0.91875            1161.81       0.001       
    70          0.0672646   0.976562       0.289723       0.918555           1178.87       0.001       
    71          0.0635346   0.978009       0.293459       0.917773           1196.21       0.001       
    72          0.0641605   0.977583       0.296556       0.914258           1213.21       0.001       
    73          0.0624464   0.978094       0.297749       0.916406           1229.1        0.001       
    74          0.0622282   0.978543       0.29798        0.916797           1245.15       0.001       
    75          0.0624434   0.978538       0.302791       0.917578           1261.09       0.001       
    76          0.062236    0.978655       0.303235       0.917383           1278.4        0.001       
    77          0.0583369   0.979759       0.294987       0.91582            1295.95       0.001       
    78          0.0563044   0.980725       0.30498        0.91875            1310.97       0.001       
    79          0.0551139   0.980846       0.304319       0.91582            1327.2        0.001       
    80          0.0538306   0.981548       0.301928       0.917578           1343.2        0.001       
    81          0.0498568   0.982599       0.303399       0.916406           1359.95       0.001       
    82          0.0522237   0.981845       0.306208       0.916602           1377.32       0.001       
    83          0.0487175   0.983284       0.307845       0.917773           1392.65       0.001       
    84          0.0507888   0.982622       0.305499       0.917383           1409.53       0.001       
    85          0.0502483   0.982394       0.309835       0.919727           1425.66       0.001       
    86          0.0495274   0.983043       0.313392       0.917578           1441.89       0.001       
    87          0.0492812   0.982594       0.308399       0.917969           1458.86       0.001       
    88          0.0467606   0.983643       0.317299       0.916211           1476.23       0.001       
    89          0.0451622   0.984042       0.320555       0.918945           1493.84       0.001       
    90          0.0457013   0.984197       0.313627       0.917773           1510.37       0.001       
    91          0.0436299   0.984952       0.311594       0.91875            1527.8        0.0001      
    92          0.0405673   0.986178       0.311017       0.917773           1545.44       0.0001      
    93          0.0399707   0.986306       0.313432       0.917969           1562.86       0.0001      
    94          0.0375223   0.987892       0.313193       0.917578           1580.18       0.0001      
    95          0.0403957   0.986461       0.3141         0.917969           1594.47       0.0001      
    96          0.0385853   0.987024       0.311208       0.918555           1610.3        0.0001      
    97          0.0380802   0.986972       0.309954       0.918359           1628.06       0.0001      
    98          0.0383922   0.986994       0.311925       0.920117           1644.28       0.0001      
    99          0.0389155   0.986979       0.309176       0.919727           1660.87       0.0001      
    100         0.0371039   0.98766        0.311089       0.919141           1676.64       0.0001      
    Test accuracy: 0.9186115


先程のData augmentationなしの場合は90%程度だったテスト精度が、学習データにaugmentationを施すことで92%程度まで向上させられることが分かりました。およそ2%の改善です。

ロスと精度のグラフを見てみましょう。


```python
Image(filename='DeepCNN_cifar10_augmented_result/loss.png')
```



![output_106_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/1f157bdd-3d94-8a1a-31e9-84ba771888bb.png)





```python
Image(filename='DeepCNN_cifar10_augmented_result/accuracy.png')
```



![output_107_0.png](https://qiita-image-store.s3.amazonaws.com/0/17934/f49da18e-e668-d7f9-5106-58e33dfb251f.png)




# もっと簡単にData Augmentationしよう

前述のようにデータセット内の各画像についていろいろな変換を行って擬似的にデータを増やすような操作をData Augmentationといいます。上では、オリジナルのデータセットクラスを作る方法を示すために変換の操作も`get_example()`内に書くという実装を行いましたが、実はもっと簡単にいろいろな変換をデータに対して行う方法があります。

それは、`TransformDataset`クラスを使う方法です。`TransformDataset`は、元になるデータセットオブジェクトと、そこからサンプルしてきた各データ点に対して行いたい変換を関数の形で与えると、変換済みのデータを返してくれるようなデータセットオブジェクトに加工してくれる便利なクラスです。かんたんな使い方は以下です。


```python
from chainer.datasets import TransformDataset

train_val, test_dataset = cifar.get_cifar10()
train_size = int(len(train_val) * 0.9)
train_dataset, valid_dataset = split_dataset_random(train_val, train_size, seed=0)


# 行いたい変換を関数の形で書く
def transform(inputs):
    x, t = inputs
    x = x.transpose(1, 2, 0)
    h, w, _ = x.shape
    x_offset = np.random.randint(4)
    y_offset = np.random.randint(4)
    x = x[y_offset:y_offset + h - 4,
          x_offset:x_offset + w - 4]
    if np.random.rand() > 0.5:
        x = np.fliplr(x)
    x = x.transpose(2, 0, 1)
    
    return x, t


# 各データをtransformにくぐらせたものを返すデータセットオブジェクト
train_dataset = TransformDataset(train_dataset, transform)
```

このようにすると、この新しい`train_dataset`は、上で自分でデータセットクラスごと書いたときと同じような変換を行った上でデータを返してくれるデータセットオブジェクトになります。

## ChainerCVでいろいろな変換を簡単に行おう

さて、上では画像に対してランダムクロップと、ランダムに左右反転というのをやりました。もっと色々な変換を行いたい場合、上記の`transform`関数に色々な処理を追加していけばよいことになりますが、毎回使いまわすような変換処理をそのたびに書くのは面倒です。何かいいライブラリとか無いのかな、となります。そこで[ChainerCV](http://chainercv.readthedocs.io/en/stable)[[Niitani 2017]](https://arxiv.org/abs/1708.08169)です！今年のACM MultimediaのOpen Source Software CompetitionにWebDNN[[Hidaka 2017]](https://dl.acm.org/citation.cfm?id=3129394)とともに出場していたChainerにComputer Vision向けの便利な機能を色々追加する補助パッケージ的なオープンソース・ソフトウェアです。


```bash
%%bash
pip install chainercv
```

    Requirement already satisfied: chainercv in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages
    Requirement already satisfied: chainer>=4.0 in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from chainercv)
    Requirement already satisfied: Pillow in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from chainercv)
    Requirement already satisfied: six>=1.9.0 in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from chainer>=4.0->chainercv)
    Requirement already satisfied: numpy>=1.9.0 in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from chainer>=4.0->chainercv)
    Requirement already satisfied: filelock in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from chainer>=4.0->chainercv)
    Requirement already satisfied: protobuf>=3.0.0 in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from chainer>=4.0->chainercv)
    Requirement already satisfied: setuptools in /home/shunta/.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages (from protobuf>=3.0.0->chainer>=4.0->chainercv)


    You are using pip version 9.0.1, however version 10.0.1 is available.
    You should consider upgrading via the 'pip install --upgrade pip' command.


[ChainerCV](http://chainercv.readthedocs.io/en/stable)には、画像に対する様々な変換があらかじめ用意されています。

- [ChainerCVで使える画像変換一覧](http://chainercv.readthedocs.io/en/stable/reference/transforms.html#image)

そのため、上でNumPyを使ってごにょごにょ書いていたランダムクロップやランダム左右反転は、`chainercv.transforms`モジュールを使うと、それぞれ以下のように1行で書くことができます：

```python
x = transforms.random_crop(x, (28, 28))  # ランダムクロップ
x = chainercv.transforms.random_flip(x)  # ランダム左右反転
```

`chainercv.transforms`モジュールを使って、`transform`関数をアップデートしてみましょう。ちなみに、`get_cifar10()`で得られるデータセットでは、デフォルトで画像の画素値の範囲が`[0, 1]`にスケールされています。しかし、`get_cifar10()`に`scale=255.`を渡しておくと、値の範囲をもともとの`[0, 255]`のままにできます。今回`transform`の中で行う処理は、以下の5つです：

1. PCA lighting: これは大雑把に言えば、少しだけ色味を変えるような変換です
2. Standardization: 訓練用データセット全体からチャンネルごとの画素値の平均・標準偏差を求めて標準化をします
3. Random flip: ランダムに画像の左右を反転します
4. Random expand: `[1, 1.5]`からランダムに決めた大きさの黒いキャンバスを作り、その中のランダムな位置へ画像を配置します
5. Random crop: `(28, 28)`の大きさの領域をランダムにクロップします


```python
from functools import partial
from chainercv import transforms

train_val, test_dataset = cifar.get_cifar10(scale=255.)
train_size = int(len(train_val) * 0.9)
train_dataset, valid_dataset = split_dataset_random(train_val, train_size, seed=0)

mean = np.mean([x for x, _ in train_dataset], axis=(0, 2, 3))
std = np.std([x for x, _ in train_dataset], axis=(0, 2, 3))


def transform(inputs, train=True):
    img, label = inputs
    img = img.copy()
    
    # Color augmentation
    if train:
        img = transforms.pca_lighting(img, 76.5)
        
    # Standardization
    img -= mean[:, None, None]
    img /= std[:, None, None]
    
    # Random flip & crop
    if train:
        img = transforms.random_flip(img, x_random=True)
        img = transforms.random_expand(img, max_ratio=1.5)
        img = transforms.random_crop(img, (28, 28))
        
    return img, label

train_dataset = TransformDataset(train_dataset, partial(transform, train=True))
valid_dataset = TransformDataset(valid_dataset, partial(transform, train=False))
test_dataset = TransformDataset(test_dataset, partial(transform, train=False))
```

ちなみに、`pca_lighting`は、大雑把にいうと色味を微妙に変えた画像を作ってくれる関数です。

では、standardizationとChainerCVによるPCA Lightingを追加した`TransformDataset`を使って学習をしてみましょう。


```python
reset_seed(0)

model = train(DeepCNN(10), max_epoch=100, train_dataset=train_dataset, valid_dataset=valid_dataset, test_dataset=test_dataset, postfix='augmented2_', base_lr=0.1, lr_decay=(30, 'epoch'))
```

    epoch       main/loss   main/accuracy  val/main/loss  val/main/accuracy  elapsed_time  lr        
    1           2.96298     0.108887       2.28773        0.116992           18.6204       0.1         
    2           2.28375     0.123069       2.26054        0.145508           35.9189       0.1         
    3           2.16893     0.173255       1.97104        0.232617           54.1333       0.1         
    4           1.95599     0.234308       1.96269        0.239453           71.4199       0.1         
    5           1.84762     0.287482       1.74268        0.316016           89.6395       0.1         
    6           1.71918     0.344682       1.48986        0.427539           107.843       0.1         
    7           1.5616      0.420028       1.33444        0.494531           126.049       0.1         
    8           1.41343     0.48635        1.34962        0.502539           144.103       0.1         
    9           1.29375     0.542591       1.27011        0.565039           161.386       0.1         
    10          1.20855     0.577858       1.09895        0.621289           179.561       0.1         
    11          1.13542     0.603427       1.12695        0.600391           197.048       0.1         
    12          1.08715     0.626625       0.967723       0.666797           215.84        0.1         
    13          1.03912     0.645552       1.09458        0.655078           232.916       0.1         
    14          0.992342    0.661659       0.813403       0.712305           251.279       0.1         
    15          0.962952    0.676736       0.89199        0.704492           269.464       0.1         
    16          0.935602    0.687433       0.871531       0.711133           287.93        0.1         
    17          0.907586    0.694935       0.826585       0.737695           305.05        0.1         
    18          0.897032    0.699907       0.759569       0.751758           322.843       0.1         
    19          0.872711    0.70958        0.747807       0.752148           341.134       0.1         
    20          0.860642    0.710849       0.897129       0.714648           359.493       0.1         
    21          0.837644    0.720998       0.877011       0.71582            377.873       0.1         
    22          0.83491     0.719682       0.811209       0.740625           395.061       0.1         
    23          0.812516    0.730591       0.991357       0.69668            411.923       0.1         
    24          0.814586    0.728493       0.870199       0.740625           428.436       0.1         
    25          0.809576    0.730358       0.851287       0.744336           444.849       0.1         
    26          0.797749    0.73413        1.0621         0.686523           462.494       0.1         
    27          0.792886    0.737416       0.765583       0.745703           479.124       0.1         
    28          0.773611    0.74123        0.59921        0.805273           497.053       0.1         
    29          0.767626    0.742587       1.15419        0.685938           514.952       0.1         
    30          0.753782    0.749355       0.937311       0.715234           533.238       0.1         
    31          0.604251    0.797053       0.399113       0.869727           550.994       0.01        
    32          0.512318    0.827613       0.375138       0.877344           569.219       0.01        
    33          0.484475    0.834229       0.353442       0.883984           587.753       0.01        
    34          0.467319    0.841242       0.364244       0.884375           606.206       0.01        
    35          0.450393    0.846911       0.341441       0.888086           624.712       0.01        
    36          0.43828     0.848855       0.342883       0.8875             644.334       0.01        
    37          0.435329    0.851206       0.34381        0.888867           662.547       0.01        
    38          0.429289    0.852561       0.334433       0.889258           681.356       0.01        
    39          0.416126    0.857639       0.335736       0.891602           699.445       0.01        
    40          0.414865    0.857333       0.340546       0.888867           717.644       0.01        
    41          0.406426    0.85993        0.331028       0.890039           736.169       0.01        
    42          0.400299    0.864984       0.325277       0.894531           754.238       0.01        
    43          0.390969    0.863414       0.31114        0.89668            773.074       0.01        
    44          0.385966    0.867032       0.315048       0.898047           791.369       0.01        
    45          0.386317    0.868342       0.311667       0.901758           809.057       0.01        
    46          0.379431    0.869881       0.326095       0.895508           826.949       0.01        
    47          0.371893    0.871693       0.30523        0.900195           845.462       0.01        
    48          0.375175    0.869257       0.313753       0.899414           863.334       0.01        
    49          0.368864    0.872203       0.335674       0.892969           881.266       0.01        
    50          0.364174    0.874911       0.329148       0.895703           898.805       0.01        
    51          0.361013    0.876113       0.325556       0.894531           917.536       0.01        
    52          0.355882    0.876309       0.312672       0.898633           935.827       0.01        
    53          0.355655    0.877315       0.314614       0.904492           953.22        0.01        
    54          0.350558    0.878507       0.335558       0.89375            971.412       0.01        
    55          0.35336     0.879808       0.309912       0.897461           989.554       0.01        
    56          0.349074    0.878307       0.315572       0.897656           1007.9        0.01        
    57          0.344636    0.881792       0.315235       0.898633           1026.21       0.01        
    58          0.351751    0.878361       0.308908       0.90625            1044.43       0.01        
    59          0.335435    0.885143       0.325415       0.895898           1062.98       0.01        
    60          0.342543    0.881366       0.307564       0.904492           1081.12       0.01        
    61          0.296395    0.897061       0.26379        0.914844           1099.85       0.001       
    62          0.273945    0.905404       0.255291       0.918945           1118          0.001       
    63          0.256059    0.911111       0.259149       0.918359           1136.27       0.001       
    64          0.250382    0.913039       0.26012        0.919922           1153.98       0.001       
    65          0.251402    0.911066       0.252121       0.919922           1172.19       0.001       
    66          0.246617    0.914795       0.254615       0.917188           1190.44       0.001       
    67          0.242367    0.916622       0.260114       0.916602           1208.65       0.001       
    68          0.237993    0.916815       0.251317       0.918359           1227.71       0.001       
    69          0.233611    0.919627       0.255463       0.92168            1246.66       0.001       
    70          0.229861    0.920543       0.251819       0.921289           1264.57       0.001       
    71          0.231212    0.919916       0.256449       0.918359           1282.98       0.001       
    72          0.230945    0.920388       0.251938       0.922852           1300.6        0.001       
    73          0.231567    0.918879       0.25467        0.922656           1319.03       0.001       
    74          0.228168    0.919649       0.25099        0.922266           1336.64       0.001       
    75          0.223481    0.922452       0.252225       0.921289           1354.76       0.001       
    76          0.217657    0.923834       0.2558         0.920898           1373.26       0.001       
    77          0.217563    0.924383       0.247747       0.923828           1392.33       0.001       
    78          0.216533    0.923722       0.244587       0.923242           1410.24       0.001       
    79          0.217534    0.924383       0.248184       0.922852           1428.02       0.001       
    80          0.217936    0.924435       0.245536       0.923828           1446.1        0.001       
    81          0.213818    0.925448       0.250882       0.921094           1465.16       0.001       
    82          0.216617    0.924028       0.245679       0.925586           1483.26       0.001       
    83          0.211348    0.924991       0.252102       0.923242           1502.12       0.001       
    84          0.21004     0.92658        0.254647       0.919922           1519.85       0.001       
    85          0.212658    0.926482       0.252338       0.924219           1539.01       0.001       
    86          0.210038    0.926824       0.256114       0.921094           1556.43       0.001       
    87          0.209864    0.927885       0.251901       0.921875           1575.17       0.001       
    88          0.210507    0.926314       0.253217       0.924414           1593.11       0.001       
    89          0.201081    0.928911       0.254178       0.925195           1610.7        0.001       
    90          0.207075    0.92873        0.249093       0.922656           1628.39       0.001       
    91          0.199509    0.929798       0.248616       0.925              1645.72       0.0001      
    92          0.199114    0.930467       0.250459       0.922266           1664.97       0.0001      
    93          0.194366    0.932773       0.250289       0.925781           1682.84       0.0001      
    94          0.192413    0.933605       0.250086       0.921875           1700.81       0.0001      
    95          0.195286    0.931374       0.25102        0.925977           1719.84       0.0001      
    96          0.191748    0.932826       0.247356       0.925391           1738.38       0.0001      
    97          0.188767    0.935258       0.25027        0.924219           1756.14       0.0001      
    98          0.190619    0.93517        0.249067       0.925              1774.45       0.0001      
    99          0.190362    0.934206       0.246483       0.925586           1792.49       0.0001      
    100         0.193294    0.93295        0.247981       0.926172           1810          0.0001      
    Test accuracy: 0.92108387


わずかに精度が向上しました。他にもネットワークにResNetと呼ばれる有名なアーキテクチャを採用するなど、簡単に試せる改善方法がいくつかあります。ぜひご自分で色々と試してみてください。

# おわりに

**Chainerの開発にコミットしてくれる方を歓迎します！**Chainerはオープンソースソフトウェアですので、皆さんが自身で欲しい機能などを提案し、Pull requestを送ることで進化していきます。興味のある方は、こちらの[Contoribution Guide](http://docs.chainer.org/en/latest/contribution.html)をお読みになった後、ぜひIssueを立てたりPRを送ったりしてみてください。お待ちしております。

chainer/chainer
[https://github.com/chainer/chainer](https://github.com/pfnet/chainer)

#### 参考文献

[Tokui 2015] Tokui, S., Oono, K., Hido, S. and Clayton, J., Chainer: a Next-Generation Open Source Framework for Deep Learning, Proceedings of Workshop on Machine Learning Systems(LearningSys) in The Twenty-ninth Annual Conference on Neural Information Processing Systems (NIPS), (2015)

[Niitani 2017] Yusuke Niitani, Toru Ogawa, Shunta Saito, Masaki Saito, "ChainerCV: a Library for Deep Learning in Computer Vision", ACM Multimedia (ACMMM), Open Source Software Competition, 2017

[Hidaka 2017] Masatoshi Hidaka, Yuichiro Kikura, Yoshitaka Ushiku, Tatsuya Harada. WebDNN: Fastest DNN Execution Framework on Web Browser. ACM International Conference on Multimedia (ACMMM), Open Source Software Competition, pp.1213-1216, 2017.

#### 脚注

[^cudnnenv]: `cudnnenv`は好きなバージョンのcuDNNを簡単に持ってこれるツールです。`pip install cudnnenv`でインストールし、[こちら](https://github.com/unnonouno/cudnnenv#install)にあるインストラクションに従えば、簡単にいろいろなCUDAバージョン向けの様々なバージョンのcuDNNをインストールすることができます。使い方も[こちら](https://github.com/unnonouno/cudnnenv)のREADMEに書いてあるとおり、単純明快です。

[^NN]: 学習データに対する予測精度は、もし学習データから抜き出されたあるデータをクエリとし、それが含まれている学習データセットから検索して発見することが必ずできるならば、そのデータについているラベルを答えることで、100%になってしまいます。