NLP基础学习6--神经网络基础

左手肿了两个手指,写博客敲代码这酸爽。。。 言归正传,这一次的学习开始转入神经网络,主要任务是:

  1. 前馈神经网络、网络层数、输入层、隐藏层、输出层、隐藏单元、激活函数的概念。
  2. 感知机相关;利用tensorflow等工具定义简单的几层网络(激活函数sigmoid),递归使用链式法则来实现反向传播。
  3. 激活函数的种类以及各自的提出背景、优缺点。(和线性模型对比,线性模型的局限性,去线性化)
  4. 深度学习中的正则化(参数范数惩罚:L1正则化、L2正则化;数据集增强;噪声添加;early stop;Dropout层)、正则化的介绍。
  5. 深度模型中的优化:参数初始化策略;自适应学习率算法(梯度下降、AdaGrad、RMSProp、Adam;优化算法的选择);batch norm层(提出背景、解决什么问题、层在训练和测试阶段的计算公式);layer norm层
    本章的内容主题参考了复旦大学邱锡鹏教授的大作《神经网络和深度学习》[3],在此特别致敬和感谢一下。

前馈神经网络

给定一组神经元,我们可以以神经元为节点来构建一个网络。不同的神经
网络模型有着不同网络连接的拓扑结构。一种比较直接的拓扑结构是前馈网络。
前馈神经网络(Feedforward Neural Network,FNN)是最早发明的简单人工神
经网络。
在前馈神经网络中,各神经元分别属于不同的层。每一层的神经元可以接
收前一层神经元的信号,并产生信号输出到下一层。第0 层叫输入层,最后一
层叫输出层,其它中间层叫做隐藏层。整个网络中无反馈,信号从输入层向输
出层单向传播,可用一个有向无环图表示。 一个简单的示意图如下:
前馈神经网络

前馈神经网络通过下面公式进行信息传播,
z ( l ) = W ( l ) ⋅ a ( l − 1 ) + b ( l ) z^{(l)} = W^{(l)} · a^{(l−1)} + b^{(l)} z(l)=W(l)a(l1)+b(l)
a ( l ) = f l ( z ( l ) ) a^{(l)} = f_l(z^{(l)}) a(l)=fl(z(l))
其中

  • L:表示神经网络的层数;
  • m ( l ) m^{(l)} m(l):表示第l 层神经元的个数;
  • f l ( ⋅ ) f_l(·) fl():表示l 层神经元的激活函数;
  • W ( l ) ∈ R m ( l ) × m ( l − 1 ) W^{(l)} \in R^{m^{(l)}×m^{(l−1)}} W(l)Rm(l)×m(l1):表示l − 1 层到第l 层的权重矩阵;
  • b ( l ) ∈ R m l b^{(l)} \in R^{m^l} b(l)Rml:表示l − 1 层到第l 层的偏置;
  • z ( l ) ∈ R m l z^{(l)} \in R^{m^l} z(l)Rml:表示l 层神经元的净输入;
  • a ( l ) ∈ R m l a^{(l)} \in R^{m^l} a(l)Rml:表示l 层神经元的输出

感知机相关

感知机模型是机器学习二分类问题中的一个非常简单的模型。它的基本结构如下图所示:
感知机
其实,感知机作为最简单的分类器,确实神经网络的基础。早期很多神经网络和机器学习的书都有很多讨论,包括其收敛性证明,推荐台湾的林轩田老师的机器学习基石的课,这一章讲的很细致。
这里我因为手疼的缘故,直接省去理论,贴代码了,最简单的minist手写识别,相当于神经网络的hello word


# coding: utf-8

# # 神经网络,TensorFlow示例
# 
# 两层(隐含层)全连接神经网络


# ## MNIST 数据库概览
# 
# MNIST手写数字数据集. 共有60,000张训练图片,10,000张测试图片. 所有图片已做过归一化与中心化,尺寸统一为28x28,像素值范围从0到1. 为了简单期间,输入时每张图片不以28x28的矩阵形式输入,而是按列排列,转为一维的长度为784=28x28的特征,输入神经网络。
# 
# ![MNIST 数据集](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)
# 
# 更多信息: http://yann.lecun.com/exdb/mnist/




