Tensorflow过拟合与欠拟合

由于真实数据的分布往往是未知而且复杂的,无法推断出其分布函数的类型和相关参 数,因此人们在选择学习模型的容量时,往往会根据经验值选择稍大的模型容量。但模型 的容量过大时,有可能出现在训练集上表现较好,但是测试集上表现较差的现象。

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

尽管统计学习理论很难给出神经网络所需要的最小容量,但是却可以根据奥卡姆剃刀 原理(Occam’s razor)来指导神经网络的设计和训练。奥卡姆剃刀原理是由 14 世纪逻辑学 家、圣方济各会修士奥卡姆的威廉(William of Occam)提出的一个解决问题的法则,他在 《箴言书注》2 卷 15 题说“切勿浪费较多东西,去做‘用较少的东西,同样可以做好的事 情’。”1。也就是说,如果两层的神经网络结构能够很好的表达真实模型,那么三层的神经 网络也能够很好的表达,但是我们应该优先选择使用更简单的两层神经网络,因为它的参 数量更少,更容易训练,也更容易通过较少的训练样本获得不错的泛化误差。

测试集中的样本不能参与模型的训练,防止模型“记忆” 住数据的特征,损害模型的泛化能力。

划分过的 训练集与原来的训练集的功能一致,用于训练模型的参数,而验证集则用于选择模型的超 参数(模型选择,Model selection).

❑ 根据验证集的性能表现来调整学习率、权值衰减系数、训练次数等。 

❑ 根据验证集的性能表现来重新调整网络拓扑结构。
❑ 根据验证集的性能表现判断是否过拟合和欠拟合。

验证集与测试集的区别在于,算法设计人员可以根据验证集的表现来调整模型的各种 超参数的设置,提升模型的泛化能力,但是测试集的表现却不能用来反馈模型的调整,否 则测试集将和验证集的功能重合,因此在测试集上的性能表现将无法代表模型的泛化能 力。

如何选择合适的 Epoch 就提前停止训练(Early Stopping),避免出现过拟合现象 呢?我们可以通过观察验证指标的变化,来预测最适合的 Epoch 可能的位置。具体地,对 于分类问题,我们可以记录模型的验证准确率,并监控验证准确率的变化,当发现验证准 确率连续𝑛个 Epoch 没有下降时,可以预测可能已经达到了最适合的 Epoch 附近,从而提 前终止训练。

通过限制网络参数的稀疏性, 可以来约束网络的实际容量。引入范数

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

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

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 
# 图片缩放到 244x244 大小,这个大小根据网络设定自行调整 
x = tf.image.resize(x, [244, 244])

 # 图片逆时针旋转 180 度
x = tf.image.rot90(x,2)
# 随机水平翻转
x = tf.image.random_flip_left_right(x)
# 随机竖直翻转
x = tf.image.random_flip_up_down(x)

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

 

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

以实心网格所在的像素为参考点,它周边 欧式距离小于或等于 𝑘 的像素点以矩形网格表示,网格内的像素点重要性较高,网格外的

像素点较低。这个高宽为𝑘的窗口称为感受野(Receptive Field),它表征了每个像素对于中心 像素的重要性分布情况,网格内的像素才会被考虑,网格外的像素对于中心像素会被简单地忽略。

这种基于距离的重要性分布假设特性称为局部相关性,它只关注和自己距离较近的部 分节点,而忽略距离较远的节点。在这种重要性分布假设下,全连接层的连接模式变成了 如图 10.4 所示,输出节点𝑗只与以𝑗为中心的局部区域(感受野)相连接,与其它像素无连 接。

2014 年,Matthew D. Zeiler 等人 [5]尝试利用可视化的方法去理解卷积神经网络到底 学到了什么。通过将每层的特征图利用“反卷积”网络(Deconvolutional Network)映射回输 入图片,即可查看学到的特征分布,如图 10.32 所示。可以观察到,第二层的特征对应到 边、角、色彩等底层图像提取;第三层开始捕获到纹理这些中层特征;第四、五层呈现了 物体的部分特征,如小狗的脸部、鸟类的脚部等高层特征。通过这些可视化的手段,我们 可以一定程度上感受卷积神经网络的特征学习过程。

