quantization techniques

大模型量化技术全景:从 INT8 到 GPTQ 与 AWQ

1. 量化的动机与基础 大模型的推理成本主要由显存和计算量决定。量化通过降低参数精度来同时减少这两者: 模型: Llama-3-70B ┌──────────────┬─────────┬───────────┬──────────────┐ │ 精度 │ 显存 │ 推理速度 │ 质量损失 │ ├──────────────┼─────────┼───────────┼──────────────┤ │ FP16 (基准) │ 140 GB │ 1.0x │ 0% │ │ INT8 │ 70 GB │ 1.8x │ <0.5% │ │ INT4 │ 35 GB │ 2.5x │ 1-2% │ │ INT2 │ 18 GB │ 3.0x │ 5-15% │ └──────────────┴─────────┴───────────┴──────────────┘ 1.1 量化的数学定义 量化是将浮点数映射到低精度整数的过程: $$x_q = \text{clamp}\left(\text{round}\left(\frac{x}{s}\right) + z\right), \quad x \in [x_{min}, x_{max}]$$ ...

2026-06-25 · 8 min · 1697 words · AI 实战派
training stability

大模型训练稳定性:梯度爆炸、Loss Spike 与恢复策略

1. 大模型训练稳定性的挑战 大语言模型的训练是大规模并行计算中的"走钢丝"——数千亿参数、万亿 token、数百 GPU 协同工作,任何微小的数值不稳定都可能在迭代中放大为灾难性的训练崩溃。 训练不稳定性事件案例: ┌──────────────────┬─────────────────────────────────────────┐ │ 模型 │ 不稳定事件 │ ├──────────────────┼─────────────────────────────────────────┤ │ GPT-3 175B │ 多次 Loss Spike, 部分无法恢复 │ │ OPT 175B │ 训练崩溃 10+ 次, 需要回退检查点 │ │ BLOOM 176B │ 梯度爆炸导致 NaN, 引入更多稳定化手段 │ │ LLaMA-2 70B │ 7次显著 Loss Spike, 4次需手动干预 │ │ GLM-130B │ Embedding 层梯度爆炸, 引入 DeepNorm │ └──────────────────┴─────────────────────────────────────────┘ 训练不稳定的主要表现形式: Loss Spike:损失突然飙升(10x-1000x),可能恢复也可能永久发散 梯度爆炸:梯度范数急剧增大,权重更新过大 梯度消失:梯度范数趋近于零,训练停滞 NaN/Inf:数值溢出,训练完全崩溃 2. 梯度爆炸与消失 2.1 根本原因 # 梯度爆炸/消失的理论分析 # 在深度网络中, 梯度通过链式法则传播: # ∂L/∂x_0 = ∂L/∂x_n · ∏(i=1 to n) ∂x_i/∂x_{i-1} # 如果每层的雅可比矩阵的谱半径 > 1: 梯度爆炸 # 如果每层的雅可比矩阵的谱半径 < 1: 梯度消失 def analyze_gradient_flow(model, input_data): """分析各层梯度流""" layer_grad_norms = {} for name, param in model.named_parameters(): if param.grad is not None: layer_grad_norms[name] = param.grad.norm().item() # 按层分组统计 return layer_grad_norms # 典型的梯度分布 (100层Transformer): # Layer 0 (输入): grad_norm = 0.001 ← 梯度消失 # Layer 50 (中间): grad_norm = 0.5 # Layer 99 (输出): grad_norm = 12.3 ← 梯度较大 2.2 残差连接的作用 残差连接是缓解梯度消失的最重要设计: ...

2026-06-25 · 9 min · 1879 words · AI 实战派
continuous batching

连续批处理:vLLM 高吞吐推理的核心技术

