1神经网络剖析
训练神经网络主要围绕以下四个方面
层,多个层组合成网络(或模型)。
输入数据和相应的目标。
损失函数,即用于学习的反馈信号。
优化器,决定学习过程如何进行。
多个层链接在一起组成了网络,将输入数据映射为预测值。然后损失函数将这些预测值与目标进行比较,得到损失值,用于衡量网络预测值与预期结果的匹配程度。优化器使用这个损失值来更新网络的权重。
1.1层:深度学习的基础组件
神经网络的基本数据结构是层。
层是一个数据处理模块,将一个或多个输入张量转换为一个或多个输出张量。有些层是无状态的,但大多数的层是有状态的,即层的权重(利用随机梯度下降学到的一个或多个张量,其中包含网络的知识)。
不同的张量格式与不同的数据处理类型需要用到不同的层。
1.简单的向量数据保存在形状为 (samples, features) 的 2D 张量中,通常用密集连接层[densely connected layer,也叫全连接层(fully connected layer)或密集层(dense layer),对应于 Keras 的 Dense 类]来处理。
2.序列数据保存在形状为 (samples, timesteps, features) 的 3D 张量中,通常用循环
层(recurrent layer,比如 Keras 的 LSTM 层)来处理。
3.图像数据保存在 4D 张量中,通常用二维卷积层(Keras 的 Conv2D )来处理。
在 Keras 中,构建深度学习模型就是将相互兼容的多个层拼接在一起,以建立有用的数据变换流程。这里层兼容性(layer compatibility)具体指的是每一层只接受特定形状的输入张量,并返回特定形状的输出张量,向模型中添加的层都会自动匹配输入层的形状。
第二层没有输入形状( input_shape )的参数,相反,它可以自动推导出输入形状等
于上一层的输出形状
1.2模型:层构成的网络
深度学习模型是层构成的有向无环图。最常见的例子就是层的线性堆叠,将单一输入映射
为单一输出。
常见的网络拓扑结构:
1.双分支(two-branch)网络
2.多头(multihead)网络
3.Inception 模块
在预先定义好的可能性空间中,利用反馈信号的指引来寻找输入数据的有用表示。
1.3损失函数与优化器:配置学习过程的关键
确定了网络架构后,需要选择:
1.损失函数(目标函数)——在训练过程中需要将其最小化(衡量当前任务是否已成功完成)
2.优化器——决定如何基于损失函数对网络进行更新(执行的是随机梯度下降(SGD)的某个变体)
梯度下降过程必须基于单个标量损失值。具有多个输出的神经网络可能具有多个损失函数(每个输出对应一个损失函数),这种网络,需要将所有损失函数取平均,变为一个标量值。
网络的目的是使损失尽可能最小化,如果目标函数与成功完成当前任务不完全相关,那么网络最终得到的结果可能会不符合预期。
对于二分类问题,你可以使用二元交叉熵(binary crossentropy)损
失函数;对于多分类问题,可以用分类交叉熵(categorical crossentropy)损失函数;对于回归
问题,可以用均方误差(mean-squared error)损失函数;对于序列学习问题,可以用联结主义
时序分类(CTC,connectionist temporal classification)损失函数,
2Keras简介
目的:快速实验
特性:
1.相同的代码可以在 CPU 或 GPU 上无缝切换运行。
2.具有用户友好的 API,便于快速开发深度学习模型的原型。
3.内置支持卷积网络(用于计算机视觉)、循环网络(用于序列处理)以及二者的任意组合。
4.支持任意网络架构:多输入或多输出模型、层共享、模型共享等,能构建任意深度学习模型,无论是生成式对抗网络还是神经图灵机。
5.Keras与所有版本的 Python 都兼容(从 Python 2.7 到 Python 3.6 都兼容)
Keras 是一个模型级(model-level)的库,为开发深度学习模型提供了高层次的构建模块,依赖于一个专门的、高度优化的张量库(后端引擎)来完成运算,以模块化的方式处理这个问题(见图 3-3),开发过程可以无缝切换后端
Keras 有三个后端实现:TensorFlow 后端(大部分深度学习任务的默认后端,应用最广泛,可扩展,而且可用于生产环境)、Theano 后端和微软认知工具包(CNTK,Microsoft cognitive toolkit)后端。
最常见的:单一损失函数
典型的 Keras 工作流程:
(1) 定义训练数据:输入张量和目标张量。
(2) 定义层组成的网络(或模型),将输入映射到目标。
(3) 配置学习过程:选择损失函数、优化器和需要监控的指标。
(4) 调用模型的 fit 方法在训练数据上进行迭代。
定义模型有两种方法:
一种是使用 Sequential 类(仅用于层的线性堆叠,这是目前最常见的网络架构)
另一种是函数式 API(functional API,用于层组成的有向无环图,构建任意形式的架构)。
利用函数式 API,可以操纵模型处理的数据张量,并将层应用于这个张量,好像这些层是函数一样
学习过程就是通过 fit() 方法将输入数据的 Numpy 数组(和对应的目标数据)传入模型
3建立深度学习工作站
Windows 用户,最简单的解决方案就是安装 Ubuntu双系统
笔记本(notebook):
是 Jupyter Notebook 应用生成的文件,可以在浏览器中编辑。
可以执行 Python 代码,具有丰富的文本编辑功能,可以对代码进行注释。
可以将冗长的实验代码拆分为可独立执行的短代码,这使得开发具有交互性
4二分类问题
将结果分为正面与负面两种情况
4.1数据集
为什么要将训练集和测试集分开?使模型面对未知数据也能很准确
与 MNIST 数据集一样,IMDB 数据集也内置于 Keras 库
train_labels 和 test_labels 都是 0 和 1 组成的列表,其中 0代表负面(negative),1 代表正面(positive)。
4.2准备数据
需要将列表转换为张量输入神经网络
转换方法有以下两种。
1.填充列表,使其具有相同的长度,再将列表转换成形状为 (samples, word_indices)
的整数张量,然后网络第一层使用能处理这种整数张量的层(即 Embedding 层)。
2.对列表进行 one-hot 编码,将其转换为 0 和 1 组成的向量。举个例子,序列 [3, 5] 将会
被转换为 10 000 维向量,只有索引为 3 和 5 的元素是 1,其余元素都是 0。然后网络第
一层可以用 Dense 层,它能够处理浮点数向量数据。
4.3构建网络
输入数据是向量,而标签是标量(1 和 0)
带有 relu 激活的全连接层( Dense )的简单堆叠在二分类问题上表现很好
传入 Dense 层的参数(16)是该层隐藏单元的个数。
一个隐藏单元(hidden unit)是该层表示空间的一个维度。
每个带有 relu 激活的 Dense 层都实现了下列张量运算:output = relu(dot(W, input) + b)
16 个隐藏单元对应的权重矩阵 W 的形状为 (input_dimension, 16) ,与 W 做点积相当于
将输入数据投影到 16 维表示空间中(然后再加上偏置向量 b 并应用 relu 运算)。
可以将表示空间的维度直观地理解为“网络学习内部表示时所拥有的自由度”。隐藏单元越多(即更高维的表示空间),网络越能够学到更加复杂的表示,但网络的计算代价也变得更大,而且可能会导致学到不好的模式(这种模式会提高训练数据上的性能,但不会提高测试数据上的性能)。
对于这种 Dense 层的堆叠,需要确定以下两个关键架构:
1.网络有多少层;
2.每层有多少个隐藏单元。
需要相信下列架构:
两个中间层,每层都有 16 个隐藏单元;
第三层输出一个标量,预测当前评论的情感。
中间层使用 relu 作为激活函数,最后一层使用 sigmoid 激活以输出一个 0~1 范围内的概率
值(表示样本的目标值等于 1 的可能性,即评论为正面的可能性)。
relu (rectified linear unit,整流线性单元)函数将所有负值归零
sigmoid 函数则将任意值“压缩”到 [0,1] 区间内,其输出值可以看作概率值。
激活函数
最后选择损失函数和优化器
由于是一个二分类问题,网络输出是一个概率值(网络最后一层使用 sigmoid 激活函数,仅包含一个单元),最好使用 binary_crossentropy (二元交叉熵)损失,还可以使用 mean_squared_error (均方误差)。但对于输出概率值的模型,交叉熵(是来自于信息论领域的概念,用于衡量概率分布之间的距离)往往是最好的选择。
例:用 rmsprop 优化器和 binary_crossentropy 损失函数来配置模型
上述代码将优化器、损失函数和指标作为字符串传入,这是因为 rmsprop 、 binary_
crossentropy 和 accuracy 都是 Keras 内置的一部分。有时希望配置自定义优化器的参数(可通过向 optimizer 参数传入一个优化器类实例来实现,如代码清单 3-5 所示)
或者传入自定义的损失函数或指标函数(后者可通过向 loss 和 metrics 参数传入函数对象来实现,如代码清单 3-6 所示)。
4.4验证
为了在训练过程中监控模型在前所未见的数据上的精度,需将原始训练数据留出样本作为验证集。
调用 model.fit() 返回一个 History 对象,有一个成员 history (一个字典)包含训练过程中所有数据
字典中包含 4 个条目,对应训练过程和验证过程中监控的指标。由于网络的随机初始化不同,结果可能会略有不同。
使用 Matplotlib 在同一张图上绘制训练损失和验证损失(见图 3-7)
以及训练精度和验证精度(见图 3-8)
训练损失每轮都在降低,训练精度每轮都在提升。这就是梯度下降优化的预期结果——你想要最小化的量随着每次迭代越来越小。但验证损失和验证精度并非如此:似乎在第四轮达到最佳值。
模型在训练数据上的表现越来越好,但在前所未见的数据上不一定表现得越来越好。准确地说,是过拟合(overfit):在第二轮之后,对训练数据过度优化,最终学到的表示仅针对于训练数据,无法泛化到训练集之外的数据。为了防止过拟合,你可以在 3 轮之后停止训练。
4.5生成结果
用 predict 方法来得到评论为正面的可能性大小,预测所需结果出现的可能性大小
4.6进一步实验
通过以下实验,可以确信前面选择的网络架构是非常合理的,虽然仍有改进的空间。
前面使用了两个隐藏层。你可以尝试使用一个或三个隐藏层,然后观察对验证精度和测
试精度的影响。
尝试使用更多或更少的隐藏单元,比如 32 个、64 个等。
尝试使用 mse 损失函数代替 binary_crossentropy 。
尝试使用 tanh 激活(这种激活在神经网络早期非常流行)代替 relu 。
4.7小结
应该从这个例子中学到的要点。
通常需要对原始数据进行大量预处理,以便将其转换为张量输入到神经网络中。单词序
列可以编码为二进制向量,但也有其他编码方式。
带有 relu 激活的 Dense 层堆叠,可以解决很多种问题(包括情感分类),你可能会经
常用到这种模型。
对于二分类问题(两个输出类别),网络的最后一层应该是只有一个单元并使用 sigmoid
激活的 Dense 层,网络输出应该是 0~1 范围内的标量,表示概率值。
对于二分类问题的 sigmoid 标量输出,你应该使用 binary_crossentropy 损失函数。
无论你的问题是什么, rmsprop 优化器通常都是足够好的选择。这一点你无须担心。
随着神经网络在训练数据上的表现越来越好,模型最终会过拟合,并在前所未见的数据
上得到越来越差的结果。一定要一直监控模型在训练集之外的数据上的性能。
5多分类问题
因为有多个类别,所以这是多分类(multiclass classification)问题的一个例子。
因为每个数据点只能划分到一个类别,所以,这是单标签、多分类(single-label, multiclass classification)问题的一个例子。
如果每个数据点可以划分到多个类别(主题),那它就是一个多标签、多分类(multilabel,
multiclass classification)问题。
5.1数据集
与 IMDB 和 MNIST 类似,路透社数据集也内置为 Keras 的一部分
5.2准备数据
将标签向量化有两种方法:
可以将标签列表转换为整数张量,或者使用 one-hot 编码。
one-hot 编码是分类数据广泛使用的一种格式,也叫分类编码(categorical encoding)。在这个例子中,标签的one-hot编码就是将每个标签表示为全零向量,只有标签索引对应的元素为 1。
5.3构建网络
对于 Dense 层的堆叠,每层只能访问上一层输出的信息。如果某一层丢失了与分类问题相关的一些信息,那么这些信息无法被后面的层找回,也就是说,每一层都可能成为信息瓶颈。维度较小的层可能成为信息瓶颈,无法区分类别,永久地丢失相关信息。
关于这个架构还应该注意
网络的最后一层是大小为 46 的 Dense 层。这意味着,对于每个输入样本,网络都会输
出一个 46 维向量。这个向量的每个元素(即每个维度)代表不同的输出类别。
最后一层使用了 softmax 激活。网络将输出在 46个不同输出类别上的概率分布——对于每一个输入样本,网络都会输出一个 46 维向量,其中 output[i] 是样本属于第 i 个类别的概率。46 个概率的总和为 1。
对于这个例子,最好的损失函数是 categorical_crossentropy (分类交叉熵)。它用于
衡量两个概率分布之间的距离,这里两个概率分布分别是网络输出的概率分布和标签的真实分
布。通过将这两个分布的距离最小化,训练网络可使输出结果尽可能接近真实标签。
5.4验证
5.5生成结果
5.6处理标签和损失的另一种方法
对于另一种编码方法,将其转换为整数张量,唯一需要改变的是损失函数的选择。对于代码清单 3-21 使用的损失函数 categorical_crossentropy ,标签应该遵循分类编码。对于整数标签,你应该使用
5.7中间层维度足够大的重要性
现在网络的验证精度下降的主要原因在于,将大量信息(这些信息足够恢复 46 个类别的分割超平面)压缩到维度很小的中间空间。网络能够将大部分必要信息塞入这个四维表示中,但并不是全部信息。
5.8进一步实验
尝试使用更多或更少的隐藏单元,比如 32 个、128 个等。
前面使用了两个隐藏层,现在尝试使用一个或三个隐藏层。
5.9小结
下面是这个例子中学到的要点。
如果要对 N 个类别的数据点进行分类,网络的最后一层应该是大小为 N 的 Dense 层。
对于单标签、多分类问题,网络的最后一层应该使用 softmax 激活,这样可以输出在 N
个输出类别上的概率分布。
这种问题的损失函数几乎总是应该使用分类交叉熵。它将网络输出的概率分布与目标的
真实分布之间的距离最小化。
处理多分类问题的标签有两种方法。
通过分类编码(也叫 one-hot 编码)对标签进行编码,然后使用 categorical_
crossentropy 作为损失函数。
将标签编码为整数,然后使用 sparse_categorical_crossentropy 损失函数。
如果你需要将数据划分到许多类别中,应该避免使用太小的中间层,以免在网络中造成
信息瓶颈。
6回归问题
不要将回归问题与 logistic 回归算法混为一谈。logistic 回归不是回归算法,而是分类算法。
6.1数据集
要预测 20 世纪 70 年代中期波士顿郊区房屋价格的中位数,已知当时郊区的一些数据点,比如犯罪率、当地房产税率等。本节用到的数据集包含的数据点相对较少,输入数据的每个特征(比如犯罪率)都有不同的取值范围
6.2准备数据
将取值范围差异很大的数据输入到神经网络中,这是有问题的。网络可能会自动适应这种
取值范围不同的数据,但学习肯定变得更加困难。对于这种数据,普遍采用的最佳实践是对每
个特征做标准化,即对于输入数据的每个特征(输入数据矩阵中的列),减去特征平均值,再除
以标准差,这样得到的特征平均值为 0,标准差为 1。用 Numpy 可以很容易实现标准化。
注意,用于测试数据标准化的均值和标准差都是在训练数据上计算得到的。在工作流程中,
你不能使用在测试数据上计算得到的任何结果
6.3构建网络
由于样本数量很少,将使用一个非常小的网络,其中包含两个隐藏层,每层有 64 个单元。
一般来说,训练数据越少,过拟合会越严重,而较小的网络可以降低过拟合。
网络的最后一层只有一个单元,没有激活,是一个线性层。这是标量回归(标量回归是预
测单一连续值的回归)的典型设置。添加激活函数将会限制输出范围。例如,如果向最后一层
添加 sigmoid 激活函数,网络只能学会预测 0~1 范围内的值。这里最后一层是纯线性的,所以
网络可以学会预测任意范围内的值。
注意,编译网络用的是 mse 损失函数,即均方误差(MSE,mean squared error),预测值与目标值之差的平方。这是回归问题常用的损失函数。
在训练过程中还监控一个新指标:平均绝对误差(MAE,mean absolute error)。它是预测值与目标值之差的绝对值。
6.4验证
验证分数可能会有很大波动,这取决于你所选择的验证集和训练集,验证集的划分方式可能会造成验证分数上有很大的方差,这样就无法对模型进行可靠的评估。
在这种情况下,最佳做法是使用 K 折交叉验证(见图 3-11)。将可用数据划分为 K个分区(K 通常取 4 或 5),实例化 K 个相同的模型,将每个模型在 K-1 个分区上训练,并在剩下的一个分区上进行评估。模型的验证分数等于 K 个验证分数的平均值。
每次运行模型得到的验证分数有很大差异,平均分数是比单一分数更可靠的指标——这就是 K 折交叉验证的关键。让训练时间更长一点,为了记录模型在每轮的表现,修改训练循环,以保存每轮的验证分数记录。
纵轴的范围较大,且数据方差相对较大,所以难以看清这张图的规律。重新绘制一张图。
删除前 10 个数据点,因为它们的取值范围与曲线上的其他点不同。
将每个数据点替换为前面数据点的指数移动平均值,以得到光滑的曲线
6.5小结
下面是你应该从这个例子中学到的要点。
回归问题使用的损失函数与分类问题不同。回归常用的损失函数是均方误差(MSE)。
同样,回归问题使用的评估指标也与分类问题不同。显而易见,精度的概念不适用于回
归问题。常见的回归指标是平均绝对误差(MAE)。
如果输入数据的特征具有不同的取值范围,应该先进行预处理,对每个特征单独进行
缩放。
如果可用的数据很少,使用 K 折验证可以可靠地评估模型。
如果可用的训练数据很少,最好使用隐藏层较少(通常只有一到两个)的小型网络,以
避免严重的过拟合。
总结
现在可以处理关于向量数据最常见的机器学习任务:二分类问题、多分类问题和标
量回归问题。
学到的要点:
在将原始数据输入神经网络之前,通常需要对其进行预处理。
如果数据特征具有不同的取值范围,那么需要进行预处理,将每个特征单独缩放。
随着训练的进行,神经网络最终会过拟合,并在前所未见的数据上得到更差的结果。
如果训练数据不是很多,应该使用只有一两个隐藏层的小型网络,以避免严重的过拟合。
如果数据被分为多个类别,那么中间层过小可能会导致信息瓶颈。
回归问题使用的损失函数和评估指标都与分类问题不同。
如果要处理的数据很少,K 折验证有助于可靠地评估模型。