一、深度学习概述
神经网路的核心组件是层(layer),它是一种数据处理模,可以将层看成数据过滤器,进去一些数据,出来的数据变得更加有用。大多数深度学习都是简单的层简单的成链接起来,从而实现间接式的数据蒸馏。
层是通过数据张量的运算实现,因此神经网络完全由一系列的张量运算组成,而这些张量运算都是输入数据的几何变换。可以将神经网路解释为高纬空间中非常复杂的几何变换,这种几何变化可以通过许多步骤来实现。
想象有两张彩纸:一张红色,一张蓝色。将其中一张纸放在另一张上。现在讲两张纸一起揉成小球。这个皱巴巴的纸球就是你的输入数据,每张纸对应于分类问题中的一个类别,神经网络(或者任何机器学习模型)要做的就是找到可以让纸球恢复平整的变换,从而能够再次让两个类别明确可分。通过深度学习,这一过程可以用三维空间中一系列简单的变换来实现,比如你用手指对纸球做的变换,每次做一个动作。神经网络将复杂的几何变换逐步分解 为一长串基本的几何变换,这与人类展开纸球所采取的策略大致相同。深度网络的每一层都通过变换使数据解开一点点——许多层堆叠在一起,可以实现非常复杂的解开过程。
二、神经网络的“引擎”:基于梯度的优化
每个神经层都用下述方法对输入数据进行变换:
output = relu(dot(W, input) + b)
在这个表达式中,W 和 b 都是张量,均为该层的属性。它们被称为该层的权重(weight)或可训练参数(trainable parameter),这些权重包含网络从观察训练数据中学到的信息。
一开始这些权重矩阵取较小的随机值,这一步叫作随机初始化(random initialization)。 W和b都是随机的,relu(dot(W, input) + b)肯定不会得到任何有用的表示。虽然得到的表示是没有意义的,但这是一个起点。下一步则是根据反馈信号逐渐调节这些权重。这个逐渐调节的过程叫作训练,也就是机器学习中的学习。
上述过程发生在一个训练循环(training loop)内,其具体过程如下。必要时一直重复这些步骤。
(1) 抽取训练样本 x 和对应目标 y 组成的数据批量。
(2) 在 x 上运行网络[这一步叫作前向传播(forward pass)],得到预测值 y_pred。
(3) 计算网络在这批数据上的损失,用于衡量 y_pred 和 y 之间的距离。
(4) 更新网络的所有权重,使网络在这批数据上的损失略微下降。
利用网络中所有运算都是可微(differentiable)的这一事实,计算损失相对于网络系数的梯度(gradient),然后向梯度的反方向改变系数,从而使损失降低。
假设有一个连续的光滑函数f(x) = y,将实数x映射为另一个实数y。由于函数是连续的,x 的微小变化只能导致 y 的微小变化——这就是函数连续性的直观解释。假设 x 增大了一个很小的因子 epsilon_x,这导致 y 也发生了很小的变化,
即 epsilon_y:f(x + epsilon_x) = y + epsilon_y
此外,由于函数是光滑的(即函数曲线没有突变的角度),在某个点 p 附近,如果 epsilon_x足够小,就可以将f近似为斜率为a的线性函数,这样epsilon_y就变成了a * epsilon_x:
f(x + epsilon_x) = y + a * epsilon_x
显然,只有在 x 足够接近 p 时,这个线性近似才有效。
斜率 a 被称为 f 在 p 点的导数(derivative)。如果 a 是负的,说明 x 在 p 点附近的微小变 化将导致 f(x) 减小;如果 a 是正的,那么 x 的微小变化将导致 f(x) 增大。 此外,a 的绝对值(导数大小)表示增大或减小的速度快慢。
如果你想要将 x 改变一个小因子 epsilon_x,目的是将 f(x) 最小化,并且知道 f 的导数, 那么问题解决了:导数完全描述了改变 x 后 f(x) 会如何变化。如果你希望减小 f(x) 的值,只需将 x 沿着导数的反方向移动一小步。
梯度(gradient)是张量运算的导数。它是导数这一概念向多元函数导数的推广。
沿着梯度的反方向更新权重,损失每次都会变小一点。
-
(1) 抽取训练样本 x 和对应目标 y 组成的数据批量。
-
(2) 在 x 上运行网络,得到预测值 y_pred。
-
(3) 计算网络在这批数据上的损失,用于衡量 y_pred 和 y 之间的距离。
-
(4) 计算损失相对于网络参数的梯度[一次反向传播(backward pass)]。
-
(5) 将参数沿着梯度的反方向移动一点,比如W -= step * gradient,从而使这批数据上的损失减小一点。
这很简单!刚刚描述的方法叫作小批量随机梯度下降(mini-batch stochastic gradient descent,又称为小批量 SGD)。
如你所见,直观上来看,为 step 因子选取合适的值是很重要的。如果取值太小,则沿着曲线的下降需要很多次迭代,而且可能会陷入局部极小点。如果取值太大,则更新权重值之后可能会出现在曲线上完全随机的位置。
注意,小批量 SGD 算法的一个变体是每次迭代时只抽取一个样本和目标,而不是抽取一批 数据。这叫作真 SGD(有别于小批量 SGD)。还有另一种极端,每一次迭代都在所有数据上 运行,这叫作批量 SGD。这样做的话,每次更新都更加准确,但计算代价也高得多。这两个极 端之间的有效折中则是选择合理的批量大小。
此外,SGD 还有多种变体,其区别在于计算下一次权重更新时还要考虑上一次权重更新, 而不是仅仅考虑当前梯度值,比如带动量的 SGD、Adagrad、RMSProp 等变体。这些变体被称 为优化方法(optimization method)或优化器(optimizer)。其中动量的概念尤其值得关注,它在 许多变体中都有应用。动量解决了 SGD 的两个问题:收敛速度和局部极小点。
根据微积分的知识,这种函数链可以利用下面这个恒等式进行求导,它称为链式法则(chain rule):(f(g(x)))' = f'(g(x)) * g'(x)。将链式法则应用于神经网络梯度值的计算,得 到的算法叫作反向传播(backpropagation,有时也叫反式微分,reverse-mode differentiation)。
三、关键网络架构
以下三种网络架构:密集连接网络、卷积网络和循环网络。每种类型的网络都针对于特定的输入模式,网络架构(密集网络、卷积网络、循环网络)中包含对数据结构的假设, 即搜索良好模型所在的假设空间。某种架构能否解决某个问题,这完全取决于数据结构与网络架构的假设之间的匹配度。
快速看一下输入模式与适当 的网络架构之间的对应关系。
- 向量数据: 密集连接网络(Dense 层)。
- 图像数据: 二维卷积神经网络。声音数据(比如波形):一维卷积神经网络(首选)或循环神经网络。
- 文本数据: 一维卷积神经网络(首选)或循环神经网络。
- 时间序列数据: 循环神经网络(首选)或一维卷积神经网络。
- 其他类型的序列数据: 循环神经网络或一维卷积神经网络。如果数据顺序非常重要(比如时间序列,但文本不是),那么首选循环神经网络。
- 视频数据: 三维卷积神经网络(如果你需要捕捉运动效果),或者帧级的二维神经网络(用于特征提取)+循环神经网络或者一维卷积神经网络(用于处理得到的序列)。
- 立体数据: 三纬卷积神经网络。
1、密集连接网络
请记住:对于二分类问题(binary classification),层堆叠的最后一层是使用sigmoid激活 且只有一个单元的 Dense 层,并使用 binary_crossentropy 作为损失。目标应该是 0 或 1。
from keras.datasets import imdb
# 下载数据集
(train_data,train_labels),(test_data,test_labels) = imdb.load_data(num_words=10000)
#将整数序列编码为二进制矩阵
import numpy as np
def vectorize_sequences( sequences, dimension = 10000):
results = np.zeros((len(sequences),dimension))
for i,sequence in enumerate(sequences):
results[i,sequence] = 1.
return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val)
)
#模型过拟合,选择epoch=4
history = model.fit(partial_x_train,
partial_y_train,
epochs=4,
batch_size=512,
validation_data=(x_val, y_val)
)
result = model.evaluate(x_test, y_test)
对于单标签多分类问题(single-label categorical classification,每个样本只有一个类别,不会超过一个),层堆叠的最后一层是一个 Dense 层,它使用 softmax 激活,其单元个数等于类 别个数。如果目标是 one-hot 编码的,使用categorical_crossentropy 作为损失;如果目标是整数,那么使用 sparse_categorical_crossentropy 作为损失。
model = models.Sequential()
model.add(layers.Dense(32, activation='relu', input_shape=(num_input_features,)))
model.add(layers.Dense(32, activation='relu')) model.add(layers.Dense(num_classes, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
对于多标签多分类问题(multilabel categorical classification,每个样本可以有多个类别), 层堆叠的最后一层是一个 Dense 层,它使用 sigmoid 激活,其单元个数等于类别个数,并使 用 binary_crossentropy 作为损失。目标应该是 k-hot 编码的。
model = models.Sequential()
model.add(layers.Dense(32, activation='relu', input_shape=(num_input_features,)))
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(num_classes, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy')
对于连续值向量的回归(regression)问题,层堆叠的最后一层是一个不带激活 Dense 层, 其单元个数等于你要预测的值的个数(通常只有一个值,比如房价)。有几种损失可用于回归问 题,最常见的是 mean_squared_error(均方误差,MSE)和 mean_absolute_error(平均绝对误差,MAE)。
model = models.Sequential()
model.add(layers.Dense(32, activation='relu', input_shape=(num_input_features,)))
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(num_values))
model.compile(optimizer='rmsprop', loss='mse')
2、卷积神经网络
卷积层能够查看空间局部模式,其方法是对输入张量的不同空间位置(图块)应用相同的 几何变换。这样得到的表示具有平移不变性,这使得卷积层能够高效利用数据,并且能够高度 模块化。这个想法适用于任何维度的空间,包括一维(序列)、二维(图像)、三维(立体数据)等。 你可以使用 Conv1D 层来处理序列(特别是文本,它对时间序列的效果并不好,因为时间序列 通常不满足平移不变的假设),使用 Conv2D 层来处理图像,使用 Conv3D 层来处理立体数据。
卷积神经网络或卷积网络是卷积层和最大池化层的堆叠。池化层可以对数据进行空间下采样, 这么做有两个目的:随着特征数量的增大,我们需要让特征图的尺寸保持在合理范围内;让后面 的卷积层能够“看到”输入中更大的空间范围。卷积神经网络的最后通常是一个 Flatten 运算 或全局池化层,将空间特征图转换为向量,然后再是 Dense 层,用于实现分类或回归。
注意,大部分(或者全部)?通卷积很可能不久后会被深度可分离卷积(depthwise separable convolution,SeparableConv2D 层)所替代,后者与前者等效,但速度更快、表示效率更高。 对于三维、二维和一维的输入来说都是如此。如果你从头开始构建一个新网络,那么一定要使用 深度可分离卷积。SeparableConv2D 层可直接替代 Conv2D 层,得到一个更小、更快的网络, 在任务上的表现也更好。
model = models.Sequential()
model.add(layers.SeparableConv2D(32, 3, activation='relu',
input_shape=(height, width, channels)))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu')) model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu')) model.add(layers.GlobalAveragePooling2D())
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(num_classes, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
3. 循环神经网络
循环神经网络(RNN,recurrent neural network)的工作原理是,对输入序列每次处理一个 时间步,并且自始至终保存一个状态(state,这个状态通常是一个向量或一组向量,即状态几 何空间中的点)。如果序列中的模式不具有时间平移不变性(比如时间序列数据,最近的过去比 遥远的过去更加重要),那么应该优先使用循环神经网络,而不是一维卷积神经网络。
Keras 中有三种 RNN 层:SimpleRNN、GRU 和 LSTM。对于大多数实际用途,你应该使用GRU 或 LSTM。两者中 LSTM 更加强大,计算代价也更高。你可以将 GRU 看作是一种更简单、计 算代价更小的替代方法。
想要将多个 RNN 层逐个堆叠在一起,最后一层之前的每一层都应该返回输出的完整序列 (每个输入时间步都对应一个输出时间步)。如果你不再堆叠更多的 RNN 层,那么通常只返回最后一个输出,其中包含关于整个序列的信息。
#下面是一个单层 RNN 层,用于向量序列的二分类。
model = models.Sequential()
model.add(layers.LSTM(32, input_shape=(num_timesteps, num_features))) model.add(layers.Dense(num_classes, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy')
#下面是 RNN 层的堆叠,用于向量序列的二分类。
model = models.Sequential()
model.add(layers.LSTM(32, return_sequences=True,
input_shape=(num_timesteps, num_features)))
model.add(layers.LSTM(32, return_sequences=True))
model.add(layers.LSTM(32))
model.add(layers.Dense(num_classes, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy')
四、神经网络的优化