图片数据的识别过程一般认为也是表示学习(Representation Learning)的过程,从接受到 的原始像素特征开始,逐渐提取边缘、角点等底层特征,再到纹理等中层特征,再到头 部、物体部件等高层特征,最后的网络层基于这些学习到的抽象特征表示(Representation) 做分类逻辑的学习。学习到的特征越高层、越准确,就越有利于分类器的分类,从而获得 较好的性能。从表示学习的角度来理解,卷积神经网络通过层层堆叠来逐层提取特征,网 络训练的过程可以看成特征的学习过程,基于学习到的高层抽象特征可以方便地进行分类 任务。

应用表示学习的思想,训练好的卷积神经网络往往能够学习到较好的特征,这种特征 的提取方法一般是通用的。比如在猫、狗任务上学习到头、脚、身躯等特征的表示,在其 它动物上也能够一定程度上使用。基于这种思想,可以将在任务 A 上训练好的深层神经网 络的前面数个特征提取层迁移到任务 B 上,只需要训练任务 B 的分类逻辑(表现为网络的 最末数层),即可取得非常好的效果,这种方式是迁移学习的一种,从神经网络角度也称为 网络微调(Fine-tuning)。

可以观察到,通过循环移动感受野的方式并没有改变网络层可导性,同时梯度的推导也并 不复杂,只是当网络层数增大以后,人工梯度推导将变得十分的繁琐。不过不需要担心, 深度学习框架可以帮我们自动完成所有参数的梯度计算与更新,我们只需要设计好网络结 构即可。

 

参数标准化很难从理论 层面解释透彻

网络层输入𝑥分布相近,并且分布在较小范围内时(如 0 附近),更有利于函数的优化。

那么如何保证输入𝑥的分布相近呢?数据标准化可以实现此目的,通过数据标准化操作可以将数据𝑥映射到𝑥̂:

\hat{x} = \frac{x - \mu _{r}}{\sqrt{\sigma _{r}^{2} + \epsilon }}     公式1

\hat{x} = \frac{x - \mu _{r}}{\sqrt{\sigma _{r}^{2} + \epsilon }}

\mu _{r}\sigma _{r}^{2}来自统计的所有数据的均值和方差。\epsilon是为防止出现除 0 错误而设置的较小数字,如 e−8。

在基于 Batch 的训练阶段,如何获取每个网络层所有输入的统计数据𝜇𝑟、𝜎𝑟2呢?

\mu _{B} = \frac{1}{m}\sum_{m}^{i = 1}x_{i}     公式2

\sigma _{B}^{2} = \frac{1}{m}\sum_{m}^{i = 1}(x_{i} - \mu _{B})^{2}    公式3

所以在训练阶段,通过公式1标准化输入,并记录每个Batch的期望与方差,

实际上,为了提高 BN 层的表达能力,BN 层作者引入了“scale and shift”技巧,将𝑥̂变量再次映射变换:

𝑥̃ = 𝑥̂ ∙ 𝛾 + 𝛽

𝛾参数实现对标准化后的𝑥̂再次进行缩放,𝛽参数实现对标准化的𝑥̂进行平移,不同的 是,𝛾、𝛽参数均由反向传播算法自动优化,实现网络层“按需”缩放平移数据的分布的目的。

训练阶段:

𝜇𝑟 ←momentum∙𝜇𝑟 +( −momentum)∙𝜇𝐵

𝜎𝑟2 ← momentum ∙ 𝜎𝑟2 + ( − momentum) ∙ 𝜎𝐵2

迭代更新全局训练数据的统计值𝜇𝑟和𝜎𝑟2,其中 momentum 是需要设置一个超参数,用于平 衡𝜇𝑟、𝜎𝑟2的更新幅度:当momentum= 时,𝜇𝑟和𝜎𝑟2直接被设置为最新一个Batch的𝜇𝐵 和𝜎𝐵2;当momentum= 时,𝜇𝑟和𝜎𝑟2保持不变,忽略最新一个Batch的𝜇𝐵和𝜎𝐵2,在 TensorFlow 中,momentum 默认设置为 0.99。

测试阶段:

计算输出𝑥̃ ,其中𝜇 、𝜎 2、𝛾、𝛽均来自训练阶段统计或优化的结果,在测试阶段直接使用,并不会更新这些参数。

普通的卷积层为了减少网络的参数量,卷积核的设计通常选择较小的 × 和3×3感受 野大小。小卷积核使得网络提取特征时的感受野区域有限,但是增大感受野的区域又会增 加网络的参数量和计算代价,因此需要权衡设计。

