机器学习(神经网络)调节超参数的方法

蝶无需花恋 12天前   阅读数 14 0

背景

最近做 李飞飞 CS231N 2019 的作业时遇到了问题,具体是assignment1中的features.ipynb中的Neural Network on image features小节,训练一个 用于图片分类的两层神经网络 (结构不重要所以不再展示),要求测试集准确率达到55%(作业提示最高可达60%)。

之前要求训练模型的作业都会预先给出 学习率 α正则化强度 λ 的几个参考值或范围,但这次没有给。我刚开始因为设置了错误的超参数范围,模型的验证集准确率一直在10%左右(该模型的任务是分辨图片属于10个给定类别中的哪一个,10%相当随机选择,已经相当差了),代价曲线也没看出问题,直到参考了其他人的代码才知道 α 和 λ 的范围错了,于是我就开始思考 如何才能快速确定超参数的合适范围?

正文

模型的结构、功能和数据集不同,会导致 合适的超参数范围相差很大 (几个数量级),如果只是 随机尝试或者凭感觉走 ,效率会很低。可视化工具 + 清晰的思路 对于简单和复杂模型的调试都很重要。

用可视化辅助调参

  • 方法一:之前在调超参数时,总是简单地用 print函数 打印出每个模型的验证集准确率和它的超参数(α、λ等)的值,重复的文字太多,很不直观

Alt

lr 和 reg 为取 log 后的值

  • 方法二:使用 print 函数格式化输出,以 表格 的形式输出。虽然理论上完全可以,但还是有缺点:不够直观、单独编程比较麻烦
    在这里插入图片描述
    excel 示意图
  • 方法三(最佳方案) 使用 像素 表示单元格中的数据,使用 plt.scatter函数 绘制 散点图 (此处参考了assignment1/svm.ipynb中的用法)并用 plt.annotate函数 在图中 标记出准确率最高的点 ,效果如下:
    在这里插入图片描述
    散点图

    每个点代表一个模型的验证集准确率,横、纵坐标分别是其对应的学习率 α 、正则化强度 λ 。右边的 颜色条 显示了准确率大小与点的颜色的对应关系,可以看出准确率越高,点的颜色越红。黄色标签所指的点代表的就是目前的 最佳模型 ,标签显示了它的 lr、reg和准确率。

python代码实现如下,其中results是一个字典,索引是 lr 和 reg ,键值是训练集和验证集的准确率;best_point存储了最优模型的 lr、 reg坐标

# Visualize validation accuracies
# results[(lr, reg)] = (train_acc, val_acc)
# best_point = (math.log10(lr), math.log10(reg)) # for labeling
x_scatter = [math.log10(x[0]) for x in results]
y_scatter = [math.log10(x[1]) for x in results]
marker_size = 100  # default: 20
colors = [results[x][1] for x in results]  # depend color on val_acc
plt.figure()

plt.scatter(x_scatter, y_scatter, marker_size, c=colors, cmap=plt.cm.coolwarm)
plt.annotate('(%.2f,%.2f,%.2f%%)'% (best_point[0],best_point[1],best_acc*100), 
             xy=best_point, xytext=(-30, 30), textcoords='offset pixels',
             bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
             arrowprops=dict(arrowstyle = '->', connectionstyle='arc3,rad=0'))
plt.colorbar()
plt.xlabel('10^lr')
plt.ylabel('10^reg')
plt.title('CIFAR-10 validation accuracy')
plt.show()

关于用法:plt.scatter 中可以通过改变颜色样式和点的大小等等, plt.annotate中xytext、textcoords决定标签的位置、bbox决定边框、arrowprops决定箭头等等,具体的用法自行查阅即可。

调试过程

1. “浅迭代”&“广撒网”

  • “浅迭代”
    为了加快速度,一开始尽量将迭代次数设置的少一点,等找到合适的超参数范围之后再增加迭代次数来 “ 冲刺 ” 到更高的准确率,这里我设置为300(即每个模型执行300次梯度下降)

  • “广撒网”
    由于刚开始不知道 α 和 λ 的大概范围,因此 范围要足够大 ,这里在 [ 1 0 10 10^{-10} , 1 0 10 10^{10} ] 之间取出 5 个数(建议奇数,为了速度先取少一点)组成 等比数列 作为 α 和 λ 的候选值

