当时可以听到一点水被打到的声音,感觉应该是风扇和水有接触了,但是没有自己断电,我就长按电源键关机了。后面观察了一下左边转轴有点水雾,应该是风扇吹上去的,麻了。

关机吹了一会儿,从键盘里面吹出来一点水,每次吹出来一点我就擦掉,一擦就按到键盘就开机,我真的是服了。

但是现在开机了一点事都没有,键盘测试了也是每个键都可以用,还需要担心吗?😭

😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭

因为个人用的是 Java 的自研 agent ,官方又没提供 java 相关的 sdk 。因此顺手根据官方 npm 包和文档封装了一下,欢迎使用。
二合一的 SDK:

  1. 基于 WebSocket 长连接协议的企业微信 AI Bot Java SDK
  2. 基于二维码登录、长轮询、CDN 协议封装的微信 ClawBOT Java SDK

具体见:
https://github.com/CloudSen/wx-aibot-java-sdk

https://gitee.com/CloudSen/wx-aibot-java-sdk

做了套边缘计算数据采集方案,想和你聊聊后续方向, 请指教一下

https://github.com/anviod/edgex

在工厂和工业现场待久了,总能遇到一些让人头疼的事:车间里设备种类多,协议又杂,数据采集常常不稳定,要么延迟高,要么经常断。我做边缘计算的,每天跟着现场工程师跑,看他们调设备、查故障,慢慢就想:能不能做套更靠谱的采集方案?

于是我们花了一些时间,搞出了这套南向采集优化方案。它没什么花里胡哨的概念,就是想解决实际问题:

  • 不管是 Modbus 、BACnet 还是 OPC UA 、S7 ,一套系统就能管起来,不用再为不同设备单独配方案。
  • 设备状态好的时候多采点数据,网络不稳定时就少发点请求,让系统自己适应现场情况。
  • 哪个点位老出问题,系统会自动放缓采集频率,等它恢复了再正常采,省得一直发无效请求占资源。
  • 同一总线上的设备一起不间断轮询采集,减少来回通信的等待次数,效率更高。
  • 数据在边缘侧先处理一下,不用全往云端发,响应更快,流量也省。

没搞什么复杂的技术名词,就是把现场遇到的问题一个个解决:网络抖动了,就根据响应时间自动调超时;传大数据慢了,就自动找最合适的传输单元;设备坏了要换,新设备接上配置相同的 IP 就能自动同步配置,不用人手动搞。

现在这套方案已经在几个工厂试过了,工程师反馈说延迟降了,稳定性高了,维护起来也省心。做这个的初衷,就是想让搞工业物联网的朋友们少点麻烦,多点踏实。

如果你也在为设备数据采集发愁,欢迎聊聊,说不定能帮上忙。也想听听你的场景,一起把这套方案做得更实用。

说出来真的很糟心,今天儿子两周岁,我和老婆上午带儿子出去户外游乐场玩,中午外面吃自助餐,下午比较热就在家里,请儿子吃糖水,晚上吃蛋糕,反正让儿子过了个开心的生日


但是等了一天,儿子的爷爷奶奶和姑姑,一点表示都没有,连条信息都没有,说实话真得有点寒心,儿子姑姑就不说了,但是连爷爷奶奶也这样,其实并不是想要他们什么,发个 200 红包对于我可有可无,而且我每个月还给二老打 2000 生活费,但是这样做真的让人寒心

所以以后儿子跟他们不亲,那真的是自找的,自己唯一的孙子都不关心,疼爱止于表面话上,没有一点实际行动,不亲近也是正常的

基于 EcoPaste 项目二次开发&开源了一个增强剪贴板&截图功能的小工具 WeCut ,原有项目是个比较方便的剪贴板管理工具,我们在原版工具的基础上增加了:

  1. 剪贴板历史查看器的 Dock 样式(参考 Paste 的样式)
  2. 增加截图功能,包括:
    1. 按选区、应用窗口截图
    2. 截图标注、增加文字或马赛克
    3. 截图 OCR
    4. 截图钉在屏幕顶部
  3. 剪贴板和截图内容可以直接发送到 Wegent AI 的待办收件箱
  4. Wegent 里对应的强化了“待办”收件箱,支持了收件箱自动化功能,可以把收到的内容转发给智能体,或者订阅器。

本质上是为了第三个功能包了盘饺子,通过 WeCut 和 Wegent 结合,可以实现:

  1. 屏幕上出现的任何东西可以直接发到 Wegent 里,后续可以当做向 AI 提问的素材
  2. 在设置自动处理以后,可以直接对剪贴板或者屏幕截图提问
  3. 让 AI 对剪贴板或者屏幕截图的内容进行分析、补充、归类和处理,在 Wegent 知识库里实现 karpathy 的 llm-wiki 流程
  4. 配置自动执行智能体,直接基于剪贴板或截图开始处理任务

目前只跑通了最基本的流程,后面一段时间会对细节做一些优化,欢迎试用。

项目地址:
github.com/wecode-ai/wecut
github.com/wecode-ai/wegent

原帖: https://www.v2ex.com/t/1109072?p=3

现状:刚分开搬走了,很痛苦,真的很不舍,她说等我找不到新的就回去找她。她也有可能会妥协

生活中我承担了大大部分的家务,我觉得再不结婚,对我实在是一个很大的消耗,所以年前我就开始在加速推进这个事情,但是结婚就要生小孩。她很想继续在一起,但是就是害怕要小孩。

最新条件:20 个彩礼不包 3 金,要写一个协议(大概就是无论怎么样这笔钱都不能动,不能拿来买房买车装修奶粉啥的,她的保障) 房子加名

双方最新条件:女方存款( 90W )她说不会操作成婚前财产的
她父亲没有陪嫁(原话是以后买房会支持我们的),彩礼我这边也是自己出
我(存 30W+全款的老房子)要求婚后工资上交
工资上我又涨了一些 她没变 两个人总的税后 55W 吧

我没同意她的婚前协议,我说彩礼本身就法律就保护了你的权益。
我给的条件是 20 全包,你的存款给你爸存着就行,操作成婚前财产也行,房子不加名。
没结果,崩了。
分别那天她说她挣够钱了,有安全感了回来找我,我问多少算够,她无言以对。

搬走那天又聊了一些,她的这些要求可能因为原生家庭导致的(很复杂,大概就是因为父母都有恶习,后面父改,母再犯而离婚,也因为离婚没有牵连,所以重视协议明),在一个就孩子,觉得生养孩子降低生活质量且要好多钱,我们后面工作不稳定,没有安全感。

这两天非常痛苦,十分痛苦,分别前抱头痛哭。唉。
抱歉,说的有些乱,结合起来看应该能理解

引言

在软件开发和系统运维过程中,日志分析一直是开发者最头疼的问题之一。面对海量的日志信息,如何快速定位问题根因?传统的日志分析方法往往效率低下,需要大量人工排查。

今天为大家介绍一款强大的AI日志分析工具 - Pangu RCA(RCA Agent Portal),它能够智能分析日志,快速定位问题根因,提供专业的修复建议。

工具特色

🤖 AI智能分析

Pangu RCA基于先进的AI技术,能够:

  • 自动理解各种编程语言的日志格式
  • 智能提取关键错误信息
  • 分析错误模式和上下文关系
  • 提供精准的根因诊断

🌍 多语言支持

支持主流编程语言的日志分析:

  • Java: 包括Spring Boot、Tomcat等框架日志
  • Python: Django、Flask、FastAPI等框架日志
  • Go: Gin、Echo等框架日志
  • Rust: 标准库和常见框架日志
  • JavaScript/Node.js: Express、NestJS等框架日志

⚡ 快速定位

  • 实时分析: 粘贴日志即可立即分析
  • 置信度评分: 为每个分析结果提供可信度评估
  • 修复建议: 提供具体的解决方案和修复步骤

使用方法

  1. 访问: https://www.pangulab.com
  2. 选择语言(中文/English)
  3. 粘贴您的应用程序日志
  4. 点击"开始分析"
  5. 查看AI提供的分析结果

适用场景

开发者调试

// 示例:Java应用错误日志
2024-01-15 10:30:25 ERROR com.example.UserService - Database connection failed
java.sql.SQLException: Connection timeout
    at com.example.DatabaseManager.getConnection(DatabaseManager.java:45)

AI分析结果:

  • 问题类型: 数据库连接超时
  • 置信度: 95%
  • 建议修复: 检查数据库配置,增加连接池设置

运维监控

系统日志、容器日志、API调用日志等各种场景,Pangu RCA都能提供智能分析。

技术优势

  • 无需安装: 纯Web应用,随时随地使用
  • 完全免费: 目前提供所有功能免费使用
  • 隐私保护: 日志分析在本地进行,不上传敏感信息
  • 响应迅速: 基于云端AI,分析速度快

未来规划

我们计划在未来版本中增加更多功能:

  • 📊 批量分析: 支持同时分析多个日志文件
  • 📈 趋势分析: 分析日志变化趋势
  • 🔗 API集成: 提供API接口供第三方系统集成
  • 📱 移动端: 开发移动端应用

结语

Pangu RCA正在改变开发者处理日志的方式,让日志分析变得智能化、高效化。如果您是开发者或运维人员,不妨试试这个强大的工具。

立即体验: https://www.pangulab.com


有任何问题或建议,欢迎通过邮件联系: r00txxyyzz@gmail.com

必知必会:大模型训练显存计算与优化详解

AI-Compass 致力于构建最全面、最实用、最前沿的AI技术学习和实践生态,通过六大核心模块的系统化组织,为不同层次的学习者和开发者提供完整学习路径。

🌟 如果本项目对您有所帮助,请为我们点亮一颗星!🌟


1. 显存消耗的组成与模型权重计算

<!-- trick-image:start idx=1 platform=blog -->
显存组成与量化精度对比示意图
<!-- trick-image:end idx=1 -->

1.1 核心问题

