WHAT'S UP?

New post every sometimes. Here's OKKAH NET.

Chainer公式サンプルMNISTを読み解いてみた

train_mnist.pyのコードを追ってみました。Chainerのバージョンは4.0.0です。

github.com


MNISTとは
手書き数字の画像セット。機械学習の分野で最も有名なデータセットの1つ。
データセットは70000枚。(訓練データ: 60000枚、テストデータ: 10000枚)


処理の流れ
main関数における大まかな処理の流れは
 ⑴ モデルの生成
 ⑵ Optimizerの生成
 ⑶ Iteratorの生成
 ⑷ Trainer、Updaterの生成
 ⑸ Extensionsの登録
 ⑹ 学習ループ
といった感じです。


Trainerについて
Trainerは学習に必要なもの全てをひとまとめにする機能を持っています。
全体図は以下のようになっています。

f:id:okkah:20180501193156p:plain

基本的にTrainerは、渡されたUpdater(必要ならばExtensionsも)を実行するだけですが、
Updaterは中にIteratorとOptimizerを持っています。

Iteratorはデータセットにアクセスする機能を持ち、
Optimizerは重み・バイアスを更新する機能を持ちます。

つまりUpdaterは
 ⑴ データセットからデータを取り出す。(Iterator)
 ⑵ モデルに渡してロスを計算する。(Model = Optimizer.target)
 ⑶ モデルのパラメータを更新する。(Optimizer)
といった一連の学習の主要部分を担っています。

Extensionsは、可視化やログの保存などの様々な便利な機能を持つので、
必要に応じて使いましょう。


実際にMNISTのサンプルコードを追ってみる
1. 必要なライブラリやモジュールをimport

from __future__ import print_function

import argparse

import chainer
import chainer.functions as F
import chainer.links as L
from chainer import training
from chainer.training import extensions

・__future__: Python2系とは互換性の無い3系の機能を、2系でも使用可能にする。
・argparse: コマンドライン引数を扱えるようにする。
・chainer.functions: パラメータを持たない関数。
・chainer.links: パラメータを持つ関数。
・training: trainer


2. モデルの生成

# Set up a neural network to train    
# Classifier reports softmax cross entropy loss and accuracy at every    
# iteration, which will be used by the PrintReport extension below.    
model = L.Classifier(MLP(args.unit, 10))    
if args.gpu >= 0:
    # Make a specified GPU current
    chainer.backends.cuda.get_device_from_id(args.gpu).use()
    model.to_gpu()  # Copy the model to the GPU
# Network definition
class MLP(chainer.Chain):

    def __init__(self, n_units, n_out):
        super(MLP, self).__init__()
        with self.init_scope():
            # the size of the inputs to each layer will be inferred
            self.l1 = L.Linear(None, n_units)  # n_in -> n_units
            self.l2 = L.Linear(None, n_units)  # n_units -> n_units
            self.l3 = L.Linear(None, n_out)  # n_units -> n_out

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)

model: Classifierインスタンス。このmodelにデータを渡すと順伝播が始まる。
MLP: レイヤー構成を定義するクラス。
・chainer.Chain: パラメータを持つlinksをまとめておくクラス。
 Optimizerが更新するパラメータを簡単に取得できるように一箇所にまとめる。
・__init__: モデルを構成するレイヤーを定義するメソッド。インスタンス化。
・__call__: クラスのインスタンスを関数として呼び出すメソッド。
L.Linear: 全結合層を実現するクラス。
 データがその層に入力されると、必要な入力ユニット数を自動的に計算し、
 (n_in) x (n_units)の大きさの行列を生成し、パラメータとして保持する。
・relu: 活性化関数(→ゼロから作るDeep Learning 3章)

モデルのインスタンスは以下のようになります。
f:id:okkah:20180501230144p:plain