# Import MNIST data,准备MNIST输入数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)

import tensorflow as tf



# Hyper Parameters,超参
learning_rate = 0.1
num_steps = 500
batch_size = 128
display_step = 100

# Network Parameters,网络参数
n_hidden_1 = 256 # 1st layer number of neurons
n_hidden_2 = 256 # 2nd layer number of neurons
num_input = 784 # MNIST data input (img shape: 28*28)
num_classes = 10 # MNIST total classes (0-9 digits)

# tf Graph input,TensorFlow图模型结构的输入定义
X = tf.placeholder("float", [None, num_input])
Y = tf.placeholder("float", [None, num_classes])


# Store layers weight & bias,定义需要学习的网络参数,即网络权值
weights = {
    'h1': tf.Variable(tf.random_normal([num_input, n_hidden_1])),
    'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),
    'out': tf.Variable(tf.random_normal([n_hidden_2, num_classes]))
}
biases = {
    'b1': tf.Variable(tf.random_normal([n_hidden_1])),
    'b2': tf.Variable(tf.random_normal([n_hidden_2])),
    'out': tf.Variable(tf.random_normal([num_classes]))
}



# Create model,定义神经网络基本结构
def neural_net(x):
    # Hidden fully connected layer with 256 neurons
    layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
    # Hidden fully connected layer with 256 neurons
    layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
    # Output fully connected layer with a neuron for each class
    out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
    return out_layer


# Construct model,初始化所定义神经网络结构的实例
logits = neural_net(X)

# Define loss and optimizer,定义误差函数与优化方式
loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
    logits=logits, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss_op)

# Evaluate model (with test logits, for dropout to be disabled),定义检验模型效果的方式
correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

# Initialize the variables (i.e. assign their default value),初始化所有定义的网络变量
init = tf.global_variables_initializer()



# Start training,开启session,将所有定义编译成实际的TensorFlow图模型并运行
with tf.Session() as sess:

    # Run the initializer
    sess.run(init)

    for step in range(1, num_steps+1):
        batch_x, batch_y = mnist.train.next_batch(batch_size)
        # Run optimization op (backprop),进行前向传播,后向传播,以及优化
        sess.run(train_op, feed_dict={X: batch_x, Y: batch_y})
        if step % display_step == 0 or step == 1:
            # Calculate batch loss and accuracy,计算每一批数据的误差及准确度
            loss, acc = sess.run([loss_op, accuracy], feed_dict={X: batch_x,
                                                                 Y: batch_y})
            print("Step " + str(step) + ", Minibatch Loss= " +                   "{:.4f}".format(loss) + ", Training Accuracy= " +                   "{:.3f}".format(acc))

    print("Optimization Finished!")

    # Calculate accuracy for MNIST test images,计算测试数据上的准确度
    print("Testing Accuracy:",         sess.run(accuracy, feed_dict={X: mnist.test.images,
                                      Y: mnist.test.labels}))


minist

激活函数

激活函数在神经元中非常重要的,是增强网络的非线性表示能力和学习能
力的关键,一般需要具备以下几点性质:

  1. 连续并可导(允许少数点上不可导)的非线性函数。可导的激活函数可以
    直接利用数值优化的方法来学习网络参数。
  2. 激活函数及其导函数要尽可能的简单,有利于提高网络计算效率。
  3. 激活函数的导函数的值域要在一个合适的区间内,不能太大也不能太小,
    否则会影响训练的效率和稳定性

Sigmoid 型激活函数

Sigmoid 型函数是指一类S 型曲线函数,为两端饱和函数。常用的Sigmoid
型函数有Logistic 函数和Tanh 函数。

Logistic 函数

Logistic 函数定义为
σ ( x ) = 1 1 + e x p ( − x ) \sigma(x) = \frac{1}{1 + exp(−x)} σ(x)=1+exp(x)1

