入门深度学习,理解神经网络、反向传播算法是第一关

2019-10-28 16:28:44 浏览数 (1)

深度学习学习7步骤

1.学习或者回忆一些数学知识

因为计算机能做的就只是计算,所以人工智能更多地来说还是数学问题[1]。我们的目标是训练出一个模型,用这个模型去进行一系列的预测。于是,我们将训练过程涉及的过程抽象成数学函数:首先,需要定义一个网络结构,相当于定义一种线性非线性函数;接着,设定一个优化目标,也就是定义一种损失函数(loss function)。

而训练的过程,就是求解最优解及次优解的过程。在这个过程中,我们需要掌握基本的概率统计、高等数学、线性代数等知识,如果学过就最好,没学过也没关系,仅仅知道原理和过程即可,有兴趣的读者可以涉猎一些推导证明。

2.掌握经典机器学习理论与基本算法

这些基本算法包括支持向量机、逻辑回归、决策树、朴素贝叶斯分类器、随机森林、聚类算法、协同过滤、关联性分析、人工神经网络和BP算法、PCA、过拟合与正则化等。

在本书“实战篇”的第8章到第13章的例子中也有贯穿这些算法知识,保证读者可以用它写出一个小的TensorFlow程序。

3.掌握一种编程工具(语言)

Python语言是一种解释型、面向对象、动态数据类型的高级程序设计语言。Python是很多新入门的程序员的入门编程语言,也是很多老程序员后来必须掌握的编程语言。我们需要重点掌握使用线性代数库和矩阵的操作,尤其是Numpy、Pandas第三方库,也要多试试机器学习的库,如sklearn,做一些SVM及逻辑回归的练习。这对直接上手写TensorFlow程序大有裨益。

有些工业及学术领域的读者还可能擅长MATLAB或R,其实现算法的思想和Python也很类似。

同时考虑到许多读者是使用C 、Java、Go语言的,TensorFlow还提供了和Python“平行语料库”的接口。虽然本书是主要是基于Python讲解的,对于其他语言的原理和应用API也都非常类似,读者把基础掌握后,只需要花很短的时间就能使用自己擅长的语言开发。另外对于Java语言的同学,本书第18章会讲解TensorFlowOnSpark,第19章会讲到TensorFlow的移动端开发。

4.研读经典论文,关注最新动态和研究成果

一些经典论文是必读的。例如,要做手写数字识别,若采用LeNet,要先阅读一下LeNet的学术论文;要做物体目标检测的训练,若选定MSCNN框架,可以先读MSCNN相关的论文。那么,论文从哪里找呢?那么多论文应该读哪篇呢?

下面以GoogleNet的TensorFlow实现为例。在GitHub[3]上,一般在开头的描述中就会说明这个模型所依据的论文,如图所示。

顺着这篇论文阅读,可以大致了解这个网络的实现原理,对迅速上手应用有很大的作用。同时,我在第6章也会对LeNet、AlexNet、ResNet这几个常见的网络进行讲解,帮助读者举一反三。

很多做模式识别的工作者之所以厉害,是因为他们有过很多、很深的论文积累,对模型的设计有很独到的见解,而他们可能甚至一行代码也不会写,而工程(写代码)能力在工作中很容易训练。许多工程方向的软件工程师,工作模式常常在实现业务逻辑和设计架构系统上,编码能力很强,但却缺少论文积累。同时具有这两种能力的人,正是硅谷一些企业目前青睐的人才。

读者平时还可以阅读一些博客、笔记,以及微信公众号、微博新媒体资讯等,往往一些很流行的新训练方法和模型会很快在这些媒体上发酵,其训练神经网络采用的一些方法可能有很大的启发性。

5.自己动手训练神经网络

接着,就是要选择一个开源的深度学习框架。选择框架时主要考虑哪种框架用的人多。人气旺后,遇到问题很容易找到答案;GitHub上关于这个框架的项目和演示会非常多;相关的论文也会层出不穷;在各个QQ群和微信群的活跃度会高;杂志、公众号、微博关注的人也会很多;行业交流和技术峰会讨论的话题也多;也能享受到国内外研究信息成果的同步。