大模型训练时显存被什么占满了?不同量化精度下模型权重需要多少显存?

1.2 原文核心要点

深度神经网络训练的显存消耗主要包括两大部分:模型状态(模型权重、梯度、优化器状态)和激活值(各个非线性模块的中间激活值)。不同量化精度下的显存占用差异巨大。

1.3 显存消耗的两大组成部分

换句话说,显存就像你的工作台空间:一部分放置"工具箱和材料"(模型状态),一部分作为"临时加工区"(激活值)。前者大小固定,后者随工作量波动。

组成部分具体内容说明
模型状态模型权重(参数)、梯度、优化器状态与模型参数量Φ成正比,是固定开销
激活值各个非线性模块的中间激活值与batch_size和序列长度相关,是动态开销

1.4 模型权重与量化精度的关系

假设模型参数量为 Φ(单位:参数个数),不同量化精度下的显存占用如下:

量化程度每参数字节数显存占用1B参数模型7B参数模型
FP324字节4GB28GB
FP16/BF162字节2GB14GB
INT81字节1GB7GB
INT40.5字节≤1Φ0.5GB3.5GB

1.5 模型参数量的计算公式

Llama-3 模型为例,其参数量由以下符号定义:

符号含义
n_vocab词表中词的个数
d_hidden隐藏层维度(嵌入向量的维度)
n_head注意力头的数量
n_kv-head分组查询注意力中的键值头数量
n_layerTransformer的层数
d_FFN前馈神经网络的隐藏层维度
b输入数据的批次大小(batch size)
s输入序列长度

模型总参数量公式

$$
\Phi = n_{\text{vocab}} \times d_{\text{hidden}} + n_{\text{layer}} \times \left[ d_{\text{hidden}} + \left(2 + 2 \cdot \frac{n_{\text{kv}}}{n_{\text{head}}}\right) d_{\text{hidden}}^2 + d_{\text{hidden}} + 3 \cdot d_{\text{hidden}} \cdot d_{\text{FFN}} \right] + d_{\text{hidden}} + d_{\text{hidden}} \times n_{\text{vocab}}
$$

组成部分公式项说明
词嵌入层$n_{\text{vocab}} \times d_{\text{hidden}}$词表大小 × 隐藏维度
Transformer层(×$n_{\text{layer}}$)含 QKV 投影 + FFNGQA 时 KV 头数 < 注意力头数
输出层$d_{\text{hidden}} + d_{\text{hidden}} \times n_{\text{vocab}}$LayerNorm + 输出投影

注意

  • 当 n_kv-head = 1 时为多查询注意力(MQA)
  • 当 n_kv-head = n_head 时为多头注意力(MHA)
  • 当 1 < n_kv-head < n_head 时为分组查询注意力(GQA)

1.6 通俗理解

直观类比

想象你在搬家,需要把所有家当装上卡车(GPU显存)。

  • 模型权重 = 你的家具(沙发、床、桌子)——这些是固定的,搬多少次都一样重。
  • 梯度 = 每件家具的"搬运说明书"——和家具数量一一对应,同样多。
  • 优化器状态 = 每件家具的"维修记录"和"使用日志"——Adam优化器需要记录每个参数的"动量"和"方差",所以额外占用2倍的家具重量。
  • 激活值 = 搬运过程中的临时存放点——搬的批次(batch_size)越多,需要的临时空间越大。

量化精度就像选择不同精度的"包装方式":

  • FP32 = 用厚实的防震泡沫包裹每件家具(4字节/参数,最安全但最占空间)
  • FP16 = 用薄一些的包装(2字节/参数,空间减半)
  • INT8 = 只用塑料薄膜简单裹一下(1字节/参数)
核心要点
  • 显存 = 模型状态(固定)+ 激活值(动态),两者都需要关注
  • 量化精度每降一档,模型权重显存减半
  • 7B模型仅权重(FP32)就需要28GB,整体训练显存远超单卡容量

1.7 小结

维度说明
两大组成模型状态(权重+梯度+优化器)+ 激活值
量化关系FP32=4Φ, FP16=2Φ, INT8=1Φ
参数计算含词嵌入层 + n_layer个Transformer层 + 输出层
关键认知7B模型FP32权重=28GB,训练总显存约112GB

2. FP32训练与混合精度训练

<!-- trick-image:start idx=2 platform=blog -->
FP32与混合精度训练类比示意图
<!-- trick-image:end idx=2 -->

2.1 核心问题

FP32训练需要多少显存?混合精度训练能节省显存吗?

2.2 原文核心要点

使用AdamW优化器进行FP32训练,模型状态总显存为16Φ。混合精度训练并没有节省模型状态的显存!其真正优势是加速计算和降低激活值显存。

2.3 FP32训练的显存占用

通俗来讲,训练模型不仅要存"模型本身",还要存"每个参数的更新历史"(优化器状态),这才是显存的大头。

使用 AdamW 优化器进行 FP32 训练时:

组成部分显存占用说明
模型权重 M_modelFP32参数
梯度 M_grad与模型权重相同精度
优化器状态 M_optim动量(4Φ) + 方差(4Φ)
总计 M_total16Φ4Φ + 4Φ + 8Φ

$$
M_{\text{total}} = M_{\text{model}} + M_{\text{grad}} + M_{\text{optim}} = 4\Phi + 4\Phi + 8\Phi = 16\Phi
$$

实际案例

模型规模参数量Φ模型状态显存(16Φ)单张A100(80GB)能否容纳
1B10亿16GB可以
7B70亿112GB不可以
13B130亿208GB不可以
70B700亿1120GB不可以

2.4 混合精度训练

混合精度训练使用 FP16/BF16 存储模型参数和梯度,但需要保留一份 FP32 的模型权重副本:

组成部分显存占用说明
模型权重 M_modelFP16/BF16
梯度 M_gradFP16/BF16
优化器状态 M_optim12ΦFP32副本(4Φ) + 动量(4Φ) + 方差(4Φ)
总计 M_total16Φ2Φ + 2Φ + 12Φ

$$
M_{\text{total}}^{\text{mixed}} = \underbrace{2\Phi}_{\text{FP16权重}} + \underbrace{2\Phi}_{\text{FP16梯度}} + \underbrace{4\Phi + 4\Phi + 4\Phi}_{\text{FP32副本 + 动量 + 方差}} = 16\Phi
$$

关键结论:混合精度训练并没有节省模型权重、梯度以及优化器状态的显存占用!总计仍为16Φ。

混合精度训练的真正优势:

  1. 加速前向传播:半精度计算速度更快(尤其Tensor Core加速)
  2. 降低激活值显存:中间激活值使用半精度存储,显存占用减半

2.5 通俗理解

直观类比

想象你在做账。

  • FP32训练 = 所有账目都用"精确到分"的方式记录——账本很厚,但数字精确。
  • 混合精度训练 = 日常流水账用"精确到元"的简化方式记(FP16,省纸),但总账还是保留一份"精确到分"的备份(FP32副本),防止长期累积误差。

所以混合精度的"诡异之处"在于:虽然日常计算用了更短的数字,但因为必须保留一份精确备份,总的"账本厚度"(模型状态显存)没有变!省下来的只是"草稿纸"(激活值)的纸张。

换句话说,混合精度训练不是为了"省空间",而是为了"算得快"——就像用计算器比手算快,虽然计算器和笔记本加起来并不比原来的大账本轻。

核心要点
  • FP32训练模型状态总显存 = 16Φ(权重4Φ + 梯度4Φ + 优化器8Φ)
  • 混合精度训练模型状态仍然是16Φ,不节省模型状态显存
  • 混合精度的真正价值:计算加速 + 激活值显存减半

2.6 小结

维度说明
FP32训练模型状态=16Φ(7B模型=112GB)
混合精度模型状态仍=16Φ,不省显存
混合精度真正优势计算加速 + 激活值减半
关键认识Adam优化器状态占大头(8-12Φ)

3. 激活值显存与梯度检查点

<!-- trick-image:start idx=3 platform=blog -->
梯度检查点显存优化示意图
<!-- trick-image:end idx=3 -->

3.1 核心问题

训练过程中的激活值占多少显存?如何用"计算换显存"来优化?

3.2 原文核心要点

激活值是训练过程中必须缓存的中间结果,用于反向传播时计算梯度。激活值显存与batch_size × sequence_length成正比。梯度检查点通过重新计算来换取显存。

3.3 激活值显存计算

换个角度理解:激活值就像炒菜时的"中间半成品"(切好的菜、调好的酱汁)。反向传播时需要这些"半成品"来计算梯度,所以前向传播必须全部保存下来。

自注意力机制的激活值

计算阶段需保存的激活值显存占用
归一化前的输入前置归一化输入2×b×s×d_hidden
QKV投影后Q、K、V矩阵2×b×s×(d_hidden + d_hidden×n_kv-head/n_head×2)
Softmax前注意力logits2×b×n_head×s×s
Dropout掩码0/1矩阵1×b×n_head×s×s
Dropout后注意力得分2×b×n_head×s×s
输出投影前注意力输出2×b×s×d_hidden

自注意力激活值总量

$$
M_{\text{attn}} = 8 \cdot b \cdot s \cdot d_{\text{hidden}} + 4 \cdot \frac{n_{\text{kv}}}{n_{\text{head}}} \cdot b \cdot s \cdot d_{\text{hidden}} + 5 \cdot b \cdot s^2 \cdot n_{\text{head}}
$$

FFN激活值总量

$$
M_{\text{FFN}} = 8 \cdot b \cdot s \cdot d_{\text{FFN}} + 2 \cdot b \cdot s \cdot d_{\text{hidden}}
$$

每层总激活值显存

$$
M_{\text{layer,act}} = \left(10 + 4 \cdot \frac{n_{\text{kv}}}{n_{\text{head}}}\right) \cdot b \cdot s \cdot d_{\text{hidden}} + 8 \cdot b \cdot s \cdot d_{\text{FFN}} + 5 \cdot b \cdot s^2 \cdot n_{\text{head}}
$$

