【Python 深度学习】高屋建瓴

Keras 之父名不虚传,吊打国内作者写的书。


更新历史

  • 2020.02.18:重新上线
  • 2019.04.22:完成全书阅读(部分章节略过)
  • 2019.04.18:完成读后感
  • 2019.03.27:完成前三章
  • 2018.11.07:开始阅读

读后感

Keras 之父名不虚传,吊打国内作者写的书。如果你只有钱买一本关于深度学习的书,那就是这本,其他的都是浮云。

阅读笔记

第一部分 深度学习基础

第 1 章 什么是深度学习

  • 人工智能的简介定义如下:努力将通常由人类完成的智力任务自动化。
  • 机器学习系统是训练出来的。

我们需要以下三个要素来进行机器学习

  1. 输入数据点
  2. 预期输出的示例
  3. 衡量算法效果好坏的方法

机器学习和深度学习的核心问题在于有意义地变换数据。神经网络中每层对输入数据所做的具体操作保存在该层的权重 weight 中,其本质是一串数字,有时也被称为该层的参数 parameter。在这种语境下,学习的意思是为神经网络的所有层找到一组权重值,使得该网络能够将每个示例输入与其目标正确地一一对应。

机器学习算法在寻找这些变换时通常没有什么创造性,仅仅是遍历一组预先定义好的操作,这组操作叫做假设空间(hypothesis space)

想要控制一件事物,首先需要能够观察它。想要控制神经网络的输出,就需要能够衡量该输出与预期值之间的距离。这是神经网络损失函数 loss function 的任务,该函数也叫目标函数 objective function。损失函数的输入是网络预测值与真实目标值,然后计算一个距离值,衡量该网络在这个示例上的效果好坏。

深度学习的基本技巧是利用这个距离值作为反馈信号来对权重值进行微调,以降低对应的损失值。这种调节由优化器 optimizer 来完成,它实现了所谓的反向传播 backpropagation 算法,这是深度学习的核心算法。

深度学习发展得如此迅速,主要原因在于它在很多问题上都表现出更好的性能。但这并不是唯一的原因。深度学习还让解决问题变得更加简单,因为它将特征工程完全自动化,而这曾经是机器学习工作流程中最关键的一步。

深度学习从数据中进行学习时有两个基本特征:第一,通过渐进的、逐层的方式形成越来越复杂的表示;第二,对中间这些渐进的表示共同进行学习,每一层的变化都需要考虑上下两层的需要。

第 2 章 神经网络的数学基础

在机器学习中,分类问题中的某个类别叫做类 class。数据点叫做样本 sample。某个样本对应的类叫做标签 label。

深度学习模型不会同时处理整个数据集,而是将数据拆分成小批量,一个批量里数据的大小就是 batch size。

小批量随机梯度下降 mini-batch stochastic gradient descent, mini-batch SGD 的步骤:

  1. 抽取训练样本 x 和对应目标 y 组成的数据批量
  2. 在 x 上运行网络,得到预测值 y_pred
  3. 计算网络在这批数据上的损失,用于衡量 y_pred 和 y 之间的距离
  4. 计算损失相对于网络参数的梯度,即一次反向传播 backward pass
  5. 将参数沿着梯度的方向反方向移动一点,已步入 W -= step * gradient,从而使这批数据上的损失减少一点

SGD 还有多种变体,起区别自傲与计算下一次权重更新时还要考虑上一次的权重更新,而不是仅仅考虑当前梯度值,比如带动量的 SGD、Adagrad、RMSProp 等。这些变体被称为优化方法 optimization method 或优化器 optimizer。其中动量的概念尤其值得关注,在许多变体中都有应用。

将链式法则应用于神经网络梯度值得计算,得到的算法叫做反向传播 backpropagation,有时也叫反式微分 reverse-mode differentiation。反向传播从最终损失值开始,从最顶层反向作用至最底层,利用链式法则计算每个参数对损失值的贡献大小。

  • 学习 是指找到一组模型参数,使得在给定的训练数据样本和对应目标值上的损失函数最小化。
  • 学习的过程:随机选取包含数据样本及其目标值的批量,并计算批量损失相对于网络参数的梯度。随后将网络参数沿着梯度的反方向稍稍移动(移动距离由学习率指定)。
  • 整个学习过程之所以能够实现,是一位神经网络是一系列可微分的张量运算,因此可以利用求导的链式法则来得到梯度函数,整个函数将当前参数和当前数据批量映射为一个梯度值。

