作为一个顶级部署工程师,我们看 Transformer 的视角和算法研究员是完全不一样的。
研究员视角:数学公式、梯度传播、语义理解能力。工程师视角:显存占用(Memory)、计算密度(FLOPS)、IO 瓶颈(Bandwidth)、并行切分点。
如果你不懂 Transformer 的内部构造,你在做**极致优化(Kernel Fusion)、故障排查(NaN Debugging)、长文本扩展(Long Context)**时就会束手无策。
下面我把 Transformer 的关键组件拆解开,告诉你它们在实际工程项目中是怎么用的。
1. Attention (注意力机制) -> 显存杀手与优化核心
原理你肯定懂:Q×KT→Score×VQ imes K^T
ightarrow ext{Score} imes VQ×KT→Score×V。
但在工程中,Attention 是最让人头疼的IO 瓶颈和显存瓶颈。
实际应用场景:
FlashAttention (算子融合):
痛点: 标准 Attention 会产生一个巨大的 N×NN imes NN×N 矩阵(Attention Matrix)。比如 100k 上下文,这个矩阵大到任何显卡都存不下。而且它需要频繁读写显存(HBM),速度受限于带宽,而不是计算能力。应用: 我们在部署时,必须强制开启 FlashAttention-2 或 3。原理应用: 它利用了 GPU 的 SRAM(极快的小缓存),把 Q/K/V 切块运算,坚决不把那个巨大的 N×NN imes NN×N 矩阵写回显存。如果你不懂 Attention 内部在算什么,你就不知道为什么用了 FlashAttn 显存瞬间降了,速度快了 5 倍。
KV Cache (PagedAttention / vLLM):
痛点: 每次生成下一个 Token,都需要前面所有 Token 的 K 和 V 矩阵。如果每次都重算,慢死;如果存下来,显存爆炸。应用: 这就是 vLLM 的核心。原理应用: 我们知道 K 和 V 是按层存储的张量。vLLM 把这些连续的张量打散,像操作系统管理内存页一样(PagedAttention)存放在显存的不连续空间里。不懂 Attention 需要缓存 K/V,你就看不懂 vLLM 的代码。
GQA / 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 + 激活函数)。
实际应用场景:
张量并行 (Tensor Parallelism, TP) 的切分:
痛点: 单卡放不下 72B 模型,怎么切?
原理应用:
对于 FFN 的第一个矩阵 AAA(扩维):我们将它按列切分(Column Parallel)。对于 FFN 的第二个矩阵 BBB(降维):我们将它按行切分(Row Parallel)。工程意义: 这种切法使得两个 GPU 计算完后,结果可以直接相加(All-Reduce),数学上是等价的。如果你不懂 FFN 是两个矩阵相乘,你就没法写 TP 的通信代码,也不知道为什么要在那里插入 All-Reduce 算子。
MoE (混合专家模型) – 如 DeepSeek-V2/Mixtral:
原理应用: MoE 本质上就是把 FFN 拆成了 8 个或 64 个小的 FFN(专家)。工程坑点: 部署 MoE 时,显存通信会变得极其复杂(All-to-All 通信)。因为不同的 Token 要去不同的卡上找专家。懂了 FFN 的结构,你才能优化 MoE 的路由策略,防止某些专家负载过高(Load Balancing)。
3. Embedding (嵌入层) & Tokenizer -> 多语言适配与微调坑
实际应用场景:
词表扩充 (Vocabulary Expansion):
场景: Llama-3 原生中文能力一般,你想让它更懂中文。应用: 它的 Embedding 层大小原本是 。我们需要把这个矩阵“拉长”,往里面塞入几千个新的中文字符的向量。工程操作: 修改模型结构的 Embedding 层权重,并重新训练这一层。不懂 Embedding 只是一个查表操作(Lookup Table),你就不知道怎么无损地扩充词表。
128256 x Hidden_Size
多模态对齐 (Multimodal Projector):
场景: 也就是像 GPT-4o 或 LLaVA 那样能看图。原理应用: 图片经过 Vision Encoder 后出来的向量,和文本 Embedding 的维度不一样(比如图片是 1024维,文本是 4096维)。工程: 我们需要在这中间加一个简单的 Linear Projector (也是个 FFN),把图片向量“强行”映射到文本 Embedding 的空间里,伪装成文本 Token 喂给 Transformer。
4. Positional Encoding (位置编码 / RoPE) -> 长文本外推
实际应用场景:
长文本扩展 (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 -> 精度溢出的罪魁祸首
实际应用场景:
FP16 vs BF16 的炸裂问题 (NaN):
现象: 训练好的模型,用 FP16 推理时突然输出全屏乱码(NaN),改用 BF16 就好了。原理: 很多大模型现在用 RMS Norm。在深层网络中,激活值可能会变得非常大(超过 65504),导致 FP16 溢出(Overflow)。工程排查: 如果你懂结构,你会去检查 Norm 层的输出。解决方案通常是强制把 Norm 层保留为 FP32 计算,或者全链路切 BF16。
总结:这些知识如何变现?
当你遇到下面这些实际问题时,原理知识就是你的武器:
老板问:“为什么这个 7B 模型显存只要 14G,那个 7B 模型要 20G?”
原理回答:“因为那个模型没用 GQA,KV Cache 太大,或者是词表(Embedding)特别大。”
客户问:“为什么输入长了之后,速度慢得像蜗牛?”
原理回答:“因为 Attention 是 O(N2)O(N^2)O(N2) 复杂度,我们需要开启 FlashAttention 来优化 IO。”
运维问:“怎么把两个 GPU 利用率跑满?”
原理回答:“按照 FFN 列切行切 + Attention 头切分的逻辑(TP),重写 Docker 启动参数。”
一句话:不懂结构,你只能当“API 调用工程师”;懂了结构,你才是能修改模型手术刀的“模型架构师”。
好!这才是做工程的态度。打破砂锅问到底,把那些故弄玄虚的数学符号扒光,看看到底是什么。
我用最通俗的大白话,配合咱们的实际工作场景,把这些概念一个个拆开。
1. Q, K, V 和 N×NN imes NN×N 到底是啥?
别被数学符号吓到,这其实就是一个**“查字典”或者“搜索引擎”**的过程。
假设你输入一句话:“我爱吃苹果”(这里有 5 个字)。
在模型眼里,NNN 就是 5(输入的字数/Token数)。
Q (Query – 查询): 手里的拿着的字。比如“我”。K (Key – 索引): 字典里的目录。比如“我”、“爱”、“吃”、“苹果”。V (Value – 内容): 这些字背后的真正含义(向量)。
过程 (Q×KTQ imes K^TQ×KT):
模型想知道:“我”这个字,和句子里其他的字有什么关系?
拿“我”(Q) 去跟“我”(K) 对一下 -> 关系很大 (Score高)。拿“我”(Q) 去跟“吃”(K) 对一下 -> 我是主语,跟动作有关,关系也不错。拿“我”(Q) 去跟“苹果”(K) 对一下 -> 关系一般。
N×NN imes NN×N (注意力矩阵):
每个人都要跟所有人比一次。
如果有 5 个字,就要比 5×5=255 imes 5 = 255×5=25 次。
如果有 1000 个字 (N=1000N=1000N=1000),就要比 1000×1000=100万次1000 imes 1000 = extbf{100万次}1000×1000=100万次。
如果有 10万 个字 (长文本),那就是 100亿次 计算。
这就解释了“为什么输入长了之后,速度慢得像蜗牛(O(N^2))”:
因为输入长度翻倍(2倍),计算量不是翻2倍,而是翻 4倍(2的平方)。输入翻10倍,计算量翻100倍。这就是 O(N2)O(N^2)O(N2) 的噩梦。
2. FlashAttention 怎么开启?
必须开启 FlashAttention 的原因: 那个 N×NN imes NN×N 的矩阵太大了,大到显存塞不下,而且读写很慢。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_map="auto",
attn_implementation="flash_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 的时候,加上一个参数 (简称 TP=2)。这就告诉程序,要把模型切成两半,让两个 GPU 一起通过 All-Reduce 协作干活。如果不加这个参数,它默认只用一张卡,另一张卡就在旁边看戏(利用率 0)。”
--tensor-parallel-size 2
4. MoE (混合专家模型) 和 路由
什么是 MoE?
以前的模型(Dense)是全科医生,不管是感冒还是骨折,所有神经元都要参与计算。
MoE (Mixture of Experts) 是专科医院。模型里住了 64 个专家(Expert)。
这个 Token 是“感冒”,路由(Router)把它发给专家 A 和 B。那个 Token 是“骨折”,路由把它发给专家 C 和 D。
好处: 模型参数巨大(几千亿),但每次计算只用一小部分,速度极快。
我需要写路由策略吗?
不需要。 路由策略是模型训练时定好的(写在模型权重里的)。
作为部署工程师,你只需要:
下载支持 MoE 的推理引擎(现在的 vLLM, TensorRT-LLM 都支持)。加载模型(如 DeepSeek-V2, Mixtral-8x7B)。引擎会自动读取模型里的路由权重,自己把数据分发给不同的专家。
5. 那些“不懂的技术”:部署工程师到底该懂啥?
第三四五条(Embedding, Positional, LayerNorm),你不需要懂公式,但你需要懂故障现象:
1. Embedding (词表) -> 解决“乱码”和“不识字”
现象: 你的模型虽然很牛,但如果你让它处理某些特殊的中文行业术语,它拆得稀碎。工程应用: 有时候需要修改 或者微调 Embedding 层,把你们公司的专业术语加进去。这个叫“扩充词表”。
tokenizer.json
2. Positional Encoding (RoPE) -> 解决“长文本傻掉”
现象: Llama-2 说它支持 4096 长度。用户输入了 5000 字,模型最后生成的全是胡言乱语或者重复的话。工程应用: 你去改配置文件 ,把
config.json 这个参数改一下(比如改成
rope_scaling 或
linear)。不需要重新训练,模型就能勉强支持更长的文本。这就是“改个参数就能跑”的奥秘。
dynamic
3. Layer Norm / BF16 -> 解决“NaN 炸裂”
现象: 你买不起 A100,用了便宜的 P40 或者 V100 (只支持 FP16)。结果模型跑着跑着报错 (输出不是数)。工程应用: 你得知道这是因为 FP16 的数字范围太小,溢出了。解决办法是强制把模型的某些层转成 FP32 跑,或者劝老板买支持 BF16 的新显卡(A10, 3090, 4090, A800)。BF16 不容易溢出,是大模型时代的标配。
output is NaN
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 -> 勉强装下权重,但没空间跑上下文了,一跑就 OOM(Out of Memory)。决策: 必须用 TP=4,把模型切分到 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 # <--- 核心参数!TP=4
--gpu-memory-utilization 0.95 # 榨干 95% 的显存
--max-model-len 32768 # 支持 32k 上下文
--port 8000
当你按下回车后,vLLM 内部发生了什么?
vLLM 读取 。
--tensor-parallel-size 4
它启动 4 个进程,分别控制 GPU 0, 1, 2, 3。
它加载 Qwen 模型权重。遇到 FFN 层(那个大汉堡)时,它自动把矩阵切成 4 份。
GPU 0 拿第 1 份,GPU 1 拿第 2 份…
它在 4 张卡之间建立 NCCL 通信环(All-Reduce 通道)。
成功启动。 此时你可以看到 4 张卡的显存都被占用了约 12GB(权重平分了)。
如果没有这个参数?
vLLM 默认 TP=1。它试图把 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)。推理速度快,也更聪明。趋势:今年(2024)MoE 越来越火,因为它的性价比极高(同样的智能水平,推理成本更低)。但它的显存需求通常比较大(因为虽然计算少,但所有专家的权重得先加载进显存)。
项目经验: 如果你的显存够大(比如有 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 到底是啥意思?
--gpu-memory-utilization 0.95
大白话解释:
这是 vLLM 的一个**“圈地”指令**。它的意思是:“vLLM 启动时,请直接把 GPU 上 95% 的显存一口气全部申请走,占为己有。”
为什么要这么霸道?
普通的程序是用多少申请多少。但大模型推理不一样:
权重 (Weights) 是死的,占多少是固定的(比如 40GB)。
KV Cache (上下文缓存) 是活的。
刚开始没用户时,它是空的。用户发来 1 万字,它瞬间膨胀。如果有 100 个用户同时发,它膨胀得更厉害。
vLLM 的逻辑是:
我不等到用户来了再去申请显存(那样容易碎片化,也慢)。我先预先霸占 95% 的显存。扣除掉权重占用的那部分(比如 40%),剩下的所有空间(55%)全部划分为一个个小格子(Block)。这些小格子就构成了 KV Cache 内存池。以后用户请求来了,我就从这个内存池里发牌。池子发完了,我就让后面的请求排队。
如果你不设这个参数(或者设太小,比如 0.5):
vLLM 只占 50%。权重占去 40%,只剩下 10% 给 KV Cache。结果:只要并发稍微高一点,或者文章稍微长一点,vLLM 就报错说“没内存池了”,尽管显卡上还有一大半空闲显存(因为你没让它用)。
如果你设太大(比如 1.0):
vLLM 试图占 100%。结果:显卡驱动、桌面环境、PyTorch 运行时本身也需要一点点显存(几百 MB)。vLLM 一点余地不留,直接导致 OOM(Out 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 可能会溢出。
代码里写了一行:(意思是把这层转 FP32)。
layer_norm.float()
V100 会怎么做?
前面的计算用 Tensor Core 飞快地算完 FP16。到了 LayerNorm,CUDA 核心接手,无缝切换到 FP32 模式,精准地算出结果(虽然比 Tensor Core 慢一点点,但因为 LayerNorm 计算量很小,整体影响忽略不计)。算完后,再转回 FP16,交给下一层。
结论:
混合精度(Mixed Precision) 就是这么玩的。V100 完全有能力处理 FP32 的部分,这甚至是解决 V100 溢出问题的标准手段。