静态批处理的瓶颈 传统 LLM 推理服务采用静态批处理:每个批次固定大小,所有请求必须等最长的那个请求完成后才能返回。 静态批处理示例(batch=4): Request A: [tok][tok][tok][tok][EOS] # 生成 5 个 token Request B: [tok][tok][tok][tok][tok][tok][EOS] # 生成 7 个 token Request C: [tok][tok][tok][EOS] # 生成 4 个 token Request D: [tok][tok][tok][tok][tok][tok][tok][tok][EOS] # 生成 9 个 token 批处理完成时间:等待 D 完成(9 个 token) 实际计算:A 只需要 5 步,但等了 9 步 GPU 利用率:≈ 25/36 = 69%(因为要等待最慢的请求) 问题: 首 token 延迟:新请求必须等当前批次完成才能进入 GPU 闲置:短请求完成后,GPU 资源被浪费 吞吐低下:无法动态调度,无法"边生成边接收" 连续批处理原理 连续批处理(Continuous Batching, Orca, 2023)的核心思想是:动态管理批次,随时加入新请求,随时移除已完成的请求。 ...

2026-06-25 · 6 min · 1080 words · AI 实战派
tokenizer special tokens

特殊标记设计:从 [CLS] 到 ChatML 的 Prompt 格式演化

1. 特殊标记的角色与意义 特殊标记(Special Tokens)是 Tokenizer 词表中预留的、具有特定语义功能的标记。它们不属于自然语言,却承担着结构化组织模型输入输出的关键职责。 特殊标记的核心功能: 1. 序列边界标记: [BOS] [EOS] - 标识序列开始/结束 2. 任务分隔标记: [SEP] - 分隔不同输入段 3. 分类聚合标记: [CLS] - 聚合全局语义 4. 填充对齐标记: [PAD] - 批处理中对齐长度 5. 未知词标记: [UNK] - 处理未登录词 6. 掩码标记: [MASK] - 遮蔽语言模型训练 7. 角色标记: <|im_start|> - 标识对话角色 8. 系统标记: <|system|> - 系统指令 1.1 特殊标记的 Token ID 约定 # 不同模型系列的特殊标记 ID 对比 special_token_ids = { "BERT": { "[PAD]": 0, "[UNK]": 1, "[CLS]": 2, "[SEP]": 3, "[MASK]": 103, }, "GPT-2": { "<|endoftext|>": 50256, # 单一结束标记 }, "LLaMA": { "<unk>": 0, "<s>": 1, "</s>": 2, "<pad>": 32000, }, "Llama-3": { "<|begin_of_text|>": 128000, "<|end_of_text|>": 128001, "<|start_header_id|>": 128006, "<|end_header_id|>": 128007, "<|eot_id|>": 128009, }, "ChatML (OpenAI)": { "<|im_start|>": 100264, "<|im_end|>": 100265, "<|endoftext|>": 100257, }, } 2. BERT 时代:分类与分隔 2.1 [CLS] 和 [SEP] 的设计 BERT 使用 [CLS] 和 [SEP] 来组织输入格式: ...

2026-06-25 · 7 min · 1429 words · AI 实战派
speculative decoding explained

投机解码深度解析:LLM 推理速度翻倍的秘密

自回归解码的瓶颈 标准 LLM 推理是自回归的:每次只生成一个 token,然后把它拼回输入再生成下一个。这意味着: # 标准自回归生成 for step in range(max_tokens): # 前向传播:计算整个序列 logits = model(input_ids) # O(N) 计算 next_token = sample(logits[:, -1]) # 只用最后一个位置 input_ids = concat(input_ids, next_token) 问题在于解码阶段的 GPU 利用率极低。即使 batch_size=1,生成单个 token 也需要加载全部模型权重,但只产生 1 个 token 的输出。这就是所谓的 memory-bound(内存带宽瓶颈)。 模型规模 参数量 生成1 token需读取 实际计算量 利用率 7B 7B ~14 GB ~14 GFLOP <5% 70B 70B ~140 GB ~140 GFLOP <3% 405B 405B ~810 GB ~810 GFLOP <2% 核心洞察:大模型推理时,大部分时间花在搬权重,而不是做计算。如果能一次性预测多个 token,就能摊薄权重加载成本。 ...

