meta分析模板(Meta最新模型LLaMA细节与代码详解)
0. 简介
今天介绍的内容是Facebook Meta AI最新提出的语言模型LLaMA ,该模型声称以更小的体积 ,在多数任务上超越了GPT-3的性能 。
模型相关项目已经开源:
https://github.com/facebookresearch/llama论文地址:https://scontent-tpe1-1.xx.fbcdn.net/v/t39.8562-6/333078981_693988129081760_4712707815225756708_n.pdf?_nc_cat=108&ccb=1-7&_nc_sid=ad8a9d&_nc_ohc=ov6yTHfLfNQAX-guxqd&_nc_ht=scontent-tpe1-1.xx&oh=00_AfDMyTEYewg-cHT9_4_sUaW5h0gqrqwjcNMylD9HtVFCWA&oe=6401C9E2
由于模型较大,目前的设备暂时没有办法支持进一步的实验 ,但是其模型代码已经开源 ,所以可以先通过代码了解一下模型结构上的一些细节 ,今天就针对github上放出的代码 ,了解一下模型的细节 。
此外 ,该模型其实就是transformer做了一点细节上的改进 ,真正更有价值的工作应该在数据和训练方面。通过阅读代码 ,可以对transformer的基础构造进行复习 ,并且了解大模型如何在多卡上分布推理 。
由于该项目源码几乎没有注释 ,这就肯定会给很多同学阅读时带来困扰,所以本文顺带着就把代码部分详细的介绍一下 。1. 项目环境依赖
此项目给出的环境依赖只有4个:
torch fairscale fire sentencepiece其中torch不比多讲 ,fairscale是用来做GPU分布的 ,一般是当使用DDP仍然遇到超显存的问题时使用fairscale 。目前fairscale我还没有试过,在下文的源码介绍中 ,我会用torch中对应的基础网络替代fairscale中的结构层进行介绍 。fire是一个命令行工具 ,用或者不用他都可以,sentencepiece是用于tokenizer的工具包 ,会在tokenizer部分简单介绍 。
2. 模型细节
由于该模型就是用的transformer的decoder ,所以在结构上它与GPT是非常类似的 ,只是有一些细节需要注意一下 。
2.1 RMS Pre-Norm
关于Pre-Norm和Post-Norm是神经网络中老生常谈的话题 ,目前比较普遍的被大家接受的结论是 ,相同的深度条件下 ,Post-Norm的效果要优于Pre-Norm ,因为Pre-Norm实际上相当于通过了一个更宽的网络而非更深的网络 ,所以在同等深度下 ,Pre-Norm的实际效果相当于一个更浅却更宽的网络,详细的推理过程参考:https://spaces.ac.cn/archives/9009 。
然而在LLaMA中却采用了Pre-Norm ,或许是因为模型够深(7B ,13B,30B ,65B的模型 ,transformer layer数量分别为32,40 ,60 ,80) ,而Pre-Norm的恒等分支更加明显 ,有利于梯度的传播(这部分暂时没有想到很合理的解释 ,如果有更好的理解 ,欢迎在评论区补充) 。
RMS Norm(Root Mean Square Layer Normalization) ,是一般LayerNorm的一种变体 ,可以在梯度下降时令损失更加平滑 。
与layerNorm相比 ,RMS Norm的主要区别在于去掉了减去均值的部分(re-centering),只保留方差部分(re-scaling) ,从归一化的表达式上可以直观地看出:
一般的LN:a
‾
i
=
a
i
−
μ
σ
g
i
\overline{a}_i = \frac {a_i- \mu} \sigma g_i
ai=σai−μgi 其中 ,μ
=
1
n
∑
i
=
1
n
a
i
\mu = \frac 1 n \sum_{i=1}^na_i
μ=n1i=1∑naiσ
=
1
n
∑
i
=
1
n
(
a
i
−
μ
)
2
\sigma= \sqrt {\frac 1 n \sum_{i=1}^n{{(a_i-\mu)}^2}}
σ=n1i=1∑n(ai−μ)2RMS Norm:
a
‾
i
=
a
i
R
M
S
(
a
)
g
i
\overline{a}_i = \frac {a_i} {RMS(a)} g_i
ai=RMS(a)aigi其中,
R
M
S
(
a
)
=
1
n
∑
i
=
1
n
a
i
2
{RMS(a)}=\sqrt {\frac 1 n \sum_{i=1}^n{{a_i}^2}}
RMS(a)=n1i=1∑nai2可以看到 ,二者的区别就在于有没有减去均值。至于RMS Norm为什么有用 ,需要求梯度进行分析,感兴趣的同学可以阅读RMS Norm的论文 。
2.2 SwiGLU激活函数
LLaMA采用SwiGLU替换了原有的ReLU 。
采用SwiGLU的FNN ,在论文中以如下公式进行表述:
F
F
N
s
w
i
G
L
U
(
x
,
W
,
V
,
W
2
)
=
(
S
w
i
s
h
1
(
x
W
)
⊗
x
V
)
W
2
FFN_{swiGLU}(x, W, V, W_2) = (Swish_1(xW)\otimes xV)W_2
FFNswiGLU(x,W,V,W2)=(Swish1(xW)⊗xV)W2其中 ,
S
w
i
s
h
β
(
x
)
=
x
σ
(
β
x
)
Swish_\beta(x) = x\sigma(\beta x)
Swishβ(x)=xσ(βx), (Ramachandran et al., 2017.)2.3 RoPE旋转位置编码
RoPE(Rotary Position Embedding)旋转位置编码 ,是苏剑林老师提出的一种旋转位置编码方法 ,其思想是采用绝对位置编码的形式 ,实现相对位置编码。这一部分比较关键 ,如果不理解的话 ,后边的代码估计就看不懂了 。读懂RoPE涉及一点复变函数的基础知识 ,不过如果你没有学过的话也没有关系 。
位置编码对大模型而言尤为重要 ,因为既然是要训练大模型,那么长文本的表征和模型对于长文本的建模能力就显得非常重要。(但是对于绝对位置编码 ,我有一个直观地感受 ,认为其本质上不适用于长文本的场景,因为它会直接导致模型的Embedding层被无限放大 ,并且由于数据分布在seq_len方向上通常是长尾的 ,这又会必然导致绝对位置编码的矩阵在尾部会越来越稀疏,一方面造成资源浪费 ,另一方面这种表示方法直观上就很不利于模型的学习 ,因为它与我们实际场景是有很大的矛盾的 。而RoPE虽然具有相对位置编码的性质 ,但是从代码部分可以看出 ,在构造的时候 ,其也是受到了最大长度的限制的 。关于这一点 ,我无法严谨得说明 ,只是一点个人的想法 。) 。
而RoPE的巧妙之处在于 ,它既保留了绝对位置编码中的绝对位置信息 ,又保留了在内积运算下,对位置信息的相对性 。
RoPE主要借助了复数的思想 。为了引入复数 ,首先假设了在加入位置信息之前 ,原有的编码向量是二维行向量
q
m
q_m
qm和k
n
k_n
kn,其中m
m
m和n
n
n是绝对位置 ,现在需要构造一个变换 ,将m
m
m和n
n
n引入到q
m
q_m
qm和k
n
k_n
kn中,即寻找变换:q
m
~
=
f
(
q
,
m
)
,
k
n
~
=
f
(
k
,
n
)
\tilde {q_m} = f(q, m), \tilde{k_n} = f(k, n)
qm~=f(q,m),kn~=f(k,n)考虑到Attention的核心计算是内积:
A
t
t
e
n
t
i
o
n
(
Q
,
K
,
V
)
=
s
o
f
t
m
a
x
(
Q
K
T
d
k
)
V
Attention(Q, K,V) = softmax(\frac {QK^T} {\sqrt{d_k}})V
Attention(Q,K,V)=softmax(dkQKT)V所以 ,寻求的这个
f
(
∗
)
f(*)
f(∗)变换 ,应该具有特性:⟨
f
(
q
,
m
)
,
f
(
k
,
n
)
⟩
=
g
(
q
,
k
,
m
−
n
)
\langle f(q, m), f(k, n) \rangle = g(q, k, m-n)
⟨f(q,m),f(k,n)⟩=g(q,k,m−n)这里直接说结论 ,寻求的变换就是
q
m
e
i
m
θ
q_me^{im\theta}
qmeimθ ,也就是给q
m
q_m
qm乘以e
i
m
θ
e^{im\theta}
eimθ ,相应地 ,k
n
k_n
kn乘以e
i
n
θ
e^{in\theta}
einθ 。具体的求解过程 ,请参考苏剑林老师的博客 。
做了这样一个变换之后 ,根据复数的特性 ,有:
⟨
q
m
,
k
n
⟩
=
R
e
[
q
m
k
n
∗
]
\langle q_m, k_n \rangle = Re[q_mk^*_n]
⟨qm,kn⟩=Re[qmkn∗]也就是,如果把二维向量看做复数 ,那么它们的内积 ,等于一个复数乘以另一个复数的共轭,得到的结果再取实部 。
带入上面的变换 ,也就有:
⟨
q
m
e
i
m
θ
,
k
n
e
i
n
θ
⟩
=
R
e
[
(
q
m
e
i
m
θ
)
(
k
n
e
i
n
θ
)
∗
]
=
R
e
[
q
m
k
n
∗
e
i
(
m
−
n
)
θ
]
\langle q_me^{im\theta}, k_ne^{in\theta} \rangle = Re[(q_me^{im\theta}) (k_ne^{in\theta})^*] =Re[q_mk_n^*e^{i(m-n)\theta}]
⟨qmeimθ,kneinθ⟩=Re[(qmeimθ)(kneinθ)∗]=Re[qmkn∗ei(m−n)θ]这样一来 ,内积的结果就只依赖于
(
m
−
n
)
(m-n)
(m−n),也就是相对位置了。换言之 ,经过这样一番操作 ,通过给Embedding添加绝对位置信息 ,可以使得两个token的编码 ,经过内积变换(self-attn)之后 ,得到结果 ,是受它们位置的差值 ,即相对位置影响的 。于是对于任意的位置为
m
m
m的二维向量[
x
,
y
]
[x, y]
[x,y] ,把它看做复数 ,乘以e
i
m
θ
e^{im\theta}
eimθ,而根据欧拉公式 ,有:e
i
m
θ
=
cos
m
θ
+
i
sin
m
θ
e^{im\theta}=\cos{m\theta}+i\sin{m\theta}
eimθ=cosmθ+isinmθ于是上述的相乘变换也就变成了:
(
x
+
i
y
)
e
i
m
θ
=
(
x
cos
m
θ
−
y
sin
m
θ
)
+
i
(
x
sin
m
θ
+
y
cos
m
θ
)
(x+iy)e^{im\theta}=(x\cos{m\theta}-y\sin{m\theta})+i(x\sin{m\theta}+y\cos{m\theta})
(x+iy)eimθ=(xcosmθ−ysinmθ)+i(xsinmθ+ycosmθ)把上述式子写成矩阵形式:
f
(
(
q
,
%2
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!