空洞卷积(Dilated/Atrous Convolution)的提出较好地解决这个问题,空洞卷积在普通卷 积的感受野上增加一个 Dilation Rate 参数,用于控制感受野区域的采样步长,如下图 10.51 所示:当感受野的采样步长 Dilation Rate 为 1 时,每个感受野采样点之间的距离为 1,此时的空洞卷积退化为普通的卷积;当 Dilation Rate 为 2 时,感受野每 2 个单元采样一 个点,如图 10.51 中间的绿色方框中绿色格子所示,每个采样格子之间的距离为 2;同样 的方法,图 10.51 右边的 Dilation Rate 为 3,采样步长为 3。尽管 Dilation Rate 的增大会使 得感受野区域增大,但是实际参与运算的点数仍然保持不变。

以输入为单通道的 × 张量,单个3×3卷积核为例,如下图10.52所示。在初始位 置,感受野从最上、最右位置开始采样,每隔一个点采样一次,共采集 9 个数据点,如图 10.52 中绿色方框所示。这 9 个数据点与卷积核相乘运算,写入输出张量的对应位置。

 

卷积核窗口按着步长为𝑠 = 向右移动一个单位,如图 10.53 所示,同样进行隔点采 样,共采样 9 个数据点,与卷积核完成相乘累加运算,写入输出张量对应位置,直至卷积 核移动至最下方、最右边位置。需要注意区分的是,卷积核窗口的移动步长𝑠和感受野区域 的采样步长 Dilation Rate 是不同的概念。

空洞卷积在不增加网络参数的条件下,提供了更大的感受野窗口。但是在使用空洞卷 积设置网络模型时,需要精心设计 Dilation Rate 参数来避免出现网格效应,同时较大的 Dilation Rate 参数并不利于小物体的检测、语义分割等任务。

转置卷积通过在输入之间填充大量的 padding 来 实现输出高宽大于输入高宽的效果,从而实现向上采样的目的,

转置卷积并不是普通卷积的逆过程,但是二者之间有一定的联系,同时转置卷积也是 基于普通卷积实现的。在相同的设定下,输入𝒙经过普通卷积运算后得到𝒐 = Conv(𝒙),我 们将𝒐送入转置卷积运算后,得到𝒙′ = ConvTranspose(𝒐),其中𝒙′ ≠ 𝒙,但是𝒙′与𝒙形状相 同。

深度残差网络并没有增加新的网络层类型,只是通过在输入和输出之间添加一条 Skip Connection,因此并没有针对 ResNet 的底层实现。在 TensorFlow 中通过调用普通卷积层即 可实现残差模块。

循环神经网络:

卷积神经网络利用数据的局部相关性和权值共享的思想大大减少了网络的参数量,非 常适合于图片这种具有空间(Spatial)局部相关性的数据,已经被成功地应用到计算机视觉领 域的一系列任务上。自然界的信号除了具有空间维度之外,还有一个时间(Temporal)维度。 具有时间维度的信号非常常见,比如我们正在阅读的文本、说话时发出的语音信号、随着 时间变化的股市参数等。这类数据并不一定具有局部相关性,同时数据在时间维度上的长 度也是可变的,卷积神经网络并不擅长处理此类数据。

那么如何解决这一类信号的分析、识别等问题是将人工智能推向通用人工智能路上必 须解决的一项任务。本章将要介绍的循环神经网络可以较好地解决此类问题。在介绍循环 神经网络之前,首先我们来介绍对于具有时间先后顺序的数据的表示方法。

具有先后顺序的数据一般叫作序列(Sequence),比如随时间而变化的商品价格数据就是 非常典型的序列。

我们已经知道神经网络本质上是一系列的矩阵相乘、相加等数学运算,它并 不能够直接处理字符串类型的数据。如果希望神经网络能够用于自然语言处理任务,那么 怎么把单词或字符转化为数值就变得尤为关键。接下来我们主要探讨文本序列的表示方 法,其他非数值类型的信号可以参考文本序列的表示方法。

我们把文字编码为数值的过程叫作 Word Embedding。One-hot 的编码方式实现 Word Embedding 简单直观,编码过程不需要学习和训练。但是 One-hot 编码的向量是高维度而 且极其稀疏的,大量的位置为 0,计算效率较低,同时也不利于神经网络的训练。

从语义 角度来讲,One-hot 编码还有一个严重的问题,它忽略了单词先天具有的语义相关性。举个 例子,对于单词“like”、“dislike”、“Rome”、“Paris”来说,“like”和“dislike”在语义角 度就强相关,它们都表示喜欢的程度;“Rome”和“Paris”同样也是强相关,他们都表示 欧洲的两个地点。对于一组这样的单词来说,如果采用 One-hot 编码,得到的向量之间没 有相关性,不能很好地体现原有文字的语义相关度,因此 One-hot 编码具有明显的缺陷。