第 3 章 神经网络入门

训练神经网络主要围绕以下四个方面:

  1. 层,多个层组合成网络(或模型)
  2. 输入数据和相应的目标
  3. 损失函数,即用于学习的反馈信号
  4. 优化器,决定学习过程如何进行

层是一个数据处理模块,将一个或多个输入张量转换为一个或多个输出张量。有些层是无状态的,但大多数的层是有状态的,即层的权重。权重是利用随机梯度下降学到的一个或多个张量,其中包含网络的知识。

选择正确的网络架构更像是一门艺术而不是科学。

一旦确定了网络架构,还需要选择以下两个参数:

  1. 损失函数(目标函数) - 在训练过程中需要将其最小化。它能够衡量当前任务是否已成功完成
  2. 优化器 - 决定如何基于损失函数对网络进行更新。它执行的是随机梯度下降(SGD)的某个变体

常见问题对应的损失函数:

  • 二分类问题 - 二元交叉熵 binary crossentropy
  • 多分类问题 - 分类交叉熵 categorical crossentropy
  • 回归问题 - 均方差误差 mean-squared error
  • 序列学习问题 - 联结主义时序分类 CTC, connectionist temporal classification

典型的 Keras 工作流如下:

  1. 定义训练数据:输入张量和目标张量
  2. 定义层组成的网络(或模型),将输入映射到目标
  3. 配置学习过程:选择损失函数、优化器和需要监控的指标
  4. 调用模型的 fit 方法在训练数据上进行迭代

定义模型有两种方法:一种是使用 Sequential 类(仅用于层的线性堆叠,目前最常见的网络架构),另一种是函数式 API(fucntional API,用于层组成的有向无环图,可以构建任意形式的架构)

IMDB 情感分析 - 二分类问题

参考 python_deep_learning/1_imdb_binary_classification.py 文件

对于 Dense 层的堆叠,需要确定两个关键架构:

  1. 网络有多少层
  2. 每层有多少个隐藏单元

小结:

  • 通常需要对原始数据进行大量预处理,以便将其转换为张量输入到神经网络中。单词序列可以编码为二进制向量
  • 带有 relu 激活的 Dense 层堆叠,可以解决很多种问题(包括情感分类)
  • 对于二分类问题,网络的最后一层应该是只有一个单元并使用 sigmoid 激活的 Dense 层,网络输出应该是 0-1 范围的标量,表示概率值
  • 对于二分类问题的 sigmoid 标量输出,应该使用 binary_crossentropy 损失函数
  • 无论问题是什么,rmsprop 优化器通常都是足够好的选择
  • 随着神经网络在训练数据上的表现越来越好,模型最终会过拟合,一定要一直监控模型在训练集之外的数据上的性能

新闻分类 - 多分类问题

这是一个**单标签、多分类(single-label, multiclass classification)**问题,每个数据点只能划分到一个类别,使用的是路透社数据集,包括 46 个不同的主题,每个主题至少有 10 个样本。

参考 python_deep_learning/2_reuters_multi_classification.py 文件

  • 如果要对 N 个类别的数据点进行分类,网络的最后一层应该是大小为 N 的 Dense 层
  • 对于单标签、多分类问题,网络的最后一层应该使用 softmax 激活,这样可以输出在 N 个输出类别上的概率分布
  • 这种问题的损失函数几乎总是应该使用分类交叉熵。它将网络输出的概率分布于目标的真实分布之间的距离最小化
  • 处理多分类问题的标签有两种方法
    • 通过 one-hot 编码,使用 categorical_crossentropy 作为损失函数
    • 将标签编码为整数,然后使用 sparse_categorical_crossentropy 作为损失函数
  • 如果你需要将数据划分到许多类别中,应该避免使用太小的中间层,以免在网络中造成信息瓶颈

