kaeken(嘉永島健司)のTech探究ブログ

主に情報科学/情報技術全般に関する知見をポストします。(最近は、特にData Science、機械学習、深層学習、統計学、Python、数学、ビッグデータ)

Deep Learning用語 日本語一覧

続いて、Deep Learning用語 日本語一覧。

こうして一覧にしてみると、うまく説明できない用語もあったり、スキップしたワンポイントコラム記事で言及されていた用語もあり、まだ復習が必要だ。

余談だが、最近は、だんだん人工知能視点になってきた。

「どこまでつぶれた文字を認識できるのか、歪み率をハイパーパラメータで導入できるのではないか。先行研究を要調査」

「歩いている人の数をカウントするアルバイト要員は、動的人間認識機能をもつ監視カメラで代替できるのではないか。要調査」

など自然に考えるようになってきた。

もっと専門的な分析ができるようにしていく。

アクティベーション
アフィン変換
アンサンブル学習
鞍点
閾値いきち
インセプション構造
エッジ
エポック
演算精度
オーバーフロー
重み
重み付き和
重みの初期値
カーネル
回帰問題
過学習
学習係数の減衰
学習率
確率的勾配降下法
隠れ層
荷重減衰
画像キャプション生成
画像スタイル変換
画像生成
逆伝播
強化学習
教師あり学習
教師データ
教師なし学習
局所的正規化
局所的計算
グリッドサーチ
訓練データ
計算グラフ
検証データ
交差エントロピー誤差
合成関数
高速化
恒等関数
勾配
勾配確認
勾配降下法
勾配消失
勾配上昇法
勾配法
誤差逆伝播法
再学習
最適化
サブサンプリング
シグモイド関数
指数移動平均
自動運転
出力層
出力特徴マップ
受容野
順伝播
順番付ディクショナリ
順方向伝播
乗算レイヤ
数値微分
スキップ構造
ステップ関数
ストライド
正規化
正則化
積和演算
セグメンテーション
全加算器
線形
全結合
前方差分
双曲線関数
ソフトマックス関数
損失関数
多次元配列
多層パーセプトロン
畳み込み演算
畳み込み層
畳み込みニューラルネットワーク
単純パーセプトロン
単調増加
チャンネル
中間層
中心差分
データ駆動
データ転送
デコンボリューション
テストデータ
転移学習
テンソル
特徴マップ
特徴量
ドット積
内積
入力層
入力特徴マップ
ニューロン
ネイピア数
ノード
パーセプトロン
パーセプトロンの収束定理
バイアス
バイアス補正
排他的論理和
ハイパーパラメータ
白色化
バス帯域
バッチ
パディング
半加算器
半精度浮動小数点数
非線形
非線形関数
ビッグデータ
微分
汎化能力
フィルター
フィルター演算
ブーリアン
プーリング層
物体検出
プラトー
ブロードキャスト
ブロブ
分散学習
分類問題
ベイズ最適化
ベクトル
偏微分
前処理
丸め誤差
ミニバッチ学習
ランダムサンプリング
連鎖律

Deep Learning用語一覧(英語)

『ゼロから作るDeep Learning』で深層学習を学び、自分自身の物理ニューラルネットワーク=脳を訓練中だが、一通り用語を一覧にした。

理論、実装、その他さまざまな用語が混ざっているが、これらを見ながら説明できない用語をピックアップして、本書を復習していく。

なお、Pythonの基本的な文法など、分かっているものは省いた。

まずは、英語編。

activation function
AdaGrad
Adam
AddLayer
Affine
AlexNet
ALU
AND gate
argmax()
Averageプーリング


batch
Batch Normalization

CNN
CNTK
Convolution
crop処理
cross entropy error
CUDA
cuDNN

Data Augmentation
DCGAN
Deep Belief Network
Deep Boltzman Machine
Deep Q-Network
DQN
Dropout

end-to-end

Faster R-CNN
CFN
fine tuning
flip処理
forward propagation
fully-connected

GAN
GoogLeNet
GPUコンピューティング
gradient
gradient ascent method
gradient descent method

half float
Heの初期値
HOG

identity_function()
ILSVRC
im2col
Image.fromarray()
ImageNet

KNN

L1ノルム
L2ノルム

learning rate
LeNet
load_mnist()
loss function
LRN

matrix
Maxノルム
Maxプーリング
mean squared error
MNIST
Momentum
MulLayer

nan
NAND gate
NIC
np.arange()
np.argmax()
np.array
np.dot()
np.exp()
np.int
np.maximum()
np.mdim()
np.random.choice()
np.random.randn()
np.sum()
np.zeros_like()
numerical differentiation
numerical_gradient()
N次元配列

one-hot表現
optimizer
OrderedDict
ORgate
overfitting

padding
pickle
PIL
plt.plot()
plt.show()
plt.title()
plt.xlabel()
plt.ylabel()

R-CNN
range()
relu()
ReLU関数
reshape()
ResNet
RMSProp
RNN

SegNet
SGD
SIFT
sigmoid()
SimpleConvNet
softmax()
Softmax-widh-Loss
step_function()
stochastic gradient descent
stride
SURF
SVM

tanh関数
TensorFlow
transpose
TwoLayerNet

VGG
Weight decay

Xavierの初期値
XOR gate

y.astype()

CS231n: Convolutional Neural Networks for Visual Recognition

『ゼロから作るDeep Learning』で何度も言及されていたスタンフォード大学の講義資料を参照する。

CS231n: Convolutional Neural Networks for Visual Recognition

cs231n.github.io

http://cs231n.stanford.edu/syllabus.html

英語は日常的に使っていないが、

キーワードの意味を押さえたうえで、

いったん日本語で概念をある程度把握しておけば、

比較的理解しやすい。

非常に図像が多用されたスライドが見ているだけでも面白い。

SVMなど、いわゆる既存の機械学習系のテーマは詳細不明だが、

backpropagationなどかなり共通しているので分かりやすい。

これから、『ゼロから作るDeep Learning』が参照している資料や、

よく分からなかった領域を深掘りしていくことにする。

やはり、英語圏の一次情報が多いので、分からないながらも、毎日英語で学習していくことにする。

8章 ディープラーニングDL『ゼロから作るDeep Learning』

本書の最後の8章は、現在のDLを俯瞰した説明なので、目次に合わせてメモ書き。

8章 ディープラーニング
    8.1 ネットワークをより深く
        8.1.1 よりディープなネットワークへ
        8.1.2 さらに認識精度を高めるには

これまで論文発表された手法の認識精度ランキング
http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html

さらに認識精度を高めるには、データ拡張などが必要。

データ拡張Data Augmentation:入力画像を回転やタテ・ヨコ方向の微小な移動などで、人工的に拡張する。

画像の一部を切り取るcrop処理や、左右反転するflip処理、輝度変更や、拡大・縮小などで増やす。

#TODO:あとで実装


        8.1.3 層を深くすることのモチベーション

層を深くするメリット:パラメータ数を少なくできる

「犬」画像をまとめて一度に学習するのは大変だが、

プリミティブな特徴を学習する浅い層から、徐々に多層化して抽象度を上げていくことができる。



    8.2 ディープラーニングの小歴史
        8.2.1 ImageNet

100万枚を超える画像データ・セット。

毎年ILSVRCという画像認識コンペティションが開催されている。

2015年のResNetは、エラー率3.5%となり、人間の認識能力を上回った。


        8.2.2 VGG
        8.2.3 GoogLeNet

「インセプション構造」と呼ばれるNNの「横方向の深さ(広がり)」を使っているのが特徴

        8.2.4 ResNet

「スキップ構造」と呼ばれる入力データの畳み込み層を跨いで出力に合算する構造をもつことで、

層を深くした際の減衰を回避している。

移転学習を行い、学習済の重みを別のNNにコピーして再学習fine tuningをさせている。

    8.3 ディープラーニングの高速化
        8.3.1 取り組むべき問題

畳み込み層の処理が一番かかっているので、大量の積和演算を以下に高速化するかが重要になっている。

        8.3.2 GPUによる高速化

もともとグラフィック処理用のGPUを使って大量の並列演算ができるので、CPUよりパフォーマンスが出る。


        8.3.3 分散学習

複数のGPUを並列で使うと、56倍の高速化が実現できた事例がある。


        8.3.4 演算精度のビット削減

通常、64bit/32bitで表現されている数値を16bitの半精度浮動小数点数half floatに落としても、問題なく学習ができるので、メモリやバス帯域の節約になる。

NVIDIAのMaxwell世代GPUでは演算自体16bitで行われていなかったが、次世代のPascalアーキテクチャでは16bitで行い、2倍の高速化が期待できる。

    8.4 ディープラーニングの実用例
        8.4.1 物体検出

物体検出には、R-CNN手法が使われる。

        8.4.2 セグメンテーション

FCN fully convolutional networkが提案されている

        8.4.3 画像キャプション生成

NIC neural image captionモデル

RNN recurrent neural network 再帰的なネットワークであり、自然言語や時系列データなど連続性のあるデータで使われる

マルチモーダル処理:画像や言語といった複数の種類の情報を組み合わせて処理


    8.5 ディープラーニングの未来
        8.5.1 画像スタイル変換

例えば、ゴッホ的なスタイルの画像に変換する

        8.5.2 画像生成

