深度学习之神经网络基础入门知识
神经网络基础入门知识
神经网络简介
主要对较为典型的全连接神经网络的算法原理进行描述,多层的神经网络结构如下,由三部分组成:输入层(input layer),隐藏层(Hidden layer),输出层(output layer)。
特点如下:
- 输入层是有训练集的实例特征向量传入。
- 经过连接接点的权重(weight)传入下一层,一层的输出是下一层的输入。
- 隐藏层的个数可以是任意的,输入层有一层,输出层有一层。
- 每个单元也可以称之为神经结点,根据生物学来源定义。
- 以上成为两层的神经网络,输入层是不算在里面的。
- 一层中加权求和,然后根据非线性方程转化输出。
- 作为多层前馈神经网络,理论上,如果有足够的隐藏层,和足够的训练集,可以模拟出任何方程。
其实之前学过的逻辑回归可以看成一种简单的浅层神经网络,只不过没有隐藏层且输出层只有一个输出单元。逻辑回归的结构如下:
神经网络的可看成由很多个逻辑回归组成,神经网络的每层结构和上述类似,也包括输入输出、权重w、偏置b(阈值)以及**函数。
多层神经网络相比逻辑回归的优势
逻辑回归在不引入其他方法的情况下解决线性可分的问题,上述第四个的结果无法使用一条直线划分,为了解决线性不可分的问题,就需要引入多层神经网络。为了更直观得比较多层神经网络和逻辑回归(单层),利用TensorFlow PlayGround演示平台来演示一下。(http://playground.tensorflow.org/)
线性可分数据
上述测试数据可看作线性可分的二分类问题,可见对于此测试集的数据,逻辑回归和两层的神经网络在此类问题的处理结果上没有太大差距。
线性不可分数据
以上四种情况比较了逻辑回归、两层神经网络和三层神经网络在线性不可分的数据下的输出结果, 逻辑回归在不引入其他方法的情况下,只能处理线性可分的数据,或者进一步说,处理二分类的问题 。 可见神经网络在一定情况下需要适当引入隐藏层或者增加隐藏层的单元个数来使推测结果更准确。
神经网络算法原理
神经网络算法原理和逻辑回归原理部分相同,主要介绍运用监督学习的神经网络算法的原理。在实际应用神经网络情况中,总体分两部分:输入训练数据通过训练构建神经网络(主要任务是确定最终的权重w和偏置项b)和输入测试数据得出预测结果。其中第一部分为主要部分,构建神经网络的一般方法是:
-
定义神经网络结构(输入、输出单元的数量,隐藏单元的数量等)。
-
初始化模型的参数(对权重w和偏置b进行初始化:向量化矩阵的形状和设置w、b的值)
-
循环:
- 实行前向传播
- 计算损失
- 实行反向传播
- 更新参数(梯度下降法更新w、b)
实例——二层神经网络的构建流程
以下一个两层单输出的神经网络详细说明一下具体构建神经网络的流程,神经网络模型及常用的符号规定如下:
-
定义神经网络结构:神经网络的结构即神经网络的层数、隐藏层的单元个数等,此处神经网络的结构已经由上图给定,一般情况下输入层个数看需输入的特征参数的个数,隐藏层需要自己进行设定。
-
初始化模型的参数:先考虑W[1]、W[2]矩阵的形状(W[1]为(4,2)矩阵,W[2]为(1,4)矩阵),再进行初始值的设置,在设置w的初始值时见下面的注释;b[1]和b[2]的矩阵形状为(4,1)和(1,1),b的初始值可全设置为0。
python代码如下:
W1 = np.random.randn(4,2) * 0.01 #通常会在生成的矩阵后面乘以小数,比如0.01,目的是为了提高梯度下降算法的收敛速度。 b1 = np.zeros(shape=(4, 1)) W2 = np.random.randn(1,4) * 0.01 b2 = np.zeros(shape=(1, 1))
注1:只有在逻辑回归模型(一层神经网络)时才设置所有w的初始值为0;在多层神经网络中w的初始不能设定全为0,若全设置为相同的0会导致下一隐藏层(若下一隐藏层单元数大于1)开始输出的a以及梯度下降时的dw、db相同,造成对称的权重问题,最终得到权重与偏置(阈值)都相同。详细解释:https://www.zhihu.com/question/36068411?sort=created。
注2:同时在初始化权值的时候,常用的一个函数是 np.random.randn() 函数。这个函数会产生一个均值是0,方差是1的的分布,以上 w = np.random.randn() * 0.01 把w设置较小的随机数,我的理解是因为sigmoid函数和tan函数在0附近的斜率大,可以提高梯度下降的收敛速度。除了本例中w初始值的设置,很多情况并不是只乘0.01,还有Xavier初始值(sigmod、tanh等S型曲线的权重初始值)和He初始值(ReLU的权重初始值)这两种初始权重w的设置方式,python实现代码如下:
w = np.random.randn(node_num, node_num) * np.sqrt(2/node_num) #Xavier w = np.random.randn(node_num, node_num) * np.sqrt(1/node_num) #He
更直观得理解w的初始值设置,可以看一下
https://blog.csdn.net/Answer3664/article/details/96108837 -
循环
前向传播
其中把各个参数进行了向量化,可以省去用循环语句并减少程序运行损耗的时间,提高了程序执行的效率。W[1]、X等的具体组成如下,其中训练样本有m个,X的上标表示样本的序号(不带[]的上标表示第几个样本),其他向量组成类似。
计算损失
以上神经网络的损失函数如下:
以上给只是两层神经网络损失函数的计算,类推可知当为神经网络为L层时且只有一个输出时计算损失:
python实现代码:
logprobs = np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2))
cost = - np.sum(logprobs) / m
cost = float(np.squeeze(cost))
cost = -np.sum(np.multiply(np.log(AL),Y) + np.multiply(np.log(1 - AL), 1 - Y)) / m
cost = np.squeeze(cost)
assert(cost.shape == ())
反向传播
所谓的反向传播其实就是应用梯段下降法,计算出各个W和b对损失函数的偏导,用到的公式如下:
此部分最终是为了求各个层间的dW和db,dW和db表示损失函数对此参数的偏导数,用到链式求导法则,上面的dZ[2]的形式只对应**函数为sigmoid函数情况,其他情况需要另外求导推导。
更新参数
我们需要使用(dW[1], db[1], dW[2], db[2])来更新(W1, b1, W2, b2)。更新算法如下:
其中 α 为学习率。(学习率的选择与调整暂时还没学习)
通过循环前向传播、反向传播和更新参数,最终可以通过训练得出较为合适的权重W和阈值b。
神经网络算法学习中遇到的疑惑
-
为什么要用**函数?
在逻辑回归二分类中我们用sigmoid作为**函数,其中选择sigmoid函数的一个目的可以使输出结果在0与1之间,但**函数的目的不能简单地理解为是为了输出限定范围, 神经网络的**函数其实是将线性转化为非线性的一个函数 ,从而使神经网络可以逼近任何函数而不只是线性函数。**函数是用来加入非线性因素的,只有线性模型的表达能力不够,而且如果只有线性函数部分增加神经网络的深度也是无意义的,只有线性函数部分就算增加再多层隐藏层,最后的结果还是线性的(可以只用一层的神经网络实现)。
-
**函数需要满足的条件?(**函数是怎么来的?)
除了sigmoid函数,常用的**函数还有tanh函数、Relu函数以及其变体( LReLU、PReLU、RReLU 等)。常用的**函数有以下:
1.sigmoid函数
- tanh函数
- ReLU函数
4. PReLU函数
**函数是为了加入非线性因素,所以**函数应满足以下条件:
-
非线性
-
可导(易于使用梯度下降法) 以上两个条件可总结为导数不为常数
-
计算简单:在深层神经网络中前向传播要多次用到**函数, 因此简单的非线性函数自然更适合用作**函数 。
-
单调性:这个条件大部分**函数满足,即使导数的符号不变,使训练时易于收敛。
-
输出范围最好有限,但不是必须条件(如Relu函数): 有限的输出范围使得网络对于一些比较大的输入也会比较稳定 ,如sigmoid函数和tanh函数, 但这导致了前面提到的梯度消失问题,而且强行让每一层的输出限制到固定范围会限制其表达能力 。
-
zero-centered:Sigmoid函数的输出值恒大于0,这会导致模型训练的收敛速度变慢。(还没弄明白为啥)
参考 https://blog.csdn.net/junjun150013652/article/details/81487059.
-
**函数怎么选择的?除了sigmoid为什么要选择其他**函数?
对于非线性**来说,中间的隐藏层普遍使用ReLU及其变体(如LReLU、PReLU、RReLU等),输出层**函数则是要根据任务配合损失函数来选择。sigmoid函数在隐藏层中很少应用,常用在二分类问题的输出层作为**函数, 一般二分类问题中,隐藏层可以用tanh函数,输出层用sigmod函数 (多分类问题还会用到softmax,详细看https://blog.csdn.net/qq_39226755/article/details/89355974)。sigmoid函数不作为深层神经网络隐藏层的**函数的原因如下:
第一,采用sigmoid等函数,算**函数时(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法,计算量相对大,而采用Relu**函数,整个过程的计算量节省很多。
第二,对于深层网络,sigmoid函数反向传播时,很容易就会出现梯度消失的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失而无法完成训练。第三, Sigmoid函数并不是以(0,0)为中心点 。(原因还带进一步理解,先放一张图)
Relu函数的优势:
第一,在输入为正数的时候,不存在梯度饱和问题。
第二,计算速度要快很多。ReLU函数只有线性关系,不管是前向传播还是反向传播,都比sigmod和tanh要快很多。(sigmod和tanh要计算指数,计算速度会比较慢)
-
都有哪些损失函数?损失函数该怎么选择?
https://blog.csdn.net/u010976453/article/details/78488279.
http://www.csuldw.com/2016/03/26/2016-03-26-loss-function/.
https://blog.csdn.net/qq_39226755/article/details/89355974.
参考: https://blog.csdn.net/fendouaini/article/details/83626441.