3. コマンドライン引数の設定

    parser = argparse.ArgumentParser(description='Chainer example: MNIST')
    parser.add_argument('--batchsize', '-b', type=int, default=100,
                        help='Number of images in each mini-batch')
    parser.add_argument('--epoch', '-e', type=int, default=20,
                        help='Number of sweeps over the dataset to train')
    parser.add_argument('--frequency', '-f', type=int, default=-1,
                        help='Frequency of taking a snapshot')
    parser.add_argument('--gpu', '-g', type=int, default=-1,
                        help='GPU ID (negative value indicates CPU)')
    parser.add_argument('--out', '-o', default='result',
                        help='Directory to output the result')
    parser.add_argument('--resume', '-r', default='',
                        help='Resume the training from snapshot')
    parser.add_argument('--unit', '-u', type=int, default=1000,
                        help='Number of units')
    parser.add_argument('--noplot', dest='plot', action='store_false',
                        help='Disable PlotReport extension')
    args = parser.parse_args()

    print('GPU: {}'.format(args.gpu))
    print('# unit: {}'.format(args.unit))
    print('# Minibatch-size: {}'.format(args.batchsize))
    print('# epoch: {}'.format(args.epoch))
    print('')

argparseモジュールを使うと、コマンドライン引数を扱うことができます。
これにより、Pythonを実行するときに様々なパラメータを指定することができます。

例)

$ python train_mnist.py -g 0 -e 10
GPU: 0
# unit: 1000
# Minibatch-size: 100
# epoch: 10

GPU: 0 (0: GPUを使用、-1: CPUを使用)
epoch: 10 (学習の反復回数: 10回)
で実行することができます。(他はデフォルトのまま)



4. Optimizerの生成

    # Setup an optimizer
    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)

モデルによって順伝播・逆伝播が実行され勾配が計算されますが、
その値を重み・バイアスに反映するのがOptimizerの仕事です。
役割上、モデルをラップするような形になります。

・Adam: パラメータの更新(→ゼロから作るDeep Learning 6章)


5. Iteratorの生成

    # Load the MNIST dataset
    train, test = chainer.datasets.get_mnist()

    train_iter = chainer.iterators.SerialIterator(train, args.batchsize)
    test_iter = chainer.iterators.SerialIterator(test, args.batchsize,
                                                 repeat=False, shuffle=False)

Iteratorでデータセットにアクセスします。

・get_mnist: mnistのデータをtrainとtestに入れる。
・SerialIterator: データを順番に取り出すもっともシンプルなIterator
・batchsize: 何枚の画像データを一括りにして取り出すかどうか。
・repeat: 何周もデータを繰り返し読むかどうか。
・shuffle: 取り出すデータの順番をepochごとにランダムに変更するかどうか。

ここまでが初期化に関する処理です。


6. Trainer、Updaterの生成

    # Set up a trainer
    updater = training.updaters.StandardUpdater(
        train_iter, optimizer, device=args.gpu)
    trainer = training.Trainer(updater, (args.epoch, 'epoch'), out=args.out)

ここから学習ループに関する処理に入ります。

・StandardUpdater: Updaterの処理を遂行するための最もシンプルなクラス。
・args.epoch: stop_trigger。学習をどのタイミングで終了するか。
・out: Extensionsで描画したグラフ画像の保存先。


