深度学习之--神经网络基础

一、前馈神经网络


概念:前馈神经网络、网络层数、输入层、隐藏层、输出层、隐藏单元、激活函数

前馈神经网络:前馈神经网络是一种最简单的神经网络,各神经元分层排列。每个神经元只与前一层的神经元相连。接收前一层的输出,并输出给下一层.各层间没有反馈。

深度学习之--神经网络基础_第1张图片

网络层数:一般是指设置或者搭建的模型有多少层。以上图为例,网络层为3。注:一般不包括输入层。

输入层:一般指数据输入模型的一层,如图中 Layer L1 层。

输出层:一般指模型的最后一层,即Layer L4 层;

隐藏层:指除开输入层和输出层之外的中间层,如图Layer L2 层和 L3层;

隐藏单元:一般指隐藏层中的单元结构。

深度学习之--神经网络基础_第2张图片

激活函数:一般指加权之后的值到输出之间函数,通过激活函数将上一层的输出作为下一层输入之前进行非线性变化,使模型不再是单一的线性变换。

二、简单的神经网络(tensorflow)


前期准备

深度学习之--神经网络基础_第3张图片

通过二层的全连接网络,实现 mnist 数据集分类任务。

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist_path = '/home/jie/Jie/codes/tf/datasets/MNIST_data/'
mnist = input_data.read_data_sets(mnist_path, one_hot=True)

# 定义超参数和其他常量
n_input = 784            # 28 * 28
n_classes = 10

max_epochs = 10000
learning_rate = 0.5
batch_size = 10
seed = 0
n_hidden = 30 

## Sigmoid 函数的导数
def sigmaprime(x):
    return tf.multiply(tf.sigmoid(x), tf.subtract(tf.constant(1.0), tf.sigmoid(x)))

# 为训练数据创建占位符
x_in = tf.placeholder(tf.float32, [None, n_input], name='x_in')
y = tf.placeholder(tf.float32, [None, n_classes], name='y')

# 创建模型
def multilayer_perceptron(x, weight, biases):
    h_layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['h1'])
    out_layer_1 = tf.sigmoid(h_layer_1)

    h_out = tf.add(tf.matmul(out_layer_1, weights['h2']), biases['h2'])
    return tf.sigmoid(h_out), h_out, out_layer_1, h_layer_1

# 权重
weights = {
    'h1':tf.Variable(tf.random_normal([n_input, n_hidden], seed=seed)),
    'h2':tf.Variable(tf.random_normal([n_hidden, n_classes], seed=seed))}
# 偏置
biases = {
    'h1':tf.Variable(tf.random_normal([1, n_hidden], seed=seed)),
    'h2':tf.Variable(tf.random_normal([1, n_classes], seed=seed))}

# 正向传播
y_hat, h_2, o_1, h_1 = multilayer_perceptron(x_in, weights, biases)

# loss function
err = y - y_hat
loss = tf.reduce_mean(tf.square(err, name='loss'))

# 反向传播
delta_2 = tf.multiply(err, sigmaprime(h_2))
delta_w_2 = tf.matmul(tf.transpose(o_1), delta_2)

wtd_error = tf.matmul(delta_2, tf.transpose(weights['h2']))
delta_1 = tf.multiply(wtd_error, sigmaprime(h_1))
delta_w_1 = tf.matmul(tf.transpose(x_in), delta_1)

eta = tf.constant(learning_rate)

# 更新权重
train = [
    tf.assign(weights['h1'], tf.add(weights['h1'], tf.multiply(eta, delta_w_1))),
    tf.assign(biases['h1'], tf.add(biases['h1'], tf.multiply(eta, tf.reduce_mean(delta_1, axis=[0])))),
    tf.assign(weights['h2'], tf.add(weights['h2'], tf.multiply(eta, delta_w_2))),
    tf.assign(biases['h2'], tf.add(biases['h2'], tf.multiply(eta, tf.reduce_mean(delta_2, axis=[0]))))
]

# 定义精度
acc_mat = tf.equal(tf.argmax(y_hat, 1), tf.argmax(y, 1))
accuracy = tf.reduce_sum(tf.cast(acc_mat, tf.float32))

