一个优秀的机器学习实践者需要知道:
实际开发过程中,实践者需要决定:是否收集更多的数据、是否需要增加/降低模型容量、是否需要添加/删除正则化项、是否需要改进模型的优化算法、是否需要改进模型的近似推断....这些都需要大量的时间
在实际应用中,正确使用一个普通算法通常要比草率的使用一个不清楚的算法要效果更好
正确应用一个算法需要掌握一些相当简单的方法论:
确定目标:使用什么样的误差度量(是准确率还是召回率?),并为此误差度量确定目标(我们期望达到什么样的效果:如召回率大于95%?)
尽快建立一个端到端的工作流程,包括计算出合适的性能度量(如:计算出召回率)
搭建系统,并确定性能瓶颈。
根据具体观察反复进行增量式改动:如收集新数据、调整超参数、改进算法
确定目标,即使用什么误差度量是第一步
对大多数应用而言,不可能实现绝对零误差
通常我们需要收集更多的数据。但是我们需要在收集更多数据的成本,与进一步减少误差的价值之间权衡
通常我们需要对错误率定一个 baseline 从而判断预测得好坏
性能度量:表明从哪个角度来度量算法的性能
常见的有:精度precision
,召回率recall
以及 PR curve
,ROC
曲线,F-score
等
还有一种:覆盖率。它是机器学习系统能够产生响应的样本占所有样本的比例
也可以从专业角度考量:点击率、用户满意度调查等等
最重要的是:确定使用哪个性能度量
根据问题的复杂性,项目开始时可能无需使用深度学习
如果问题属于 “AI-完全”类型的,如对象识别、语音识别等,那么项目开始于一个合适的深度学习模型,则效果会比较好
根据数据结构选择一类合适的模型:
刚开始时可以使用某些分段线性单元:如 ReLU
或者其扩展
可以选择具有衰减学习率以及动量的SGD
作为优化算法
常见的衰减方法有:
另一种优化选择是 Adam
算法
batch normalization
对优化性能有着显著的影响,特别是对于卷积网络和具有sigmoid
非线性函数的网络而言
batch normalization
batch normalization
除非训练集包含数千万或者更多的样本,否则项目一开始就应该包含一些温和的正则化
dropout
策略,它也兼容很多模型以及许多正则化项batch normalization
有时可以降低泛化误差,此时可以省略dropout
策略。因为用于 normalize
的统计量估计本身就存在噪音如果我们的任务和另一个被广泛研究的任务相似,那么通过复制之前研究中已知的性能良好的模型和算法,可能会得到很好的效果
ImageNet
上训练好的卷积网络的特征来解决其他计算机视觉任务对于是否使用无监督学习,和特定领域有关
建立第一个端到端的系统后,就可以度量算法的性能并决定如何改进算法
决定是否需要收集更多数据的标准:
首先:确定训练集上的性能可否接受。如果模型在训练集上的性能就很差,那么没必要收集更多的数据。
如果模型在训练集上的性能可以接受,那么我们开始度量测试集上的性能。
如果测试集上的性能也可以接受,则任务完成
如果测试集上的性能比训练集的要差得多,则收集更多的数据时最有效的解决方案之一
此时主要考虑三个要素:
如果增加数据数量代价太大,那么一个替代的方案是降低模型规模,或者改进正则化(调整超参数、或者加入正则化策略)
当决定增加数据数量时,还需要确定收集多少数据。可以绘制曲线来显式训练集规模和泛化误差之间的关系
如果收集更多的数据是不可行的(成本太高无法实现,或者无法改进泛化误差),那么改进泛化误差的唯一方法是:改进学习算法本身
大部分深度学习算法都有许多超参数来控制不同方面的算法表现
有两种选择超参数的方法:
手动设置超参数:
我们必须了解超参数、训练误差、泛化误差、计算资源(内存和运行时间) 之间的关系。这要求我们切实了解一个学习算法有效容量的基本概念
手动搜索超参数的任务是:在给定运行时间和内存预算范围的条件下,最小化泛化误差
手动搜索超参数的主要目标是:调整模型的有效容量以匹配任务的复杂性
模型的有效容量受限于三个因素:
模型的表示容量并不是越高越好。如果无法找到合适的代价函数来最小化训练代价、或者正则化项排除了某些合适的函数,那么即使模型的表达能力再强,也无法学习出合适的函数。
如果以超参数为自变量,泛化误差为因变量。那么会在的曲线通常会表现为 U 形
在某个极端情况下,超参数对应着低容量(并不是超参数越小,模型容量越低;也可能是相反的情况)。此时泛化误差由于训练误差较大而很高。这就是欠拟合
在另一个极端情况下,超参数对应着高容量,此时泛化误差也很大。这就是过拟合
最优的模型容量位于曲线中间的某个位置
对于某些超参数,当超参数值太大时,会发生过拟合。如中间层隐单元的数量,数量越大,模型容量越高,也更容易发生过拟合。
对于某些超参数,当超参数值太小时,也会发生过拟合。如 正则化的权重系数,系数为0,表示没有正则化,此时很容易过拟合。
并不是每个超参数都对应着完整的 U 形曲线
maxout
单元中线性片段的数目学习率可能是最重要的超参数
如果你只有时间来调整一个超参数,那么就调整学习率
相比其他超参数,学习率以一种更复杂的方式控制模型的有效容量:
学习率关于训练误差具有 U 形曲线。泛化误差也是类似的 U 形曲线,但是正则化作用在学习率过大或者过小处比较复杂
调整学习率以外的其他参数时,需要同时监测训练误差和测试误差,从而判断模型是否过拟合或者欠拟合,然后适当调整其容量
如果训练集错误率大于目标错误率(这个根据任务背景人工指定),那么只能增加模型容量以改进模型。但是这增加了模型的计算代价
如果测试集错误率大于目标错误率,则有两个方法:
通常最佳性能来自于正则化很好的大规模模型,如使用Dropout
的神经网络
大部分超参数可以推论出是否增加或者减少模型容量,部分示例如下:
超参数 | 容量何时增加 | 原因 | 注意事项 |
---|---|---|---|
隐单元数量 | 增加 | 增加隐单元数量会增加模型的表示能力 | 几乎模型每个操作所需要的时间和内存代价都会随隐单元数量的增加而增加 |
学习率 | 调至最优 | 不正确的学习率,不管是太高还是太低都会由于优化失败而导致低的有效容量的模型 | |
卷积核宽度 | 增加 | 增加卷积核宽度会增加模型的参数数量 | 较宽的卷积核导致较窄的输出尺寸,除非使用隐式零填充来减少此影响,否则会降低模型容量。较宽的卷积核需要更多的内存来存储参数,并增加运行时间 |
隐式零填充 | 增加 | 在卷积之前隐式添加零能保持较大尺寸的表示 | 大多数操作的时间和内存代价会增加 |
权重衰减系数 | 降低 | 降低权重衰减系数使得模型参数可以自由地变大 | |
dropout 比率 | 降低 | 较少地丢弃单元可能更多的让单元彼此“协力”来适应训练集 |
手动调整超参数时不要忘记最终目标:提升测试集性能
理想的学习算法应该是只需要输入一个数据集,然后就可以输出学习的函数而不需要人工干预调整超参数
原则上可以开发出封装了学习算法的超参数优化算法,并自动选择其超参数
当只有三个或者更少的超参数时,常见的超参数搜索方法是:网格搜索
如何确定搜索集合的范围?
通常网格搜索会在对数尺度下挑选合适的值
通常重复进行网格搜索时,效果会更好。假设在集合 {-1,0,1}
上网格搜索超参数
{1,2,3}
上搜索{-0.1,0,0.1}
上搜索网格搜索的一个明显问题时:计算代价随着超参数数量呈指数级增长。
随机搜索是一种可以替代网格搜索的方法,它编程简单、使用方便、能更快收敛到超参数的良好取值:
随机搜索的优点:
与网格搜索一样,我们通常会基于前一次运行结果来重复运行下一个版本的随机搜索
随机搜索比网格搜索更快的找到良好超参数的原因是:没有浪费的实验
在网格搜索中,两次实验之间只会改变一个超参数 (假设为 )的值,而其他超参数的值保持不变。如果这个超参数 的值对于验证集误差没有明显区别,那么网格搜索相当于进行了两个重复的实验
在随机搜索中,两次实验之间,所有的超参数值都不会相等(因为每个超参数的值都是从它们的分布函数中随机采样而来)。因此不大可能会出现两个重复的实验
如果 超参数与泛化误差无关,那么不同的 值:
超参数搜索问题可以转化为一个优化问题:
实际上,大多数问题中,超参数的梯度是不可用的
为了弥补超参数梯度的缺失,我们可以使用贝叶斯回归模型来估计每个超参数的验证集误差的期望和该期望的不确定性(贝叶斯回归模型不需要使用梯度)
大部分超参数优化算法比随机搜索更复杂,并且具有一个共同的缺点:在获取任何有效的超参数信息之前,你必须完整运行整个训练过程
当一个机器学习系统效果不好时,很难判断效果不好的原因是算法本身,还是算法实现错误。由于各种原因,机器学习系统很难调试
大多数情况下,我们不能提前知道算法的行为
大部分机器学习模型有多个自适应的部分
大部分神经网络的调试策略都是解决上述两个难点的一个或者两个
UnitTest
),独立检查神经网络实现的各个部分一些重要的调试方法如下:
可视化计算中模型的行为:直接观察机器学习模型运行机器任务,有助于确定其达到的量化性能数据是否看上去合理
可视化那些最严重的错误:大多数模型能够输出运行任务时的某种置信度(如基于softmax
函数输出层的分类器给每个类分配了一个概率)。如果某个样本被错误的分类,而且其分类置信度很低,那么可以检查这些样本的问题
根据训练和测试误差检测软件:通常我们很难确定底层软件是否正确实现,而测试和训练误差提供了一些线索
监控激活函数值和梯度的直方图:可视化激活函数值和梯度的统计量往往是有用的。
许多深度学习算法为每一步的结果产生了某种保证。其中包括:
目标函数值确保在算法的迭代步中不会增加
某些变量的导数在算法的每一步中都是 0
所有变量的梯度在收敛时,会变为 0
由于计算机存储浮点数的舍入误差,这些条件可能会有误差
如果违反了这些保证,那么一定是软件错误
比较反向传播导数和数值导数:如果我们正在使用一个软件框架或者库,那么必须定义 bprop
方法,常见的错误原因是未能正确的实现梯度表达。验证该错误的一个方法是:比较实现的自动求导和通过有限差分计算的导数
或者采用中心差分法:
其中 必须足够大,从而确保不会由于有限精度问题产生舍入误差
如果梯度是雅克比矩阵(输出为向量,输入也是向量),则我们可以多次使用有限差分法来评估所有的偏导数
通常推荐使用中心差分法,因为根据泰勒展开,有:
使用中心差分法的误差更小。
理论上进行梯度检查很简单,就是把解析的梯度和数值计算的梯度进行比较。但是实际操作过程中,这个过程复杂且容易出错。
使用中心化公式来计算数值梯度。
通常我们计算数值梯度时,采用:
其中 为一个很小的数字,在实践过程中近似为 。但是实践中证明,使用中心化公式效果更好:
使用相对误差来比较。对于数值梯度 和解析梯度 ,通常都是比较其相对误差而不是绝对误差来判断数值计算得到的梯度是否正确:
tanh/softmax
的目标函数,还是有点高使用双精度类型。当使用单精度浮点数来进行梯度检查时,即使梯度计算过程正确,相对误差也会很高(如 )
梯度检查时,一个导致不准确的原因是:不可导点。
不可导点是目标函数不可导的部分,由于 relu 等函数的引入导致。
解决方法是:使用更少的数据点。
谨慎设置步长
建议让网络预热一小段时间,等到损失函数开始下降之后在进行梯度检查
不要让正则化吞没数据
关闭 dropout 和数据 augmentation
检查少量的维度。由于梯度可以有上百万的参数,此时只能检查其中一些维度然后假设其他维度是正确的
只有在调试过程中进行梯度检验。不要在训练过程中执行梯度检验,因为非常耗计算资源。
拟合极小的数据集:
当训练集上有很大的误差时,我们需要确定问题是欠拟合,还是软件错误。尝试对一个小数据子集进行训练,然后确保能达到 0 的损失值。
在训练神经网络时,应该跟踪多个重要数值
这些数值输出的图表是观察训练进程的一个窗口,是直观理解不同超参数设置效果的工具
通常 轴都是以周期 epoch 为单位,它衡量了训练中每个样本数据都被观察过次数的期望
第一个要跟踪的数值就是损失函数
从左图中可见:
从右图可见:
在训练分类器时,第二个要跟踪的就是验证集和训练集的准确率。从该图标可以获知模型过拟合的程度
另一个应该跟踪的数值是:权重更新的相对值
假设权重为 ,更新的增量为 。 为学习率。那么权重更新的相对值为:
一个不正确的初始化可能让学习过程变慢,甚至停止。这个问题可以比较简单的诊断出来:
如果数据是图像像素数据,那么把第一层特征可视化会有帮助:
项目开始于性能度量的选择以及这些度量的期望。
选择基准系统:对于视觉任务而言,基准系统是带有 ReLU
单元的卷积网络
常见数据预处理方式:(红色的线指出各维度的数值范围)
均值减法:对数据中每个独立特征减去均值,集合上理解为:在每个维度上,都将数据云的中心迁移到原点
归一化:将所有维度都做归一化:
PCA 降维:取得数据的主成分,可以对数据去除相关性
白化whitening
:先对数据进行旋转(旋转的矩阵就是 SVD 分解中的 U矩阵),然后对每个维度除以特征值(为防止分母为0,通常加一个很小的值作为平滑系数)来对数值范围进行归一化
实际在神经网络中,并不会采用 PCA 和白化。
任何预处理策略都只能在训练集的数据上进行,然后再应用到验证集或测试集上。
激活函数:当前推荐使用 ReLU 激活函数
Batch Normalization:让数据在通过激活函数之前,添加一个 batch normalization
权重一定不能全零初始化。因为这会导致神经元在前向传播中计算出同样的输出,然后在反向传播中计算出同样的梯度,从而进行同样的权重更新。这就产生了大量对称性神经元。
通常采用小随机数初始化,通过这样来打破对称性。至于使用高斯分布还是均匀分布,对结果影响很小。
之所以用小的随机数,是因为:如果网络中存在tanh
或者 sigmoid
激活函数,或者网络的输出层为sigmoid
等单元,则它们的变量值必须很小。
如果使用较大的随机数,则很有可能这些单元会饱和,使得梯度趋近于零。这意味着基于梯度下降的算法推进的很慢,从而学习很慢。
如果网络中不存在sigmoid/tanh
等激活函数,网络的输出层也不是sigmoid
等单元,则可以使用较大的随机数初始化。
通常使用 来校准权重初始化标准差。随着输入数据的增长,随机初始化的神经元的输出数据的分布中的方差也在增大。
假设权重 和输入 之间的内积为 (不考虑非线性激活函数)。我们检查 的方差
其中假设输入和权重的平均值都是0,因此有 。同时假设所有的 服从同样的分布,假设所有的 服从同样的分布
Glorot
的论文中,他推荐初始化公式为: 。其中 为输入和输出的数量。对于任何给定的问题,很难提前去预测到底需要多深的神经网络。
一个常规的做法是:首先尝试使用逻辑回归(可以视为没有隐层的神经网络)。然后尝试单隐层的神经网络、双隐层的神经网络....
这种做法将隐层的数量看做是一个超参数。通过交叉验证来确定该超参数的值。