房价预测 - 回归问题

数据集:波士顿郊区房屋价格的中位数。参考 python_deep_learning/3_price_prediction.py

小结

  • 回归问题使用的损失函数与分类问题不同。回归常用的损失函数是均方误差 MSE
  • 回归问题使用的评估指标也与分类问题不同,常见的回归指标是平均绝对误差 MAE
  • 如果输入数据的特征具有不同的取值范围,应该先进行预处理,对每个特征单独进行缩放
  • 如果可用的数据很少,使用 K 折验证可以可靠地评估模型
  • 如果可用的训练数据很少,最好使用隐藏层较少的小型网络,以避免严重的过拟合

第 4 章 机器学习基础

  • 评估模型:留出验证(hold-out validation),K 折验证(K-fold validation),带打乱的重复 K 折验证(iterated K-fold validation with shuffling)
  • 评估模型注意事项
    • 数据代表性(data representativeness):一般来说随机打乱数据
    • 时间箭头(the arrow of time):根据过去预测未来,那么不应该随机打乱数据
    • 数据冗余(redundancy in your data):训练集与验证集有相同的数据
  • 数据预处理
    • 向量化
    • 值标准化
    • 处理缺失值
  • 特征工程:用深度神经网络,同样需要在意特征工程
    • 良好的特征可以让你用更少的资源更优雅地解决问题
    • 良好的特征可以让你用更少的数据解决问题

机器学习的根本问题是优化和泛化之间的对立。**优化(optimization)是指调节模型以在训练数据上得到最佳性能,而泛化(generalization)**是指训练好的模型在前所未见的数据上的性能好坏。机器学习的目的是得到良好的泛化,但你无法控制泛化,只能基于训练数据调节模型。

训练开始时,优化和泛化是相关的,但迭代一定次数后,就会从欠拟合变为过拟合。为了防止模型从训练数据中学到错误或无关紧要的默哀是,最优解决方法是获取更多的训练数据。如果无法获取更多数据,次优解决方法是调节模型允许存储的信息量,或对模型允许存储的信息加以约束。这种降低过拟合的方法叫做正则化(regularization)

防止过拟合的常用方法有:

  1. 获取更多的训练数据
  2. 减小网络容量
  3. 添加权重正则化
  4. 添加 dropout

机器学习的通用工作流程

  1. 定义问题,收集数据
    1. 输入数据是什么,预测什么
    2. 面对的是什么类型的问题:二分类、多分类、标量回归、向量回归等等
    3. 所做的假设:
      1. 假设输出是可以根据输入进行预测的
      2. 假设可用数据包含足够多的信息,足以学习输入和输出之间的关系
  2. 选择衡量成功的指标:presicion、recall、roc auc
  3. 确定评估方法
    1. 留出验证集:大部分情况可以满足需求
    2. K 折交叉验证:样本少
    3. 重复的 K 折验证:样本少,且需要评估地非常准确
  4. 准备数据
    1. 将数据格式化为张量
    2. 这些张量的值通常应该缩放为较小的值,比如 [-1, 1] 区间或 [0, 1] 区间
    3. 如果不同的特征具有不同的取值范围(异质数据),那么应该做数据标准化
    4. 可能需要做特征工程,尤其对于小数据
  5. 开发比基准更好的模型
    1. 最后一层的激活
    2. 损失函数
    3. 优化配置
  6. 扩大模型规模:开发过拟合的模型
    1. 理想的模型是刚好在欠拟合和过拟合的界线上,在容量不足和容量过大的界线上
    2. 如何过拟合:
      1. 添加更多层
      2. 让每一层变得更大
      3. 训练更多轮次
  7. 模型正则化与调节超参数:不断地调节模型、训练、在验证数据上评估、再次调节模型,然后不断重复,直到模型达到最佳性能:
    1. 添加 dropout
    2. 尝试不同的架构:增加或减少参数
    3. 添加 L1 和/或 L2 正则化
    4. 尝试不同的超参数,找到最佳配置
    5. 反复做特征工程:添加新特征或删除没有信息量的特征