lr_range = np.logspace(-10, 10, num=5)
reg_range = np.logspace(-10, 10, num=5)

运行结果就是上一张图,这里再贴一下:
在这里插入图片描述

第1次运行

图中只有两个红色的点还可以,其余的都很低,最高验证集准确率为 54.40%(目标是测试集达到55%甚至60%)

2. 缩小范围&增加候选点密度

上图中,从 x轴 (代表学习率 α ,程序中的 lr )方向上看: 红点 的分布范围很窄,因此考虑将 x 轴范围缩小到 [ 1 0 5 10^{-5} , 1 0 5 10^{5} ] ;从 y 轴(正则化强度 λ ,程序中的 reg )方向看:红点的分布出现 触底 ,可以在收缩上限的同时扩展下限,即将 y 轴改为 [ 1 0 12 10^{-12} , 1 0 0 10^{0} ]

lr_range = np.logspace(-5, 5, num=5)
reg_range = np.logspace(-12, 0, num=5)

运行结果:
在这里插入图片描述

第2次运行

可以看到红点增加了,此时最高准确率为 54.00%
再次缩小 lr 范围至** [ 1 0 3 10^{-3} , 1 0 2 10^{2} ]**,将 reg 改为 [ 1 0 12 10^{-12} , 1 0 0 10^{0} ],考虑到红点的分布还是很窄,使用 增加候选点密度 的方法:将 reg (y轴)的候选点个数加到 7 个:

lr_range = np.logspace(-3, 2, num=5)
reg_range = np.logspace(-12, 0, num=7)

运行结果:
在这里插入图片描述

第3次运行

虽然最高准确率反而下降了2%, 但并没有关系。现在的目的只是为了确定一个合适的范围,迭代次数比较少,所以会出现较大的波动。

接下来,按照上述思路交替使用 缩小范围增加候选点 的方法,使得红色和蓝色各自代表的准确率的差值尽可能小,此时说明找到了合适的超参数范围,如图:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

多次调整

最终调整好的范围如下图,此时最高验证集准确率为 55.40%,可以看出最好(55%)和最差(44%)之间的差距已经缩小至 11% ,说明已经找到了比较合适的超参数范围。
在这里插入图片描述

3. “深入迭代”

找到超参数合适的取值范围之后,将迭代次数从 300 增加到 2000 ,此时最高验证集准确率达到了 60.60% ,并且最好的和最差的之间的差距仍然不大:
在这里插入图片描述

最终结果

最后在测试集上运行最佳模型,达到了 58.70% 的准确率,虽然离60%还差点,不过已经达到了55%的要求。如果想追求更高的准确率,也可以继续尝试增加点密度、迭代次数,当然运算时间会更长,还可以选择其他梯度下降算法。
在这里插入图片描述
测试集表现

写在最后

  • 这次调试的过程让我对 梯度下降 过程有了 更直观的理解——如果把 梯度下降类比成下山 的过程(当然真实情况是比三维复杂的多的高维空间),我们的目的就是到达整片山岭中 海拔最低 的位置。散点图中每个点都是一个 山谷(局部最优解),训练的目的就是 在这些山谷中找出最深的那个 。迭代次数300时的最优解不一定是1500时的最优解,这是因为迭代次数相当于朝着山下走的步数 ,在没有到达山谷的底部之前我们并不知道山谷有多深

  • 虽然本文针对的是简单的两层神经网络,并且散点图有一些缺点(例如因为是二维图像,只能同时调节两个参数),但我相信 可视化工具清晰的思路 这两个核心思想对于层数更多、结构更复杂、参数更多、训练时间更长的 深层神经网络 来说也将会是很重要的。

  • 最后,这是我的第一篇博客,处女座的处女作。欢迎评论、转载和收藏,希望能帮到更多的人!

发布了1 篇原创文章 · 获赞 2 · 访问量 181

注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: