NLP实践五:神经网络基础概念
神经网络结构
1神经网络的输入输出
来自:神经网络算法推演
假如神经网络仅由“神经元”构成,以下即是这个“神经元”的图示:
这个“神经元”是一个以截距+1 为输入值的运算单元,其输出为:
可以看出,这个单一“神经元”的输入-输出映射关系其实就是一个逻辑回归.
所谓神经网络就是将许多个单一“神经元”联结在一起,这样,一个“神经元”的输出就可以是另一个“神经元”的输入。例如,下图就是一个简单的神经网络:
我们使用圆圈来表示神经网络的输入,标上“+1+1”的圆圈被称为”’偏置节点”’,也就是截距项。神经网络最左边的一层叫做”‘输入层”’,最右的一层叫做”‘输出层’”(本例中,输出层只有一个节点)。中间所有节点组成的一层叫做”’隐藏层”’,因为我们不能在训练样本集中观测到它们的值。同时可以看到,以上神经网络的例子中有3个”’输入单元”’(偏置单元不计在内),3个”’隐藏单元”’及一个”’输出单元”’。
我们用来表示网络的层数,本例中 ,我们将第层记为 ,于是 是输入层,输出层是 。本例神经网络有参数,其中(下面的式子中用到)是第 层第jj 单元与第 层第 单元之间的联接参数(其实就是连接线上的权重,注意标号顺序),是第层第单元的偏置项。因此在本例中,注意,没有其他单元连向偏置单元(即偏置单元没有输入),因为它们总是输出+1+1。同时,我们用表示第 层的节点数(偏置单元不计在内)。
我们用 表示第ll 层第ii 单元的”’**值”’(输出值)。当 时,,也就是第 个输入值(输入值的第个特征)。对于给定参数集合,我们的神经网络就可以按照 来计算输出结果。本例神经网络的计算步骤如下:
我们用 表示第层第单元输入加权和(包括偏置单元),,这样我们就可以得到一种更简洁的表示法。这里我们将**函数 扩展为用向量(分量的形式)来表示,即
我们将上面的计算步骤叫作”’前向传播”’。回想一下,之前我们用 表示输入层的**值,那么给定第层的**值
第 层的**值就可以按照下面步骤计算得到:
将参数矩阵化,使用矩阵-向量运算方式,我们就可以利用线性代数的优势对神经网络进行快速求解。
目前为止,我们讨论了一种神经网络,我们也可以构建另一种”’结构”’的神经网络(这里结构指的是神经元之间的联接模式),也就是包含多个隐藏层的神经网络。最常见的一个例子是层的神经网络,第 层是输入层,第层是输出层,中间的每个层与层 紧密相联。这种模式下,要计算神经网络的输出结果,我们可以按照之前描述的等式,按部就班,进行前向传播,逐一计算第 层的所有**值,然后是第 层的**值,以此类推,直到第层。这是一个”’前馈”’神经网络的例子,因为这种联接图没有闭环或回路。
神经网络也可以有多个输出单元。比如,下面的神经网络有两层隐藏层:及 ,输出层 有两个输出单元。
1 **函数
**函数的作用:就是将权值结果转化成分类结果。
为什么使用非线性的**函数:
只通过线性变换,任意层的全连接神经网络和单层的神经网络模型的表达能力没有任何区别,而且它们都是线性模型。线性模型能够解决的问题是有限的,这就是线性模型最大的局限性。如果使用线性的**函数,那么输入x跟输出y之间的关系为线性的,便可以不需要网络结构,直接使用线性组合便可以.只有在输出层极小可能使用线性**函数,在隐含层都使用非线性**函数.
来自(深度学习使用到的**函数种类和优缺点解释)
常见三种**函数:
Sigmoid.
Sigmoid(也叫逻辑**函数) 非线性**函数的形式是,其图形如上图所示。之前我们说过,sigmoid函数输入一个实值的数,然后将其压缩到0~1的范围内。特别地,大的负数被映射成0,大的正数被映射成1。sigmoid function在历史上流行过一段时间因为它能够很好的表达“**”的意思,未**就是0,完全饱和的**则是1。而现在sigmoid已经不怎么常用了,主要是因为它有两个缺点:
Sigmoids saturate and kill gradients. Sigmoid容易饱和,并且当输入非常大或者非常小的时候,神经元的梯度就接近于0了,从图中可以看出梯度的趋势。这就使得我们在反向传播算法中反向传播接近于0的梯度,导致最终权重基本没什么更新,我们就无法递归地学习到输入数据了。另外,你需要尤其注意参数的初始值来尽量避免saturation的情况。如果你的初始值很大的话,大部分神经元可能都会处在saturation的状态而把gradient kill掉,这会导致网络变的很难学习。
Sigmoid outputs are not zero-centered. Sigmoid 的输出不是0均值的,这是我们不希望的,因为这会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响:假设后层神经元的输入都为正(e.g. x>0 elementwise in ),那么对w求局部梯度则都为正,这样在反向传播的过程中w要么都往正方向更新,要么都往负方向更新,导致有一种捆绑的效果,使得收敛缓慢。
当然了,如果你是按batch去训练,那么每个batch可能得到不同的符号(正或负),那么相加一下这个问题还是可以缓解。因此,非0均值这个问题虽然会产生一些不好的影响,不过跟上面提到的 kill gradients 问题相比还是要好很多的。
Tanh.
Tanh和Sigmoid是有异曲同工之妙的,它的图形如上图右所示,不同的是它把实值得输入压缩到-1~1的范围,因此它基本是0均值的,也就解决了上述Sigmoid缺点中的第二个,所以实际中tanh会比sigmoid更常用。但是它还是存在梯度饱和的问题。Tanh是sigmoid的变形,
ReLU.
近年来,ReLU 变的越来越受欢迎。它的数学表达式是: f(x)=max(0,x)。很显然,从上图左可以看出,输入信号
<0时,输出为0,>0时,输出等于输入。ReLU的优缺点如下:
优点1:Krizhevsky et al. 发现使用 ReLU 得到的SGD的收敛速度会比 sigmoid/tanh 快很多(如上图右)。有人说这是因为它是linear,而且梯度不会饱和
优点2:相比于 sigmoid/tanh需要计算指数等,计算复杂度高,ReLU 只需要一个阈值就可以得到**值。
缺点1: ReLU在训练的时候很”脆弱”,一不小心有可能导致神经元”坏死”。举个例子:由于ReLU在x<0时梯度为0,这样就导致负的梯度在这个ReLU被置零,而且这个神经元有可能再也不会被任何数据**。如果这个情况发生了,那么这个神经元之后的梯度就永远是0了,也就是ReLU神经元坏死了,不再对任何数据有所响应。实际操作中,如果你的learning rate 很大,那么很有可能你网络中的40%的神经元都坏死了。 当然,如果你设置了一个合适的较小的learning rate,这个问题发生的情况其实也不会太频繁。
3代码实现
pytorch代码实现请参考:
FortiLeiZhang的CS231N作业