您的位置:首页 > 运维架构 > 网站架构

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

2020-08-15 17:57 906 查看


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

  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, acc
else:
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 = data
img = 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 LogWriter
log_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/),将该网址输入浏览器地址栏刷新页面的效果如下图所示。除了右侧对数据点的作图外,左侧还有一个控制板,可以调整诸多作图的细节。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