AI智能
改变未来

飞桨百度架构师手把手带你零基础实践深度学习——手写数字识别训练调试与优化


其实对于这个模型,我们还有很多优化的方向。
有一些关键问题:

  1. 计算分类准确率,观测模型训练效果
    交叉熵损失函数只能作为优化目标,无法直接准确衡量模型的训练效果。准确率可以直接衡量训练效果,但由于其离散性质,不适合做为损失函数优化神经网络。)

  2. 检查模型训练过程,识别潜在问题
    (如果模型的损失或者评估指标表现异常,通常需要打印模型每一层的输入和输出来定位问题,分析每一层的内容来获取错误的原因。)

  3. 加入校验或测试,更好评价模型效果
    (理想的模型训练结果是在训练集和验证集上均有较高的准确率,如果训练集上的准确率高于验证集,说明网络训练程度不够;如果验证集的准确率高于训练集,可能是发生了过拟合现象。通过在优化目标中加入正则化项的办法,解决过拟合的问题。)

  4. 加入正则化项,避免模型过拟合
    (飞桨框架支持为整体参数加入正则化项,这是通常的做法。此外,飞桨框架也支持为某一层或某一部分的网络单独加入正则化项,以达到精细调整参数训练的效果。若测试集表现不好)

  5. 可视化分析
    (用户不仅可以通过打印或使用matplotlib库作图,飞桨还提供了更专业的可视化分析工具VisualDL,提供便捷的可视化分析方法。)

评估分类分准率

分类准确率accuracy:测量直观,但是不适合作为loss,公平比较两种损失函数的优劣。
实现方案:
在forward函数中加入acc计算并返回结果。
训练过程中取得该批次样本的acc
打印acc

if label is not None:acc = fluid.layers.accuracy(input=x, label=label)return x, accelse:return x······#前向计算的过程,同时拿到模型输出值和分类准确率predict, acc = model(image, label)

检查模型训练过程

若模型报错或loss不下降。
实现方案:
在forward函数中,打印模型每一层的参数和输出
打印尺寸和内容值。
通过尺寸验证网络结构是否正确,通过内容值验证数据分布是否合理。
在合适时机打印即可

# 选择是否打印神经网络每层的参数尺寸和输出尺寸,验证网络结构是否设置正确if check_shape:# 打印每层网络设置的超参数-卷积核尺寸,卷积步长,卷积padding,池化核尺寸print(\"\\n########## print network layer\'s superparams ##############\")print(\"conv1-- kernel_size:{}, padding:{}, stride:{}\".format(self.conv1.weight.shape, self.conv1._padding, self.conv1._stride))print(\"conv2-- kernel_size:{}, padding:{}, stride:{}\".format(self.conv2.weight.shape, self.conv2._padding, self.conv2._stride))print(\"pool1-- pool_type:{}, pool_size:{}, pool_stride:{}\".format(self.pool1._pool_type, self.pool1._pool_size, self.pool1._pool_stride))print(\"pool2-- pool_type:{}, poo2_size:{}, pool_stride:{}\".format(self.pool2._pool_type, self.pool2._pool_size, self.pool2._pool_stride))print(\"fc-- weight_size:{}, bias_size_{}, activation:{}\".format(self.fc.weight.shape, self.fc.bias.shape, self.fc._act))# 打印每层的输出尺寸print(\"\\n########## print shape of features of every layer ###############\")print(\"inputs_shape: {}\".format(inputs.shape))print(\"outputs1_shape: {}\".format(outputs1.shape))print(\"outputs2_shape: {}\".format(outputs2.shape))print(\"outputs3_shape: {}\".format(outputs3.shape))print(\"outputs4_shape: {}\".format(outputs4.shape))print(\"outputs5_shape: {}\".format(outputs5.shape))# 选择是否打印训练过程中的参数和输出内容,可用于训练过程中的调试if check_content:# 打印卷积层的参数-卷积核权重,权重参数较多,此处只打印部分参数print(\"\\n########## print convolution layer\'s kernel ###############\")print(\"conv1 params -- kernel weights:\", self.conv1.weight[0][0])print(\"conv2 params -- kernel weights:\", self.conv2.weight[0][0])# 创建随机数,随机打印某一个通道的输出值idx1 = np.random.randint(0, outputs1.shape[1])idx2 = np.random.randint(0, outputs3.shape[1])# 打印卷积-池化后的结果,仅打印batch中第一个图像对应的特征print(\"\\nThe {}th channel of conv1 layer: \".format(idx1), outputs1[0][idx1])print(\"The {}th channel of conv2 layer: \".format(idx2), outputs3[0][idx2])print(\"The output of last layer:\", outputs5[0], \'\\n\')

