Machine Learning-神经网络(Neural Network)

Hits: 358

本文覆盖Coursera Machine Learning Week 4&Week 5的内容。

什么是神经网络?
在机器学习和认知科学领域,人工神经网络(Artificial Neural Network,缩写ANN),简称神经网络(英文:Neural Network,缩写NN)或类神经网络,是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。(摘自:维基百科-人工神经网络
神经网络并不是什么新鲜的玩意儿,早在1958年,美国心理学家Frank Rosenblatt就提出了一种具有单层计算单元的神经网络,称为感知器(Perceptron)。这个概念一经提出就被认为有着良好的发展潜能,因为他结构简单,也能解决一些复杂问题。但是感知器有一个天生不可解决的问题:只能解决线性可分问题,连最简单的异或(XOR)模型都无能为力。1969年Marvin Minsky写了一本叫做《Perceptrons》的书,他提出了著名的两个观点:1.单层感知机没用,我们需要多层感知机来解决复杂问题 2.没有有效的训练算法。大意就是这玩意儿没什么用,你们也别研究了,因为Marvin Minsky毕竟是人工智能领域的先驱和权威,他的这番言论直接把神经网络打倒,这是一个历史转折点,此后十多年神经网络无人问津。1974年哈佛大学博士毕业生Paul J. Werbos首次提出反向传播算法应用在神经网络的可能,但并未得到学术界的重视。直到1986年BP算法才真正开始流行起来,主要是因为Rumelhart、Hinton、Williams合著的《Learning representations by back-propagating errors》,虽然训练算法有了突破,但是还存在很多问题,比如以当时计算机的计算能力,训练一次神经网络耗时太久,不具备实际使用价值。同时还会存在过拟合以及梯度消失等问题。而90年代中期,由Vapnik等人发明的支持向量机(Support Vector Machines,SVM)算法诞生,它同样解决了线性不可分问题,但是对比神经网络有全方位优势:1高效,可以快速训练;2无需调参,没有梯度消失问题;3高效泛化,全局最优解,不存在过拟合问题,几乎全方位的碾压神经网络。SVM一度成为主流,而神经网络再一次被冷落。这期间只有Hinton等几位学者在研究神经网络。直到2012年,在ImageNet竞赛中,Hinton教授的团队,使用以卷积神经网络为基础的深度学习方案,他们训练的模型面对15万张测试图像时,预测的头五个类别的错误率只有 15.3%,而排名第二的日本团队,使用的SVM方法构建的模型,相应的错误率则高达 26.2%。从此一战成名!2012年后深度学习就成为主流。2016年,以走棋网络和估值网络两个卷积神经网络为基础,结合了蒙特卡洛树搜索和强化学习两种方法开发的人工智能围棋程序AlphaGo,4:1 击败了围棋界的小李子——曾经的人类围棋第一人李世石。震惊世界,而现在各种基于神经网络的深度学习的研究已经热火朝天了。这就是神经网络的小历史(主要摘录:曾经历过两次低谷的人工神经网络,还会迎来下一个低谷么?),本想一笔带过的,但是仔细想想还是有必要了解一下的。

为什么需要神经网络?
OK,回到正题上来,那么神经网络到底是什么呢?
神经网络这个概念看上去非常高大上,毕竟和人脑的中枢神经系统联系在了一起,但其实说白了它只是一种数学或者计算模型。更具体地说,神经网络的本质就是函数的叠加嵌套!在分类领域,这个模型最大的作用是他能模拟出复杂的非线性函数,从而能解决复杂的非线性分类问题(线性分类器解决不了的问题)。但是我们知道使用多元线性回归也能解决非线性分类问题啊,为什么还要神经网络呢?这是因为如果使用多元线性回归解决非线性分类问题,那么就需要构造许多高阶多次项,当特征数量n很大时,会导致计算复杂度变高,还会出现过拟合的问题。

神经网络结构如何表达?
一个典型的神经网络的抽象结构如下:
neural_network_1
这个图用向量的方式的表示如下:
nn_vector
对照上面的抽象结构图,先介绍几个概念:
输入层:神经网络的第一层,原始的样本数据
输出层:神经网络的最后一层,最终的计算结果
隐藏层:其余的中间层都被称为隐藏层(hidden layer)
比如在上图中,输入层就是Layer1,隐藏层就是Layer2,输出层就是Layer3。
激活结点(activation):\(a_{1},a_{2},a_{3}\)被称为激活结点,上标表示第几层,而下标则表示这一层的第几个结点
权重(weight):\(\theta \left ( j \right )\)就是之前所说的参数,这里被称为一个神经节点的权重。
激活函数(activation function):激活函数是两层神经元之间的映射函数,是一种输出到输入的转换,一般是非线性的,而且是单调可微函数(因为优化方法是基于梯度的)。常见的激活函数有:sigmoid,tanh,ReLU等,我们这里用的是sigmoid。
整个神经网络的计算过程如下:
forword_propogation
这个过程看似复杂,但你只要认真理清各神经元之间的映射关系,其实很简单的,不再细说了。这个计算过程也就是所谓的正向传播(forward propagation),其中\(g(x) = \frac{1}{1+e^{-x}}\)就是上面提到的激活函数,这里的神经网络每一层的激活函数都一样。

一个例子
使用一个单层神经网络来表达“与”逻辑,与逻辑的真值表和函数图像如下图所示:
and_truth_table
从函数图像可以看出这个分类是线性可分的(也就是说可以用一条直线分割),那么我们可以构造如下的神经网络来表示这样的“与”逻辑。
\(\begin{bmatrix}x_{0}\\ x_{1}\\ x_{2}\end{bmatrix} \to \begin{bmatrix}g\left ( z^\left ( 2 \right ) \right )\end{bmatrix}\to h_{\theta }\left ( x \right )\)
并且使得
\(\Theta^{\left (1 \right )} = \begin{bmatrix}-30 & 20 & 20 \end{bmatrix}\)
这样就可以得到:
res
同样的,我们可以使用单层神经网络来表示“或”逻辑,这里就不再赘述了。
这里,我们思考一个问题,能不能使用单层神经网络来表达“异或(XOR)”或者“同或(XNOR)”呢?异或的真值表和函数图像如下:
xor_truth_table
你会发现无论你怎么选取参数Theta都不能表达,这是因为“异或(XOR)”或者“同或(XNOR)”本质上是线性不可分的,而单层神经网络所能逼近的函数是有限的,他对线性不可分问题无能为力,这也为了进一步阐述为什么我们需要多层神经网络来解决复杂的分类问题。举这个例子主要是为了说明一点:理论证明,两层神经网络就可以无限逼近任意连续函数了。具体的解释和证明可以参考这篇文章:A visual proof that neural nets can compute any function

如何训练一个神经网络?
Cost Function
本文的神经网络因为采用了sigmoid函数作为激活函数,所以它的cost function和逻辑回归的形式大致相似,具体如下:
cost_function_nn
在理解这个公式之前,先介绍以下变量:
\(L\):神经网络的总层数
\(sl\):\(l\)层包含的神经节点数量(不包括bias结点,也就是\(\theta_{0}\))
\(K\):输出单元的数量(多分类问题会有多个输出)
这个公式咋一看有点复杂,有点懵逼,但其实拆开来看也是很容易理解的。先看前一部分,最外层的求和很好理解,就是求所有的样本总和,m代表样本的总数。而内层的求和,\(sigma(1-K)\),计算的是所有输出单元的cost function之和。这里可能会有一个误区,根据第一直觉你可能会去尝试推导计算出第k个输出单元的假设函数:\(h_{\theta}\left ( x^{\left ( i \right )} \right )\),但是这样做复杂度太高了,因为函数经过层层叠加,最终的形式会很复杂。而实际上我们并不需要关心这个最终的假设函数的形式是什么样的,只需要知道他的值就行了。第二部分就是正则化(regularization),这部分的意思就是把所有神经元的权重都包括进来,计算他们的平方和。

反向传播算法(Backpropagation Algorithm)
反向传播是训练神经网络最重要的算法,可以这么说,没有反向传播算法就没有深度学习的今天。这个算法用数学语言描述起来比较复杂,如果一上来就用一堆数学公式来介绍他的概念会让人比较难以理解,所以我并不打算一上来就介绍他的数学表达,而是从“我们为什么需要反向传播算法”入手,一步一步引入。
先回顾一下机器学习的基本套路:我们先得到假设函数和代价函数,然后使用梯度下降算法求出代价函数的最小值,最后得到最优参数。神经网络的参数优化方法也不例外,我们的目的仍然是最小化代价函数,即:
$$\min _{\Theta}J\left ( \Theta \right )$$
根据前几章的介绍,我们使用梯度下降算法优化参数,则需要求得J(Θ)的偏导数:
$$\frac{\partial }{\partial \Theta_{i,j}^{\left ( l \right )}}J\left ( \Theta \right )$$
我们知道最终的假设函数是每一层激活函数复合而成的,所以它的形式肯定是很复杂的,而且是跟每一层的参数都相关,所以如果我们推导出最终的假设函数,并且计算出关于每一层的每一个神经节点的参数的偏导数,这样会导致计算过程太过复杂。其实,我们可以把神经网络抽象成只有两层:输入层和输出层,至于隐藏层我们可以把它想象成一个黑盒子,不需要关心隐藏层到底发生了什么。所以我们的目标就是求得cost function J(Θ)的相对于输入层的偏导数,这里问题就来了,在线性回归或者逻辑回归里面,这个偏导数是可以直接算出来的,但是在神经网络里,经过函数的多层叠加,我们无法一下子直接算出J(Θ)的偏导数。那怎么算呢?这里就要用到一个数学知识:链式求导法则。
什么是链式求导法则
这里引用一下知乎@胡逸夫同学的回答来介绍一下链式求导法则
chain_derive_graph
根据链式求导法则,我们有:
$$\frac{\partial e}{\partial a} = \frac{\partial e}{\partial c} . \frac{\partial c}{\partial a}$$
以及
$$\frac{\partial e}{\partial b} = \frac{\partial e}{\partial c} . \frac{\partial c}{\partial b} + \frac{\partial e}{\partial d} . \frac{\partial d}{\partial b} $$
看到这里,说白了,反向传播算法实际就是:我们使用链式求导法则,反向层层推进,计算出每一层神经节点的偏导数,然后使用梯度下降,不断调整每一个节点的权重(也就是参数theta),从而达到求得全局最小值的目的。

算法流程
上面介绍了反向传播算法的基本原理,接下来看下具体算法是怎么样的。算法执行流程如下:
backpropagation_algorithm

对照这个流程图,具体解释如下:

变量声明:
a).给定训练数据集:\(\left \{ \left ( x^{\left ( 1 \right )},y^{\left ( 1 \right )} \right )... \left ( x^{\left ( m \right )},y^{\left ( m \right )} \right )\right \}\)
b).定义一个和神经网络一样大的零矩阵\(\Delta _{i,j}^{\left ( l \right )}:=0\)用于保存所有样本的梯度下降的权重值。

