0%

【实战 Google 深度学习框架】动手学得快

这本书比较清晰,讲解也很接地气,例子比较简单直接,有助于理解。还是比较推荐新人从这里开始学习。


更新历史

  • 2020.02.18:重新上线
  • 2019.04.05:完成全书(除第九章 NLP)
  • 2019.03.31:完成读后感(还差 NLP 章节)
  • 2019.03.20:开始阅读

读后感

这本书比较清晰,讲解也很接地气,例子比较简单直接,有助于理解。还是比较推荐新人从这里开始学习。

阅读笔记

对应 notebook:wdx-jupyter-notebooks/tensorflow-in-action

第 2 章 Tensorflow 环境搭建

最重要的两个工具包 Protocol Buffer 和 Bazel。其中 Protocol Buffer 处理结构化数据(需要预先定义 schema,是二进制流,但体积小 3-10 倍,速度快 20-100倍)

1
2
3
4
5
message user {
optional string name = 1;
required int32 id = 2;
repeated string email = 3;
}

Bazel 是从谷歌开源的自动化构建工具,在一个项目空间中,Bazel 通过 BUILD 文件来找到需要编译的目标,可以编译成二进制文件。

可以用 Anaconda 来安装,比较省事。使用 Anaconda Navigator 创建一个独立环境 tf1.4-py3.7 来运行本书的代码。如果因为网络问题无法访问,那么可以直接用原始的 pip 方式安装 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple xxxx

(微众的网没办法,连自己的热点即可。我也在家里的 anaconda 建了一个相同的环境)需要安装 tensorflow, bazel, bottleneck, pandas, matplotlib

测试参考 01-最简单的例子.ipynb

第 3 章 Tensorflow 入门

Tensorflow 程序一般分为两个阶段,在第一个阶段需要定义图中所有的计算。第二个阶段为执行计算。

TensorFlow 中维护的集合列表:

  • tf.GraphKeys.VARIABLES 所有变量,持久化模型
  • tf.GraphKeys.TRAINABLE_VARIABLES 可学习的变量(一般指神经网络中的参数),模型训练、生成模型可视化内容
  • tf.GraphKeys.SUMMARIES 日志生成相关的张量,TensorFlow 计算可视化
  • tf.GraphKeys.QUEUE_RUNNERS 处理输入的 QueueRunner,输入处理
  • tf.GraphKeys.MOVING_AVERAGE_VARIABLES 所有计算了滑动平均值的变量

支持通过 tf.Graph 函数来生成新的计算图。不同计算图上的张量和运算都不会共享。参考 02-定义不同的计算图.ipynb

一个张量中主要保存了三个属性:名字 name,维度 shape 和类型 type。张量的主要用途有二:

  1. 对中间计算结果的引用
  2. 当计算图构造完成之后,张量可以用来获得计算结果

会话 Session 拥有并管理 TensorFlow 程序运行时的所有资源。

  • tf.InteractiveSession() 在交互环境下可以省去将会话注册为默认会话的过程

可以通过 ConfigProto 来配置需要生成的会话:

1
2
3
4
config = tf.ConfigProto(allow_soft_placement=True,
log_device_placement=True)
sess1 = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)

最常用的配置有:

  • alow_soft_placement,支持在 CPU 上执行无法在 GPU 上执行的功能,兼容性更好
  • log_device_placement,记录每个节点被安排在哪个设备上

可视化游乐场 https://playground.tensorflow.org/

tf.matmul 实现了矩阵乘法的功能

神经网络训练的过程可以分为三个步骤:

  1. 定义神经网络的结构和前向传播的输出结果
  2. 定义损失函数以及选择反向传播优化算法
  3. 生成会话 tf.Session 并且在训练数据上反复运行反向传播优化算法

第 4 章 深层神经网络

  • 深度学习:多层和非线性
  • 通过非线性的激活函数,神经网络不再是线性的

经典损失函数:

  • 分类问题:交叉熵 cross entropy。一般和 softmax 一起使用,有统一封装 tf.nn.softmax_cross_entropy_with_logits
  • 回归问题:均方误差 MSE, mean squared error。mse = tf.reduce_mean(tf.square(y_ - y))