第二部分 深度学习实践


第 5 章 深度学习用于计算机视觉

全连接层和卷积层的根本区别在于,Dense 层从输入特征空间中学到的是全局模型,而卷积层选到的是局部模式,这使得 CNN 具有两个有趣的性质:

  1. 学到的模式具有平移不变性(translation invariant)
  2. 可以学到模式的空间层次结构(spatial hierarchies of patterns)

对于包含两个空间轴和一个深度轴(也叫通道轴)的 3D 张量,其卷积也叫特征图(feature map)

在小型数据集上进行图像分类的三种策略:

  1. 从头开始训练一个小型模型
  2. 使用预训练的网络做特征提取:使用卷积基(convolutional base),自定义分类器
  3. 对预训练的网络进行微调(fine-tuning):需要已有一个训练好的分类器,才可以微调预训练模型的其他层。

小结

  • 卷积神经网络是用于计算机视觉任务的最佳机器学习模型。即使在非常小的数据集上也可以从头开始训练一个卷积神经网络,而且得到的结果还不错
  • 在小型数据集上的主要问题是过拟合。在处理图像数据时,数据增强是一宗降低过拟合的强大方法
  • 利用特征提取,可以很容易将现有的卷积神经网络复用于新的数据集。对于小型图像数据集,这时一种很有价值的方法
  • 作为特征提取的补充,你还可以使用微调,将现有模型之前学到的一些数据表示应用于新问题。这种方法可以进一步提高模型性能

卷积神经网络的可视化最常用的三种方式:

  1. 可视化卷积神经网络的中间输出(中间激活):有助于理解卷积神经网络连续的层如何对输入进行变换,也有助于初步了解卷积神经网络每个过滤器的含义
  2. 可视化卷积神经网络的过滤器:有助于精确理解卷积神经网络中每个过滤器容易接受的视觉模式或视觉概念
  3. 可视化图像中类激活的热力图:有助于理解图像的哪个部分被识别为属于某个类别,从而可以定位图像中的物体。这类技术称为类激活图(CAM, class activation map)

随着层数的加深,层所提取的特征变得越来越抽象。更高的层激活包含关于特定输入的信息越来越少,而关于目标的信息越来越多。深度神经网络可以有效地作为信息蒸馏管道(information distillation pipeline)

本章小结

  • 卷积神经网络是解决视觉分类问题的最佳工具
  • 卷积神经网络通过学习模块化模式和概念的层次结构来表示视觉世界
  • 卷积神经网络学到的表示很容易可视化,卷积神经网络不是黑盒
  • 使用视觉数据增强来防止过拟合

第 6 章 深度学习用于文本和序列

用于处理序列的两种基本深度学习算法分别是循环神经网络(recurrent neural network)一维卷积神经网络(1D convnet)

文本向量化(vectorize)是指将文本转换为数值张量的过程,有多种实现方式:

  1. 将文本分割为单词,并将每个单词转换为一个向量
  2. 将文本分割为字符,并将每个字符转换为一个向量
  3. 提取单词或字符的 ngram,并将每个 ngram 转换为一个向量

一些常用术语:token、embedding、bag-of-words、one-hot

  • 利用 Embedding 层学习词嵌入
  • SimpleRNN 不擅长处理长序列,比如文本
  • LSTM 单元的作用:允许过去的信息稍后重新进入,从而解决梯度消失的问题。但是计算量大不少

循环神经网络的三种技巧:

  1. 循环dropout(recurrent dropout)。在循环层中使用 dropout 来降低过拟合
  2. 堆叠循环层(stacking recurrent layers)。这回提高网络的表示能力(但计算量也更大)
  3. 双向循环层(bidirectional recurrent layer)。将相同的信息以不同的方式呈现给循环网络,可以提高精度并缓解遗忘问题。

如果学习算法没有被硬编码要求去寻找特定类型的简单模型,那么有时候参数学习时无法找到简单问题的简单解决方案的。

如果一种数据表示不同但有用,那么总是值得加以利用,这种表示与其他表示的差异越大越好,它们提供了查看数据的全新角度,抓住了数据中被其他方法忽略的内容,因此可以提高模型在某个任务上的性能。这是**集成(ensembling)**方法背后的直觉。

为了提高温度预测问题的性能,还可以尝试下面这些方法:

  • 在堆叠循环层中调节每层的单元个数
  • 调节 RMSprop 优化器的学习率
  • 尝试使用 LSTM 层代替 GRU 层
  • 在循环层上面尝试使用更大的密集连接回归器,即更大的 Dense 层或 Dense 层的堆叠
  • 不要忘记最后在测试集上运行性能最佳的模型

GRU 小结

  • 遇到新问题时,最好首先为你选择的指标建立一个基于常识的基准。如果没有需要打败的基准,那么就无法分辨是否取得了真正的进步。
  • 在尝试计算代价较高的模型之前,先尝试一些简单的模型,以此证明增加计算代价是有意义地。有时简单模型就是你的最佳选择。
  • 如果时间顺序对数据很重要,那么循环网络是一种很适合的方法,与那些先将时间数据展平的模型相比,其性能要更好
  • 想要在循环网络中使用 dropout,应该使用一个不随时间变化的 dropout 掩码与循环 dropout 掩码
  • 与单个 RNN 相比,堆叠 RNN 的表示能力更强大。但它的计算代价也更高,因此不一定总是需要。虽然它在机器翻译等复杂问题上很有效,但在较小、较简单的问题上可能不一定有用
  • 双向 RNN 从两个方向查看一个序列,它对自然语言处理问题非常有用。但如果在序列数据中最近的数据比序列开头包含更多的信息,那么这种方法的效果就不明显

CNN 小结

  • 二维 CNN 在二维空间中处理视觉模式时表现很好,与此相同,一维 CNN 在处理时间模式时表现也很好。对于某些问题,特别是自然语言处理,它可以替代 RNN,并且速度更快
  • 通常情况下,一维 CNN 的架构与计算机视觉领域的二维 CNN 很相似,它将 Conv1D 层和 MaxPooling1D 层堆叠在一起,最后是一个全局池化运算或展平操作
  • 因为 RNN 在处理非常长的序列时计算代价很大,但一维 CNN 的计算代价很小,所以在 RNN 之前使用一维 CNN 作为预处理步骤是一个好主意,这样可以使序列变短,并提取出有用的表示交给 RNN 来处理

本章总结

  • 可以用 RNN 进行时间序列回归(预测未来)、时间序列分类、时间序列异常检测和序列标记(找出句子中的人名或日期)
  • 可以将一维 CNN 用于机器翻译 seq2seq、文档分类和拼写校正
  • 如果序列数据的整体顺序很重要,那么最好使用循环网络来处理。时间序列通常都是这样,最近的数据可能比久远的数据包含更多的信息量。
  • 如果整体顺序没有意义,那么一维 CNN 可以实现同样好的效果,并且计算代价更小。文本数据通常都是这样,在句首发现关键词和在句尾发现关键词一样都很有意义。

第 7 章 高级的深度学习最佳实践

Keras 函数式 API

  • 残差连接是将前面的输出张量与后面的输出张量相加,从而将前面的表示重新注入下游数据流中,这有助于防止信息处理流程中的信息损失
  • 使用函数式 API,你可以直接操作张量,也可以把层当做函数来使用,接收张量并返回张量

1x1 卷积的作用

我们知道卷积能够在输入张量的每一个方块周围提取空间图块,并对所有图块应用相同的变化。极端情况是提取的图块只包含一个方块。这时卷积运算等价于让每个方块向量经过一个 Dense 层:它计算得到的特征能够将输入张量通道中的信息混合在一起,但不会将跨空间的信息混合在一起。这也是 Inception 模块的特色。如果你假设每个通道在跨越时空时时高度自相关的,但不同的通道之间可能并不相关,那么这种做法是很合理的。

  • 残差连接解决了困扰所有大规模深度学习模型的两个共性问题:梯度消失和表示瓶颈。通常来说,向任何多于 10 层的模型中添加残差连接,都可能会有所帮助。
  • 残差连接是让前面某层的输出作为后面某层的输入,从而在序列网络中有效地创造了一条捷径。前面层的输出密友与后面层的激活连接在一起,二是与后面层的激活相加(假设两个激活的形状相同)。如果他们的形状不同,可以用一个线性变换将前面层的激活改变成目标形状。
# 残差连接
from keras import layers
# 假设 x 是一个四维张量
x = ...
# 对 x 进行变换
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
# 将原始 x 与输出特征相加
y = layers.add([y, x])

如果特征图尺寸不同,可以用线性残差连接(linear residual connection) 来实现残差连接,如下:

# 线性残差连接
from keras import layers
# 假设 x 是一个四维张量
x = ...
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.MaxPooling2D(2, strides=2)(y)

# 使用 1x1 卷积,将原始 x 张量线性下采样为与 y 具有相同的形状
residual = layers.Conv2D(128, 1, strides=2, padding='same')(x)
# 残差张量与输出特征相加
y = layers.add([y, residual])

共享层权重:几个分支全部都共享相同的知识并执行相同的运算。例如一个比较两个句子是否相似的分类器,那么对句子进行 embedding 的 LSTM 层是可以共享的,如下:

from keras import layers
from keras import Input
from keras.models import Model

# 将一个 LSTM 层实例化一次
lstm = layers.LSTM(32)
# 构建左分支
left_input = Input(shape=(None, 128))
left_output = lstm(left_input)
# 构建右分支,用的是同一个 lstm 实例
right_input = Input(shape=(None, 128))
right_output = lstm(right_input)
# 构建一个分类器
merged = layers.concatenate([left_output, right_output], axis=-1)
predictions = layers.Dense(1, activation='sigmoid')(merged)
# 实例化并训练,这时会根据两个输入对 LSTM 进行更新
model = Model([left_input, right_input], predictions)
model.fit([left_data, right_data], targets)

也可以直接调用模型作为层。

Keras 回调函数

可以用来在模型训练的不同时间点执行我们想要的操作。

# 通过回调进行 Early Stop
import keras

callbacks_list = [
    keras.callbacks.EarlyStopping(
        monitor='acc', # 监控模型的验证精度
        patience=1, # 如果精度在多于一轮内不改善,中断训练
    ),
    keras.callbacks.ModelCheckpoint( # 每轮过后保存当前权重
        filepath='my_model.h5',  # 目标模型文件的保存路径
        monitor='val_loss', # 如果 val_loss 没有改善,就不覆盖模型
        save_best_only=True,
    )
]

model.compile(optimizer='rmsprop', 
            loss='binary_crossentropy',
            metrics=['acc']) # 监控精度,这里必须要有

model.fit(x, y, epochs=10,
            batch_size=32,
            callbacks=callbacks_list,
            validation_data=(x_val, y_val))
            
# -------------------
# 如果验证损失不再改善,可以使用 ReduceLROnPlateau 来降低学习率。
# 在训练过程中如果出现了损失平台,那么增大或减小学习率是一个好的方法

callbacks_list = [
    keras.callbacks.ReduceLROnPlateau(
        monitor='val_loss', # 监控模型的验证损失
        factor=0.1, # 触发时学习率乘以 0.1
        patience=10, # 如果 10 轮都没有改善,就触发
    )
]

model.fit(x, y,
        epochs=10,
        batch_size=32,
        callbacks=callbacks_list,
        validation_data=(x_val, y_val)) # 需要传入验证数据

也可以编写自己的回调函数,创建 keras.callbacks.Callback 类的子类,然后实现如下方法:

  • on_epoch_begin
  • on_epoch_end
  • on_batch_begin
  • on_batch_end
  • on_train_begin
  • on_train_end

让模型性能发挥到极致

另外两种设计模式:标准化深度可分离卷积