# train
init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)
    for epoch in range(max_epochs):
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        _, loss1 = sess.run([train, loss], feed_dict={x_in: batch_xs, y: batch_ys})
        if epoch % 1000 == 0:
            print('Epoch: {0}   loss: {1}'.format(epoch, loss1))

    acc_test = sess.run(accuracy, feed_dict={x_in: mnist.test.images, y:mnist.test.labels})
    acc_train = sess.run(accuracy, feed_dict={x_in: mnist.train.images, y:mnist.train.labels})

    # 评估
    print('Accuracy Train%: {1}  Accuracy Test%: {2}'.format(epoch, acc_train / 600, (acc_test / 100)))


输出结果

Epoch: 0   loss: 0.3155866861343384
Epoch: 1000   loss: 0.023114416748285294
Epoch: 2000   loss: 0.017101742327213287
Epoch: 3000   loss: 0.01927866041660309
Epoch: 4000   loss: 0.019498592242598534
Epoch: 5000   loss: 0.017000144347548485
Epoch: 6000   loss: 0.006083908025175333
Epoch: 7000   loss: 0.018798980861902237
Epoch: 8000   loss: 0.04835653677582741
Epoch: 9000   loss: 0.0037784664891660213
Accuracy Train%: 84.58166666666666  Accuracy Test%: 92.65



三、激活函数


1、概述


主要作用:提供网络的非线性建模能力。
假设一个神经网络中仅包含线性卷积和全连接运算,那么该网络仅能够表达线性映射,即无论神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当。加入(非线性)激活函数之后,深度神经网络才具备了分层的非线性映射学习能力,几乎可以逼近任意函数。

激活函数的性质:

可微性: 当优化方法是基于梯度的时候,这个性质是必须的。
单调性: 当激活函数是单调的时候,单层网络能够保证是凸函数。
输出值的范围: 当激活函数输出值是 有限 的时候,基于梯度的优化方法会更加 稳定,因为特征的表示受有限权值的影响更显著;当激活函数的输出是 无限 的时候,模型的训练会更加高效,不过在这种情况小,一般需要更小的learning rate
常用的激活函数:

sigmoid
tanh
ReLU
Leaky ReLU
Maxout
ELU

2、sigmoid 激活函数


(1)定义


sigmoid sigmoidsigmoid 函数,即著名的 Logistic LogisticLogistic 函数,是常用的非线性激活函数,可将变量映射到 (0,1) (0,1)(0,1) 之间,公式如下:

 

几何图像如下:

深度学习之--神经网络基础_第4张图片


(2)特点


能将输入的连续实值映射到 (0,1) (0,1)(0,1) 之间。特别的,若非常小的负值,输出为 0;非常大的正值,输出为1。

 

(3)缺点


缺点1:梯度爆炸和梯度消失

深度学习之--神经网络基础_第5张图片深度学习之--神经网络基础_第6张图片

 

缺点2:Sigmoid 的 output 不是0均值(即zero-centered)


其值域在[0,1]之间,函数输出不是0均值的,权重更新效率降低,因为这会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响:假设后层神经元的输入都为正(e.g. x>0 x>0x>0 elementwise in ),那么对w求局部梯度则都为正,这样在反向传播的过程中w要么都往正方向更新,要么都往负方向更新,导致有一种捆绑的效果,使得收敛缓慢。 当然了,若按batch去训练,那么每个batch可能得到不同的符号(正或负),那么相加一下这个问题还是可以缓解。因此,非0均值这个问题虽然会产生一些不好的影响,不过跟上面提到的梯度消失问题相比还是要好很多的。

缺点3:耗时


其解析式中含有幂运算,计算机求解时相对来讲比较耗时。对于规模比较大的深度网络,这会较大地增加训练时间。

缺点4:梯度弥散(死区)


受现有的梯度下降算法所限(严重依赖逐层的梯度计算值),Sigmoid函数对落入 (−∞,−5)∪(5,+∞) (-∞,-5) ∪ (5,+∞)(−∞,−5)∪(5,+∞)的输入值,梯度计算为 0 00,发生 梯度弥散。因此该函数存在一正一负 两块“死区”[蓝框区域]:

深度学习之--神经网络基础_第7张图片


3、tanh函数

 

深度学习之--神经网络基础_第8张图片

深度学习之--神经网络基础_第9张图片


优点

ReLU解决了梯度消失的问题,至少 x xx 在正区间内,神经元不会饱和。
由于ReLU线性、非饱和的形式,在SGD中能够快速收敛。
计算速度要快很多。线性计算
缺点