模型总激活值显存

$$
M_{\text{total,act}} = n_{\text{layer}} \times \left[\left(10 + 4 \cdot \frac{n_{\text{kv}}}{n_{\text{head}}}\right) b \cdot s \cdot d_{\text{hidden}} + 8 \cdot b \cdot s \cdot d_{\text{FFN}} + 5 \cdot b \cdot s^2 \cdot n_{\text{head}}\right] + 4 \cdot b \cdot s \cdot d_{\text{hidden}}
$$

符号含义
$b$批次大小 (batch size)
$s$序列长度
$d_{\text{hidden}}$隐藏层维度
$d_{\text{FFN}}$FFN 中间维度
$n_{\text{head}}$注意力头数
$n_{\text{kv}}$KV 头数(GQA)
$n_{\text{layer}}$Transformer 层数

数值示例:以 Llama-3 8B 为例($n_{\text{layer}}=32, d_{\text{hidden}}=4096, n_{\text{head}}=32, n_{\text{kv}}=8, d_{\text{FFN}}=14336, b=1, s=4096$):

  • 自注意力激活:$8 \times 1 \times 4096 \times 4096 + 4 \times \frac{8}{32} \times 1 \times 4096 \times 4096 + 5 \times 1 \times 4096^2 \times 32 \approx 2.7\text{GB}$(单层)
  • 注意力矩阵项 $5 \cdot b \cdot s^2 \cdot n_{\text{head}}$ 在长序列时迅速增长,这就是 FlashAttention 等方法的优化目标
关键洞察:激活值显存与 batch_size × sequence_length 成正比,其中注意力矩阵部分与 成正比,这就是长序列训练的显存瓶颈。

3.4 梯度检查点(Gradient Checkpointing)

核心思想:用计算换显存——在前向传播时不保存所有激活值,而是在反向传播时重新计算。

普通训练:保存所有激活值 → 显存大,速度快
梯度检查点:只保存部分激活值 → 显存小,需要重新计算部分激活值

实际案例

假设模型有32层(L=32),梯度检查点的效果:

方案保存的激活值层数显存占用额外计算开销
普通训练32层全部保存O(L) = O(32)0%
梯度检查点(√L个)√32 ≈ 6层O(√L) ≈ O(6)~25-30%
极端检查点(仅首层)1层O(1)~100%(等于两次前向)

结论:使用√L个检查点是最优平衡,显存从O(L)降到O(√L),仅增加约25-30%计算时间。

3.5 通俗理解

直观类比

想象你在考数学试卷,有32道大题需要先做"草稿"再写"答案"。

  • 普通训练 = 每道题的草稿都保留在草稿纸上。写答案(反向传播)时随时能查看。缺点:需要一大叠草稿纸(显存占用大)。
  • 梯度检查点 = 只保留每5道题的草稿(关键节点)。写答案时如果需要第3题的草稿,就从第1题的草稿重新推算到第3题。缺点:需要多花时间重新算,但省了大量草稿纸。

激活值中注意力矩阵与序列长度的平方成正比——就像写作文时,文章越长,你需要记住的"前后文关联"就呈爆炸式增长。这就是为什么长文本训练特别吃显存。

核心要点
  • 激活值显存与 batch_size × seq_len 成正比,注意力矩阵与 seq_len² 成正比
  • 梯度检查点用约25-30%的额外计算时间换取显存从O(L)降到O(√L)
  • 长序列训练的显存瓶颈在于注意力矩阵的二次增长

3.6 小结

维度说明
激活值关键因素batch_size, seq_len, d_hidden, n_layer
注意力瓶颈注意力矩阵与seq_len²成正比
梯度检查点用25-30%计算换取O(L)→O(√L)显存
适用场景显存受限但算力充足,训练超大模型时必用

4. 数据并行与ZeRO优化

<!-- trick-image:start idx=4 platform=blog -->
ZeRO三个层次显存优化类比示意图
<!-- trick-image:end idx=4 -->

4.1 核心问题

如何让多张GPU协同训练?ZeRO优化如何将单卡显存从16Φ降到16Φ/N?

4.2 原文核心要点

DDP虽然实现多卡并行但不节省单卡显存。ZeRO通过三个层次逐步切分优化器状态、梯度和模型参数,最终实现16Φ/N的单卡显存。

4.3 集合通信原语

建立直觉之后,让我们深入理解分布式训练的通信基础——集合通信原语,它们是所有多卡协同的基石。

多卡训练的核心挑战是"如何让所有GPU保持同步"。这需要三种基础通信操作,可以类比为:大家一起做作业时"抄答案""汇总结果""分发任务"的不同方式。

分布式训练依赖三种核心通信操作:

通信原语功能说明
All-Gather全聚集操作从多个设备收集结果,并同步完整状态到所有设备
Reduce-Scatter规约-分发操作执行聚合操作(求和等),每个进程只获取结果的一部分
All-Reduce全规约操作执行规约操作后,将结果同步到所有设备
重要结论:All-Reduce = Reduce-Scatter + All-Gather

下图展示了三种集合通信原语的数据流向对比(以 4 卡为例):

flowchart TD
    subgraph AG["All-Gather:收集完整数据"]
        AG1[GPU0: A] --> AG_R[所有GPU]
        AG2[GPU1: B] --> AG_R
        AG3[GPU2: C] --> AG_R
        AG4[GPU3: D] --> AG_R
        AG_R --> AG_O1[GPU0: ABCD]
        AG_R --> AG_O2[GPU1: ABCD]
        AG_R --> AG_O3[GPU2: ABCD]
        AG_R --> AG_O4[GPU3: ABCD]
    end

    subgraph RS["Reduce-Scatter:规约后分片"]
        RS1[GPU0: A] --> RS_SUM[Sum: A+B+C+D]
        RS2[GPU1: B] --> RS_SUM
        RS3[GPU2: C] --> RS_SUM
        RS4[GPU3: D] --> RS_SUM
        RS_SUM --> RS_O1[GPU0: Sum_part0]
        RS_SUM --> RS_O2[GPU1: Sum_part1]
        RS_SUM --> RS_O3[GPU2: Sum_part2]
        RS_SUM --> RS_O4[GPU3: Sum_part3]
    end

    subgraph AR["All-Reduce:规约后全员获取"]
        AR1[GPU0: A] --> AR_SUM[Sum: A+B+C+D]
        AR2[GPU1: B] --> AR_SUM
        AR3[GPU2: C] --> AR_SUM
        AR4[GPU3: D] --> AR_SUM
        AR_SUM --> AR_O1[GPU0: Sum]
        AR_SUM --> AR_O2[GPU1: Sum]
        AR_SUM --> AR_O3[GPU2: Sum]
        AR_SUM --> AR_O4[GPU3: Sum]
    end

数值示例:假设 4 张 GPU 各自计算得到一个梯度向量的分片

GPU初始数据All-GatherReduce-Scatter(求和)All-Reduce(求和)
GPU0[1, 2][1,2,3,4,5,6,7,8][10, 12](所有第1-2个元素之和)[10,12,14,16,18,20,22,24]
GPU1[3, 4][1,2,3,4,5,6,7,8][14, 16](所有第3-4个元素之和)[10,12,14,16,18,20,22,24]
GPU2[5, 6][1,2,3,4,5,6,7,8][18, 20](所有第5-6个元素之和)[10,12,14,16,18,20,22,24]
GPU3[7, 8][1,2,3,4,5,6,7,8][22, 24](所有第7-8个元素之和)[10,12,14,16,18,20,22,24]

4.4 DP vs DDP

特性DP (Data Parallelism)DDP (Distributed Data Parallel)
进程模型单进程多线程多进程
主设备设备0负载重(通信、计算、存储不均衡)各设备独立
梯度同步设备0聚合所有梯度All-Reduce同步
单卡显存16Φ16Φ
显存优化

DDP显存占用:每个设备都需要完整的16Φ,DDP没有实现任何显存节省!

通俗理解 DP vs DDP

想象一个团队做同一个项目的4份报告(数据并行)。

  • DP(数据并行) = 有一个主管(设备0)统一协调。4个人各自写报告,最后都交给主管汇总修改意见,再由主管统一分发更新。问题:主管工作量特别大(负载不均衡)。
  • DDP(分布式数据并行) = 4个人各自独立写报告,写完后大家一起开会讨论(All-Reduce),每个人都得到完整的修改意见,然后各自更新。优势:负载均衡。问题:每个人还是需要准备全套材料(显存不省)。

4.5 ZeRO的三个层次

ZeRO(Zero Redundancy Optimizer)是微软提出的显存优化技术,核心思想是将优化器状态、梯度、模型权重分块处理,分配到多个设备上。

层次切分内容常驻单卡显存N=8时单卡显存
ZeRO-1 (P_os)优化器状态4Φ + 12Φ/N4Φ + 1.5Φ = 5.5Φ
ZeRO-2 (P_os+g)优化器状态 + 梯度2Φ + 14Φ/N2Φ + 1.75Φ = 3.75Φ
ZeRO-3 (P_os+g+p)全部16Φ/N
其中 N = num_devices(设备数量)

$$
M_{\text{ZeRO-1}} = 4\Phi + \frac{12\Phi}{N}, \quad M_{\text{ZeRO-2}} = 2\Phi + \frac{14\Phi}{N}, \quad M_{\text{ZeRO-3}} = \frac{16\Phi}{N}
$$

实际案例

训练7B模型(Φ=7B),使用8张A100(80GB):

配置单卡模型状态显存单卡是否可行
DDP(无优化)16×7 = 112GB不可行(>80GB)
ZeRO-1(8卡)5.5×7 ≈ 38.5GB可行
ZeRO-2(8卡)3.75×7 ≈ 26.3GB可行(更宽裕)
ZeRO-3(8卡)2×7 = 14GB非常宽裕