DCGAN Deep Convolutional Generative Adversarial Networkという手法で、画像が自動生成される。

GeneratorとDiscriminatorを対決させて精度を上げる


        8.5.3 自動運転

SegNet

        8.5.4 Deep Q-Network(強化学習)


強化学習reinforcement learning:エージェントが自律的によりよい報酬を目指して学習する

    8.6 まとめ

省略

これでひととおり本書を学び終えたが、まだまだ理解が十分ではないので、何度も読み返しながら、プログラミングして深く理解していく。

人工知能の領域はかなり広いので、一分野である機械学習のさらに一分野である深層学習をまずは徹底的に学び、学習領域を広げて実装につなげていく。

7章 畳み込みニューラルネットワークCNN 畳み込み層とプーリング層の実装『ゼロから作るDeep Learning』

f:id:kaeken:20161113170416p:plain

f:id:kaeken:20161113170431p:plain

7章 CNNの実装について。

畳み込み演算では、4次元データを処理する必要がある。

(batch_num, channel, hegiht, width)

そこでフィルタにとって都合の良い入力データを展開する関数im2colを使用する

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    """

    Parameters
    ----------
    input_data : (データ数, チャンネル, 高さ, 幅)の4次元配列からなる入力データ
    filter_h : フィルターの高さ
    filter_w : フィルターの幅
    stride : ストライド
    pad : パディング

    Returns
    -------
    col : 2次元配列
    """
    N, C, H, W = input_data.shape
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
    return col

実際に使ったサンプルコード

# coding: utf-8
import sys, os
sys.path.append(os.pardir)
import numpy as np
from common.util import im2col

x1 = np.random.rand(1,3,7,7)
col1 = im2col(x1,5,5,stride=1,pad=0)
print(col1.shape) #=>(9, 75)

x2 = np.random.rand(10,3,7,7)
col2 = im2col(x2,5,5,stride=1,pad=0)
print(col2.shape) #=>(90, 75)

続いてconvolution layerの順伝播と逆伝播。

class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        # 中間データ(backward時に使用)
        self.x = None
        self.col = None
        self.col_W = None

        # 重み・バイアスパラメータの勾配
        self.dW = None
        self.db = None

    def forward(self, x):
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        out_h = 1 + int((H + 2*self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2*self.pad - FW) / self.stride)

        col = im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0,2,3,1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx

続いて、Pooling layerの順伝播と逆伝播

class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h*self.pool_w)

        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)

        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,))

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

        return dx

続いて、CNNの可視化について。

1層目のフィルタを画像として表示。

最も小さな値は黒(0)、最も大きな値は白(255)に正規化して表示

学習前 f:id:kaeken:20161113170416p:plain

学習後 f:id:kaeken:20161113170431p:plain

「エッジedge(色が変化する境目)」や「ブロブblob(局所的に塊のある領域)」などのプリミティブな情報を抽出し、反応するフィルタであることが分かる。

階層が増えていくことで抽象度が上がり、テクスチャやパーツや対象のクラスなどに反応していくようになる。

最後に、代表的なCNNについて。

・LeNet:CNNの元祖で1998年提案された。シグモイド関数が使われ、サブサンプリングで中間データのサイズ縮小が行われる。

・AlexNet:DLブームの火付け役となったCNNで2012年に提案された。ReLU関数が使われ、LRN(Local Response Normalization)という局所的正規化を行う層を用いており、Dropoutを使用する。

また、環境的な変化として、大量データ=ビッグデータを扱える環境になり、大量の並列計算を得意とするGPUが普及すること、この2点がDL発展に寄与。

7章まとめ問題

・CNNは、既存の全結合層のNNに対して、◯◯と◯◯が加わる
・◯◯と◯◯は、◯◯関数を用いることで効率化する
・CNNの可視化で、層が深くなるにつれて◯◯が分かる
・代表的なCNNは、◯◯と◯◯である
・DL発展には、◯◯と◯◯が大きく貢献

畳み込み演算を図解するライブラリconv_arithmetic

f:id:kaeken:20161113135857p:plain

7章のCNNで畳み込み演算を図解するツールを探していたら以下を見つけた。

github.com

パディングやストライドの条件を指定できる。

texを使って記述するようだが、texはそれほど詳しくないので、必要に応じて調べておく。

文章や数式だけでは理解しづらい場合は、図解することで理解を促す。

7章 畳み込みニューラルネットワーク 畳み込み層Convolution layerとプーリング層Pooling layer『ゼロから作るDeep Learning』

f:id:kaeken:20161113135225g:plain

さて、いよいよ畳み込みニューラルネットワークConvolution Neural Network, CNNまで来た。

最新の画像認識・音声認識でほぼ使われているCNNをじっくり理解していく。

CNNでは、今までのレイヤに、

畳み込み層Convolution layer

プーリング層Pooling layer

の2つを追加する。プーリング層は省略されることがある。

今までのNNでは、全結合層Affine layer

で隣接する層のすべてのニューロン同士が結合されていた。

全結合層の問題点として、

データの空間的情報が消えてしまう問題がある。

具体的には、MNISTデータ・セットのように、

入力画像の3次元構造(チャンネル、縦、横)が、

1次元構造にされてAffine layerに入力される。

空間的に近接したピクセル同士の関係情報が消えてしまう。

ここで、

畳み込み層Convolution layerを使えば、空間情報が維持される。

なお、

畳み込み層の入出力データを特徴マップfeature mapとも言う。

入力特徴マップinput feature map

出力特徴マップoutput feature map

続いて、畳み込み演算(画像処理ではフィルター演算)について。

畳み込み演算は、縦・横の形状をした入力データに対して、フィルターを適用する。

以下、(height, width)として、フィルタのウィンドウを一定間隔で移動させながら、積和演算を行い、対応する出力場所へ格納する。

入力データ:(4,4)
1230
0123
3012
2301

フィルターサイズ:(3,3)
201
012
102

出力サイズ:(2,2)
  1x2 + 0x0 + 3x1
+ 2x0 + 1x1 + 0x0
+ 3x1 + 2x2 + 1x2
= 15→出力左上
以下同様
15 16
 6 15

イメージ図のとおり、入力データにフィルタをスライドさせながら積和演算し出力する。

f:id:kaeken:20161113135225g:plain

畳み込み演算のフィルタは、重みに相当し、(1,1)のバイアスも付加してデータを出力する。

また、

パディングpaddingとは、畳み込み演算前に入力データの周囲に固定データ(0など)を埋める処理。

パディングを使う理由は、出力サイズが1になって演算が適用できなくならないように調整するため。

例えば、入力データ(5,5)に幅1のパディングを適用すると、

入力データが(7,7)に変わり、

(5,5)フィルタをかけると以下のとおり、スライド位置が増えて、(5,5)の出力となる。

f:id:kaeken:20161113135257g:plain

続いて、ストライドについて。

ストライドstrideとは、フィルタを適用する位置間隔。

今までの説明ではストライド1だったが、ストライド2にすると、ウィンドウが2要素ごと移動する。

f:id:kaeken:20161113135455g:plain

ストライドを大きくすると、出力サイズは小さくなり、

パディングを大きくすると、出力サイズは大きくなる。

以下関係式。

入力サイズ(H,W)
フィルタサイズ(FH,FW)
出力サイズ(OH,OW)
パディングP
ストライドS

OH(OW) = 1 + { H(W) + 2P - FH(FW) } / 2

※HとWは読み替える
※H(W) + 2P - FH(FW) の値は割り切れるように設定する

次に、3次元畳み込み演算について。

3次元畳み込み演算:2次元にチャンネルを追加したデータの畳み込み演算

(channel, height, width)

※必ず、入力データのチャンネル数とフィルタのチャンネル数は一致させる必要がある。

フィルタが複数個ある場合は、output_channelを追加した4次元データにする。

(output_channel, input_channel, height, width)

バッチ処理の場合も、バッチ数を追加した4次元データにする。

(batch_num, input_channel, height, width)

最後に、プーリング層について。

プーリングpoolingとは、縦・横方向の空間を小さくする演算。

(2,2)サイズ領域を、一定のルールで(1,1)サイズにするなど。

Maxプーリング:対象領域の最大値を残して集約する手法。(以下ではプーリング=Maxプーリングとする)

Averageプーリング:対象領域の平均を算出する手法。

プーリング層の特徴3つ

1.学習するパラメータがない

2.チャンネル数は変化しない

3.微小な位置変化に対してロバスト(必ずしも保証されない)

以上、Convolution / Poolingレイヤの説明で、

次は、実装に入る。

深層学習用の数学プログラミングは『Pythonからはじめる数学入門』より『行列プログラマー』かも

以下、手を動かしていないが、いったん通読完了。

www.oreilly.co.jp

目次

1章 数を扱う
    1.1 基本数学演算
    1.2 ラベル:名前に数を割り当てる
    1.3 さまざまな種類の数
        1.3.1 分数を扱う
        1.3.2 複素数
    1.4 ユーザ入力を受け取る
        1.4.1 例外と不当入力の処理
        1.4.2 分数と複素数を入力
    1.5 数学を行うプログラムを書く
        1.5.1 整数の因数を計算する
        1.5.2 乗算表を生成する
        1.5.3 測定単位を変換する
        1.5.4 2次方程式の解を求める
    1.6 学んだこと
    1.7 プログラミングチャレンジ
        問題1-1 奇数偶数自動判別プログラム
        問題1-2 乗算表生成器の拡張
        問題1-3 単位変換プログラムの拡張
        問題1-4 分数電卓
        問題1-5 ユーザに脱出能力を与える