**批标准化(batch normalization)**是在训练过程中均值和方差随时间发生变化,它可以适应性地将数据标准化。工作原理:训练过程中在内部保存已读取每批数据均值和方差的指数移动平均值。效果:有助于梯度传播,对于有些特别深的网络,只有包含多个 BatchNormalization 层才能进行训练。通常在卷积层或 Dense 层之后使用。

深度可分离卷积(depthwise separable convolution)层是对输入的每个通道分别执行空间卷积,然后通过逐点卷积(1x1)将输出通道混合,相当于将空间特征学习和通道特征学习分开。

超参数优化的过程通常为:

  1. 选择一组超参数(自动选择)
  2. 构建相应的模型
  3. 将模型在训练数据上拟合,并衡量其在验证数据上的最终性能
  4. 选择要尝试的下一组超参数(自动选择)
  5. 重复上述过程
  6. 最后,衡量模型在测试数据上的性能

这个过程的关键在于如何选择超参数,有多种不同的技术:贝叶斯优化、遗传算法、简单随机搜索等。

更新超参数的难点:

  1. 计算反馈信号(这组超参数在这个任务上是否得到了一个高性能的模型)的计算代价可能非常高,它需要在数据集上创建一个新模型并从头开始训练
  2. 超参数空间通常由许多离散的决定组成,因而既不是连续的,也不是可微的,这样优化起来效率比较低

注意:在进行大规模超参数自动优化时,有一个重要的问题需要牢记,那就是验证集过拟合。因为你是使用验证数据计算出一个信号,然后根据这个信号更新超参数,所以实际上是在验证数据上训练超参数,很快会对验证数据过拟合。请牢记!

想要在一项任务上获得最佳结果,另一种强大的技术是模型集成(model ensembling),是指将一系列不同的模型的预测结果汇集到一起,从而得到更好的预测结果。集成的模型应该尽可能好,同时尽可能不同。

第 8 章 生成式深度学习

使用 LSTM 生成文本

用深度学习生成序列数据的通用方法,就是使用前面的标记作为输入,训练一个网络(通常是 RNN 或 CNN)来预测序列中接下来的一个或多个标记。标记(token)通常是单词或字符,给定前面的标记,能够对下一个标记的概率进行建模的任何网络都叫做语言模型(language model)。语言模型能够捕捉到语言的潜在空间(latent space),即语言的统计结构。

一旦训练好了这样一个语言模型,就可以从中采样(sample,即生成新序列)。想模型中输入一个初始文本字符串,即条件数据(conditioning data),要求模型生成下一个字符或下一个单词,然后将生成的输出添加到输入数据中,并多次重复这一过程。这个循环可以生成任意长度的序列,这些序列反映了模型训练数据的结构。

生成文本时,如何选择下一个字符至关重要。一种方法是贪婪采样(greedy sampling),就是始终选择可能性最大的下一个字符。但这种方法会得到重复的、可预测的字符串。另一种是随机采样(stochastic sampling),引入随机性,比如下一个字符是 e 的概率为 0.3,那么就有 30% 的概率选择它(从 softmax 里采样就可以)。但问题在于采样过程中无法控制随机性的大小。

为了在采样过程中控制随机性的大小,引入了一个叫做**softmax温度(softmax temperature)**的参数,用于表示采样概率分布的熵,即表示所选择的下一个字符会有多么出人意料或多么可预测。更高的温度得到的是熵更大的采样分布,会生成更加出人意料、更加无结构的生成数据,而更低的温度对应更小的随机性,以及更加可预测的生成数据。

小结:

  • 我们可以生成离散的序列数据,其方法是:给定前面的标记,训练一个模型来预测接下来的一个或多个标记
  • 对于文本来说,这种模型叫做语言模型。它可以是单词级的,也可以是字符集级的
  • 对下一个标记进行采样,需要在坚持模型的判断与引入随机性之间寻找平衡
  • 处理整个问题的一种方法是使用 softmax 温度。一定要尝试多种不同的温度,以找到合适的那一个

DeepDream

  • 一种艺术性的图像修改技术,用到了 CNN 学到的表示。
  • 对于每个连续的尺度,从最小到最大,我们都需要在当前尺度运行梯度上升,以便将之前定义的损失最大化。每次运行完梯度上升之后,将得到的图像放大 40%

小结

  • DeepDream 的过程是反向运行一个卷积神经网络,基于网络学到的表示来生成输入
  • 得到的结果是很有趣的,有些类似于通过迷幻剂扰乱视觉皮层而诱发的视觉伪影
  • 这个过程并不局限于图像模型,甚至不局限于卷积神经网络,可以应用于语音、音乐等更多内容

神经风格迁移

神经风格迁移(neural style transfer)是指将参考图像的风格应用于目标图像,同时保留目标图像的内容。

网络更靠底部的层激活包含关于图像的局部信息,而更靠近顶部的层则包含更加全局,更加抽象的信息。卷积神经网络不同层的激活用另一种方式提供了图像内容在不同空间尺度上的分解。因此,图像的内容是更加全局和抽象的,我们认为它能够被卷积神经网络更靠顶部的层的表示所捕捉到。因此,内容损失的一个很好的候选者就是两个激活之间的 L2 范数,一个激活是预训练的卷积神经网络更靠顶部的某层在目标图像上计算得到的激活,另一个激活是同一层在生成图像上计算得到的激活。

神经风格迁移的一般过程如下:

  1. 创建一个网络,它能够同时计算风格参考图像、目标图像和生成图像的 VGG19 层激活
  2. 使用这三张图像上计算的层激活来定义之前所述的损失函数,为了实现风格迁移,需要将这个损失函数最小化
  3. 设置梯度下降过程来将这个损失函数最小化

小结

  1. 风格迁移是指创建一张新图像,保留目标图像的内容的同时还抓住了参考图像的风格
  2. 内容可以被卷积神经网络更靠顶部的层激活所捕捉到
  3. 风格可以被卷积神经网络不同层激活的内部相互关系所捕捉到
  4. 因此,深度学习可以将风格迁移表述为一个最优化过程,并用到了一个用预训练卷积神经网络所定义的损失
  5. 从这个基本想法出发,可以有许多变体和改进

用变分自编码器生成图像

主要介绍 VAE(variational autoencoder) 和 GAN(generative adversarial network),这里与工作不是特别相关,暂时略过

第 9 章 总结

在深度学习中,一切都是向量,即一切都是几何空间(geometric space)中的点(point)。首先将模型输入和目标向量化(vectorize),即将其转换为初始输入向量空间和目标向量空间。深度学习模型的每一层都对通过它的数据做一个简单的几何变换。模型中的层链共同形成了一个非常复杂的几何变换,它可以分解为一系列简单的几何变换。这个复杂变换试图将输入空间映射到目标空间,每次映射一个点。这个变换由层的权重来参数化,权重根据模型当前表现进行迭代更新。这种几何变换有一个关键性质,就是它必须是可微的(differentiable),这样我们才能通过梯度下降来学习其参数。直观上来看,这意味着从输入到输出的几何变形必须是平滑且连续的,这是一个很重要的约束条件。

机器学习的通用工作流程参考第四章。

输入模式与网络架构的对应关系:

  • 向量数据:Dense
  • 图像数据:二维 CNN
  • 声音数据:一维 CNN 或 RNN
  • 文本数据:一维 CNN 或 RNN
  • 时间序列:RNN 或 一维 CNN
  • 其他类型序列数据:RNN 或 一维 CNN
  • 视频数据:三维 CNN,或二维 CNN+RNN
  • 立体数据:三维 CNN

密集连接网络的特点:

  • 对于二分类问题,最后一层 Dense 使用 sigmoid,损失使用 binary_crossentropy,目标是 0 或 1
  • 对于单标签多分类问题,最后一层 Dense 使用 softmax,如果目标是 one-hot 编码,使用 categorical_crossentropy,如果是整数则使用 sparse_categorical_crossentropy
  • 对于多标签多分类问题,最后一层 Dense 使用 sigmoid 激活,使用 binary_crossentropy,目标应该是 k-hot 编码
  • 对于连续值向量的回归问题,最后一层 Dense 不带激活,常用 MSE 和 MAE 做损失函数