深度学习入门与实战
Posted gonghaiyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习入门与实战相关的知识,希望对你有一定的参考价值。
基本名词解释
-
训练:确定算法模型中参数的过程称训练。训练是一个不断迭代的过程。
-
训练集:训练中使用的数据称为训练集。训练集的质量决定模型的执行。
-
Epoch:遍历一遍训练数据称为一个"Epoch"。训练模型可以告诉模型要训练多少个Epoch。训练模型的epoch数必须让模型达到一个收敛的状态。
-
Batch size:每次取一定数量的数据进行学习,这个数量叫Batch size。Batch size的大小一般取决于计算机硬件资源。如GPU(CPU)、内存。
-
Step(Iterator):每一次取学习的数据叫一个Step。
-
评估:在训练的过程中,我们可能会保存很多个模型,利用这些评估指标衡量模型表现,最后通过指标的结果挑选一个最优的模型。这一过程就是“评估”。
-
评估集:验证模型正确与否的数据集称为“评估集”,“验证集”。这部分数据不会在训练过程中使用。
-
混淆矩阵:混淆矩阵是我们经常使用的一种对分类结果可视化的方法。我们可以通过混淆矩阵计算出精确率、准确率与召回率。
我们来看它们分别所代表的含义。
(1)正例:代表我们目标的类别。
(2)负例:代表我们不关心的那个类别。
(3)TP(真正):真实的类别是正例,同时模型也预测为正例。
(4)FN(假负):真实的类别是正例,但模型预测为负例。
(5)FP(假正):真实的类别是负例,但模型预测为正例。
(6)TN(真负):真实的类别是负例,同时模型也预测为负例。 -
准确率:准确率(Accuracy)可以衡量预测值与真值相符合的程度,它是非常常见的评估指标。通常准确率越高,分类模型的性能就越好。
Accuracy = (TP + TN ) / (TP + FP + FN + TN)
在这个场景中,显而易见,正例是机器的异常情况,而负例则是正常状况,是我们不关心的“其他”。假设验证集中有 1 万条数据,其中只有 5 条数据是异常,其他都是正常的。但我们的模型把这 1 万条数据全部预测为正常,它的准确度为:
accuracy = (10000 - 5) / 10000 = 0.9995
此时这个模型的准确率非常高,但它的性能又如何呢?非常差,因为我们关心的异常记录,在模型的预测中,一条也没有发现。
- 精确率
精确率(Precision)指的是如果模型认为一条数据是正例,那么有多大的概率确实是正例。计算公式如下。
precision = TP / (TP + FP)
- 召回率
召回率(Recall)可以用来衡量模型能在验证集中找回多少正例。计算公式如下。
recall = TP / (TP + FN)
- F1-Score
F1-Score是根据精确率(Precision)与召回率(Recall)来计算的。F1-Score 是一个综合了精准率与召回率的评估指标,在矛盾的情况下,它能够准确地评估模型的性能。它的计算公式如下。
F1 = 2 * Precision * Recall / (Precision + Recall)
-
均方误差
上面讲到的评估指标都是用于分类任务的,下面我们来看看回归任务常用的评估指标:均方误差。假设我们有模型 f(x),输入 x 的真实值为 y,验证集中一共有 m 条记录。那么均方误差的计算公式如下:
均方误差衡量的是模型预测的结果与真实值之间的差距,均方误差越小意味着模型的预测结果越接近真实结果。 -
损失函数
下面举个例子来说明损失函数。
下图的黄色曲线,我们假定其函数式为 f1,函数跟样本点的拟合较差,基本上都不在曲线上。我们称为“欠拟合”。
下图的红色曲线,我们假定其函数式为 f2,虽然把每个样本点都拟合到位了,但是如果我们新增一个点,红色曲线就拟合不好了。而且这条曲线实在太过复杂,很难用一个相对简单的函数表达式去表示。这种情况较“过拟合”。
绿色曲线,我们假定其函数式为 f3,这条曲线看起来很简单,意味着参数少、好表示,并且每个样本点都离曲线很近,没有大的偏差。
假定这几个样本点的真实函数式为 F(x),我们拟合出来的函数式为 f(x)。那么,对于每一个样本点,真实值和拟合值之间就存在了一个误差,我们可以通过一个公式来表示这个误差:L(x)=(F(x)-f(x))2。通过这个计算函数,我们可以知道用这个拟合函数去表示真实函数时,表示效果的好坏,我们称这个函数为损失函数(loss fuction)。损失函数越小,拟合函数就能越好的拟合真实函数。 -
代价函数
沿用前面的例子,当我们手里的样本很多时,它们构成了一个训练集。我们将训练集上所有点的拟合误差做一个平均,可以用如下的函数进行表示:
这个函数我们称为代价函数(cost function),这种训练集数据的平均损失,我们称为经验风险。同样,只要我们将经验风险最小化,那么拟合出来的函数就会更接近实际的函数。 -
目标函数
当我们把经验风险最小化的同时,就意味着我们要尽力照顾到所有的样本点,那我们得到的函数有可能就成为前面红色的曲线,产生“过拟合”。同时也会让曲线变得复杂,带来更多的参数。于是我们就需要解决两个问题:如何尽可能地减少参数,以及如何不要产生过拟合。
那什么函数可以让模型变得简单,或者让模型尽量不要过拟合呢?对的,就是我在“01 课时”提到的范数:L1 范数可以实现权值稀疏,L2 范数则用来防止过拟合。
我们在损失函数后面加上度量模型复杂度的函数 J(f),称作正则化(regularization)。L1 和 L2 范数是较为常用的度量模型复杂度的函数。训练集的平均损失为经验风险,这个度量模型的复杂度函数项我们就称为结构风险。
由此,我们将经验风险和结构风险同时最小化,就得到了如下的函数:
函数的第一部分就是刚才说的损失函数,J(f)就是正则化项,λ 为正则化项系数。
我们可以把 λ 看成是一种惩罚因子,如果我们不希望模型的结构风险太大,那么就可以增大 λ,这样模型就会朝着减小参数复杂度的方向训练。在实际项目开发中,正则化项不用在一开始添加,可以先只用带有经验风险项的损失函数来训练模型,模型训练结束后,再尝试加入正则化项,且 λ 也需要人为或者按照一定规律进行不断的调整和尝试。
常用的损失函数
前面有了解过损失函数,那有哪些常用的损失函数帮我们优化模型?接下来了解下常见的损失函数:0-1 损失函数、平方损失函数、均方差损失函数、交叉熵损失函数和 Softmax 损失函数。
0-1 损失函数
我们评估一个分类模型的时候,最简单的评估方法就是:如果模型预测对了,我们就认为损失函数的值为 0,如果模型预测错了就给出 1。这个就是最简单的 0-1 损失函数,公式化的表示就是:
0-1 损失函数是最简单的损失函数。
平方损失函数
前面讲的损失函数L(x)=(F(x)-f(x))2。这个就是平方损失函数,它直接测量了机器学习模型的输出与实际结果之间的距离,一般多用于回归问题。
均方差损失函数
均方误差(Mean Squared Error,MSE)是回归损失函数中最常用的误差,它是预测值与目标值之间差值的平方和。其公式化表示为:
其中 y 为目标值的向量表示,y’ 为预测值的向量表示。
交叉熵(shang)损失函数
在前面的文章中说过,交叉熵表示用拟合分布来表示实际分布的困难程度。公式如下。
我们把函数的格式稍微改变一下就得到了交叉熵损失函数(Cross entropy loss)的公式:
其中,p(x) 表示真实概率分布,q(x) 表示预测概率分布。
通过函数不难发现,交叉熵损失函数实际上就是通过缩小两个概率分布的差异(误差),来使预测的概率分布尽可能达到真实的概率分布,一般多用于分类问题。
Softmax 损失函数
前面我们了解到,Softmax 函数它可以把输入的几个数映射为 0-1 之间的实数,并且归一化后仍能够保证几个数的和为 1。它的公式化表示为:
假设有一个数组,共有 T 个元素,其中第 j 个元素的 Softmax 值就可以通过上面的公式计算得到,即该元素的指数与所有元素指数和的比值。回到刚才的交叉熵损失函数公式中的 q(xi),也就是预测的概率分布,如果我们换成 Softmax 方式的表示,即为:
该损失函数我们就称为Softmax 损失函数(Softmax loss),也称为 Softmax with cross-entropy loss,它是交叉熵损失函数的一个特例,和交叉熵损失函数一样,一般也用于分类问题。
优化方法之梯度下降
有了约束(损失函数),模型就可以根据它来学习,而这个学习又要使用到优化方法,也就是模型如何学习或更新。
优化算法实际上做的就是如何在模型表征的空间中找到模型评估效果指标最好的模型的过程。具体来说,假定我们有一个函数 f(x), 我们需要找到一组参数权重,使 f(x)的值最小。常见的优化方法包括梯度下降法、牛顿法和拟牛顿法、共轭梯度法等,种类和变体非常多,不同的优化方法适用场景有所不同,但目的都是为了寻找最优的模型。
这些优化方法,一般都已经有了封装好的函数来调用,我们这里只了解几种经典的优化方法。
梯度下降法
假设现在有一座山,你站在山顶,但是你有点饿,想赶紧下山,去饭店吃饭,那此时我们该怎么办呢?
在不考虑性命危险的情况下,最快的方法就是:哪个地方坡度大,哪个地方最陡峭,就往哪里走。于是,在确认自己的位置之后,我们就可以对比当前所在位置的所有方向,寻找坡度下降最快的方向,然后往前走一步到达新的位置,再寻找新的位置所有方向中下坡度最陡的方向,如此反复,直到达到最低点。
具体来说,在一个三维空间曲线中,任何一点我们都能找到一个与之相切的平面(更高维则是超平面),这个平面上有无穷多个方向,但是只有一个是曲线函数下降最快的梯度。每次优化我们都沿着最快下降的梯度方向进行,这就是梯度下降。
下山的过程中,方向很关键,但是步子大小也同样重要。步子太大了,就可能跑到别的山谷中,也有可能永远也走不到饭店的位置,最后每次都要多走一段然后再走回来,反反复复地在接近和远离饭店的路上大踏步地走动(在模型训练中,这种反复称为“震荡”,比如人脸核身接口某段时间内识别率较差);但步子太小就要计算很多次,还没有走到饭店,人就饿死了。
这个步子的大小,在算法中就是参数的学习率(learning rate)。绝大多数情况下,因为步长的存在,我们不可能走到最精确的最低点,最后会在最小值的某一个区域内反复震荡。这个误差我们是可以接受的,如果在一段时间内误差没有发生新的变化,那我们就可以近似地认为训练已经收敛了。
批量梯度下降法(Batch Gradient Descent,BGD)
假设有一个线性回归函数模型,y 是真实的数据分布函数,hθ(x)是我们拟合的函数,θ 是参数,也是我们要求的权值。那我们整个优化的过程,就是为了找到最好的 θ。损失函数J(θ)可以表示为:
其中,m 表示样本数量。要想拟合真实分布,那么损失函数就要最小(误差最小),为了得到这个最小值,我们就要使用梯度,即“梯度向量的方向即为函数值增长最快的方向”,让损失函数以最快的速度减小。
因此,我们要先对 J(θ)中的 θ 求偏导数,这样就可以得到每个 θ 对应的梯度:
得到了每个 θ 的梯度之后,我们就可以按照下降的方向去更新每个 θ:
其中α为学习率。更新 θ 后,我们就得到一个更新之后的损失函数,它的值也就更小了,我们就越接近要拟合的函数了。
从以上过程我们可以看到,这个方式的优化可以得到全局的最优解,因为每次都会使用所有的 m 个样本。但在很多的场景中,样本很多,如果每走一步就要动用全部的数据,会造成十分庞大的计算量。因此,在数据量大的场合我们一般不推荐使用批量梯度下降法。为了解决这个问题,随机梯度下降法就出现了。
随机梯度下降法(Stochastic Gradient Descent,SGD)
不同于批量梯度下降法,随机梯度下降每次只使用一个随机选择的样本来更新θ,则更新过程变成了:
SGD 在每轮迭代中,会根据随机选择的一条数据对应的损失函数值来优化,这样一来,即便样本数量很大,整个过程也只需要使用其中一部分数据就可以完成更新了。每一轮参数的更新速度会大大加快,能够更快地得到最优解。
但 SGD 带来高速和便捷的同时,也带来了一些风险。
比如,我们的训练数据中不可避免地会存在错误样本,即噪声数据。如果在一次更新中好巧不巧的,就是使用了噪声数据,就会造成一定程度的准确度的下降,这一次优化就不是朝着最好的方向发展了。此外,因为每次只用一个样本进行优化,而单个样本并不能代表全体样本的情况,所以可能会收敛到局部最优。
随机梯度下降方法用损失很小的一部分精确度和增加一定数量的迭代次数为代价,换取了最终总体的优化效率的提高。当然,这个过程中增加的迭代次数是要远远小于样本的数量的。
小批量梯度下降法(Mini-Batch Gradient Descent, MBGD)
MBGD 的方法实际上相当于批量梯度下降和随机梯度下降的折中,即每次使用一个固定数量的数据进行优化。这个固定数量,就是 Batch Size。
不同于批量梯度下降,该方法每次选择一个 Batch(例如 32、64 等)进行优化,这会显著减少每次的计算量,提高收敛的速度。并且,在实际的计算过程中,引入了矩阵计算,让该方法计算速度并不比 SGD 慢太多。
需要注意的是,Batch Size 也是一个不断尝试的参数,过大过小都不好。如果 Batch Size 太大,每次计算的样本数就大,计算量就会急剧增大,内存或者显存的空间占用也就大;如果太小,则达不到 Batch Size 的初衷。
基于随机梯度下降法,人们又提出了 Momentum、Nesterov Momentum 等方法,这部分知识你如果有兴趣可以自行查阅学习。
深度学习分类
有监督学习
有监督学习就是我们常说的预测任务,即分类与回归。分类与回归的区别就是输出的内容。当给定输入时,分类输出的是具体的类别, 而回归输出的是一个数值。比如上篇中输出0-9具体的数字,就是一个分类任务。比如对某个地区的房价进行预测,最后预测的结果是一个数值,这就是回归任务。
在有监督学习中,它的训练集是需要被标记好的。
无监督学习
无监督学习是在没有额外信息的情况下自动提取数据中模式和结构。常见的无监督学习算法有聚类算法。K-Means、层级聚类、EM 算法等。深度学习中具有代表性的无监督学习是 GAN 与自编码器。
有监督学习和无监督学习最大的区别就在于,有监督学习的训练集是需要人工提前标记的。
模型的自我学习
前馈网络
前馈神经网络(Feedforward Neural Network,FNN),简称前馈网络,它是一种单向的多层结构,也是最简单的神经网络,其简化结构图如下所示:
在这个网络中,蓝色的层是第 0 层,我们称为输入层,这里将接受模型的输入数据,即向量 x;中间黄色的层,分别是有 5 个神经元的第 1 层和有 3 个神经元的第 2 层,它们是模型的内部环节,我们称为隐藏层(实际的网络中,隐藏层可以有很多层,不仅限于 2 个);绿色的层,是网络的最后一层,我们称为输出层。神经元之间的单向箭头连线,就是两个节点之间的权重。
既然叫前馈神经网络,那我们理解了“前馈”就相当于理解了这个网络。前馈是指这个网络中的每一层神经元,产生信号之后会传递到下一层,而下一层的神经元产生的信号无法反传递给上一层,即数据是单向流动的。
下面举个例子来说明计算过程。
这个简单的网络中,有一个输入层(a、b 节点),一个隐藏层(c、d 节点)和一个输出层(e 节点)。
- 输入数据 x:
2. 权重 Wij,表示从节点 i 和 j 之间的权重,方向由 j 到 i。在上图中,我们假设 Wcb=0.7,Wda=0.3,Wec=0.4。
3. 权重矩阵 Wj,表示第 j 层的权重矩阵,在这里有两个权重矩阵,W1 和 W2:
4. 激活函数,这节课我选择 Sigmoid 函数作为激活函数,激活函数实际上是一个将线性计算过程进行非线性变换的函数。
5. 节点 c 的输入,就是 x1 和 x2 沿着各自的路径与对应的权重相乘后求和,即:
同理,节点d的输入为0.30.5+0.20.8=0.31。
模型在计算的过程中是使用矩阵的方式,所以得到的结果也是以层为单位的,计算过程如下:
现在,输入的数据走到了 c 和 d 两个节点,这里还有一层Sigmoid激活函数。因此,第一层的输出是:
也就是节点 c 的输出为 0.6479,节点 d 的输出为 0.5769。
这样,咱们就完成了隐藏层的计算了。咱们可以用同样的方式求到最后节点 e 的输出,过程如下:
输入的数据 x 经过网络的各个节点之后,最后模型给出计算结果为 0.6469。
链式法则
深度学习的内容主要就是优化更新各个节点之间链接的权重,也就是刚才提到的 W。
那如何更新权重呢?我在上一课时提到“模型的学习就是不断减小损失函数”。最小化损失函数通常采用梯度下降的方式,即每一次给模型更新权重的时候按照梯度的方向进行。
假设我们把 cost 函数表示为:H(W11,W12,…,Wij…,Wmn),则其梯度向量▽H为:
是否发现了一个问题?就是上面梯度向量的一些项不太好求。比如第一项,W11 似乎跟 H 的关联不是很直接,因为中间隔了很多的节点和层。这个时候我们就需要微积分中的链式法则出场了。
所谓链式法则,是指两个函数组合起来的复合函数,其导数等于里面函数代入外函数值的导数,乘以里面函数的导数。假设有函数 f(g(x)),链式法则有两种形式:
链式法则的定义看起来很拗口,没关系,咱们以第一种形式举个例子。
有函数 f(x)=sin(x3 + 5)。我们可以把函数分解为:
- f(x)=sin(x);
- g(x)=x3 + 5。
g(x)的导数为 g’(x) = 3x2,f(x)的导数为 f’(x)=cos(x),则 f’(x) = f’(g(x))g’(x)=cos(x3 + 5)·3x2,相当于各自求导后再相乘。
在求梯度之前,咱们把链式法则的例子变得稍微复杂一点,通过一个例子来感受链式法则的具体过程。下图是一个 y=(a+b)(b*c)函数的关系图,运算方向为自下向上。
要想求函数 y 在 a=1,b=2,c=3 条件下的梯度,我们可以先利用偏导数的定义求出不同层之间相邻节点的偏导关系。我将每个导数的计算写在了图中,如下:
通过链式法则我们知道:
可以看出,函数 F 在 a 的偏导数相当于从 a 到 F 路径上的偏导值的乘积,同理 F 在 b 的偏导数等于 b 到 F 的所有路径上偏导值乘积的和。是不是很简单?很好,咱们已经把链式法则基本弄明白了。可以开始探讨反向传播的过程了。
反向传播
反向传播算法(Backpropagation)是目前训练神经网络最常用且最有效的算法。通过反向传播,模型会不断更新自己的参数,以达到学习的目的。在这个过程中会经历 3 个步骤。
- 前向传播:将训练数据输入到网络中,数据经过隐藏层,最后达到输出层并作为结果输出。
- 误差及其传播:计算输出值和实际值之间的误差,将误差从输出层向隐藏层反向传递,直到输入层。
- 迭代:在反向传播的过程中,根据误差调整模型各个参数的值,并不断迭代前两个步骤,直至达到结束模型训练的条件。
反向传播的公式推导和证明是相对冗长和复杂的,在实际的深度学习的研发中,该部分的内容都是封装好的,所以咱们这里并不会对反向传播的数学定义、推导、证明进行详细的介绍。但是我们需要知道和了解这个过程的具体运转方式,这将有助于我们更好地理解深度学习是如何学习的。
还是以前面的前馈网络为例。
在前面的内容中,我们已经学会如何得到这个网络中所有节点和边(权重)的数值了,其中
输入为:x1=0.5,x2=0.8。
第一层权重:Wca=0.1,Wcb=0.7,Wda=0.3,Wdb=0.2
节点 c 输入:h©=Wcax1+Wcbx2=0.61,节点 d 输入:h(d)=Wdax1+Wdbx2=0.31
节点 c 输出:O©=0.647941,节点 d 输出:O(d)=0.576885
第二层权重:Wec=0.4,Wed=0.6
节点 e 输入:h(e)=WecOutput©+WedOutput(d)=0.605307
节点 e 输出:O(e)=0.646870
实际标签:y=1
损失函数为:L(x)=1/2(0.646870-1)^2=0.06235
激活函数为:
咱们用这个简化的网络,一步步看反向传播是怎么进行的。
反向传播的特点是反馈从输出往输入方向流动,所以最先要更新的权重是最后一个隐藏层的权重,即 Wec和Wed。我们以更新 Wec 为例,具体过程分为 4 个步骤。
1.确定权重到输出的路径。很显然,数据是从节点 c 流动到 e 然后输出的。
2.确定路径上的函数关系。首先由 O©和权重 Wec 相乘得到节点 e 的一部分输入,另一部分则是 O(d)和权重 Wed 相乘得到的。涉及的函数为 h=WecO©+WedO(d),e 节点还有一个激活函数 f(x)。所以最后的函数关系为:
(1)L=1/2 * (O(e) - y)^2
(2)O(e) = f(h)
(3)h = WecO©+WedO(d)
3.计算损失函数对 Wec的偏导。
4.更新权重。该环节就是之前咱们介绍梯度下降的时候介绍过的环节:更新参数。假定我们的学习率α=0.1,则Wec 的更新值为:
由此,我们完成了 Wec 的权重更新。同理,我们按照这个步骤更新其他的五个权重。Wed,路径关系为节点 d 到节点 e。更新过程如下:
Wca、Wcb、Wda、Wdb根据上面的公式可以求出。
为了验证模型是否真的学到了知识,我们不妨重新使用 x1 和 x2 在更新权重后的模型进行预测。按照之前的前馈计算步骤可以得到输出 O(e)=0.648366218,对应的损失函数 L(x)=0.061823158。对比原来的 L(x)=0.062351,我们可以发现损失函数变小了。如果有更多数据进行更多的迭代,这个损失函数会越来越小,模型的表现也会越来越好。
以上就是反向传播的基本过程。细心的你一定会发现一个问题,那就是在进行梯度更新的时候,很多变量被反复计算了,比如 Oe、he,如果每次更新梯度都要重新计算,那整个的运算量就很大,所以在实际的反向传播算法中,每个节点都会暂存从上层反馈回来的各个数据值信息,也就避免了重复计算,提高运算速度。
参考:https://kaiwu.lagou.com/course/courseInfo.htm?sid=&courseId=522&lagoufrom=noapp&sharetype=wx_friend&wxinfo=2#/detail/pc?id=4980
以上是关于深度学习入门与实战的主要内容,如果未能解决你的问题,请参考以下文章
对比《Keras图像深度学习实战》PDF+《深度学习技术图像处理入门》PDF代码分析