2章 データをグラフで可視化する
    2.1 デカルト座標平面を理解する
    2.2 リストとタプルの操作
        2.2.1 リストやタプルで繰り返す
    2.3 matplotlibでグラフを作る
        2.3.1 グラフで点を作る
        2.3.2 ニューヨーク市の年間平均気温をグラフ化する
        2.3.3 ニューヨーク市の月間気温傾向を比較する
        2.3.4 グラフのカスタマイズ
        2.3.5 プロットの保存
    2.4 式をプロットする
        2.4.1 ニュートンの万有引力の法則
        2.4.2 投射運動
    2.5 学んだこと
    2.6 プログラミングチャレンジ
        問題2-1 1日の間に気温はどのように変化するか
        問題2-2 2次関数を視覚的に探索する
        問題2-3 投射軌跡比較プログラムの拡張
        問題2-4 支出を可視化する
        問題2-5 フィボナッチ数列と黄金比の関係を調べる

3章 データを統計量で記述する
    3.1 平均を求める
    3.2 中央値を求める
    3.3 最頻値を求め度数分布表を作る
        3.3.1 一番多い要素を見つける
        3.3.2 最頻値を探す
        3.3.3 度数分布表を作る
    3.4 散らばりを測る
        3.4.1 数集合の範囲を決める
        3.4.2 分散と標準偏差を求める
    3.5 2つのデータセットの相関を計算する
        3.5.1 相関係数を計算する
        3.5.2 高校の成績と大学入試の点数
    3.6 散布図
    3.7 ファイルからデータを読み込む
        3.7.1 テキストファイルからデータを読み込む
        3.7.2 CSVファイルからデータを読み込む
    3.8 学んだこと
    3.9 プログラミングチャレンジ
        問題3-1 よりよい相関係数を求めるプログラム
        問題3-2 統計電卓
        問題3-3 他のCSVデータでの実験
        問題3-4 百分位を求める
        問題3-5 グループ度数分布表を作る

4章 SymPyで代数と式を計算する
    4.1 式の記号と記号演算を定義する
    4.2 式を扱う
        4.2.1 式の因数分解と展開
        4.2.2 プリティプリント
        4.2.3 値に代入する
        4.2.4 文字列を数式に変換する
    4.3 方程式を解く
        4.3.1 2次方程式を解く
        4.3.2 1変数を他の変数について解く
        4.3.3 連立方程式を解く
    4.4 SymPyを使ってプロットする
        4.4.1 ユーザが入力した式をプロットする
        4.4.2 複数の関数をプロットする
    4.5 学んだこと
    4.6 プログラミングチャレンジ
        問題4-1 因数ファインダ
        問題4-2 グラフを使った方程式ソルバー
        問題4-3 級数の和
        問題4-4 1変数の不等式を解く
        ヒント:役立つ関数

5章 集合と確率を操作する
    5.1 集合とは何か
        5.1.1 集合の構成
        5.1.2 部分集合、上位集合、べき集合
        5.1.3 集合演算
    5.2 確率
        5.2.1 事象Aまたは事象Bの確率
        5.2.2 事象Aおよび事象Bの確率
        5.2.3 乱数生成
        5.2.4 非一様乱数
    5.3 学んだこと
    5.4 プログラミングチャレンジ
        問題5-1 ベン図を使って集合の関係を可視化する
        問題5-2 大数の法則
        問題5-3 お金がなくなるまで何回硬貨を投げられるか
        問題5-4 トランプをよく切る
        問題5-5 円の領域を推定する
        πの値を推定する

6章 幾何図形とフラクタルを描画する
    6.1 matplotlibのパッチで幾何図形を描く
        6.1.1 円を描く
        6.1.2 図形のアニメーションを作る
        6.1.3 投射軌跡のアニメーション
    6.2 フラクタルを描く
        6.2.1 平面上の点の変換
        6.2.2 バーンスレイのシダを描く
    6.3 学んだこと
    6.4 プログラミングチャレンジ
        問題6-1 正方形に円を詰める
        問題6-2 シェルピンスキーの三角形
        問題6-3 エノンの関数を調べる
        問題6-4 マンデルブロ集合を描く
        imshow()関数
        リストのリストを作る
        マンデルブロ集合を描く

7章 初等解析問題を解く
    7.1 関数とは何か
        7.1.1 関数の定義域と値域
        7.1.2 よく使われる数学関数
    7.2 SymPyでの仮定
    7.3 関数の極限を求める
        7.3.1 連続複利(Continuous Compound Interest)
        7.3.2 瞬間変化率
    7.4 関数の微分を求める
        7.4.1 微分電卓
        7.4.2 偏微分を求める
    7.5 高階微分と極大極小の計算
    7.6 勾配上昇法を用いて最大値を求める
        7.6.1 勾配上昇法のジェネリックなプログラム
        7.6.2 初期値について一言
        7.6.3 ステップサイズとイプシロンの役割
    7.7 関数の積分を求める
    7.8 確率密度関数
    7.9 学んだこと
    7.10 プログラミングチャレンジ
        問題7-1 ある点での関数の連続性を検証する
        問題7-2 勾配降下法を実装する
        問題7-3 2曲線で囲まれた領域の面積
        問題7-4 曲線の長さを求める

付録A ソフトウェアのインストール
    A.1 Microsoft Windows
        A.1.1 SymPyの更新
        A.1.2 matplotlib-vennのインストール
        A.1.3 Pythonシェルの開始
    A.2 Linux
        A.2.1 SymPyの更新
        A.2.2 matplotlib-vennのインストール
        A.2.3 Pythonシェルの開始
    A.3 Mac OS X
        A.3.1 SymPyの更新
        A.3.2 matplotlib-vennのインストール
        A.3.3 Pythonシェルの開始

付録B Pythonについて
    B.1 if __name__ == '__main__' 
    B.2 リスト内包表記
    B.3 辞書データ構造
    B.4 複数戻り値
    B.5 例外処理
        B.5.1 複数の例外型を指定する
        B.5.2 elseブロック
    B.6 Pythonでのファイル読み込み
        B.6.1 全行を一度に読み込む
        B.6.2 ファイル名を入力で指定する
        B.6.3 ファイル読み込み時のエラー処理
    B.7 コードの再利用

Pythonを使っていろいろな数学のプログラミングを仕方のポイントを学べる点で有益だった。

ただ、正直、深層学習をやる分には関係のない分野もあり、それよりも、

『ゼロから作るDeep Learning』を学ぶ過程で必要だと感じたことは、

行列プログラミングの徹底的な理解。

その意味では、以下の分厚い行列に特化した参考書のほうがよいかもしれない(こっちは未読)。

www.oreilly.co.jp

0章 関数(とその他の数学とコンピュータに関する予備知識)
    0.1 集合に関する用語と記法
    0.2 デカルト積
    0.3 関数
        0.3.1 関数 vs プロシージャ vs 計算問題
        0.3.2 関数に関連する2つの計算問題
        0.3.3 特定の定義域と余定義域を持つ関数の集合に関する記法
        0.3.4 恒等関数
        0.3.5 関数の合成
        0.3.6 関数の合成の結合則
        0.3.7 逆関数
        0.3.8 可逆関数の合成関数についての可逆性
    0.4 確率
        0.4.1 確率分布
        0.4.2 事象及び確率の和
        0.4.3 関数への無作為な入力
        0.4.4 完全秘匿
        0.4.5 完全秘匿で可逆な関数
    0.5 ラボ:Python入門..集合、リスト、辞書、内包表記
        0.5.1 簡単な式
        0.5.2 代入文
        0.5.3 条件式
        0.5.4 集合
        0.5.5 リスト
        0.5.6 タプル
        0.5.7 その他の繰り返し処理について
        0.5.8 辞書
        0.5.9 1行プロシージャを定義する
    0.6 ラボ:Pythonと逆インデックス..モジュールと制御構造
        0.6.1 既存のモジュールの利用
        0.6.2 モジュールの作成
        0.6.3 ループと条件文
        0.6.4 字下げによるPythonのグループ化機能
        0.6.5 ループからの脱出
        0.6.6 ファイルからの読み込み
        0.6.7 ミニ検索エンジン
    0.7 確認用の質問
    0.8 問題

1章 体
    1.1 複素数入門
    1.2 Pythonにおける複素数
    1.3 体への抽象化
    1.4 Cと遊ぼう
        1.4.1 複素数の絶対値
        1.4.2 複素数を足すこと
        1.4.3 複素数に正の実数を掛けること
        1.4.4 複素数に負の実数を掛けること:180度の回転
        1.4.5 iを掛けること:90度の回転
        1.4.6 複素平面上の単位円:偏角と角度
        1.4.7 オイラーの公式
        1.4.8 複素数の極表示
        1.4.9 指数の第1法則
        1.4.10 ラジアンの回転
        1.4.11 変換の合成
        1.4.12 2次元を越えて
    1.5 GF(2)で遊ぼう
        1.5.1 完全秘匿な暗号化システム、再訪
        1.5.2 ワンタイムパッド
        1.5.3 ネットワークコーディング
    1.6 確認用の質問
    1.7 問題