目前这个阶段,TensorFlow因为背靠谷歌公司这座靠山,再加上拥有庞大的开发者群体,而且采用了称为“可执行的伪代码”的Python语言,更新和发版速度着实非常快。目前TensorFlow已经升级到1.0版,在性能方面也有大幅度提高,而且新出现的Debugger、Serving、XLA特性也是其他框架所不及的。此外,一些外围的第三方库(如Keras、TFLearn)也基于它实现了很多成果,并且Keras还得到TensorFlow官方的支持。TensorFlow支持的上层语言也在逐渐扩大,对于不同工程背景的人转入的门槛正在降低。

在GitHub[4]上有一个关于各种框架的比较,从建模能力、接口、模型部署、性能、架构、生态系统、跨平台等7个方面进行比较,TensorFlow也很占综合优势。截至2017年1月,TensorFlow的star数已经超过了其他所有框架的总和,如图1-8所示。

因此,从目前来看,投身TensorFlow是一个非常好的选择,掌握TensorFlow在找工作时是一个非常大的加分项。

接下来就是找一个深度神经网络,目前的研究方向主要集中在视觉和语音两个领域。初学者最好从计算机视觉入手,因为它不像语音等领域需要那么多的基础知识,结果也比较直观。例如,用各种网络模型来训练手写数字(MNIST)及图像分类(CIFAR)的数据集。

6.深入感兴趣或者工作相关领域

人工智能目前的应用领域很多,主要是计算机视觉和自然语言处理,以及各种预测等。对于计算机视觉,可以做图像分类、目标检测、视频中的目标检测等;对于自然语言处理,可以做语音识别、语音合成、对话系统、机器翻译、文章摘要、情感分析等,还可以结合图像、视频和语音,一起发挥价值。

更可以深入某一个行业领域。例如,深入医学行业领域,做医学影像的识别;深入淘宝的穿衣领域,做衣服搭配或衣服款型的识别;深入保险业、通信业的客服领域,做对话机器人的智能问答系统;深入智能家居领域,做人机的自然语言交互;等等。

7.在工作中遇到问题,重复前六步

在训练中,准确率、坏案例(bad case)、识别速度等都是可能遇到的瓶颈。训练好的模型也不是一成不变的,需要不断优化,也需要结合具体行业领域和业务进行创新,这时候就要结合最新的科研成果,调整模型,更改模型参数,一步步更好地贴近业务需求。 这里,一些人担心人工智能超越人类还会产生哲学和伦理问题。我认为做这种讨论还为时尚早,严谨的数据基础是要突破的主要方向。

深度学习所需要的数学基础并没有想象中的那么难,只需要知道导数和相关的函数概念即可。假如你高等数学也没学过,很好,这篇文章其实是想让文科生也能看懂,只需要学过初中数学。

不必有畏难的情绪,我比较推崇李书福的精神,在一次电视采访中,李书福说:谁说中国人不能造汽车?造汽车有啥难的,不就是四个轮子加两排沙发嘛。当然,他这个结论有失偏颇,不过精神可嘉。

导数是什么?

无非就是变化率,比如:王小二今年卖了 100 头猪,去年卖了 90 头,前年卖了 80 头。。。变化率或者增长率是什么?每年增长 10 头猪,多简单。

这里需要注意有个时间变量---年。王小二卖猪的增长率是 10头/年,也就是说,导数是 10。

函数 y = f(x) = 10x 30,这里我们假设王小二第一年卖了 30 头,以后每年增长 10 头,x代表时间(年),y代表猪的头数。

当然,这是增长率固定的情形,而现实生活中,很多时候,变化量也不是固定的,也就是说增长率不是恒定的。

比如,函数可能是这样: y = f(x) = 5x² 30,这里 x 和 y 依然代表的是时间和头数,不过增长率变了,怎么算这个增长率,我们回头再讲。或者你干脆记住几个求导的公式也可以。

