TensorFlow 实现深度网络——拟合
使用某个训练集训练机器学习模型,并且通过计算在模型训练集上的损失函数来度量一些被称为训练误差( Training Error )的误差,使用前几节介绍的优化算法去优化损失函数可以逐渐减小训练、误差。
然而在真实的应用中,不仅仅是让模型能够在训练集上表现良好,而且希望训练得到的模型也能在未知的新输入数据上(这些新输入数据就是测试集)表现良好。这种能在未知的新输入数据上表现良好的能力被称为泛化(Generalization)。
模型在未知的新输入数据上得到的误差称为泛化误差( GeneralizationError ,或称测试误差( Test Error))。在降低训练误差和测试误差的过程中,我们面临着机器学习中的两个主要挑战:欠拟合和过拟合。欠拟合( Underfitting )是指模型不能够在训练集上获得足够低的误差,而过拟合( Overfitti ng )是指训练误差和测试误差之间差距太大。
假设需要拟合一个样本分布满足二次函数的训练集,我们可以尝试分别设计3 个容量不同的模型并比较拟合的情况。第一个模型是线性回归模型,这可以通过一次多项式来完成:
y = wx +b
接下来对线性回归模型引入x2项作为第二个模型,这样模型就能够学习关于x 的二次函数:
y = w1x2+ w2x +b
为了再次提高模型的容量,我们可以对模型继续追加x 的更高次幂,例如下面的九次多项式:
图5-12 、图5 - 13 和图5-14 显示了线性模型、二次模型和九次模型拟合真实二次函数的3 种不同情况。在第一种情况下,由于线性模型过于简单而无法刻画真实分布的趋势,所以欠拟合;第二个模型是比较合理的,它比较好地刻画了分布的整体趋势,并且没有过于关注训练数据中的噪音分布;第三个模型能够正确地表示函数,但是因为训练参数过多导致了对训练数据中的噪音的拟合,从而使得模型无法很好地对未知数据作出判断,这就是过拟合。
在实际中,学习算法找到最优函数的概率极低,而更多的情况是找到一个或多个可以大大降低训练误差的函数。在图5-12、图5-13和图5-14所示的这些例子中,我们需要明白机器学习模型的:设计要遵循简约原则(现在通常称之为奥卡姆剃刀原则, 即在同样能够解决问题的方案中,我们应该挑选“最简单”的那个,这样就能在提高泛化能力的同时又不会造成过拟合。
解决过拟合问题通常可以采用正则化的方法:假设模型在训练集上的损失函数为J(的(注意这里ω表示的是整个神经网络中所有的参数,包括边上的权重w 和偏置项b),那么在优化时不是直接优化J(ω),而是优化J(ω)+λR(ω)。
常用的刻画模型复杂度的函数R(w)有两种,一种是L1正则化(对权重参数w求解L1范数〉,计算公式是:
另一种是L2 正则化(对权重参数w 求解平方L2 范数),计算公式是:
TensorFlow 提供了计算L2 正则化的函数contrib.layers.12_regularizer()函数。它可以返回一个函数。这个函数可以计算一个给定参数的L2 正则化项的值。Tensor Flow也提供了计算L1正则化的函contrib.layers.11_regularizer()函数,它可以返回一个函数,这个函数可以计算一个给定参数的L1正则化项的值。以下代码给出了使用这两个函数的样例:
contrib.layers.12_regularizer()或contrib.layers.11_regularize()函数的参数.5表示正则化项的权重,对应于公式J() +λR()中的λ。在实际应用中,一般会取一个非常小的值,比如0.01 ,在以后取值的时候也可以探索性地尝试其他的值。为了解决过拟合问题, Hinton 教授团队提出了一种思路简单但是非常有效的方法Dropout。它的大致意思是在训练时,将神经网络某一层的单元(不包括输出层的单元)数据随机丢弃一部分。
具体而言,使用Dropout 集成方法需要训练的是从原始网络去掉一些不属于输出层的单元后形成的子网络。图5-15 (a)展示了一个原始网络(为了方便说明,这个网络看起来过于简单〉,其输入层有2个输入单元,隐藏层有2 个隐藏单元,输出层有l个输出单元。随机删除4个单元(隐藏单元与输入单元)中的某些单元会形成16个可能的子网络,这16个子网络可以被归结到一个集合中,如图5- 15 (b)所示。
丢弃(删除)某些单元并不是指真的构建出这种结构的网络。为了能有效地删除一个单元,我们只需要将该单元的输出乘以0 即可(结果就是该单元的值变为了0)。
Tensor Flow 提供了实现Dropout功能的函数,它就是nn.dropout()函数。以下是这个函数的原型:
参数noise_shape、seed 和name的值都是默认的None,参数x代表了需要进行Dropout处理的数据,keep_prob 是float 类型的参数。以下样例代码演示了这个函数的使用:
对于nn.dropout()函数,我们将keep_prob 参数的值设为了0.5。输出经过Dropout 处理之后的x 会发现将近一半左右的数据被置0,这些都是被舍弃的,而其余的数据都被乘以l/keep_prob。以下是上面这段程序的输出结果:
也可以尝试对keep_prob 赋予其他值,当keep_prob=0.1 时会发现大部分元素都被舍弃了,当keep_prob=0.8 时会发现大部分元素都成了1.25 。对于输入单元,一般会将keep_prob 设为0.8 ;对于隐藏单元,一般会将keep_prob 设为0.5 。
总结
今天小鲸给大家介绍了泛化过程中可能存在的欠拟合和过拟合的问题,以及如何运用正则化等手段通过Tensor Flow 来优化这两大问题。