神经网络基础学习笔记(五) 与学习相关的技巧

 

目录

与学习相关的技巧

6.1 参数的更新

6.1.1 探险家的故事

6.1.2 SGD

6.1.3 SGD的缺点

6.1.4 Momentum

6.1.5 AdaGrad

6.1.6 Adam

6.1.7 使用哪种更新方法呢

6.1.8 基于MNIST数据集的更新方法的比较

6.2 权重的初始值

6.2.1 可以将权重初始值设为0吗

6.2.2 隐藏层的激活值的分布​​

6.2.3 ReLU的权重初始值

6.2.4 基于MNIST数据集的权重初始值的比较

6.3 Batch Normalization

6.3.1 Batch Normalization 的算法

6.4 正则化

6.4.1 过拟合

6.4.2 权值衰减

6.4.3 Dropout

6.5 超参数的验证

6.5.1 验证数据

6.5.2 超参数的最优化

6.5.3 超参数最优化的实现

6.6 小结


与学习相关的技巧

题涉及寻找最优权重 参数最优化方法权重参数的初始值超参数的设定方法

为了应对过拟合,本章还将介绍权值衰减、Dropout等正则化方法,对近年来众多研究中使用的Batch Normalization方法进行简单的介绍

6.1 参数的更新

神经网络的学习的目的是找到使损失函数的值尽可能小的参数。

前面我们通过的梯度(导数)作为了线索寻找最优参赛

随机梯度下降法(stochastic gradient descent), 简称SGD

6.1.1 探险家的故事

简称瞎走,通过感觉找到

6.1.2 SGD

教学式:

式子中的←表示用右边的值更新左边的值

参数更新:

后面我们马上会实现另一个最优化方法Momentum,optimizer = SGD()这一语句换成optimizer = Momentum(),就可以从SGD切 换为Momentum。

6.1.3 SGD的缺点

举个例子:

上式(6.2)表示的函数的梯度,如果用图表示,则如下:

虽然式 (6.2)的最小值在(x, y) = (0, 0)处,但是图6-2中的梯度在很多地方并没有指 向(0, 0)。

SGD的缺点是,如果函数的形状非均向(anisotropic),比如呈延伸状,搜索 的路径就会非常低效。

我们需要比单纯朝梯度方向前进的SGD更聪明的方法。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向

将介绍Momentum、AdaGrad、Adam这3 种方法来取代SGD。

6.1.4 Momentum

这里新出现了一个变量v,对应物理上的速度。

式(6.3)表示了物体在梯度方向上受力在这个力的作用下,物体的速度增 加这一物理法则。

实现代码:

和SGD相比,我们发现 “之”字形的“程度”减轻了。这是因为虽然x轴方向上受到的力非常小,但 是一直在同一方向上受力,所以朝同一个方向会有一定的加速。

6.1.5 AdaGrad

在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。

在更新参数时,通过乘以1/根号h ,就可以调整学习的尺度。这意味着, 参数的元素中变动较大(被大幅更新)的元素的学习率将变小.

注意:

AdaGrad会记录过去所有梯度的平方和。学习越深入,更新 的幅度就越小,所以可以使用 RMSProp“指数移动平均”,呈指数函数式地减小 过去的梯度的尺度。

使用AdaGrad解决式(6.2)的最优化问题

由图6-6的结果可知,函数的取值高效地向着最小值移动。由于y轴方 向上的梯度较大,因此刚开始变动较大,但是后面会根据这个较大的变动按 比例进行调整,减小更新的步伐。因此,y轴方向上的更新程度被减弱,“之” 字形的变动程度有所衰减

6.1.6 Adam

Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参 数的每个元素适当地调整更新步伐。

于是乎两个结合在一起?这就是Adam方法的基本思路 

通过组合前面两个方法的优点,有望 实现参数空间的高效搜索。具体的原理后面的时候可以看论文

注意:

Adam会设置 3个超参数。一个是学习率(论文中以α出现),另外两 个是一次momentum系数β1和二次momentum系数β2。根据论文, 标准的设定值是β1为 0.9,β2 为 0.999。设置了这些值后,大多数情 况下都能顺利运行。

6.1.7 使用哪种更新方法呢

非常遗憾,(目前)并不存在能在所有问题中都表现良好 的方法。这4种方法各有各的特点,都有各自擅长解决的问题和不擅长解决 的问题。

最近,很多研究人员和技术人员都喜欢用Adam。本书将主要使用 SGD或者Adam,读者可以根据自己的喜好多多尝试。

6.1.8 基于MNIST数据集的更新方法的比较

# 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)
    
    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()

效果图:

这个实验以一个5层神经网络为对象,其中每层有100个神经元。激活 函数使用的是ReLU。

与SGD相比,其他3种方法学习得更快,而且 速度基本相同,仔细看的话,AdaGrad的学习进行得稍微快一点

6.2 权重的初始值

设定什么样的 权重初始值,经常关系到神经网络的学习能否成功

6.2.1 可以将权重初始值设为0吗

抑制过拟合、提高泛化能力的技巧——权值衰减(weight decay)

