百度PaddlePaddle入门-12(网络优化)

神经网络所拟合的函数是高度非凸函数,理想的训练目标是,优化这类函数,达到函数最小值点或接近最小值的极小值点。选择合适的优化器和学习率是实现训练目标的关键因素。接下来主要探讨如何设置合理的优化器和学习率参数,保证训练结果达到理想目标。

注:设置模型的优化算法在启动训练前,所以加载数据和网络结构的代码均不变


  1 # 加载相关库
  2 import os
  3 import random
  4 import paddle
  5 import paddle.fluid as fluid
  6 from paddle.fluid.dygraph.nn import Conv2D, Pool2D, FC
  7 import numpy as np
  8 from PIL import Image
  9 
 10 import gzip
 11 import json
 12 
 13 # 定义数据集读取器
 14 def load_data(mode='train'):
 15 
 16     # 读取数据文件
 17     datafile = './work/mnist.json.gz'
 18     print('loading mnist dataset from {} ......'.format(datafile))
 19     data = json.load(gzip.open(datafile))
 20     # 读取数据集中的训练集,验证集和测试集
 21     train_set, val_set, eval_set = data
 22 
 23     # 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
 24     IMG_ROWS = 28
 25     IMG_COLS = 28
 26     # 根据输入mode参数决定使用训练集,验证集还是测试
 27     if mode == 'train':
 28         imgs = train_set[0]
 29         labels = train_set[1]
 30     elif mode == 'valid':
 31         imgs = val_set[0]
 32         labels = val_set[1]
 33     elif mode == 'eval':
 34         imgs = eval_set[0]
 35         labels = eval_set[1]
 36     # 获得所有图像的数量
 37     imgs_length = len(imgs)
 38     # 验证图像数量和标签数量是否一致
 39     assert len(imgs) == len(labels), \
 40           "length of train_imgs({}) should be the same as train_labels({})".format(
 41                   len(imgs), len(labels))
 42 
 43     index_list = list(range(imgs_length))
 44 
 45     # 读入数据时用到的batchsize
 46     BATCHSIZE = 100
 47 
 48     # 定义数据生成器
 49     def data_generator():
 50         # 训练模式下,打乱训练数据
 51         if mode == 'train':
 52             random.shuffle(index_list)
 53         imgs_list = []
 54         labels_list = []
 55         # 按照索引读取数据
 56         for i in index_list:
 57             # 读取图像和标签,转换其尺寸和类型
 58             img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
 59             label = np.reshape(labels[i], [1]).astype('int64')
 60             imgs_list.append(img) 
 61             labels_list.append(label)
 62             # 如果当前数据缓存达到了batch size,就返回一个批次数据
 63             if len(imgs_list) == BATCHSIZE:
 64                 yield np.array(imgs_list), np.array(labels_list)
 65                 # 清空数据缓存列表
 66                 imgs_list = []
 67                 labels_list = []
 68 
 69         # 如果剩余数据的数目小于BATCHSIZE,
 70         # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
 71         if len(imgs_list) > 0:
 72             yield np.array(imgs_list), np.array(labels_list)
 73 
 74     return data_generator
 75 
 76 
 77 # 定义模型结构
 78 class MNIST(fluid.dygraph.Layer):
 79      def __init__(self, name_scope):
 80          super(MNIST, self).__init__(name_scope)
 81          name_scope = self.full_name()
 82          # 定义卷积层,输出通道20,卷积核大小为5,步长为1,padding为2,使用relu激活函数
 83          self.conv1 = Conv2D(name_scope, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
 84          # 定义池化层,池化核为2,采用最大池化方式
 85          self.pool1 = Pool2D(name_scope, pool_size=2, pool_stride=2, pool_type='max')
 86          # 定义卷积层,输出通道20,卷积核大小为5,步长为1,padding为2,使用relu激活函数
 87          self.conv2 = Conv2D(name_scope, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
 88          # 定义池化层,池化核为2,采用最大池化方式
 89          self.pool2 = Pool2D(name_scope, pool_size=2, pool_stride=2, pool_type='max')
 90          # 定义全连接层,输出节点数为10,激活函数使用softmax
 91          self.fc = FC(name_scope, size=10, act='softmax')
 92          
 93     # 定义网络的前向计算过程
 94      def forward(self, inputs):
 95          x = self.conv1(inputs)
 96          x = self.pool1(x)
 97          x = self.conv2(x)
 98          x = self.pool2(x)
 99          x = self.fc(x)
100          return x

最优的学习率需要多轮调试

在深度学习神经网络模型中,通常使用标准的随机梯度下降算法更新参数,学习率代表参数更新幅度的大小当学习率最优时,模型的有效容量最大。学习率设置和当前深度学习任务有关,合适的学习率往往需要调参经验和大量的实验,总结来说,学习率选取需要注意以下两点:

  • 学习率不是越小越好。学习率越小,损失函数的变化速度越慢,意味着我们需要花费更长的时间进行收敛。
  • 学习率不是越大越好。因为只根据总样本集中的一个批次计算梯度,抽样误差会导致计算出的梯度不是全局最优的方向,且存在波动。同时,在接近最优解时,过大的学习率会导致参数在最优解附近震荡,导致损失难以收敛。

上图左边,学习率合理下降均匀;右边,学习率过大,导致在底部最小点附近震荡。

 1 #仅优化算法的设置有所差别
 2 with fluid.dygraph.guard():
 3     model = MNIST("mnist")
 4     model.train()
 5     #调用加载数据的函数
 6     train_loader = load_data('train')
 7     
 8     #设置不同初始学习率
 9     optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01)    #018
10     #optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001)  #0.23
11     #optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.1)    #0.07
12     
13     EPOCH_NUM = 5
14     for epoch_id in range(EPOCH_NUM):
15         for batch_id, data in enumerate(train_loader()):
16             #准备数据,变得更加简洁
17             image_data, label_data = data
18             image = fluid.dygraph.to_variable(image_data)
19             label = fluid.dygraph.to_variable(label_data)
20             
21             #前向计算的过程
22             predict = model(image)
23             
24             #计算损失,取一个批次样本损失的平均值
25             loss = fluid.layers.cross_entropy(predict, label)
26             avg_loss = fluid.layers.mean(loss)
27             
28             #每训练了100批次的数据,打印下当前Loss的情况
29             if batch_id % 200 == 0:
30                 print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
31             
32             #后向传播,更新参数的过程
33             avg_loss.backward()
34             optimizer.minimize(avg_loss)
35             model.clear_gradients()
36 
37     #保存模型参数
38     fluid.save_dygraph(model.state_dict(), 'mnist')
loading mnist dataset from ./work/mnist.json.gz ......
epoch: 0, batch: 0, loss is: [2.6056936]
epoch: 0, batch: 200, loss is: [0.47590914]
epoch: 0, batch: 400, loss is: [0.23337086]
epoch: 1, batch: 0, loss is: [0.29190642]
epoch: 1, batch: 200, loss is: [0.27118027]
epoch: 1, batch: 400, loss is: [0.1074637]
epoch: 2, batch: 0, loss is: [0.158496]
epoch: 2, batch: 200, loss is: [0.30373794]
epoch: 2, batch: 400, loss is: [0.1660601]
epoch: 3, batch: 0, loss is: [0.15808547]
epoch: 3, batch: 200, loss is: [0.26393268]
epoch: 3, batch: 400, loss is: [0.09973648]
epoch: 4, batch: 0, loss is: [0.08508419]
epoch: 4, batch: 200, loss is: [0.10338296]
epoch: 4, batch: 400, loss is: [0.04516026]

选择适合的优化算法训练模型

学习率是优化器的一个参数,虽然参数更新都是采用梯度下降算法,但是不同的梯度下降算法影响着神经网络的收敛效果。当随机梯度下降算法SGD无法满足我们的需求时,可以尝试如下三个思路选取优化器。

  1. 加入“动量”,参数更新的方向更稳定,比如Momentum优化器。每个批次的数据含有抽样误差,导致梯度更新的方向波动较大。如果我们引入物理动量的概念,给梯度下降的过程加入一定的“惯性”累积,就可以减少更新路径上的震荡!即每次更新的梯度由“历史多次梯度的累积方向”和“当次梯度”加权相加得到。历史多次梯度的累积方向往往是从全局视角更正确的方向,这与“惯性”的物理概念很像,也是为何其起名为“Momentum”的原因。类似不同品牌和材质的篮球有一定的重量差别,街头篮球队中的投手(擅长中远距离投篮)喜欢稍重篮球的比例较高。一个很重要的原因是,重的篮球惯性大,更不容易受到手势的小幅变形或风吹的影响。

  2. 根据不同参数距离最优解的远近,动态调整学习率,比如AdaGrad优化器。通过调整学习率的实验可以发现:当某个参数的现值距离最优解较远时(表现为梯度的绝对值较大),我们期望参数更新的步长大一些,以便更快收敛到最优解。当某个参数的现值距离最优解较近时(表现为梯度的绝对值较小),我们期望参数的更新步长小一些,以便更精细的逼近最优解。类似于打高尔夫球,专业运动员第一杆开球时,通常会大力打一个远球,让球尽量落在洞口附近。当第二杆面对离洞口较近的球时,他会更轻柔而细致的推杆,避免将球打飞。与此类似,参数更新的步长应该随着优化过程逐渐减少,减少的程度与当前梯度的大小有关。根据这个思想编写的优化算法称为“AdaGrad”,Ada是Adaptive的缩写,表示“适应环境而变化”的意思。

  3. 因为上述两个优化思路是正交的,所以可以将两个思路结合起来,这就是当前广泛应用的Adam算法。

 1 #仅优化算法的设置有所差别
 2 with fluid.dygraph.guard():
 3     model = MNIST("mnist")
 4     model.train()
 5     #调用加载数据的函数
 6     train_loader = load_data('train')
 7     
 8     #四种优化算法的设置方案,可以逐一尝试效果
 9     opti_SGD = fluid.optimizer.SGDOptimizer(learning_rate=0.01)   #0.12 
10     #opti_Mom = fluid.optimizer.MomentumOptimizer(learning_rate=0.01, momentum=0.9)
11     #opti_Adag = fluid.optimizer.AdagradOptimizer(learning_rate=0.01)#0.2
12     #opti_Adam = fluid.optimizer.AdamOptimizer(learning_rate=0.01) #0.03
13     x=[]
14     y1=[]
15     y2=[]
16     y3=[]
17     y4=[]
18     iter_num=0
19     
20     EPOCH_NUM = 5
21     for epoch_id in range(EPOCH_NUM):
22         for batch_id, data in enumerate(train_loader()):
23             #准备数据,变得更加简洁
24             image_data, label_data = data
25             image = fluid.dygraph.to_variable(image_data)
26             label = fluid.dygraph.to_variable(label_data)
27             
28             #前向计算的过程
29             predict = model(image)
30             
31             #计算损失,取一个批次样本损失的平均值
32             loss1 = fluid.layers.cross_entropy(predict, label)
33             avg_loss1 = fluid.layers.mean(loss1)
34             #loss2 = fluid.layers.cross_entropy(predict, label)
35             #avg_loss2 = fluid.layers.mean(loss2)
36             #loss3 = fluid.layers.cross_entropy(predict, label)
37             #avg_loss3 = fluid.layers.mean(loss3)
38             #loss4 = fluid.layers.cross_entropy(predict, label)
39             #avg_loss4 = fluid.layers.mean(loss4)
40             
41             #每训练了100批次的数据,打印下当前Loss的情况
42             if batch_id % 200 == 0:
43                 print("epoch: {}, batch: {}, loss is: {},iter: {}".format(epoch_id, batch_id, avg_loss1.numpy(),iter_num))
44                 y1.append(avg_loss1.numpy())
45                 #y2.append(avg_loss2.numpy())
46                 #y3.append(avg_loss3.numpy())
47                 #y4.append(avg_loss4.numpy())
48                 iter_num+=1
49             #y1.append(avg_loss1.numpy())
50             #y2.append(avg_loss2.numpy())
51             #y3.append(avg_loss3.numpy())
52             #y4.append(avg_loss4.numpy())
53             
54             #后向传播,更新参数的过程
55             avg_loss1.backward()
56             #avg_loss2.backward()
57             #avg_loss3.backward()
58             #avg_loss4.backward()
59             opti_SGD.minimize(avg_loss1)
60             #opti_Mom.minimize(avg_loss2)
61             #opti_Adag.minimize(avg_loss3)
62             #opti_Adam.minimize(avg_loss4)
63             model.clear_gradients()
64             
65     x=range(iter_num)
66     import matplotlib.pyplot as plt
67     #plt.title('Compare loss tendency with different Optimizer')
68     '''plt.plot(x,y1,'b',label="SGD")
69     plt.plot(x,y2,'g',label="Mom")
70     plt.plot(x,y3,'r',label="Adag")
71     plt.plot(x,y4,'y',label="Adam")
72     plt.legend()
73     plt.xlabel("Every 2400 Batch")
74     plt.ylabel("Loss")
75     plt.show()'''
76     fig,ax=plt.subplots()
77     #ax.plot(x,y1,label='SGD')
78     #ax.plot(x,y2,label='Mom')
79     #ax.plot(x,y3,label='Adag')
80     #ax.plot(x,y4,label='Adam')
81     ax.legend(loc='upper right',frameon=False)
82     plt.plot(x,y1,label='SGD')
83     #plt.plot(x,y2,label='second')
84     #plt.plot(x,y[:,2])
85     plt.legend(frameon=True,framealpha=1)
86 
87     #保存模型参数
88     fluid.save_dygraph(model.state_dict(), 'mnist')

学习率相同,SGD的测试效果曲线如下(暂时无法同时在一张图上画出四个曲线)。

loading mnist dataset from ./work/mnist.json.gz ......
epoch: 0, batch: 0, loss is: [2.5861013],iter: 0
epoch: 0, batch: 200, loss is: [0.3419642],iter: 1
epoch: 0, batch: 400, loss is: [0.3126796],iter: 2
epoch: 1, batch: 0, loss is: [0.2929134],iter: 3
epoch: 1, batch: 200, loss is: [0.22857675],iter: 4
epoch: 1, batch: 400, loss is: [0.2534462],iter: 5
epoch: 2, batch: 0, loss is: [0.16139439],iter: 6
epoch: 2, batch: 200, loss is: [0.23658308],iter: 7
epoch: 2, batch: 400, loss is: [0.15185605],iter: 8
epoch: 3, batch: 0, loss is: [0.27450126],iter: 9
epoch: 3, batch: 200, loss is: [0.20561613],iter: 10
epoch: 3, batch: 400, loss is: [0.14927392],iter: 11
epoch: 4, batch: 0, loss is: [0.21470158],iter: 12
epoch: 4, batch: 200, loss is: [0.10310929],iter: 13
epoch: 4, batch: 400, loss is: [0.07067939],iter: 14
No handles with labels found to put in legend.
下面就是测试效果:

 1 # 预测100张图片准确率
 2 with fluid.dygraph.guard():
 3     print('start evaluation .......')
 4     datafile = './work/mnist.json.gz'
 5     print('loading mnist dataset from {} ......'.format(datafile))
 6     data = json.load(gzip.open(datafile))
 7     # 读取数据集中的训练集,验证集和测试集
 8     _, _, eval_set = data
 9     # 随机抽取100张测试数据(图片)
10     num_img = 100
11     test_imgs = eval_set[0]
12     test_labs = eval_set[1]
13     index = list(range(len(test_imgs)))
14     random.shuffle(index) # 随机图片排序
15     imgs_list = []
16     labels_list = []
17     # 按照索引读取数据
18     for i in range(num_img):
19         # 读取图像和标签,转换其尺寸和类型
20         img = np.reshape(test_imgs[index[i]], [1, 28, 28]).astype('float32')
21         label = np.array(test_labs[index[i]]).astype('int64')
22         imgs_list.append(img) 
23         labels_list.append(label)
24     test_img = np.array(imgs_list)
25     test_lab = np.array(labels_list)
26     
27     print(f"There are {test_img.shape[0]} eval images in total.")
28     
29     # 加载模型
30     model = MNIST("mnist")
31     model_state_dict, _ = fluid.load_dygraph('mnist')
32     model.load_dict(model_state_dict)
33     model.eval()
34     
35     # 预测图片
36     test_img = fluid.dygraph.to_variable(test_img) # 转化为paddle数据格式
37     results = model(test_img)
38     results_num = np.argmax(results.numpy(), axis=1) # 获取概率最大值的标签
39     correct_cls = (results_num == test_lab)
40     acc = np.sum(correct_cls) / num_img
41     
42     print(f"{num_img}张测试图片测试的准确率是: {acc*100}%。")
start evaluation .......
loading mnist dataset from ./work/mnist.json.gz ......
There are 100 eval images in total.
100张测试图片测试的准确率是: 97.0%。

 

猜你喜欢

转载自www.cnblogs.com/yuzaihuan/p/12304803.html