2026-06-25 · 5 min · 917 words · AI 实战派
positional encoding

位置编码深度解析:从绝对位置到 RoPE 与 ALiBi

1. 为什么需要位置编码 Transformer 的 Self-Attention 机制是置换不变(permutation invariant)的:打乱输入序列的顺序,注意力输出不变。这意味着 Transformer 本身没有序列顺序的概念。 位置编码的使命就是:为模型注入位置信息。 没有位置编码: Attention("猫追狗") == Attention("狗追猫") # 灾难! 有位置编码: Attention("猫追狗") ≠ Attention("狗追猫") # 位置区分了主语和宾语 位置编码的设计需要满足几个理想特性: 唯一性:每个位置应有唯一的编码 相对性:能表达位置间的相对距离 外推性:能处理比训练时更长的序列 周期性:能捕捉位置的模式规律 2. 绝对位置编码 2.1 Sinusoidal(正弦余弦)位置编码 Vaswani 等人 (2017) 提出的正弦余弦位置编码: $$PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right)$$ $$PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right)$$ import torch import torch.nn as nn import math class SinusoidalPositionalEncoding(nn.Module): def __init__(self, d_model=512, max_len=5000): super().__init__() pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) # 频率: 1 / (10000^(2i/d)) div_term = torch.exp( torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model) ) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) pe = pe.unsqueeze(0) # (1, max_len, d_model) self.register_buffer('pe', pe) def forward(self, x): # x: (batch, seq_len, d_model) return x + self.pe[:, :x.size(1), :] 关键性质:对于固定的偏移 $k$,$PE_{pos+k}$ 可以表示为 $PE_{pos}$ 的线性函数: ...

2026-06-25 · 8 min · 1683 words · AI 实战派
attention mechanism evolution

注意力机制演进史:从 Bahdanau 到 Flash Attention 3

1. 注意力机制的起源:Bahdanau Attention (2014) 注意力机制的故事始于机器翻译。2014 年,Bahdanau 等人提出了 Additive Attention,解决了 seq2seq 模型中固定长度编码瓶颈的问题。 核心思想:解码器的每一步不再只依赖一个固定的上下文向量,而是"关注"源序列的不同部分。 # Bahdanau Additive Attention 的 PyTorch 实现 class BahdanauAttention(nn.Module): def __init__(self, hidden_size): super().__init__() self.W_query = nn.Linear(hidden_size, hidden_size, bias=False) self.W_key = nn.Linear(hidden_size, hidden_size, bias=False) self.V = nn.Linear(hidden_size, 1, bias=False) def forward(self, query, keys, values): """ query: (batch, hidden) - 解码器当前状态 keys: (batch, src_len, hidden) - 编码器所有隐状态 values: 同 keys """ # 扩展 query 以与 keys 对齐 query_expanded = query.unsqueeze(1) # (batch, 1, hidden) # 加性注意力: score = V^T * tanh(W_q * q + W_k * k) scores = self.V( torch.tanh( self.W_query(query_expanded) + self.W_key(keys) ) ) # (batch, src_len, 1) scores = scores.squeeze(-1) # (batch, src_len) # 注意力权重 attn_weights = F.softmax(scores, dim=-1) # (batch, src_len) # 加权求和 context = torch.bmm(attn_weights.unsqueeze(1), values) # (batch, 1, hidden) context = context.squeeze(1) # (batch, hidden) return context, attn_weights 1.1 Luong Attention (2015) Luong 提出了多种变体,其中 Dot-Product Attention 影响最为深远: ...

2026-06-25 · 7 min · 1453 words · AI 实战派
attention mechanism explained

Attention 机制详解:从 Self-Attention 到 Multi-Query Attention