ReLU的输出不是“零为中心”(Notzero-centered output)。
随着训练的进行,可能会出现神经元死亡,权重无法更新的情况。(不可逆转的死亡)
解释:训练神经网络的时候,一旦学习率没有设置好,第一次更新权重的时候,输入是负值,那么这个含有ReLU的神经节点就会死亡,再也不会被激活。
因为:ReLU的导数在x>0的时候是1,在x<=0的时候是0。如果x<=0,那么ReLU的输出是0,那么反向传播中梯度也是0,权重就不会被更新,导致神经元不再学习。 也就是说,这个ReLU激活函数在训练中将不可逆转的死亡,导致了训练数据多样化的丢失。
在实际训练中,如果学习率设置的太高,可能会发现网络中40%的神经元都会死掉,且在整个训练集中这些神经元都不会被激活。所以,设置一个合适的较小的学习率,会降低这种情况的发生。为了解决神经元节点死亡的情况,有人提出了Leaky ReLU、P-ReLU、R-ReLU、ELU等激活函数。

4、ReLU函数

深度学习之--神经网络基础_第10张图片

优点:

ReLU解决了梯度消失的问题,至少 x xx 在正区间内,神经元不会饱和。
由于ReLU线性、非饱和的形式,在SGD中能够快速收敛。
计算速度要快很多

缺点:

ReLU的输出不是“零为中心”(Notzero-centered output)。
随着训练的进行,可能会出现神经元死亡,权重无法更新的情况。(不可逆转的死亡)
解释:训练神经网络的时候,一旦学习率没有设置好,第一次更新权重的时候,输入是负值,那么这个含有ReLU的神经节点就会死亡,再也不会被激活。
因为:ReLU的导数在x>0的时候是1,在x<=0的时候是0。如果x<=0,那么ReLU的输出是0,那么反向传播中梯度也是0,权重就不会被更新,导致神经元不再学习。 也就是说,这个ReLU激活函数在训练中将不可逆转的死亡,导致了训练数据多样化的丢失。
在实际训练中,如果学习率设置的太高,可能会发现网络中40%的神经元都会死掉,且在整个训练集中这些神经元都不会被激活。所以,设置一个合适的较小的学习率,会降低这种情况的发生。为了解决神经元节点死亡的情况,有人提出了Leaky ReLU、P-ReLU、R-ReLU、ELU等激活函数。
 

5、Leaky ReLU

Leaky ReLU 是 ReLU 激活函数的改进版本,公式如下:

深度学习之--神经网络基础_第11张图片



​   
优点

神经元不会出现死亡的情况。
对于所有的输入,不管是大于等于0还是小于0,神经元不会饱和。
由于Leaky ReLU线性、非饱和的形式,在SGD中能够快速收敛。
计算速度要快很多。只有线性关系。
缺点

Leaky ReLU函数中的 α αα,需要通过先验知识人工赋值,通常 α=0.01 \alpha=0.01α=0.01。
6、Maxout
 

深度学习之--神经网络基础_第12张图片

优点

Maxout能够缓解梯度消失
规避了ReLU神经元死亡的缺点

缺点

增加了参数和计算量。


7、ELU


ELU(Exponential Linear Units,指数线性单元)。它试图将激活函数的输出平均值接近零,从而加快学习速度。同时,它还能通过正值的标识来避免梯度消失的问题。
公式如下:

深度学习之--神经网络基础_第13张图片

 

其中,超参数 α 常被设定为 1
 

深度学习之--神经网络基础_第14张图片


优点

完美解决了死区问题。
ELU激活函数的输出均值是接近于零的

缺点

计算较复杂。


8、激活函数的选择


(1)深度学习需要大量时间处理大量数据,模型的收敛速度尤为重要。所以,总体来讲,训练深度神经网络尽量使用 zero-centered 数据(预处理实现) 和 zero-centered 输出。
==》尽量选择输出具有 zero-centered 特点的激活函数来加速模型的收敛速度。
(2)在使用 ReLU 时,小心设置 learning_rate,而且注意不要出现很多神经元死亡。若不好解决,可尝试 Leaky ReLU、Maxout 等。
(3)最好不使用 sigmoid、tanh
(4)conv -> bn -> relu 标配的 module。

四、正则化


正则化是选择模型的一种方法,具体来说,选择经验风险与模型复杂度同时较小的模型(防止过拟合),这样可以较少泛化误差而不是训练误差。

常用正则化方法

  • 参数范数惩罚:L1正则化、L2正则化;
  • 数据集增强;
  • 噪声添加;
  • early stop;
  • Dropout层

1、参数范数惩罚


参数范数惩罚通过对目标函数 J 添加一个参数范数惩罚 Ω(θ) ,限制模型的学习能力。

 