7. Extensionsの登録

    # Evaluate the model with the test dataset for each epoch
    trainer.extend(extensions.Evaluator(test_iter, model, device=args.gpu))

    # Dump a computational graph from 'loss' variable at the first iteration
    # The "main" refers to the target link of the "main" optimizer.
    trainer.extend(extensions.dump_graph('main/loss'))

    # Take a snapshot for each specified epoch
    frequency = args.epoch if args.frequency == -1 else max(1, args.frequency)
    trainer.extend(extensions.snapshot(), trigger=(frequency, 'epoch'))

    # Write a log of evaluation statistics for each epoch
    trainer.extend(extensions.LogReport())

    # Save two plot images to the result dir
    if args.plot and extensions.PlotReport.available():
        trainer.extend(
            extensions.PlotReport(['main/loss', 'validation/main/loss'],
                                  'epoch', file_name='loss.png'))
        trainer.extend(
            extensions.PlotReport(
                ['main/accuracy', 'validation/main/accuracy'],
                'epoch', file_name='accuracy.png'))

    # Print selected entries of the log to stdout
    # Here "main" refers to the target link of the "main" optimizer again, and
    # "validation" refers to the default name of the Evaluator extension.
    # Entries other than 'epoch' are reported by the Classifier link, called by
    # either the updater or the evaluator.
    trainer.extend(extensions.PrintReport(
        ['epoch', 'main/loss', 'validation/main/loss',
         'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))

    # Print a progress bar to stdout
    trainer.extend(extensions.ProgressBar())

・Extensions: 可視化やログの保存などの様々な便利な機能を持つ。
 ・Evaluator: モデルの評価。
 ・dump_graph: グラフの保存。
 ・snapshot: Trainerを保存。
 ・LogReport: ログを保存。
 ・PlotReport: ロスを可視化して保存。
 ・PrintReport: ログを出力。
 ・ProgressBar: 学習の進行状況を出力。


8. 最後に

    if args.resume:
        # Resume from a snapshot
        chainer.serializers.load_npz(args.resume, trainer)

    # Run the training
    trainer.run()


if __name__ == '__main__':
    main()

・serializers.load_npz: snapshotから再開。
・trainer.run: 学習を開始。
・if __name__ == '__main__':
 直接実行されていればTrue、importされて実行されていればFalse。

ゼロから作るDeep Learning学習備忘録(5〜8章)

ゼロから作るDeep Learning学習備忘録(1〜4章) - OKKAH NETの続きです。


5章 誤差逆伝播
順伝播: 入力→出力へと計算 ・逆伝播: 出力→入力へと計算
誤差逆伝播法(Backpropagation): 損失関数を誤差と見立てて、
 逆伝播によって重みとバイアスを修正していくアルゴリズム
 連鎖律で逆伝播を行い、最急降下法で誤差を最小にする。
 ・連鎖律(chain rule): 合成関数の微分はそれぞれの導関数の積で与えられる。
 ・最急降下法(gradient discent): 関数の傾きのみから関数の最小値を探索する。
勾配確認: 数値微分誤差逆伝播法の結果を比較することで、
 誤差逆伝播法の実装に謝りがないことを確認できる。


6章 学習に関するテクニック
・パラメータの更新: 4章のSGD(確率的勾配降下法)の欠点を改善する手法3つ。
 ・Momentum: 慣性力を利用。関連性のある方向へSGDを加速させ振動を抑制する。
 ・AdaGrad: 学習係数を各パラメータ毎にその勾配の大きさに従って減らしていく。
  ・RMSProp: 過去の更新を徐々に忘れることで、いずれ更新量が0になる点を補う。
 ・Adam: MomentumとAdaGradの融合。
Batch Normalization: NNの学習を加速させる汎用的で強力な手法。
 2015年にGoogleのLoffeさんらによって提案された。
 各層でのアクティベーションの分布を、適度な広がりを持つように調整する。
 具体的には、各ミニバッチごとにデータ分布を平均0、分散1になるように正規化。
 これにより、学習効率が上がり、初期値依存性も小さく、かつ過学習を抑制する。
・正規化: モデルの表現力が高い時、訓練データが少ない時は、過学習の対策が必要。
 ・Weight decay(荷重減衰): 大きな重みを持つことに対してペナルティを課す。
  L2正則化。複雑なニューラルネットワークには対応しきれない。
 ・Dropout: ニューロンをランダムに消去しながら学習する。
  Weight decayだけでは対応が困難なときに使用する。
ハイパーパラメータ: 学習を行う際に人が予め設定しておく必要のあるパラメータ。
 例)ニューロンの数、バッチサイズ、学習係数、Weight decay
 ・検証データ: ハイパーパラメータの調整用のデータ。
 ・ハイパーパラメータの最適化
 ⑴ハイパーパラメータの範囲を指定する。
 ⑵指定した範囲から、ランダムにサンプリングする。
 ⑶学習を行い、検証データで認識精度を評価(epochは小さく設定)。
 ⑷⑵⑶をある程度繰り返し、認識精度からハイパーパラメータの範囲を狭める。
 他にはベイズの最適化で行う方法もある。


7章 畳み込みニューラルネットワーク
全結合: 隣接する層の全てのニューロン間で結合がある。
 問題点はデータの形状が無視されてしまう点。
畳み込みニューラルネットワーク(CNN):
 これまでの全結合のネットワークに畳み込み層プーリング層が加わる。
 画像処理や音声認識など至るところで使われている。
畳み込み層(Convolutionレイヤ): データの形状を維持することができる。
 ・特徴マップ: 畳み込み層の入出力データ。
 ・畳み込み演算: 画像処理でいうところのフィルター演算
  フィルターという用語はカーネルという言葉で表現されることもある。
  フィルターのウィンドウを一定の感覚でスライドさせながら適用していく。
  それぞれで、フィルターと入力の対応する要素を乗算し、その和を求める。
  バイアス項の加算は、フィルター適用後のデータに対して行われる。
 ・パディング: 入力データの周囲に固定データ(例えば0)を埋めること。
 ・ストライド: フィルターを適用する位置の間隔。
  例)ストライド=2 → フィルターを適用する窓の間隔が2要素ごとになる。
 ・3次元の畳み込み演算→直方体のブロックで考えるとわかりやすい。
 ・バッチ処理→N回分の処理を1回にまとめて行う。
プーリング層(poolingレイヤ): 縦・横方向の空間を小さくする演算。
 学習するパラメータがない、チャンネル数不変、
 微小な位置変化に対してロバスト、といった特徴がある。
 例) 領域の最大値をとるMaxプーリング、平均値をとるavarageプーリング
