基于 Python 的理论与实践,从零开始实现深度学习底层的内容。
更新历史
- 2020.02.18:重新上线
- 2019.07.30:完成阅读与读后感
- 2018.11.12:完成第三章
- 2018.11.09:完成前两章的阅读
读后感
基于 Python 的理论与实践,从零开始实现深度学习底层的内容。通过实现深度学习的过程,来逼近深度学习的本质。
在光看数学式和理论说明无法理解的情况下,可以尝试阅读源代码并运行,很多时候思路都会变得清晰起来。这本书帮助我再次深入理解了 BP,也通过从零实现神经网络了解到了很多细节。
不过这里没有涉及到 RNN 的部分,介绍更多也偏向于图像,少了 NLP 等其他热门领域的介绍。
读书笔记
第一章 Python 入门
所使用的版本和库,通过 Anaconda 安装
- Python 3.X
- NumPy
- Matplotlib
Numpy 常用操作
- 一维数组
A = np.array([1, 2, 3])
- 二维数组
B = np.array([[1,2], [3,4]]
- 矩阵大小
B.shape
- 矩阵元素类型
B.dtype
- 转为一维数组
C = B.flatten()
第二章 感知机 Perceptron
感知机接收多个输入信号,输出一个信号。感知机的多个输入信号都有各种固有的权重,这些权重发挥着控制各个信号的重要性的作用。
使用感知机可以表示与门、与非门、或门的逻辑电路。感知机的局限在于它只能表示由一条直线分割的空间,没有办法实现异或门,这也成了神经网络最开始被打压的原因。
异或门可以通过与门、与非门、或门的组合来实现,同样的,我们可以用多层感知机 multi-layered perceptron 来实现异或门。
第三章 神经网络
激活函数 activation function 作用在于决定如何激活输入信号的总和。
感知机中使用阶跃函数作为激活函数,而神经网络使用的是其他函数,常用的有:
- sigmoid 函数(非线性函数,这是神经网络的要求,不然多层叠加无意义)
- ReLU, Rectified Linear Unit 函数
神经网络可以用在分类和回归问题上,不过需要根据情况改变输出层的激活函数。一般而言,回归问题用恒等函数,分类问题用 softmax 函数。
一般而言,神经网络只把输出值最大的神经元所对应的类别作为识别结果,在进行分类时,输出层的 softmax 函数可以省略(但是在学习阶段是不能省略的)
第四章 神经网络的学习
学习的目的是以损失函数为基准,找出能使它的值达到最小的权重的参数。
深度学习的价值在于从人工设计规则转变为由机器从数据中学习。
神经网络的学习中所用的指标称为损失函数 loss function。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。
利用微小的差分求导数的过程称为数值微分 numerical differentiation。而基于数学式的推导来求导数的过程称为解析性求导。数值微分求出来的是有误差的(只是一个近似)
偏导数和单变量的导数一样,都是求某个地方的斜率。不过,偏导数需要将多个变量中的某一个变量定为目标变量,并将其他变量固定为某个值。
由全部变量的偏导数汇总而成的向量称为梯度 gradient。梯度表示的是各点处的函数值减少最多的方向。
机器学习的主要任务是在学习时寻找最优参数。同样地,神经网络也必须在学习时找到最优参数(权重和偏置)
学习率是超参数,是人工设定的,一般来说需要尝试多个不同的值。
神经网络的学习步骤如下:
- mini-batch,从训练数据中随机选出一部分数据,称为 mini-batch,我们的目标是减少 mini-batch 的损失函数的值。
- 计算梯度,为了减少 mini-batch 损失函数的值,需要求出各个权重参数的梯度。梯度表示损失函数的值减少最多的方向。
- 更新参数,将权重参数沿梯度方向进行微小更新。
- 重复,重复前三个步骤。
第五章 误差反向传播法
- 神经网络的正向传播中进行的矩阵的乘积运算在几何学领域被称为“仿射变换”(Affine)
- 通过使用计算图,可以直观把握计算过程
- 计算图的节点是由局部计算构成的。局部计算构成全局计算
- 计算图的正向传播进行一般的计算。通过计算图的反向传播,可以计算各个节点的导数
- 通过将神经网络的组成元素实现为层,可以高效地计算梯度
第六章 与学习相关的技巧
- 神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化(optimization)
- 随机梯度下降 SGD(stochastic gradient descent):如果函数的形状非均相 anisotropic,比如呈延伸状,搜索的路径就会非常低效。根本原因:梯度的方向并没有指向最小值的方向
- Momentum 动量:保存物体的速度,可以减弱 之 字形变动
- AdaGrad:会记录过去所有梯度的平方和。大幅更新的参数其学习率会变小。这样可能导致学习率无限趋近与 0。这时可以使用 RMSProp 方法,会逐渐遗忘最早的梯度(指数移动平均)
- Adam:融合 Momentum 和 AdaGrad 方法。设置学习率,beta 系数的标准值为 0.9 和 0.999,一般这样设置就可以。
- 为了防止“权重均一化”,必须随机生成初始值
- 偏向 0 和 1 的数据分布会造成反向传播中梯度的值不断变小,最后消失,也就是梯度消失问题(gradient vanishing)
- 各层的激活值的分布都要求有适当的广度,如果传递的是有所偏向的数据,就会出现梯度消失或者“表现力受限”的问题,导致学习可能无法顺利进行。
- Xavier 初始值:与前一层有 n 个节点连接时,初始值用标准差为 $1/\sqrt{n}$ 的分布
- 如果设定了合适的权重初始值,则各层的激活值分布会有适当的广度,从而可以顺利进行学习。那么为了使各层拥有适当的广度,“强制性”调整激活值的分布,这就是 batch normalization 的原始想法
- 可以增大学习率
- 不那么依赖初始值
- 抑制过拟合(降低 Dropout 等的必要性)
- 发生过拟合的原因主要有两个:1)模型拥有大量参数、表现力强;2)训练数据少
- 权值衰减是常用的抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合(也就是正则化
- Dropout 是一种在学习的过程中随机删除神经元的方法
- 逐渐缩小“好值”存在范围是搜索超参数的一个有效方法
第七章 卷积神经网络
- 新增了 Convolution 层和 Pooling 层
- 全连接的问题在于数据的形状被“忽视”了
- 新增概念:卷积运算、填充 padding、步幅 stride
- 对于多个通道的输入数据,滤波器也需要有同样的通道数,但是计算得到的卷积,最终只输出一个特征图。如果需要生成多个,就需要多个滤波器(这里的参数数量需要仔细理解!)
- 池化层的特征
- 没有要学习的参数:只是选取最大值、平均值
- 通道数不发生变化:计算按照通道独立运行
- 对微小的位置变化具有鲁棒性
- Numpy 中访问元素最好不要用 for 语句,可以使用 im2col 语句
- 具有代表性的 CNN
- 1998 LeNet:sigmoid,用 subsampling 而非 maxpooling
- 2012 AlexNet:relu,局部正规化 LRN 层,Dropout
第八章 深度学习
- VGG 网络
- 基于 3x3 的小型滤波器的卷积层
- 激活函数 ReLU
- 全连接后面使用 Dropout
- 基于 Adam 的最优化
- 使用 He 初始值作为权重初始值
- Data Augmentation 方法简单,但可以显著提高识别精度
- 加深层可以减少网络的参数数量,用更少的参数达到同等水平的表现力
- 叠加小型滤波器来加深网络的好处是可以减少参数的数量,扩大感受野 receptive field。
- 加深层可以分层次地分解需要学习的问题,可以分层次传递信息
- VGG, GoogLeNet, ResNet
- 深度学习的高速化的主要课题就是如何高速、高效地进行大量乘积累加运算
- 在深度学习中,即使是 16 位的半精度浮点数也可以顺利进行学习,计算量减半
- 深度学习的应用:物体检测、图像分割、图像标题生成(引申出多模态处理)
- 未来发展:图像风格变换、图像的生成(GAN)、自动驾驶、强化学习
- 注,本章代码可以参考 这里