2章 ベクトル
    2.1 ベクトルとは何か?
    2.2 ベクトルは関数
        2.2.1 Pythonの辞書を用いたベクトルの表現
        2.2.2 スパース性
    2.3 ベクトルで何が表現できるか?
    2.4 ベクトルの加法
        2.4.1 平行移動とベクトルの加法
        2.4.2 ベクトルの加法の結合則と可換則
        2.4.3 矢印としてのベクトル
    2.5 スカラーとベクトルの積
        2.5.1 矢印のスケーリング
        2.5.2 スカラーとベクトルの積の結合則
        2.5.3 原点を通る線分
        2.5.4 原点を通る直線
    2.6 ベクトルの和とスカラーとの積の組み合わせ
        2.6.1 原点を通らない線分と直線
        2.6.2 スカラーとベクトルの積とベクトルの和に対する分配則
        2.6.3 はじめての凸結合
        2.6.4 はじめてのアフィン結合
    2.7 辞書によるベクトルの表現
        2.7.1 セッターとゲッター
        2.7.2 スカラーとベクトルの積
        2.7.3 加法
        2.7.4 ベクトルの反転、ベクトルの和、差の可逆性
    2.8 GF(2)上のベクトル
        2.8.1 完全秘匿性、再再訪
        2.8.2 GF(2)を用いた1か0の秘密共有
        2.8.3 ライツアウト
    2.9 ドット積
        2.9.1 総費用と利益
        2.9.2 線形方程式
        2.9.3 類似性の測定
        2.9.4 GF(2)上のベクトルのドット積
        2.9.5 パリティビット
        2.9.6 簡単な認証方式
        2.9.7 この簡単な認証方式への攻撃
        2.9.8 ドット積の代数的性質
        2.9.9 簡単な認証方式への攻撃、再訪
    2.10 Vecの実装
        2.10.1 Vecを扱う構文
        2.10.2 実装
        2.10.3 Vecの利用
        2.10.4 Vecの表示
        2.10.5 Vecのコピー
        2.10.6 リストからVecへ
    2.11 上三角線形方程式系を解く
        2.11.1 上三角線形方程式系
        2.11.2 後退代入
        2.11.3 後退代入の最初の実装
        2.11.4 このアルゴリズムはどのような場合に役に立つか?
        2.11.5 任意の定義域のベクトルを用いた後退代入
    2.12 ラボ:ドット積を用いた投票記録の比較
        2.12.1 動機
        2.12.2 ファイルから読み込む
        2.12.3 ドット積を用いてベクトルを比較する2つの方法
        2.12.4 ポリシーの比較
        2.12.5 平均的でない民主党員
        2.12.6 宿敵
        2.12.7 更なる課題
    2.13 確認用の質問
    2.14 問題

3章 ベクトル空間
    3.1 線形結合
        3.1.1 線形結合の定義
        3.1.2 線形結合の利用
        3.1.3 係数から線形結合へ
        3.1.4 線形結合から係数へ
    3.2 線形包
        3.2.1 線形包の定義
        3.2.2 他の方程式を含む線形方程式系
        3.2.3 生成子
        3.2.4 線形結合の線形結合
        3.2.5 標準生成子
    3.3 ベクトルの集合の幾何学
        3.3.1 R上のベクトルの線形包の幾何学
        3.3.2 同次線形系の解集合の幾何学
        3.3.3 原点を含むフラットの2通りの表現
    3.4 ベクトル空間
        3.4.1 2つの表現に共通するものは何か?
        3.4.2 ベクトル空間の定義と例
        3.4.3 部分空間
        3.4.4 *抽象ベクトル空間
    3.5 アフィン空間
        3.5.1 原点を通らないフラット
        3.5.2 アフィン結合
        3.5.3 アフィン空間
        3.5.4 線形系の解集合によるアフィン空間の表現
        3.5.5 2通りの表現について、再訪
    3.6 同次線形系とその他の線形系
        3.6.1 同次線形系と対応する一般の線形系
        3.6.2 解の個数、再訪
        3.6.3 平面と直線の交差について
        3.6.4 チェックサム関数
    3.7 確認用の質問
    3.8 問題

4章 行列
    4.1 行列とは何か?
        4.1.1 伝統的な行列
        4.1.2 行列の正体
        4.1.3 行、列、要素
        4.1.4 Pythonにおける行列の実装
        4.1.5 単位行列
        4.1.6 行列の表現間の変換
        4.1.7 matutil.py
    4.2 列ベクトル空間と行ベクトル空間
    4.3 ベクトルとしての行列
    4.4 転置
    4.5 線形結合による行列とベクトルの積及びベクトルと行列の積の表現
        4.5.1 線形結合による行列とベクトルの積の表現
        4.5.2 線形結合によるベクトルと行列の積の表現
        4.5.3 ベクトルの線形結合による表現の行列とベクトルの方程式による定式化
        4.5.4 行列とベクトルの方程式を解く
    4.6 ドット積による行列とベクトルの積の表現
        4.6.1 定義
        4.6.2 応用例
        4.6.3 線形方程式の行列とベクトルの方程式としての定式化
        4.6.4 三角系と三角行列
        4.6.5 行列とベクトルの積の代数的性質
    4.7 ヌル空間
        4.7.1 同次線形系と行列の方程式
        4.7.2 行列とベクトルの方程式の解空間
        4.7.3 はじめてのエラー訂正コード
        4.7.4 線形符号
        4.7.5 ハミングコード
    4.8 スパース行列とベクトルの積の計算
    4.9行列と関数
        4.9.1 行列から関数へ
        4.9.2 関数から行列へ
        4.9.3 行列の導出例
    4.10 線形関数
        4.10.1 行列とベクトルの積で表現できる関数
        4.10.2 定義と簡単な例
        4.10.3 線形関数とゼロベクトル
        4.10.4 線形関数による直線の変換
        4.10.5 単射な線形関数
        4.10.6 全射な線形関数
        4.10.7 FCからFRへの線形関数の行列による表現
        4.10.8 対角行列
    4.11 行列と行列の積
        4.11.1 行列と行列の積の行列とベクトルの積及びベクトルと行列の積による表現
        4.11.2 グラフ、隣接行列、そして道の数え上げ
        4.11.3 行列と行列の積と関数の合成
        4.11.4 行列と行列の積の転置
        4.11.5 列ベクトルと行ベクトル
        4.11.6 全てのベクトルは列ベクトルと解釈される
        4.11.7 線形結合の線形結合、再訪
    4.12 内積と外積
        4.12.1 内積
        4.12.2 外積
    4.13 逆関数から逆行列へ
        4.13.1 線形関数の逆関数も線形である
        4.13.2 逆行列
        4.13.3 逆行列の利用
        4.13.4 可逆行列の積は可逆行列である
        4.13.5 逆行列についての補足
    4.14 ラボ:エラー訂正コード
        4.14.1 チェック行列
        4.14.2 生成子行列
        4.14.3 ハミングコード
        4.14.4 復号化
        4.14.5 エラーシンドローム
        4.14.6 エラーの検出
        4.14.7 全てをまとめる
    4.15 ラボ:2次元幾何学における座標変換
        4.15.1 平面上の点の表現
        4.15.2 座標変換
        4.15.3 画像表現
        4.15.4 画像の読み込みと表示
        4.15.5 線形変換
        4.15.6 平行移動
        4.15.7 スケーリング
        4.15.8 回転
        4.15.9 原点以外の点を中心とした回転
        4.15.10 反転
        4.15.11 色の変換
        4.15.12 より一般的な反転
    4.16 確認用の質問
    4.17 問題

5章 基底
    5.1 座標系
        5.1.1 ルネ・デカルトのアイデア
        5.1.2 座標表現
        5.1.3 座標表現及び行列とベクトルの積
    5.2 はじめての非可逆圧縮
        5.2.1 方法1:最も近いスパースベクトルに置き換える
        5.2.2 方法2:画像ベクトルを座標で表現する
        5.2.3 方法3:複合的な方法
    5.3 生成子を探す2つの欲張りなアルゴリズム
        5.3.1 グローアルゴリズム
        5.3.2 シュリンクアルゴリズム
        5.3.3 欲張って失敗する場合
    5.4 最小全域森とGF(2)
        5.4.1 定義
        5.4.2 最小全域森に対するグローアルゴリズムとシュリンクアルゴリズム
        5.4.3 線形代数での最小全域森
    5.5 線形従属性
        5.5.1 余分なベクトルの補題
        5.5.2 線形従属性の定義
        5.5.3 最小全域森における線形従属性
        5.5.4 線形従属、線形独立の性質
        5.5.5 グローアルゴリズムの考察
        5.5.6 シュリンクアルゴリズムの考察
    5.6 基底
        5.6.1 基底の定義
        5.6.2 FDの標準基底
        5.6.3 全てのベクトル空間が基底を持つことの証明への準備
        5.6.4 ベクトルの有限集合は全てその線形包の基底を含むということ
        5.6.5 Vの任意の線形独立なベクトルの部分集合はVの基底の形にすることができるか?
    5.7 一意的な表現
        5.7.1 基底による座標表現の一意性
    5.8 はじめての基底の変換
        5.8.1 表現からベクトルへの関数
        5.8.2 ある表現から別の表現へ
    5.9 遠近法によるレンダリング
        5.9.1 3次元空間内の点
        5.9.2 カメラと画像平面
        5.9.3 カメラ座標系
        5.9.4 3次元空間内のある点のカメラ座標から対応する画像平面上の点のカメラ座標へ
        5.9.5 3次元空間内の座標からカメラ座標へ
        5.9.6 ピクセル座標系へ
    5.10 基底を求める計算問題
    5.11 交換の補題
        5.11.1 交換の補題
        5.11.2 最小全域森に対するグローアルゴリズムの正当性の証明
    5.12 ラボ:透視補正
        5.12.1 カメラ基底
        5.12.2 ホワイトボード基底
        5.12.3 ピクセルからホワイトボード上の点への写像
        5.12.4 ホワイトボード上にない点から対応するホワイトボードの上の点への写像
        5.12.5 基底の変換行列
        5.12.6 基底の変換行列の計算
        5.12.7 画像表現
        5.12.8 透視補正した画像の生成
    5.13 確認用の質問
    5.14 問題

6章 次元
    6.1 基底ベクトルの個数
        6.1.1 モーフィングの補題とその結果
        6.1.2 モーフィングの補題の証明
    6.2 次元とランク
        6.2.1 定義と例
        6.2.2 幾何学
        6.2.3 グラフの次元とランク
        6.2.4 GF(2)上のベクトル空間の濃度
        6.2.5 Vに属する任意の線形独立なベクトルの集合からVの基底への拡張
        6.2.6 次元原理
        6.2.7 グローアルゴリズムの終了
        6.2.8 ランク定理
        6.2.9 簡単な認証、再訪
    6.3 直和
        6.3.1 定義
        6.3.2 直和の生成子
        6.3.3 直和の基底
        6.3.4 ベクトルの分解の一意性
        6.3.5 補空間
    6.4 次元と線形関数
        6.4.1 線形関数の可逆性
        6.4.2 可逆な最大部分関数
        6.4.3 次元定理
        6.4.4 線形関数の可逆性、再訪
        6.4.5 ランクと退化次数の定理
        6.4.6 チェックサム問題、再訪
        6.4.7 行列の可逆性
        6.4.8 行列の可逆性と基底の変換
    6.5 アニヒレーター
        6.5.1 表現の間の変換
        6.5.2 ベクトル空間のアニヒレーター
        6.5.3 アニヒレーター次元定理
        6.5.4 Vの生成子からVoの生成子へ、及びその逆
        6.5.5 アニヒレーター定理
    6.6 確認用の質問
    6.7 問題

7章 ガウスの掃き出し法
    7.1 階段形式
        7.1.1 階段形式から行ベクトル空間の基底へ
        7.1.2 階段形式における行ベクトルのリスト
        7.1.3 行を0でない要素がはじめて現れる位置でソートする
        7.1.4 行基本変形
        7.1.5 行基本行列を掛ける
        7.1.6 行基本変形による行ベクトル空間の保存
        7.1.7 ガウスの掃き出し法を通じた基底、ランク、そして線形独立性
        7.1.8 ガウスの掃き出し法が失敗する場合
        7.1.9 ピボット化と数値解析
        7.1.10 GF(2)上におけるガウスの掃き出し法
    7.2 ガウスの掃き出し法の他の応用
        7.2.1 逆行列を持つ行列MでMAが階段形式になるものが存在すること
        7.2.2 行列の積を用いずにMを計算する
    7.3 ガウスの掃き出し法による行列とベクトルの方程式の解法
        7.3.1 行列が階段形式の場合の行列とベクトルの方程式の解法..逆行列を持つ場合
        7.3.2 ゼロ行を処理する
        7.3.3 無関係な列を処理する
        7.3.4 単純な認証方式への攻撃とその改善
    7.4 ヌル空間の基底を見つける
    7.5 整数の素因数分解
        7.5.1 素因数分解へのはじめての試み
    7.6 ラボ:閾値シークレットシェアリング
        7.6.1 最初の試み
        7.6.2 うまくいく方法
        7.6.3 本手法の実装
        7.6.4 uの生成
        7.6.5 条件を満たすベクトルの探索
        7.6.6 文字列の共有
    7.7 ラボ:整数の素因数分解
        7.7.1 平方根を用いた最初の試み
        7.7.2 最大公約数を求めるためのユークリッドの互除法
        7.7.3 平方根を用いた試み、再訪
    7.8 確認用の質問
    7.9 問題

8章 内積
    8.1 消防車問題
        8.1.1 距離、長さ、ノルム、内積
    8.2 実数上のベクトルの内積
        8.2.1 実数上のベクトルのノルム
    8.3 直交性
        8.3.1 直交性が満たす性質
        8.3.2 ベクトルbの平行成分と直交成分への分解
        8.3.3 直交性が持つ性質と消防車問題の解
        8.3.4 射影と最も近い点の探索
        8.3.5 消防車問題の解
        8.3.6 *外積と射影
        8.3.7 高次元版の問題の解法に向けて
    8.4 ラボ:機械学習
        8.4.1 データ
        8.4.2 教師あり学習
        8.4.3 仮説クラス
        8.4.4 訓練データにおける誤差を最小化する分類器の選択
        8.4.5 ヒルクライミングによる非線形最適化
        8.4.6 勾配
        8.4.7 最急降下法
    8.5 確認用の質問
    8.6 問題

9章 直交化
    9.1 複数のベクトルに直交する射影
        9.1.1 ベクトルの集合に対する直交性
        9.1.2 ベクトル空間への射影とそれに直交する射影
        9.1.3 ベクトルのリストに直交する射影の第一歩
    9.2 互いに直交するベクトルに直交する射影
        9.2.1 project_orthogonalが正しいことの証明
        9.2.2 project_orthogonalの拡張
    9.3 直交する生成子の集合の作成
        9.3.1 プロシージャorthogonalize
        9.3.2 orthogonalizeが正しいことの証明
    9.4 計算問題「ベクトルの線形包の中で最も近い点」を解く
    9.5 直交化の他の問題への応用
        9.5.1 基底の計算
        9.5.2 部分集合から成る基底の計算
        9.5.3 aug_orthogonalize
        9.5.4 丸め誤差がある場合のアルゴリズム
    9.6 直交補空間
        9.6.1 直交補空間の定義
        9.6.2 直交補空間と直和
        9.6.3 線形包またはアフィン包で与えられたR3における平面に対する法ベクトル
        9.6.4 直交補空間、ヌル空間、アニヒレーター
        9.6.5 方程式で与えられたR3の中の平面に対する法ベクトル
        9.6.6 直交補空間の計算
    9.7 QR分解
        9.7.1 直交行列と列直交行列
        9.7.2 列直交行列の積はノルムを保存する
        9.7.3 行列のQR分解の定義
        9.7.4 Aが線形独立な列ベクトルを持つための条件
    9.8 QR分解による行列の方程式Ax = bの解法
        9.8.1 正方行列の場合
        9.8.2 正方行列の場合の正しさ
        9.8.3 最小二乗問題
        9.8.4 列直交行列の列ベクトルによる座標表現
        9.8.5 Aの行が列より長い場合のQR_solveの使用
    9.9 最小二乗法の応用
        9.9.1 線形回帰(直線へのフィッティング)
        9.9.2 放物線へのフィッティング
        9.9.3 2変数の放物線へのフィッティング
        9.9.4 産業スパイ問題の近似値への応用
        9.9.5 センサーノード問題の近似値への応用
        9.9.6 機械学習の問題への最小二乗法の利用
    9.10 確認用の質問
    9.11 問題

10章 特別な基底
    10.1 最も近いkスパースベクトル
    10.2 与えられた基底に対する表現がkスパースであるような最も近いベクトル
        10.2.1 正規直交基底による座標表現を求める
    10.3 ウェーブレット
        10.3.1 解像度の異なる1次元「画像」
        10.3.2 Vnの直和への分解
        10.3.3 ハールウェーブレット基底
        10.3.4 V1の基底
        10.3.5 一般のnについて
        10.3.6 ウェーブレット変換の最初のステップ
        10.3.7 ウェーブレット分解の以降のステップ
        10.3.8 正規化
        10.3.9 逆変換
        10.3.10 実装
    10.4 多項式の評価と補間
    10.5 フーリエ変換
    10.6 離散フーリエ変換
        10.6.1 指数法則
        10.6.2 n個のストップウォッチ
        10.6.3 離散フーリエ空間:基底関数のサンプリング
        10.6.4 フーリエ行列の逆行列
        10.6.5 高速フーリエ変換(FFT)アルゴリズム
        10.6.6 FFTの導出
        10.6.7 FFTのコーディング
    10.7 複素数上のベクトルの内積
    10.8 巡回行列
        10.8.1 巡回行列とフーリエ行列の列の積
        10.8.2 巡回行列と基底の変換
    10.9 ラボ:ウェーブレットを用いた圧縮
        10.9.1 正規化されていない順変換
        10.9.2 順変換の正規化
        10.9.3 抑制による圧縮
        10.9.4 非正規化
        10.9.5 正規化されていない逆変換
        10.9.6 逆変換
    10.10 2次元画像の処理
        10.10.1 補助プロシージャ
        10.10.2 2次元ウェーブレット変換
        10.10.3 2次元順変換
        10.10.4 いくつかの補助プロシージャ
        10.10.5 2次元逆変換
        10.10.6 画像圧縮の実験
    10.11 確認用の質問
    10.12 問題

11章 特異値分解
    11.1 低ランク行列による行列の近似
        11.1.1 低ランク行列の利点
        11.1.2 行列のノルム
    11.2 路面電車の路線配置問題
        11.2.1 路面電車の路線配置問題の解
        11.2.2 行列のランク1近似
        11.2.3 最適なランク1近似
        11.2.4 最適なランク1近似の表現
        11.2.5 最も近い1次元アフィン空間
    11.3 最も近いk次元ベクトル空間
        11.3.1 特異値と特異ベクトルを求めるための「思考実験的」アルゴリズム
        11.3.2 特異値と右特異ベクトルの性質
        11.3.3 特異値分解
        11.3.4 右特異ベクトルを用いた最も近いk次元空間の求め方
        11.3.5 Aの最適なランクk近似
        11.3.6 最適なランクk近似の行列による表現
        11.3.7 0でない特異値の数とrank Aとの一致
        11.3.8 数値的なランク
        11.3.9 最も近いk次元アフィン空間
        11.3.10 Uが列直交であることの証明
    11.4 特異値分解の利用
        11.4.1 特異値分解の最小二乗問題への応用
    11.5 主成分分析
    11.6 ラボ:固有顔
    11.7 確認用の質問   55211.8問題

12章 固有ベクトル
    12.1 離散力学過程のモデル化
        12.1.1 2つの利息付きの口座
        12.1.2 フィボナッチ数
    12.2 フィボナッチ行列の対角化
    12.3 固有値と固有ベクトル
        12.3.1 相似と対角化可能性
    12.4 固有ベクトルによる座標表現
    12.5 インターネットワーム
    12.6 固有値の存在
        12.6.1 正定値行列と準正定値行列
        12.6.2 異なる固有値を持つ行列
        12.6.3 対称行列
        12.6.4 上三角行列
        12.6.5 一般の正方行列
    12.7 冪乗法
    12.8 マルコフ連鎖
        12.8.1 人口動態のモデル化
        12.8.2 ランディのモデル化
        12.8.3 マルコフ連鎖の定義
        12.8.4 メモリの取り出しにおける空間の局所性のモデル化
        12.8.5 文書のモデル化:不思議の国のハムレット
        12.8.6 それ以外のもののモデル化
        12.8.7 マルコフ連鎖の定常分布
        12.8.8 定常分布が存在するための十分条件
    12.9 ウェブサーファーのモデル化:ページランク
    12.10 *行列式
        12.10.1 平行四辺形の面積
        12.10.2 超平行体の体積
        12.10.3 平行四辺形を用いた多角形面積の表現
        12.10.4 行列式
        12.10.5 行列式関数を用いた固有値の特性化
    12.11 *いくつかの固有定理の証明
        12.11.1 固有値の存在
        12.11.2 対称行列の対角化
        12.11.3 三角化
    12.12 ラボ:ページランク
        12.12.1 概念の導入
        12.12.2 大きなデータセットの使用
        12.12.3 冪乗法を用いたページランクの実装
        12.12.4 データセット
        12.12.5 検索の処理
        12.12.6 ページランクの偏り
        12.12.7 おまけ:複数単語検索の処理
    12.13 確認用の質問
    12.14 問題

13章 線形計画法
    13.1 規定食の問題
    13.2 規定食の問題を線形計画としてとらえる
    13.3 線形計画法の起源
        13.3.1 用語
        13.3.2 線形計画法の異なる形式
        13.3.3 整数による線形計画法
    13.4 線形計画法の幾何学:多面体と頂点
    13.5 多面体の頂点であるような最適解の存在
    13.6 線形計画法の列挙アルゴリズム
    13.7 線形計画法における双対性入門
    13.8 シンプレックスアルゴリズム
        13.8.1 アルゴリズムの終了方法
        13.8.2 現在の解を表現する
        13.8.3 ピボットステップ
        13.8.4 単純な例
    13.9 頂点を見つける
    13.10 ゲーム理論
        13.10.1 線形計画としての定式化
        13.10.2 ノンゼロサムゲーム
    13.11 ラボ:線形計画法を用いた学習
        13.11.1 訓練データの読み込み
        13.11.2 線形計画の準備
        13.11.3 主制約条件
        13.11.4 非負制約条件
        13.11.5 行列A
        13.11.6 右辺のベクトルb
        13.11.7 目的関数ベクトルc
        13.11.8 これまでの作業の統合
        13.11.9 頂点の探索
        13.11.10 線形計画を解く
        13.11.11 結果の利用
    13.12 圧縮センシング
        13.12.1 より高速にMRI画像を得る
        13.12.2 少年を救うための計算
        13.12.3 前進
    13.13 確認用の質問
    13.14 問題

かなりヘビーな内容の印象だが、ベクトル・行列・テンソルを極めることでディープラーニングの肝を押さえられると感じた。

DL/MLと平行して行列プログラミングも取り組んでいくことにする。

6章 学習に関するテクニック ハイパーパラメータ(hyper-parameter)の検証 『ゼロから作るDeep Learning』

f:id:kaeken:20161112201050p:plain 6章 最後に、ハイパーパラメータ(hyper-parameter)の検証について。

ハイパーパラメータは人為的に試行錯誤しながら調整がするので検証が必要である。

しかし、汎化性能を評価するには、最終的に評価するための本番のテストデータが使えない。

そこで、ハイパーパラメータ専用の検証データvalidation dataを訓練データから分離して検証が必要。

次にハイパーパラメータの最適化は以下の手順で行う。

ステップ0:ハイパーパラメータの範囲を10^-3から10^3などの範囲でざっくり指定する
↓
ステップ1:設定された範囲からランダムにサンプリングする
↓
ステップ2:サンプリングされた値で学習し、検証データで認識精度を評価する。ただしエポックは小さく設定しておく。
↓
ステップ3:ステップ1−2を一定回数(100回など)繰り返し、さらにハイパーパラメータの範囲を狭めていく

以下、ハイパーパラメータの最適化をしていくサンプルコード

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')

from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.util import shuffle_dataset
from common.trainer import Trainer

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 高速化のため訓練データの削減
x_train = x_train[:500]
t_train = t_train[:500]

# 検証データの分離
validation_rate = 0.20
validation_num = x_train.shape[0] * validation_rate
x_train, t_train = shuffle_dataset(x_train, t_train)
x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[validation_num:]
t_train = t_train[validation_num:]


def __train(lr, weight_decay, epocs=50):
    network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
                            output_size=10, weight_decay_lambda=weight_decay)
    trainer = Trainer(network, x_train, t_train, x_val, t_val,
                      epochs=epocs, mini_batch_size=100,
                      optimizer='sgd', optimizer_param={'lr': lr}, verbose=False)
    trainer.train()

    return trainer.test_acc_list, trainer.train_acc_list


# ハイパーパラメータのランダム探索======================================
optimization_trial = 100
results_val = {}
results_train = {}
for _ in range(optimization_trial):
    # 探索したハイパーパラメータの範囲を指定===============
    weight_decay = 10 ** np.random.uniform(-8, -4)
    lr = 10 ** np.random.uniform(-6, -2)
    # ================================================

    val_acc_list, train_acc_list = __train(lr, weight_decay)
    print("val acc:" + str(val_acc_list[-1]) + " | lr:" + str(lr) + ", weight decay:" + str(weight_decay))
    key = "lr:" + str(lr) + ", weight decay:" + str(weight_decay)
    results_val[key] = val_acc_list
    results_train[key] = train_acc_list

# グラフの描画========================================================
print("=========== Hyper-Parameter Optimization Result ===========")
graph_draw_num = 20
col_num = 5
row_num = int(np.ceil(graph_draw_num / col_num))
i = 0

for key, val_acc_list in sorted(results_val.items(), key=lambda x:x[1][-1], reverse=True):
    print("Best-" + str(i+1) + "(val acc:" + str(val_acc_list[-1]) + ") | " + key)

    plt.subplot(row_num, col_num, i+1)
    plt.title("Best-" + str(i+1))
    plt.ylim(0.0, 1.0)
    if i % 5: plt.yticks([])
    plt.xticks([])
    x = np.arange(len(val_acc_list))
    plt.plot(x, val_acc_list)
    plt.plot(x, results_train[key], "--")
    i += 1

    if i >= graph_draw_num:
        break

#plt.show()
plt.savefig('hyperparameter_optimization_save.png')

以下、実行結果とグラフ(検証データ:実線、訓練データ:点線)