具体步骤
1.使得输入层\(a^{\left ( 1 \right )}:=x^{\left ( t \right )}\)

2.执行正向传播算法计算每一层神经节点的值,具体计算过程如下:
forward_propagation

3.计算输出层神经节点与实际值之间的误差δ:\(\delta ^{\left ( L \right )}=a^{\left ( L \right )} - y^{\left ( t \right )}\)

4.从输出层往前层层递推,按照公式:\(\delta ^{\left ( l \right )}=\left ( \left ( \Theta^{\left ( l \right )} \right )^{T} \delta^{\left ( l+1 \right )}\right ).*a^{\left ( l \right )}.*\left ( 1-a^{\left ( l \right )} \right )\)计算\(\delta ^{\left ( L-1 \right )},\delta ^{\left ( L-2 \right )},...,\delta ^{\left ( 2 \right )}\)其中,\(g{}'\left ( z^\left ( l \right ) \right ) = a^{\left ( l \right )}.*\left ( 1 - a^{\left ( l \right )} \right )\)
这里的误差δ其实就偏导数,这里的表达方式是矩阵形式,展开后可以发现使用的计算规则就是我们之前所说的链式求导法则。

5.计算梯度(compute gradient),使得\(\Delta _{i,j}^{(l)}:=\Delta _{i,j}^{(l)} + a_{j}^{(l)}\delta _{i}^{(l+1)}\)向量化表示为:\(\Delta _{i,j}^{(l)}:=\Delta _{i,j}^{(l)} + \delta^{(l+1)}(a^{(l)})^T\)
考虑正则化:

  • \(D_{i,j}^{(l))} := \frac{1}{m}(\Delta_{i,j}^{(l)} + \lambda \Theta_{i,j}^{(l)})\) ,if \(j\neq 0.\)
  • \(D_{i,j}^{(l))} := \frac{1}{m}\Delta_{i,j}^{(l)}\) ,if \(j=0\)
  • 最后,经过以上五步即可得到:
    $$\frac{\partial }{\partial \Theta_{ij}^{(l)}}J(\Theta) = D_{ij}^{(l)}$$

    这里有几个问题要说明一下:
    1.为什么最后一层的δ值计算方式和其他层不一样?因为计算的是误差值,所以咋一看很符合直觉。但实际上是因为所选取的cost function不一样导致的,最终得到的δ(L)的形式与代价函数J和输出层激活函数g直接相关。所以最后一层采用的并不是那个log形式的cost function,而是采用了平方差的形式:
    square_error
    2.循环外面的大写的delta值中j=0和j!=0分别代表什么意思?
    很简单,j=0表示是bias unit(偏差项),自然不需要再加上正则化参数。
    3.梯度下降的权重值公式的具体推导可以参考:零基础入门深度学习(3) - 神经网络和反向传播算法

    每一层每个节点的梯度权重算出来后,就可以使用梯度下降算法来求解最优参数了,公式和之前的一样:
    $$\Theta_{ij}^{(l)}:=\Theta_{ij}^{(l)}-\alpha \frac{\partial }{\partial \Theta_{ij}^{(l)}}J(\Theta)$$
    值得注意的是,神经网络的代价函数J(θ)是一个非凸函数,因此理论上是只能够停留在局部最小值的位置。

    最后,本文只是从全局的角度大致理解下神经网络和反向传播算法,并没有覆盖太多数学公式和推导过程,对这方面感兴趣的童鞋可以参考这几篇写的非常好的文章:
    Coursera ML笔记 - 神经网络(Learning) - Micheal Wang的机器学习乐园
    漫谈ANN(2):BP神经网络
    Principles of training multi-layer neural network using backpropagation

    其他参考:
    神经网络中激活函数的作用
    What is the best visual explanation for the back propagation algorithm for neural networks?
    【机器学习】神经网络-激活函数-面面观(Activation Function)



    课后编程作业已经上传至本人GitHub,如有需要可以参考,但请遵守课堂准则,不要抄袭。


    One thought on “Machine Learning-神经网络(Neural Network)

    发表评论

    电子邮件地址不会被公开。 必填项已用*标注