深度学习还有一个重要的数学概念:偏导数

偏导数的偏怎么理解?偏头疼的偏,还是我不让你导,你偏要导?

都不是,我们还以王小二卖猪为例,刚才我们讲到,x 变量是时间(年),可是卖出去的猪,不光跟时间有关啊,随着业务的增长,王小二不仅扩大了养猪场,还雇了很多员工一起养猪。

所以方程式又变了:y = f(x) = 5x₁² 8x₂ 35x₃ 30

这里 x₂ 代表面积,x₃ 代表员工数,当然 x₁ 还是时间。

偏导数是什么

偏导数无非就是多个变量的时候,针对某个变量的变化率。在上面的公式里,如果针对 x₃ 求偏导数,也就是说,员工对于猪的增长率贡献有多大。

或者说,随着(每个)员工的增长,猪增加了多少,这里等于 35---每增加一个员工,就多卖出去 35 头猪。

计算偏导数的时候,其他变量都可以看成常量,这点很重要,常量的变化率为 0,所以导数为 0,所以就剩对 35x₃ 求导数,等于 35。对于 x₂ 求偏导,也是类似的。

求偏导,我们用一个符号表示:比如 y / x₃ 就表示 y 对 x₃ 求偏导。

深度学习是采用神经网络,用于解决线性不可分的问题。

什么是学习系数?

另一个是学习系数,为什么叫学习系数?

刚才我们上面讲到∆增量,到底每次增加多少合适?是不是等同于偏导数(变化率)?

经验告诉我们,需要乘以一个百分比,这个就是学习系数,而且,随着训练的深入,这个系数是可以变的。

当然,还有一些很重要的基本知识,比如 SGD(随机梯度下降),mini batch 和 epoch(用于训练集的选择)。

我们已经看到不少人提到如果想实现强AI,就必须让机器学会观察并总结规律的言论。具体地说,要让机器观察什么是圆的,什么是方的,区分各种颜色和形状,然后根据这些特征对某种事物进行分类或预测。其实这就是回归问题。 如何解决回归问题?我们用眼睛看到某样东西,可以一下子看出它的一些基本特征。可是计算机呢?它看到的只是一堆数字而已,因此要让机器从事物的特征中找到规律,其实是一个如何在数字中找规律的问题。

例:假如有一串数字,已知前六个是1、3、5、7,9,11,请问第七个是几? 你一眼能看出来,是13。对,这串数字之间有明显的数学规律,都是奇数,而且是按顺序排列的。

那么这个呢?前六个是0.14、0.57、1.29、2.29、3.57、5.14,请问第七个是几? 这个就不那么容易看出来了吧!我们把这几个数字在坐标轴上标识一下,可以看到如下图形:

用曲线连接这几个点,延着曲线的走势,可以推算出第七个数字——7。 由此可见,回归问题其实是个曲线拟合(Curve Fitting)问题。那么究竟该如何拟合?机器不可能像你一样,凭感觉随手画一下就拟合了,它必须要通过某种算法才行。

假设有一堆按一定规律分布的样本点,下面我以拟合直线为例,说说这种算法的原理。 其实很简单,先随意画一条直线,然后不断旋转它。每转一下,就分别计算一下每个样本点和直线上对应点的距离(误差),求出所有点的误差之和。这样不断旋转,当误差之和达到最小时,停止旋转。说得再复杂点,在旋转的过程中,还要不断平移这条直线,这样不断调整,直到误差最小时为止。这种方法就是著名的梯度下降法(Gradient Descent)。为什么是梯度下降呢?在旋转的过程中,当误差越来越小时,旋转或移动的量也跟着逐渐变小,当误差小于某个很小的数,例如0.0001时,我们就可以收工(收敛, Converge)了。啰嗦一句,如果随便转,转过头了再往回转,那就不是梯度下降法。

我们知道,直线的公式是y=kx b,k代表斜率,b代表偏移值(y轴上的截距)。也就是说,k可以控制直线的旋转角度,b可以控制直线的移动。强调一下,梯度下降法的实质是不断的修改k、b这两个参数值,使最终的误差达到最小

求误差时使用 累加(直线点-样本点)^2,这样比直接求差距 累加(直线点-样本点) 的效果要好。这种利用最小化误差的平方和来解决回归问题的方法叫最小二乘法(Least Square Method)

问题到此使似乎就已经解决了,可是我们需要一种适应于各种曲线拟合的方法,所以还需要继续深入研究。

我们根据拟合直线不断旋转的角度(斜率)和拟合的误差画一条函数曲线,如图:

从图中可以看出,误差的函数曲线是个二次曲线,凸函数(下凸, Convex),像个碗的形状,最小值位于碗的最下端。如果在曲线的最底端画一条切线,那么这条切线一定是水平的,在图中可以把横坐标轴看成是这条切线。如果能求出曲线上每个点的切线,就能得到切线位于水平状态时,即切线斜率等于0时的坐标值,这个坐标值就是我们要求的误差最小值和最终的拟合直线的最终斜率。

这样,梯度下降的问题集中到了切线的旋转上。切线旋转至水平时,切线斜率=0,误差降至最小值。

切线每次旋转的幅度叫做学习率(Learning Rate),加大学习率会加快拟合速度,但是如果调得太大会导致切线旋转过度而无法收敛。 注意:对于凹凸不平的误差函数曲线,梯度下降时有可能陷入局部最优解。下图的曲线中有两个坑,切线有可能在第一个坑的最底部趋于水平。

微分就是专门求曲线切线的工具,求出的切线斜率叫做导数(Derivative),用dy/dx或f'(x)表示。扩展到多变量的应用,如果要同时求多个曲线的切线,那么其中某个切线的斜率就叫偏导数(Partial Derivative),用∂y/∂x表示,∂读“偏(partial)”。由于实际应用中,我们一般都是对多变量进行处理,我在后面提到的导数也都是指偏导数。

以上是线性回归(Linear Regression)的基本内容,以此方法为基础,把直线公式改为曲线公式,还可以扩展出二次回归、三次回归、多项式回归等多种曲线回归。

在多数情况下,曲线回归会比直线回归更精确,但它也增加了拟合的复杂程度。 直线方程y=kx b改为二次曲线方程y=ax^2 bx c时,参数(Parameter)由2个(分别是k、b)变为3个(分别是a、b、c),特征(Feature)由1个(x)变为2个(x^2和x)。三次曲线和复杂的多项式回归会增加更多的参数和特征。

前面讲的是总结一串数字的规律,现实生活中我们往往要根据多个特征(多串数字)来分析一件事情,每个原始特征我们都看作是一个维度(Dimension)。例如一个学生的学习成绩好坏要根据语文、数学、英语等多门课程的分数来综合判断,这里每门课程都是一个维度。当使用二次曲线和多变量(多维)拟合的情况下,特征的数量会剧增,特征数=维度^2/2 这个公式可以大概计算出特征增加的情况,例如一个100维的数据,二次多项式拟合后,特征会增加到100*100/2=5000个。 下面是一张50*50像素的灰度图片,如果用二次多项式拟合的话,它有多少个特征呢?——大约有3百万!

它的维度是50*50=2500,特征数=2500*2500/2=3,125,000。如果是彩色图片,维度会增加到原来的3倍,那么特征数将增加到接近3千万了!

这么小的一张图片,就有这么巨大的特征量,可以想像一下我们的数码相机拍下来的照片会有多大的特征量!而我们要做的是从十万乃至亿万张这样的图片中找规律,这可能吗? 很显然,前面的那些回归方法已经不够用了,我们急需找到一种数学模型,能够在此基础上不断减少特征,降低维度。 于是,“人工神经网络(ANN, Artificial Neural Network)”就在这样苛刻的条件下粉墨登场了,神经科学的研究成果为机器学习领域开辟了广阔的道路。