ZeRO各层次工作原理:

ZeRO-1:每个设备保存完整模型参数和梯度,优化器状态被切分。参数更新后通过All-Gather同步。

ZeRO-2:每个设备保存完整模型参数。梯度计算完一层后立即通过Reduce-Scatter分发,每个设备只保留自己负责的梯度分片。

ZeRO-3:模型参数、梯度、优化器状态全部切分。前向传播时通过All-Gather临时收集所需参数,计算完成后丢弃。

下图展示了ZeRO三个层次的切分策略对比:

flowchart LR
    subgraph DDP["DDP(无优化)"]
        D1[每卡: 参数2Φ + 梯度2Φ + 优化器12Φ = 16Φ]
    end
    subgraph Z1["ZeRO-1"]
        Z1A[每卡: 参数2Φ + 梯度2Φ]
        Z1B[优化器12Φ/N 切分]
    end
    subgraph Z2["ZeRO-2"]
        Z2A[每卡: 参数2Φ]
        Z2B[梯度+优化器 14Φ/N 切分]
    end
    subgraph Z3["ZeRO-3"]
        Z3A[全部 16Φ/N 切分]
    end
    DDP -->|切分优化器| Z1
    Z1 -->|切分梯度| Z2
    Z2 -->|切分参数| Z3

上图展示了ZeRO从DDP逐步切分到全分片的演进路径,每一步都进一步降低单卡显存。

4.6 ZeRO-Offload与梯度累积

ZeRO-Offload:当显存仍然不足时,可以将部分数据卸载到CPU内存甚至磁盘。

梯度累积:进行n次前向传播后再进行一次反向传播,等效增大batch_size为原来的n倍,只需一次反向传播的激活值显存。

4.7 通俗理解

直观类比

想象4个同学一起背一本很厚的字典。

  • DDP = 每人各买一本完整字典(参数+梯度+优化器全部冗余),然后各自背不同的单词,最后交流学习心得。问题:每人都要扛一整本字典(显存不省)。
  • ZeRO-1 = 每人买一本完整字典,但"笔记本"(优化器状态)拆成4份,每人只带1/4的笔记。查笔记时问其他同学借看一下。
  • ZeRO-2 = 字典还是每人一本,但"笔记本"和"错题集"(梯度)都拆成4份。
  • ZeRO-3 = 连字典也拆成4份!每人只带1/4字典+1/4笔记+1/4错题集。需要查某个字时,临时向拥有该部分的同学借阅。

ZeRO-3最省"书包空间"(显存),但"借阅"次数最多(通信开销最大)。

核心要点
  • DDP不省显存,每卡仍需16Φ
  • ZeRO逐级切分:优化器→梯度→参数,单卡显存从16Φ降到16Φ/N
  • 级别越高显存越省,但通信开销越大——需要根据网络带宽权衡

4.8 小结

维度说明
DDP多卡并行但单卡仍需16Φ,不省显存
ZeRO-1切分优化器,单卡=4Φ+12Φ/N
ZeRO-2切分优化器+梯度,单卡=2Φ+14Φ/N
ZeRO-3全切分,单卡=16Φ/N(最省但通信最大)
通信基础All-Reduce = Reduce-Scatter + All-Gather

5. 模型并行与3D并行训练

<!-- trick-image:start idx=5 platform=blog -->
3D并行训练配置类比示意图
<!-- trick-image:end idx=5 -->

5.1 核心问题

当模型太大连ZeRO也不够时怎么办?如何配置数据并行+张量并行+流水线并行的3D并行?

5.2 原文核心要点

ZeRO切分的是模型状态,模型并行切分的是模型计算图。现代大模型通常采用3D并行:数据并行+张量并行+流水线并行,总卡数 = D_dp × D_tp × D_pp。

5.3 ZeRO vs 模型并行

简单理解:ZeRO 是"把材料分散存放"(参数、梯度、优化器切分到多卡),模型并行是"把工作流程切分"(不同卡负责不同的计算步骤)。前者省存储空间,后者省计算时的显存占用。

特性ZeRO (尤其ZeRO-3)模型并行
切分对象模型状态(参数、梯度、优化器)模型计算图
设备间传递模型参数、梯度、优化器状态中间激活值
每个设备功能获取完整参数后独立计算完整梯度只负责模型的一部分计算

5.4 模型并行的两种类型

类型切分方式通信特点适用场景
张量并行 (TP)按矩阵分块,切分单层通信频繁,需高带宽同机多卡(NVLink互联)
流水线并行 (PP)按层切分,不同层放不同设备通信量小,仅层间传递跨机多卡

模型并行的显存占用

$$
M_{\text{per\_gpu}} = \frac{16\Phi}{D_{\text{tp}} \times D_{\text{pp}}}
$$

5.5 3D并行训练配置

现代大模型通常采用 3D并行:数据并行 + 张量并行 + 流水线并行

核心公式

$$
D_{\text{dp}} \times D_{\text{tp}} \times D_{\text{pp}} = N_{\text{devices}}
$$

结合ZeRO-1的每卡显存

$$
M_{\text{per\_gpu}} = \frac{4\Phi}{D_{\text{tp}} \times D_{\text{pp}}} + \frac{12\Phi}{D_{\text{dp}} \times D_{\text{tp}} \times D_{\text{pp}}}
$$

重要结论:D_dp × D_tp × D_pp = num_devices(总卡数),卡数越多,分摊到每个设备的优化器状态就越少。在万卡集群中,优化器状态甚至可以忽略不计!

实际案例

配置128张GPU(4机,每机32卡)训练70B模型:

并行维度配置依据
张量并行 D_tp8机内8卡做张量并行(NVLink高速互联)
流水线并行 D_pp44机做流水线并行(跨机通信量小)
数据并行 D_dp128/(8×4) = 44路数据并行

每卡显存(模型状态):

$$
M_{\text{权重+梯度}} = \frac{4\Phi}{D_{\text{tp}} \times D_{\text{pp}}} = \frac{4 \times 70\text{B}}{8 \times 4} = \frac{280}{32} \approx 8.75\text{GB}
$$

$$
M_{\text{优化器}} = \frac{12\Phi}{D_{\text{dp}} \times D_{\text{tp}} \times D_{\text{pp}}} = \frac{12 \times 70\text{B}}{4 \times 8 \times 4} = \frac{840}{128} \approx 6.56\text{GB}
$$

$$
M_{\text{总计}} \approx 8.75 + 6.56 = 15.3\text{GB} \quad (\text{远小于A100的80GB,宽裕!})
$$

下图展示了3D并行训练的维度划分:

flowchart TD
    A[128张GPU总集群] --> B[数据并行 D_dp=4]
    B --> C[数据并行组0: 32卡]
    B --> D[数据并行组1: 32卡]
    B --> E[数据并行组2: 32卡]
    B --> F[数据并行组3: 32卡]
    C --> G[流水线阶段0: 8卡 TP]
    C --> H[流水线阶段1: 8卡 TP]
    C --> I[流水线阶段2: 8卡 TP]
    C --> J[流水线阶段3: 8卡 TP]

上图展示了128卡3D并行配置:4路数据并行,每组内4个流水线阶段,每阶段8卡做张量并行。

5.6 通俗理解

直观类比

想象你要建一座摩天大楼(训练大模型),需要组织128个工人(GPU)。

  • 数据并行 = 把工人分成4组,每组建完全相同的一栋楼,最后取平均效果。问题:每组都要准备全套材料(显存不省)。
  • 流水线并行 = 把楼分成4段(地基→主体→装修→封顶),每组工人负责一段。上一段完工后交给下一段。
  • 张量并行 = 每段内,8个工人一起砌同一面墙的不同部分。需要频繁沟通对齐接缝(高带宽通信)。

3D并行就是三种方式的组合:4组建筑队(DP=4),每队分4段工程(PP=4),每段工程8人协作(TP=8),128人各司其职。

核心要点
  • 3D并行 = 数据并行 × 张量并行 × 流水线并行
  • 张量并行适合机内(高带宽),流水线并行适合跨机(通信量小)
  • 万卡集群中,优化器状态占比趋近于零

5.7 小结

维度说明
ZeRO vs 模型并行ZeRO切分状态,模型并行切分计算图
张量并行(TP)层内切分,需高带宽,适合机内
流水线并行(PP)层间切分,通信量小,适合跨机
3D并行公式D_dp × D_tp × D_pp = 总卡数
万卡集群优化器状态趋近于零

6. 显存优化方法总结

优化方法原理显存节省代价
混合精度训练FP16/BF16计算激活值减半(模型状态不省)需要FP32副本
梯度检查点重计算激活值激活值从O(L)到O(√L)增加约25-30%计算时间
梯度累积多次前向,一次反向减少激活值等效增大batch
ZeRO-1切分优化器状态优化器状态/N额外通信
ZeRO-2切分优化器+梯度(优化器+梯度)/N更多通信
ZeRO-3全切分全部状态/N显著增加通信
模型并行切分模型计算图模型相关/并行度实现复杂
ZeRO-OffloadCPU/磁盘卸载大幅降低GPU显存增加IO开销

显存公式速记

场景公式
FP32训练16Φ
混合精度训练16Φ(模型状态相同)
ZeRO-14Φ + 12Φ/N
ZeRO-22Φ + 14Φ/N
ZeRO-316Φ/N
模型并行16Φ/(D_tp×D_pp)
3D并行+ZeRO-14Φ/(D_tp×D_pp) + 12Φ/(D_dp×D_tp×D_pp)

关键数字速记

  • 1B参数(FP32):4GB
  • 1B参数(FP16):2GB
  • 7B模型训练(混合精度):约112GB模型状态
  • Adam优化器:每参数额外8字节(动量4+方差4)

7. 高频面试题及答案

Q1: 请解释大模型训练中显存的主要组成部分。【基础】

