TensorFlow2.0深度学习(九)过拟合

1. 模型的容量

        通俗的讲,模型的容量或表达能力,是指模型拟合复杂函数的能力。一种体现模型容量的指标为模型的假设空间大小,即模型可以表示的函数集的大小。假设空间越大越完备,从假设空间中搜索出逼近真实模型的函数也就越有可能;反之,如果假设空间非常受限,就很难从中找到逼近真实模型的函数。

        但是过大的假设空间会增加搜索难度和计算代价,在有限的计算资源的阅读下,较大的假设空间并不一定能搜索出更好的函数模型。同时由于观测误差的存在,较大的假设空间中可能包含了大量表达能力过强的函数,能够将训练样本的观测误差也学习进来,从而伤害了模型的泛化能力。

2. 过拟合和欠拟合

        当模型容量过大时,网络模型除了学习到训练集数据的模态之外,还把额外的观测误差也学习进来,导致学习的模型在训练集上面表现较好,但是在未见的样本上表现不佳,也就是泛化能力偏弱,把这种现象叫做过拟合。当模型的容量过小时,模型不能够很好的学习到训练集数据的模态,导致训练集上表现不佳,同时在未见的样本上表现也不佳,这种现象叫做欠拟合

        当发现当前的模型在训练集上面误差一直维持较高的状态,很难优化减少,同时在测试集上也表现不佳时,可以考虑是否出现了欠拟合的现象,这个时候可以通过增加神经网络的层数、增大中间维度的大小等手段,比较好的解决欠拟合的问题。

        现代深度神经网络中过拟合现象非常容易出现,主要是因为神经网络的表达能力非常强,很容易就出现了神经网络的容量偏大的现象。接下来介绍一系列方法,来检测并抑制过拟合现象。

3. 数据集划分

        数据集一般需要划分为训练集和测试集,但是为了挑选模型的超参数和检测过拟合现象,一般需要将原来的训练集再次切分为新的训练集和验证集,即数据集需要切分为训练集、验证集和测试集3个子集。

3.1 验证集与超参数

        训练集\mathbb{D}^{train}用于训练模型参数,测试集\mathbb{D}^{test }用于测试模型的泛化能力,测试集中的样本不能参与模型的训练,防止模型“记忆”住数据的特征,损害模型的泛化能力。训练集和测试集都是采用自相同的数据分布。

        但是数据集划分为训练集和测试集是不够的,由于测试集的性能不能作为模型训练的反馈,而需要在模型训练时能够挑选出较合适的模型超参数,判断模型是否过拟合等,因此需要将训练集再次切分为训练集\mathbb{D}^{train}和验证集\mathbb{D}^{val}。划分过的训练集与原来的训练集的功能一致,用于训练模型的参数,而验证集用于选择模型的超参数(称为模型选择),它的功能包括:

  • 根据验证集的性能表现来调整学习率,权值衰减系数,训练次数等
  • 根据验证集的性能表现来重新调整网络拓扑结构
  • 根据验证集的性能表现判断是否过拟合和欠拟合
3.2 提前停止

        把对训练集中的一个Batch运算更新一次叫做Step,对训练集的所有样本循环迭代一次叫做一个Epoch。验证集可以在数次Step或数据Epoch后使用。验证的步骤过于频繁,能够精准地观测模型的训练状况,但是也会引入额外的计算代价,一般建议几个Epoch后进行一次验证运算。以分类任务为例,在训练时,一般关注的指标有训练误差,训练准确率等,相应地,验证时也有验证误差和验证准确率,测试时也有测试误差和测试准确率。通过观测训练准确率和验证准确率可以大致推断模型是否过拟合和欠拟合。如果模型的训练误差较低,训练准确率较高,但是验证误差较高,验证准确率较低,那么可能出现了过拟合,如果误差均较高,准确率较低,那么可能出现了欠拟合现象。

        那么如何选择合适的epoch停止训练呢?可以通过观察验证指标的变化,来预测最适合的epoch可能的位置。可以记录模型的验证准确率,并监控验证准确率的变化,当发现验证准确率连续P个epoch没有下降时,可以预测已经达到了最适合的epoch附近,从而提前终止训练。如下图所示,可以观察到,在epoch为160左右时,模型达到最佳状态,提前终止训练。

4. 模型设计

        通过验证集可以判断网络模型是否过拟合或者欠拟合,从而为调整网络模型的容量提供判断依据。对于神经网络来说,网络的层数参数量是网络容量很重要的参考指标,通过减少网络的层数,减少每层中网络参数量的规模可以有效降低网络的容量。反之,如果发现模型欠拟合,需要增大网络的容量,可以通过增加层数,增大每层的参数量等方式实现。

5. 正则化

        通过设计不同层数、大小的网络模型可以为优化算法提供初始的函数假设空间,但是模型的实际容量可以随着网络参数的优化更新而产生变化。以多项式函数模型为例:

y=\beta_0+\beta_1x+\beta_2x^2+\beta_3x^3+\cdots+\beta_nx^n+\varepsilon

上述模型的容量可以通过n简单衡量。在训练的过程中,如果网络参数\beta_{k+1},...,\beta_n=0,那么网络的实际容量退化到k次多项式的函数容量。因此,通过限制网络参数的稀疏性,可以来约束网络的实际容量。

        这种约束一般通过在损失函数上添加额外的参数稀疏性惩罚项实现,在未加约束之前的优化目标是

Minimize\quad\mathcal{L}(f_\theta(x),y),(x,y)\in\mathbb{D}^{train}

对模型的参数添加额外的约束后,优化的目标变为