在这之前的权重初始值都是像0.01 * np.random.randn(10, 100)这样,使用 由高斯分布生成的值乘以0.01后得到的值(标准差为0.01的高斯分布)

事实上,将权重初始值设为 0的话,将无法正确进行学习。为什么不能将权重初始值设为0呢?严格地说,为什么不能将权重初始 值设成一样的值呢?这是因为在误差反向传播法中,所有的权重值都会进行 相同的更新

为了防止“权重均一化” (严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值

6.2.2 隐藏层的激活值的分布

观察隐藏层的激活值 A (激活函数的输出数据)的分布,可以获得很多启 发。

实验的目的是通过改变这个尺度(标准差),观察激活值的分布如何变 化。现在,我们将保存在activations中的各层数据画成直方图

随着输出不断地靠近0(或者靠近1),它的导数的值逐渐接 近0。

因此,偏向0和1的数据分布会造成反向传播中梯度的值不断变小,最后消失。这个问题称为梯度消失(gradient vanishing)。层次加深的深度学习 中,梯度消失的问题可能会更加严重。

下面,将权重的标准差设为0.01,进行相同的实验。实验的代码只需要 把设定权重初始值的地方换成下面的代码即可。

使用标准差为0.01的高斯分布时,各层的激活值的分布 如图6-11所示

因为不像刚才的例子那样偏向0和1,所 以不会发生梯度消失的问题。但是,激活值的分布有所偏向,说明在表现力 上会有很大问题。为什么这么说呢?因为如果有多个神经元都输出几乎相同 的值,那它们就没有存在的意义了。

表现力受限问题

为了使各层的激活值呈现出具有相同广度的分布,推导了合适的权重尺度。

前一层的节点数越多,要设定为目标节点的初始值的权重尺度就越小

使用Xavier初始值后的结果如图6-13所示。从这个结果可知,越是后 面的层,图像变得越歪斜,但是呈现了比之前更有广度的分布。

如果用tanh 函数(双曲线函数)代替sigmoid函数,这个稍微歪斜的问题就能得 到改善。

6.2.3 ReLU的权重初始值

Xavier初始值是以激活函数线性函数为前提而推导出来的。因为 sigmoid函数和tanh函数左右对称,且中央附近可以视作线性函数,所以适 合使用Xavier初始值。但当激活函数使用ReLU时,一般推荐使用ReLU专 用的初始值,也就是Kaiming He等人推荐的初始值,也称为“He初始值

因此:
总结一下:

当激活函数使用ReLU时权重初始值使用He初始值;

当 激活函数为sigmoid或tanh等S型曲线函数时,初始值使用Xavier初始值

这是目前的最佳实践。

6.2.4 基于MNIST数据集的权重初始值的比较

实现代码:

# coding: utf-8
import os
import sys

sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
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 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()

基于MNIST数据集的权重初始值的比较:横轴是学习的迭代次数(iterations), 纵轴是损失函数的值(loss)

从图6-15的结果可知,std = 0.01时完全无法进行学习。这和刚 才观察到的激活值的分布一样,是因为正向传播中传递的值很小(集中在0 附近的数据)。因此,逆向传播时求到的梯度也很小,权重几乎不进行更新。 相反,当权重初始值为Xavier初始值和He初始值时,学习进行得很顺利。 并且,我们发现He初始值时的学习进度更快一些。

综上:

权重初始值非常重要,关系到学习能否成功。

6.3 Batch Normalization

为了使各层拥有适当的广度,“强制性”地调整激活值的分布 会怎样呢?实际上,Batch Normalization为了使各层拥有适当的广度,“强制性”地调整激活值的分布 会怎样呢?实际上,Batch Normalization。

6.3.1 Batch Normalization 的算法

为什么Batch Norm这么惹人注目呢?因为Batch Norm有以下优点

  • 可以使学习快速进行(可以增大学习率)
  • 不那么依赖初始值(对于初始值不用那么神经质)。
  • 抑制过拟合(降低Dropout等的必要性)。

要向神经网络中插入对数据分布进行正规化的层,即Batch Normalization层(下文简称Batch Norm层)

Batch Norm,顾名思义,以进行学习时的mini-batch为单位,按minibatch进行正规化

具体而言,就是进行使数据分布的均值为0方差为1的 正规化

式(6.7)中的ε是一个微小值(比如,10e-7等),它是为了防止出现 除以0的情况。

式(6.7)所做的是将mini-batch的输入数据{x1, x2, ... , xm}变换为均值为0、方差为1的数据 ,非常简单。通过将这个处理插入到 激活函数的前面(或者后面)A,可以减小数据分布的偏向

Batch Norm层会对正规化后的数据进行缩放和平移的变换

Batch Norm可以表示为图6-17

Batch Norm的反向传播的推导有些复杂,这里我们不进行介绍。

6.3.2 Batch Normalization的评估

实现代码:

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
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),fontsize = 10)
    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()

实验效果图字体比较难调,所以用书本上的了

学习过程时间特别短:

综上,通过使用Batch Norm,可以推动学习的进行。并且,对权重初 始值变得健壮(“对初始值健壮”表示不那么依赖初始值)。Batch Norm具备 了如此优良的性质,一定能应用在更多场合中”

