文章目录

  • 第三章:神经网络
  • 3.1 数学模型
  • 3.2 激活函数
  • 3.3 代码实现
  • 3.4 学习容量和正则化
  • 3.5 生物神经科学基础



第三章:神经网络

神经网络是对线性模型的升级,使之能对线性不可分的训练集达到好的分类效果,同时也是理解卷积神经网络的基础,其核心是引入非线性激活函数和多层结构。

3.1 数学模型

线性模型只能对线性可分的训练集达到较好的分类效果,那么怎么对其升级,使之能对线性不可分的训练集也达到好的分类效果呢?如果对线性模型的计算过程进行抽象,设输入行向量为 cnn控制python cnn python_线性变换,参数矩阵为 cnn控制python cnn python_机器学习_02,分值向量为 cnn控制python cnn python_cnn控制python_03 ,则 cnn控制python cnn python_线性变换_04。这个公式从线性代数的角度看,就是线性变换, 即把向量 cnn控制python cnn python_线性变换 通过变换矩阵 cnn控制python cnn python_机器学习_02 变换为向量 cnn控制python cnn python_cnn控制python_03。从线性变换的角度对线性模型进行升级,就很容易理解。向量可以看作高维空间的点,线性变换就是把一个维度空间的点变换到另一个维度空间的点。线性模型只进行了一次线性变换,那么能不能通过多次线性变换对其升级呢?尝试进行两次线性变换,第一次变换矩阵为 cnn控制python cnn python_神经网络_08,输出为 cnn控制python cnn python_机器学习_09 ; 第二次变换矩阵为 cnn控制python cnn python_机器学习_10 ,输出为 cnn控制python cnn python_cnn控制python_03。则公式为:

cnn控制python cnn python_神经网络_12


cnn控制python cnn python_线性变换_13


注意非线性函数 cnn控制python cnn python_激活函数_14 作用在向量 cnn控制python cnn python_神经网络_15 上,是逐元素进行运算的,而不是对向量 cnn控制python cnn python_神经网络_15 的整体运算。不同的非线性函数 cnn控制python cnn python_激活函数_14 对最终的学习效果有很大影响。

cnn控制python cnn python_机器学习_18

3.2 激活函数

cnn控制python cnn python_神经网络_19


sigmoid 激活函数

  • 激活函数是神经网络的关键,用于对输入向量进行逐元素计算。sigmoid 激活函数的数学公式是 cnn控制python cnn python_机器学习_20,函数图像如图3 .1 左图所示,把输入的实数值“压缩”到(0, 1 )开区间:负数端趋近0,正数端趋近 1 。sigmoid 函数的应用曾经非常广泛,因为它能很好地解释神经元的激活: 从完全不激活(输出0 )到完全激活(输出1 )。
  • 但现在sigmoid 函数很少使用了,这是因为它有一个重大缺点:饱和特性使梯度消失。当输入比较大的负数或比较大的正数时,函数值接近0 或 1,处于饱和状态,此时梯度几乎为 0 ,这导致网络不能进行有效学习,第6 章会详细解释这一点。现在你只需记住,网络在激活函数梯度接近0 的区域学习困难。

tanh 非线性函数

  • 如图3.1 右图所示,将实数值压缩到(-1, 1)开区间。虽然它和sigmoid 函数一样,也存在饱和区域,但与sigmoid 神经元不同的是,它的输出是零中心的,因此tanh 会比sigmoid 的学习效果更好。tanh 是放大平移后的sigmoid

ReLU ( Rectified Linear Unit,修正线性单元)函数

  • 数学公式是 cnn控制python cnn python_机器学习_21,其图像如图3.2 所示。它与线性函数 cnn控制python cnn python_神经网络_22 十分相似,只是输人小于0 的部分都输出为0 ,函数图形是过原点的折线,十分接近线性函数。这几年ReLU 非常流行,相比于sigmoid 和tanh 函数,ReLU 采用随机梯度下降法进行优化时,收敛速度更快,如2012 年的AlexNet 网络表明ReLU 收敛速度是tanh 的6 倍左右。这可能是因为该函数在输入大于0 时的梯度为1 。sigmoid 和tanh 函数需要指数运算,而ReLU 只需与阔值( 0 )进行比较,运算更快。其缺点是,当ReLU的输入在小于0 的区域时,函数值永远为0 ,梯度为0 。同样是饱和区域,这会导致学习困难。但是当学习率较小时,能较好地克服该缺点。
  • cnn控制python cnn python_机器学习_23

  • 代码
import numpy as np

dim1 = 164  
dim2 = 128  
Hin = np.random.randn(dim1, dim2)
Hout = np.maximum(0, Hin)

