章11 实践方法论
一个优秀的机器学习实践者需要知道:
- 存在哪些算法以及这些算法为何有效的原理
- 如何针对具体应用挑选一个合适的算法
- 如何监控算法,并根据实验反馈改进机器学习系统
实际开发过程中,实践者需要决定:是否收集更多的数据、是否需要增加/降低模型容量、是否需要添加/删除正则化项、是否需要改进模型的优化、是否需要改进模型的近似推断....这些都需要大量的时间
在实际应用中,正确使用一个普通算法通常要比草率的使用一个不清楚的算法要效果更好
正确应用一个算法需要掌握一些相当简单的方法论:
确定目标:使用什么样的误差度量(是准确率还是召回率?),并为此误差度量确定目标(我们期望达到什么样的效果:召回率大于95%?)
这些目标和误差度量取决于该应用为了解决什么问题
尽快建立一个端到端的工作流程,包括估计合适的性能度量
搭建系统,并确定性能瓶颈。
- 检查哪个部分的性能差于预期
- 检查差于预期的原因:是否过拟合、欠拟合、还是数据或者软件缺陷造成的
根据具体观察反复进行增量式改动:如收集新数据、调整超参数、改进算法
1. 性能度量
- 确定目标,即使用什么误差度量是第一步
- 因为误差度量将指导接下来的所有工作
- 同时我们也能够了解大概能得到什么级别的目标性能
- 对大多数应用而言,不可能实现绝对零误差
- 即使有无限的训练数据,并且恢复了真正的概率分布,但是由于输入特征可能无法包含输出变量的完整信息、或者系统本质上是个随机系统,则仍然产生了误差
- 当然实际上我们也不可能有无限的训练数据
- 通常我们需要收集更多的数据。但是我们需要在收集更多数据的成本,与进一步减少误差的价值之间权衡
- 通常我们需要对错误率定一个标准从而判断预测得好坏
- 对于学术界,我们可以将先前公布的基准结果作为标准
- 在工业界,我们从安全的、能带来性价比、或者能吸引用户的角度来确定错误率的标准
- 性能度量:表明从哪个角度来度量算法的性能
- 常见的有:精度
precision
,召回率recall
以及PR curve
,ROC
曲线,F-score
等 - 还有一种:覆盖率。它是机器学习系统能够产生响应的样本占所有样本的比例
- 一个系统可以拒绝处理任何样本,从而达到 100% 的精度。但是覆盖率为 0%
- 也可以从专业角度考量:点击率、用户满意度调查等等
- 最重要的是:确定使用哪个性能度量
- 常见的有:精度
2. 默认的基准模型
- 根据问题的复杂性,项目开始时可能无需使用深度学习
- 如果只需要正确选择几个线性权重就能解决问题,那么项目开始可以使用一个简单的统计模型,如逻辑回归
- 如果问题属于 “AI-完全”类型的,如对象识别、语音识别等,那么项目开始于一个合适的深度学习模型,则效果会比较好
- 根据数据结构选择一类合适的模型:
- 如果是以固定大小的向量作为输入的有监督学习,那么可以使用全连接的前馈网络
- 如果输入已知的拓扑结构(如图像),则可以使用卷积网络
- 如果输入或者输出是一个序列,则使用门控循环网络(LSTM 或者 GRU)
- 刚开始时可以使用某些分段线性单元:如
ReLU
或者其扩展 - 可以选择具有衰减学习率以及动量的
SGD
作为优化算法- 常见的衰减方法有:
- 衰减到固定最低学习率的线性衰减
- 指数衰减
- 每次发生验证错误停滞时将学习率降低 2-10 倍的衰减策略
- 另一种优化选择是
Adam
算法
- 常见的衰减方法有:
batch normalization
对优化性能有着显著的影响,特别是对于卷积网络和具有sigmoid
非线性函数的网络而言- 最初的基准中,可以忽略
batch normalization
- 当优化似乎出现问题时,应立即使用
batch normalization
- 最初的基准中,可以忽略
- 除非训练集包含数千万或者更多的样本,否则项目一开始就应该包含一些温和的正则化
- 建议采用早停策略
- 建议采用
dropout
策略,它也兼容很多模型以及许多正则化项 batch normalization
有时可以降低泛化误差,此时可以省略dropout
策略。因为用于normalize
的统计量估计本身就存在噪音
- 如果我们的任务和另一个被广泛研究的任务相似,那么通过复制之前研究中已知的性能良好的模型和算法,可能会得到很好的效果
- 你可以从该任务中复制一个训练好的模型。如从
ImageNet
上训练好的卷积网络的特征来解决其他计算机视觉任务
- 你可以从该任务中复制一个训练好的模型。如从
- 对于是否使用无监督学习,和特定领域有关
- 对于某些领域,如自然语言处理,能大大受益于无监督学习技术
- 在其他领域,目前无监督学习并没有带来好处
- 如果你所处理的应用,无监督学习是非常重要的,那么将其包含在第一个端到端的基准中;否则只有在解决无监督问题时,才第一次尝试使用无监督学习
3. 决定是否收集更多数据
- 建立第一个端到端的系统后,就可以度量算法的性能并决定如何改进算法
- 很多新手忍不住尝试很多不同的算法来改进
- 实际上,收集更多的数据往往比改进学习算法要有用的多
- 决定是否需要收集更多数据的标准:
- 首先:确定训练集上的性能可否接受。如果模型在训练集上的性能就很差,那么没必要收集更多的数据。
- 此时可以尝试增加更多的网络层
- 或者每层增加更多的隐单元从而增加模型的规模
- 也可以尝试调整学习率等超参数来改进算法
- 如果这些都不行,则说明问题可能源自训练数据的质量:数据包含太多噪声,或者数据未能包含预测输出所需要的正确输入。此时我们需要重新开始收集干净的数据,或者收集特征更丰富的数据
- 如果模型在训练集上的性能可以接受,那么我们开始度量测试集上的性能。
- 如果测试集上的性能也可以接受,则任务完成
- 如果测试集上的性能比训练集的要差得多,则收集更多的数据时最有效的解决方案之一
- 此时主要考虑三个要素:
- 收集更多数据的代价和可行性
- 其他方法降低测试误差的代价和可行性
- 增加数据数量能否显著提升测试集性能
- 首先:确定训练集上的性能可否接受。如果模型在训练集上的性能就很差,那么没必要收集更多的数据。
- 如果增加数据数量代价太大,那么一个替代的方案是降低模型规模,或者改进正则化(调整超参数、或者加入正则化策略)
- 如果调整正则化参数之后,训练集性能和测试集性能之间的差距还是无法接受,则只能收集更多的数据
- 当决定增加数据数量时,还需要确定收集多少数据。可以绘制曲线来显式训练集规模和泛化误差之间的关系
- 根据曲线的延伸,可以预测还需要多少训练数据来到达一定的性能
- 通常加入小比例的样本不会对泛化误差产生显著的影响。因此建议在对数尺度上考虑训练集的大小,以及增加的数据数量
- 如果收集更多的数据是不可行的(成本太高无法实现,或者无法改进泛化误差),那么改进泛化误差的唯一方法是:改进学习算法本身
- 这是属于研究领域,并不是对实践者的建议
4. 选择超参数
- 大部分深度学习算法都有许多超参数来控制不同方面的算法表现
- 有的超参数会影响算法运行的时间和存储成本
- 有的超参数会影响学习到的模型质量,以及在新输入上推断正确结果的能力
- 有两种选择超参数的方法:
- 手动选择。手动选择超参数需要了解超参数做了些什么,以及机器学习模型如何才能取得良好的泛化
- 自动选择。自动选择超参数算法不需要你了解超参数做了什么以及机器学习模型如何才能取得零号的泛化,但是它往往需要更高的计算成本
4.1 手动调整超参数
手动设置超参数:我们必须了解超参数、训练误差、泛化误差、计算资源(内存和运行时间) 之间的关系。这要求我们切实了解一个学习算法有效容量的基本概念
手动搜索超参数的任务是:在给定运行时间和内存预算范围的条件下,最小化泛化误差
- 我们不讨论超参数对于运行时间和内存的影响,因为它们高度依赖于平台
手动搜索超参数的主要目标是:调整模型的有效容量以匹配任务的复杂性
- 模型的有效容量受限于三个因素:
- 模型的表示容量。更多的网络层、每层更多的隐单元的模型具有更大的容量(能表达更复杂的函数)
- 学习算法成功最小化训练模型代价函数的能力
- 训练过程正则化模型的程度
- 模型的表示容量并不是越高越好。如果无法找到合适的代价函数来最小化训练代价、或者正则化项排除了某些合适的函数,那么即使模型的表达能力再强,也无法学习出合适的函数。
- 模型的有效容量受限于三个因素:
如果以超参数为自变量,泛化误差为因变量。那么会在的曲线通常会表现为 U 形
- 在某个极端情况下,超参数对应着低容量(并不是超参数越小,模型容量越低;也可能是相反的情况)。此时泛化误差由于训练误差较大而很高。这就是欠拟合
- 在另一个极端情况下,超参数对应着高容量,此时泛化误差也很大。这就是过拟合
- 过拟合中,泛化误差较大的原因是:虽然此时训练误差较小,但是训练误差和测试误差之间的差距较大。
- 最优的模型容量位于曲线中间的某个位置
对于某些超参数,当超参数值太大时,会发生过拟合。如中间层隐单元的数量,数量越大,模型容量越高,也更容易发生过拟合。
对于某些超参数,当超参数值太小时,也会发生过拟合。如 $L2$ 正则化的权重系数,系数为0,表示没有正则化,此时很容易过拟合。
并不是每个超参数都对应着完整的 U 形曲线
- 很多超参数是离散的,如中间层隐单元的数量,或者
maxout
单元中线性片段的数目 - 有些超参数甚至是二值的。如是否决定对输入特征进行标准化这个布尔值的超参数
- 有些超参数可能有最小值或者最大值
- 很多超参数是离散的,如中间层隐单元的数量,或者
学习率可能是最重要的超参数
如果你只有时间来调整一个超参数,那么久调整学习率
相比其他超参数,学习率以一种更复杂的方式控制模型的有效容量:
- 当学习率适合优化问题时,模型的有效容量最高
- 当学习率过大时,梯度下降可能会不经意地增加而非减少训练误差。在理想的二次情况下,如果学习率是最佳值的两倍时,会发生这种情况
- 当学习率太小时,训练不仅会很慢,还有可能永久停留在一个很高的训练误差。对于这种情况我们知之甚少(但是我们可以知道这种情况不会发生在一个凸损失函数中)
学习率关于训练误差具有 U 形曲线。泛化误差也是类似的 U 形曲线,但是正则化作用在学习率过大或者过小处比较复杂
调整学习率以外的其他参数时,需要同时监测训练误差和测试误差,从而判断模型是否过拟合或者欠拟合,然后适当调整其容量
- 如果训练集错误率大于目标错误率(这个根据任务背景人工指定),那么只能增加模型容量以改进模型。但是这增加了模型的计算代价
- 如果测试集错误率大于目标错误率,则有两个方法:
- 如果训练误差较小(这说明模型容量较大),则表明测试误差取决于训练误差与测试误差之间的差距。要减少这个差距,我们可以改变正则化超参数,以减少有效的模型容量
- 如果训练误差较大(者说明模型容量较小),那么也只能增加模型容量以改进模型
通常最佳性能来自于正则化很好的大规模模型,如使用
Dropout
的神经网络大部分超参数可以推论出是否增加或者减少模型容量,部分示例如下: | 超参数 | 容量何时增加 | 原因 | 注意事项 | | :--------: | :----: | :-----------------------------------: | :--------------------------------------: | | 隐单元数量 | 增加 | 增加隐单元数量会增加模型的表示能力 | 几乎模型每个操作所需要的时间和内存代价都会随隐单元数量的增加而增加 | | 学习率学习率 | 调至最优 | 不正确的学习率,不管是太高还是太低都会由于优化失败而导致低的有效容量的模型 | | | 卷积核宽度 | 增加 | 增加卷积核宽度会增加模型的参数数量 | 较宽的卷积核导致较窄的输出尺寸,除非使用隐式零填充来减少此影响,否则会降低模型容量。较宽的卷积核需要更多的内存来存储参数,并增加运行时间 | | 隐式零填充 | 增加 | 在卷积之前隐式添加零能保持较大尺寸的表示 | | | 权重衰减系数 | 降低 | 降低权重衰减系数使得模型参数可以自由地变大 | | | dropout 比率 | 降低 | 较少地丢弃单元可能更多的让单元彼此“协力”来适应训练集 | |
手动调整超参数时不要忘记最终目标:提升测试集性能
- 加入正则化只是实现这个目标的一种方法
- 如果训练误差很低,也可以通过收集更多的训练数据来减少泛化误差。如果训练误差太大,则收集更多的训练数据就没有意义。
- 实践中的一种暴力方法是:不断提高模型容量和训练集的大小。这种方法增加了计算代价,只有在拥有充足的计算资源时才可行
4.2 自动超参数优化算法
- 理想的学习算法应该是只需要输入一个数据集,然后就可以输出学习的函数而不需要人工干预调整超参数
- 一些流行的算法如逻辑回归、支持向量机,其流行的部分原因是:这类算法只需要调整一到两个超参数,而且性能也不错
- 某些情况下,神经网络只需要调整少量的超参数就能达到不错的性能;但是如果大多数情况下需要调整更多的超参数
- 原则上可以开发出封装了学习算法的超参数优化算法,并自动选择其超参数
- 超参数优化算法往往有自己的超参数(如学习算法的每个超参数的取值范围),这将问题变得更复杂
- 我们可以人工选择参数优化算法这一级的超参数,因为这一级的超参数通常更容易选择
4.3 网格搜索
- 当只有三个或者更少的超参数时,常见的超参数搜索方法是:网格搜索
- 对于每个超参数,选择一个较小的有限值集合去搜索
- 然后这些超参数笛卡尔乘积得到多组超参数
- 网格搜索使用每一组超参数训练模型,挑选验证集误差最小的超参数作为最好的超参数
- 如何确定搜索集合的范围?
- 如果超参数是数值,则搜索集合的最小、最大元素可以基于先前相似实验的经验保守地挑选出来
- 如果超参数是离散的,则直接使用离散值
- 通常网格搜索会在对数尺度下挑选合适的值
- 通常重复进行网格搜索时,效果会更好。假设在集合
{-1,0,1}
上网格搜索超参数 $\alpha$- 如果找到的最佳值是 1,那么说明我们低估了 $\alpha$ 的取值范围。此时重新在
{1,2,3}
上搜索 - 如果找到的最佳值是 0,那么我们可以细化搜索范围以改进估计。此时重新在
{-0.1,0,0.1}
上搜索
- 如果找到的最佳值是 1,那么说明我们低估了 $\alpha$ 的取值范围。此时重新在
- 网格搜索的一个明显问题时:计算代价随着超参数数量呈指数级增长。
- 如果有 $m$ 个超参数,每个最多取 $n$ 个值,那么所需的试验数将是 $O(n^m)$ 。虽然我们可以并行试验,但是指数级增长的计算代价仍然不可行
4.4 随机搜索
- 随机搜索是一种可以替代网格搜索的方法,它编程简单、使用方便、能更快收敛到超参数的良好取值:
- 首先为每个超参数定义一个边缘分布,如伯努利分布(对应着二元超参数)或者对数尺度上的均匀分布(对应着正实值超参数)
- 然后假设超参数之间相互独立,从各分布中抽样出一组超参数。
- 使用这组超参数训练模型
- 经过多次抽样 -> 训练过程,挑选验证集误差最小的超参数作为最好的超参数
- 随机搜索的优点:
- 不需要离散化超参数的值,也不需要限定超参数的取值范围。这允许我们在一个更大的集合上进行搜索
- 当某些超参数对于性能没有显著影响时,随机搜索相比于网格搜索指数级地高效,它能更快的减小验证集误差
- 与网格搜索一样,我们通常会基于前一次运行结果来重复运行下一个版本的随机搜索
- 随机搜索比网格搜索更快的找到良好超参数的原因是:没有浪费的实验
- 在网格搜索中,两次实验之间只会改变一个超参数 (假设为 $\beta$ )的值,而其他超参数的值保持不变。如果这个超参数 $\beta$ 的值对于验证集误差没有明显区别,那么网格搜索相当于进行了两个重复的实验
- 在随机搜索中,两次实验之间,所有的超参数值都不会相等(因为每个超参数的值都是从它们的分布函数中随机采样而来)。因此不大可能会出现两个重复的实验
- 如果 $\beta$ 超参数与泛化误差无关,那么不同的 $\beta$ 值:
- 在网格搜索中,不同 $\beta$ 值、相同的其他超参数值,会导致大量的重复实验
- 在随机搜索中,其他超参数值每次也都不同,因此不大可能出现两个重复的实验(除非所有的超参数都与泛化误差无关)
4.5 基于模型的超参数优化
- 超参数搜索问题可以转化为一个优化问题:
- 决策变量是超参数
- 优化的代价是超参数训练出来的模型在验证集上的误差
- 在简化的设定下,可以计算验证集上可导的误差函数关于超参数的梯度,然后基于这个梯度进行更新
- 实际上,大多数问题中,超参数的梯度是不可用的
- 一方面可能是因为因为高额的计算代价和存储成本。因为你需要计算非常多的超参数才能获得一系列的超参数的导数(这就要求你运行非常轮次的算法)
- 另一方面可能是因为验证集误差在超参数上本质不可导(如超参数时离散值的情况)
- 为了弥补超参数梯度的缺失,我们可以使用贝叶斯回归模型来估计每个超参数的验证集误差的期望和该期望的不确定性(贝叶斯回归模型不需要使用梯度)
- 目前无法明确的确定:贝叶斯超参数优化是否有效。实际上,它有时表现得像一个人类专家,但是有时候又发生灾难的失误。目前该方法还不够成熟或可靠
- 大部分超参数优化算法比随机搜索更复杂,并且具有一个共同的缺点:在获取任何有效的超参数信息之前,你必须完整运行整个训练过程
- 这种做法是相当低效的。实际上人类手动搜索之前,可以很早就判断某些超参数组合是否是完全病态的
5. 调试策略
当一个机器学习系统效果不好时,很难判断效果不好的原因是算法本身,还是算法实现错误。由于各种原因,机器学习系统很难调试
- 大多数情况下,我们不能提前知道算法的行为
- 实际上,使用机器学习的出发点是:机器学习会发现一些我们无法发现的有用的行为
- 如果我们在一个新的分类任务上训练一个神经网络,它达到了 5% 的测试误差。我们无法直接知道:这是期望的结果,还是次优的结果?
- 大部分机器学习模型有多个自适应的部分
- 如果某个部分失效了,那么其他部分仍然可以自适应,并获得大致可接受的性能
- 比如在多层神经网络中,假设我们在梯度下降中对偏置更新时犯了一个错误 $b=b-\alpha$ (错误原因:没有使用梯度。其中 $\alpha$ 为学习率)。这个错误导致偏置在学习过程中不断减小。但是只是检查模型输出的话,这个错误并不是显而易见的。因为权重 $\mathbf W$ 可以自适应的补偿偏置 $b$ 的错误
- 大多数情况下,我们不能提前知道算法的行为
大部分神经网络的调试策略都是解决上述两个难点的一个或者两个
- 我们可以设计一种足够简单的情况,能够提前得到正确结果,判断模型预测是否与之相符
- 我们可以设计一个测试(类似于
UnitTest
),独立检查神经网络实现的各个部分
一些重要的调试方法如下:
可视化计算中模型的行为:直接观察机器学习模型运行机器任务,有助于确定其达到的量化性能数据是否看上去合理
- 仅仅评估模型性能可能是最具破坏性的错误之一,因为它会使你在系统出问题时误以为系统运行良好
可视化最严重的错误:大多数模型能够输出运行任务时的某种置信度(如基于
softmax
函数输出层的分类器给每个类分配了一个概率)。如果某个样本被错误的分类,而且其分类置信度很低,那么可以检查这些样本的问题根据训练和测试误差检测软件:通常我们很难确定底层软件是否正确实现,而测试和训练误差提供了一些线索
- 如果训练误差较低,而测试误差较高,那么很可能是由于算法过拟合。也有可能是软件错误,导致测试误差未能正确地度量
- 如果训练误差和测试误差都很高,那么很难确定是由于软件错误,还是由于算法模型的欠拟合。此时需要进一步测试,如下所述
拟合极小的数据集:当训练集上有很大的误差时,我们需要确定问题是欠拟合,还是软件错误
- 通常即使是极小模型也能保证很好的拟合一个足够小的数据集。如只有一个样本的分类数据,它可以通过正确设置输出层的偏置来拟合
- 如果分类器不能正确标准一个单样本组成的训练集、自编码器无法成功再现一个单独的样本、生成模型无法一致的生成一个单独的样本,那么很有可能是由于软件错误
- 这种测试可以推广到少量样本的小数据集上,不一定是只有一个样本的数据集
比较反向传播导数和数值导数:如果我们正在使用一个软件框架或者库,那么必须定义
bprop
方法,常见的错误原因是未能正确的实现梯度表达。验证该错误的一个方法是:比较实现的自动求导和通过有限差分计算的导数 $$ f^\prime\simeq \frac{f(x+\epsilon)-f(x)}{\epsilon} $$ 或者采用中心差分法: $$ f^\prime\simeq \frac{f(x+\frac 12\epsilon)-f(x-\frac 12\epsilon)}{\epsilon} $$ 其中 $\epsilon$ 必须足够大,从而确保不会由于有限精度问题产生舍入误差- 如果梯度是雅克比矩阵(输出为向量,输入也是向量),则我们可以多次使用有限差分法来评估所有的偏导数
监控激活函数值和梯度的直方图:可视化激活函数值和梯度的统计量往往是有用的。
- 隐单元的激活函数值告诉我们:该单元是否饱和,或者它们饱和的频率如何(如多久关闭一次,是否永远关闭,以及采用双曲正切单元时饱和的程度)
- 梯度的统计量告诉我们:梯度是否快速的增长或者快速的消失
- 参数的梯度的量级和参数的量级也有意义:我们希望参数在一个小批量更新中变化的幅度是参数量值的 1% 这样的级别(而不是 50% 或者 0.0001%,这两者要么导致参数移动太快,要么太慢)
- 如果数据时稀疏的(如自然语言),则有些参数可能是很少更新的,检测时需要注意
许多深度学习算法为每一步的结果产生了某种保证。其中包括:
目标函数值确保在算法的迭代步中不会增加
某些变量的导数在算法的每一步中都是 0
所有变量的梯度在收敛时,会变为 0
由于计算机存储浮点数的舍入误差,这些条件可能会有误差
如果违反了这些保证,那么一定是软件错误
6. 示例:数字识别系统
- 项目开始于性能度量的选择以及这些度量的期望。
- 总的原则是:度量的选择要符合项目的业务目标
- 因为地图只有高准确率时才有价值,所以该系统要求达到 98% 的准确率(达到人类水平)
- 为了达到这个级别的准确率,系统牺牲了覆盖率。因此在保持准确率 98% 的前提下,覆盖率成了项目的主要性能度量
- 选择基准系统:对于视觉任务而言,基准系统是带有
ReLU
单元的卷积网络
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论