有一种假说:“智能来源于单一的算法(One Learning Algorithm)”。如果这一假说成立,那么利用单一的算法(神经网络)处理世界上千变万化的问题就成为可能。我们不必对万事万物进行编程,只需采用以不变应万变的策略即可。有越来越多的证据证明这种假说,例如人类大脑发育初期,每一部分的职责分工是不确定的,也就是说,人脑中负责处理声音的部分其实也可以处理视觉影像。

神经元(Neuron),或者说一个脑细胞的生理结构:

下面是单个神经元的数学模型,可以看出它是生理结构的简化版,模仿的还挺像:

解释一下: 1代表偏移值(偏置项, Bias Units);X1,X2,X2代表初始特征;w0,w1,w2,w3代表权重(Weight),即参数,是特征的缩放倍数;特征经过缩放和偏移后全部累加起来,此后还要经过一次激活运算然后再输出。激活函数有很多种,后面将会详细说明。 举例说明:

X1*w1 X2*w2 ... Xn*wn这种计算方法称为加权求和(Weighted Sum)法,此方法在线性代数里极为常用。加权求和的标准数学符号是,

不过为了简化,我在教程里使用女巫布莱尔的符号

表示,刚好是一个加号和一个乘号的组合。

这个数学模型有什么意义呢?下面我对照前面那个 y=kx b 直线拟合的例子来说明一下。

这时我们把激活函数改为Purelin(45度直线),Purelin就是y=x,代表保持原来的值不变。 这样输出值就成了 Y直线点 = b X直线点*k,即y=kx b。看到了吧,只是换了个马甲而已,还认的出来吗?下一步,对于每个点都进行这种运算,利用Y直线点和Y样本点计算误差,把误差累加起来,不断地更新b、k的值,由此不断地移动和旋转直线,直到误差变得很小时停住(收敛)。这个过程完全就是前面讲过的梯度下降的线性回归。 一般直线拟合的精确度要比曲线差很多,那么使用神经网络我们将如何使用曲线拟合?答案是使用非线性的激活函数即可,最常见的激活函数是Sigmoid(S形曲线),Sigmoid有时也称为逻辑回归(Logistic Regression),简称logsig。logsig曲线的公式如下:

还有一种S形曲线也很常见到,叫双曲正切函数(tanh),或称tansig,可以替代logsig。

下面是它们的函数图形,从图中可以看出logsig的数值范围是0~1,而tansig的数值范围是-1~1。

下图是几种比较常见的网络形式:

- 左边蓝色的圆圈叫“输入层”,中间橙色的不管有多少层都叫“隐藏层”,右边绿色的是“输出层”。 - 每个圆圈,都代表一个神经元,也叫节点(Node)。 - 输出层可以有多个节点,多节点输出常常用于分类问题。 - 理论证明,任何多层网络可以用三层网络近似地表示。 - 一般凭经验来确定隐藏层到底应该有多少个节点,在测试的过程中也可以不断调整节点数以取得最佳效果。 计算方法:

- 虽然图中未标识,但必须注意每一个箭头指向的连线上,都要有一个权重(缩放)值。 - 输入层的每个节点,都要与的隐藏层每个节点做点对点的计算,计算的方法是加权求和 激活,前面已经介绍过了。(图中的红色箭头指示出某个节点的运算关系) - 利用隐藏层计算出的每个值,再用相同的方法,和输出层进行计算。 - 隐藏层用都是用Sigmoid作激活函数,而输出层用的是Purelin。这是因为Purelin可以保持之前任意范围的数值缩放,便于和样本值作比较,而Sigmoid的数值范围只能在0~1之间。 - 起初输入层的数值通过网络计算分别传播到隐藏层,再以相同的方式传播到输出层,最终的输出值和样本值作比较,计算出误差,这个过程叫前向传播(Forward Propagation)

前面讲过,使用梯度下降的方法,要不断的修改k、b两个参数值,使最终的误差达到最小。神经网络可不只k、b两个参数,事实上,网络的每条连接线上都有一个权重参数,如何有效的修改这些参数,使误差最小化,成为一个很棘手的问题。从人工神经网络诞生的60年代,人们就一直在不断尝试各种方法来解决这个问题。直到80年代,误差反向传播算法(BP算法)的提出,才提供了真正有效的解决方案,使神经网络的研究绝处逢生。