Logistic 函数可以看成是一个“挤压”函数,把一个实数域的输入“挤压”
到(0, 1)。当输入值在0 附近时,Sigmoid 型函数近似为线性函数;当输入值靠近
两端时,对输入进行抑制。输入越小,越接近于0;输入越大,越接近于1。这
样的特点也和生物神经元类似,对一些输入会产生兴奋(输出为1),对另一些
输入产生抑制(输出为0)。和感知器使用的阶跃激活函数相比,Logistic 函数
是连续可导的,其数学性质更好。
因为Logistic 函数的性质,使得装备了Logistic 激活函数的神经元具有以下
两点性质:1)其输出直接可以看作是概率分布,使得神经网络可以更好地和统
计学习模型进行结合。2)其可以看作是一个软性门(Soft Gate),用来控制其
它神经元输出信息的数量。

Tanh 函数

Tanh 函数是也一种Sigmoid 型函数。其定义为
t a n h ( x ) = e x p ( x ) − e x p ( − x ) e x p ( x ) + e x p ( − x ) tanh(x) = \frac{exp(x) − exp(−x)}{exp(x) + exp(−x)} tanh(x)=exp(x)+exp(x)exp(x)exp(x)

Tanh 函数可以看作是放大并平移的Logistic 函数,其值域是(−1, 1),即 t a n h ( x ) = 2 σ ( 2 x ) − 1 tanh(x) = 2σ(2x) − 1 tanh(x)=2σ(2x)1
在这里插入图片描述

修正线性单元 Relu