im2col(image to column):
 フィルター(重み)によって都合の良いように入力データを展開する関数。
・代表的なCNN
 ・LeNet: 最初のCNN。手書き数字認識を行うネットワーク。
  シグモイド関数、サブサンプリングによる中間データの縮小を採用。
  (現在はReLU、Maxプーリングが主流)
 ・AlexNet: ディープラーニング・ブームの火付け役。
  ReLU、LRN、Dropoutを採用している点以外はLeNetとあまり変わらず。
ディープラーニングの発展に、ビッグデータGPUが大きく貢献している。    


8章 ディープラーニング
Data Augmentation: 訓練画像を回転、平行移動、切り出し(crop)、
 左右反転(flip)、輝度変化などによって、人工的に増やす手法。
・ネットワークを深くすることの利点
 ・より少ないパラメータで同レベル以上の表現力を達成できる。
 ・各層がより簡単なタスクに取り組むことになるので効率的。
ImageNet: ILSVRC(2012)で使われた100万枚を超える画像データセット
ディープラーニングフレームワーク
 ・VGG(2014):
  畳み込み層とプーリング層から構成される基本的なCNN。
  3x3の小さなフィルターによる畳み込み層を連続して行い、
  重みのある層(畳み込み層や全結合層)は16~19層。
 ・GoogLeNet(2015):
  ネットワークが横方向にも幅を持つインセプション構造
  サイズの違うフィルター(とプーリング)を複数適用し、その結果を適合。
  1x1のフィルターの畳み込み層を多くの場所で使用する事で、
  チャンネル方向にサイズを減らし、パラメータを削減。
 ・ResNet(2015):
  畳み込み層の一部をまたぐスキップ構造(ショートカット、バイパス)
  学習時のBPの勾配消失問題を軽減できる(微分が1に近くなる)。
  これにより150層以上を繋げることができるようになった。
転移学習: 学習済みの重み(の一部)を別のNNにコピーして再学習を行う。
 手元にあるデータセットが少ない場合において特に有効な手法。
GPUや分散学習、ビット精度の削減などによって学習の高速化を実現。
ディープラーニングの実用事例
 ・物体検出: 画像中から物体の位置の特定を含めてクラス分類を行う。
  R-CNN、Faster R-CNNの手法が有名。
 ・セグメンテーション: 画像のピクセルレベルでのクラス分類を行う。
  FCNという全てが畳み込み層からなるネットワークが使われる。
 ・画像キャプション設定: 入力画像の説明文を自動で生成する。
  代表的な手法はNIC(画像を理解するディープなCNNと、
  自然言語や時系列データを扱うRNNから構成)と呼ばれる。
 ・画像スタイル変換: アーティストのような画像を描かせる。
 ・画像生成: 画像をゼロから生成。DCGAN。
 ・自動運転: SegNet(CNNベース)
 ・強化学習: Deep Q-Network。最適価値行動関数。


