DL2 - 在实际应用中如何使得神经网络高效工作

Welcome to MyBlog!
本文所有截图和文字均借鉴于:Coursera


1 如何设置你的训练集/开发集/测试集

在训练一个神经网络时, 你必须做出许多决定, 例如你的神经网络将会有多少层啊 并且每一层中包含多少个隐藏神经元啊, 学习速率是多少啊, 还有每一层你想用什么激活函数啊 当你开始一个新的应用时, 你几乎不可能一次性就正确地猜到上面提及的, 以及未提及的超参数的准确数值 因此在实际应用中 机器学习是一个**高度迭代的过程**

机器学习的应用是相当反复的迭代的过程, 你只需要将这个循环进行许多次, 就有希望能为你的应用中的网络找出好的参数, 所以有一件事能决定你能多快地取得进展, 那就是你进行迭代过程时的效率, 而恰当地将你的数据集分为训练集, 开发集和测试集让你的迭代效率更高

1.1 比例

当样本个数只有100、1000、10000时,被广泛认为的最佳比例是60/20/20%。

但是在大数据时代,当你有100万个训练样本时,可能只需要1万个用作开发集和测试集就足够了。

1.2 确保开发集和测试集中的数据分布相同

你需要用开发集对许多不同的模型进行评估, 费尽全力改善模型在开发集上的性能, 如果开发集和测试集的数据分布相同就很方便, 但是因为深度学习算法对训练数据量需求巨大, 我能看到一种趋势是用各种有创意的办法, 比如爬取网页, 来获得比其它途径大得多的训练集, 即使这会带来一些代价, 也就是训练集的数据分布, 与开发集和测试集的数据分布不同, 但你只需要遵守这个经验法则, 你的算法进步速度就会更快

2 bias(偏差)和variance(方差)

2.1 偏差和方差的区别

偏差:预测值或者估计值期望与真实值期望之间的差距。

方差:预测结果的分布情况/分布范围/离散程度。

偏差:评价对象时单个模型,期望输出和真实标记的差别。

方差:评价对象时多个模型,表示多个模型差异程度。

以上图为例:

  1. 左上的模型偏差最大,右下的模型偏差最小;
  1. 左上的模型方差最小,右下的模型方差最大;

2.2 过拟合和欠拟合

高方差:你的训练集误差是1%,而对于开发集误差 为了便于讨论 我们假设是11% 在这个例子里 你的模型对训练集处理得非常好 但是相对来说,开发集处理得就有些不尽如人意 所以这可能是在处理训练集时过拟合了

高偏差:假设训练集的误差是15%,假设你的开发集误差是16%, 在这种情况下,我们假设人工识别误差是0% 因为人可以直接看到这些图片,并判断出这是否是一只猫, 所以看上去,这个算法在训练集上的表现并不尽如人意 如果它并未将训练集数据处理得很好 这就是欠拟合。

3 机器学习的基本准则

high bias: bigger network、train longer、更高级的优化算法、更换神经网络结构

high variance: more data、正则化?、更适合的神经网络结构

只要你能不断扩大所训练的网络的规模 只要你能不断获得更多数据 虽然这两点都不是永远成立的 但如果这两点是可能的 那扩大网络几乎总是能够 减小偏差而不增大方差 只要你用恰当的方式正则化的话 而获得更多数据几乎总是能够 减小方差而不增大偏差 所以归根结底 有了这两步以后 再加上能够选取不同的网络来训练 以及获取更多数据的能力 我们就有了能够且只单独削减偏差 或者能够并且单独削减方差 同时不会过多影响另一个指标的能力 我认为这就是诸多原因中的一个 它能够解释为何深度学习在监督学习中如此有用

4 regularization正则化

4.1 什么是L2正则化

在神经网络中 你有一个代价函数 它是你所有参数的函数 包括w[1] b[1]到w[L] b[L] 这里大写的L是神经网络的层数 因此 代价函数是m个训练样本上 的损失之和 至于正则化 再加上lambda/2m 乘以所有参数W的范数的平方之和 这里W是你的参数矩阵 这里矩阵范数的平方定义为 对于i和j 对矩阵中每一个元素的平方求和

L2正则化也被称为权重衰减(表现在目标函数/参数更新)

4.2 为什么正则化可以防止过拟合(减少方差问题)

如果不加此项,模型必定倾向于最小化损失函数J(θ)
,这么一来就很可能发生overfitting。引入该项后,如果模型过于复杂,该项的次数(degree)也更高,引发的惩罚(penalization)值也更大,由此抑制了模型的过度复杂化,λ也被称为惩罚因子。
λ过小,则对“防止过拟合”几乎无影响。λ过大,则使损失函数前半部分的权重大大降低,试想如果λ接近无限大,最终的结果是所有的θ都接近0,因此需要选择适当的λ。

举一个极端的例子,当lanbda非常大的时候,神经网络的许多神经节点将被弱化,看起来就像一个不容易过拟合的小型网络。

4.3 随机失活正则化(丢弃发dropout)

4.4 其他防止过拟合的方法

1 数据集扩充:比如水平翻转,随机裁剪、随机扭曲、随机放大来变化图片(廉价的方式)

2 早终止法:

5 标准化处理