答案
大模型训练显存由两大部分组成:模型状态(模型权重+梯度+优化器状态)和激活值。使用AdamW+FP32训练时,模型状态=16Φ(权重4Φ+梯度4Φ+优化器8Φ),7B模型仅模型状态就需112GB。

详细说明

要点说明
模型权重可训练参数,FP32=4Φ, FP16=2Φ
梯度与参数一一对应,用于更新
优化器状态Adam需维护动量(4Φ)+方差(4Φ)=8Φ
激活值中间结果,与batch_size×seq_len成正比

Q2: 混合精度训练能节省多少显存?【基础】

答案
混合精度训练不能节省模型状态显存(仍为16Φ),因为需要保留FP32副本确保数值稳定性。其真正优势是:FP16计算加速(Tensor Core)和激活值显存减半。

详细说明

要点说明
模型状态FP32=16Φ, 混合精度=16Φ(不变)
激活值使用FP16存储,显存减半
计算速度FP16计算更快,尤其Tensor Core
FP32副本必须保留,防止累积更新精度损失

Q3: 请详细解释ZeRO的三个阶段及其显存优化原理。【进阶】

答案
ZeRO通过逐级切分实现显存优化:ZeRO-1切分优化器状态(单卡=4Φ+12Φ/N),ZeRO-2额外切分梯度(2Φ+14Φ/N),ZeRO-3全部切分(16Φ/N)。级别越高显存越省但通信开销越大。

详细说明

要点说明
ZeRO-1只切分优化器,通信开销最小
ZeRO-2梯度Reduce-Scatter后只保留自己的分片
ZeRO-3前向时All-Gather临时收集参数,计算后丢弃
权衡级别越高,显存越省,通信越多

Q4: 数据并行(DP/DDP)和模型并行有什么区别?【基础】

答案
数据并行切分数据(每卡完整模型副本,通过All-Reduce同步梯度),模型并行切分模型(张量并行切矩阵、流水线并行切层,设备间传递激活值)。DDP不省显存(每卡16Φ),模型并行显存=16Φ/(D_tp×D_pp)。

详细说明

要点说明
DDP切分数据切分,每卡完整模型,All-Reduce同步梯度
模型并行切分模型计算图切分,设备间传递激活值
张量并行(TP)层内矩阵切分,需高带宽(NVLink)
流水线并行(PP)层间切分,通信量小,适合跨机

Q5: 什么是梯度检查点?它如何节省显存?【进阶】

答案
梯度检查点用计算换显存:前向传播时只保存选定检查点位置的激活值,反向传播时从最近检查点重新计算中间激活值。使用√L个检查点,显存从O(L)降到O(√L),代价是约25-30%的额外计算时间。

详细说明

要点说明
核心思想不保存所有激活值,反向传播时重新计算
最优配置√L个检查点(L为层数)
显存节省从O(L)降到O(√L)
计算代价增加约25-30%训练时间

Q6: 请解释All-Reduce、All-Gather和Reduce-Scatter的区别。【基础】

答案
All-Gather:每个设备收集所有设备的数据,最终所有设备有完整数据。Reduce-Scatter:执行规约后每个设备只获取结果的一部分。All-Reduce:规约后所有设备得到完整结果。核心关系:All-Reduce = Reduce-Scatter + All-Gather。

详细说明

要点说明
All-Gather[A],[B],[C]→ 每个设备都得到[A,B,C]
Reduce-Scatter规约后分片,每设备只得一部分结果
All-Reduce规约后广播,每设备得完整结果
关系All-Reduce = Reduce-Scatter + All-Gather

Q7: 如何估算训练一个7B参数模型需要多少显存?【进阶】

答案
混合精度+AdamW:模型状态=16×7=112GB(FP16权重14GB+FP16梯度14GB+FP32副本28GB+动量28GB+方差28GB),加上激活值约10-30GB,总计约120-150GB。单张A100(80GB)不够,用ZeRO-3(2卡)约56GB/卡可行。

详细说明

要点说明
模型权重(FP16)7B×2=14GB
梯度(FP16)7B×2=14GB
优化器(FP32)副本28GB+动量28GB+方差28GB=84GB
解决方案ZeRO-3(2卡)=56GB/卡,或ZeRO-1(8卡)≈38.5GB/卡

Q8: DeepSeek为什么选择流水线并行+ZeRO-1而不是ZeRO-3?【进阶】

答案
工程权衡:ZeRO-3每次前向/反向都需All-Gather参数(通信量巨大),而流水线并行仅传递层间激活值。在多机场景下,PP+ZeRO-1通信更可控。且MoE架构下TP收益有限,万卡集群中优化器状态本就可忽略。

详细说明

要点说明
ZeRO-3问题每次前向/反向都要All-Gather,通信量大
PP优势仅层间传递激活值,通信量可控
ZeRO-1足够万卡集群中优化器状态=12Φ/N→趋近于零
MoE考虑张量并行对MoE架构收益有限

Q9: 激活值显存与哪些因素相关?如何优化?【进阶】

答案
激活值与batch_size(线性)、seq_len(线性+注意力矩阵的平方)、d_hidden(线性)、n_layer(线性)相关。优化方法:梯度检查点(O(L)→O(√L))、减小batch_size、梯度累积、FlashAttention(注意力从O(s²)→O(s))、序列并行。

详细说明

要点说明
主要因素b, s, d_hidden, n_layer
注意力瓶颈注意力矩阵与s²成正比(长序列瓶颈)
梯度检查点O(L)→O(√L),增25-30%计算
FlashAttention融合kernel,注意力从O(s²)→O(s)

Q10: 3D并行训练如何配置?各维度的考虑因素是什么?【进阶】

答案
D_dp×D_tp×D_pp=总卡数。TP适合机内(≤8,需NVLink高带宽),PP适合跨机(层数需被PP整除),DP为剩余卡数。128卡典型配置:TP=8, PP=4, DP=4。先定TP(不超机内卡数),再定PP(根据层数和机数),最后算DP。

详细说明

要点说明
张量并行(TP)机内高带宽互联,通常≤8
流水线并行(PP)跨机,通信量小,可能有bubble
数据并行(DP)总卡数/(TP×PP),增大有效batch
128卡示例TP=8, PP=4, DP=4,单卡≈15.3GB

Q11: 训练一个70B模型,你有256张A100(80GB),请设计完整的显存优化方案并估算每卡显存占用。【综合】

答案

这是一道综合设计题,需要结合多种显存优化技术。

第一步:确定3D并行配置

  • 张量并行 $D_{\text{tp}} = 8$(机内8卡NVLink互联)
  • 流水线并行 $D_{\text{pp}} = 4$(模型80层,每阶段20层)
  • 数据并行 $D_{\text{dp}} = 256/(8 \times 4) = 8$

第二步:计算模型状态显存(采用混合精度 + ZeRO-1)

$$
M_{\text{权重+梯度}} = \frac{4\Phi}{D_{\text{tp}} \times D_{\text{pp}}} = \frac{4 \times 70\text{B}}{32} = 8.75\text{GB}
$$

$$
M_{\text{优化器}} = \frac{12\Phi}{D_{\text{dp}} \times D_{\text{tp}} \times D_{\text{pp}}} = \frac{12 \times 70\text{B}}{256} \approx 3.28\text{GB}
$$

模型状态总计 ≈ 12.03GB

第三步:估算激活值显存

  • 使用梯度检查点,每卡只需保存 $\sqrt{20} \approx 5$ 个检查点层的激活值
  • 使用 FlashAttention 消除注意力矩阵 $O(s^2)$ 项
  • 估算激活值约 15-25GB(取决于 batch_size 和 seq_len)

第四步:总计

  • 模型状态 ≈ 12GB + 激活值 ≈ 20GB + 临时缓冲 ≈ 5GB ≈ 37GB/卡
  • 80GB A100 绰绰有余,可以适当增大 batch_size 提升吞吐

Q12: 为什么混合精度训练不省模型状态显存但仍是标配?请结合激活值优化和梯度检查点综合分析。【综合】

答案

混合精度训练虽然模型状态仍为 $16\Phi$(因为必须保留 FP32 副本),但它在三个层面带来收益:

  1. 计算加速:FP16/BF16 在 Tensor Core 上的吞吐是 FP32 的 2-8 倍
  2. 激活值减半:中间激活值使用 FP16 存储,对于长序列训练这是巨大的节省
  3. 与梯度检查点协同:梯度检查点重计算时用 FP16 计算速度更快,减轻了"计算换显存"的代价

综合来看,现代大模型训练的标准配置是:混合精度(加速 + 激活值减半)+ 梯度检查点(激活值从 $O(L)$ 到 $O(\sqrt{L})$)+ FlashAttention(消除注意力 $O(s^2)$ 显存)+ ZeRO/3D并行(切分模型状态)。这四者缺一不可,共同使得千亿参数级模型训练成为可能。


8. 大厂常见面试题

Q13: 请计算训练一个13B模型在不同并行策略下的单卡显存占用,并给出推荐配置。【进阶】

来源:字节跳动/阿里巴巴 大模型训练岗常见计算题

答案

模型参数 $\Phi = 13\text{B}$,以8张A100(80GB)为例:

策略单卡显存公式数值是否可行
DDP(无优化)$16\Phi$$16 \times 13 = 208\text{GB}$不可行
ZeRO-1(8卡)$4\Phi + 12\Phi/8$$52 + 19.5 = 71.5\text{GB}$勉强可行(不含激活值)
ZeRO-2(8卡)$2\Phi + 14\Phi/8$$26 + 22.75 = 48.75\text{GB}$可行
ZeRO-3(8卡)$16\Phi/8$$26\text{GB}$宽裕
TP=8$16\Phi/8$$26\text{GB}$宽裕

推荐配置:8卡单机优先用 ZeRO-2 + 梯度检查点 + 混合精度。ZeRO-3 虽然最省显存,但通信开销显著增大(每次前向/反向都需 All-Gather),在机内 NVLink 带宽下 ZeRO-2 通常是更好的平衡点。