其中 α≥0 \alpha\geq0α≥0 是权衡范数惩罚项 Ω ΩΩ 和标准目标函数 J(X;θ) J(X;θ)J(X;θ) 相对贡献的超参数,通过调整 α \alphaα 的大小,可以获得不同的参数选择偏好。

注意:参数包括模型中每一层仿射变换的权重和偏置,我们通常只对权重做惩罚,而不对偏置做正则惩罚。因为精确拟合偏置所需的数据通常比权重少的多,正则化偏置参数可能会导致明显的欠拟合。

(1)相关定义


L0范数:权重向量 W WW 中非0的元素的个数,通常表示为 ∣∣W∣∣0 |。
L1范数:权值向量 W WW 中各个元素的绝对值之和,通常表示为 ∣∣W∣∣1 |。
L2范数:权值向量 W WW 中各个元素的平方的和的开方值,通常表示为 ∣∣W∣∣2 | 。

任何的规则化算子,如果它在 Wi=0 处不可微,并且可以分解为一个“求和”的形式,则该规则化算子就可以实现稀疏。

稀疏的好处

特征选择(Feature Selection):能实现特征的自动选择
稀疏规则化算子会学习地去掉这些对最终输出结果没有关系或者不提供任何信息的特征,也就是把这些特征对应的权重置为0。

可解释性(Interpretability)
非零权重的特征为输出结果提供的信息是巨大的、决策性的。

(2)L0正则化
从直观上看,利用非零参数的个数,可以很好的来选择特征,实现特征稀疏的效果,具体操作时选择参数非零的特征即可。

但因为L0正则化很难求解,是个NP难问题,因此一般采用L1正则化。L1正则化是L0正则化的最优凸近似,比L0容易求解,并且也可以实现稀疏的效果。

(3)L1正则化
L1范数也称叫“稀疏规则算子”(Lasso regularization),L1范数和 L0范数 可以实现稀疏,L1因具有比L0更好的优化求解特性而被广泛应用。

(4)L2正则化
L2范数,在回归中称为:“岭回归” (Ridge Regression) 或 “权值衰减weight decay”。

让L2范数的规则项 ∣∣W∣∣2 |最小,可以使得 W 的每个元素都很小,都接近于0(不会让它等于0)。而越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象。

2、数据扩增(data agumentation)
较少过拟合的最简单方法:增加训练集样本,也称数据扩增(data agumentation)。但是由于标注数据昂贵,需要通过其他方式增加样本。

图像处理:旋转、翻转、放缩、平移等等。
GAN(对抗式生成网络)
3、噪声添加
噪声添加:将其直接添加到学习到的权重上。这项技术主要被用于循环神经网络的情况下。
在某些假设下,施加于权重的噪声可以被解释为与更传统的正则化形式等同,鼓励要学习的函数保持稳定。

4、Dropout
Dropout:在用前向传播算法和反向传播算法训练模型时,随机的从全连接DNN网络中去掉一部分隐含层的神经元。

两种理解:

减少神经元之间复杂的共适应关系:在每次训练的时候使用dropout,每个神经元有一定的概率被移除,这样可以使得一个神经元的训练不依赖于另外一个神经元,同样也就使得特征之间的协同作用被减弱。 Hinton认为,过拟合可以通过阻止某些特征的协同作用来缓解。
多模型效果平均的方式。对于减少测试集中的错误,我们可以将多个不同神经网络的预测结果取平均,而因为dropout的随机性,在每次dropout后,网络模型可看成不同结构的神经网络,而要训练的参数数目却是不变的,这就解脱了训练多个独立的不同神经网络的时耗问题。在测试输出的时候,将输出权重乘以保留概率1-p%,从而达到类似平均的效果。
左边的图为一个完全的全连接层,右边为应用dropout后的全连接层。


做法

训练阶段:每次更新参数之前,每个神经元有一定的概率被丢弃,假设为p%,p可以设为50或者根据验证集的表现来选取,输入层的p比较小,保留概率接近于1
测试阶段不dropout,保留所有单元的权重,而且要乘以保留概率1-p%,为了保证输出期望一致。
dropout不只用于前馈神经网络,还可以用于图模型,比如玻尔兹曼机。

5、早期停止(early stopping)
早期停止是一种交叉验证策略,将一部分数据集作为验证集(validation set)。

当我们看到验证集上的性能越来越差时,就停止对模型的训练。
 

你可能感兴趣的:(NLP学习笔记,神经网络)