Minimize \quad\mathcal{L}(f_\theta(x),y)+\lambda*\Omega(\theta),(x,y)\in\mathbb{D}^{train}

其中\Omega(\theta)表示对网络参数\theta的稀疏性约束函数。一般地,参数\theta的稀疏性约束通过约束参数\theta的L范数实现,即

\Omega(\theta)=\sum_{\theta_i}\|\theta_i\|_l

        新的优化目标除了要最小化原来的损失函数\mathcal{L}(x,y)之外,还需要约束网络参数的稀疏性,优化算法会在降低\mathcal{L}(x,y)的同时,尽可能地迫使网络参数\theta_i变得稀疏,它们之间的权重关系通过超参数\lambda来平衡,较大的\lambda意味着网络的稀疏性更重要;较小的\lambda则意味着网络训练误差更重要。通过选择合适的\lambda超参数可以获得较好的训练性能,同时保证网络的稀疏性,从而获得不错的泛化能力。

        常用的正则化方法有L0,L1,L2正则化。

5.1 L0正则化

\Omega(\theta)=\sum_{\theta_i}\|\theta_i\|_0

定义为\theta_i中非零元素的个数。可以迫使网络中的连接权值不部分为0,从而降低网络的实际参数量和网络容量。但是L0范数不可导,不能利用梯度下降算法进行优化,在神经网络中使用的并不多。

5.2 L1正则化

\Omega(\theta)=\sum_{\theta_i}\|\theta_i\|_1

定义为张量\theta_i中所有元素的绝对值之和。L1正则化也叫Lasso Regularization,它是连续可导的,在神经网络中使用广泛。

        L1正则化可以实现如下:

# 创建网络参数w1, w2
w1 = tf.random.normal([4,3])
w2 = tf.random.normal([4,2])
# 计算L1正则化项
loss_reg = tf.reduce_sum(tf.math.abs(w1)) + tf.reduce_sum(tf.math.abs(w2))
5.3 L2正则化

\Omega(\theta)=\sum_{\theta_i}\|\theta_i\|_2

定义为张量\theta_i中所有元素的平方和。L2正则化也叫Ridge Regularization,它和L1正则化一样,也是连续可导的,在神经网络中使用广泛。

        L2正则化项实现如下:

# 计算L2正则化项
loss_reg = tf.reduce_sum(tf.square(w1)) + tf.reduce_sum(tf.square(w2))

6. Dropout

        Dropout通过随机断开神经网络的连接,减少每次训练时实际参与计算的模型的参数量;但是在测试时,Dropout会恢复所有的连接,保证模型测试时获得最好的性能。

        在TensorFlow中,可以通过tf.nn.dropout(x, rate)函数实现某条连接的Dropout功能,其中rate参数设置断开的概率值p:

# 添加dropout操作
x = tf.nn.dropout(x, rate=0.5)

也可以将Dropout作为一个网络层使用,在网络中间插入一个Dropout层:

model.add(layers.Dropout(rate=0.5))

7. 数据增量

        数据增强是指在维持样本标签不变的条件下,根据先验知识改变样本的特征,使得新产生的样本也符合或者近似符合数据的真实分布。

        TensorFlow中提供了常用图片的处理函数,位于tf.image子模块中。通过tf.image.resize函数可以实现图片的缩放功能,将数据增强一般实现在预处理函数preprocess中,将图片从文件系统读取进来后,即可进行图片数据增强操作:

def preprocess(x,y):
    # x: 图片的路径,y: 图片的数字编码
    x = tf.io.read_file(x)
    x = tf.image_decode_jpeg(x, channels=3) # RGBA
    # 图片缩放到244×244大小,这个大小根据网络设定自行调整
    x = tf.image.resize(x, [244, 244])
7.1 旋转

        旋转图片是非常常见的图片数据增强方式,通过将原图进行一定角度的旋转运算,可以获得不同角度的新图片,这些图片的标签信息维持不变。

通过tf.image.rot90(x,k=1)可以实现图片按逆时针方式旋转k个90°:

# 图片逆时针旋转180度
x = tf.image.rot90(x, 2)
7.2 翻转

        图片的翻转分为沿水平轴翻转和竖直翻转。

在TensorFlow中,可以通过tf.image.random_flip_left_right和tf.image.random_flip_up_down实现图片在水平方向和竖直方向的随机翻转操作:

# 随机水平翻转
x = tf.image.random_flip_left_right(x)
# 随机竖直翻转
x = tf.image.random_flip_up_down(x)
7.3 裁剪

        通过在原图的左右或者上下方向去掉部分边缘像素,可以保持图片主体不变,同时获得新的图片样本。在实际裁剪时,一般先将图片缩放到略大于网络输入尺寸的大小,再进行裁剪到合适大小。

# 图片先缩放到稍大尺寸
x = tf.image.resize(x, [244,244])
# 在随机裁剪到合适尺寸
x = tf.image.random_crop(x, [224, 224, 3])
7.4 生成数据

        通过生成模型在原有数据上学习到数据的分布,从而生成新的样本,这种方式也可以在一定程度上提升网络性能。如通过条件生成对抗网络(Conditional GAN, CGAN)可以生成带标签的样本数据。

7.5 其他方式

        除了上述介绍的典型图片数据增强方式以外,可以根据先验知识,在不改变图片标签信息的条件下,任意变换图片数据,获得新的图片。下图分别演示了在原图上叠加高斯噪声后的图片数据、通过改变图片的观察视角后获得的新图片和在原图上随机遮挡部分区域获得新图片。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值