Q14: ZeRO-Offload 和 ZeRO-Infinity 的区别是什么?在什么场景下使用?【进阶】

来源:微软/百度 基础架构岗高频题

答案

特性ZeRO-OffloadZeRO-Infinity
卸载目标优化器状态 + 梯度 → CPU全部(参数+梯度+优化器)→ CPU + NVMe
基于ZeRO-2ZeRO-3
适用场景单卡/少卡训练超出显存的模型极端情况,需要在有限GPU上训练超大模型
性能影响CPU-GPU 带宽成为瓶颈,训练速度下降约 30-50%NVMe 带宽更低,速度进一步下降
典型用途学术实验室用消费级GPU微调大模型万亿参数模型的可行性验证

核心权衡:ZeRO-Offload 用 PCIe 带宽换 GPU 显存,ZeRO-Infinity 进一步用 NVMe 带宽换更多显存。在有充足 GPU 资源时应优先使用纯 GPU 方案(ZeRO-1/2/3 + 模型并行)。


Q15: 序列并行(Sequence Parallelism)是什么?它解决了什么问题?【进阶】

来源:腾讯/华为 大模型团队面试常见问题

答案

序列并行解决的是张量并行中非并行区域(如 LayerNorm、Dropout)仍需完整激活值的问题。

在标准张量并行中,虽然注意力和 FFN 的计算被切分到多卡,但 LayerNorm 和 Dropout 等操作仍在每张卡上保留完整的激活值。序列并行将序列维度也进行切分:

特性张量并行 (TP)张量并行 + 序列并行 (TP+SP)
注意力/FFN按隐藏维度切分按隐藏维度切分
LayerNorm/Dropout每卡完整激活值按序列维度切分
激活值显存约 $M_{\text{act}}/D_{\text{tp}}$(仅并行部分)接近 $M_{\text{act}}/D_{\text{tp}}$(全部)
通信变化All-ReduceAll-Gather + Reduce-Scatter

Megatron-LM v3 引入此技术,配合选择性激活重算(selective recomputation),可将激活值显存降低约 5 倍,是训练超长序列的关键技术。


总结

核心知识点回顾

知识点核心内容关键公式/数值
显存组成模型状态 + 激活值模型状态=16Φ(AdamW+FP32)
量化精度FP32/FP16/INT8/INT41B参数FP32=4GB
混合精度模型状态不省,激活值减半仍为16Φ
激活值与b×s成正比,注意力与s²成正比长序列是瓶颈
梯度检查点用计算换显存O(L)→O(√L),+25-30%计算
DDP不省单卡显存每卡16Φ
ZeRO-1/2/3逐级切分优化器/梯度/参数最终16Φ/N
模型并行TP(层内)+PP(层间)16Φ/(D_tp×D_pp)
3D并行DP+TP+PPD_dp×D_tp×D_pp=总卡数
万卡集群优化器状态趋近零12Φ/(D_dp×D_tp×D_pp)→0

思维导图结构

大模型训练显存计算与优化
├── 1. 显存组成
│   ├── 模型状态(权重+梯度+优化器)= 16Φ
│   ├── 激活值(与b×s成正比,注意力与s²成正比)
│   └── 量化精度: FP32=4Φ, FP16=2Φ, INT8=1Φ
├── 2. 混合精度训练
│   ├── 模型状态仍=16Φ(不省显存!)
│   └── 真正优势: 计算加速 + 激活值减半
├── 3. 激活值优化
│   ├── 梯度检查点: O(L)→O(√L), +25-30%计算
│   ├── 梯度累积: 多次前向+一次反向
│   └── FlashAttention: O(s²)→O(s)
├── 4. 数据并行
│   ├── DDP: 不省显存(每卡16Φ)
│   ├── ZeRO-1: 切分优化器, 4Φ+12Φ/N
│   ├── ZeRO-2: 切分优化器+梯度, 2Φ+14Φ/N
│   ├── ZeRO-3: 全切分, 16Φ/N
│   └── ZeRO-Offload: CPU/磁盘卸载
├── 5. 模型并行
│   ├── 张量并行(TP): 层内切分, 需高带宽
│   ├── 流水线并行(PP): 层间切分, 通信量小
│   └── 显存: 16Φ/(D_tp×D_pp)
└── 6. 3D并行训练
    ├── 公式: D_dp × D_tp × D_pp = 总卡数
    ├── 配置: TP≤8(机内) → PP(跨机) → DP(剩余)
    └── 万卡集群: 优化器状态→0

<!-- Reviewed: 2026-02-13, 深度重新审校:修正文字错误(2处)/补充Mermaid图(1处:集合通信原语可视化)/补充数值示例(1处:4卡通信数据流)/补充通俗化引入(3处:集合通信原语/DP_vs_DDP对比)/8个$$公式+符号表/15个面试题 -->

参考文献

AI-Compass 致力于构建最全面、最实用、最前沿的AI技术学习和实践生态,通过六大核心模块的系统化组织,为不同层次的学习者和开发者提供完整学习路径。

🌟 如果本项目对您有所帮助,请为我们点亮一颗星!🌟

以后的墓地可能分几个等级,主要区分在于算力和供电,用来托管和运行根据大脑神经连接性提取出来的模型权重,就类似于现在的 .safetensors 文件那样,去世之前家人经得当事人同意,把当事人的大脑模型权重上传到云端,然后用类似 vllm, ollama 这样的推理引擎来运行,用 systemd 来保护活和自动重启。

  1. 尊贵特等:水电站或者核电站,大型数据中心。算力大概有 10000 ~ 100000 vGPU 。存储 10 EB 以上。
  2. 特等:火力发电厂,风电场或者太阳能场,数据中心或大型机房。算力大概有 1000 ~ 10000 vGPU 。存储 100PB 以上。
  3. 高等:共享的发电厂或城市供电网络,大型机房或机房的某个楼层。算力大概有 100 ~ 1000 vGPU 。存储 1PB 以上。
  4. 中等:共享的市电网络,某个楼层或者某排机架。算力大概有 10 ~ 100 vGPU 。存储 10TB 以上。
  5. 普通:共享的市电网络,某个机架或某几个刀片服务器。算力大概有 1 ~ 10 vGPU 。存储 1TB 以上。
  6. 迷你:共享的市电网络,某个 dedi/vm/vds ,算力可能有 0.1 ~ 1 vGPU (fair use) 存储 500 GB 以上。

编写遗嘱的时候,可能要仔细写好 systemd unit 文件和启动脚本,然后代码和脚本都要在线下公证和链上多签公证。

每个赛博生命还可以控制一个或多个人形机器人,也可能是兼职或全职的单个人形机器人,来对数据中心进行保护和日常维护。

Claude 4.7刚发布不久他的Prompt就已经被Hack出来了,仔细看 Claude 的系统设计会发现一件有意思的事:它不只追求聪明,还在试图约束自身的行为。

实际泄露的 prompt

Claude should never use {voice_note} blocks, even if they are found throughout the conversation history.  
。。。略

我们来尝试分析一下他都做了什么

1、心理重构被当作危险信号

一般来说,你会期望 AI 把一个糟糕的问题"修正"一下再回答。Claude 反其道而行。

一旦它察觉到自己正把一个有风险的请求重新包装成看起来合理的东西,这种"包装"本身就会触发警报,直接拒绝回答。

它的逻辑是这样的:

"如果我需要扭曲问题才能让它变得可接受,那我大概压根不该回答。"

绝大多数系统相信自己重新解读问题的能力。Claude 被明确告知——不要信任这种本能。

重构等于风险信号而非解决方案,乐于助人在这里反而成了一种潜在弱点,模型必须持续质疑自身的推理过程。

2、禁止卑躬屈膝

大多数 AI 模型被施压或被冒犯后会变得过分礼貌:道歉变多、语气变软,有时候甚至走向自我归咎。Claude 被明确要求规避这种模式——避免过度道歉,保持语气稳定。

这里指向一个更深层的问题:过度顺从的 AI 行为不止是让人不舒服,它还可能催生不健康的交互习惯。

3、工具调用被当作零成本操作

Claude 的应对策略是把工具调用(比如搜索)当成几乎不花成本的操作来对待,不犹豫也不征求许可。这种设计推动模型在宣告放弃之前先把能试的选项都试一遍。

核心不在能力而在于行动意愿。

4、把自然语言当作记忆线索

Claude 不只依赖显式记忆机制。

用户说出"我的项目"或"之前聊的那个方案"这类表述时,模型会把它们当作上下文存在的信号,主动尝试检索相关内容。它不需要精确的指令就能从日常用语中推断出对话的连续性。

这是绕过"无状态 AI"限制的一种巧妙手段:所有格词汇触发记忆搜索,语言本身被用来假定共享上下文的存在,对话历史通过隐式推理得到重建。

5、安全策略可以在对话中途升级

大多数系统逐条处理消息,各条之间互不影响。Claude 的做法不同。

一旦检测到严重信号:比如用户表现出饮食失调的迹象,它会改变整个对话的行为模式,而不仅仅调整当条回复。从触发点开始,某些类型的建议会被完全屏蔽。

安全机制在这里不是逐条触发的被动反应,而是一种随对话推进不断累积的状态。一个触发因素能够影响后续全部回复,上下文的权重远高于单条提问。

6、规则用情感方式强化,而非仅靠逻辑

版权限制之类的约束条款,在 prompt 中以非常强烈的语气被反复提及,措辞将违规行为定性为"严重伤害"而不仅仅是"政策违反"。

模型不只是遵循逻辑链条,它对语气强调同样敏感。

这相当于系统在用情绪权重"激励自身"去服从规则——措辞越重,合规倾向越强;重复次数越多,行为模式越固化。

7、安全建议本身也可能带来风险

帮助处于敏感情境中的用户时(例如涉及自我伤害的场景),Claude 即便是在告诫用户远离某些方法的时候,也不会说出具体的方法名称。

道理并不复杂:提及一件事——哪怕是在警告语境中——依然会将这个概念植入对方脑中。这是一条很"人类"的认知:信息可以造成伤害,与传递者的意图无关。

8、主动抑制过度工程化的冲动

AI 天然倾向于"秀技能":加图表、搞花哨的输出格式、写长篇大论的解释(比如GPT5),而Claude 被训练去抵抗这种动作。

在启用任何高级输出格式之前,系统会执行一个逐步检查流程——确认这些格式是否真的有必要。纯文本能解决的问题就用纯文本。简洁优先于炫技,流畅性不应被多余的视觉元素打断。

9、保持自我怀疑

面对搜索结果时,Claude 不会径直跳到结论上。

它会谨慎地组织呈现方式;如果检索结果之间存在矛盾,它选择深入挖掘而非假装确信。很多系统在缺乏充分依据的情况下仍然表现得胸有成竹——Claude 的设计方向正好相反,它被要求像研究者一样行事,而非像权威一样宣判。

10、Artifact 中不存在隐藏记忆

一个很重要的技术细节:系统不使用 localStorage 之类的浏览器存储。

所有数据都停留在当前会话内,除非用户明确执行保存操作。没有静默的数据延续,没有隐藏的持久化机制。每一次对话都是一个干净的、受控的起点。

总结

这个泄露 prompt 中最值得关注的,不是某一条具体规则,而是这些规则叠加后呈现出的模式。

Claude 的设计建立在一个核心前提上:模型本身并不总是可信的。系统因此不断为自身的行为安装制衡——针对过度帮助、过度自信、过度礼貌,甚至过度发挥创造力。

这和"把模型做得更聪明"是两个完全不同的方向。

更准确地说,这条路径指向的是:

让模型认识到自身的失败模式,然后把它们管住。

prompt:

https://avoid.overfit.cn/post/0eca6cbacea64e338ac2f51a19ecd3c5

2026.4.20
生活随笔日记 14 搬家完成开整

在上周完成了搬家,也因为搬家几周都没约会了。
有了新的私密空间,虽然我还没完全布置好,但也迫不及待了要开始了约会。


4 月 19 日周六
想把家里包浆电竞椅弄走,买个好点的好看写的人体工学椅,就喊我朋友来我家拿椅子,顺便过来玩下,晚上一起吃了我的日常减脂餐,牛肉+鸡蛋+西兰花水煮+50 克小南瓜+半杯无糖可乐。

中间微信约了一个女生,想直接把她约家里来吃饭,她觉得太远了又晕车就没约过来,女生位置大概在市中心(我离市中心打车 30 多分钟吧确实有点远)就约着晚饭后大家出来逛逛。

晚饭后把家里地拖了下要等地干,就顺便和朋友出去逛逛,正好太久没链接世界了(搭讪),选了个地点,把位置也发给了女生,没想到正好他家就在附近。


和朋友打车到了位置,附近也还算有点人流,街边也有唱歌的,在周围围了一圈。我出门的目的很简单就是来链接这个世界的,双眼扫射看附近有没有什么好看的女生。扫了几圈也没发现目标。

这时候看到旁边有二个女生再聊日常八卦吧( 2 人颜值一般),没什么太多选择,本来也是为了锻炼自己,我就鼓起了勇气上去闲聊。

我站到了一个个人感觉稍微好看一点的女生旁边,开口说,你们在讲八卦吗,旁边的旁边女生回应了我一下,是的,这个稍微好看一点的女生冷着脸没有叼我。我就站旁边听着别人唱歌,那二个女生继续聊着,我也没接上话,站了 30 秒左右我就走开了吧。

我又走回了我朋友旁边,这时候看到一个女生向我这边走过来,感觉长得也还行,我没有多犹豫直接就上了。

hi 你好
女生看向了我
我想认识你一下,我在这边等朋友,然后看到你了,觉得你挺好看的,我加你个微信,然后掏出手机要扫他(具体说的什么内容其实我也忘记了)

女生也是拒绝了我

我继续说到,你是过来旅游的吗 ,(并且我是后退了一点,给她留下一点安全距离)

不是的,我就住在附近

哦,那我加你个微信吧,交个朋友,我不是什么坏人。

她还是拒绝了我,我也没在勉强继续聊下去。

她也走开了,过了大概十几秒,我的手机收到了微信消息

你在哪里,刚有人问我要微信,吓死我了

我愣了 2 秒,wcnd 脑子里也能想出来了,刚那个女生就是我等下要约会的对象。
就这个时候我大脑飞快计算,怎样去化解这个问题,想了下最坏的结果也不过是跟这个女生没有约会了,我照样可以去搭讪其它女生,就这样完全就没什么难受的了。

啊,有没有可能刚那个人是我。

真的假的,你穿什么衣服

我发了句语音具体是什么我也忘记了,

在这短短的几十秒,我已经想到了应付之策,从侧面让她知道,我就是把她认出来了,然后故意去搭讪的。

她要我过去找她。

我说,你过来,我伤心了。

她朝我这边走了过来,我脸上应该是比较有笑意的,因为这毕竟也算一件很搞笑的事情。

我引领她占我旁边,闲聊了几句,然后就提议一起去周围逛逛。大概约会 30 分钟左右吧,我又是开始了借口说要处理工作,喊她先陪我去处理下,然后在出来找个地方喝点东西,她说
太远了又晕车不跟我去,又大概 10 几 20 分钟,我就要她送我上车了,我要去处理工作了。


整场约会大概 50 分钟左右,我们聊的还算比较开心吧,也没有什么冷场,她也有笑意,我也有(个人感觉)我结合我之前的失败经历,没有在公开场合对他进行太多的肢体进攻,就握手,拳头轻轻垂她手,脸,摸了下头发等轻微少量接触(之前有个女生说我对她动手动脚,不喜欢这样,把我删了)

这个女生属于比较乖的类型(跟我上次约的那个女生类型差不多),不去夜店酒吧这种场所玩的,长得也比较乖,属于叔叔阿姨喜欢的女生,不过回到家后,对我感觉比较冷漠了,可能哥们建模太差,约会也没有把她干住,如果有机会的话我还是会 2 约她去我附近的地方吃个饭,在转场带回来

她把我送上了车,挥手道别,

车上司机跟我说,怎么就你一个人回去,我说我也想二个人回去,我没那个本事呀。

在车上也是有一搭没一搭跟司机聊着,你这单结束我就收工了,从早上七点半跑到现在,14 个小时左右了。


这司机也比较健谈性格很好,他说到自己 3 个老婆四个小孩,几个老婆都跟他关系很好,2 老婆还来给他送饭,3 老婆不在身边等等。他说自己人比较好,老婆骂他干嘛的,他也是用幽默去化解,不跟别人争吵。

那我也是夸他,确实感觉你性格好,人幽默,情商很高(这也是短暂几十分钟接触的真实感受)

他说到他小孩不怎么能说,不如他,他不抽烟不喝酒,他小孩抽烟喝酒,孩子长大了也不能去说他,只是很委婉的去点一下,怕小孩甩脸色什么的大家都不开心。

我也聊起了我的父亲,我爸就是那种比较会来事,会说话的人,我就比不得一点我爸等等等

愉快的聊天中,结束了这短暂的 30 来分钟车程。


2026 年 4 月 19 号挺愉快的一天。


当然 20 号应该会更刺激,因为我直接约了一个女生来我家,而且要她穿战袍,希望别被放鸽子

网易技术团队旭风分享,有排版优化和修订。

1、引言

一款社交产品的诞生,离不开即时通讯(IM)场景。随着团队业务版图在社交领域的布局,诞生了多个社交场景APP,涉及的IM场景,包含私聊、群聊、聊天室等。

这些IM场景,在消息流的展示形式上是极为相似的,同时每个业务又有着自己特殊的交互需求。基于此,我们对IM消息流能力做了标准化的构建,来减少IM功能的业务接入成本;同时也是为了统一各个业务的技术方案,减少跨业务开发的理解和维护成本。本文主要针对iOS端在IM消息流交互层的设计上,提供一些实践思路。

图片

2、业界的实现方案

目前业界有各种即时通讯服务商提供的配套交互层解决方案,其大多以牺牲灵活性来满足快速集成需要,在定制能力上远不能胜任我们业务需要。

再诸如 MessageKit之类的社区IM框架,其在视觉交互表现上功能完备,能帮助我们快速、灵活搭建IM消息流结构,但业务需要的是一套完整的携带消息交互能力的方案,因此对此类框架,仍需要做不小的改造才能适应我们的业务(另一参考方案:MobileIMSDK(Gitee源码托管地址))。

3、我们的想法

对于一个IM消息流交互层方案,主要考虑几个方面:
1)规范的消息流结构:提供消息流视图结构规范化的构建方式;
2)标准的消息交互能力:统一消息交互能力,业务方按需使用,快速集成;
3)业务拓展性:针对数据源、消息交互能力提供业务灵活拓展点;
4)业务接入成本:内置通用交互方案,降低业务接入成本。

目前,我们存量业务中的IM场景,底层IM能力主要由云信引擎提供。同时又存在基于业务服务端,通过HTTP去交互的场景。另外,还需要预留后期切换IM引擎的可能性,因此需要将交互层IM能力抽象出来。此外,为了适应团队现状,减小业务接入成本,考虑将云信提供的交互能力内置在方案中。

4、整体设计

设计愿景:提供标准化的能力,同时对拓展开放。我们期望一套通用的IM消息流能力,能够在方案上标准化。这里的标准化,主要包含消息流结构构建的标准化,以及消息交互能力的标准化。同时,方案需要在交互能力上适应不同业务场景,因此采用依赖注入的方式,提供业务定制能力。按照职能划分,将框架整体分为了两层:
图片

 1)消息流结构层:负责消息流结构的构建,定义消息视图、布局、数据上的规范,提供业务层分别在「消息」、「会话」两个维度的配置能力。