ReLU 存在各种改进版本,都是为了克服ReLU 在输入小于0 时函数值恒为0 的缺点,使其不恒为0 ,具有一定梯度值。当输入大于0时,保持不变。

Leaky ReLU

  • Leaky ReLU 是ReLU 的一种改进版本,其公式为: cnn控制python cnn python_线性变换_24
  • 其中 cnn控制python cnn python_激活函数_25 是一个小的正常量, cnn控制python cnn python_神经网络_26 是指示函数,括号内的逻辑表达式为真时,输出 1 ,否则输出0 。函数 cnn控制python cnn python_cnn控制python_27 在输入小于0 时会给出一个很小的梯度值 cnn控制python cnn python_激活函数_25 ,比如cnn控制python cnn python_cnn控制python_29这个激活函数有时表现不错,但并不稳定

PReLU (参数化ReLU )

  • PReLU (参数化ReLU )把负区间上的斜率当作可学习的参数,而不是固定的0 或小常数,由此增大学习容量。但是有研究指出,该函数并不是在所有任务中都能提高学习效果。

ELU (指数线性单元)

  • ELU (指数线性单元)的左侧软饱和特性使ELU 对输入变化或噪声更健壮, ELU 的输出均值接近于零,这会提高收敛速度,公式为:
  • 代码
import numpy as np

dim1 = 164  
dim2 = 128  
Hin = np.random.randn(dim1, dim2)
expHin = np.exp(Hin) - 1
Hout = np.where(Hin > 0, Hin, expHin)

再次强调,在同一个网络中一般使用同一种激活函数,而不混合使用多种激活函数。

在目前的实践中,推荐使用ReLU ,读者也可以尝试LeakyReLU或ELU 。tanh 理论上不如ReLU不推荐使用sigmoid 。

3.3 代码实现

神经网络的代码和线性模型很相似,只是增加了激活函数。下面是三层神经网络计算分值向量的代码:

import numpy as np

D = 784  # 数据维度 
K = 10  # 类别数
N = 128  # 样本数量

dim1 = 128 # 隐含层宽度
dim2 = 36
W1 = 0.01 * np.random.randn(D, dim1) 
b1 = np.zeros((1, dim1)) 
W2 = 0.01 * np.random.randn(dim1, dim2) 
b2 = np.zeros((1, dim2)) 
W3 = 0.01 * np.random.randn(dim2, K) 
b3 = np.zeros((1, K))

X = np.random.randn(N, D) # 数据矩阵,每行一个样本
hidden_layer1 = np.maximum(0, np.dot(X, W1) + b1) # ReLU激活
hidden_layer2 = np.maximum(0, np.dot(hidden_layer1, W2) + b2) # ReLU激活
scores = np.dot(hidden_layer2, W3) + b3 # 输出层不需要激活

权重和偏置参数采用随机初始化,输入样本也是随机创建的,方便读者运行程序。其中, cnn控制python cnn python_机器学习_30 是样本属性维度, cnn控制python cnn python_cnn控制python_31 是类别数,这两个变量对具体任务来说是常量。cnn控制python cnn python_神经网络_32cnn控制python cnn python_线性变换_33

从代码中能直观地看到参数数量,权重 cnn控制python cnn python_机器学习_34 的数量为 cnn控制python cnn python_cnn控制python_35;权重 cnn控制python cnn python_cnn控制python_36 的数量 cnn控制python cnn python_cnn控制python_37 ,权重 cnn控制python cnn python_机器学习_38 的数量 cnn控制python cnn python_神经网络_39 ,偏置的数量分别为cnn控制python cnn python_神经网络_32cnn控制python cnn python_线性变换_33cnn控制python cnn python_cnn控制python_31。由此可见, cnn控制python cnn python_神经网络_32cnn控制python cnn python_线性变换_33 越大,参数数量越多,而模型学习容量越大,越容易导致过拟合。因此,为了缓和过拟合,需要正则化,最常用的正则化是权重参数的L2 范数。

3.4 学习容量和正则化

神经网络的数学模型表现为多层嵌套的线性变换加非线性变换,在数学上称为函数族。机器学习的目的是拟合由特征向量到分值向量的映射,该映射可能是任意的,如此产生的问题是:该函数族的拟合能力如何存在不能被神经网络拟合的函数吗

目前,理论上已经证明,只需一个包含足够多神经元的隐含层的神经网络,就能够以任意精度逼近任意的复杂连续函数,这是一个通用的函数拟合器。但实际中的效果相对较差,这是由于隐含层需要大量的神经元, 优化困难。实践表明,多层网络比单层网络好,也就说是深层网络比浅层网络好,它可以减小神经元数量,而且比较容易学习到最优参数(如采用梯度下降法)。