6.4 正则化

6.4.1 过拟合

发生过拟合的原因,主要有以下两个。

• 模型拥有大量参数、表现力强。

• 训练数据少

我们在实验过程中,先尝试这两个错误:

从 MNIST数据集原本的60000个训练数据中只选定300个,并且,为了增加网 络的复杂度,使用7层网络(每层有100个神经元,激活函数为ReLU)。

过了 100 个 epoch 左右后,用训练数据测量到的识别精度几乎都为 100%。但是,对于测试数据,离100%的识别精度还有较大的差距。如此大 的识别精度差距,是只拟合了训练数据的结果。从图中可知,模型对训练时 没有使用的一般数据(测试数据)拟合得不是很好。

6.4.2 权值衰减

权值衰减是一直以来经常被使用的一种抑制过拟合的方法。

对于所有权重,权值衰减方法都会为损失函数加上 

因此,在求权 重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数λW

实现代码:

# coding: utf-8
import os
import sys

sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
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(权值衰减)的设定 =======================
#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()

结果:

图6-21 使用了权值衰减的训练数据(train)和测试数据(test)的识别精度的变化
图6-21 使用了权值衰减的训练数据(train)和测试数据(test)的识别精度的变化
 

如图6-21所示,虽然训练数据的识别精度和测试数据的识别精度之间有 差距,但是与没有使用权值衰减的图6-20的结果相比,差距变小了

6.4.3 Dropout

如果网络的模型变得很复杂,我们经常会使用Dropout方法.

Dropout是一种在学习的过程中随机删除神经元的方法。训练时,每传递一次数据,就会随机选择要删除的神经元。

下面我们来实现Dropout。这里的实现重视易理解性。不过,因为训练 时如果进行恰当的计算的话,正向传播时单纯地传递数据就可以了(不用乘 以删除比例),所以深度学习的框架中进行了这样的实现。关于高效的实现, 可以参考Chainer中实现的Dropou

这里的要点是,每次正向传播时,self.mask中都会以False的形式保 存要删除的神经元。self.mask会随机生成和x形状相同的数组,并将值比 dropout_ratio大的元素设为True。反向传播时的行为和ReLU相同

我们使用MNIST数据集进行验证,以确认Dropout的效果。源代 码在ch06/overfit_dropout.py中。另外,源代码中使用了Trainer类来简化实现

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
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]

# 设定是否使用Dropuout,以及比例 ========================
use_dropout = True  # 不使用Dropout的情况下为False
dropout_ratio = 0.2
# ====================================================
#用Dropout的情况下
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.train_acc_list, trainer.test_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()

结果:

图6-23 右边没有使用Dropout,左边使用了Dropout(dropout_rate=0.15)

图6-23中,通过使用Dropout,训练数据和测试数据的识别精度的差距 变小了。

6.5 超参数的验证

这里所说的超参数是指,比如各层的神经元数量、batch大小、参 数更新时的学习率或权值衰减

6.5.1 验证数据

为什么不能用测试数据评估超参数的性能呢?这是因为如果使用测试数 据调整超参数,超参数的值会对测试数据发生过拟合。

用于调整超参 数的数据,一般称为验证数据(validation data)。我们使用这个验证数据来 评估超参数的好坏。

与吴恩达的三部分数据划分一致。有的会事先分成训练数据、验证数据、测试数据三 部分,有的只分成训练数据和测试数据两部分。

6.5.2 超参数的最优化

所谓逐渐缩小范围,是指一开始先大致设定一个范围,从这个范围中随机选 出一个超参数(采样),用这个采样到的值进行识别精度的评估

超参数的范围只要“大致地指定”就可以了。所谓“大致地指定”,是指 像0.001(10^−3 )到1000(10^3 )这样,以“10的阶乘”的尺度指定范围(也表述 为“用对数尺度(log scale)指定”)。

超参数的最优化的内容,简单归纳一下,如下所示。

这里介绍的超参数的最优化方法是实践性的方法。... 人为调参

6.5.3 超参数最优化的实现

如前所述,通过从 0.001(10^−3 )到 1000(10^3)

这样的对数尺度的范围 中随机采样进行超参数的验证。

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
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 = int(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):
    # 指定搜索的超参数的范围===============
    # 0.001(10^−3 )到 1000(10^3) 对数尺度的范围
    # 中随机采样进行超参数的验证
    # 权值衰减系数的初始范围为10−8 到10−4
    # 学习率的初始范围为10^−6到10^−2

    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()

这样就能缩小到合适的超参数的存在范围,然后在某个阶段,选择一个最终 的超参数的值

6.6 小结

本章我们介绍了神经网络的学习中的几个重要技巧。

参数的更新方法、 权重初始值的赋值方法、Batch Normalization、Dropout等,这些都是现代 神经网络中不可或缺的技术。另外,这里介绍的技巧,在最先进的深度学习 中也被频繁使用。

吴恩达的视屏看完后看这个真的会有不一样的收获。

猜你喜欢

转载自blog.csdn.net/qq_37457202/article/details/107606153