《深度学习入门:基于python的理论与实现》
目录
第七章 卷积神经网络(convolutional neural network)
第一章 python入门
深度学习的框架有:caffe、tensorflow、chainer、theano
python使用的外部库:numpy库:用于数值计算的库,提供高级的数学算法和便利的数组(矩阵)操作方法
matplotlib库:用来画图的库,可将实验结果可视化
第二章 感知机
感知机接收多个信号,输出一个信号。其信号只有流/不流俩种取值,0对应不传递信号,1对应传递信号。
输入信号在被送往神经元的时候,会分别乘以固定的权重,神经元计算传送过来的信号的总和,当总和超过某个界限值时,输出1,即神经元被激活。
权重就相当于电阻,权重越大,对应该权重的信号的重要性就越高。
感知机的实现
- 可以用简单的逻辑电路关系得到
- 导入权重和偏置:b称为偏置,是调整神经元被激活的容易程度的参数;w1,w2称为权重,是控制输入信号的重要性的参数
偏置的输入信号一直是1,为了区别其他神经元,一般涂成灰色
与门、与非门、或门是具有相同构造的感知机,区别只在于权重和偏置不同。
感知机的局限性
它只能表示由一条直线分割的空间,单层感知器无法实现异或门,但可以通过叠加实现。
理论上来说,2层感知机可以表示任意函数。,可以表示计算机进行的处理,通过叠加层能够进行非线性的表示。
第三章 神经网络
感知机的好处是即便是复杂的函数也可以表示,缺点是设定合适的、能符合预期的输入输出的权重是由人工进行的。
神经网络的一个重要性质就是可以自动地从数据中学习到合适的权重参数。
1. 神经网络
包括有输入层、中间层(隐藏层)、输出层。由3层神经元构成,但实质上只有2层神经元有权重,故称为“2层网络”
引入新函数h(x). y=h(b+).
h(x)会将输入信号的总和转换为输出信号,这种函数一般称为激活函数。(激活函数的作用在于决定如何来激活输入信号的总和)激活函数是连接感知机和神经网络的桥梁。
- 朴素感知机:单层网络,激活函数使用阶跃函数。
- 多层感知机:神经网络,使用sigmoid函数等平滑的激活函数的多层网络。
2. 激活函数
感知机和神经网络的主要区别就在于激活函数。激活函数最好具有关于原点对称的性质。
- 阶跃函数:给定某个输入后,会返回某个输出的转换器。转换后的信号传送到下一个神经元。阶跃函数的参数x只能接受实数(浮点数),但不允许参数取numpy数组step_function(np.array([1.0, 2.0]))。
>>> import numpy as np
>>> x = np.array([-1.0, 1.0, 2.0])
>>> x
array([-1., 1., 2.])
>>> y = x > 0
>>> y
array([False, True, True], dtype=bool)数组中大于0的元素转换为true,小于等于0的元素转换为false,从而生成一个新的数组y,也可以用astype()方法转换numpy数组的类型。
>>> y = y.astype(np.int)
>>> y
array([0, 1, 1])
-
sigmoid函数
sigmoid函数能支持numpy数组(因为numpy的广播功能)。sigmoid函数时关于(0,0.5)对称的S型曲线。
对比阶跃函数和sigmoid函数
不同点:阶跃函数对应感知机中神经元之间流动的0,1信号;而神经网络中流动的是连续的实数值信号。
相同点:输入小时,输出接近与0,随着输入增大,输出向1靠近;俩者均是非线性函数 。
- ReLU函数(rectified linear unit 函数)
神经网络的激活函数必须使用非线性函数,使用线性函数时,无法发挥多层网络带来的优势,为了发挥叠加层所带来的优势,激活函数必须使用非线性函数。
- 输出层的激活函数
神经网络可以用在分类问题(数据属于哪一个类别,如区分男性和女性)和回归问题(根据某个输入预测一个数值)上,回归问题使用恒等函数,二元分类问题使用sigmoid函数,多元分类问题使用softmax函数。输出层的激活函数使用表示。
- 恒等函数:将输入按原样输出。当输出层使用恒等函数时,输入信号会原封不动的被输出。
- softmax函数:分子时输入信号
的指数函数,分母是所有输入信号的指数函数的和。输出层的各个神经元都受到所有输入信号的影响。
注:输出是0到1之间的实数;函数的输出值的总和是1;所以可以称softmax函数的输出解释为“概率”。
机器学习的问题可以分为“学习”和“推理”两个阶段。学习也称为训练,强调算法从数据中学习模型。指使用训练数据、自动调整参数的过程。推理处理也称为神经网络的前向传播。
3. 多维数组的运算
由二维数组的乘积过渡到神经网络的内积,神经网络的运算可以作为矩阵运算打包进行。如下图所示为权重的符号。
输出层的神经元数量:根据待解决的问题来决定
分类问题:输出层的神经元数量一般设定为类别的数量。
4. 手写数字识别
- 学习方法:先学习再推理,使用学习到的参数进行神经网络的推理处理,进行推理时,使用刚才学习到的参数,对输入数据进行分类。
- 数据集——MNIST数据集:
图像数据是28*28像素的灰度图像,各个像素取值在0到255之间。每个图像都有相应的标签。
- 神经网络的推理处理:
输入层有784(28*28)个神经元,输出有10个神经元,神经网络有2个隐藏层,第一个隐藏层50个神经元,第二个隐藏层100个神经元。用predict()函数进行分类,即计算出该数组为0,1……的概率分别为多少,再用np.argmax()取出数组中的最大值的索引。
正规化:将数据限定在某个范围内的处理
预处理:对神经网络的输入数据进行某种既定的转换称为预处理
数据白化:将数据整体的分布形状均匀化的方法
- 批处理:输入数据和权重参数的形状。打包式的输入数据称为批(能够实现高速运算)
第四章 神经网络的学习
学习是指从训练数据中自动获取最优权重参数的过程,学习的目的是以损失函数为基准,找出能使它的值达到最小的权重参数,所用的方法是函数斜率阶梯法。
- 从数据中学习(数据自动决定权重参数的值)
- 数据驱动
先从图像中提取特征量(指可以从输入数据中准确地提取本质数据的转换器),再用机器学习技术学习这些特征量的模式。
在计算机视觉领域,常用的特征量包括SIFT、SURF和HOG等,使用这些特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。
这一步需要人为的去实现,对于不同的问题,使用合适的特征量。比如为了区分狗的脸部。
神经网络的优点是对所有的问题都可以用同样的流程来实现。是通过不断地学习所提供的数据,尝试发现待求解的问题的模式。也就是说,与待处理的问题无关,神经网络可以将数据直接作为原始数据,进行“端对端”的学习。(端到端的学习是指从原始数据中获得目标结果)
- 训练数据和测试数据
训练数据(监督数据):寻找最优的参数
测试数据:评价训练得到的模型的实际能力
之所以将数据分为这俩部分,主要是为了正确评价模型的泛化能力。
泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力。
2. 损失函数
神经网络的学习中所用的指标称为损失函数(loss function)。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。
- 均方误差
yk是表示神经网络的输出,tk表示监督数据,k表示数据的维数。
均方误差会计算神经网络的输出和正确解监督数据的各个元素之差的平方,再求总和。
将正确解标签表示为1,其他标签表示为0的表示方法称为one-hot表示
为什么要导入损失函数呢?在神经网络中,寻找最优参数时,要寻找使损失函数尽可能小的参数,此时需要计算参数的导数(梯度)。如果导数的值为正,则通过使该权重参数向负方向改变,可以减少损失函数的值;如果导数的值为负,通过使该权重参数向正方向改变,可以减少损失函数的值。
- 交叉熵误差(cross entropy error)
yk是表示神经网络的输出,tk是正确解标签,k表示数据的维数。
交叉熵误差的值由正确解标签所对应的输出结果决定的。正确解标签对应的输出越小,误差就越大。
- mini-batch学习
机器学习使用训练数据进行学习➡️针对训练数据计算损失函数的值➡️找出使该值尽可能小的参数,因此计算损失函数时必须将所有的训练数据作为对象,要将所有的损失函数作为学习的指标。
将单个数据的损失函数扩大了N份数据,之后再除以N进行正规化,获得平均损失函数。当数据太多时,从全部数据中选一部分作为近似值,选出的这批数据称为mini-batch,小批量,然后对每个mini-batch进行学习。np.random.choice()
3. 数值微分
- 导数➡️梯度
导数表示的是某个瞬间的变化量,x的微小变化将导致函数f(x)的值在多大程度上有变化。
数值微分:
- numerical_diff(f, x)。这个函数有俩个参数,函数f和传给函数f的参数x
- 利用微小的差分求导数的过程
梯度:
- 由全部变量的偏导数汇总而成的向量称为梯度(gradient)。
- 梯度会指向各点处的函数值减小最多的方向。
- 使用梯度来寻找损失函数最小值的方法就是梯度法。目的是寻找梯度为0的点。
梯度法:
- 通过不断地沿梯度方向前进,逐渐减小函数值的过程就是梯度法(gradient method)。梯度法是解决机器学习中最优化问题的常用方法,特别是在神经网络的学习中经常被使用。
- 学习率决定在一次学习中,应该学习多少,以及在多大程度上更新参数。学习率过大的话,会发散成一个很大的值;反过来,学习率过小的话,基本上没怎么更新就结束了。也就是说,设定合适的学习率是一个很重要的问题。
- 学习率也称为超参数,不同于神经网络的权重参数,由训练数据和学习算法自动获得。
- 神经网络的梯度是指损失函数关于权重参数的梯度。
神经网络存在合适的权重和偏置,调整权重和偏置以便拟合训练数据的过程称为“学习”。神经网络的学习分成下面4个步骤:
步骤1(mini-batch)
从训练数据中随机选出一部分数据,这部分数据称为mini-batch。我们的目标是减小mini-batch的损失函数的值。
步骤2(计算梯度)
为了减小mini-batch的损失函数的值,需要求出各个权重参数的梯度。梯度表示损失函数的值减小最多的方向。
步骤3(更新参数)
将权重参数沿梯度方向进行微小更新。
步骤4(重复以上步骤)
这个方法是通过梯度下降法更新参数,称为随机梯度下降法(stochastic gradient descent)
随机是因为使用的数据是随机选择的mini batch数据,一般使用SGD函数来实现。
第五章 误差反向传播法
一、反向传播:
基于链式法则成立(计算各个节点的导数)。计算图的节点是由局部计算构成的,局部计算构成全局计算。计算图的优点就是可以通过正向传播和反向传播高效地计算出各个变量的导数值。
- 加法节点的反向传播:只是将输入信号输出到下一个节点(加法节点的反向传播只乘1,所以输入的值会原封不动的流向下一个节点),不需要正向传播的输入信号。
- 乘法节点的反向传播:会将上游的值乘以正向传播时的输入信号的“翻转值”后传递给下游,需要正向传播的输入信号。
二、简单层的实现:
“层”是神经网络中功能的单位,乘法节点和加法节点都称为层。将神经网络的组成元素实现为层,可以高效地计算梯度。层的实现有俩个方法,forward和backward
- 加法层原封不动传到下一层;
- 乘法层将上游的值乘以翻转后输入信号的值传递给下一层
三、激活函数层的实现:
- ReLU层:如果正向传播时的输入x大于0,则反向传播会将上游的值原封不动地传给下游(函数的导数值在大于0时为1)。反过来,如果正向传播时的x小于等于0,则反向传播中传给下游的信号将停在此处(函数的导数值在小于0时为0)。
- Sigmoid层:明确记住输入和输出结果即可,是简化后的sigmoid层的计算图。
四、Affine/Softmax层的实现:
- Affine层:神经网络的正向传播中进行的矩阵的乘积运算在几何学领域被称为“仿射变换”。因此,这里将进行仿射变换的处理实现为“Affine层”。其表达式为
。
- 批版本的Affine层:N个数据一起进行正向传播的情况。
- Softmax-with-Loss 层:softmax函数会将输入值正规化之后再输出。所以在神经网络的最后一层都有softmax层。
神经网络中进行的处理有推理和学习俩个阶段。推理过程只需要给出一个答案,只对得分最大值感兴趣,所以不需要softmax层;而神经网络的学习阶段则需要softmax层。
Softmax-with-Loss 层(softmax层和交叉熵误差),神经网络学习的目的就是通过调整权重参数,使神经网络的输出接近教师标签。使用交叉熵误差作为softmax函数的损失函数后,反向传播可以得到(y1 − t1, y2 − t2, y3 − t3)这样“漂亮”的结果。
五、小结
本章内容主要是以层为单位实现了神经网络中的处理。学过的有ReLU层、Softmax-with-Loss层、Affine层、Softmax层,在层中实现了forward和backward方法,通过将数据正向和反向地传播,可以高效地计算权重参数的梯度。通过使用层进行模块化,神经网络中可以自由地组装层,轻松构建出自己喜欢的网络。
神经网络学习的步骤:
前提:神经网络中有合适的权重和参数,调整权重和偏置以便拟合训练数据的过程称为学习。
步骤1:从训练数据中随机选择一部分数据(mini-batch)
步骤2:计算梯度。计算损失函数关于各个权重参数的梯度(用误差反向传播算法计算)
梯度确认:比较数值微分和误差反向传播法的结果,可以确认误差反向传播法的实现是否正确。
步骤3:更新参数。将权重参数沿梯度方向进行微小的更新。
步骤4:重复以上步骤。
第六章 与学习相关的技巧
参数的更新
寻找最优参数的过程就是最优化:神经网络学习的目的就是找到使损失函数的值尽可能小的参数。参数数量庞大,空间复杂,使得最优化问题很复杂。
- 方法一:SGD随机梯度下降法(stochastic gradient descent):使用参数的梯度,沿着梯度方向更新,重复多次,从而逐渐靠近最优参数。
效率低下,呈“之”字形朝最小值移动,效率低下
- 方法二:Momentum:出现了一个新的变量v,对应物理上的速度。表示在梯度方向上受力,在这个力的作用下,物体的速度增加。可以更快的朝x轴方向靠近,减弱“之”字形的变动程度。
- 方法三:AdaGrad(Adaptive):针对一个一个参数,赋予其定制的值,即为每个元素适当的调整学习率。出现了新的变量h,保存了以前的所有梯度值的平方和,在更新参数时,乘上h,来调整学习的尺度。参数的元素中变动较大(被大幅更新)的元素的学习率将变小。也就是说,可以按参数的元素进行学习率衰减,使变动大的参数的学习率逐渐减小。
- 方法四:Adam:融合了Momentum和AdaGrad的方法。Adam会设置 3个超参数。一个是学习率(论文中以α出现),另外两个是一次momentum系数β1和二次momentum系数β2。根据论文,标准的设定值是β1为 0.9,β2 为 0.999。
权重的初始值
- 权值衰减:抑制过拟合,提高泛化能力。一种以减小权重参数的值为目的的进行学习的方法,从而抑制过拟合的发生。但是不可以将权重初始值设为0,因为在误差反向传播中,所有的权重值会进行相同的更新。未来防止权重均一化,必须随机生成初始值。
- 隐藏层的激活值分布:各层的激活值的分布都要求有适当的广度。因为通过在各层间传递多样性的数据,神经网络可以进行高效的学习。反过来,如果传递的是有所偏向的数据,就会出现梯度消失或者“表现力受限”的问题,导致学习可能无法顺利进行。梯度消失就是偏向0和1的数据分布会造成反向传播中梯度的值不断变小,最后消失。
- Xavier初始值被作为标准使用,如果前一层的节点数为n,则初始值使用标准差为
的分布。使用该初始值后,前一层的节点数越多,要设定为目标节点的初始值的权重尺度就越小。Xavier初始值是以激活函数是线性函数为前提而推导出来的,sigmoid函数和tanh函数等S型曲线函数。
- ReLU的权重初始值(也称为“He初始值”):激活函数为ReLU函数,当前一层的节点数为n时,He初始值使用标准差为
的高斯分布。当Xavier初始值是
时,(直观上)可以解释为,因为ReLU的负值区域的值为0,为了使它更有广度,所以需要2倍的系数。
Batch Normalization
在明确各个激活函数所对应的权重初始值,以及所对应的激活值分布会有适当的广度。那么为了使各层拥有适当的广度,“强制性”的调整激活值的分布会怎样呢?引入了Batch Normalization的概念(简称Batch Norm),其思路是调整各层的激活值分布使其拥有适当的广度,即向神经网络中插入对数据分布进行正规化的层(Batch Norm层)。
Batch Norm:以进行学习时的mini-batch为单位进行正规化。即进行使数据分布的均值为0、方差为1的正规化。其具有很多优点;
- 快速的进行学习(增大学习率)
- 不那么依赖初始值
- 抑制过拟合(降低dropout的必要性)
正则化
过拟合:是指只能拟合训练数据,但不能很好的拟合不包含在训练数据里的未观测数据。出现的原因:模型拥有大量参数,表现力强,训练数据少。
抑制过拟合的方法有:
- 权值衰减可以抑制过拟合,对大的权重进行惩罚来抑制过拟合,例如为损失函数加上权重的平方范数(L2范数),表示为
,将
加到损失函数上,
是控制正则化强度的超参数,λ设置得越大,对大的权重施加的惩罚就越重。此外, 开头的分数是用于将求导结果变成λW的调整用常量。对于所有权重,权值衰减方法都会为损失函数加上 L2范数。因此,在求权
重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数λW。
L2范数是各个元素的平方和。L2范数、L1范数、L∞范数都可以用作正则化项,它们各有各的特点,不过这里我们要实现的是比较常用的 L2范数。 - Dropout:在学习过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元然后将其删除。训练时,每传递一次数据,就会随机选择要删除的神经元。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出。
正向传播时传递了信号的神经元,反向传播时按原样传递信号;正向传播时没有传递信号的神经元,反向传播时信号将停在那里。
在学习过程中随机删除神经元,从而每一次都让不同的模型进行学习;在推理过程中,通过对神经元的输出乘以删除比例,可以取得模型的平均值
超参数的验证
神经网络中的参数有,权重,偏置,超参数(各层的神经元数量、batch大小、参数更新时的学习率或权值衰减等)
- 验证数据:数据集分为训练数据和测试数据,训练数据用于学习,测试数据用来评估泛化能力(评估是否只过度拟合了训练数据,以及泛化能力如何)。设置超参数的值来进行验证,不能使用测试数据评估超参数的性能,因为会导致超参数的值被调整为只拟合测试数据,从而出现过拟合现象。调整超参数时,必须使用超参数专用的确认数据,即验证数据。
- 超参数的最优化:实践上,随机采样的搜索方式。科学上,贝叶斯最优化。
步骤1:设定超参数范围
步骤2:从设定的参数范围中随机采样
步骤3: 使用步骤1中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置得很小)
步骤4: 重复步骤1和步骤2(100次等),根据它们的识别精度的结果,缩小超参数的范围。
第七章 卷积神经网络(convolutional neural network)
整体结构
CNN中新增卷积层和池化层,池化层可以省略
卷积层和池化层的实现
- 卷积层
- 全连接图使用的是Affine层,相邻的神经元全部连接在一起,输出的数量可以任意决定。但全连接图存在的问题是,数据的形状被忽略了,将全部的输入数据作为相同的神经元处理,无法利用与形状相关的信息。卷积层可以保持形状不变,图像可以理解为具有形状的数据。将卷积层的输入输出数据称为特征图。
- 卷积运算:相当于图像处理中的“滤波运算”。滤波器的参数对应之前的权重,在与权重运算完成后,给每一个数据都加同一个偏置。
-
在进行卷积层的处理之前,有时要向输入数据的周围填入固定的数据,这称为填充(pad)。幅度为1的填充是指用幅度为1像素的0填充周围。使用填充主要是为了调整输出的大小,防止每次进行卷积运算都会缩小空间,直到缩小为1无法进行计算。使用填充可以保持空间大小不变的情况下将数据传递给下一层。
应用滤波器的位置间隔称为步幅(stride)。增大步幅后,输出大小会变小,增大填充后,输出大小会变大。
假设输入大小为(H, W),滤波器大小为(FH, FW),输出大小为(OH, OW),填充为P,步幅为S。此时,输出大小可通过这个式子进行计算。注意设定的值必须使式子可以除尽。
- 三维数据的卷积运算:除了长和宽,还需要处理通道方向,写为多维数组时,顺序为(channel, height, width)。通道方向上有多个特征图时,会按通道进行输入数据和滤波器的卷积运算,并将结果相加得到输出。滤波器和输入数据的通道数要相同。左图数据输出时一张特征图,通道数为1.如果要在通道方向上拥有多个卷积运算的输出,需要用到多个滤波器。
- 池化层
池化是缩小高、长方向上的空间的运算。一般有Max池化(获取最大值的运算),Average池化(计算目标区域的平均值),池化的窗口大小会和步幅设定成相同的值。
池化层的特征:
没有要学习的参数:池化层和卷积层不同,没有要学习的参数。池化只是从目标区域中取最大值(或者平均值),所以不存在要学习的参数。
通道数不发生变化:经过池化运算,输入数据和输出数据的通道数不会发生变化,计算是按通道独立进行的。
对微小的位置变化具有鲁棒性(健壮):输入数据发生微小偏差时,池化仍会返回相同的结果。因此,池化对输入数据的微小偏差具有鲁棒性。
- 卷积层和池化层的实现
给卷积层和池化层进行实现的类赋予forward和backward方法。
- 卷积层的实现:基于im2col(image to column从图像到矩阵)展开输入数据。im2col是一个函数,将输入数据展开以适合滤波器(权重),对3维的输入数据应用到im2col后,数据转换为2维矩阵。卷积层的滤波器纵向展开为1列,再计算2个矩阵的乘积。CNN中数据会保存为4维数组。(reshape的原因)
- 池化层的实现:展开输入数据;求各行的最大值;转换为合适的输出大小。在通道上是独立的,输入和输出的通道数相同。
CNN
- CNN的可视化
- 学习前的滤波器是随机进行初始化的,没有规律可循,在学习后,更新成了有规律的滤波器(如从白到黑渐变,含有块状区域)
- 权重的元素是实数,但在图像的显示上,将最小值显示为黑色(0),最大值显示为白色(255)。
- 可视化的内容:有规律的滤波器在观察什么?在观察边缘(颜色变化的分界线)和斑块(局部的块状区域)等。第一层:卷积层的滤波器会提取边缘或斑块等原始信息,CNN会将这些原始信息传递给后面的层。随着层次加深:提取的信息也越来越抽象,神经元从简单的形状向“高级”信息变化。
- 具有代表性的CNN
LeNet(手写数字识别的网络):激活函数是sigmoid函数;使用子采样(subsampling)缩小中间数据的大小
AlexNet:叠有多个卷积层和池化层。激活函数是ReLU;使用局部正规化的LRN层;使用Dropout
VGG:它的特点在于将有权重的层(卷积层或全连接层)叠加至16层(或者19层),具备了深度(根据层的深度,有时也称为“VGG16”或“VGG19”)。基于3×3的小型滤波器的卷积层的运算是
连续进行的。重复进行“卷积层重叠2次到4次,再通过池化层将大小减半”的处理,最后经由全连接层输出结果。
GoogLeNet:网络不仅在纵向上有深度,在横向上也有深度。
ResNet:比以前的网络具有更深的结构。引入了横跨层的快捷结构。
还有很多基本的网络结构之后会做详细的论述😄
np | 调用numpy。import numpy as np |
array | 数组 |
np.dim | 数组的维数 |
A.shape | 数组的形状,及几行几列,以数组的形式输出 |
np.dot | 接收俩个numpy数组作为参数,返回数组的乘积 |
identity_function | 恒等函数 |
init_network | 进行权重和偏置的初始化,并保存在字典变量network中 |
forward | 函数封装了将输入信号转换为输出信号的处理过程 |
numerical_diff(f,x) | 数值微分,函数f和传给函数f的参数x |
np.zeros_like(x) | 生成一个形状和x相同,所有元素都为0的数组 |
numerical_gradient(f,x) | 参数f为函数,x为numpy数组,该函数对numpy数组x的各个元素求数值微分 |