梯度下降算法主要用于优化单个参数的取值,而反向传播算法给出了一个高效地方式在所有参数上使用梯度下降算法,从而使神经网络模型在训练数据上的损失函数尽可能小。

梯度下降算法并不能保证被优化的函数达到全局最优解。只有当损失函数为凸函数时,梯度下降算法才能保证达到全局最优解。

在海量训练数据下,要计算所有训练数据的损失函数是非常消耗时间的。为了加速训练过程,可以使用随机梯度下降的算法。在工程中一般折中,用 mini-batch

通过指数衰减的学习率既可以让模型在训练的前期快速接近较优解,又可以保证模型在训练后期不会有太大的波动,从而更加接近局部最优。

例子:

1
2
3
4
global_step = tf.Variable(0)
# 这里每训练 100 轮,学习率乘以 0.96,一般初始学习率、衰减系数和衰减速度都是根据经验设置的
learning_rate = tf.train.exponential_decay(0.1, gloable_step, 100, 0.96, staircase=True)
learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(...my los..., global_step=global_step)

过拟合指的是当一个模型过为复杂之后,它可以很好地“记忆”每一个训练数据中随机噪音的部分而忘记了要去“学习”训练数据中通用的趋势。

为了避免过拟合,一个非常常用的方法的方法是正则化 regularization。正则化的思想就是在损失函数中加入刻画模型复杂程度的指标。常用的有 L1 和 L2 正则化。L1 正则化会让参数变得更稀疏(即更多的参数变为 0,可以达到类似特征选取的功能),而 L2 正则化不会(当参数很小时,这个参数的平方基本上可以忽略,所以不会进一步调整为 0)。L1 正则化的计算公式不可导,L2 正则化公式可导(优化起来更简洁)。在实践中,L1 和 L2 可以同时使用。

在采用随机梯度下降算范训练神经网络时,使用滑动平均模型在很多应用中都可以在一定程度提高最终模型在测试数据上的表现。

第 5 章 MNIST 数字识别问题

参见 tensorflow-in-action/mnist 文件夹中的代码。

  • 交叉验证时间太长,海量数据下,一般更多采用验证数据集的形式 来评测模型效果

五种优化方法:

  1. 使用激活函数
  2. 使用多层隐藏数
  3. 使用指数衰减学习率
  4. 加入正则化损失函数
  5. 使用滑动平均模型

重构后的代码:mnist_inference.py, mnist_train.py , mnist_eval.py

第 6 章 图像识别与卷积神经网络

  • CIFAR-10:10 个种类 60000 张图片
  • ImageNet:20000 个名次,1500 万张图片

一个卷积神经网络主要由以下 5 种结构组成:

  1. 输入层
  2. 卷积层:提取不同特征
  3. 池化层:缩小矩阵大小
  4. 全连接层:完成分类任务
  5. Softmax层:转为分类概率

LeNet-5 模型

总共有 7 层:

  1. 卷积层
    1. 输入大小为 32x32x1
    2. 过滤器尺寸为 5x5,深度为 6,不使用全 0 填充,步长为 1
    3. 输出尺寸 32-5+1=28,深度为 6。共有 5x5x1x6+6=156 个参数,其中 6 为偏置项参数
    4. 下一层的节点矩阵有 28x28x6=4704 个节点,每个节点和5x5=25 个当前层节点相连,本层卷积共有 4704x(25+1)=122304 个连接
  2. 池化层
    1. 输入为 28x28x6 的节点矩阵(上一层输出)
    2. 过滤器尺寸 2x2,长和宽步长为 2
    3. 输出矩阵 14x14x6
  3. 卷积层
    1. 输入为 14x14x6
    2. 过滤器大小为 5x5,深度为 16。不使用全 0 填充,步长为 1
    3. 输出矩阵大小为 10x10x16(10 = 14 - 5 +1)
    4. 本层有 5x5x6x16=2416 个参数,10x10x16x(25+1)=41600 个连接
  4. 池化层
    1. 输入 10x10x16,
    2. 过滤器尺寸 2x2,长和宽步长为 2
    3. 输出 5x5x16
  5. 全连接层
    1. 输入 5x5x16
    2. 输出节点 120 个
    3. 总共有 5x5x16x120+120=48120 个参数
  6. 全连接层
    1. 输入 120 个
    2. 输出 84 个
    3. 参数 120x84+84=10164 个
  7. 全连接层
    1. 输入 84 个
    2. 输出 10 个
    3. 参数 84x10+10 = 850 个