如果你对左图的那种代价函数使用梯度下降法 那可能必须使用非常小的学习率 因为假如从这里开始 梯度下降法需要经历许多步 反复辗转 才能好不容易终于挪到这个最小值 而如果等值线更趋近于圆形 那无论从何开始 梯度下降法几乎都能直接朝向最小值而去 你可以在梯度下降中采用更长的步长 而无需像左图那样来回摇摆缓慢挪动 当然在实践中 w是一个高维向量 把它画在二维空间中可能无法正确传递出高维中的感觉 但大体上的感觉是你的代价函数会更圆 优化过程更容易进行 因为各种特征的尺度会比较接近

6 梯度 消失/爆发

当训练神经网络时我们会遇到一个问题 尤其是当训练层数非常多的神经网络时 这个问题就是梯度的消失和爆炸 它的意思是当你在训练一个深度神经网络的时候 损失函数的导数或者说斜率 有时会变得非常大 或者非常小甚至是呈指数级减小 这使训练变得很困难

针对此问题的**部分**解决方法:虽然不能完全解决它 但帮助很大 该方法就是更好 更细致地随机初始化你的神经网络

7 梯度检测

导数的正式定义 就是对于很小的𝜀 计算[f(𝜃+𝜀)-f(𝜃-𝜀)]/(2𝜃) 就是对于很小的𝜀 计算[f(𝜃+𝜀)-f(𝜃-𝜀)]/(2𝜃)

原因:对于一个非零的𝜀值 你可以证明这个近似的误差 在𝜀平方这个阶上 𝜀是个很小的数 如果𝜀是0.01 就像这里 那么𝜀平方就是0.0001 这个大O记号就表示误差就是某个常数乘以这个 这就是我们的近似误差 这个例子中的大O的常数恰好就是1 相比而言 如果我们用这边的另一个公式 误差就在𝜀这个阶上 当𝜀是一个小于1的数时 𝜀就比𝜀平方大很多 这也就是为什么 这个公式不如左边这个公式精确 这也就是为什么我们做梯度检验时采用双侧差值 你计算f(𝜃+𝜀)-f(𝜃-𝜀)再除以2𝜀 而不使用这个不够精确的单侧差值

怎么进行双侧差值梯度检测

从W1,B1,一直到,WL,bL 要实现梯度检查算法 首先要把你的所有参数 重新拼成一个巨大的参数向量θ 你要把W矩阵转化成一个向量 把所有的W矩阵都转化成向量 然后把他们首尾相接拼在一起 成为一个巨大的参数向量θ 之前代价函数J是所有W和b的函数 经过向量转化后它变成了θ的函数 W和b按照同样的顺序转化后 你也可以把dW[1],dB[1]等等参数都转化成 和θ的维度相同的向量dθ 和之前一样,把dW[1]转化成向量 db[1]已经是向量 把所有的dW矩阵转化成向量 记住dW[1]和W[1]的维度相同 db[1]和b[1]的维度相同 经过相同的矩阵转化和向量拼接之后 你就把所有的微分也转化成 一个参数向量dθ dθ和θ的维度一样

在实际中 我取ε为10的负7次方 这样 如果这个式子的结果 小于10的负7次方 那就认为计算正确 它表示你的微分近似是对的 因为(误差)很小 如果该式的结果在10的负5次方的量级的话 我会很仔细地检查一遍 有可能式子也是对的 但我会再三检查这个向量的每个分量 确定没有某个分量很大 如果某个分量很大的话 那么可能你的式子里有错误了 如果左面的式子在10的负3次方的量级的话 我觉得你一定要检查代码 它很可能有错误 它的结果应该远远小于10的负3次方 如果(某个分量)大于10的负3次方 我很担心你的代码有错误

梯度检测的使用原则如下:

  1. 不要在训练中使用梯度检查 而仅仅在调试时
  2. 如果一个算法没有通过梯度检测 你需要检查它的组成 检查每一个组成成分 尝试找出漏洞 我的意思是 如果d(θapprox)与dθ差距很大的话 我会这么做 检查不同的i值 看看哪些 d(θapprox)的值 与dθ的值差距最大
  3. 如果使用了正则化,别忘了你的正则项
  4. 梯度检验不能与随机失活一起使用,因为:因为在每一次的迭代中 随机失活(dropout)将随机消除 隐藏层单元的不同子集 在使用随机失活(dropout) 进行梯度下降的过程中 并不存在一个容易计算的代价函数J 随机失活(dropout)可以被视为 对于代价函数的优化 但是这个代价函数的定义是 在每一次迭代中 对所有 非常大的可消除节点集进行求和 所以这个代价函数是很难计算的 你只需要对代价函数进行抽样 在那些使用随机失活(dropout)的集合中 每次消除不同的随机集合 所以使用梯度检验来检查 包含了随机失活(dropout)的运算是很困难的
  5. 你对于梯度下降的使用是正确的 同时w和b在随机初始化的时候 是很接近0的数 但随着梯度下降的进行 w和b有所增大 也许你的反向传播算法 在w和b接近0的时候是正确的 但是当w和b变大的时候 算法精确度有所下降 所以虽然我不经常使用它 但是你可以尝试的一个方法是 在随机初始化的时候 运行梯度检验 然后训练网络一段时间 那么w和b 将会在0附近摇摆一段时间 即很小的随机初始值 在进行几次训练的迭代后 再运行梯度检验