关键网络架构
深度学习有4种类型的网络架构:密集连接网络、卷积神经网络、循环神经网络和Transformer。每种类型的模型都是针对特定的输入模式,网络架构包含了关于数据结构的假设,即模型搜索的假设空间。某种架构能否解决某个问题,完全取决于问题的数据结构与所选的网络架构假设之间是否匹配。
这些不同类型的网络可以很容易组合起来,实现更大的多模式模型,就像拼乐高积木一样。某种程度上来说,深度学习的层就是信息处理领域的乐高积木。下面列出了输入模式与网络架构之间的对应关系。
向量数据:密集连接网络(Dense层)。
图像数据:二维卷积神经网络。
序列数据:对于时间序列,选择循环神经网络(RNN);对于离散序列(比如单词序列),选择Transformer。一维卷积神经网络也可用于平移不变的连续序列数据,比如鸟鸣波形。
视频数据:三维卷积神经网络(假设需要捕捉运动效果),或者帧级二维卷积神经网络(用于特征提取)再加上序列处理模型。立体数据:三维卷积神经网络。下面来快速回顾一下每种网络架构的特点。
密集连接网络
密集连接网络是Dense层的堆叠,用于处理向量数据(每个样本都是一个数值向量或分类向量)。这类网络假设输入特征中没有特定结构 :之所以叫密集连接,是因为Dense层的每个单元都与其他所有单元相连 。该层试图映射任意两个输入特征之间的关系,它与二维卷积层不同,后者仅关注局部关系。
密集连接网络最常用于分类数据(比如输入特征是属性的列表),如第波士顿房价数据集。它还用于大多数网络的最终分类或回归,比如卷积神经网络或循环神经网络,最后通常是一两个Dense层。
对于二分类问题,层堆叠的最后一层应该是使用sigmoid激活函数且只有一个单元的Dense层,并使用binary_crossentropy作为损失函数。目标应该是0或1。
python
from tensorflow import keras
from tensorflow.keras import layers
inputs = keras.Input(shape=(num_input_features,))
x = layers.Dense(32, activation="relu")(inputs)
x = layers.Dense(32, activation="relu")(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="binary_crossentropy")
对于单标签、多分类问题(每个样本只对应一个类别),层堆叠的最后一层应该是一个Dense层,它使用softmax激活函数,其单元个数等于类别个数。如果目标采用的是one-hot编码,则使用categorical_crossentropy作为损失函数;如果目标是整数,则使用sparse_categorical_crossentropy作为损失函数。
python
inputs = keras.Input(shape=(num_input_features,))
x = layers.Dense(32, activation="relu")(inputs)
x = layers.Dense(32, activation="relu")(x)
outputs = layers.Dense(num_classes, activation="softmax")(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="categorical_crossentropy")
对于连续值向量的回归问题,层堆叠的最后一层应该是不使用激活函数的Dense层,其单元个数等于要预测的值的个数(通常只有一个值,比如房价)。有几种损失函数可用于回归问题,最常用的是mean_squared_error(均方误差,MSE)。
python
inputs = keras.Input(shape=(num_input_features,))
x = layers.Dense(32, activation="relu")(inputs)
x = layers.Dense(32, activation="relu")(x)
outputs layers.Dense(num_values)(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="mse")
卷积神经网络
卷积层能够查看空间局部模式 ,其方法是对输入张量的不同空间位置(图块)应用相同的几何变换。这样得到的表示具有平移不变性,这使得卷积层能够高效利用数据,并且可以模块化。这个想法适用于任意维度,包括一维(连续序列)、二维(图像数据)、三维(立体数据)等。你可以使用Conv1D层来处理序列数据,使用Conv2D层来处理图像数据,使用Conv3D层来处理立体数据。你还可以使用深度可分离卷积层,比如SeparableConv2D层,它比卷积层更精简、更高效。
卷积神经网络是卷积层和最大汇聚层的堆叠。汇聚层可以对数据进行空间下采样,这样做有两个目的:随着特征数量增加,让特征图的尺寸保持在合理范围内;让后续卷积层能够"看到"输入中更大的空间范围。卷积神经网络的最后通常是Flatten运算或全局汇聚层,将空间特征图转换为向量,然后再使用Dense层实现分类或回归。典型的图像分类网络(本例是多分类)如下所示,其中用到了SeparableConv2D层。
python
inputs = keras.Input(shape=(height, width, channels))
x = layers.SeparableConv2D(32, 3, activation="relu")(inputs)
x = layers.SeparableConv2D(64, 3, activation="relu")(x)
x = layers.MaxPooling2D(2)(x)
x = layers.SeparableConv2D(64, 3, activation="relu")(x)
x = layers.SeparableConv2D(128, 3, activation="relu")(x)
x = layers.MaxPooling2D(2)(x)
x = layers.SeparableConv2D(64, 3, activation="relu")(x)
x = layers.SeparableConv2D(128, 3, activation="relu")(x)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(32, activation="relu")(x)
outputs = layers.Dense(num_classes, activation="softmax")(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="categorical_crossentropy")
在构建非常深的卷积神经网络时,通常会添加批量规范化和残差连接。这两种架构模式有助于梯度信息在网络中顺利传播。
循环神经网络
循环神经网络(RNN)的工作原理是,对输入序列每次处理一个时间步 ,并且始终保存一个状态 (这个状态通常是一个向量或一组向量)。如果序列中的模式不具有时间平移不变性(比如时间序列数据,最近的过去比遥远的过去更重要),那么应该优先使用循环神经网络,而不是一维卷积神经网络。
Keras中有3种循环层:SimpleRNN、GRU和LSTM。对于大多数实际用途,你应该使用GRU或LSTM。二者之中,LSTM更强大,计算代价也更大。你可以将GRU看作一种更简单、计算代价更小的替代方法。要将多个RNN层逐个堆叠,最后一层之前的每一层都应该返回完整的输出序列(每个输入时间步都对应一个输出时间步)。如果只有一个RNN层,则通常只返回最后一个输出,其中包含关于整个序列的信息。
下面是单一的RNN层,用于向量序列的二分类。
python
inputs = keras.Input(shape=(num_timesteps, num_features))
x = layers.LSTM(32)(inputs)
outputs = layers.Dense(num_classes, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="binary_crossentropy")
下面是RNN层的堆叠,用于向量序列的二分类。
python
inputs = keras.Input(shape=(num_timesteps, num_features))
x = layers.LSTM(32, return_sequences=True)(inputs)
x = layers.LSTM(32, return_sequences=True)(x)
x = layers.LSTM(32)(x)
outputs = layers.Dense(num_classes, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="binary_crossentropy")
Transformer
Transformer查看一组向量(比如词向量),并利用神经注意力将每个向量转化为一个具有上下文感知的表示 ,这个上下文由这组向量中的其他向量所提供。对于有序序列,你也可以利用位置编码来构建一个同时考虑全局上下文和词序的Transformer。它对长文本段落的处理比循环神经网络或一维卷积神经网络更高效。
Transformer可用于任何集合处理任务或序列处理任务(包括文本分类),尤其擅长序列到序列学习,比如将源语言的段落翻译成目标语言。
序列到序列Transformer由以下两部分组成。
TransformerEncoder(Transformer编码器):将输入向量序列转化为上下文感知且顺序感知的输出向量序列。
TransformerDecoder(Transformer解码器):接收TransformerEncoder的输出和目标序列,并预测目标序列的后续内容。
如果仅处理单一向量序列(或向量集合),那么可以只使用TransformerEncoder。
下面是一个序列到序列Transformer,它将源序列映射到目标序列(这种设置可用于机器翻译或问题回答)。
python
encoder_inputs = keras.Input(shape=(sequence_length,), dtype="int64") ←----源序列
x = PositionalEmbedding(
sequence_length, vocab_size, embed_dim)(encoder_inputs)
encoder_outputs = TransformerEncoder(embed_dim, dense_dim, num_heads)(x)
decoder_inputs = keras.Input(shape=(None,), dtype="int64") ←----当前的目标序列
x = PositionalEmbedding(
sequence_length, vocab_size, embed_dim)(decoder_inputs)
x = TransformerDecoder(embed_dim, dense_dim, num_heads)(x, encoder_outputs)
decoder_outputs = layers.Dense(vocab_size, activation="softmax")(x) ←----向后偏移一个时间步的目标序列
transformer = keras.Model([encoder_inputs, decoder_inputs], decoder_outputs)
transformer.compile(optimizer="rmsprop", loss="categorical_crossentropy")
下面是一个仅使用TransformerEncoder对整数序列进行二分类的例子。
python
inputs = keras.Input(shape=(sequence_length,), dtype="int64")
x = PositionalEmbedding(sequence_length, vocab_size, embed_dim)(inputs)
x = TransformerEncoder(embed_dim, dense_dim, num_heads)(x)
x = layers.GlobalMaxPooling1D()(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="binary_crossentropy")