LLaMa、Qwen、ChatGLM、ChatGLM2的区别

以下比较的前提是首先和BERT(transfomer)的对比

PS: 大模型基础和进阶付费课程(自己讲的):《AIGC大模型理论与工业落地实战》-CSDN学院 或者《AIGC大模型理论与工业落地实战》-网易云课堂

LLaMa:

  1. 去掉bias
  2. LayNorm方式:RMSnorm:https://zhuanlan.zhihu.com/p/650231190
    # torch自带LayerNorm
    if self.norm_mode == 'torch_buildin':
    	return F.layer_norm(hidden_states, self.normalized_shape, self.weight, self.bias, self.eps)
    # RMSnorm: t5、大模型系列均使用
    elif self.norm_mode == 'rmsnorm':
    	variance = hidden_states.float().pow(2).mean(-1, keepdim=True)
    	o = (hidden_states.float() * torch.rsqrt(variance + self.eps)).type_as(hidden_states)
    
    1. torch自带LayerNorm (F.layer_norm)
      • 这是PyTorch库中内置的Layer Normalization实现。
      • 输入参数包括:hidden_states(需要归一化的张量)、normalized_shape(归一化的维度,通常是最后一维)、weightbias(可学习的缩放和平移参数)以及eps(为了数值稳定性添加的小常数)。
      • 它首先计算输入在指定维度上的均值和方差,然后使用这些统计量对输入进行归一化,并通过应用可学习的缩放和平移参数来恢复模型的表达能力。
    2. RMSNorm (Root Mean Square Normalization)
      • 只计算输入的方差(即每个元素的平方的平均值),然后通过元素级操作计算归一化后的输出。
      • 具体步骤如下:
        • 计算输入的平方的平均值(variance)。
        • 使用逆平方根(torch.rsqrt())来计算方差的倒数(相当于标准差的倒数)。
        • 将输入与计算出的标准差倒数相乘,得到归一化的结果。

    torch自带的LayerNorm是最完整的实现,包括了可学习的参数;而RMSNorm和自定义LayerNorm则省略了这些参数,可能会牺牲一些模型的表达能力,但在某些情况下可能更简单或更高效。RMSNorm特别适用于那些不需要额外参数的大规模模型。

  3. feedForward不同, 三层全连接
    # 普通bert的FFN:
    self.outDense(self.inter_act_fn(self.interDense(x)))
    # llama、qwen的FFN:
    self.outDense(self.inter_act_fn(self.interDense(x)) * self.interDense2(x))
    
  4. 新增rotary相对位置编码(RoPE)

InternLM:

模型结构: 基本和llama基本一致, 只是各个linear层多了bias; 和Qwen基本一致, 除了o有bias

  1. FeedForward和Llama一致, 三个dense层
  2. 除了qkvo有bias, 其余均没有bias

Qwen:

  1. FeedForward和Llama一致, 三个dense层
  2. 除了qkv有bias, 其余均没有bias
  3. 和InternLM基本一致, 唯一的差别是InternLM的multiHeadAttention.o有bias

ChatGLM

  1. rotary(RoPE)使用的updown+position_encoding_2d

    这是ROPE的二维表达方式,普通的ROPE是一维

  2. qkv合并成一个权重convert时不是concat在一起的
  3. attention_mask的最后一个token仅能访问之前的, 之前的tokens可以互相访问
  4. 跳跃连接有权重设计;embedding之后没有layernorm
# 原始bert,LayerNorm + multiHeadAttention + dropout + FFN + dropout + x+ FFN:其中x来自第一次FFN之后
        x = self.attnLayerNorm(hidden_states, conditional_emb) if self.pre_layernorm else hidden_states  # pre/post layernorm
        self_attn_output = self.multiHeadAttention(x, attention_mask, past_key_value=past_key_value, position_ids=position_ids) 
        residual = x if self.apply_residual_post_layernorm else hidden_states
        hidden_states = self.dropout_add(self_attn_output[0], residual)
        hidden_states = self.attnLayerNorm(hidden_states, conditional_emb) if not self.pre_layernorm else hidden_states
        
        x = self.ffnLayerNorm(hidden_states, conditional_emb) if self.pre_layernorm else hidden_states  # pre/post layernorm
        feedforward_output = self.feedForward(x)
        residual = x if self.apply_residual_post_layernorm else hidden_states
        hidden_states = self.dropout_add(feedforward_output, residual)   # x在这
        hidden_states = self.ffnLayerNorm(hidden_states, conditional_emb) if not self.pre_layernorm else hidden_states
        