github.com

ゼロから作るDeep Learning学習備忘録(1〜4章)

ゼロから作るDeep Learningの学習備忘録です。

github.com


目的
・最低限の外部ライブラリだけ利用し、Pythonを使って、ゼロからディープラーニングによるプログラムを実装する。
・簡単な機械学習の問題からスタートし、最終的には画像を高精度に認識するシステムを実装する。


1章 Python入門
Python: 汎用のプログラミング言語。コードがシンプルで扱いやすい。
Anaconda: データ分析などに必要なライブラリを一括してインストールできる。
NumPy: 数値計算のためのライブラリ。
Matplotlib: グラフ描写のためのライブラリ。


2章 パーセプトロン
パーセプトロン: ニューラルネットワークの起源。入出力を備えたアルゴリズム
 複数の信号を入力として受け取り、ひとつの信号を出力する。
 重みとバイアスをパラメータとして設定する。
・単層パーセプトロン→AND, NAND, OR, 線形領域
・多層パーセプトロン→XOR, 非線形領域
・多層のパーセプトロンは理論上コンピュータを実装可能。
 

3章 ニューラルネットワーク
ニューラルネットワーク: パーセプトロンの重みをデータから自動で学習できる。
 入力層、中間層(隠れ層)、出力層からなる。
 入力層で重み付けされた入力値に対して、
 中間層で重みとバイアス値を利用して多次元配列の行列計算を行い、
 活性化関数のいずれかを利用してさらに加工して結果を出力する。
活性化関数: 入力信号の総和がどのように発火するかを決定する。
 ・シグモイド関数: 入力値が大きいほど1に近づき、小さいほど0に近づく。
 ・ステップ関数: 入力値が0以下のとき0、0より大きいとき1を出力。
 ・ReLU関数: 入力値が0以下のとき0、0より大きいとき入力値をそのまま出力。
機械学習の分類
 ・分類問題: データがどのクラスに属するか。例)人の画像から性別を判断
  出力層はソフトマックス関数(1つの出力層に全ての入力が結びつく)。
 ・回帰問題: データから数値の予測。例)人の画像から体重を判断
  出力層は恒等関数(入力をそのまま出力する)。
MNIST: 手書き数字の画像セット。機械学習の分野で最も有名なデータセットの1つ。
バッチ処理: 入力データをまとめることで計算を高速化。


4章 ニューラルネットワークの学習
・データから学習する
 ・訓練データ: 学習を行い、最適なパラメータを探索する。
 ・テストデータ: 訓練したモデルの実力を評価する。
損失関数: ニューラルネットワークの学習で用いられる指標。
 損失関数を指標として、値が小さくなるように重みパラメータを更新する。
 2乗和誤差交差エントロピー誤差などがある。
・学習の流れ
 ⑴ミニバッチ学習
  膨大な量のデータに対して損失関数を計算するのは現実的ではないので、
  一部データを無作為で抽出し、近似として利用する。
  選ばれたデータをミニバッチという。
 ⑵勾配の算出
  ミニバッチの損失関数を減らすために、各重みパラメータの勾配を求める。
  勾配は、損失関数の値を最も減らす方向を示す。
 ⑶パラメータの更新
  重みパラメータを勾配方向に微小量だけ更新する。
 ⑴〜⑶を繰り返す。
・数値微分による計算は時間がかかるが簡単。
 5章の誤差逆伝播法は複雑だが高速に勾配を求めることができる。


続きはこちら
okkah.hateblo.jp

pyenvでPythonのバージョンを切り替える

pyenvは、Pythonのバージョンを簡単に切り替えてくれるツールです。2系列と3系列の切り替えに便利です。


1. pyenvのインストール
まずはpyenvをインストールしましょう。以下のコマンドでgitからcloneします。

$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv


2. pyenvのパスを通す
bash, zshと、fishの場合で設定ファイルに記述する内容が違うので気をつけて下さい。

・~/.bash_profileや~/.zshenvを編集する場合

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)


・~/.config/fish/config.fishを編集する場合

set -gx PYENV_ROOT "$HOME/.pyenv"
set -x PATH $PATH "$PYENV_ROOT/bin"
status --is-interactive; and . (pyenv init - | psub)


これで、pyenvコマンドが使えるようになるはずです。


3. pyenvでPythonをインストール
では、pyenvを用いてPythonをインストールしてみましょう。

$ pyenv install 2.x.x
$ pyenv install 3.x.x


以下のコマンドで現在利用できるバージョンを確認することができます。

$ pyenv install --list


4. pyenvでバージョンを切り替える
バージョンの切り替えをします。localの場合は今いるディレクトリに、globalの場合は全体に反映されます。

$ pyenv local x.x.x
$ pyenv global x.x.x


以下のコマンドでPythonのバージョンを確認することができます。

$ python --version
Python x.x.x

GitHubの基本的な使い方

GitHubは、ソフトウェア開発プロジェクトのためのソースコード管理サービスです。

基本的な使い方をメモしておきます。


1. repositoryの作成
リポジトリはデータを保存する場所のことで、パソコン内にあるものをローカルリポジトリGitHubのサーバ上にあるものをリモートリポジトリと呼ぶ。

GitHubのwebページ上で作成する。


f:id:okkah:20171204212947p:plain


2. clone
リモートリポリトジを複製して、パソコン内に新しいローカルリポリトジを作成すること。

作成したいディレクトリにcd した後に、

git clone https://github.com/XXXXX/XXXX.git

URLは、GitHubのcloneしたいリポジトリページの右上の緑色のボタン(Clone or download)のUse HTTPSをクリックすれば表示されるので、それをコピーする。


3. add
ファイルやディレクトリをインデックスに保存すること。

git add ファイル名やディレクトリ名など
例)git add .


4. commit
インデックスに変更された記録をローカルリポジトリに登録すること。

git commit -m "コメント"
例)git commit -m "initial commit"


5. push
ローカルリポジトリの内容をアップロードしてGitHub上のリモートリポジトリに反映させること。

git push リモート名 ブランチ名
例)git push origin master

DHCP(CentOS7)

LinuxサーバーをDHCPサーバーとして、内部のクライアントマシンへIPアドレスを自動的に割当てるようにしました。


DHCPとは
DHCP(Dynamic Host Configuration Protocol):
インターネットに接続しようとするパソコンや周辺機器などに対し、IPアドレス、またそれに付随するサブネットマスクなど、必要な情報を自動的に割り当てる仕組み。


DHCPサーバーの設定

#
# DHCP Server Configuration file.
#   see /usr/share/doc/dhcp*/dhcpd.conf.example
#   see dhcpd.conf(5) man page
#
option domain-name-servers 133.25.243.11;

subnet 192.168.1.0 netmask 255.255.255.0 {
        range 192.168.1.3 192.168.1.254;
        option routers 192.168.1.1;
        max-lease-time 7200;
}

iptables(CentOS7)

Linuxに実装されたパケットフィルタリング型のファイアウォールであるiptablesを用いて、パケットフィルタリングルールや、アドレス変換ルールを適用しました。


・構成
LANポートを2つ搭載したデスクトップの一方にクライアントPC enp2s0を、もう一方に外部のネットワーク enp3s0を接続。


iptables

# sample configuration for iptables service
# you can edit this manually or use system-config-firewall
# please do not ask us to add additional ports/services to this default configuration
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -p icmp -j ACCEPT
-A FORWARD -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -i enp2s0 -j ACCEPT
-A FORWARD -i enp2s0 -o enp3s0 -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
COMMIT
*nat
-A POSTROUTING -s 192.168.1.0/24 -o enp3s0 -j MASQUERADE
COMMIT


・各テーブルとチェインの関係

f:id:okkah:20170527152929p:plain


(参考)
oxynotes.com