那么,面对一个具体的任务,如何确定最优网络结构,如隐含层的层数( 0 、1、2 层或更深)及每层神经元的数量。经过几十年的研究,目前理论上还没有严格的公式能指导这些超参数的设置,但得到一些经验法则。

当网络层数和每层神经元的数量增加时,网络的学习容量增加,即网络能拟合更复杂的函数(分类界面更复杂)。然而这是一把双刃剑: 优点是可以分类更复杂的训练集,缺点是可能会造成过拟合。

如下图,最右边就是过拟合的典型例子。

cnn控制python cnn python_激活函数_45


这个例子看起来支持采用学习容量小的网络,以防止过拟合。但实际中,如果仅是为了防止过拟合,一般不采用小网络而是用正则化来控制过拟合

这主要是因为对于小网络,当优化算法采用梯度下降法等局部方法时,优化算法比较容易收敛到损失值比较大局部极小值,导致分类准确率过低。而对于大网络,优化算法不管收敛到哪个局部极小值,这些局部极小值的损失值均比较低,而且分类准确率都较高。实践表明:小网络对参数初始值很敏感,好的初始值能收敛到低损失值,坏的初始值会收敛到很高的损失值;而大网络对参数初始值不敏感,均能收敛到较小的损失值。在实践中,一般采用多次随机初始化参数,观察损失值分布,如果方差较小,说明网络规模较大;如果方差过大,说明网络规模过小,这时需要增加网络规模。

神经网络超参数设置的技巧是尽可能地使用大网络,采用正则化来控制过拟合。多次随机初始化参数,观察损失值方差来判断网络规模是否合适,如果方差大,说明网络规模过小。

再次强调,正则化是减弱网络过拟合的好方法,如图3.4 所示。

cnn控制python cnn python_cnn控制python_46

3.5 生物神经科学基础

神经网络完全可以从数学角度解释。
神经网络算法最初是从生物神经系统获得启发的,目的是尽量模拟生物神经功能,但渐渐与其分道扬镇,成为一个偏工程的领域,并在机器学习领域取得良好效果。现在,我们从生物神经的角度理解上述计算。

生物神经网络最本质的特点是通过大量神经元相互连接进行信息传递。神经元是大脑的基本计算单位,人类的神经系统中大约有860亿个神经元,大约 cnn控制python cnn python_机器学习_47cnn控制python cnn python_cnn控制python_48

该模型可以抽象为如下的计算模型:神经元轴突传递的信号 cnn控制python cnn python_线性变换 基于突触强度 cnn控制python cnn python_神经网络_50 (权重),与其他神经元的树突进行乘法交互。突触的强度 cnn控制python cnn python_神经网络_50 是可学习的,且控制一个神经元对另一个神经元的影响(正权重使其兴奋,负权重使其抑制)。树突将信号传递到细胞体,信号在细胞体中相加。相加之和高于某个阈值(偏置)时,神经元将会被激活, 向其轴突输出一个峰值信号。神经元的激活被建模为激活函数,早期主要采用 cnn控制python cnn python_神经网络_52

#输入和权重是 1D 的numpy 数纽,阈值是一个数字
cell_body_sum =np.sum(inputs * weights) + bias
firing_rate = 1.0 / (1.0 +np.exp(-cell_body_sum))
# sigmoid 激活函数

每个神经元都对输入和权重向量进行内积,然后加上阈值,最后使用sigmoid 激活函数。

注意,这个计算模型非常粗糙地模拟了生物神经元的核心功能。神经元计算模型与线性模型很像(都是向量内积),当神经元的激活函数值接近 1 时,认为输入样本是正类;当激活函数值接近0 时,认为输入样本是负类,此时单个神经元就是一个线性二分类器

神经网络是多个神经元的集合,如何组织多个神经元呢?最简单的方式是将神经元分层,每层有多个数量不等的神经元,整个网络有多层。那么,这些神经元之间如何连接呢?最简单的方式是同一层内神经元没有连接,每个神经元只与前后层神经元连接,且是全连接,即每个神经元与前后层的所有神经元都连接。这就是著名的多层前馈神经网络,其数学模型是:

cnn控制python cnn python_神经网络_53


上面的模型通过函数嵌套实现神经元分层,运用矩阵乘法实现相邻层神经元的全连接,利用非线性函数实现神经元激活。模型中变量的具体含义见3.1 节。

多层前馈神经网络是最简单的神经网络之一,除此之外还有很多,比较著名的有RBF, ART 、SOM 、Elman 和Boltzmann 等,如果读者感兴趣,可查阅相关资料。