要懂 transformer 大模型(如 LLM)的基本构造 +关键组件(Attention, FFN, embedding 等)
作为一个顶级部署工程师我们看 Transformer 的视角和算法研究员是完全不一样的。研究员视角数学公式、梯度传播、语义理解能力。工程师视角显存占用Memory、计算密度FLOPS、IO 瓶颈Bandwidth、并行切分点。如果你不懂 Transformer 的内部构造你在做极致优化Kernel Fusion、故障排查NaN Debugging、长文本扩展Long Context时就会束手无策。下面我把 Transformer 的关键组件拆解开告诉你它们在实际工程项目中是怎么用的。1. Attention (注意力机制) -显存杀手与优化核心原理你肯定懂。但在工程中Attention 是最让人头疼的IO 瓶颈和显存瓶颈。实际应用场景1FlashAttention (算子融合)痛点标准 Attention 会产生一个巨大的矩阵Attention Matrix。比如 100k 上下文这个矩阵大到任何显卡都存不下。而且它需要频繁读写显存HBM速度受限于带宽而不是计算能力。应用我们在部署时必须强制开启 FlashAttention-2 或 3。原理应用它利用了 GPU 的 SRAM极快的小缓存把 Q/K/V 切块运算坚决不把那个巨大的矩阵写回显存。如果你不懂 Attention 内部在算什么你就不知道为什么用了 FlashAttn 显存瞬间降了速度快了 5 倍。2KV Cache (PagedAttention / vLLM)痛点每次生成下一个 Token都需要前面所有 Token 的 K 和 V 矩阵。如果每次都重算慢死如果存下来显存爆炸。应用这就是vLLM的核心。原理应用我们知道 K 和 V 是按层存储的张量。vLLM 把这些连续的张量打散像操作系统管理内存页一样PagedAttention存放在显存的不连续空间里。不懂 Attention 需要缓存 K/V你就看不懂 vLLM 的代码。3GQA / MQA (分组查询注意力)场景为什么 Llama-2-70B 推理那么慢而 Llama-3 或 Yi-34B 那么快原理应用传统的 MHA多头注意力是 Q, K, V 头的数量一样多1:1:1。这导致 KV Cache 巨大。工程决策在选型模型时如果是高并发场景我们会优先选支持GQA (Grouped Query Attention)的模型如 Llama-3。因为它让几组 Q 共享一组 K, V直接把KV Cache 的显存占用砍掉了 8 倍甚至更多意味着你能支持的并发量Batch Size大了 8 倍。2. FFN (前馈神经网络 / MLP) -参数大户与并行切分Transformer 结构里Attention 层通常参数不多2/3 的参数量其实都在 FFN 里就是那是两个大的线性层 UpProj, DownProj 激活函数。实际应用场景1张量并行 (Tensor Parallelism, TP) 的切分痛点单卡放不下 72B 模型怎么切原理应用对于 FFN 的第一个矩阵扩维我们将它按列切分Column Parallel。对于 FFN 的第二个矩阵降维我们将它按行切分Row Parallel。工程意义这种切法使得两个 GPU 计算完后结果可以直接相加All-Reduce数学上是等价的。如果你不懂 FFN 是两个矩阵相乘你就没法写 TP 的通信代码也不知道为什么要在那里插入 All-Reduce 算子。2MoE (混合专家模型) - 如 DeepSeek-V2/Mixtral原理应用MoE 本质上就是把 FFN 拆成了 8 个或 64 个小的 FFN专家。工程坑点部署 MoE 时显存通信会变得极其复杂All-to-All 通信。因为不同的 Token 要去不同的卡上找专家。懂了 FFN 的结构你才能优化 MoE 的路由策略防止某些专家负载过高Load Balancing。3. Embedding (嵌入层) Tokenizer -多语言适配与微调坑实际应用场景1词表扩充 (Vocabulary Expansion)场景Llama-3 原生中文能力一般你想让它更懂中文。应用它的 Embedding 层大小原本是128256 x Hidden_Size。我们需要把这个矩阵“拉长”往里面塞入几千个新的中文字符的向量。工程操作修改模型结构的 Embedding 层权重并重新训练这一层。不懂 Embedding 只是一个查表操作Lookup Table你就不知道怎么无损地扩充词表。2多模态对齐 (Multimodal Projector)场景也就是像 GPT-4o 或 LLaVA 那样能看图。原理应用图片经过 Vision Encoder 后出来的向量和文本 Embedding 的维度不一样比如图片是 1024维文本是 4096维。工程我们需要在这中间加一个简单的Linear Projector (也是个 FFN)把图片向量“强行”映射到文本 Embedding 的空间里伪装成文本 Token 喂给 Transformer。4. Positional Encoding (位置编码 / RoPE) -长文本外推实际应用场景1长文本扩展 (Long Context Scaling)场景模型训练时只看了 4k 长度用户非要传 20k 的文档模型直接胡说八道。原因旋转位置编码RoPE没见过那么大的位置索引。工程 Hack我们不需要重新训练模型线性插值 (Linear Scaling)骗模型说“现在的第 20 步其实是第 10 步”。NTK-Aware Scaled RoPE修改推理配置文件config.json调整 RoPE 的base参数。这完全依赖于你对位置编码数学原理的理解。改一个参数模型就能从支持 4k 变成支持 32k虽然精度略降这是部署工程师的高光时刻。5. Layer Norm / RMS Norm -精度溢出的罪魁祸首实际应用场景1FP16 vs BF16 的炸裂问题 (NaN)现象训练好的模型用 FP16 推理时突然输出全屏乱码NaN改用 BF16 就好了。原理很多大模型现在用RMS Norm。在深层网络中激活值可能会变得非常大超过 65504导致 FP16 溢出Overflow。工程排查如果你懂结构你会去检查 Norm 层的输出。解决方案通常是强制把 Norm 层保留为 FP32 计算或者全链路切 BF16。总结这些知识如何变现当你遇到下面这些实际问题时原理知识就是你的武器1、老板问“为什么这个 7B 模型显存只要 14G那个 7B 模型要 20G”原理回答“因为那个模型没用 GQAKV Cache 太大或者是词表Embedding特别大。”2、客户问“为什么输入长了之后速度慢得像蜗牛”原理回答“因为 Attention 是 O(N2)O(N^2)O(N2) 复杂度我们需要开启 FlashAttention 来优化 IO。”3、运维问“怎么把两个 GPU 利用率跑满”原理回答“按照 FFN 列切行切 Attention 头切分的逻辑TP重写 Docker 启动参数。”一句话不懂结构你只能当“API 调用工程师”懂了结构你才是能修改模型手术刀的“模型架构师”。好这才是做工程的态度。打破砂锅问到底把那些故弄玄虚的数学符号扒光看看到底是什么。我用最通俗的大白话配合咱们的实际工作场景把这些概念一个个拆开。1. Q, K, V 和到底是啥别被数学符号吓到这其实就是一个“查字典”或者“搜索引擎”的过程。假设你输入一句话“我爱吃苹果”这里有 5 个字。在模型眼里就是 5输入的字数/Token数。Q (Query - 查询):手里的拿着的字。比如“我”。K (Key - 索引):字典里的目录。比如“我”、“爱”、“吃”、“苹果”。V (Value - 内容):这些字背后的真正含义向量。过程 ():模型想知道“我”这个字和句子里其他的字有什么关系1、拿“我”(Q) 去跟“我”(K) 对一下 - 关系很大 (Score高)。2、拿“我”(Q) 去跟“吃”(K) 对一下 - 我是主语跟动作有关关系也不错。3、拿“我”(Q) 去跟“苹果”(K) 对一下 - 关系一般。(注意力矩阵):每个人都要跟所有人比一次。如果有 5 个字就要比 5×5255 \times 5 255×525 次。如果有1000 个字()就要比 1000×1000100万次。如果有10万 个字(长文本)那就是100亿次计算。这就解释了“为什么输入长了之后速度慢得像蜗牛O(N^2)”因为输入长度翻倍2倍计算量不是翻2倍而是翻4倍2的平方。输入翻10倍计算量翻100倍。这就是的噩梦。2. FlashAttention 怎么开启必须开启 FlashAttention 的原因那个的矩阵 太大了大到显存塞不下而且读写很慢。FlashAttention 的绝招是不把这个巨大的矩阵写到显存里切成小块在计算核心SRAM里偷偷算完。怎么开启实战篇通常不需要你写代码而是在启动命令或安装环境时搞定。场景 A用 vLLM 部署最常见vLLM 默认自动开启。只要你安装了库。# 1. 安装 (环境准备) pip install flash-attn # 2. 启动 vLLM python -m vllm.entrypoints.openai.api_server --model /models/Qwen-72B # vLLM 启动日志会显示 Model uses FlashAttention-2: True如果没装这个库vLLM 会退化到慢速模式你会发现推理速度慢了 5 倍。场景 B用 HuggingFace Transformers 代码加载from transformers import AutoModelForCausalLM # 显式指定使用 flash_attention_2 model AutoModelForCausalLM.from_pretrained( /models/Qwen-72B, device_mapauto, attn_implementationflash_attention_2 # --- 关键就是这一行参数 )3. FFN、TP、All-Reduce 是怎么回事FFN (Feed-Forward Network) 是啥它是模型的“大脑皮层”用来存储知识的。结构很简单输入 - 变宽 (扩维) - 激活 - 变窄 (降维) - 输出。你可以把它想象成一个汉堡包两片面包两个矩阵夹着肉激活函数。TP (张量并行) 是自动的吗要写代码吗不用你写底层 C 通信代码那是 NVIDIA 工程师干的事。但是你需要通过配置参数来“遥控”它。什么是扩维、降维假设输入向量长度是 10。扩维 (Up-Proj):乘以一个大矩阵把它变成长度 40。降维 (Down-Proj):乘以另一个矩阵把它变回长度 10。为什么要这么折腾为了让数据在更高维度里“混合”一下产生智能。TP 是怎么切分工作的通俗版假设我们要算这个巨大的汉堡包只有两张显卡 (GPU A 和 GPU B)。切第一刀 (列切):汉堡的上半部分A 做左边一半B 做右边一半。大家各干各的不用交流。切第二刀 (行切):汉堡的下半部分A 接着处理它的那份B 接着处理它的。All-Reduce (关键时刻):最后一步A 和 B 手里各有一部分结果。All-Reduce就是 A 对 B 喊“把你算的给我”B 对 A 喊“把你算的给我”两人把对方的数据拿过来加在一起得到最终完整结果。这就是为什么需要 NVLink因为这一步大家要疯狂交换数据没有高速通道就卡死了。运维问“怎么把两个 GPU 利用率跑满”你的回答翻译成人话“不用你写代码。你在启动 Docker 或者 vLLM 的时候加上一个参数--tensor-parallel-size 2(简称 TP2)。这就告诉程序要把模型切成两半让两个 GPU 一起通过 All-Reduce 协作干活。如果不加这个参数它默认只用一张卡另一张卡就在旁边看戏利用率 0。”4. MoE (混合专家模型) 和 路由什么是 MoE?以前的模型Dense是全科医生不管是感冒还是骨折所有神经元都要参与计算。MoE (Mixture of Experts) 是专科医院。模型里住了 64 个专家Expert。这个 Token 是“感冒”路由Router把它发给专家 A 和 B。那个 Token 是“骨折”路由把它发给专家 C 和 D。好处模型参数巨大几千亿但每次计算只用一小部分速度极快。我需要写路由策略吗不需要。路由策略是模型训练时定好的写在模型权重里的。作为部署工程师你只需要1、下载支持 MoE 的推理引擎现在的 vLLM, TensorRT-LLM 都支持。2、加载模型如 DeepSeek-V2, Mixtral-8x7B。3、引擎会自动读取模型里的路由权重自己把数据分发给不同的专家。5. 那些“不懂的技术”部署工程师到底该懂啥第三四五条Embedding, Positional, LayerNorm你不需要懂公式但你需要懂故障现象1. Embedding (词表) - 解决“乱码”和“不识字”现象你的模型虽然很牛但如果你让它处理某些特殊的中文行业术语它拆得稀碎。工程应用有时候需要修改tokenizer.json或者微调 Embedding 层把你们公司的专业术语加进去。这个叫“扩充词表”。2. Positional Encoding (RoPE) - 解决“长文本傻掉”现象Llama-2 说它支持 4096 长度。用户输入了 5000 字模型最后生成的全是胡言乱语或者重复的话。工程应用你去改配置文件config.json把rope_scaling这个参数改一下比如改成linear或dynamic。不需要重新训练模型就能勉强支持更长的文本。这就是“改个参数就能跑”的奥秘。3. Layer Norm / BF16 - 解决“NaN 炸裂”现象你买不起 A100用了便宜的 P40 或者 V100 (只支持 FP16)。结果模型跑着跑着报错output is NaN输出不是数。工程应用你得知道这是因为 FP16 的数字范围太小溢出了。解决办法是强制把模型的某些层转成 FP32 跑或者劝老板买支持BF16的新显卡A10, 3090, 4090, A800。BF16 不容易溢出是大模型时代的标配。1. 实战 TP (Tensor Parallelism)手把手配置 vLLM你说“遥控太抽象”那我们就来一次真实的部署操作。项目背景老板给了你一台服务器里面插了4 张 RTX 4090 (24G)。任务是部署Qwen1.5-72B-Chat-Int4通义千问 72B 的量化版。算账72B 的 Int4 模型权重大概占用 40GB 显存。但是推理时还需要 KV Cache为了存上下文如果想支持长文本还需要几十 GB 空间。单张 4090 只有 24G -绝对装不下。两张 4090 有 48G - 勉强装下权重但没空间跑上下文了一跑就 OOMOut of Memory。决策必须用 TP4把模型切分到 4 张卡上。这样总显存 96G权重占 40G还有 50G 可以跑很长的上下文。操作步骤这就是“遥控”我们不写 C 代码我们写 Docker Compose 或者命令行。# 这是一个真实的启动命令 python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen1.5-72B-Chat-Int4 \ --tensor-parallel-size 4 \ # --- 核心参数TP4 --gpu-memory-utilization 0.95 \ # 榨干 95% 的显存 --max-model-len 32768 \ # 支持 32k 上下文 --port 8000当你按下回车后vLLM 内部发生了什么1、vLLM 读取--tensor-parallel-size 4。2、它启动 4 个进程分别控制 GPU 0, 1, 2, 3。3、它加载 Qwen 模型权重。遇到 FFN 层那个大汉堡时它自动把矩阵切成 4 份。GPU 0 拿第 1 份GPU 1 拿第 2 份…4、它在 4 张卡之间建立 NCCL 通信环All-Reduce 通道。5、成功启动。此时你可以看到 4 张卡的显存都被占用了约 12GB权重平分了。如果没有这个参数vLLM 默认 TP1。它试图把 40GB 模型塞进 GPU 0 (24G)。结果CUDA out of memory。程序崩溃。这就是 TP 在项目 中的实际应用用多张小卡拼凑出大显存通过配置参数让框架自动切分模型。2. 现在主流大模型都是 MoE 吗不是。现在的江湖是Dense稠密模型和MoE混合专家分庭抗礼。Dense 模型 (传统派)代表Llama-3 (70B),Qwen1.5/2 (72B),Yi-34B。特点结构简单全是实打实的参数。推理比较慢因为每个字都要算几百亿参数但很稳微调生态好。很多企业私有化部署首选这个因为好维护。MoE 模型 (新贵派)代表DeepSeek-V2,Mixtral-8x7B,Qwen1.5-MoE-A2.7B。特点参数量贼大比如 DeepSeek 有 236B但推理时激活参数少只有 21B。推理速度快也更聪明。趋势今年2024MoE 越来越火因为它的性价比极高同样的智能水平推理成本更低。但它的显存需求通常比较大因为虽然计算少但所有专家的权重得先加载进显存。项目经验如果你的显存够大比如有 A100/H800且追求高并发低延迟首选 MoE。如果显存捉襟见肘或者要搞复杂的微调Dense可能更省心。3. RoPE 与 config.json为什么改个参数就能跑 5000 字为什么输入 5000 字就胡言乱语模型训练时就像在操场上跑步。训练数据大多只有 4096 步长4k。位置编码RoPE负责告诉模型“这是第 1 步那是第 100 步”。RoPE 用了三角函数正弦/余弦来表示位置。训练时它只见过频率范围在一定区间内的数值。当你输入第 5000 个字时位置索引变成了 5000。这个数字超出了 RoPE 在训练时见过的“相位”范围。模型一脸懵逼“没见过这个位置啊这到底是第 5000 步还是第 1 步”周期性函数的混叠效应。于是注意力机制失效模型开始乱猜输出乱码。config.json 哪来的在你下载的模型文件夹里比如/models/Llama-2-7b-chat-hf/config.json。这是模型的“身份证”和“说明书”。为啥改个参数就能勉强支持Linear Scaling 原理我们用一个欺骗战术。假设模型只认识 0 到 4096 的位置。现在来了个 8192 的位置。我们修改配置rope_scaling: {type: linear, factor: 2.0}原理推理时把真实位置 pospospos 除以 2。当位置是 8000 时我们骗模型说“嘿其实这是位置 4000。”当位置是 4000 时我们骗模型说“这是位置 2000。”结果所有输入位置都被压缩回了 0-4096 的区间内。模型觉得“哦这些位置我都认识” 于是它就能正常处理了。代价虽然能跑了但分辨率变低了有点像把高清图压缩了所以精度会稍微下降一点点但比起直接乱码这简直是神技。4. P40 / V100 这种老卡强制转 FP32 可行吗可行但是有代价。硬件支持P40 和 V100 绝对支持 FP32单精度。实际上P40 的 FP32 算力非常强它就是为 FP32 设计的。为什么这么做P40 的 FP16 支持很烂甚至可以说是残废它是 Pascal 架构的老古董。如果你强行用 FP16 跑 Llama大概率溢出 NaN。V100 支持 FP16 很好但不支持 BF16。如果模型训练时用了 BF16 且数值范围很大转成 FP16 可能会溢出。怎么操作解决方案如果是 P40 跑 Llama-3全量 FP32显存占用直接翻倍。FP16 下 7B 模型占 14G。FP32 下 7B 模型占 28G - P40 只有 24G -爆显存跑不了。混合精度Layer-wise Cast这是高手的做法。我们不把整个模型转 FP32。我们只把最容易溢出的那一层通常是最后的 Norm 层或者 Softmax 前的层强制转成 FP32 计算。其他的 FFN、Attention 还是跑 FP16。结果显存增加很少速度稍微慢一点点但 NaN 消失了模型能用了。项目经验现在国内有很多公司买退役的P40 (24G)做推理因为只要 800 块钱一张性价比无敌。如果你要用 P40你必须学会这种 FP32/FP16 混合或者量化的技巧否则这卡就是废铁。但对于V100通常 FP16 就能跑得不错不需要强转 FP32除非遇到极端 NaN 情况。来我们继续死磕这些细节这对你上战场非常有帮助。1.--gpu-memory-utilization 0.95到底是啥意思大白话解释这是 vLLM 的一个**“圈地”指令**。它的意思是“vLLM 启动时请直接把 GPU 上95%的显存一口气全部申请走占为己有。”为什么要这么霸道普通的程序是用多少申请多少。但大模型推理不一样权重 (Weights)是死的占多少是固定的比如 40GB。KV Cache (上下文缓存)是活的。刚开始没用户时它是空的。用户发来 1 万字它瞬间膨胀。如果有 100 个用户同时发它膨胀得更厉害。vLLM 的逻辑是我不等到用户来了再去申请显存那样容易碎片化也慢。我先预先霸占95% 的显存。扣除掉权重占用的那部分比如 40%剩下的所有空间55%全部划分为一个个小格子Block。这些小格子就构成了KV Cache 内存池。以后用户请求来了我就从这个内存池里发牌。池子发完了我就让后面的请求排队。如果你不设这个参数或者设太小比如 0.5vLLM 只占 50%。权重占去 40%只剩下 10% 给 KV Cache。结果只要并发稍微高一点或者文章稍微长一点vLLM 就报错说“没内存池了”尽管显卡上还有一大半空闲显存因为你没让它用。如果你设太大比如 1.0vLLM 试图占 100%。结果显卡驱动、桌面环境、PyTorch 运行时本身也需要一点点显存几百 MB。vLLM 一点余地不留直接导致OOMOut of Memory启动失败。经验值0.9 或 0.95 是最安全的最佳实践。2. 32k 上下文是多少字简单换算公式在中文语境下1 个 Token ≈ 1.5 到 1.8 个汉字取决于模型的分词器 Tokenizer。但在英文语境下1 个 Token ≈ 0.75 个单词。所以32k (32,768) Tokens大概对应纯英文约 2.4 万个单词。纯中文约5 万 到 6 万个汉字。实战感知一部中篇小说。一份非常详尽的几十页财报 PDF。半小时的会议录音转写文本。这些都可以在 32k 窗口内一次性塞给模型处理。3. V100 显卡支持 FP32 吗既然只支持 FP16怎么还能转这里有个误区我们需要澄清一下显卡的**“支持”**到底指什么。事实V100 完美支持 FP32。实际上V100 的 FP32 计算速度非常快15.7 TFLOPS。那为什么大家说“V100 适合 FP16”或者“V100 不支持 BF16”FP32 (单精度 32-bit)V100支持。速度快精度高。缺点占显存大一个数 4 字节算力比 Tensor Core 慢。FP16 (半精度 16-bit)V100支持且极其强悍。V100 里面有专门的Tensor Core电路专门加速 FP16 的矩阵运算。用 FP16 跑速度是 FP32 的8 倍125 TFLOPS。所以大家都在 V100 上用 FP16是为了快。BF16 (BFloat16)V100不支持硬件层面不支持。这是 V100 最大的痛点。A100 才开始支持 BF16。回到你的问题“有的转成 FP32它支持嘛”答案绝对支持而且很稳。场景复现你在 V100 上跑一个模型绝大部分层FFN, Attention走FP16利用 Tensor Core 加速。突然到了LayerNorm这一层数值波动很大FP16 可能会溢出。代码里写了一行layer_norm.float()意思是把这层转 FP32。V100 会怎么做前面的计算用 Tensor Core 飞快地算完 FP16。到了 LayerNormCUDA 核心接手无缝切换到 FP32 模式精准地算出结果虽然比 Tensor Core 慢一点点但因为 LayerNorm 计算量很小整体影响忽略不计。算完后再转回 FP16交给下一层。结论混合精度Mixed Precision就是这么玩的。V100 完全有能力处理 FP32 的部分这甚至是解决 V100 溢出问题的标准手段。转自https://blog.csdn.net/qq_41834780/article/details/155570268
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2545840.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!