在自然语言处理领域,有专门的一个研究方向在探索如何学习到单词的表示向量(Word Vector),使得语义层面的相关性能够很好地通过 Word Vector 体现出来。一个衡量词向量之 间相关度的方法就是余弦相关度(Cosine similarity):

similarity(a, b) = cos(\theta ) = \frac{a \cdot b}{|a|\cdot|b|}

similarity(a, b) = cos(\theta ) = \frac{a \cdot b}{|a|\cdot|b|}

其中𝒂和𝒃代表了两个词向量。下图演示了单词“France”和“Italy”的相似度,以及单 词“ball”和“crocodile”的相似度,𝜃为两个词向量之间的夹角。可以看到cos(𝜃)较好地 反映了语义相关性。

所以词向量是怎么来的?

在神经网络中,单词的表示向量可以直接通过训练的方式得到,我们把单词的表示层 叫作 Embedding 层。Embedding 层负责把单词编码为某个词向量𝒗,它接受的是采用数字 编码的单词编号𝑖,如 2 表示“I”,3 表示“me”等,系统总单词数量记为𝑁vocab,输出长 度为𝑛的向量𝒗: 𝒗 = 𝑓 (𝑖|𝑁 , 𝑛)

Embedding 层的查询表是随机初始化的,需要从零开始训练。实际上,我们可以使用 预训练的 Word Embedding 模型来得到单词的表示方法,基于预训练模型的词向量相当于 迁移了整个语义空间的知识,往往能得到更好的性能。

目前应用的比较广泛的预训练模型有 Word2Vec 和 GloVe 等。它们已经在海量语料库 训练得到了较好的词向量表示方法,并可以直接导出学习到的词向量表,方便迁移到其它 任务。

那么如何使用这些预训练的词向量模型来帮助提升 NLP 任务的性能?非常简单,对于 Embedding 层,不再采用随机初始化的方式,而是利用我们已经预训练好的模型参数去初始化 Embedding 层的查询表。

经过预训练的词向量模型初始化的 Embedding 层可以设置为不参与训练:net.trainable = False,那么预训练的词向量就直接应用到此特定任务上;如果希望能够学到区别于预训 练词向量模型不同的表示方法,那么可以把 Embedding 层包含进反向传播算法中去,利用 梯度下降来微调单词表示方法。

现在我们来考虑如何处理序列信号,以文本序列为例,考虑一个句子:

“I hate this boring movie”

通过 Embedding 层,可以将它转换为 shape 为[𝑏, 𝑠, 𝑛]的张量,𝑏为句子数量,𝑠为句子长 度,𝑛为词向量长度。上述句子可以表示为 shape 为[1,5,10]的张量,其中 5 代表句子单词 长度,10 表示词向量长度。

情感分类任务通过分析给出的文本序列,提炼出文本数据表达的整 体语义特征,从而预测输入文本的情感类型:正面评价或者负面评价。从分类角度来看, 情感分类问题就是一个简单的二分类问题,与图片分类不一样的是,由于输入是文本序 列,传统的卷积神经网络并不能取得很好的效果。

首先我们想到的是,对于每个词向量,分别使用一个全连接层网络

𝒐 = 𝜎(𝑾𝑡𝒙𝑡 + 𝒃𝑡)

提取语义特征,如图 11.4 所示,各个单词的词向量通过𝑠个全连接层分类网络 1 提取每个 单词的特征,所有单词的特征最后合并,并通过分类网络 2 输出序列的类别概率分布,对 于长度为𝑠的句子来说,至少需要𝑠个全网

络层。

缺点:

网络参数量是相当可观的,内存占用和计算代价较高,同时由于每个序列的长度𝑠并不 相同,网络结构是动态变化的

每个全连接层子网络𝑾𝑖和𝒃𝑖只能感受当前词向量的输入,并不能感知之前和之后的语 境信息,导致句子整体语义的缺失,每个子网络只能根据自己的输入来提取高层特 征,有如管中窥豹。

所以需要去解决。

上面𝑠个全连接层的网络并没有实现权值同享。我们尝试将这𝑠个网络层 参数共享,这样其实相当于使用一个全连接网络来提取所有单词的特征信息

通过权值共享后,参数量大大减少,网络训练变得更加稳定高效。但是,这种网络结 构并没有考虑序列之间的先后顺序,将词向量打乱次序仍然能获得相同的输出,无法获取 有效的全局语义信息。

如何赋予网络提取整体语义特征的能力呢?或者说,如何让网络能够按序提取词向量 的语义信息,并累积成整个句子的全局语义信息呢?我们想到了内存(Memory)机制。如果 网络能够提供一个单独的内存变量,每次提取词向量的特征并刷新内存变量,直至最后一 个输入完成,此时的内存变量即存储了所有序列的语义特征,并且由于输入序列之间的先 后顺序,使得内存变量内容与序列顺序紧密关联。

 

我们将上述Memory机制实现为一个状态张量,如图11.6所示,除了原来的𝑾 参数共享外,这里额外增加了一个𝑾 参数,每个时间戳𝑡上状态张量 刷新机制为:

h𝑡=𝜎(𝑾 𝒙𝑡+𝑾 𝑡−1+𝒃)

其中状态张量 为初始的内存状态,可以初始化为全0,经过𝑠个词向量的输入后得到网络最终的状态张量 𝑠, 𝑠较好地代表了句子的全局语义信息,基于 𝑠通过某个全连接层分类器即可完成情感分类任务。

通过一步步地探索,我们最终提出了一种“新型”的网络结构,如图 11.7 所示,在每个时间戳𝑡,网络层接受当前时间戳的输入𝒙𝑡和上一个时间戳的网络状态向量 h𝑡−1,变换后得到当前时间戳的新状态向量 ,并写入内存状态中,其中𝑓 代表了网络的运算逻辑,𝜃为网络参数集。

在循环神经网络中,激活函数更多地采用 tanh 函数,并且可 以选择不使用偏执𝒃来进一步减少参数量。状态向量 𝑡可以直接用作输出,即𝒐𝑡 = h𝑡,也 可以对 𝑡做一个简单的线性变换𝒐𝑡 = 𝑊 𝑜 𝑡后得到每个时间戳上的网络输出h𝒐𝑡。

需要注意的是,在 TensorFlow 中,RNN 表示通用意义上的循环神经网络,对于我们 目前介绍的基础循环神经网络,它一般叫做 SimpleRNN。SimpleRNN 与 SimpleRNNCell 的 区别在于,带 Cell 的层仅仅是完成了一个时间戳的前向运算,不带 Cell 的层一般是基于 Cell 层实现的,它在内部已经完成了多个时间戳的循环运算,因此使用起来更为方便快 捷。

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于TensorFlow欠拟合过拟合Python示例: 首先,我们需要导入必要的库和数据集: ```python import tensorflow as tf from tensorflow import keras import numpy as np # 加载数据集 (x_train, y_train), (x_test, y_test) = keras.datasets.boston_housing.load_data(seed=42) ``` 接下来,我们需要对数据进行预处理: ```python # 标准化数据 mean = x_train.mean(axis=0) std = x_train.std(axis=0) x_train = (x_train - mean) / std x_test = (x_test - mean) / std ``` 然后,我们可以定义一个函数来构建模型: ```python def build_model(): model = keras.Sequential([ keras.layers.Dense(64, activation='relu', input_shape=(x_train.shape[1],)), keras.layers.Dense(64, activation='relu'), keras.layers.Dense(1) ]) optimizer = tf.keras.optimizers.RMSprop(0.001) model.compile(loss='mse', optimizer=optimizer, metrics=['mae']) return model ``` 接下来,我们可以训练模型并绘制训练和验证的损失曲线: ```python # 构建模型 model = build_model() # 训练模型 history = model.fit(x_train, y_train, epochs=500, validation_split=0.2, verbose=0) # 绘制训练和验证的损失曲线 import matplotlib.pyplot as plt def plot_history(history): plt.figure() plt.xlabel('Epoch') plt.ylabel('Mean Abs Error [$1000]') plt.plot(history.epoch, np.array(history.history['mae']), label='Train Loss') plt.plot(history.epoch, np.array(history.history['val_mae']), label = 'Val loss') plt.legend() plt.ylim([0,5]) plt.show() plot_history(history) ``` 最后,我们可以使用测试集评估模型的性能: ```python # 使用测试集评估模型的性能 test_loss, test_mae = model.evaluate(x_test, y_test, verbose=0) print('Test MAE: ${:,.2f}'.format(test_mae * 1000)) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值