赞
踩
最近在公众号中了解到了刘知远团队退出的视频课程《大模型交叉研讨课》,看了目录觉得不错,因此拜读一下。
观看地址: https://www.bilibili.com/video/BV1UG411p7zv
目录:
人工神经网络(Artificial Neural Network)受到生物中的神经网络所启发。
受到生物里面的神经网络和生物神经元的启发,人们设计出由计算机能够计算的人工神经元,这里简称为神经元(Neuron)。
一个神经元可以有
n
n
n个输入和一个输出,同时包含了参数
w
w
w和
b
b
b。
经过线性运算
w
T
x
w^Tx
wTx加上偏置
b
b
b后经过激活函数,得到神经元的输出。
我们上面看到的是单个神经元,我们利用很多单个的神经元就可以构成一个只有一层的神经网络。
上图中有3个神经元,它们经过矩阵运算并行计算,可以一次计算出这三个神经元的输出。
我们得到了单层神经网络之后,就可以在后面继续叠加类似的层,形成多层神经网络(Multilayer Neural Network)。
通常我们从输入开始依次计算每层的结果,这就是所谓的前向计算(Feedforward Computation)。
我们通常将输入层之上添加的多层网络称之为隐藏层(Hidden Layer)。
这里有三个隐藏层,每层的输入由上层的输入经由线性变换和激活函数得到的。
这里的激活函数一般都是非线性的,那么为什么要使用非线性的激活函数?
如果没有非线性激活函数,那么每层相当于对上一层的输出进行了一次线性变换,
这里假设有两层没有线性激活函数的网络,把第一层的计算结果
h
1
h_1
h1代入到
h
2
h_2
h2的计算公式中之后,我们可以发现,实际上就可以转换为单层神经网络的线性变换。
同时,有了非线性激活函数,可以大大增强网络的表达能力,可以拟合更复杂的函数。
常用的激活函数有:
我们知道,可以通过叠加更多的隐藏层来提升网络的表达能力,那我们如何得到想要的输出结果呢?
此时,我们就需要增加网络的输出层。
输出层根据需要,它也有不同的形态。
对于这种只有一个输出的输出层,对应了两种情况。
如果有多个输出,那么可以看成是多分类问题,每个输出值代表对应类别的得分,那么经过Softmax函数后,就可以转换为概率:
y
i
=
softmax
(
z
i
)
=
exp
(
z
i
)
∑
j
exp
(
z
j
)
z
=
W
h
+
b
y_i = \text{softmax}(z_i) = \frac{\exp (z_i)}{\sum_j \exp (z_j)} \\ z = Wh + b
yi=softmax(zi)=∑jexp(zj)exp(zi)z=Wh+b
这里
z
i
z_i
zi对应其中第
i
i
i个输出。
想要训练一个神经网络,我们首先需要设定一个训练目标(Objective)。
然后根据该训练目标对网络进行调整。
这里举个例子,假设有
N
N
N条训练数据,它的输入是某个电脑
x
i
x_i
xi的一些属性,希望模型能预测出电脑的假设
y
i
y_i
yi。这显然是一个回归问题。
我们希望训练一个神经网络
F
θ
F_\theta
Fθ,根据输入样本的属性来预测电脑价格。此时,合理的训练目标就是均方误差(Mean Squared Error):
min
θ
J
(
θ
)
=
min
θ
1
N
∑
i
=
1
N
(
y
i
−
F
θ
(
x
i
)
)
2
\min_\theta J(\theta) = \min_\theta \frac{1}{N} \sum_{i=1}^N \left(y_i - F_\theta(x_i) \right)^2
θminJ(θ)=θminN1i=1∑N(yi−Fθ(xi))2
其中
θ
\theta
θ是神经网络
F
θ
F_\theta
Fθ的参数。
这里误差(loss)越小,说明模型预测的越准确,因此我们要最小化这个目标函数。
我们来看另外一种情况。
给定
N
N
N段文本,我们希望模型去预测情感标签:正面、中性、负面。此时,合理的训练目标是交叉熵(Cross Entropy):
min
θ
J
(
θ
)
=
min
θ
−
1
N
∑
i
=
1
N
log
P
m
o
d
e
l
(
F
θ
(
x
i
)
=
y
i
)
\min_\theta J(\theta) = \min_\theta - \frac{1}{N} \sum_{i=1}^N \log P_{model}(F_\theta(x_i) = y_i)
θminJ(θ)=θmin−N1i=1∑NlogPmodel(Fθ(xi)=yi)
我们知道为了达到训练目标,我们需要最小化某一个损失函数。
那么如何最小化一个损失函数呢?
在神经网络中我们通常会采用随机梯度下降法(Stochastic Gradient Descent)。
我们希望把整个优化步骤拆分成若干个子步骤,在每个子步骤中我们缩小一点损失函数。那么要如何调整参数,才能做到每一步能缩小一点损失函数呢。
首先要计算损失函数对于模型参数的梯度,这个梯度代表了对这个参数进行单位大小的改动下,损失函数它变化最快的一个方向。顺着这个方向向前走一步,就可以降低一点点损失函数值。
给定一个有
n
n
n个输入
1
1
1个输出的函数
F
F
F,我们可以通过偏导数来计算对向量
x
x
x的梯度。
给定一个有
n
n
n个输入
m
m
m个输出的函数
F
F
F,我们可以把它拆分成具有
m
m
m个单个输出的函数
[
F
1
,
F
2
,
⋯
,
F
m
]
[F_1,F_2,\cdots,F_m]
[F1,F2,⋯,Fm]。
然后让这
m
m
m个函数对这
n
n
n个输入去求偏导数,得到一个
m
×
n
m \times n
m×n的矩阵,称为Jacobian矩阵。
如果我们按行来看这个矩阵的话,就是一个函数对应每个输入的偏导数。
在求微分时会用到链式法则,这里所有的函数都是一元函数,要求
z
z
z对
x
x
x的导数,我们先求
z
z
z对
y
y
y的导数,乘以
y
y
y对
x
x
x的导数即可。
推广到多输入多输出的函数情形。
假设我们要计算
h
h
h对
x
x
x的微分,那么我们也可以先计算
h
h
h对
z
z
z的微分,然后计算
z
z
z对
x
x
x的矩阵。这里这两者都可以表示成Jacobian矩阵,然后把这两个矩阵进行相乘,就得到了我们想要的结果。
如果回到神经网络,假设 s = u T h s=u^Th s=uTh是损失函数得到的结果, h = f ( z ) , z = W x + b h=f(z), z=Wx+b h=f(z),z=Wx+b中 z z z是线性变换, f f f是非线性激活函数。那么如果计算 s s s对参数 b b b的导数?
利用链式法则,我们可以列出需要计算的微分式子。然后分别求解即可。
在实际深度学习中,是通过反向传播算法去计算对每个参数的梯度。
这里不得不提到计算图这个工具,在计算图中第一个节点代表网络的输入
x
x
x,其他的节点代表计算操作。图中的有向边代表操作的操作数,同时也可以表示传递计算值的过程。
这里由于在博主的另外一篇博客自动求导神器计算图中有详细的探讨,因此就跳过了。
这里介绍一个简单的神经网络的例子,Word2Vec,它利用非常简单的神经网络进行训练,就可以得到词向量,即词的低维表示。
Word2Vec利用两类架构来产生分布式的词表示:
在每个窗口中,中间的单词是目标词,其他的单词是上下文词。
一个滑动窗口的例子:
这里窗口大小为5,蓝色背景的单词代表目标词,注意边界情况。
在CBOW架构中,模型给定周围的上下文词来预测目标词。
根据词袋假设:上下文词的顺序不影响预测。
假设窗口大小为5,我们要预测never too __ to learn中间的词。
这里只画出了两个上下文词,首先将上下文词表示为one-hot向量,然后在要训练的词向量矩阵中得到这两个词的词向量,然后取这两个词向量的均值,得到一个向量。为了能计算词表中每个单词作为目标词的概率,我们需要输出层的大小和词表大小一致。那么,经过Softmax就可以得到词表中单词的概率分布。因此,经过求均值得到的向量需要进行一个线性变换,转换为词表大小的维度。
而在Skip-Gram中,通过目标词来预测上下文词。
同样,假设窗口大小为5,如果Never是目标词,那么模型需要预测too和late是上下文词;如果too是目标词,那么模型需要预测Never,late和to是上下文词。
但让神经网络来预测出多个输出是不容易的,所以我们把这个任务进行分解,让它分别预测每个上下文词的概率:
如果Never是目标词,那么先让模型学习输出too,再让模型学习去输出late。
在预测是,看起来和CBOW很类似,除了这里的输入只有一个目标词,输出一次只是一个上下文词。
在这两种架构中,我们都默认去对整个词表进行Softmax,这里可能出现性能问题。
如果词表非常大,那么对它进行Softmax需要非常大的计算量。
因此我们需要提升计算效率。
这里有两种提升Word2vec计算效率的技术:
这里只介绍负采样。
在这种技术中,我们不对整个词表进行Softmax。简单来说是把多分类问题变成二分类问题。
即将上下文词作为正例,同时采样一小部分非上下文词作为负例。现在只需要判断某个词是正例还是负例即可。
这里在进行负采样时也有要注意的地方,即为了让稀有词被采样的概率增大,通过下面的公式来计算最终单词被采样的概率:
f
(
w
i
)
f(w_i)
f(wi)是单词
w
i
w_i
wi的频次。
有了负采样之后,我们不需要再计算整个词表的Softmax概率。这里假设只采样了4个负例,那么现在只需要拿出4个负例和1个正例对应的得分计算Softmax。
这样计算量会大大降低,假设词向量维度是300,词表中词个数为10000。那么原本要对这10000个词进行Softmax,然后在反向传播时,需要对整个
300
×
10000
300 \times 10000
300×10000的矩阵进行梯度计算。那么计算量非常大。
我们使用负采样之后,假设只采4个负例,那么在Softmax的时候只有5个值,我们只需要更新
300
×
5
300 \times 5
300×5的权重矩阵,计算量只有原来的
0.05
%
0.05\%
0.05%。
Word2vec还有其他学习技巧,一个是Sub-Sampling。它为了平滑常见词和罕见次。罕见词出现的概率虽然较低,但可能包含丰富的语义信息。而常见词(像“的”、“呢”)出现的概率较高,可能含有的意义就没那么重要。
Sub-Sampling的方法是在训练过程中去掉一些词,具体去掉的概率是根据上面的公式进行计算。
还有一种方法是非固定的滑动窗口,称为软滑动窗口。它认为离目标词越近的上下文词越重要(或者说相关)。
它不对窗口大小进行固定,窗口大小是通过这样一种方法来采样得到的。
首先定义滑动窗口大小的最大值
S
m
a
x
S_{max}
Smax,在生成训练数据时从
1
1
1到
S
m
a
x
S_{max}
Smax之间去采样一个值。让这个采样的值作为这一次训练的滑动窗口大小。
这样,通过变化的滑动窗口大小,离目标词更近的上下文词,有更大的机会被采样成上下文词。
循环神经网络(RNN)的关键点在于处理序列数据时会有一个顺序的记忆。所谓顺序记忆就是我们看到熟悉顺序的内容时能更顺畅的阅读,比较浅显的例子是阅读中文文章和英文文章的体验。虽然有时你发现英文单词都认识,但是读起来就是很吃力。
RNN通过递归更新顺序记忆来为序列数据建模。
这是一个典型的RNN例子。
这里画出了输入序列长度为3,对应会生成三个隐藏状态,和三个输出。
那给定某时刻的输入和上一时刻的隐藏状态,它是如何计算隐藏状态和输出的呢?
根据上图中的公式。
RNN能处理时间序列的这种性质,特别适用于NLP任务,尤其是作为语言模型。
上图是一个例子,首先在时刻1有输入
w
1
w_1
w1,它是一个one-hot向量,那么经过词嵌入矩阵得到对应的词嵌入向量
x
1
x_1
x1,作为RNN的输入。
然后根据
x
1
x_1
x1和上一时刻的隐藏状态
h
0
h_0
h0来计算当前时刻的隐藏状态,由于这里是第一个时刻,因此
h
0
h_0
h0需要我们自己先初始化,一般初始化为零向量。
以此类推,将"never too late to"依次输入到RNN网络中,我们知道语言模型需要预测下一个单词是什么。
这时候,我们需要用到最后时刻"to"计算出来的隐藏状态
h
4
h_4
h4,因为该隐藏状态包含了前面四个词的所有信息。同理,我们这里也会经过线性层,把它的维度转换为词表大小,然后经过Softmax计算词表中每个单词作为下一个词的概率。
从图中也可以发现,每个时刻的 W x W_x Wx和 W h W_h Wh都是一样的,因为每个时刻其实都是同一个RNN单元的不断复制,可以很好地实现参数共享,这样有助于模型处理不同长度的样本,也助于模型更好地学习,节省参数量。
RNN的应用场景也很多:
综上RNN的优点如下:
但也有一些缺点:
随着序列的变长,根据链式法则,在求解某个权重时会产生很长的计算公式。
具体是梯度消失还是梯度爆炸会处决于每个式子中的
∂
h
n
∂
h
n
−
1
\frac{ \partial h_n}{\partial h_{n-1}}
∂hn−1∂hn的结果。如果该结果每次都大于1,会导致梯度呈指数型增大,从而形成梯度爆炸;反之若小于1,那么也会导致梯度呈指数型衰减,从而形成梯度消失。
有几个RNN的变体专门来解决这个问题,比较有名的变体是GRU和LSTM。
普通的RNN在计算下一时刻隐藏状态时,通过
h
i
=
tanh
(
W
x
x
i
+
W
h
h
i
−
1
+
b
)
h_i = \tanh(W_xx_i + W_hh_{i-1}+ b)
hi=tanh(Wxxi+Whhi−1+b)
GRU在此基础上引入了门控机制,来控制哪些信息可以传到下一时刻。
GRU有两个门,分别是更新门
z
i
=
σ
(
W
x
(
z
)
x
i
+
W
h
(
z
)
h
i
−
1
+
b
(
z
)
)
z_i = \sigma(W_x^{(z)} x_i + W_h^{(z)} h_{i-1} + b^{(z)})
zi=σ(Wx(z)xi+Wh(z)hi−1+b(z))
和重置门
r
i
=
σ
(
W
x
(
r
)
x
i
+
W
h
(
r
)
h
i
−
1
+
b
(
r
)
)
r_i = \sigma(W_x^{(r)}x_i + W_h^{(r)}h_{i-1} + b^{(r)})
ri=σ(Wx(r)xi+Wh(r)hi−1+b(r))
它们的作用就是权衡过去的信息和当前输入信息在计算新的隐藏状态上的比重。
新隐藏状态
h
i
h_i
hi计算为:
h
i
=
z
i
⊙
h
i
−
1
+
(
1
−
z
i
)
⊙
h
~
i
h_i = z_i \odot h_{i-1} + (1-z_i) \odot \tilde h_i
hi=zi⊙hi−1+(1−zi)⊙h~i
这里
⊙
\odot
⊙代表按元素相乘,
h
~
i
\tilde h_i
h~i是候选隐藏状态,它的计算公式类似于RNN中的隐藏状态,代表当前时刻的信息。
可以看到,这里更新门控制了当前隐藏状态
h
i
h_i
hi中有多少来自前一隐藏状态
h
i
−
1
h_{i-1}
hi−1,如果来自前一隐藏状态的信息越多,那么来自候选隐藏状态的信息就越少。
我们来看下候选隐藏状态是如何计算的:
h
~
i
=
tanh
(
W
x
x
i
+
r
i
⊙
W
h
h
i
−
1
+
b
)
\tilde h_i = \tanh(W_x x_i + r_i \odot W_h h_{i-1} + b)
h~i=tanh(Wxxi+ri⊙Whhi−1+b)
如果上式中没有
r
i
r_i
ri,那么就完全等同于普通RNN中隐藏状态的计算,那么这里的
r
i
r_i
ri是控制是否会遗忘过去的隐藏状态(历史信息)。
对于重置门来说。
如果过重置门
r
i
r_i
ri接近于0,那么相当于
h
~
i
=
tanh
(
W
x
x
i
+
b
)
\tilde h_i = \tanh(W_x x_i + b)
h~i=tanh(Wxxi+b)
即忽略了前一时刻隐藏状态,表示当前激活值与过去无关。
对于更新门来说,它控制了过去的状态与当前激活相比之下有多大的关系。
当
z
i
z_i
zi接近于1时,那么
h
i
=
h
i
−
1
h_i=h_{i-1}
hi=hi−1,即完全保留了上一时刻的信息。
当 z i z_i zi接近于0时,那么 h i = h ~ i h_i = \tilde h_i hi=h~i,即完全丢弃了上一时刻的信息。
相比于GRU只有两个门,LSTM有三个门,它更加复杂一点。
LSTM也是RNN的变体,它比GRU多了一个门。LSTM关键之处在于它增加了一个新的状态
C
t
C_t
Ct(单元状态)。
它的作用有:
具体地,LSTM也是通过一系列门进行操作的。我们分别来看下。
首先是遗忘门,决定上一个状态有哪些信息可以从单元状态中移除。
这里
[
h
t
−
1
,
x
t
]
[h_{t-1},x_t]
[ht−1,xt]是上一时刻隐藏状态和当前时刻输入的拼接,代表同时考虑了这两者。
如果
f
t
=
0
f_t=0
ft=0,则表示直接丢弃信息。
下面来看输入门。
以及候选单元状态
C
~
t
\tilde C_t
C~t。
输入门决定哪些候选单元状态信息可以存储到单元状态。
有了上面计算的结果,就可以来更新单元状态。计算公式如上图。
它通过遗忘门来控制遗忘多少旧(上一时刻)单元状态信息,以及保留多少新的候选单元状态信息。
有了当前时刻的单元状态,就可以来计算新的隐藏状态。
在计算隐藏状态时,有一个输出门,它控制单元状态中的哪些信息可以输出到隐藏状态。
前面我们介绍的RNN(包括简单RNN、GRU和LSTM)中,只能沿着一个方向从左到右读取输入。但有时候,如果能双向地读取输入,那么问题就解决得更好。比如在命名实体识别任务中。
卷积神经网络(CNN)通常应用于图像中,但也可以应用到NLP领域内。比如ELMo中就应用到了CNN。
CNN擅长于提取局部和位置不变的模式。在NLP中,可能是一些短语或局部语法结构。
CNN提取模式通过:
CNN在处理NLP时,往往包含了输入层、卷积层、池化层和非线性层。
输入层对输入进行处理,只有也会有一个词向量矩阵,每个单词都能获取一个维度固定的表示。
然后经过不同大小的卷积层对词向量进行卷积。
接着进入池化层,对特征进一步提取,一般选择最大池化。
最后进入非线性层得到非线性输出,根据任务不同。会进一步对得到的特征向量进行处理,可能经过全连接层进行维度缩放。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。