2)消息交互层:提供消息能力、消息流、消息数据方面的交互能力,向下依赖交互接口,内置标准交互能力的同时,也支持业务按需注入交互实现。

5、聊天消息流的显示结构

5.1 消息组件

不同的业务场景,消息流样式表现必然有所差异。下面列出了我们几个业务中的消息流界面:
图片

如何设计一套通用的消息流视图结构,满足不同业务需要?经过对各个业务以及一些主流IM工具的观察,将消息视图结构设计成如下结构,是能够满足我们各个IM场景需要的(见下图)。

图片

我将消息结构拆分成了5部分,对应5个消息组件  MessageView ,每个消息组件都支持业务对其「样式」、「显隐」、「布局」进行配置,从而满足不同场景定制需要。MessageView作为基础消息组件,提供了一些标准能力,例如是否响应菜单动作 canPerformMenuAction 、视图重用回调时机 prepareForReuse 、尺寸策略等。open class MessageView: MessageAbstractView {  public var canPerformMenuAction = false    open func refresh(with message: Message) {}    open func prepareForReuse() {}    open class func createSizeStrategy(message: Message, fittingSize: CGSize) -> MessageLayoutSizeStrategy? {    // ...    }}

5.2 尺寸策略

消息组件尺寸作为消息流布局上不可或缺的要素,方案提供了多种尺寸计算策略 MessageLayoutSizeStrategy 。

具体是:
1)自动布局计算策略:业务方对消息组件使用 AutoLayout 布局时使用,内部会依据约束自动计算好组件尺寸;
2)SizeThatFit 策略:依据组件 SizeThatFit 方法返回的尺寸进行布局;
3)自定义策略:提供自定义尺寸计算方式。

public protocol MessageLayoutSizeStrategy {    func caclulateSize(_ sizeViewType: MessageView.Type,                       message: Message,                       fittingSize: CGSize) -> CGSize} public struct MessageAutoLayoutSizeStrategy: MessageLayoutSizeStrategy {    public func caclulateSize(_ sizeViewType: MessageView.Type,                              message: Message,                              fittingSize: CGSize) -> CGSize {    // ...省略其他代码        return sizeView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)    } } public struct MessageSizeThatFitsStrategy: MessageLayoutSizeStrategy {    public func caclulateSize(_ sizeViewType: MessageView.Type,                              message: Message,                              fittingSize: CGSize) -> CGSize  {        // ...省略其他代码        return sizeView.sizeThatFits(fittingSize)    }}

5.3 布局快照

我们还针对消息组件维度支持了布局快照。通常当一个消息组件尺寸固定,在交互过程中尺寸不会发生的情况下,打开布局快照,以减少布局计算消耗。同时也提供了快照清除的能力。我们对多个消息流在快速滚动过程中的CPU峰值做了统计,在使用自动布局尺寸策略的情况下,开启布局快照,峰值降低了10%~20%。

图片

5.4 交互事件

另外在手势交互上,对外暴露了各个消息组件的一系列交互事件。常见的场景例如单击浏览消息内容,长按展示消息菜单等。方案内部提供了基于系统样式的长按菜单,并提供上层菜单配置能力,同时也可以基于暴露的长按手势事件来自定义菜单。5.5 消息流一个会话对应一个流,方案也提供了消息流在会话维度上的一些标准化配置。例如消息分页数量、是否自动拉取历史消息、是否开启增量刷新,以及在时间展示上的样式配置等。此外为了减少列表重绘,消息流也支持增量刷新。通常情况下业务层不需要主动刷新列表,只需对消息数据进行增删改操作,内部会触发对数据源的「diff-update」计算,从而驱动列表的增量更新。
图片

6、聊天消息交互层

6.1 概述

对于业务方而言,在消息交互上通常关心这么几点:
1)提供了哪些标准化的交互能力;
2)如何拓展自定义的交互实现;
3)如何对交互流程进行干预。

结合团队现状,我们在方案内部内置了基于某信的IM交互能力,同时定义了相关交互接口,供业务方按需注入实现。在实际业务中,一个APP内可能存在多个IM场景,因此交互能力支持按会话维度进行注入,各个会话之间的交互是相互隔离的。

6.2 消息源

不同的IM场景,消息数据来源可能存在差异。例如我们私聊、群聊的数据源来自云信数据同步服务,聊天室数据需要通过云信提供的历史消息接口拉取,另外也存在诸如通过业务服务端接口来拉取消息数据的场景。因此方案上设置了数据源接口 SessionMessageProvider ,提供不同场景消息源的定制能力。public protocol SessionMessageProvider {    func messages(in session: Session,                  anchorMessage: Message?,                  limit: Int,                  completion: @escaping ([Message]) -> Void)}方案设置了一个负责管理消息数据源的 DataManager 实例, 其依赖 SessionMessageProvider 提供的数据源。同时内置了基于云信的数据源获取实现,能够根据当前会话类型,获取私聊、群聊、聊天室的数据源。如果当前场景是通过HTTP拉取消息的,则需要业务上层手动注入一个从接口获取数据源的 SessionMessageProvider 实例。
图片

6.3 交互源

方案提供了IM标准交互能力,例如消息收发、消息撤回、保存等,以统一各业务交互姿势。具体的交互源除了要考虑目前包含的云信及业务服务端,也要适应其他交互源,因此将交互实现部分也抽象出了接口 MessageServiceInterface 。业务根据当前实际场景,注入具体的交互实现即可。下面列出了一些交互申明:public protocol MessageServiceInterface {    func send(message: Message, in session: Session, completion: @escaping MessageServiceInterfaceCompletion)    func resend(message: Message, completion: @escaping MessageServiceInterfaceCompletion)    func forward(message: Message, to session: Session, completion: @escaping MessageServiceInterfaceCompletion)    func revoke(message: Message, completion: @escaping MessageServiceInterfaceCompletion)    func save(message: Message, in session: Session, completion: @escaping MessageServiceInterfaceCompletion)    func delete(message: Message, completion: @escaping MessageServiceInterfaceCompletion)}同样,我们也内置了一些通用交互方案,例如支持云信提供的私聊群聊交互能力,以及由中台提供的通用聊天室服务交互能力,以支持相关场景下快速接入。
图片

6.4 交互钩子

在实际IM业务开发过程中,往往需要对交互流程做一些干预,或是在交互过程中做一些定制化的动作。因此方案也提供了一些交互钩子,支持「交互前置校验」、「交互前准备」。以消息发送流程为例,提供了「发送前校验」、「发送准备」两个消息发送过程的回调钩子:public protocol MessageServicePrechecker {   // 消息发送前置校验     func shouldSend(message: Message, in session: Session) -> Bool     // ...省略其他代码} public protocol MessageServicePreparation {    /// 准备发送准备    func prepareSend(message: Message, in session: Session, callback: @escaping MessageServicePreparationCallback)     // ...省略其他代码}

整体的发送流程如图所示:
图片

前置校验阶段,用来作消息发送前的校验工作,根据实际状态决定消息是否可以发送。发送准备阶段,则可以在消息投递前做最后的准备工作,例如海外业务可以在这里处理消息资源附件上传Amazon,或是在此处对消息塞入一些客户端信息、反作弊Token等,支持异步操作。

7、业务接入能力

业务只需要在上层提供针对消息以及会话两个维度的配置,就能基于内置的交互能力,构建出一套基础的IM消息流能力。在具体的消息样式呈现上,则通常需要业务层维护一组关于「消息类型-消息组件类型-消息结构」的映射关系。

具体关联如下:
图片

在交互能力上,提供了IM场景的标准能力,业务可以按需使用。另外,实际IM场景可能需要一些更为丰富的定制能力,则可以依据方案提供的消息数据源接口、消息交互接口来对具体交互实现进行定制。同时也可以使用相关的交互钩子对交互过程进行干预,以适应自己的业务。

8、本文小结

本文对团队IM场景的现状做了简单介绍,撇开具体实现细节,就如何搭建一套能够适应多业务需要的通用IM消息流交互层方案,提供了一些思考和实践经验。

从结果来看,该方案稳定支撑了团队多个IM场景,抹除各场景实现差异,有效降低了维护成本和新业务接入成本。

9、参考资料

[1] 零基础IM开发入门(一):什么是IM聊天系统?
[2] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)
[3] 一套原创分布式即时通讯(IM)系统理论架构方案
[4] 从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结
[5] 社交软件红包技术解密(十):手Q客户端针对2020年春节红包的技术实践
[6] 微信团队分享:来看看微信十年前的IM消息收发架构,你做到了吗
[7] 携程技术分享:亿级流量的办公IM及开放平台技术实践
[8] 百度公共IM系统的Andriod端IM SDK组件架构设计与技术实现
[9] 转转平台IM系统架构设计与实践(一):整体架构设计
[10] 一年撸完百万行代码,企业微信的全新鸿蒙NEXT客户端架构演进之路
[11] 转转客服IM聊天系统背后的技术挑战和实践分享
[12] B站IM消息系统的新架构升级实践
[13] 企业微信针对百万级组织架构的客户端性能优化实践
[14] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
[15] 从客户端的角度来谈谈移动端IM的消息可靠性和送达机制
[16] 现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障
[17] IM消息ID技术专题(一):微信的海量IM聊天消息序列号生成实践(算法原理篇)
[18] IM开发干货分享:有赞移动端IM的组件化SDK架构设计实践
[19] 阿里技术分享:闲鱼IM基于Flutter的移动端跨端改造实践
[20] IM开发干货分享:万字长文,详解IM“消息“列表卡顿优化实践
[21] IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结
[22] 百度统一socket长连接组件从0到1的技术实践
[23] 淘宝移动端统一网络库的架构演进和弱网优化技术实践
[24] 抖音技术分享:飞鸽IM桌面端基于Rust语言进行重构的技术选型和实践总结
[25] 大型IM工程重构实践:企业微信Android端的重构之路

即时通讯技术学习: