LoginSignup
2
1

More than 3 years have passed since last update.

「ゼロから作るDeep Learning」自習メモ(その9)MultiLayerNet クラス

Last updated at Posted at 2020-09-16

「ゼロから作るDeep Learning」(斎藤 康毅 著 オライリー・ジャパン刊)を読んでいる時に、参照したサイト等をメモしていきます。 その8 ← →その10

5章でニューラルネットワークの構成要素をレイヤとして実装し、6章でそれを使っています。
個々のレイヤについては5章で説明がありますが、実装した MultiLayerNet クラスは、本の中では説明されていません。ソースは、フォルダcommon の multi_layer_net.py にあります。

ということで、MultiLayerNet クラスの中身を見ていきます。

6.1.8 MNIST データセットによる更新手法の比較 で使っているソースコード

ch06/optimizer_compare_mnist.py でMultiLayerNet クラスを参照しています。

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import matplotlib.pyplot as plt
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)

MNISTデータセットを使うので、
input_size=784   入力は784 列(元は28 × 28)の画像データ
output_size=10   出力は0~9のどれであるかの予測確率
hidden_size_list=[100, 100, 100, 100] 隠れ層が4層あり、ニューロンの数が各100個
そして
batch_size ミニバッチのサイズ(入力データの行数)が 128

この設定で全結合による多層ニューラルネットワークを作成するから、
こんな感じで、配列が作られる。

w6-a.jpg

以下、networksオブジェクトの中にある配列やオブジェクトを参照したときのものです。

networks

{'SGD': common.multi_layer_net.MultiLayerNet at 0x8800d50,
'Momentum': common.multi_layer_net.MultiLayerNet at 0x8800a50,
'AdaGrad': common.multi_layer_net.MultiLayerNet at 0x8800710,
'Adam': common.multi_layer_net.MultiLayerNet at 0x88003d0}

networks['SGD']

common.multi_layer_net.MultiLayerNet at 0x8800d50

networks['SGD'].layers

OrderedDict([('Affine1', common.layers.Affine at 0x8800c30),
('Activation_function1', common.layers.Relu at 0x8800c70),
('Affine2', common.layers.Affine at 0x8800bf0),
('Activation_function2', common.layers.Relu at 0x8800bd0),
('Affine3', common.layers.Affine at 0x8800b90),
('Activation_function3', common.layers.Relu at 0x8800b70),
('Affine4', common.layers.Affine at 0x8800ab0),
('Activation_function4', common.layers.Relu at 0x8800b30),
('Affine5', common.layers.Affine at 0x8800af0)])

networks['SGD'].layers['Affine1'].x

array([[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]], dtype=float32)

networks['SGD'].layers['Affine1'].x.shape

(128, 784)

networks['SGD'].layers['Affine1'].W

array([[-0.04430735, -0.00916858, -0.05385046, ..., -0.01356, 0.0366878, -0.04629992],
[-0.0974915 , 0.01896 , 0.0016755 , ..., 0.00820512, -0.01012246, -0.04869024],
...,
[ 0.03065034, -0.02653425, -0.00433941, ..., -0.06933382, 0.03986452, 0.06821553],
[ 0.01673732, 0.04850334, -0.0291053 , ..., -0.05045292, 0.00599257, 0.08265754]])

networks['SGD'].layers['Affine1'].W.shape

(784, 100)

networks['SGD'].layers['Affine1'].b

array([ 0.01646891, 0.01467293, 0.02892796, 0.02414651, 0.02259769,
-0.00919552, -0.01567924, 0.0039934 , 0.00693527, 0.03932801,
...,
-0.00536202, 0.00508444, 0.00204647, 0.01040528, 0.00355356,
-0.00960685, 0.06204312, 0.02886584, 0.06678846, 0.0186539 ])

networks['SGD'].layers['Affine1'].b.shape

(100,)

networks['SGD'].layers['Activation_function1']

common.layers.Relu at 0x8800c70

networks['SGD'].layers['Affine2'].x.shape

(128, 100)

networks['SGD'].layers['Affine2'].W.shape

(100, 100)

networks['SGD'].layers['Affine2'].b.shape

(100,)

networks['SGD'].layers['Activation_function2']

common.layers.Relu at 0x8800bd0

networks['SGD'].layers['Affine3'].x.shape

(128, 100)

networks['SGD'].layers['Affine3'].W.shape

(100, 100)

networks['SGD'].layers['Affine3'].b.shape

(100,)

networks['SGD'].layers['Activation_function3']

common.layers.Relu at 0x8800b70

networks['SGD'].layers['Affine4'].x.shape

(128, 100)

networks['SGD'].layers['Affine4'].W.shape

(100, 100)

networks['SGD'].layers['Affine4'].b.shape

(100,)

networks['SGD'].layers['Activation_function4']

common.layers.Relu at 0x8800b30

指定した中間層の数は4だが、その後に出力層として5番目の層を作っている。

networks['SGD'].layers['Affine5'].x.shape

(128, 100)

networks['SGD'].layers['Affine5'].W.shape

(100, 10)

networks['SGD'].layers['Affine5'].b.shape

(10,)

出力層の活性化関数は、last_layer に定義されている。
ここではソフトマックス関数を使っている。

networks['SGD'].last_layer

common.layers.SoftmaxWithLoss at 0x8800770

networks['SGD'].last_layer.y

array([[2.08438091e-05, 2.66555051e-09, 1.29436456e-03, ...,
1.83391350e-07, 9.98317338e-01, 6.77137764e-05],
[5.68871828e-04, 1.59787427e-06, 3.60265866e-03, ...,
7.25385216e-05, 1.80220134e-03, 4.95014520e-02],
...,
[3.01731618e-03, 5.57601184e-03, 1.40908372e-02, ...,
8.49627989e-02, 5.44208078e-03, 2.32114245e-01],
[9.82201047e-07, 3.01213101e-07, 1.05657504e-03, ...,
1.03584551e-05, 9.92242677e-01, 5.06642654e-03]])

networks['SGD'].last_layer.y.shape

(128, 10)

学習結果でテストしてみた 

P177
この実験では、5 層のニューラルネットワークで、各層100 個のニューロンを持つ
ネットワークを対象にしました。また、活性化関数としてReLU を使用しました。
図6-9 の結果を見ると、SGD よりも他の手法が速く学習できていることが分か
ります。残り3 つの手法は同じように学習が行われているようです。よく見ると
AdaGrad の学習が少しだけ速く行われているようです。この実験の注意点として
は、学習係数のハイパーパラメータや、ニューラルネットワークの構造(何層の深さ
か、など)によって結果は変化するということです。ただし、一般にSGD よりも他
の3 つの手法のほうが速く学習でき、時には最終的な認識性能も高くなります。

ということなので、テストデータで認識性能の違いを調べて見た。

# テストデータで評価
x = x_test
t = t_test

for key in optimizers.keys():
    network = networks[key]

    y = network.predict(x)

    accuracy_cnt = 0
    for i in range(len(x)):
        p= np.argmax(y[i])
        if p == t[i]:
            accuracy_cnt += 1

    print(key + " Accuracy:" + str(float(accuracy_cnt) / len(x))) 

SGD Accuracy:0.934
Momentum Accuracy:0.9676
AdaGrad Accuracy:0.97
Adam Accuracy:0.9701

確かに、SGDの認識率は低いようです。

ミニバッチの回数を2000から5000に変えて学習し、テストしてみたら、こうなりました。

max_iterations = 5000

SGD Accuracy:0.9557
Momentum Accuracy:0.9754
AdaGrad Accuracy:0.9755
Adam Accuracy:0.9752

回数を増やすと、4つとも認識率が上がりました。SGDも上がりましたが、2000回のときの他の手法よりも低いままです。

中間層を2つに減らして、3層のニューラルネットで学習してみた。

    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[100, 100],
        output_size=10)

SGD Accuracy:0.922
Momentum Accuracy:0.9633
AdaGrad Accuracy:0.9682
Adam Accuracy:0.9701

4つとも認識率が1%程度悪くなっている。

中間層を8つに増やして、9層のニューラルネットで学習してみた。

    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100, 100, 100],
        output_size=10)

SGD Accuracy:0.9479
Momentum Accuracy:0.9656
AdaGrad Accuracy:0.9692
Adam Accuracy:0.9701

SGDの認識率は上がりましたが、他は同じか少し悪くなってます。
層を増やすことで認識率が上がった、というよりは、学習の速度が速くなったと考えたほうがいいのかも?

中間層のニューロンの数を50に減らしてみた

    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[50, 50, 50, 50],
        output_size=10)

SGD Accuracy:0.9275
Momentum Accuracy:0.9636
AdaGrad Accuracy:0.962
Adam Accuracy:0.9687

中間層のニューロンの数を200に増やしてみた

    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[200, 200, 200, 200],
        output_size=10)

SGD Accuracy:0.9372
Momentum Accuracy:0.9724
AdaGrad Accuracy:0.9775
Adam Accuracy:0.9753

その8 ← →その10

読めない用語集

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1