若打印的参数一直不变,说明参数没有加入梯度下降模型。

加入校验或测试,评价模型效果

校验:未参与训练,决策模型超参数
测试:未参与训练和校验,评估模型效果
实现方案:
加载参数,eval状态
读取校验的样本集
根据模型预测计算评估指标,注意不同批次评估结果取平均。
一定要先保存

with fluid.dygraph.guard():print(\'start evaluation .......\')#加载模型参数model = 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 = []for 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, acc = model(img, label)loss = fluid.layers.cross_entropy(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(\'loss={}, acc={}\'.format(avg_loss_val_mean, acc_val_mean))

差别不大只不过数据不用训练数据,用校验的数据

过拟合的原理和解决方案

过拟合:模型在训练集数据表现良好,测试集表现差(泛化能力差)原因:模型过于复杂,或学习能力敏感,数据较少,或噪音多。
一般测试误差先降低后上升。
回归问题模型
图1 过拟合 图3 欠拟合

分类问题模型

过拟合成因

  • 训练数据存在噪音->数据清洗和修正
  • 强大模型(表示空间大)+训练数据少=在训练数据上表现良好的候选假设太多
    可以限制模型表示能力or更多训练数据

加入正则化项

为了防止过拟合,我们一般限制模型表示能力。给参数加入反胃。

正则化项

  • 防止模型过度拟合
  • 优化目标中加入正则化项,惩罚参数
  • 模型在参数大小和训练集loss间取得平衡->在预测时效果最好

实现方案:

在优化目标中整体加入;
对某一层的参数加入。

#各种优化算法均可以加入正则化项,避免过拟合,参数regularization_coeff调节正则化项的权重#optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, regularization=fluid.regularizer.L2Decay(regularization_coeff=0.1),parameter_list=model.parameters()))optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.01, regularization=fluid.regularizer.L2Decay(regularization_coeff=0.1),parameter_list=model.parameters())

*regularization_coeff为权重系数,参数和loss谁更小的权重

可视化

使用PLT库制作变化曲线

Matplotlib库
2D
实现方案:

  1. 引入PLT库
  2. 批次编号作为x轴,记录在列表iters
  3. 该批次的训练损失作为y轴,记录在loses
  4. 训练后将数据以参数灌入plt.plot
#画出训练过程中Loss的变化曲线plt.figure()plt.title(\"train loss\", fontsize=24)plt.xlabel(\"iter\", fontsize=14)plt.ylabel(\"loss\", fontsize=14)plt.plot(iters, losses,color=\'red\',label=\'train loss\')plt.grid()plt.show()

Visual DL分析工具

  1. 创建LogWriter对象,设置数据存放路径
from visualdl import LogWriterlog_writer = LogWriter(\"./log\")
  1. 训练过程中插入作图语句
#每训练一百批次的数据,打印loss情况if batch_id % 100 == 0:print(\"epoch: {}, batch: {}, loss is: {}, acc is {}\".format(epoch_id, batch_id, avg_loss.numpy(), avg_acc.numpy()))log_writer.add_scalar(tag = \'acc\', step = iter, value = avg_acc.numpy())log_writer.add_scalar(tag = \'loss\', step = iter, value = avg_loss.numpy())iter = iter + 100
  1. 命令行输入 $visualdl –logdir ./log
    $ visualdl –logdir ./log –port 8080
  2. 打开浏览器,查看作图结果。

查阅的网址在第三步的启动命令后会打印出来(如http://127.0.0.1:8080/),将该网址输入浏览器地址栏刷新页面的效果如下图所示。除了右侧对数据点的作图外,左侧还有一个控制板,可以调整诸多作图的细节。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 飞桨百度架构师手把手带你零基础实践深度学习——手写数字识别训练调试与优化