llm大模型基本结构RMSNorm概述公式归一化前置比较总结LLM的解码策略激活函数SwiGLUlora低秩分解微调大模型微调面临的问题低秩分解A-LoRA和T-LoRA**1. A-LoRAAdaptive LoRA****2. T-LoRATask-Aware LoRA****对比总结****代码实现示例**GQAGrouped Query Attention和MHA、MQALLM中的MHAMQA多头潜在注意力机制MLA低秩键值联合压缩理论公式为什么RoPE与MLA不兼容以及如何解决图解解耦Rope解耦Rope实现混合专家模型MoEMoE结构路由router专家expertSwitch Transformer的典型MOE模型流程解析最后MoE总结DeepSeek-v3MTPdeepseek R1LLaVAclipLLaVA结构及训练问题LLM为什么推理多次LLM的温度什么用基本结构下面是一个Decoder-Only 的LLM模型。为简单起见我们假设一次只处理一个序列即批处理大小为 1。在下图中我描述了一个简单的基于 Transformer 的解码器的主要层用于从一系列输入词元中生成输出词元需要注意的是解码器本身并不会输出词元而是输出 logit其数量与词汇表大小相同。输出 logit 的最后一层通常被称为语言模型头Language Model Head或 LM 头。将 logit 转换为词元是通过一种启发式算法来完成的这种算法通常被称为解码策略decoding strategy也叫做词元搜索策略token search strategy或生成策略generation strategy。解码策略的目的是在保持文本连贯性和合理性的同时提高生成结果的质量和多样性。解码器的temperature也是在这里吧。RMSNorm概述RMSNormRoot Mean Square Layer Normalization均方根归一化是一种用于深度学习模型的归一化技术特别适用于Transformer等架构。它作为LayerNorm层归一化的替代方案旨在简化归一化过程降低计算复杂度同时保持或提升模型的性能。公式γ、β是可学习的参数维度与输入x相同所以公式中是点乘符号LayerNorm公式归一化前置此外在llama、deep seek等模型中RMSNorm都放在了attention和FFN之前归一化前置这样能减少输入数据的波动提高模型训练稳定性。通过前置归一化模型能够更好地处理输入数据减少内部协变量偏移ICS问题从而提高模型的泛化能力和整体性能比较总结计算高效由于省略了均值的计算RMSNorm在计算上更加简单快捷数值更加稳定避免了在均值较大或较小情况下的数值不稳定问题性能表现优异在某些模型和任务中RMSNorm显示出更快的收敛速度和更好的性能易于实现由于计算过程简单RMSNorm易于在各类深度学习框架中实现LLM的解码策略LLM采用自回归解码在每个时间步模型基于已生成序列计算下一个token的条件概率分布。解码策略关系到模型的准确性、多样性、前后一致性等。常见的解码算法包括贪婪解码、beam search、top-K、top-P等。待总结添加链接描述激活函数SwiGLUlora低秩分解微调大模型微调面临的问题fine-tuning微调是指通过新的数据集调整训练后模型的某些权重以获得更好性能。传统的微调方法通常会更新模型中的大部分或全部参数。但当面对大模型时传统微调方法就不再适用了低秩分解微调模型的权重需要添加 ΔW 来获得更新后的参数神经网络包含许多执行矩阵乘法的密集层。这些层中的权重矩阵通常满秩。当适应特定任务时Aghajanyan 等人2020表明预训练语言模型具有低 “内在维度”即使对较小子空间的随机投影它仍然可以有效学习。受此启发我们假设权重的更新在适应过程中也具有低 “内在秩”。对于一个预训练的权重矩阵 W0 ∈ R^(d×k)我们通过使用低秩分解来表示其更新 W0ΔWW0BA其中 B ∈ R^(d×r)A ∈ R^(r×k) 秩 r min(d,k)。在训练过程中W0 被冻结并且不接收梯度更新而 A 和 B 包含可训练的参数。请注意W0 和 ΔWBA 都与相同的输入进行乘法运算它们各自的输出向量在坐标方向上求和。对于 hW0x我们的修改后的前向传播为所以可以将 ΔW BA 看作是在一个低维子空间中搜索最优解矩阵奇异值分解https://blog.csdn.net/qq_44681809/article/details/136040848peft算法库的get_peft_model提供lora微调方法可以直接调用A-LoRA和T-LoRA在参数高效微调Parameter-Efficient Fine-Tuning, PEFT领域A-LoRA和T-LoRA是两种基于LoRALow-Rank Adaptation的改进方法旨在进一步降低计算成本或提升微调效果。以下是它们的核心区别和应用场景1. A-LoRAAdaptive LoRA核心改进动态调整低秩矩阵的秩rank根据任务需求或层的重要性自适应分配参数预算。实现方式通过评估权重矩阵的敏感度如梯度信息、Hessian矩阵动态调整 ( B ) 和 ( A ) 的秩。例如对关键层分配更高秩次要层分配更低秩。优点更高效地利用参数预算提升微调效果。适合异构任务不同层/模块对任务的贡献差异大。2. T-LoRATask-Aware LoRA核心改进引入任务特定Task-Specific的适配器在共享主干上为不同任务生成独立的低秩矩阵。实现方式使用任务编码Task Embedding生成动态的 ( B ) 和 ( A )。类似“软共享”机制避免多任务干扰。优点适用于多任务学习避免任务间冲突。比独立训练多个LoRA更节省参数。对比总结方法核心思想适用场景计算开销LoRA固定秩的低秩适配单任务微调低固定秩A-LoRA动态调整秩按层/模块敏感度异构任务、资源受限环境中等需评估T-LoRA任务感知的低秩生成多任务适配多任务学习较高任务编码代码实现示例A-LoRA伪代码rankcompute_adaptive_rank(layer_gradients)# 根据梯度动态计算秩lora_layerLoRALayer(original_weight,rankrank)T-LoRA伪代码task_embedget_task_embedding(task_id)# 获取任务编码Bgenerate_B_matrix(task_embed)# 动态生成低秩矩阵Agenerate_A_matrix(task_embed)GQAGrouped Query Attention和MHA、MQALLM中的MHALLM的transformer一般采用decoder结构多头注意力是从emb_len维度上把特征通道就行切分分别就行attentionMQAQAGrouped Query Attention是一种注意力机制它介于MHAMulti-Head Attention和MQAMulti-Query Attention之间旨在结合两者的优点以实现在保持MQA推理速度的同时接近MHA的精度。MHA是一种基础的注意力机制 。MQA则是一种优化的注意力机制它通过让所有头共享相同的键keys和值values减少了参数量和计算量从而加快了推理速度但可能会牺牲一些精度 。GQA作为MHA和MQA的折中方案它将查询头query heads分组每组共享一个键和值而不是所有头都共享。这样GQA能够在减少计算量的同时保持更多的多样性从而在推理速度和模型精度之间取得平衡 。MQA一个单独的组等同于 Multi-Query Attention (MQA)。MHA组数等于头数基本上与 Multi-Head Attention (MHA) 相同。GQA一个中间配置具有G个组平衡了效率和表达能力。多头潜在注意力机制MLA低秩键值联合压缩MLA不像普通的多头注意力一样直接把k v乘以相应的权重后送入attention而是先乘以一个权重向量降低维度后将低维度的k v再乘以普通的权重然后送入多头attention。这也就是所谓的低秩压缩。deepseek对查询矩阵q也做了压缩注意这里q是可做可不做的因为q的计算只是在最后一步q乘以softmax计算得到的权重矩阵就结束了如果压缩了维度还得再升回去。attention中q的维度和最后结果的维度相同。此外压缩是在token向量维度上压缩的对batch、token数量的维度保持不变。q k生成的权重矩阵仍是(seq_len, seq_len)在代码实现中将低秩权重矩阵和普通的权重矩阵合并用一个linear层实现。理论公式MLA的做法是首先对RMS的输出进行降维即图片中的第一个式子然后构建两个升维矩阵WUK和WUV进行升维以便后续进行Attention的计算。 这样做的好处是在推理过程中kv_cache并不需要存储完整的KV而只是存储这个降维后的CtKV这样就大大降低了内存的占用。同时在推理过程中可以将WUK融合到WQ中(就是做一个矩阵乘法)同样的可以将WUV融合到Wo中这样就不需要单独计算出KtC以及VtC了。但是上面这样的做法是在不考虑RoPE的前提下才能实现如果加上RoPE的话必须得先求出KtC才行。原因是位置编码是直接加于KtC所以就必须求出来了。这导致不能将WUK融合到WQ中这样就会增加计算量。因此论文有了deepseek v2的解耦旋转位置编码importtorchimporttorch.nnasnnclassMultiHeadLatentAttention(nn.Module):def__init__(self,input_dim,latent_dim,num_heads):super(MultiHeadLatentAttention,self).__init__()self.latent_projnn.Linear(input_dim,latent_dim)# 映射到潜在空间self.attentionnn.MultiheadAttention(embed_dimlatent_dim,num_headsnum_heads)self.output_projnn.Linear(latent_dim,input_dim)# 映射回原始空间defforward(self,x):# 输入映射到潜在空间latentself.latent_proj(x)# 在潜在空间中计算多头注意力attn_output,_self.attention(latent,latent,latent)# 映射回原始空间outputself.output_proj(attn_output)returnoutput# 示例输入batch_size,seq_len,input_dim32,128,512xtorch.rand(batch_size,seq_len,input_dim)mlaMultiHeadLatentAttention(input_dim512,latent_dim128,num_heads8)outputmla(x)为什么RoPE与MLA不兼容以及如何解决RoPE一般是在计算Attention的过程中Q*KT这一步骤的时候将位置信息加进去也就是说需要求出完整的Q和K矩阵才行其实原文这部分给了详细的解释如果不进行解耦的话会出现 WQ和WUK中间多出一个RoPE的旋转矩阵这就导致不能将二者合到一次进行推理了就会增大计算量。所以前面提到的MLA形式就不太行了。因此作者的做法是将RoPE进行解耦。解耦的具体做法就是单独构建一个qt,iR和一个ktR从而带上位置信息然后和qt,iCconcat到一起就构成了完整的q具体公式如下nh表示multy attention的head数量i是第i个头。第t个token的输出等于前面t-1个token输出的累加和。这个不太明白图解解耦Rope从这幅图中可以比较清楚的分析先看右面需要cache的k v分支首先存储的内容尽量是多头共享的这样就少了nhnum head这个维度MQA就是如此。这里原本只需要存储Ctkv这个压缩后的矩阵就可以了但是需要加入旋转位置编码。所以这里又额外添加了一个KtR分支注意KtR是所有头共享的也是少了一个维度的把这个矩阵添加Rope然后reshape成多头再和右面升维的多头的KtiC拼接组成最终的k送入attention。左边的q分支也进行了低秩压缩流程和K差不多但是由于无序cache所以这里用于位置编码的qtiT是多头的此外是从压缩后的CtQ接过去的这个区别不大试验所得。一言以蔽之所谓的解耦就是k的位置编码多头共享的q的位置编码是多头不共享的。还是k的升维矩阵和添加位置编码的矩阵之间的解耦不再直接对升维后的数据添加位置编码了另辟小分支加上了位置编码。再总结核心是下图的两个拼接如果没有拼接的位置编码qtr和ktr矩阵就是最原始的没添加位置编码的MLA需要位置编码现在不给它再上采样了而是退而求其次另加一个分支生成带位置信息的特征k这一分支需要存储少一个维度多头共享q那边不需要存储就是多头的不共享的特征。下面是一个示例的维度图理解更直观非deepseek原有维度deepseek v2技术文档中kv cache使用情况对比解耦位置编码参考链接讲的很好解耦Rope实现和上面的流程图一样对比着看易懂importtorchimporttorch.nnasnnimportmathclassMLA(nn.Module):def__init__(self,d_model512,down_dim128,up_dim256,num_heads8,rope_head_dim26,dropout_prob0.1):super(MLA,self).__init__()self.d_modeld_model self.down_dimdown_dim self.up_dimup_dim self.num_headsnum_heads self.head_dimd_model//num_heads self.rope_head_dimrope_head_dim self.v_head_dimup_dim//num_heads# 初始化kv联合以及q对应的dow,up projectionself.down_proj_kvnn.Linear(d_model,down_dim)# W^{DKV}self.up_proj_knn.Linear(down_dim,up_dim)# W^{UK}self.up_proj_vnn.Linear(down_dim,up_dim)# W^{UV}self.down_proj_qnn.Linear(d_model,down_dim)#W^{DQ}self.up_proj_qnn.Linear(down_dim,up_dim)# W^{UQ}# 初始化解耦的q,k进行MQA计算的映射矩阵self.proj_qrnn.Linear(down_dim,rope_head_dim*num_heads)self.proj_krnn.Linear(d_model,rope_head_dim*1)#初始化解耦的q,k对应的rope类因为头的数量不同初始化2个实例self.rope_qRotaryEmbedding(rope_head_dim*num_heads,num_heads)self.rope_kRotaryEmbedding(rope_head_dim,1)# Dropout and final linear layerself.dropoutnn.Dropout(dropout_prob)self.fcnn.Linear(num_heads*self.v_head_dim,d_model)self.res_dropoutnn.Dropout(dropout_prob)defforward(self,h,maskNone):bs,seq_len,_h.size()# setp1 :低秩转换c_t_kvself.down_proj_kv(h)k_t_cself.up_proj_k(c_t_kv)v_t_cself.up_proj_v(c_t_kv)c_t_qself.down_proj_q(h)q_t_cself.up_proj_q(c_t_q)#step2:解耦的q,k进行MQA计算同时引入ROPE#q_t_r,k_t_r施加rope时均扩展了n_h_r维度-[bs,n_h_r,seq_len,rope_head_dim]q_t_rself.rope_q(self.proj_qr(c_t_q))k_t_rself.rope_k(self.proj_kr(h))#step3:拼接step1step2得到的q,k,进行sdpa计算#q_t_c扩展出num_heads为4维以便于和q_t_r拼接q_t_cq_t_c.reshape(bs,seq_len,self.num_heads,-1).transpose(1,2)#head_dim,rope_head_dim拼接qtorch.cat([q_t_c,q_t_r],dim-1)#k_t_c扩展出num_heads为4维以便于和k_t_r拼接k_t_ck_t_c.reshape(bs,seq_len,self.num_heads,-1).transpose(1,2)#k_t_r为MQA,n_h_k_r1,为了和q_t_r计算需要在n_h_k_r维度复制#k_t_r:[bs,n_h_r_k,seq_len,rope_head_dim]-[bs,num_heads,seq_len,rope_head_dim]k_t_rk_t_r.repeat(1,self.num_heads,1,1)#head_dim,rope_head_dim拼接ktorch.cat([k_t_c,k_t_r],dim-1)# 注意力计算,[bs,num_heads,seq_len,seq_len]scorestorch.matmul(q,k.transpose(-1,-2))ifmaskisnotNone:scoresscores.masked_fill(mask0,-1e9)scorestorch.softmax(scores/(math.sqrt(self.head_dim)math.sqrt(self.rope_head_dim)),dim-1)scoresself.dropout(scores)#v_t_c和scores计算扩展出num_heads维度v_t_cv_t_c.reshape(bs,seq_len,self.num_heads,self.v_head_dim).transpose(1,2)outputtorch.matmul(scores,v_t_c)#压缩num_head,送入最终统一映射层outputoutput.transpose(1,2).reshape(bs,seq_len,-1)outputself.fc(output)outputself.res_dropout(output)returnoutput bs,seq_len,d_model4,10,512htorch.randn(bs,seq_len,d_model)mlaMLA(d_modeld_model)outputmla(h)混合专家模型MoE模型规模是提升LLM大语言模型性能的关键因素但也会增加计算成本。Mixture of Experts (MoE) 架构通过分布式专家层和动态门控机制有效降低了计算资源使模型能够在扩展参数规模的同时保持高效的运行。因为MoE是稀疏的。MoE结构典型的MOE结构包括两个部分Router 路由决定采用哪个ExpertExperts多个Expert路由routertransformer结构中每个token分词是一个向量。哪个token被分到哪个或哪些专家模型。比如有的网络专家适合处理数字有的网络专家适合处理动词等。所以门控或路由是一个线性层路由层的输出维度等于专家数量。定义 Wg为路由层权重其形状为(dim, n_experts)。TopK是超参每个token选择K个专家然后对K个特征取softmax进行归一化。专家expert混合专家层定义为为 { E0 , E i , . . . , E n − 1 }路由层定义为 G计算公式如下在Mixtral中每个专家层都是一个FFN。路由层提供不同专家的权重与专家层的输出加权求和得到MoE的输出Switch Transformer的典型MOE模型替换transformer中的FFN layer为MoE layer包含4 experts。通过Router选中一个Expert进行激活。注意下图FFN1-FFN4一共四个所有的token都是共享这四个专家。每个token都有自己的router如下面的0.65、0.8这些router决定如何选择专家。流程解析假设我们定义了4个专家路由取前2名专家即expert4 top_k2。接收注意力层的输出作为输入X即将输入从Batch sizeTokensn_embed的形状2432投影到对应于Batch sizeTokensnum_experts的形状244其中num_experts即expert4。其中返回的indices可以理解为对于每个token的4个专家来说选的两个专家的序号索引importtorchimporttorch.nnasnnimporttorch.nn.functionalasF# 假设n_embed32num_experts4top_k2n_embed32num_experts4top_k2classExpert(nn.Module):单个专家网络示例简单的两层MLPdef__init__(self,n_embed):super().__init__()self.fc1nn.Linear(n_embed,n_embed*4)# 扩张维度self.fc2nn.Linear(n_embed*4,n_embed)# 压缩回原维度self.activationnn.GELU()defforward(self,x):xself.fc1(x)xself.activation(x)xself.fc2(x)returnxclassMoE(nn.Module):def__init__(self,n_embed,num_experts,top_k):super().__init__()self.routerTopkRouter(n_embed,num_experts,top_k)# 路由模块self.expertsnn.ModuleList([Expert(n_embed)for_inrange(num_experts)])# 专家网络集合self.top_ktop_kdefforward(self,x):# x形状(batch_size, seq_len, n_embed)例如(2,4,32)batch_size,seq_len,_x.shape# 1. 路由模块计算每个位置的专家权重和选中的专家索引router_weights,indicesself.router(x)# weights: (2,4,4)indices: (2,4,2)# 2. 准备专家输入将输入展平为(batch*seq_len, n_embed)方便批量处理flat_xx.view(-1,n_embed)# (8, 32)其中82*4flat_indicesindices.view(-1,self.top_k)# (8, 2)每个样本选2个专家# 3. 专家网络计算对每个专家处理所有分配给它的输入# 先初始化一个存储专家输出的张量expert_outputstorch.zeros_like(flat_x)# (8, 32)# 遍历每个专家找到需要它处理的输入位置并应用专家网络forexpert_idxinrange(num_experts):# 找到所有选中当前专家的位置mask# mask形状(8,2)True表示该位置选中了expert_idxmask(flat_indicesexpert_idx)# 收集需要当前专家处理的输入展平为一维索引positionsmask.nonzero().squeeze(1)# 例如[0, 2, 5]表示这些位置需要专家expert_idx处理iflen(positions)0:continue# 没有输入分配给该专家跳过# 专家处理输入expert_inputflat_x[positions]expert_outself.experts[expert_idx](expert_input)# 累加专家输出乘以对应的路由权重# 提取这些位置对应的权重router_weights中expert_idx的权重weightsrouter_weights.view(-1,num_experts)[positions,expert_idx].unsqueeze(1)# (num_positions, 1)expert_outputs[positions]expert_out*weights# 4. 恢复形状(batch*seq_len, n_embed) → (batch_size, seq_len, n_embed)outputexpert_outputs.view(batch_size,seq_len,n_embed)returnoutput# 你的路由模块保持不变classTopkRouter(nn.Module):def__init__(self,n_embed,num_experts,top_k):super(TopkRouter,self).__init__()self.top_ktop_k self.linearnn.Linear(n_embed,num_experts)# 预测每个专家的重要性defforward(self,mh_output):# mh_output: (batch_size, seq_len, n_embed) → 例如(2,4,32)logitsself.linear(mh_output)# (2,4,4)每个位置对4个专家的打分top_k_logits,indiceslogits.topk(self.top_k,dim-1)# 选top2专家indices形状(2,4,2)# 构造稀疏的logits矩阵只保留topk的分数其余为-infsparse_logitstorch.full_like(logits,float(-inf)).scatter(-1,indices,top_k_logits)router_weightsF.softmax(sparse_logits,dim-1)# 归一化权重形状(2,4,4)returnrouter_weights,indices看完代码之后配合整体流程图将会更清晰更清晰的图示如下每个字代表一个token有了router的输出router_output[Batch sizeTokensnum_experts]和indices后然后再专家维度上进行遍历对该专家负责的token向量进行加权累加最后获得加权后的token向量最终输出维度还是Batch sizeTokensn_embed此外还有负责加噪的router我们不希望所有token都发送给同一组“受青睐”的expert。需要一个良好平衡因此将标准正态噪声添加到来自门控线性层的logits。更详细的学习参考添加链接描述最后MoE总结DeepSeek-v3学习链接待整理https://zhuanlan.zhihu.com/p/14988009150MTP仅训练的时候使用用于加速收敛推理的时候没有用到这部分代码没有开源deepseek R1提出 GPRO 强化学习思想对传统 PPO 进行深入改进创新性地提出 GPRO 强化学习思想并将其与 SFT有监督微调相结合成功训练出 R1 模型。这一整合优化的策略在提升模型性能方面成效显著为强化学习在大模型训练中的应用带来新的突破有望推动相关技术的进一步发展与革新LLaVA这是一个vlm模型使用了clipclipCLIP使用了对比学习的方法即通过正样本匹配的图像-文本对和负样本不匹配的图像-文本对来训练模型。在训练过程中模型会尝试最大化正样本对的相似度比如通过计算余弦相似度同时最小化负样本对的相似度。CLIP模型在zero-shot学习中表现强大可以直接用于zero-shot推理比如将猫的图片emb后将猪狗猫等类的文本描述也分别emb计算图片和类别emb之间的相似度从而进行分类。CLIP-ViT-L/14模型的14表示每个patch的分辨率为14X14比如在224x224像素的图像上总共有(224 / 14) x (224 / 14) 16 x 16 256个patch。LLaVA结构及训练LLaVA的模型结构非常简单就是CLIPLLM(VicunaLLaMA结构)利用Vison Encoder将图片转换为[N1, grid_H x grid_W, hidden_dim]的feature map然后接一个插值层Projection W将图像特征和文本特征进行维度对齐。经过Projection后得到[N1, grid_H x grid_Wimage_seqlen, emb_dim]。然后将 image token embedding和text token embedding合并到一起作为语言模型的输入生成描述的文本。与InstructBLIP或Qwen-VL在数亿甚至数十几亿的图像文本配对数据上训练的、专门设计的视觉重新采样器相比LLaVA用的是最简单的LMM架构设计只需要在600K个图像-文本对上训练一个简单的完全连接映射层即可。问题LLM为什么推理多次自回归方式每次预测一个tokentransformer中的k和v是输入特征q来自上一个token的特征即输入中最后一个token看看链接知道咋回事就明白了添加链接描述LLM的温度什么用温度是softmax 函数中的一个参数用于控制概率分布的 “平滑度”和蒸馏的软标签差不多仅推理的时候会用T → 1分布更平滑随机性增强更 “创意”。T → 0分布更尖锐确定性增强更 “保守”。T 1标准 softmax。
llm大模型学习
llm大模型基本结构RMSNorm概述公式归一化前置比较总结LLM的解码策略激活函数SwiGLUlora低秩分解微调大模型微调面临的问题低秩分解A-LoRA和T-LoRA**1. A-LoRAAdaptive LoRA****2. T-LoRATask-Aware LoRA****对比总结****代码实现示例**GQAGrouped Query Attention和MHA、MQALLM中的MHAMQA多头潜在注意力机制MLA低秩键值联合压缩理论公式为什么RoPE与MLA不兼容以及如何解决图解解耦Rope解耦Rope实现混合专家模型MoEMoE结构路由router专家expertSwitch Transformer的典型MOE模型流程解析最后MoE总结DeepSeek-v3MTPdeepseek R1LLaVAclipLLaVA结构及训练问题LLM为什么推理多次LLM的温度什么用基本结构下面是一个Decoder-Only 的LLM模型。为简单起见我们假设一次只处理一个序列即批处理大小为 1。在下图中我描述了一个简单的基于 Transformer 的解码器的主要层用于从一系列输入词元中生成输出词元需要注意的是解码器本身并不会输出词元而是输出 logit其数量与词汇表大小相同。输出 logit 的最后一层通常被称为语言模型头Language Model Head或 LM 头。将 logit 转换为词元是通过一种启发式算法来完成的这种算法通常被称为解码策略decoding strategy也叫做词元搜索策略token search strategy或生成策略generation strategy。解码策略的目的是在保持文本连贯性和合理性的同时提高生成结果的质量和多样性。解码器的temperature也是在这里吧。RMSNorm概述RMSNormRoot Mean Square Layer Normalization均方根归一化是一种用于深度学习模型的归一化技术特别适用于Transformer等架构。它作为LayerNorm层归一化的替代方案旨在简化归一化过程降低计算复杂度同时保持或提升模型的性能。公式γ、β是可学习的参数维度与输入x相同所以公式中是点乘符号LayerNorm公式归一化前置此外在llama、deep seek等模型中RMSNorm都放在了attention和FFN之前归一化前置这样能减少输入数据的波动提高模型训练稳定性。通过前置归一化模型能够更好地处理输入数据减少内部协变量偏移ICS问题从而提高模型的泛化能力和整体性能比较总结计算高效由于省略了均值的计算RMSNorm在计算上更加简单快捷数值更加稳定避免了在均值较大或较小情况下的数值不稳定问题性能表现优异在某些模型和任务中RMSNorm显示出更快的收敛速度和更好的性能易于实现由于计算过程简单RMSNorm易于在各类深度学习框架中实现LLM的解码策略LLM采用自回归解码在每个时间步模型基于已生成序列计算下一个token的条件概率分布。解码策略关系到模型的准确性、多样性、前后一致性等。常见的解码算法包括贪婪解码、beam search、top-K、top-P等。待总结添加链接描述激活函数SwiGLUlora低秩分解微调大模型微调面临的问题fine-tuning微调是指通过新的数据集调整训练后模型的某些权重以获得更好性能。传统的微调方法通常会更新模型中的大部分或全部参数。但当面对大模型时传统微调方法就不再适用了低秩分解微调模型的权重需要添加 ΔW 来获得更新后的参数神经网络包含许多执行矩阵乘法的密集层。这些层中的权重矩阵通常满秩。当适应特定任务时Aghajanyan 等人2020表明预训练语言模型具有低 “内在维度”即使对较小子空间的随机投影它仍然可以有效学习。受此启发我们假设权重的更新在适应过程中也具有低 “内在秩”。对于一个预训练的权重矩阵 W0 ∈ R^(d×k)我们通过使用低秩分解来表示其更新 W0ΔWW0BA其中 B ∈ R^(d×r)A ∈ R^(r×k) 秩 r min(d,k)。在训练过程中W0 被冻结并且不接收梯度更新而 A 和 B 包含可训练的参数。请注意W0 和 ΔWBA 都与相同的输入进行乘法运算它们各自的输出向量在坐标方向上求和。对于 hW0x我们的修改后的前向传播为所以可以将 ΔW BA 看作是在一个低维子空间中搜索最优解矩阵奇异值分解https://blog.csdn.net/qq_44681809/article/details/136040848peft算法库的get_peft_model提供lora微调方法可以直接调用A-LoRA和T-LoRA在参数高效微调Parameter-Efficient Fine-Tuning, PEFT领域A-LoRA和T-LoRA是两种基于LoRALow-Rank Adaptation的改进方法旨在进一步降低计算成本或提升微调效果。以下是它们的核心区别和应用场景1. A-LoRAAdaptive LoRA核心改进动态调整低秩矩阵的秩rank根据任务需求或层的重要性自适应分配参数预算。实现方式通过评估权重矩阵的敏感度如梯度信息、Hessian矩阵动态调整 ( B ) 和 ( A ) 的秩。例如对关键层分配更高秩次要层分配更低秩。优点更高效地利用参数预算提升微调效果。适合异构任务不同层/模块对任务的贡献差异大。2. T-LoRATask-Aware LoRA核心改进引入任务特定Task-Specific的适配器在共享主干上为不同任务生成独立的低秩矩阵。实现方式使用任务编码Task Embedding生成动态的 ( B ) 和 ( A )。类似“软共享”机制避免多任务干扰。优点适用于多任务学习避免任务间冲突。比独立训练多个LoRA更节省参数。对比总结方法核心思想适用场景计算开销LoRA固定秩的低秩适配单任务微调低固定秩A-LoRA动态调整秩按层/模块敏感度异构任务、资源受限环境中等需评估T-LoRA任务感知的低秩生成多任务适配多任务学习较高任务编码代码实现示例A-LoRA伪代码rankcompute_adaptive_rank(layer_gradients)# 根据梯度动态计算秩lora_layerLoRALayer(original_weight,rankrank)T-LoRA伪代码task_embedget_task_embedding(task_id)# 获取任务编码Bgenerate_B_matrix(task_embed)# 动态生成低秩矩阵Agenerate_A_matrix(task_embed)GQAGrouped Query Attention和MHA、MQALLM中的MHALLM的transformer一般采用decoder结构多头注意力是从emb_len维度上把特征通道就行切分分别就行attentionMQAQAGrouped Query Attention是一种注意力机制它介于MHAMulti-Head Attention和MQAMulti-Query Attention之间旨在结合两者的优点以实现在保持MQA推理速度的同时接近MHA的精度。MHA是一种基础的注意力机制 。MQA则是一种优化的注意力机制它通过让所有头共享相同的键keys和值values减少了参数量和计算量从而加快了推理速度但可能会牺牲一些精度 。GQA作为MHA和MQA的折中方案它将查询头query heads分组每组共享一个键和值而不是所有头都共享。这样GQA能够在减少计算量的同时保持更多的多样性从而在推理速度和模型精度之间取得平衡 。MQA一个单独的组等同于 Multi-Query Attention (MQA)。MHA组数等于头数基本上与 Multi-Head Attention (MHA) 相同。GQA一个中间配置具有G个组平衡了效率和表达能力。多头潜在注意力机制MLA低秩键值联合压缩MLA不像普通的多头注意力一样直接把k v乘以相应的权重后送入attention而是先乘以一个权重向量降低维度后将低维度的k v再乘以普通的权重然后送入多头attention。这也就是所谓的低秩压缩。deepseek对查询矩阵q也做了压缩注意这里q是可做可不做的因为q的计算只是在最后一步q乘以softmax计算得到的权重矩阵就结束了如果压缩了维度还得再升回去。attention中q的维度和最后结果的维度相同。此外压缩是在token向量维度上压缩的对batch、token数量的维度保持不变。q k生成的权重矩阵仍是(seq_len, seq_len)在代码实现中将低秩权重矩阵和普通的权重矩阵合并用一个linear层实现。理论公式MLA的做法是首先对RMS的输出进行降维即图片中的第一个式子然后构建两个升维矩阵WUK和WUV进行升维以便后续进行Attention的计算。 这样做的好处是在推理过程中kv_cache并不需要存储完整的KV而只是存储这个降维后的CtKV这样就大大降低了内存的占用。同时在推理过程中可以将WUK融合到WQ中(就是做一个矩阵乘法)同样的可以将WUV融合到Wo中这样就不需要单独计算出KtC以及VtC了。但是上面这样的做法是在不考虑RoPE的前提下才能实现如果加上RoPE的话必须得先求出KtC才行。原因是位置编码是直接加于KtC所以就必须求出来了。这导致不能将WUK融合到WQ中这样就会增加计算量。因此论文有了deepseek v2的解耦旋转位置编码importtorchimporttorch.nnasnnclassMultiHeadLatentAttention(nn.Module):def__init__(self,input_dim,latent_dim,num_heads):super(MultiHeadLatentAttention,self).__init__()self.latent_projnn.Linear(input_dim,latent_dim)# 映射到潜在空间self.attentionnn.MultiheadAttention(embed_dimlatent_dim,num_headsnum_heads)self.output_projnn.Linear(latent_dim,input_dim)# 映射回原始空间defforward(self,x):# 输入映射到潜在空间latentself.latent_proj(x)# 在潜在空间中计算多头注意力attn_output,_self.attention(latent,latent,latent)# 映射回原始空间outputself.output_proj(attn_output)returnoutput# 示例输入batch_size,seq_len,input_dim32,128,512xtorch.rand(batch_size,seq_len,input_dim)mlaMultiHeadLatentAttention(input_dim512,latent_dim128,num_heads8)outputmla(x)为什么RoPE与MLA不兼容以及如何解决RoPE一般是在计算Attention的过程中Q*KT这一步骤的时候将位置信息加进去也就是说需要求出完整的Q和K矩阵才行其实原文这部分给了详细的解释如果不进行解耦的话会出现 WQ和WUK中间多出一个RoPE的旋转矩阵这就导致不能将二者合到一次进行推理了就会增大计算量。所以前面提到的MLA形式就不太行了。因此作者的做法是将RoPE进行解耦。解耦的具体做法就是单独构建一个qt,iR和一个ktR从而带上位置信息然后和qt,iCconcat到一起就构成了完整的q具体公式如下nh表示multy attention的head数量i是第i个头。第t个token的输出等于前面t-1个token输出的累加和。这个不太明白图解解耦Rope从这幅图中可以比较清楚的分析先看右面需要cache的k v分支首先存储的内容尽量是多头共享的这样就少了nhnum head这个维度MQA就是如此。这里原本只需要存储Ctkv这个压缩后的矩阵就可以了但是需要加入旋转位置编码。所以这里又额外添加了一个KtR分支注意KtR是所有头共享的也是少了一个维度的把这个矩阵添加Rope然后reshape成多头再和右面升维的多头的KtiC拼接组成最终的k送入attention。左边的q分支也进行了低秩压缩流程和K差不多但是由于无序cache所以这里用于位置编码的qtiT是多头的此外是从压缩后的CtQ接过去的这个区别不大试验所得。一言以蔽之所谓的解耦就是k的位置编码多头共享的q的位置编码是多头不共享的。还是k的升维矩阵和添加位置编码的矩阵之间的解耦不再直接对升维后的数据添加位置编码了另辟小分支加上了位置编码。再总结核心是下图的两个拼接如果没有拼接的位置编码qtr和ktr矩阵就是最原始的没添加位置编码的MLA需要位置编码现在不给它再上采样了而是退而求其次另加一个分支生成带位置信息的特征k这一分支需要存储少一个维度多头共享q那边不需要存储就是多头的不共享的特征。下面是一个示例的维度图理解更直观非deepseek原有维度deepseek v2技术文档中kv cache使用情况对比解耦位置编码参考链接讲的很好解耦Rope实现和上面的流程图一样对比着看易懂importtorchimporttorch.nnasnnimportmathclassMLA(nn.Module):def__init__(self,d_model512,down_dim128,up_dim256,num_heads8,rope_head_dim26,dropout_prob0.1):super(MLA,self).__init__()self.d_modeld_model self.down_dimdown_dim self.up_dimup_dim self.num_headsnum_heads self.head_dimd_model//num_heads self.rope_head_dimrope_head_dim self.v_head_dimup_dim//num_heads# 初始化kv联合以及q对应的dow,up projectionself.down_proj_kvnn.Linear(d_model,down_dim)# W^{DKV}self.up_proj_knn.Linear(down_dim,up_dim)# W^{UK}self.up_proj_vnn.Linear(down_dim,up_dim)# W^{UV}self.down_proj_qnn.Linear(d_model,down_dim)#W^{DQ}self.up_proj_qnn.Linear(down_dim,up_dim)# W^{UQ}# 初始化解耦的q,k进行MQA计算的映射矩阵self.proj_qrnn.Linear(down_dim,rope_head_dim*num_heads)self.proj_krnn.Linear(d_model,rope_head_dim*1)#初始化解耦的q,k对应的rope类因为头的数量不同初始化2个实例self.rope_qRotaryEmbedding(rope_head_dim*num_heads,num_heads)self.rope_kRotaryEmbedding(rope_head_dim,1)# Dropout and final linear layerself.dropoutnn.Dropout(dropout_prob)self.fcnn.Linear(num_heads*self.v_head_dim,d_model)self.res_dropoutnn.Dropout(dropout_prob)defforward(self,h,maskNone):bs,seq_len,_h.size()# setp1 :低秩转换c_t_kvself.down_proj_kv(h)k_t_cself.up_proj_k(c_t_kv)v_t_cself.up_proj_v(c_t_kv)c_t_qself.down_proj_q(h)q_t_cself.up_proj_q(c_t_q)#step2:解耦的q,k进行MQA计算同时引入ROPE#q_t_r,k_t_r施加rope时均扩展了n_h_r维度-[bs,n_h_r,seq_len,rope_head_dim]q_t_rself.rope_q(self.proj_qr(c_t_q))k_t_rself.rope_k(self.proj_kr(h))#step3:拼接step1step2得到的q,k,进行sdpa计算#q_t_c扩展出num_heads为4维以便于和q_t_r拼接q_t_cq_t_c.reshape(bs,seq_len,self.num_heads,-1).transpose(1,2)#head_dim,rope_head_dim拼接qtorch.cat([q_t_c,q_t_r],dim-1)#k_t_c扩展出num_heads为4维以便于和k_t_r拼接k_t_ck_t_c.reshape(bs,seq_len,self.num_heads,-1).transpose(1,2)#k_t_r为MQA,n_h_k_r1,为了和q_t_r计算需要在n_h_k_r维度复制#k_t_r:[bs,n_h_r_k,seq_len,rope_head_dim]-[bs,num_heads,seq_len,rope_head_dim]k_t_rk_t_r.repeat(1,self.num_heads,1,1)#head_dim,rope_head_dim拼接ktorch.cat([k_t_c,k_t_r],dim-1)# 注意力计算,[bs,num_heads,seq_len,seq_len]scorestorch.matmul(q,k.transpose(-1,-2))ifmaskisnotNone:scoresscores.masked_fill(mask0,-1e9)scorestorch.softmax(scores/(math.sqrt(self.head_dim)math.sqrt(self.rope_head_dim)),dim-1)scoresself.dropout(scores)#v_t_c和scores计算扩展出num_heads维度v_t_cv_t_c.reshape(bs,seq_len,self.num_heads,self.v_head_dim).transpose(1,2)outputtorch.matmul(scores,v_t_c)#压缩num_head,送入最终统一映射层outputoutput.transpose(1,2).reshape(bs,seq_len,-1)outputself.fc(output)outputself.res_dropout(output)returnoutput bs,seq_len,d_model4,10,512htorch.randn(bs,seq_len,d_model)mlaMLA(d_modeld_model)outputmla(h)混合专家模型MoE模型规模是提升LLM大语言模型性能的关键因素但也会增加计算成本。Mixture of Experts (MoE) 架构通过分布式专家层和动态门控机制有效降低了计算资源使模型能够在扩展参数规模的同时保持高效的运行。因为MoE是稀疏的。MoE结构典型的MOE结构包括两个部分Router 路由决定采用哪个ExpertExperts多个Expert路由routertransformer结构中每个token分词是一个向量。哪个token被分到哪个或哪些专家模型。比如有的网络专家适合处理数字有的网络专家适合处理动词等。所以门控或路由是一个线性层路由层的输出维度等于专家数量。定义 Wg为路由层权重其形状为(dim, n_experts)。TopK是超参每个token选择K个专家然后对K个特征取softmax进行归一化。专家expert混合专家层定义为为 { E0 , E i , . . . , E n − 1 }路由层定义为 G计算公式如下在Mixtral中每个专家层都是一个FFN。路由层提供不同专家的权重与专家层的输出加权求和得到MoE的输出Switch Transformer的典型MOE模型替换transformer中的FFN layer为MoE layer包含4 experts。通过Router选中一个Expert进行激活。注意下图FFN1-FFN4一共四个所有的token都是共享这四个专家。每个token都有自己的router如下面的0.65、0.8这些router决定如何选择专家。流程解析假设我们定义了4个专家路由取前2名专家即expert4 top_k2。接收注意力层的输出作为输入X即将输入从Batch sizeTokensn_embed的形状2432投影到对应于Batch sizeTokensnum_experts的形状244其中num_experts即expert4。其中返回的indices可以理解为对于每个token的4个专家来说选的两个专家的序号索引importtorchimporttorch.nnasnnimporttorch.nn.functionalasF# 假设n_embed32num_experts4top_k2n_embed32num_experts4top_k2classExpert(nn.Module):单个专家网络示例简单的两层MLPdef__init__(self,n_embed):super().__init__()self.fc1nn.Linear(n_embed,n_embed*4)# 扩张维度self.fc2nn.Linear(n_embed*4,n_embed)# 压缩回原维度self.activationnn.GELU()defforward(self,x):xself.fc1(x)xself.activation(x)xself.fc2(x)returnxclassMoE(nn.Module):def__init__(self,n_embed,num_experts,top_k):super().__init__()self.routerTopkRouter(n_embed,num_experts,top_k)# 路由模块self.expertsnn.ModuleList([Expert(n_embed)for_inrange(num_experts)])# 专家网络集合self.top_ktop_kdefforward(self,x):# x形状(batch_size, seq_len, n_embed)例如(2,4,32)batch_size,seq_len,_x.shape# 1. 路由模块计算每个位置的专家权重和选中的专家索引router_weights,indicesself.router(x)# weights: (2,4,4)indices: (2,4,2)# 2. 准备专家输入将输入展平为(batch*seq_len, n_embed)方便批量处理flat_xx.view(-1,n_embed)# (8, 32)其中82*4flat_indicesindices.view(-1,self.top_k)# (8, 2)每个样本选2个专家# 3. 专家网络计算对每个专家处理所有分配给它的输入# 先初始化一个存储专家输出的张量expert_outputstorch.zeros_like(flat_x)# (8, 32)# 遍历每个专家找到需要它处理的输入位置并应用专家网络forexpert_idxinrange(num_experts):# 找到所有选中当前专家的位置mask# mask形状(8,2)True表示该位置选中了expert_idxmask(flat_indicesexpert_idx)# 收集需要当前专家处理的输入展平为一维索引positionsmask.nonzero().squeeze(1)# 例如[0, 2, 5]表示这些位置需要专家expert_idx处理iflen(positions)0:continue# 没有输入分配给该专家跳过# 专家处理输入expert_inputflat_x[positions]expert_outself.experts[expert_idx](expert_input)# 累加专家输出乘以对应的路由权重# 提取这些位置对应的权重router_weights中expert_idx的权重weightsrouter_weights.view(-1,num_experts)[positions,expert_idx].unsqueeze(1)# (num_positions, 1)expert_outputs[positions]expert_out*weights# 4. 恢复形状(batch*seq_len, n_embed) → (batch_size, seq_len, n_embed)outputexpert_outputs.view(batch_size,seq_len,n_embed)returnoutput# 你的路由模块保持不变classTopkRouter(nn.Module):def__init__(self,n_embed,num_experts,top_k):super(TopkRouter,self).__init__()self.top_ktop_k self.linearnn.Linear(n_embed,num_experts)# 预测每个专家的重要性defforward(self,mh_output):# mh_output: (batch_size, seq_len, n_embed) → 例如(2,4,32)logitsself.linear(mh_output)# (2,4,4)每个位置对4个专家的打分top_k_logits,indiceslogits.topk(self.top_k,dim-1)# 选top2专家indices形状(2,4,2)# 构造稀疏的logits矩阵只保留topk的分数其余为-infsparse_logitstorch.full_like(logits,float(-inf)).scatter(-1,indices,top_k_logits)router_weightsF.softmax(sparse_logits,dim-1)# 归一化权重形状(2,4,4)returnrouter_weights,indices看完代码之后配合整体流程图将会更清晰更清晰的图示如下每个字代表一个token有了router的输出router_output[Batch sizeTokensnum_experts]和indices后然后再专家维度上进行遍历对该专家负责的token向量进行加权累加最后获得加权后的token向量最终输出维度还是Batch sizeTokensn_embed此外还有负责加噪的router我们不希望所有token都发送给同一组“受青睐”的expert。需要一个良好平衡因此将标准正态噪声添加到来自门控线性层的logits。更详细的学习参考添加链接描述最后MoE总结DeepSeek-v3学习链接待整理https://zhuanlan.zhihu.com/p/14988009150MTP仅训练的时候使用用于加速收敛推理的时候没有用到这部分代码没有开源deepseek R1提出 GPRO 强化学习思想对传统 PPO 进行深入改进创新性地提出 GPRO 强化学习思想并将其与 SFT有监督微调相结合成功训练出 R1 模型。这一整合优化的策略在提升模型性能方面成效显著为强化学习在大模型训练中的应用带来新的突破有望推动相关技术的进一步发展与革新LLaVA这是一个vlm模型使用了clipclipCLIP使用了对比学习的方法即通过正样本匹配的图像-文本对和负样本不匹配的图像-文本对来训练模型。在训练过程中模型会尝试最大化正样本对的相似度比如通过计算余弦相似度同时最小化负样本对的相似度。CLIP模型在zero-shot学习中表现强大可以直接用于zero-shot推理比如将猫的图片emb后将猪狗猫等类的文本描述也分别emb计算图片和类别emb之间的相似度从而进行分类。CLIP-ViT-L/14模型的14表示每个patch的分辨率为14X14比如在224x224像素的图像上总共有(224 / 14) x (224 / 14) 16 x 16 256个patch。LLaVA结构及训练LLaVA的模型结构非常简单就是CLIPLLM(VicunaLLaMA结构)利用Vison Encoder将图片转换为[N1, grid_H x grid_W, hidden_dim]的feature map然后接一个插值层Projection W将图像特征和文本特征进行维度对齐。经过Projection后得到[N1, grid_H x grid_Wimage_seqlen, emb_dim]。然后将 image token embedding和text token embedding合并到一起作为语言模型的输入生成描述的文本。与InstructBLIP或Qwen-VL在数亿甚至数十几亿的图像文本配对数据上训练的、专门设计的视觉重新采样器相比LLaVA用的是最简单的LMM架构设计只需要在600K个图像-文本对上训练一个简单的完全连接映射层即可。问题LLM为什么推理多次自回归方式每次预测一个tokentransformer中的k和v是输入特征q来自上一个token的特征即输入中最后一个token看看链接知道咋回事就明白了添加链接描述LLM的温度什么用温度是softmax 函数中的一个参数用于控制概率分布的 “平滑度”和蒸馏的软标签差不多仅推理的时候会用T → 1分布更平滑随机性增强更 “创意”。T → 0分布更尖锐确定性增强更 “保守”。T 1标准 softmax。