神经网络基础

前馈神经网络

人工神经元(Artificial Neuron),简称神经元(Neuron),是构成神经网络的基本单元,其主要是模拟生物神经元的结构和特性,接受一组输入信号并产出输出。

1558513900032

前馈网络中各个神经元按接受信息的先后分为不同的组。每一组可以看作一个神经层。每一层中的神经元接受前一层神经元的输出,并输出到下一层神经元。整个网络中的信息是朝一个方向传播,没有反向的信息传播,可以用一个有向无环路图表示。

在前馈神经网络中,各神经元分别属于不同的层。每一层的神经元可以接收前一层神经元的信号,并产生信号输出到下一层。第0层叫输入层,最后一层叫输出层,其它中间层叫做隐藏层。整个网络中无反馈,信号从输入层向输出层单向传播,可用一个有向无环图表示。

1558514396889

感知机

假设采用随机梯度下降进行神经网络参数学习,给定一个样本(x, y),将其输入到神经网络模型中,得到网络输出为yˆ。假设损失函数为L(y, yˆ),要进行参数学习就需要计算损失函数关于每个参数的导数。

1558514509767

激活函数

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

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

Sigmoid 型激活函数

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

Logistic 函数

Logistic函数定义为 $$σ(x)=\frac{1}{1 + exp(−x)}$$Logistic函数可以看成是一个“挤压”函数,把一个实数域的输入“挤压”到(0, 1)。当输入值在0附近时,Sigmoid型函数近似为线性函数;当输入值靠近两端时,对输入进行抑制。输入越小,越接近于0;输入越大,越接近于1。这样的特点也和生物神经元类似,对一些输入会产生兴奋(输出为1),对另一些输入产生抑制(输出为0)。和感知器使用的阶跃激活函数相比,Logistic 函数是连续可导的,其数学性质更好。

Tanh 函数

$$tanh(x)=\frac{exp(x) − exp(−x)}{exp(x) + exp(−x)}$$

Tanh函数可以看作是放大并平移的Logistic函数,其值域是(−1, 1)。

$$tanh(x) = 2σ(2x) − 1. $$

修正线性单元

修正线性单元(Rectified Linear Unit,ReLU)[Nair and Hinton, 2010],也叫rectifier函数[Glorot et al.,2011],是目前深层神经网络中经常使用的激活函数。

$$ReLU(x) =max(0,x)$$

优点 采用 ReLU 的神经元只需要进行加、乘和比较的操作,计算上更加高效。ReLU函数被认为有生物上的解释性,比如单侧抑制、宽兴奋边界(即兴奋程度也可以非常高)。在生物神经网络中,同时处于兴奋状态的神经元非常稀疏。

缺点 ReLU 函数的输出是非零中心化的,给后一层的神经网络引入偏置偏移,会影响梯度下降的效率。此外,ReLU神经元在训练时比较容易“死亡”。在训练时,如果参数在一次不恰当的更新后,第一个隐藏层中的某个ReLU神经元在所有的训练数据上都不能被激活,那么这个神经元自身参数的梯度永远都会是0,在以后的训练过程中永远不能被激活。

Swish 函数

Swish 函数是一种自门控(Self-Gated)激活函数 [Ramachandran et al.,2017],定义为

$$swish(x) = xσ(βx)$$

其中σ(·)为Logistic函数,β 为可学习的参数或一个固定超参数。σ(·) ∈ (0, 1)可以看做是一种软性的门控机制。当σ(βx)接近于1时,门处于“开”状态,激活函数的输出近似于x本身;当σ(βx)接近于0时,门的状态为“关”,激活函数
的输出近似于0。

Maxout 单元

Maxout单元[Goodfellow et al., 2013]也是一种分段线性函数。Sigmoid型函数、ReLU 等激活函数的输入是神经元的净输入 z,是一个标量。而 maxout单元的输入是上一层神经元的全部原始输入,是一个向量x = [x1; x2; · · · , xd]。

每个maxout单元有K 个权重向量wk ∈ $R_{d}$ 和偏置$b_{k}$ (1 ≤ k ≤ K)。对于输入x,可以得到K 个净输入$z_{k}$, 1 ≤ k ≤ K。

$$z_{k} = w^{T}{k} x + b{k}$$

前馈神经网络的实现

基于tensorflow以及MNIST手写数字实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import tensorflow as tf
import numpy as np
import input_data

#按照高斯分布初始化权重矩阵
def init_weights(shape):
return tf.Variable(tf.random_normal(shape, stddev=0.01))

#定义神经网络模型
def model(X, w_h, w_o):
h = tf.nn.sigmoid(tf.matmul(X, w_h)) # 激活函数采用sigmoid函数
return tf.matmul(h, w_o) # note that we dont take the softmax at the end because our cost fn does that for us


mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)#读取数据
#mnist.train.images是一个55000 * 784维的矩阵, mnist.train.labels是一个55000 * 10维的矩阵
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels

X = tf.placeholder("float", [None, 784])#创建占位符,在训练时传入图片的向量
Y = tf.placeholder("float", [None, 10])#图像的label用一个10维向量表示

w_h = init_weights([784, 625]) # 输入层到隐藏层的权重矩阵,隐藏层包含625个隐藏单元
w_o = init_weights([625, 10])#隐藏层到输出层的权重矩阵

py_x = model(X, w_h, w_o)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, Y)) # 计算py_x与Y的交叉熵
train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost) #通过步长为0.05的梯度下降算法求参数
predict_op = tf.argmax(py_x, 1)# 预测阶段,返回py_x中值最大的index作为预测结果

# Launch the graph in a session
with tf.Session() as sess:
# you need to initialize all variables
tf.initialize_all_variables().run()

for i in range(100):
for start, end in zip(range(0, len(trX), 128), range(128, len(trX)+1, 128)):
sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end]})
print(i, np.mean(np.argmax(teY, axis=1) ==
sess.run(predict_op, feed_dict={X: teX, Y: teY})))

正则化

加上L1正则化后的优化目标(lasso回归):

1558514601653

 加上L2正则化后的优化目标(岭回归):

1558514618918

使用等高线图来表示原目标函数的图像为:

1558514646227

也就是说,当参数 w1与w2w1与w2 取值为图像中最里面那个紫色圆圈上的值时,可以使得原目标函数最小。
  当加上L1正则项之后,目标函数图像为:

1558514680293

当加上L2正则项之后,目标函数图像为:

1558514694328

深度模型中的优化

参数初始化策略

目的

为了让神经网络在训练过程中学习到有用的信息,需要参数更新时的梯度不为0。在一般的全连接网络中,参数更新的梯度和反向传播得到的状态梯度以及输入激活值有关。那么参数初始化应该满足以下两个条件:

  1. 初始化必要条件一:各层激活值不会出现饱和现象(对于sigmoid,tanh);
  2. 初始化必要条件二:各层激活值不为0。

分类

自适应学习率算法

SGD

此处的SGD指mini-batch gradient descent,关于batch gradient descent, stochastic gradient descent, 以及 mini-batch gradient descent的具体区别就不细说了。现在的SGD一般都指mini-batch gradient descent。

SGD就是每一次迭代计算mini-batch的梯度,然后对参数进行更新,是最常见的优化方法了。即:

$$g_t=\nabla_{\theta_{t-1}}{f(\theta_{t-1})}$$

其中,$\eta$是学习率,g_t是梯度 SGD完全依赖于当前batch的梯度,所以可以将$\eta$理解为允许当前batch的梯度多大程度影响参数更新

缺点:(正因为有这些缺点才让这么多大神发展出了后续的各种算法)

  • 选择合适的learning rate比较困难 - 对所有的参数更新使用同样的learning rate。对于稀疏数据或者特征,有时我们可能想更新快一些对于不经常出现的特征,对于常出现的特征更新慢一些,这时候SGD就不太能满足要求了

  • SGD容易收敛到局部最优,并且在某些情况下可能被困在鞍点

Momentum

momentum是模拟物理里动量的概念,积累之前的动量来替代真正的梯度。公式如下:

$$m_t=\mum_{t-1}+g_t$$
$$\Delta{\theta_t}=-\eta
m_t$$

特点:

  • 下降初期时,使用上一次参数更新,下降方向一致,乘上较大的$\mu$
  • 下降中后期时,在局部最小值来回震荡的时候,$gradient\to0$,$\mu$使得更新幅度增大,跳出陷阱
  • 在梯度改变方向的时候,$\mu$能够减少更新 总而言之,momentum项能够在相关方向加速SGD,抑制振荡,从而加快收敛

Nesterov

Adagrad

Adagrad其实是对学习率进行了一个约束。即:

$$n_t=n_{t-1}+g_t^2$$
$$\Delta{\theta_t}=-\frac{\eta}{\sqrt{n_t+\epsilon}}*g_t$$

特点:

  • 前期$g_t$较小的时候, regularizer较大,能够放大梯度
  • 后期$g_t$较大的时候,regularizer较小,能够约束梯度
  • 适合处理稀疏梯度

缺点:

  • 由公式可以看出,仍依赖于人工设置一个全局学习率
  • 设置过大的话,会使regularizer过于敏感,对梯度的调节太大
  • 中后期,分母上梯度平方的累加将会越来越大,使,使得训练提前结束

Adadelta

Adadelta是对Adagrad的扩展,最初方案依然是对学习率进行自适应约束,但是进行了计算上的简化。 Adagrad会累加之前所有的梯度平方,而Adadelta只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值。即:

$$n_t=\nun_{t-1}+(1-\nu)g_t^2$$
$$\Delta{\theta_t} = -\frac{\eta}{\sqrt{n_t+\epsilon}}*g_t$$

$$E|g^2|t=\rho*E|g^2|{t-1}+(1-\rho)*g_t^2$$

其中,$E$代表求期望。

此时,可以看出Adadelta已经不用依赖于全局学习率了。

特点:

  • 训练初中期,加速效果不错,很快
  • 训练后期,反复在局部最小值附近抖动

RMSprop

RMSprop可以算作Adadelta的一个特例:

当$\rho=0.5$时,$E|g^2|t=\rho*E|g^2|{t-1}+(1-\rho)*g_t^2$就变为了求梯度平方和的平均数。

如果再求根的话,就变成了RMS(均方根):

$$RMS|g|_t=\sqrt{E|g^2|_t+\epsilon}$$

此时,这个RMS就可以作为学习率\eta的一个约束:

$$\Delta{x_t}=-\frac{\eta}{RMS|g|_t}*g_t$$

特点:

  • 其实RMSprop依然依赖于全局学习率
  • RMSprop算是Adagrad的一种发展,和Adadelta的变体,效果趋于二者之间
  • 适合处理非平稳目标 - 对于RNN效果很好

Adam

Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。公式如下:

$$m_t=\mum_{t-1}+(1-\mu)g_t$$
$$n_t=\nun_{t-1}+(1-\nu)g_t^2$$

$$\hat{n_t}=\frac{n_t}{1-\nu^t}$$

其中,$m_t$,$n_t$分别是对梯度的一阶矩估计和二阶矩估计,可以看作对期望$E|g_t|$,$E|g_t^2|$的估计;$\hat{m_t}$,$\hat{n_t}$是对$m_t$,$n_t$的校正,这样可以近似为对期望的无偏估计。 可以看出,直接对梯度的矩估计对内存没有额外的要求,而且可以根据梯度进行动态调整,而$-\frac{\hat{m_t}}{\sqrt{\hat{n_t}}+\epsilon}$对学习率形成一个动态约束,而且有明确的范围。

特点:

  • 结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点
  • 对内存需求较小
  • 为不同的参数计算不同的自适应学习率
  • 也适用于大多非凸优化 - 适用于大数据集和高维空间

Batchnorm

参见 https://zhuanlan.zhihu.com/p/34879333

LayerNorm

FastText

fastText是一种文本分类器。其特点就是快。相对于其他文本分类器,fastText在保持分类效果的同时,缩短了训练时间。适合于大型数据+高效的训练速度、支持多语言表达、专注于文本分类

FastText的原理

fastText 方法包含三部分:模型架构、层次 Softmax 和 N-gram 特征。

fastText 模型输入一个词的序列(一段文本或者一句话),输出这个词序列属于不同类别的概率。 在序列中的词和词组构成特征向量,特征向量通过线性变换映射到中间层,再由中间层映射到标签。fastText在预测标签时使用了非线性激活函数,但在中间层不使用非线性激活函数。

第一部分:fastText的模型架构类似于CBOW,两种模型都是基于Hierarchical Softmax,都是三层架构:输入层、 隐藏层、输出层。

第二部分:层次之间的映射

第三部分:fastText的N-gram特征

FastText词向量与word2vec对比

FastText= word2vec中 cbow + h-softmax的灵活使用
灵活体现在两个方面:

模型的输出层:word2vec的输出层,对应的是每一个term,计算某term的概率最大;而fasttext的输出层对应的是
分类的label。不过不管输出层对应的是什么内容,起对应的vector都不会被保留和使用;
模型的输入层:word2vec的输入层,是 context window 内的term;而fasttext 对应的整个sentence的内容,包括term,也包括 n-gram的内容;
两者本质的不同,体现在 h-softmax的使用。
Word2vec的目的是得到词向量,该词向量 最终是在输入层得到,输出层对应的 h-softmax 也会生成一系列的向量,但最终都被抛弃,不会使用。
fasttext则充分利用了h-softmax的分类功能,遍历分类树的所有叶节点,找到概率最大的label(一个或者N个)。

算法实现

使用facebook开源的实现:https://github.com/facebookresearch/fastText#building-fasttext-for-python

应用

https://blog.csdn.net/qq_16633405/article/details/80578431

https://www.cnblogs.com/DjangoBlog/p/7903690.html

https://jepsonwong.github.io/2018/05/02/fastText/

https://www.cnblogs.com/lliuye/p/9354972.html

https://blog.csdn.net/u010089444/article/details/52563514

https://blog.csdn.net/suixinsuiyuan33/article/details/69229605