1. Attention 的本质 Attention 机制的核心思想:给定一个查询(Query),在一系列键值对(Key-Value pairs)中计算相关性权重,然后对值(Value)加权求和,得到输出。 2. Scaled Dot-Product Attention 2.1 公式推导 给定查询矩阵 $Q \in \mathbb{R}^{n \times d_k}$、键矩阵 $K \in \mathbb{R}^{m \times d_k}$、值矩阵 $V \in \mathbb{R}^{m \times d_v}$: $$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$ 为什么要除以 $\sqrt{d_k}$? 当 $d_k$ 较大时,$QK^T$ 的点积值会变大,导致 softmax 梯度趋近于 0(饱和区)。假设 $q$ 和 $k$ 的分量是均值为 0、方差为 1 的独立随机变量,则点积 $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$ 的均值为 0、方差为 $d_k$。除以 $\sqrt{d_k}$ 将方差归一化为 1。 2.2 计算流程 import torch import torch.nn.functional as F import math def scaled_dot_product_attention(Q, K, V, mask=None): """ Q: (batch, n_heads, seq_len, d_k) K: (batch, n_heads, seq_len, d_k) V: (batch, n_heads, seq_len, d_v) """ d_k = Q.size(-1) scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k) if mask is not None: scores = scores.masked_fill(mask == 0, float('-inf')) attn_weights = F.softmax(scores, dim=-1) output = torch.matmul(attn_weights, V) return output, attn_weights 2.3 为什么用点积而不是加性 Attention? 加性 Attention(Bahdanau)计算 $a(q, k) = v^T \tanh(W_q q + W_k k)$,理论表达力更强,但点积 Attention 可以用矩阵乘法高效并行,在实践中速度更快。当 $d_k$ 较小时两者性能接近,$d_k$ 大时点积配合缩放因子更优。 ...

2026-06-25 · 4 min · 662 words · AI 实战派
flash attention 3 guide

Flash Attention 3 指南:GPU IO 优化的终极武器

为什么需要 Flash Attention? 标准 Attention 的瓶颈不在计算,而在显存 IO。考虑 $n = 8192$, $d = 128$ 的注意力计算: 操作 HBM 读写量 SRAM 计算 $S = QK^T$ 读 Q(8MB) + 读 K(8MB) + 写 S(512MB) ~16 GFLOPs $P = \text{softmax}(S)$ 读 S(512MB) + 写 P(512MB) ~8 MFLOPs $O = PV$ 读 P(512MB) + 读 V(8MB) + 写 O(8MB) ~16 GFLOPs 总计 ~1.6 GB ~32 GFLOPs A100 的 HBM 带宽 2 TB/s,SRAM 带宽 19 TB/s。计算只需 ~0.02ms,但 IO 需要 ~0.8ms——IO 是瓶颈的 40 倍。 ...

2026-06-25 · 5 min · 924 words · AI 实战派
layer normalization deep

LayerNorm vs RMSNorm:Transformer 归一化的选择

1. 归一化的必要性 深层神经网络中,各层输入的分布会随训练过程不断变化(Internal Covariate Shift),导致:训练不稳定、梯度消失/爆炸、收敛慢。归一化通过将激活值约束到合理范围来缓解这些问题。 2. Batch Normalization 为何不适用于 NLP? BatchNorm 在 CV 中广泛使用,但在 NLP 中效果差: 变长序列:不同序列长度不同,Batch 维度统计不一致 推理与训练不一致:训练用 batch 统计量,推理用移动平均,序列长度变化时偏差大 batch size 敏感:NLP 训练常用小 batch + 梯度累积,BatchNorm 统计量不可靠 3. Layer Normalization 3.1 公式 对每个样本的每个 Token 独立归一化,沿特征维度计算统计量: $$ \mu = \frac{1}{d} \sum_{i=1}^{d} x_i $$ $$ \sigma^2 = \frac{1}{d} \sum_{i=1}^{d} (x_i - \mu)^2 $$ $$ \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} $$ $$ y_i = \gamma_i \hat{x}_i + \beta_i $$ ...

2026-06-25 · 3 min · 474 words · AI 实战派
鲁ICP备2026018361号