BP算法是一种计算偏导数的有效方法,它的基本原理是:利用前向传播最后输出的结果来计算误差的偏导数,再用这个偏导数和前面的隐藏层进行加权求和,如此一层一层的向后传下去,直到输入层(不计算输入层),最后利用每个节点求出的偏导数来更新权重。

为了便于理解,后面我一律用“残差(error term)”这个词来表示误差的偏导数

输出层→隐藏层:残差 = -(输出值-样本值) * 激活函数的导数 隐藏层→隐藏层:残差 = (右层每个节点的残差加权求和)* 激活函数的导数 如果输出层用Purelin作激活函数,Purelin的导数是1,输出层→隐藏层:残差 = -(输出值-样本值) 如果用Sigmoid(logsig)作激活函数,那么:Sigmoid导数 = Sigmoid*(1-Sigmoid) 输出层→隐藏层:残差 = -(Sigmoid输出值-样本值) * Sigmoid*(1-Sigmoid) = -(输出值-样本值)*输出值*(1-输出值) 隐藏层→隐藏层:残差 = (右层每个节点的残差加权求和)* 当前节点的Sigmoid*(1-当前节点的Sigmoid)

如果用tansig作激活函数,那么:tansig导数 = 1 - tansig^2 残差全部计算好后,就可以更新权重了:

输入层:权重增加 = 输入值 * 右层对应节点的残差 * 学习率

隐藏层:权重增加 = 当前节点的Sigmoid * 右层对应节点的残差 * 学习率 偏移值的权重增加 = 右层对应节点的残差 * 学习率 学习率前面介绍过,学习率是一个预先设置好的参数,用于控制每次更新的幅度。 此后,对全部数据都反复进行这样的计算,直到输出的误差达到一个很小的值为止。

以上介绍的是目前最常见的神经网络类型,称为前馈神经网络(FeedForward Neural Network),由于它一般是要向后传递误差的,所以也叫BP神经网络(Back Propagation Neural Network)

BP神经网络的特点和局限: - BP神经网络可以用作分类、聚类、预测等。需要有一定量的历史数据,通过历史数据的训练,网络可以学习到数据中隐含的知识。在你的问题中,首先要找到某些问题的一些特征,以及对应的评价数据,用这些数据来训练神经网络。 - BP神经网络主要是在实践的基础上逐步完善起来的系统,并不完全是建立在仿生学上的。从这个角度讲,实用性 > 生理相似性。 - BP神经网络中的某些算法,例如如何选择初始值、如何确定隐藏层的节点个数、使用何种激活函数等问题,并没有确凿的理论依据,只有一些根据实践经验总结出的有效方法或经验公式。 - BP神经网络虽然是一种非常有效的计算方法,但它也以计算超复杂、计算速度超慢、容易陷入局部最优解等多项弱点著称,因此人们提出了大量有效的改进方案,一些新的神经网络形式也层出不穷。

这里介绍的是计算完一条记录,就马上更新权重,以后每计算完一条都即时更新权重。实际上批量更新的效果会更好,方法是在不更新权重的情况下,把记录集的每条记录都算过一遍,把要更新的增值全部累加起来求平均值,然后利用这个平均值来更新一次权重,然后利用更新后的权重进行下一轮的计算,这种方法叫批量梯度下降(Batch Gradient Descent)

Andrew Ng的《机器学习》公开课: https://class.coursera.org/ml Coursera公开课笔记中文版(神经网络的表示): http://52opencourse.com/139/coursera公开课笔记-斯坦福大学机器学习第八课-神经网络的表示-neural-networks-representation Coursera公开课视频(神经网络的学习): http://52opencourse.com/289/coursera公开课视频-斯坦福大学机器学习第九课-神经网络的学习-neural-networks-learning 斯坦福深度学习中文版: http://deeplearning.stanford.edu/wiki/index.php/UFLDL教程

0 人点赞