=========== Hyper-Parameter Optimization Result ===========
Best-1(val acc:0.77) | lr:0.00757365478878744, weight decay:4.612578587686737e-07
Best-2(val acc:0.76) | lr:0.007433061542623079, weight decay:1.0344933324636203e-07
Best-3(val acc:0.76) | lr:0.0069442614770180554, weight decay:3.5083892101171096e-07
Best-4(val acc:0.76) | lr:0.00786568274411027, weight decay:2.128003163191066e-06
Best-5(val acc:0.76) | lr:0.009977128388270529, weight decay:5.034544517460483e-07
Best-6(val acc:0.75) | lr:0.006651552434409378, weight decay:9.770817734920272e-08
Best-7(val acc:0.72) | lr:0.006787994923710441, weight decay:1.538788471810156e-08
Best-8(val acc:0.69) | lr:0.005309167300473579, weight decay:7.092519614173075e-07
Best-9(val acc:0.65) | lr:0.004171913096880143, weight decay:3.7534502493629925e-07
Best-10(val acc:0.64) | lr:0.0036177186197134006, weight decay:3.419650414313433e-07
Best-11(val acc:0.55) | lr:0.004657623419509241, weight decay:1.3150591635104507e-05
Best-12(val acc:0.45) | lr:0.002600118643665009, weight decay:6.101368606264845e-06
Best-13(val acc:0.42) | lr:0.0014781722372820818, weight decay:6.555096165359242e-07
Best-14(val acc:0.35) | lr:0.00112894903737969, weight decay:6.35129426114411e-08
Best-15(val acc:0.35) | lr:0.001122116867042764, weight decay:2.8146026473082357e-05
Best-16(val acc:0.31) | lr:0.0019504915229014257, weight decay:4.5759839999890385e-08
Best-17(val acc:0.29) | lr:0.001523802348758895, weight decay:1.3898280180637062e-07
Best-18(val acc:0.22) | lr:0.0011058186976594655, weight decay:2.7502587954667702e-08
Best-19(val acc:0.21) | lr:0.0004461535980654127, weight decay:7.17362065696084e-05
Best-20(val acc:0.21) | lr:0.0006365291753190107, weight decay:4.449107159041093e-06

f:id:kaeken:20161112201050p:plain

Best-5ぐらいまで順調に学習が進んでいることが分かる。

学習係数lrが0.001から0.01

Weight decay係数が10^-8から10^-6

このように、試行錯誤しながら、最適なハイパーパラメータを人為的に特定していかなければならない。

最後に、6章のまとめ問題

・パラメータの更新方法には、SGD以外に◯◯、◯◯、◯◯、などの手法がある
・重みの初期値の推奨は、「◯◯」や「◯◯」である
・◯◯を用いることで学習を加速でき、初期値に左右されにくい◯◯になる
・過学習を抑制する正則化技術として、◯◯や◯◯がある
・ハイパーパラメータの探索は、◯◯するのが効率的である

6章 学習に関するテクニック 正則化, Weight decay, Dropout 『ゼロから作るDeep Learning』

f:id:kaeken:20161112190945p:plain 続いて、正則化について。

過学習overfittingの起きる主な2つの原因

・パラメータを大量に持ち、表現力の高いモデルであること
・訓練データが少ないこと

敢えて過学習させたケースをサンプルコードで確認する

# cat overfit_weight_decay_pre_save.py

# coding: utf-8
import os
import sys

sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')

from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
t_train = t_train[:300]

weight_decay_lambda = 0
#weight_decay_lambda = 0.1
network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        weight_decay_lambda=weight_decay_lambda)
optimizer = SGD(lr=0.01)

max_epochs = 201
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0

for i in range(1000000000):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    grads = network.gradient(x_batch, t_batch)
    optimizer.update(network.params, grads)

    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)

        print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))

        epoch_cnt += 1
        if epoch_cnt >= max_epochs:
            break



# 3.グラフの描画==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
#plt.show()
plt.savefig('overfit_weight_decay_pre.png')

訓練データの認識精度が100%になり、テストデータと大きく乖離していることが分かる。 f:id:kaeken:20161112190917p:plain

そこで過学習抑制のため荷重減衰weight decayという手法を使う。 以下のように正則化の強さをコントールするハイパーパラメータを0.1に変更する

# diff overfit_weight_decay_pre_save.py overfit_weight_decay_save.py
20,21c20,21
< weight_decay_lambda = 0
< #weight_decay_lambda = 0.1
---
> #weight_decay_lambda = 0
> weight_decay_lambda = 0.1
68c68
< plt.savefig('overfit_weight_decay_pre.png')
---
> plt.savefig('overfit_weight_decay.png')

訓練データの認識精度が100%ではなくなり、テストデータとの乖離が小さくなっていることが分かる。 f:id:kaeken:20161112190945p:plain

続いて、Dropoutという過学習抑制手法について。

Dropoutとは:ニューロンをランダムに消去しながら学習する手法

以下実行したサンプルコード

# cat overfit_dropout_save.py

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')

from dataset.mnist import load_mnist
from common.multi_layer_net_extend import MultiLayerNetExtend
from common.trainer import Trainer

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
t_train = t_train[:300]

use_dropout = True  # Dropoutなしのときの場合はFalseに
dropout_ratio = 0.15
network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
                              output_size=10, use_dropout=use_dropout, dropout_ration=dropout_ratio)
trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=301, mini_batch_size=100,
                  optimizer='sgd', optimizer_param={'lr': 0.01}, verbose=True)
trainer.train()

train_acc_list, test_acc_list = trainer.test_acc_list, trainer.train_acc_list

# グラフの描画==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
#plt.show()
plt.savefig('overfit_dropout.png')

確かに荷重減衰手法と同様に、認識精度の乖離が小さくなった。 f:id:kaeken:20161112191022p:plain

6章 学習に関するテクニック Batch Normalization 『ゼロから作るDeep Learning』

f:id:kaeken:20161111205001p:plain

6章 学習に関するテクニック『ゼロから作るDeep Learning』

続いて、Batch Normalization

Batch Normalization(Batch Norm)とは:ミニバッチごとに正規化する手法。

メリット:

学習を速く進行させることができる
初期値にそれほど依存しない(初期値にロバスト)
過学習を抑制する

以下さまざまな初期値で比較した、サンプルスクリプト

# cat batch_norm_test_save.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')

from dataset.mnist import load_mnist
from common.multi_layer_net_extend import MultiLayerNetExtend
from common.optimizer import SGD, Adam

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 学習データを削減
x_train = x_train[:1000]
t_train = t_train[:1000]

max_epochs = 20
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01


def __train(weight_init_std):
    bn_network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100], output_size=10,
                                    weight_init_std=weight_init_std, use_batchnorm=True)
    network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100], output_size=10,
                                weight_init_std=weight_init_std)
    optimizer = SGD(lr=learning_rate)

    train_acc_list = []
    bn_train_acc_list = []

    iter_per_epoch = max(train_size / batch_size, 1)
    epoch_cnt = 0

    for i in range(1000000000):
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        t_batch = t_train[batch_mask]

        for _network in (bn_network, network):
            grads = _network.gradient(x_batch, t_batch)
            optimizer.update(_network.params, grads)

        if i % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            bn_train_acc = bn_network.accuracy(x_train, t_train)
            train_acc_list.append(train_acc)
            bn_train_acc_list.append(bn_train_acc)

            print("epoch:" + str(epoch_cnt) + " | " + str(train_acc) + " - " + str(bn_train_acc))

            epoch_cnt += 1
            if epoch_cnt >= max_epochs:
                break

    return train_acc_list, bn_train_acc_list


# 3.グラフの描画==========
weight_scale_list = np.logspace(0, -4, num=16)
x = np.arange(max_epochs)

for i, w in enumerate(weight_scale_list):
    print( "============== " + str(i+1) + "/16" + " ==============")
    train_acc_list, bn_train_acc_list = __train(w)

    plt.subplot(4,4,i+1)
    plt.title("W:" + str(w))
    if i == 15:
        plt.plot(x, bn_train_acc_list, label='Batch Normalization', markevery=2)
        plt.plot(x, train_acc_list, linestyle = "--", label='Normal(without BatchNorm)', markevery=2)
    else:
        plt.plot(x, bn_train_acc_list, markevery=2)
        plt.plot(x, train_acc_list, linestyle="--", markevery=2)

    plt.ylim(0, 1.0)
    if i % 4:
        plt.yticks([])
    else:
        plt.ylabel("accuracy")
    if i < 12:
        plt.xticks([])
    else:
        plt.xlabel("epochs")
    plt.legend(loc='lower right')

#plt.show()
plt.savefig('batch_norm_test_save.png')

f:id:kaeken:20161111205001p:plain 若干表示が崩れたが、確かに多くの場合Batch Normの方が速く学習が進む。

6章 学習に関するテクニック 推奨される重み初期値 『ゼロから作るDeep Learning』

f:id:kaeken:20161111191540p:plain 6章 学習に関するテクニック 『ゼロから作るDeep Learning』

続き。

重みの初期値について。

結論として、重みの初期値をゼロにするのはNG。

重みの対照的な構造を崩し、ランダムな初期値を設定する。

勾配消失gradient vanishing:逆伝播での勾配の値が徐々に小さくなること。

多層NNでは勾配消失が問題になるので、推奨された初期値を使う。

活性化関数にS字カーブ(sigmoid,tanh)を使うなら「Xavierの初期値」

活性化関数にReLUを使うなら「Heの初期値」

以下、比較したサンプルコード

# cat weight_init_compare_save.py


# coding: utf-8
import os
import sys

sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')

from dataset.mnist import load_mnist
from common.util import smooth_curve
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD


# 0:MNISTデータの読み込み==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000


# 1:実験の設定==========
weight_init_types = {'std=0.01': 0.01, 'Xavier': 'sigmoid', 'He': 'relu'}
optimizer = SGD(lr=0.01)

networks = {}
train_loss = {}
for key, weight_type in weight_init_types.items():
    networks[key] = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100],
                                  output_size=10, weight_init_std=weight_type)
    train_loss[key] = []


# 2:訓練の開始==========
for i in range(max_iterations):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    for key in weight_init_types.keys():
        grads = networks[key].gradient(x_batch, t_batch)
        optimizer.update(networks[key].params, grads)

        loss = networks[key].loss(x_batch, t_batch)
        train_loss[key].append(loss)

    if i % 100 == 0:
        print("===========" + "iteration:" + str(i) + "===========")
        for key in weight_init_types.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))

# 3.グラフの描画==========
markers = {'std=0.01': 'o', 'Xavier': 's', 'He': 'D'}
x = np.arange(max_iterations)
for key in weight_init_types.keys():
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 2.5)
plt.legend()
#plt.show()
plt.savefig('weight_init_compare.png')

f:id:kaeken:20161111191540p:plain

確かにXavier,Heの初期値の方が学習の進みが速い。

重みを適当に設定せず、適切な学習が進むように適切に設定する必要がある。

6章 学習に関するテクニック SGDとMomentum, AdaGrad, Adam 『ゼロから作るDeep Learning』

f:id:kaeken:20161110202746p:plain

6章 学習に関するテクニック

この章では、NN学習でキーとなるアイデアについて学ぶ。

パラメータの更新
重みの初期値
Batch Normalization
正則化
ハイパーパラメータの検証
まとめ

パラメータの更新について。

最適化optimizationとは:NN学習目的である「損失関数を最小化する最適なパラメータ」を見つける問題を解くこと。

確率的勾配降下法SGDの欠点として、関数の形状が等方向でないと非効率な経路で探索する

class SGD:

    """確率的勾配降下法(Stochastic Gradient Descent)"""

    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]

欠点を改善するため3つの手法Momentum, AdaGrad, Adamがある。以下、サンプルコード。

# Momentum: 速度概念の導入
class Momentum:

    """Momentum SGD"""

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
            params[key] += self.v[key]

# AdaGrad: 学習係数の減衰learning rate decayの導入
class AdaGrad:

    """AdaGrad"""

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)


# RMSProp: AdaGradで更新量が0になる問題に対して指数移動平均を導入
class RMSprop:

    """RMSprop"""

    def __init__(self, lr=0.01, decay_rate = 0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)


# Adam: MomentumとAdaGradを融合したような手法
class Adam:

    """Adam (http://arxiv.org/abs/1412.6980v8)"""

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)

        self.iter += 1
        lr_t  = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)

        for key in params.keys():
            #self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
            #self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])

            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

            #unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
            #unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
            #params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)

続いて、SGD, Momentum, AdaGrad, Adamの比較

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import matplotlib.pyplot as plt
plt.switch_backend('agg')
from dataset.mnist import load_mnist
from common.util import smooth_curve
from common.multi_layer_net import MultiLayerNet
from common.optimizer import *


# 0:MNISTデータの読み込み==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000


# 1:実験の設定==========
optimizers = {}
optimizers['SGD'] = SGD()
optimizers['Momentum'] = Momentum()
optimizers['AdaGrad'] = AdaGrad()
optimizers['Adam'] = Adam()
#optimizers['RMSprop'] = RMSprop()

networks = {}
train_loss = {}
for key in optimizers.keys():
    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[100, 100, 100, 100],
        output_size=10)
    train_loss[key] = []

# 2:訓練の開始==========
for i in range(max_iterations):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    for key in optimizers.keys():
        grads = networks[key].gradient(x_batch, t_batch)
        optimizers[key].update(networks[key].params, grads)

        loss = networks[key].loss(x_batch, t_batch)
        train_loss[key].append(loss)

    if i % 100 == 0:
        print( "===========" + "iteration:" + str(i) + "===========")
        for key in optimizers.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))


# 3.グラフの描画==========
markers = {"SGD": "o", "Momentum": "x", "AdaGrad": "s", "Adam": "D"}
x = np.arange(max_iterations)
for key in optimizers.keys():
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 1)
plt.legend()
#plt.show()
plt.savefig('optimizer_compare_mnist.png')

確かにSGDより優れた手法であることが分かった。 f:id:kaeken:20161110202746p:plain

以下、続く。

重みの初期値 Batch Normalization 正則化 ハイパーパラメータの検証 まとめ

5章 誤差逆伝播法(ごさぎゃくでんぱほう)Backpropagation『ゼロから作るDeep Learning』

5章誤差逆伝播

いよいよ誤差逆伝播法まできた。

数式より計算グラフの方が理解しやすいらしい。

詳しくは本書参照。

まずはレイヤ概念の導入。

レイヤ(層)とは:NNにおける機能の単位を指す。レイヤ単位で実装することで、ブロックのように積み上げていける。

まず、乗算レイヤの実装と、加算レイヤの実装について。

# cat layer_naive.py


# coding: utf-8


class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

    def forward(self, x, y):
        self.x = x
        self.y = y
        out = x * y

        return out

    def backward(self, dout): #dout微分
        dx = dout * self.y
        dy = dout * self.x

        return dx, dy


class AddLayer:
    def __init__(self):
        pass

    def forward(self, x, y):
        out = x + y

        return out

    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1

        return dx, dy

つぎに、活性化関数レイヤ(ReLUレイヤ、Sigmoidレイヤ)の実装について。

# cat common/layers.py

# coding: utf-8
import numpy as np
from common.functions import *
from common.util import im2col, col2im


class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0

        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx


class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = sigmoid(x)
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out

        return dx

つぎに、Affineレイヤの実装について。

Affineとは:行列の内積を行うレイヤ。幾何学で「アフィン変換」と呼ばれる。

class Affine:
    def __init__(self, W, b):
        self.W =W
        self.b = b

        self.x = None
        self.original_x_shape = None
        # 重み・バイアスパラメータの微分
        self.dW = None
        self.db = None

    def forward(self, x):
        # テンソル対応
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0], -1)
        self.x = x

        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        dx = dx.reshape(*self.original_x_shape)  # 入力データの形状に戻す(テンソル対応)
        return dx

つぎに、Softmaxレイヤの実装について。

Softmaxレイヤは学習時のみ必要で推論時には必要ない。

サンプルでは、Softmax関数と交差エントロピー誤差のレイヤSoftmax-with-Lossを定義している

導出過程が複雑なので、付録Aをあとで参照。

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None # softmaxの出力
        self.t = None # 教師データ

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)

        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size: # 教師データがone-hot-vectorの場合
            dx = (self.y - self.t) / batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size), self.t] -= 1
            dx = dx / batch_size

        return dx

最後に、誤差逆伝播法の実装について。

以下の学習4ステップのうち、ステップ2勾配の算出で登場

学習の4ステップ

ステップ1ミニバッチ:ミニバッチをランダムに選択
ステップ2勾配算出:ミニバッチの損失関数を減らす勾配を算出
ステップ3パラメータ更新:重みパラメータを勾配方向に微小量だけ更新
ステップ4反復:ステップ1−3を反復
# cat two_layer_net.py


# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collections import OrderedDict


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 重みの初期化
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

        # レイヤの生成
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)

        return x

    # x:入力データ, t:教師データ
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)

    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=1)

        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy

    # x:入力データ, t:教師データ
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)

        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

        return grads

    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 設定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

実装した誤差逆伝播法の勾配確認をおこなう。

勾配確認gradient checkとは:誤差逆伝播法と数値微分の結果を比較し、ほぼ近似できることで誤差逆伝播法の正しさを確認すること。

# cat deep-learning-from-scratch/ch05/gradient_check.py
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# データの読み込み
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

x_batch = x_train[:3]
t_batch = t_train[:3]

grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)

for key in grad_numerical.keys():
    diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )
    print(key + ":" + str(diff))

勾配確認のサンプルコードを実行してみる:

# py gradient_check.py
W2:9.77654477536e-13
W1:2.37797639143e-13
b2:1.2012613404e-10
b1:7.43019632366e-13

ほぼ誤差がないことが分かった。

そして、誤差逆伝播法を訓練データを学習させてみる。

# py train_neuralnet.py

0.102266666667 0.1016
0.90125 0.9073
0.9205 0.9221
0.936033333333 0.9349
0.945816666667 0.9461
0.9512 0.9504
0.957433333333 0.955
0.961133333333 0.959
0.965266666667 0.962
0.967566666667 0.9636
0.970366666667 0.9637
0.97065 0.9635
0.974466666667 0.9674
0.974416666667 0.9671
0.976816666667 0.968
0.97775 0.9698
0.978783333333 0.971

認識精度がかなり向上していることがわかる。

5章まとめ問題

・◯◯1を用いれば計算過程を視覚的に把握できる
・◯◯2は局所的な計算によって全体の計算を構成する
・◯◯1の◯◯3は通常の計算を行い、◯◯4によって各ノードの微分が求まる
・◯◯5とは、NNの構成要素を◯◯6で実装することで勾配計算を効率化する
・◯◯6とは、◯◯5と◯◯7の結果を比較して◯◯5の実装が誤っていないことが確認できる