# GhatGLM:LayerNorm + multiHeadAttention + alpha*x + FFN + alpha*x:多了一个alpha,其中x来自一开始的LayerNorm之后
        x = self.attnLayerNorm(hidden_states)
        alpha = (2 * self.num_hidden_layers) ** 0.5
        self_attn_output = self.multiHeadAttention(x, attention_mask, past_key_value=past_key_value, **model_kwargs)
        hidden_states = x * alpha + self_attn_output[0]
        x = self.ffnLayerNorm(hidden_states)
        hidden_states = x *alpha +  self.feedForward(x)

ChatGLM2

  1. 不使用Unilm式的mask
    1. flash_attention

      这段代码定义了一个名为flash_attention_forward的函数,用于实现Flash Attention机制。以下是该函数的详细解释:

      1. 函数接受以下参数:

        • query_layer:查询(query)层的输出。
        • key_layer:键(key)层的输出。
        • value_layer:值(value)层的输出。
        • attention_mask:注意力掩码,用于指示哪些位置应该被忽略。
        • query_length:查询序列的长度。
        • softmax_scale:可选的softmax缩放因子。
      2. _get_unpad_data函数用于处理未填充的数据。它计算每个批次中的序列长度、累积和最大序列长度,并返回未填充数据的索引、累积序列长度和最大序列长度。

      3. _upad_input函数负责对输入进行解压和平铺操作。它根据attention_mask计算出需要保留的序列部分,并将查询、键和值层的输出调整为未填充的形状。

      4. dropout变量用于控制dropout概率,如果模型处于训练状态,则使用self.attention_probs_dropout_prob作为概率。

      5. 将查询、键和值层的输出进行转置,以便于后续的矩阵运算。

      6. 根据是否为因果自注意力(即是否为序列到序列任务)和注意力掩码的形状,选择不同的计算方法:

        • 如果不是因果自注意力且注意力掩码为二维(表示仅包含键填充掩码),则使用Flash Attention进行计算。首先调用_upad_input函数对输入进行解压和平铺,然后调用flash_attn_varlen_func函数执行Flash Attention操作,并在计算完成后将结果恢复为原始形状。
        • 如果不是因果自注意力但注意力掩码不符合要求,则发出警告并使用PyTorch内置的注意力计算方法。
        • 如果是因果自注意力,则直接调用flash_attn_func函数执行Flash Attention操作。
      7. 最后,将注意力输出转置回原来的形状并返回。

      总的来说,这段代码实现了Flash Attention机制,通过矩阵分解和局部注意力等技术优化了Transformer模型的计算效率和内存使用效率。在处理非因果自注意力任务时,它支持仅包含键填充掩码的情况,并在其他情况下退回到使用PyTorch内置的注意力计算方法。

    2. multi_query_attention
  • "multi_query_attention"是一种创新的注意力机制设计,它扩展了传统Transformer中的单查询注意力机制。
  • 在Multi-Query Attention中,每个位置的输入可能会生成多个查询向量,这些查询向量可以独立地参与注意力计算,并与键(key)和值(value)矩阵进行交互。
  • 多个查询向量可以捕捉到输入的不同方面或信息源,增强模型的理解和表达能力。例如,一个查询可能关注词汇级别的信息,另一个查询可能关注句法或语义级别的信息。
  • 具体实现可能包括对输入向量进行分解、复制或学习多个查询权重等技术。生成的多个查询向量将分别用于计算注意力分数,并与相应的值向量进行加权求和,得到最终的注意力输出。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