上海网站建设收费标准,微信app下载安装官方版2021,建设三轮摩托车官方网站,南昌seo顾问2023年的深度学习入门指南(20) - LLaMA 2模型解析
上一节我们把LLaMA 2的生成过程以及封装的过程的代码简单介绍了下。还差LLaMA 2的模型部分没有介绍。这一节我们就来介绍下LLaMA 2的模型部分。 这一部分需要一些深度神经网络的基础知识#xff0c;不懂的话不用着急#xf…2023年的深度学习入门指南(20) - LLaMA 2模型解析
上一节我们把LLaMA 2的生成过程以及封装的过程的代码简单介绍了下。还差LLaMA 2的模型部分没有介绍。这一节我们就来介绍下LLaMA 2的模型部分。 这一部分需要一些深度神经网络的基础知识不懂的话不用着急后面的文章我们都会介绍到。
均平方根标准化
RMSNorm是一种改进的LayerNorm技术LayerNorm是Layer normalization意思是层归一化。。层归一化用于帮助稳定训练并促进模型收敛因为它具备处理输入和权重矩阵的重新居中和重新缩放的能力。
RMSNorm是2019年的论文《Root Mean Square Layer Normalization》中提出的。它假设LayerNorm中的重新居中性质并不是必需的于是RMSNorm根据均方根RMS对某一层中的神经元输入进行规范化赋予模型重新缩放的不变性属性和隐式学习率自适应能力。相比LayerNormRMSNorm在计算上更简单因此更加高效。
理解了之后我们来看下RMSNorm的代码实现。我把注释直接写在代码里。
class RMSNorm(torch.nn.Module):# 类的构造函数它接受两个参数dim和eps。dim是希望标准化的特征维度eps是一个非常小的数用于防止除以零的错误。def __init__(self, dim: int, eps: float 1e-6):super().__init__()# 设置类的属性。eps是构造函数传入的参数。self.eps eps# weight是一个可学习的参数它是一个由1填充的张量尺寸为dim。self.weight nn.Parameter(torch.ones(dim))def _norm(self, x):# 首先对输入x求平方并计算最后一个维度的平均值然后加上一个非常小的数self.eps防止出现零接着对结果开平方根并求倒数最后将结果与原始输入x相乘。return x * torch.rsqrt(x.pow(2).mean(-1, keepdimTrue) self.eps)def forward(self, x):# 首先它将输入x转化为浮点数并进行标准化然后将标准化的结果转化回x的类型。最后将结果与权重self.weight相乘得到最终的输出。output self._norm(x.float()).type_as(x)return output * self.weight位置编码
我们复习下第3讲曾经介绍过的Transformer结构。 位置编码是Transformer中的一个重要组成部分它的作用是为输入序列中的每个位置提供一个位置向量以便Transformer能够区分不同位置的单词。
Transformer中的位置编码是通过将正弦和余弦函数的值作为位置向量的元素来实现的。这些函数的周期是不同的因此它们的值在不同的位置是不同的。这样Transformer就可以通过位置编码来区分不同位置的单词。
LLaMA并没有使用正弦函数。
# dim是特征的维度end应该是预计算的时序位置的数量theta是一个常数用于调整频率的尺度
def precompute_freqs_cis(dim: int, end: int, theta: float 10000.0):# 首先生成一个从0到dim的步长为2的整数序列然后取前dim // 2个元素将这些元素转换为浮点类型然后除以dim# 得到的结果再次被用作theta的指数最后取其倒数得到一组频率freqs 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))# 生成一个从0到end的整数序列这个序列在同一个设备上创建这个设备是freqs的设备t torch.arange(end, devicefreqs.device) # type: ignore# 计算t和freqs的外积然后将结果转换为浮点类型freqs torch.outer(t, freqs).float() # type: ignore# 将freqs从直角坐标系转换为极坐标系# torch.polar(r, theta)的功能是根据极径r和极角theta生成复数这里的r是freqs的形状的全1张量theta则是freqs。freqs_cis torch.polar(torch.ones_like(freqs), freqs) # complex64return freqs_cis将位置坐标计算出来之后我们还需要将其变型成与输入的形状一致。 我们来看下是如何实现的
def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):# 获取输入张量x的维度数ndim x.ndimassert 0 1 ndimassert freqs_cis.shape (x.shape[1], x.shape[-1])# 对于每个维度如果它是第二个维度或最后一个维度则保留原来的大小否则将其设置为 1shape [d if i 1 or i ndim - 1 else 1 for i, d in enumerate(x.shape)]# 将freqs_cis调整为shape指定的形状并返回结果return freqs_cis.view(*shape)然后将矩阵和位置编码相乘起来
def apply_rotary_emb(xq: torch.Tensor,xk: torch.Tensor,freqs_cis: torch.Tensor,
) - Tuple[torch.Tensor, torch.Tensor]:# 将 xq 和 xk 转换为复数张量并将它们的形状调整为最后一个维度为 2 的形状xq_ torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))xk_ torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))# 调用刚刚讲过的 reshape_for_broadcast 函数来将 freqs_cis 调整为与 xq_ 兼容的形状freqs_cis reshape_for_broadcast(freqs_cis, xq_)# 将xq_和xk_与freqs_cis进行逐元素的复数乘法然后将得到的结果视为实数最后将最后两个维度合并为一个维度xq_out torch.view_as_real(xq_ * freqs_cis).flatten(3)xk_out torch.view_as_real(xk_ * freqs_cis).flatten(3)# 使用 type_as 方法将其转换回与输入相同的数据类型return xq_out.type_as(xq), xk_out.type_as(xk)LLaMA的注意力机制
解释下面代码
class Attention(nn.Module):def __init__(self, args: ModelArgs):super().__init__()self.n_kv_heads args.n_heads if args.n_kv_heads is None else args.n_kv_headsmodel_parallel_size fs_init.get_model_parallel_world_size()self.n_local_heads args.n_heads // model_parallel_sizeself.n_local_kv_heads self.n_kv_heads // model_parallel_sizeself.n_rep self.n_local_heads // self.n_local_kv_headsself.head_dim args.dim // args.n_headsself.wq ColumnParallelLinear(args.dim,args.n_heads * self.head_dim,biasFalse,gather_outputFalse,init_methodlambda x: x,)self.wk ColumnParallelLinear(args.dim,self.n_kv_heads * self.head_dim,biasFalse,gather_outputFalse,init_methodlambda x: x,)self.wv ColumnParallelLinear(args.dim,self.n_kv_heads * self.head_dim,biasFalse,gather_outputFalse,init_methodlambda x: x,)self.wo RowParallelLinear(args.n_heads * self.head_dim,args.dim,biasFalse,input_is_parallelTrue,init_methodlambda x: x,)self.cache_k torch.zeros((args.max_batch_size,args.max_seq_len,self.n_local_kv_heads,self.head_dim,)).cuda()self.cache_v torch.zeros((args.max_batch_size,args.max_seq_len,self.n_local_kv_heads,self.head_dim,)).cuda()代码虽然多但是都是实现head和q,k,v,很好理解
初始化时定义了n_kv_heads, n_local_heads等表示head数量的变量。wq,wk,wv三个线性层分别用于生成query,key和value。采用ColumnParallelLinear实现分布并行。wo线性层对多头attention的输出做融合,采用RowParallelLinear实现分布并行。cache_k和cache_v用于缓存key和value,加速自注意力的计算。并行线性层的使用以及caching机制,可以加速自注意力在大batch大小场景下的训练和推理。整体设计实现了高效的分布式并行自注意力计算,可以扩展到大规模多GPU/机器环境,处理长序列任务。
然后我们将各注意力的子模块集成起来 def forward(self,x: torch.Tensor,start_pos: int,freqs_cis: torch.Tensor,mask: Optional[torch.Tensor],):bsz, seqlen, _ x.shapexq, xk, xv self.wq(x), self.wk(x), self.wv(x)xq xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)xk xk.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim)xv xv.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim)xq, xk apply_rotary_emb(xq, xk, freqs_cisfreqs_cis)self.cache_k self.cache_k.to(xq)self.cache_v self.cache_v.to(xq)self.cache_k[:bsz, start_pos : start_pos seqlen] xkself.cache_v[:bsz, start_pos : start_pos seqlen] xvkeys self.cache_k[:bsz, : start_pos seqlen]values self.cache_v[:bsz, : start_pos seqlen]# repeat k/v heads if n_kv_heads n_headskeys repeat_kv(keys, self.n_rep) # (bs, seqlen, n_local_heads, head_dim)values repeat_kv(values, self.n_rep) # (bs, seqlen, n_local_heads, head_dim)xq xq.transpose(1, 2) # (bs, n_local_heads, seqlen, head_dim)keys keys.transpose(1, 2)values values.transpose(1, 2)scores torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)if mask is not None:scores scores mask # (bs, n_local_heads, seqlen, cache_len seqlen)scores F.softmax(scores.float(), dim-1).type_as(xq)output torch.matmul(scores, values) # (bs, n_local_heads, seqlen, head_dim)output output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)return self.wo(output)主要流程如下:
计算query、key、value的线性映射表示xq、xk、xv对xq和xk应用位置参数将xk、xv写入cache从cache读取key和value,重复其head维度以匹配query的head数计算query和key的点积获得相关度得分对scores加mask并softmax归一化将scores与value做权重和,得到多头自注意力输出将多头输出拼接并线性映射,即是Self-Attention的结果
其中用到了一个函数repeat_kv它的作用是将key和value的head维度重复n_rep次以匹配query的head数。
解释下面代码
def repeat_kv(x: torch.Tensor, n_rep: int) - torch.Tensor:torch.repeat_interleave(x, dim2, repeatsn_rep)bs, slen, n_kv_heads, head_dim x.shapeif n_rep 1:return xreturn (x[:, :, :, None, :].expand(bs, slen, n_kv_heads, n_rep, head_dim).reshape(bs, slen, n_kv_heads * n_rep, head_dim))repeat_kv函数使用 expand 方法将输入张量在第四个维度上扩展 n_rep 次并使用 reshape 方法将其调整为适当的形状。
LLaMA的Transformer结构
核心的自注意力模块实现了之后我们就可以像搭积木一样将其组装成Transformer结构了。
首先我们看看全连接网络
class FeedForward(nn.Module):def __init__(self,dim: int,hidden_dim: int,multiple_of: int,ffn_dim_multiplier: Optional[float],):super().__init__()hidden_dim int(2 * hidden_dim / 3)# custom dim factor multiplierif ffn_dim_multiplier is not None:hidden_dim int(ffn_dim_multiplier * hidden_dim)hidden_dim multiple_of * ((hidden_dim multiple_of - 1) // multiple_of)self.w1 ColumnParallelLinear(dim, hidden_dim, biasFalse, gather_outputFalse, init_methodlambda x: x)self.w2 RowParallelLinear(hidden_dim, dim, biasFalse, input_is_parallelTrue, init_methodlambda x: x)self.w3 ColumnParallelLinear(dim, hidden_dim, biasFalse, gather_outputFalse, init_methodlambda x: x)def forward(self, x):return self.w2(F.silu(self.w1(x)) * self.w3(x))LLaMA的前馈神经网络主要是立足于并行化。
主要顺序为:
初始化时构建了3个线性层w1,w2,w3。其中w1和w3使用ColumnParallelLinear实现分布式并行,w2使用RowParallelLinear。forward时,先过w1做第一次线性投影,然后使用SiLU激活函数。跟一个w3对原输入做的线性投影加起来,实现残差连接。最后过w2线性层输出。
这样的结构形成了一个带残差连接的两层前馈网络。它结合并行计算和残差连接,使模型对长序列任务拟合效果更佳。
然后我们将前馈全连接网络和之前讲的自注意力机制结合起来构建Transformer块
class TransformerBlock(nn.Module):def __init__(self, layer_id: int, args: ModelArgs):super().__init__()self.n_heads args.n_headsself.dim args.dimself.head_dim args.dim // args.n_headsself.attention Attention(args)self.feed_forward FeedForward(dimargs.dim,hidden_dim4 * args.dim,multiple_ofargs.multiple_of,ffn_dim_multiplierargs.ffn_dim_multiplier,)self.layer_id layer_idself.attention_norm RMSNorm(args.dim, epsargs.norm_eps)self.ffn_norm RMSNorm(args.dim, epsargs.norm_eps)def forward(self,x: torch.Tensor,start_pos: int,freqs_cis: torch.Tensor,mask: Optional[torch.Tensor],):h x self.attention.forward(self.attention_norm(x), start_pos, freqs_cis, mask)out h self.feed_forward.forward(self.ffn_norm(h))return out我们来分块讲解一下。
self.n_heads args.n_heads
self.dim args.dim
self.head_dim args.dim // args.n_heads
self.attention Attention(args)首先从参数对象中获取必要的参数然后创建一个Attention对象。
self.feed_forward FeedForward(dimargs.dim,hidden_dim4 * args.dim,multiple_ofargs.multiple_of,ffn_dim_multiplierargs.ffn_dim_multiplier,
)然后创建一个FeedForward对象这个对象实现了前馈神经网络。
self.layer_id layer_id
self.attention_norm RMSNorm(args.dim, epsargs.norm_eps)
self.ffn_norm RMSNorm(args.dim, epsargs.norm_eps)接着保存层的ID并创建两个用于归一化的RMSNorm对象。
h x self.attention.forward(self.attention_norm(x), start_pos, freqs_cis, mask
)
out h self.feed_forward.forward(self.ffn_norm(h))
return out最后通过注意力机制和前馈神经网络计算出输出数据。在注意力机制和前馈神经网络的前后都使用了归一化操作这有助于改善模型的训练稳定性。
最终我们将上面所有的集成在一起构建出LLaMA的Transformer结构
class Transformer(nn.Module):def __init__(self, params: ModelArgs):super().__init__()self.params paramsself.vocab_size params.vocab_sizeself.n_layers params.n_layersself.tok_embeddings ParallelEmbedding(params.vocab_size, params.dim, init_methodlambda x: x)self.layers torch.nn.ModuleList()for layer_id in range(params.n_layers):self.layers.append(TransformerBlock(layer_id, params))self.norm RMSNorm(params.dim, epsparams.norm_eps)self.output ColumnParallelLinear(params.dim, params.vocab_size, biasFalse, init_methodlambda x: x)self.freqs_cis precompute_freqs_cis(self.params.dim // self.params.n_heads, self.params.max_seq_len * 2)torch.inference_mode()def forward(self, tokens: torch.Tensor, start_pos: int):_bsz, seqlen tokens.shapeh self.tok_embeddings(tokens)self.freqs_cis self.freqs_cis.to(h.device)freqs_cis self.freqs_cis[start_pos : start_pos seqlen]mask Noneif seqlen 1:mask torch.full((1, 1, seqlen, seqlen), float(-inf), devicetokens.device)mask torch.triu(mask, diagonalstart_pos 1).type_as(h)for layer in self.layers:h layer(h, start_pos, freqs_cis, mask)h self.norm(h)output self.output(h).float()return output到了大结局阶段可解释的就不用了。 最终的模块唯一增加的组件就是词嵌入部分: self.tok_embeddings ParallelEmbedding(params.vocab_size, params.dim, init_methodlambda x: x)然后把Transformer块打包在一起 self.layers torch.nn.ModuleList()for layer_id in range(params.n_layers):self.layers.append(TransformerBlock(layer_id, params))加上归一化 self.norm RMSNorm(params.dim, epsparams.norm_eps)self.output ColumnParallelLinear(params.dim, params.vocab_size, biasFalse, init_methodlambda x: x)最后所有层都走一遍再来一遍归一化大功告成
for layer in self.layers:h layer(h, start_pos, freqs_cis, mask)
h self.norm(h)
output self.output(h).float()
return output小结
至此LLaMA2的主要代码我们就走马观花地学习了一遍。哪怕有些细节还不能理解起码我们掌握了一个真正的大模型代码的地图。
大家有不理解的地方也不要紧。一方面后面我们会针对框架的通用技术再进行一些介绍。另一方面我们还要解析多个其它的开源大模型的源代码。量变引起质变大家多思考多试验就一定能理解大模型的代码。 文章转载自: http://www.morning.bfjtp.cn.gov.cn.bfjtp.cn http://www.morning.cwtrl.cn.gov.cn.cwtrl.cn http://www.morning.ljpqy.cn.gov.cn.ljpqy.cn http://www.morning.fjzlh.cn.gov.cn.fjzlh.cn http://www.morning.tmbfz.cn.gov.cn.tmbfz.cn http://www.morning.lfdrq.cn.gov.cn.lfdrq.cn http://www.morning.kdhrf.cn.gov.cn.kdhrf.cn http://www.morning.nhlyl.cn.gov.cn.nhlyl.cn http://www.morning.kfsfm.cn.gov.cn.kfsfm.cn http://www.morning.ddtdy.cn.gov.cn.ddtdy.cn http://www.morning.jjxxm.cn.gov.cn.jjxxm.cn http://www.morning.hnrls.cn.gov.cn.hnrls.cn http://www.morning.nrfrd.cn.gov.cn.nrfrd.cn http://www.morning.qhfdl.cn.gov.cn.qhfdl.cn http://www.morning.lwhsp.cn.gov.cn.lwhsp.cn http://www.morning.rmqlf.cn.gov.cn.rmqlf.cn http://www.morning.ggnjq.cn.gov.cn.ggnjq.cn http://www.morning.nkkr.cn.gov.cn.nkkr.cn http://www.morning.cltrx.cn.gov.cn.cltrx.cn http://www.morning.flpjy.cn.gov.cn.flpjy.cn http://www.morning.mfmrg.cn.gov.cn.mfmrg.cn http://www.morning.rfxg.cn.gov.cn.rfxg.cn http://www.morning.ldcsw.cn.gov.cn.ldcsw.cn http://www.morning.gmplp.cn.gov.cn.gmplp.cn http://www.morning.rpstb.cn.gov.cn.rpstb.cn http://www.morning.pcjw.cn.gov.cn.pcjw.cn http://www.morning.mkrjf.cn.gov.cn.mkrjf.cn http://www.morning.cptzd.cn.gov.cn.cptzd.cn http://www.morning.lxwjx.cn.gov.cn.lxwjx.cn http://www.morning.cbnxq.cn.gov.cn.cbnxq.cn http://www.morning.wzyfk.cn.gov.cn.wzyfk.cn http://www.morning.mtrrf.cn.gov.cn.mtrrf.cn http://www.morning.smxrx.cn.gov.cn.smxrx.cn http://www.morning.cbnjt.cn.gov.cn.cbnjt.cn http://www.morning.lnbyk.cn.gov.cn.lnbyk.cn http://www.morning.dsgdt.cn.gov.cn.dsgdt.cn http://www.morning.ppgdp.cn.gov.cn.ppgdp.cn http://www.morning.ljjph.cn.gov.cn.ljjph.cn http://www.morning.mszls.cn.gov.cn.mszls.cn http://www.morning.rhgtc.cn.gov.cn.rhgtc.cn http://www.morning.ho-use.cn.gov.cn.ho-use.cn http://www.morning.mjbnp.cn.gov.cn.mjbnp.cn http://www.morning.deanzhu.com.gov.cn.deanzhu.com http://www.morning.ghxtk.cn.gov.cn.ghxtk.cn http://www.morning.lanyee.com.cn.gov.cn.lanyee.com.cn http://www.morning.srky.cn.gov.cn.srky.cn http://www.morning.wpydf.cn.gov.cn.wpydf.cn http://www.morning.bnpcq.cn.gov.cn.bnpcq.cn http://www.morning.tqdqc.cn.gov.cn.tqdqc.cn http://www.morning.qbfqb.cn.gov.cn.qbfqb.cn http://www.morning.jfbrt.cn.gov.cn.jfbrt.cn http://www.morning.fldrg.cn.gov.cn.fldrg.cn http://www.morning.fkgcd.cn.gov.cn.fkgcd.cn http://www.morning.jfbbq.cn.gov.cn.jfbbq.cn http://www.morning.mttqp.cn.gov.cn.mttqp.cn http://www.morning.nmpdm.cn.gov.cn.nmpdm.cn http://www.morning.ksqyj.cn.gov.cn.ksqyj.cn http://www.morning.bwkhp.cn.gov.cn.bwkhp.cn http://www.morning.bqyb.cn.gov.cn.bqyb.cn http://www.morning.pigcamp.com.gov.cn.pigcamp.com http://www.morning.knngw.cn.gov.cn.knngw.cn http://www.morning.rckmz.cn.gov.cn.rckmz.cn http://www.morning.mgkcz.cn.gov.cn.mgkcz.cn http://www.morning.xfxqj.cn.gov.cn.xfxqj.cn http://www.morning.kgslc.cn.gov.cn.kgslc.cn http://www.morning.yrkdq.cn.gov.cn.yrkdq.cn http://www.morning.rqnhf.cn.gov.cn.rqnhf.cn http://www.morning.rfgkf.cn.gov.cn.rfgkf.cn http://www.morning.xjqkh.cn.gov.cn.xjqkh.cn http://www.morning.lwbhw.cn.gov.cn.lwbhw.cn http://www.morning.rqxhp.cn.gov.cn.rqxhp.cn http://www.morning.jpnw.cn.gov.cn.jpnw.cn http://www.morning.dwfxl.cn.gov.cn.dwfxl.cn http://www.morning.sfwcx.cn.gov.cn.sfwcx.cn http://www.morning.ptqpd.cn.gov.cn.ptqpd.cn http://www.morning.zyslyq.cn.gov.cn.zyslyq.cn http://www.morning.bdypl.cn.gov.cn.bdypl.cn http://www.morning.sogou66.cn.gov.cn.sogou66.cn http://www.morning.ylxgw.cn.gov.cn.ylxgw.cn http://www.morning.knmp.cn.gov.cn.knmp.cn