内容都是百度AIstudio的内容,我只是在这里做个笔记,不是原创。
学习率是优化器的一个参数,虽然参数更新都是采用梯度下降算法,但是不同的梯度下降算法影响着神经网络的收敛效果。当随机梯度下降算法SGD无法满足我们的需求时,可以尝试如下三个思路选取优化器。
-
加入“动量”,参数更新的方向更稳定,比如Momentum优化器。每个批次的数据含有抽样误差,导致梯度更新的方向波动较大。如果我们引入物理动量的概念,给梯度下降的过程加入一定的“惯性”累积,就可以减少更新路径上的震荡!即每次更新的梯度由“历史多次梯度的累积方向”和“当次梯度”加权相加得到。历史多次梯度的累积方向往往是从全局视角更正确的方向,这与“惯性”的物理概念很像,也是为何其起名为“Momentum”的原因。类似不同品牌和材质的篮球有一定的重量差别,街头篮球队中的投手(擅长中远距离投篮)喜欢稍重篮球的比例较高。一个很重要的原因是,重的篮球惯性大,更不容易受到手势的小幅变形或风吹的影响。
-
根据不同参数距离最优解的远近,动态调整学习率,比如AdaGrad优化器。通过调整学习率的实验可以发现:当某个参数的现值距离最优解较远时(表现为梯度的绝对值较大),我们期望参数更新的步长大一些,以便更快收敛到最优解。当某个参数的现值距离最优解较近时(表现为梯度的绝对值较小),我们期望参数的更新步长小一些,以便更精细的逼近最优解。类似于打高尔夫球,专业运动员第一杆开球时,通常会大力打一个远球,让球尽量落在洞口附近。当第二杆面对离洞口较近的球时,他会更轻柔而细致的推杆,避免将球打飞。与此类似,参数更新的步长应该随着优化过程逐渐减少,减少的程度与当前梯度的大小有关。根据这个思想编写的优化算法称为“AdaGrad”,Ada是Adaptive的缩写,表示“适应环境而变化”的意思。
-
因为上述两个优化思路是正交的,所以可以将两个思路结合起来,这就是当前广泛应用的Adam算法。
# 加载相关库
import os
import random
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D, Pool2D, FC
import numpy as np
from PIL import Imageimport gzip
import json# 定义数据集读取器
def load_data(mode='train'):# 读取数据文件datafile = './work/mnist.json.gz'print('loading mnist dataset from {} ......'.format(datafile))data = json.load(gzip.open(datafile))# 读取数据集中的训练集,验证集和测试集train_set, val_set, eval_set = data# 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLSIMG_ROWS = 28IMG_COLS = 28# 根据输入mode参数决定使用训练集,验证集还是测试if mode == 'train':imgs = train_set[0]labels = train_set[1]elif mode == 'valid':imgs = val_set[0]labels = val_set[1]elif mode == 'eval':imgs = eval_set[0]labels = eval_set[1]# 获得所有图像的数量imgs_length = len(imgs)# 验证图像数量和标签数量是否一致assert len(imgs) == len(labels), \"length of train_imgs({}) should be the same as train_labels({})".format(len(imgs), len(labels))index_list = list(range(imgs_length))# 读入数据时用到的batchsizeBATCHSIZE = 100# 定义数据生成器def data_generator():# 训练模式下,打乱训练数据if mode == 'train':random.shuffle(index_list)imgs_list = []labels_list = []# 按照索引读取数据for i in index_list:# 读取图像和标签,转换其尺寸和类型img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')label = np.reshape(labels[i], [1]).astype('int64')imgs_list.append(img) labels_list.append(label)# 如果当前数据缓存达到了batch size,就返回一个批次数据if len(imgs_list) == BATCHSIZE:yield np.array(imgs_list), np.array(labels_list)# 清空数据缓存列表imgs_list = []labels_list = []# 如果剩余数据的数目小于BATCHSIZE,# 则剩余数据一起构成一个大小为len(imgs_list)的mini-batchif len(imgs_list) > 0:yield np.array(imgs_list), np.array(labels_list)return data_generator# 定义模型结构
class MNIST(fluid.dygraph.Layer):def __init__(self, name_scope):super(MNIST, self).__init__(name_scope)name_scope = self.full_name()# 定义卷积层,输出通道20,卷积核大小为5,步长为1,padding为2,使用relu激活函数self.conv1 = Conv2D(name_scope, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')# 定义池化层,池化核为2,采用最大池化方式self.pool1 = Pool2D(name_scope, pool_size=2, pool_stride=2, pool_type='max')# 定义卷积层,输出通道20,卷积核大小为5,步长为1,padding为2,使用relu激活函数self.conv2 = Conv2D(name_scope, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')# 定义池化层,池化核为2,采用最大池化方式self.pool2 = Pool2D(name_scope, pool_size=2, pool_stride=2, pool_type='max')# 定义全连接层,输出节点数为10,激活函数使用softmaxself.fc = FC(name_scope, size=10, act='softmax')# 定义网络的前向计算过程def forward(self, inputs):x = self.conv1(inputs)x = self.pool1(x)x = self.conv2(x)x = self.pool2(x)x = self.fc(x)return x#仅优化算法的设置有所差别
with fluid.dygraph.guard():model = MNIST("mnist")model.train()#调用加载数据的函数train_loader = load_data('train')# optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01)optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=0.01,momentum=0.9)# optimizer = fluid.optimizer.AdagradOptimizer(learning_rate=0.01)optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.01)EPOCH_NUM = 5for epoch_id in range(EPOCH_NUM):for batch_id, data in enumerate(train_loader()):#准备数据,变得更加简洁image_data, label_data = dataimage = fluid.dygraph.to_variable(image_data)label = fluid.dygraph.to_variable(label_data)#前向计算的过程predict = model(image)#计算损失,取一个批次样本损失的平均值loss = fluid.layers.cross_entropy(predict, label)avg_loss = fluid.layers.mean(loss)#每训练了100批次的数据,打印下当前Loss的情况if batch_id % 200 == 0:print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))#后向传播,更新参数的过程avg_loss.backward()optimizer.minimize(avg_loss)model.clear_gradients()#保存模型参数fluid.save_dygraph(model.state_dict(), 'mnist')
with fluid.dygraph.guard():print('start evaluation .......')#加载模型参数model = MNIST("mnist")model_state_dict, _ = fluid.load_dygraph('mnist')model.load_dict(model_state_dict)model.eval()eval_loader = load_data('eval')acc_set = []avg_loss_set = []cnt=0sum=0for batch_id, data in enumerate(eval_loader()):x_data, y_data = dataimg = fluid.dygraph.to_variable(x_data)label = fluid.dygraph.to_variable(y_data)prediction= model(img)# prediction=prediction.numpy().astype('int32')label=label.numpy().astype('int64')for i in range(len(label)):# print(prediction[i],label[i])tmp=np.argsort(prediction[i].numpy())if(tmp[-1]==label[i]):# print('aaaa:',tmp[-1],label[i])cnt+=1sum+=len(label)# print(len(prediction))# print("hello:",prediction.numpy().astype('int32'),label.numpy().astype('int32'))# loss = fluid.layers.square_error_cost(input=prediction, label=label)# avg_loss = fluid.layers.mean(loss)# acc_set.append(float(acc.numpy()))# avg_loss_set.append(float(avg_loss.numpy()))#计算多个batch的平均损失和准确率# acc_val_mean = np.array(acc_set).mean()# avg_loss_val_mean = np.array(avg_loss_set).mean()print("acc:",cnt/sum)# print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))
# 读取一张本地的样例图片,转变成模型输入的格式
def load_image(img_path):# 从img_path中读取图像,并转为灰度图im = Image.open(img_path).convert('L')im.show()im = im.resize((28, 28), Image.ANTIALIAS)im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)# 图像归一化im = 1.0 - im / 127.5return im# 定义预测过程
with fluid.dygraph.guard():model = MNIST("mnist")params_file_path = 'mnist'# img_path = './work/example_1.png'img_p= './work/example_'# 加载模型参数cnt=0model_dict, _ = fluid.load_dygraph("mnist")model.load_dict(model_dict)model.eval()for i in range(10):img_path=img_p+str(i)+'.png'tensor_img = load_image(img_path)#模型反馈10个分类标签的对应概率results = model(fluid.dygraph.to_variable(tensor_img))#取概率最大的标签作为预测输出lab = np.argsort(results.numpy())# print(lab)print("本次预测的数字是: ", lab[0][-1],i)if(lab[0][-1]==i):cnt+=1print(cnt/10)