修正线性单元(Rectified Linear Unit,ReLU)是目前深层神经网络中经常使用的激活函数。ReLU实际上是一个斜坡(ramp)函数,定义为
R e L U ( x ) = { x x &gt; = 0 0 x &lt; 0 = m a x ( 0 , x ) . ReLU(x) =\left\{ \begin{aligned} x &amp; &amp; x&gt;=0 \\ 0&amp; &amp; x&lt;0 \end{aligned} \right. = max(0, x). ReLU(x)={x0x>=0x<0=max(0,x).

优点: 采用ReLU 的神经元只需要进行加、乘和比较的操作,计算上更加高效。
ReLU函数被认为有生物上的解释性,比如单侧抑制、宽兴奋边界(即兴奋程度
也可以非常高)。在生物神经网络中,同时处于兴奋状态的神经元非常稀疏。人
脑中在同一时刻大概只有1 ∼ 4%的神经元处于活跃状态。Sigmoid 型激活函数
会导致一个非稀疏的神经网络,而ReLU却具有很好的稀疏性,大约50% 的神
经元会处于激活状态。 在优化方面,相比于Sigmoid 型函数的两端饱和,ReLU函数为左饱和函数,且在x > 0 时导数为1,在一定程度上缓解了神经网络的梯度消失问题,加速梯度下降的收敛速度。
缺点: ReLU 函数的输出是非零中心化的,给后一层的神经网络引入偏置偏移,
会影响梯度下降的效率。此外,ReLU神经元在训练时比较容易“死亡”。在训
ReLU 神经元指采用ReLU练时,如果参数在一次不恰当的更新后,第一个隐藏层中的某个ReLU神经元在 作为激活函数的神经元。所有的训练数据上都不能被激活,那么这个神经元自身参数的梯度永远都会是0,在以后的训练过程中永远不能被激活。这种现象称为死亡ReLU问题(Dying ReLU Problem),并且也有可能会发生在其它隐藏层。
在实际使用中,为了避免上述情况,有几种ReLU的变种也会被广泛使用,如 Leaky ReLU,Parametric ReLU,Exponential Linear Unit,Softplus。当然除此之外,还有用swish函数和分段线性的maxout做激活函数,具体可以参考[1]。

正则化

正则化(Regularization)是通过限制模型复杂度,从而避免过拟合,提
高泛化能力的方法,这里主要总结一下L1/2正则化、数据集增强、噪声添加、early stop和Dropout。

ℓ1 和ℓ2 正则化

ℓ1 和ℓ2 正则化是机器学习中最常用的正则化方法,通过约束参数的ℓ1 和ℓ2
范数来减小模型在训练数据集上的过拟合现象。
通过加入ℓ1 和ℓ2 正则化,优化问题可以写为:
θ ∗ = a r g m i n θ 1 N ∑ L ( y ( n ) , f ( x ( n ) , θ ) + λ ℓ p ( θ ) θ^∗ = argmin_θ \frac{1}{N} \sum \mathcal{L}(y(n), f(x(n), θ)+ λℓ_p(θ) θ=argminθN1L(y(n),f(x(n),θ)+λp(θ)
其中, ℓ p ℓ_p p 为范数函数,p 的取值通常为{1, 2} 代表ℓ1 和ℓ2 范数,λ 为正则化系数。从概率角度看,ℓ1 正则对应着稀疏先验,ℓ2正则对应着高斯先验。ℓ1 范数的约束通常会使得最优解位于坐标轴上,而从使得最终的参数为稀疏性向量。

数据集增强

深层神经网络一般都需要大量的训练数据才能获得比较理想的效果。在数
据量有限的情况下,可以通过数据增强(Data Augmentation)来增加数据量,
提高模型鲁棒性,避免过拟合。目前,数据增强还主要应用在图像数据上,在
文本等其它类型的数据还没有太好的方法。
图像数据的增强主要是通过算法对图像进行转变,引入噪声等方法来增加
数据的多样性。增强的方法主要有几种:

  • 旋转(Rotation):将图像按顺时针或逆时针方向随机旋转一定角度;
  • 翻转(Flip):将图像沿水平或垂直方法随机翻转一定角度;
  • 缩放(Zoom In/Out):将图像放大或缩小一定比例;
  • 平移(Shift):将图像沿水平或垂直方法平移一定步长;
  • 加噪声(Noise):加入随机噪声。

Early stop

提前停止(early stop)对于深层神经网络来说是一种简单有效的正则化方
法。由于深层神经网络的拟合能力非常强,因此比较容易在训练集上过拟合。在使用梯度下降法进行优化时,我们可以使用一个和训练集独立的样本集合,称
为验证集(validation set),并用验证集上的错误来代替期望错误。当验证集
上的错误率不再下降,就停止迭代。 然而在实际操作中,验证集上的错误率变化曲线并不一定是平衡曲线,很可能是先升高再降低。因此,提前停止的具体停止标准需要根据实际任务上进行优化

Dropout

当训练一个深层神经网络时,我们可以随机丢弃一部分神经元(同时丢弃
其对应的连接边)来避免过拟合,这种方法称为丢弃法(Dropout Method)。每次选择丢弃的神经元是随机的。最简单的方法是设置一个固定的概率p。对每一个神经元都一个概率p 来判定要不要保留。对于一个神
经层y = f(Wx+b),我们可以引入一个丢弃函数d(·) 使得y = f(Wd(x)+b)。
丢弃函数d(·) 的定义为:
d ( x ) = { m ⊙ x 训 练 阶 段 p x 测 试 阶 段 d(x) =\left\{ \begin{aligned} m\odot x &amp; &amp; 训练阶段 \\ px&amp; &amp; 测试阶段 \end{aligned} \right. d(x)={mxpx
其中 m ∈ { 0 , 1 } d m \in \{0, 1\}^d m{0,1}d是丢弃掩码(dropout mask),通过以概率为p 的贝努力分布随机生成。p 可以通过验证集来选取一个最优的值。或者p 也可以设为0.5,这
对大部分的网络和任务有比较有效。在训练时,激活神经元的平均数量为原来
的p 倍。而在测试时,所有的神经元都是可以激活的,这会造成训练和测试时
网络的输出不一致。为了缓解这个问题,在测试时需要将每一个神经元的输出
乘以p,也相当于把不同的神经网络做了平均。

集成学习的解释:每做一次丢弃,相当于从原始的网络中采样得到一个子网络。
如果一个神经网络有n个神经元,那么总共可以采样出2n 个子网络。每次迭代
都相当于训练一个不同的子网络,这些子网络都共享原始网络的参数。那么,最
终的网络可以近似看作是集成了指数级个不同网络的组合模型。
当然对于Dropout 还有贝叶斯解释,大家可以看[1]和[3]。

深度模型中的优化

参数初始化

神经网络的训练过程中的参数学习是基于梯度下降法进行优化的。梯度下
降法需要在开始训练时给每一个参数赋一个初始值。这个初始值的选取十分关
键。在感知器和logistic 回归的训练中,我们一般将参数全部初始化为0。但是
这在神经网络的训练中会存在一些问题。因为如果参数都为0,在第一遍前向
计算时,所有的隐层神经元的激活值都相同。这样会导致深层神经元没有区分
性。这种现象也称为对称权重现象。
为了打破这个平衡,比较好的方式是对每个参数都随机初始化,这样使得
不同神经元之间的区分性更好。
如果要高质量地训练一个网络,给参数选取一个合适的初始化区间
是非常重要的。一般而言,参数初始化的区间应该根据神经元的性质进行差异
化的设置。如果一个神经元的输入连接很多,它的每个输入连接上的权重就应
该小一些,以避免神经元的输出过大(当激活函数为ReLU 时)或过饱和(当
激活函数为sigmoid 函数时)。
经常使用的初始化方法有Gaussian 分布初始化 和 Xavier 初始化方法, 后者可以参考文献 [4].

自适应学习率算法

优化算法,写起来就非常的多,这里我手指实在疼痛,偷个懒,贴几个总结。后面有空,我在专门写一个专题。
优化总结
下图给出了这几种优化方法在MNIST数据集上收敛性的比较
优化方法比较

batch norm

在深层神经网络中,中间某一层的输入是其之前的神经层的输出。因此,其
之前的神经层的参数变化会导致其输入的分布发生较大的差异。在使用随机梯
度下降来训练网络时,每次参数更新都会导致网络中间每一层的输入的分布发
生改变。越深的层,其输入的分布会改变得越明显。就像一栋高楼,低楼层发
生一个较小的偏移,都会导致高楼层较大的偏移。
从机器学习角度来看,如果某个神经层的输入分布发生了改变,那么其参
数需要重新学习,这种现象叫做内部协变量偏移(Internal Covariate Shift)
协变量偏移:在传统机器学习中,一个常见的问题的协变量偏移(Covariate
Shift)。协变量是一个统计学概念,是可能影响预测结果的统计变量。
在机器学习中,协变量可以看作是输入。一般的机器学习算法都要求输
入在训练集和测试集上的分布是相似的。如果不满足这个假设,在训练
集上学习到的模型在测试集上的表现会比较差。

为了解决内部协变量偏移问题,就要使得每一个神经层的输入的分布在训
练过程中要保持一致。最简单直接的方法就是对每一个神经层都进行归一化操
作,使其分布保存稳定。
批量归一化(Batch Normalization,BN)方法是一种有效的逐层归一化方法,可以对神经网络中任意的中间层进行归一化操作。对于一个深层神经网络,令第l 层的净输入为 z ( l ) z^(l) z(l),神经元的输出为 a ( l ) a^(l) a(l)。为了减少内部协变量偏移问题,就要使得净输入 z ( l ) z^(l) z(l) 的分布一致,比如都归一化到标准正态分布为了提高归一化效率,一般使用标准归一化,将净输入 z ( l ) z^(l) z(l)的每一维都归一到标准正态分布
z ^ ( l ) = z ( l ) − E [ z ( l ) ] v a r ( z ( l ) ) + ϵ \hat{z}^{(l)} = \frac{z^{(l)} − E[z^{(l)}] }{\sqrt{var(z^{(l)}) + ϵ}} z^(l)=var(z(l))+ϵ z(l)E[z(l)]
var(z(l)) + ϵ
因为目前主要的训练方法是基于小批量的随机梯度下降方法,所以准确
地计算 z ( l ) z^(l) z(l) 的期望和方差是不可行的。因此, z ( l ) z^(l) z(l) 的期望和方差通常用当前小批量样本集的均值和方差近似估计。
给定一个包含K 个样本的小批量样本集合,第l 层神经元的净输入 z ( 1 , l ) z^{(1,l)} z(1,l),
· · · , z ( K , l ) z^{(K,l)} z(K,l) 的均值和方差为:
μ β = 1 K ∑ k = 1 K z ( k , l ) μ_\beta = \frac{1}{K} \sum_{k=1}^K z^{(k,l)} μβ=K1k=1Kz(k,l)
σ β 2 = 1 K ∑ k = 1 K ( z ( k , l ) − μ β ) ⊙ ( z ( k , l ) − μ β ) σ_{\beta}^2 = \frac{1}{K} \sum_{k=1}^K (z^{(k,l)} − μ_\beta) \odot (z^{(k,l)} − μ_\beta) σβ2=K1k=1K(z(k,l)μβ)(z(k,l)μβ)
对净输入z(l) 的标准归一化会使得其取值集中的0 附近,如果使用sigmoid
型激活函数时,这个取值区间刚好是接近线性变换的区间,减弱了神经网络的
非线性性质。因此,为了使得归一化不对网络的表示能力造成负面影响,我们
可以通过一个附加的缩放和平移变换改变取值区间
z ^ ( l ) = z ( l ) − μ β σ β 2 + ϵ ⊙ γ + β \hat{z}^{(l)} = \frac{ z^{(l)} − μ_\beta} {\sqrt{σ_{\beta}^2+ ϵ}}⊙ γ + β z^(l)=σβ2+ϵ z(l)μβγ+β
≜ B N γ , β ( z ( l ) ) \triangleq BN_{γ,β}(z^{(l)}) BNγ,β(z(l))

其中γ 和β 分别代表缩放和平移的参数向量.
批量归一化操作可以看作是一个特殊的神经层,加在每一层非线性激活函
数之前,即 a ( l ) = f ( B N γ , β ( z ( l ) ) ) = f ( B N γ , β ( W a ( l − 1 ) ) ) a(l) = f(BN_{γ,β}(z^{(l)})) =f(BN_{γ,β}(Wa^{(l−1)})) a(l)=f(BNγ,β(z(l)))=f(BNγ,β(Wa(l1)))

layer norm

批量归一化是对一个中间层的单个神经元进行归一化操作,因此要求小批
量样本的数量不能太小,否则难以计算单个神经元的统计信息。此外,如果一
个神经元的净输入的分布在神经网络中是动态变化的,比如循环神经网络,那
么就无法应用批量归一化操作

层归一化(Layer Normalization) 是和批量归一化非常类似的方法。和批量归一化不同的是,层归一化是对一个中间层的所有神经元进行归一化。层归一化定义为:
z ^ ( l ) = z ( l ) − μ l σ l 2 + ϵ ⊙ γ + β \hat{z}^{(l)} = \frac{ z^{(l)} − μ^{l}} {\sqrt{σ^{l^2}+ ϵ}}⊙ γ + β z^(l)=σl2+ϵ z(l)μlγ+β
≜ L N γ , β ( z ( l ) ) \triangleq LN_{γ,β}(z^{(l)}) LNγ,β(z(l))
其中γ 和β 分别代表缩放和平移的参数向量,和 z ( l ) z^{(l)} z(l)维数相同

在标准循环神经网络中,循环神经层的净输入一般会随着时间慢慢变大或变小,从而导致梯度爆炸或消失。而层归一化的循环神经网络可以有效地缓解这种状况。
,

参考文献

  1. Goodfellow I, Bengio Y, Courville A. Deep learning[M]. MIT press, 2016.
  2. 红色石头Will, 一看就懂的感知机算法PLA,CSDN(https://blog.csdn.net/red_stone1/article/details/80491895)
  3. 邱锡鹏, 《神经网络和深度学习》
  4. Xavier Glorot and Yoshua Bengio. Understanding the difficulty of training deep
    feedforward neural networks. In International conference on artificial intelligence
    and statistics, pages 249–256, 2010
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值