Inception-v3 模型

Inception 结构将不同的卷积层通过并联的方式结合在一起

迁移学习

将一个问题上训练好的模型通过简单的调整使其适用于一个新的问题

第 7 章 图像数据处理

  • TFRecord 格式可以统一不同的原始数据格式,并更加有效地管理不同的属性
  • TFRecord 文件中的数据都是通过 tf.train.Example 以 Protocol Buffer 的格式存储的
  • TersorFlow 提供了 tf.Coordinatortf.QueueRunner 两个类来完成多线程协同的功能。
  • Dataset API 很有用

第 8 章 循环神经网络

在经典的循环神经网络中,状态的传输是从前往后单向的。然而,在有些问题中,当前时刻的输出不仅和之前的状态有关系,也和之后的状态相关。这时就需要使用双向循环神经网络(bidirectional RNN)来解决这类问题

双向循环神经网络的主体结构就是两个单向循环神经网络的结合。在每一个时刻 t,输入会同时提供给这两个方向相反的循环神经网络。两个网络独立进行计算,各自产生该时刻的新状态和输出,而双向循环网络的最终输出是这两个单向循环神经网络的输出的简单拼接。

CNN 只在最后的全连接层中使用 dropout,RN 一般只在不同循环体结构之间使用 dropout,而不在同一层循环体结构之间使用。也就是说从时刻 t-1 传递到 t 时,RNN 不会进行状态的 dropout;而在同一个时刻 t 中,不同层循环体之间会使用 dropout

第 9 章 自然语言处理

语言模型是自然语言处理问题中一类最基本的问题,有着非常广泛的应用。假设一门语言中所有可能的句子服从某一个概率分布,每个句子出现的概率加起来为 1,那么“语言模型”的任务就是预测每个句子在语言中出现的概率。对于语言中常见的句子,一个好的语言模型应得出相对较高的概率;而对于不合语法的句子,计算出的概率则应接近于零。语言模型仅仅对句子出现的概率进行建模。

语言模型效果好坏的常用指标是复杂度(perplexity),在一个测试集上得到的 perplexity 越低,说明建模的效果越好。

  • tf.nn.softmax_cross_entropy_with_logits 其中的 labels 输入需要是一个概率分布
  • 采用一种比较特殊的 batching 方法来处理:将长序列切割为固定长度的子序列。循环神经网络在处理完一个子序列后,它最终的隐藏状态将复制到下一个序列中作为初始值。
  • 词向量主要用于降低输入的维度和增加语义信息
  • 共享词向量层和 softmax 层的参数,可以减少参数数量,还可以提高最终模型效果

第 10 章 Tensorflow 高层封装

高层封装主要有 4 个:TensorFlow-Slim, TFLearn, Keras, Estimator。最常用的是 Keras,我也只学一下 Keras。具体参考代码即可。

第 11 章 TensorBoard 可视化

具体参考 tensorboard 文件夹,有详细的代码

  • 使用 tf.summary 系列 API 来写计算图的相关信息,然后就可以用 tensorboard 查看
  • 使用 tensorboard --logdir=/path/to/log 来查看
  • 使用 tf.variable_scope 生成的变量不受 tf.name_scope 影响
  • 使用 tf.Variable 生成的变量受 tf.name_scope 影响
  • TensorBoard 还可以展示 TensorFlow 计算图上每个节点的基本信息以及运行时消耗的时间和空间
  • Projector 可以展示高维向量

第 12 章 Tensorflow 计算加速

  • 将计算密集型放到 GPU 来做
1
2
3
4
5
# 动态分配显存
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
# 固定比例分配
config.gpu_options.per_process_gpu_memory_fraction = 0.4