Rosetta Experimental Learning Hymotion1.0
HY-Motion 1.0 底层架构原理的罗塞塔石碑示踪分析
核心理念:通过追踪"文本提示"→"SMPL-H运动"的数据流转路径,穿透从用户输入到3D人体运动生成的完整技术栈,破译 HY-Motion 1.0 的黑盒系统。
🎯 为什么要用这个方法分析 HY-Motion 1.0?
现实痛点
| 痛点场景 | 不懂底层怎么做 | 懂底层怎么做 |
|---|---|---|
| 生成的运动抖动 | 猜测数据质量问题,不知道具体原因 | 理解Flow Matching的ODE求解路径,知道是噪声分布问题 |
| 语义不遵循 | 模糊提示词,反复试验 | 理解双流注意力机制的非对称掩码,知道如何精准控制 |
| 训练不收敛 | 盲目调参,增加数据 | 理解三阶段训练范式的学习率衰减策略 |
| 性能瓶颈 | 不知道优化点 | 理解DiT架构的局部窗口掩码,知道复杂度瓶颈 |
理解底层能带来什么
层次1:调试能力 ↑
从"猜测" → "精确定位"
示例:生成的运动有足部滑动,能立即定位到预训练数据质量
层次2:性能优化 ↑
从"盲目调参" → "针对性优化"
示例:从增加数据量到优化Flow Matching的步数
层次3:架构能力 ↑
从"使用模型" → "设计模型"
示例:理解为什么需要双流-单流混合架构
层次4:研究能力 ↑
从"复现代码" → "创新改进"
示例:能设计新的运动生成模型
📐 第一步:定层级
目标:明确数据流转经过的每一层级
核心公式:
Input(用户文本提示) → 转换器 → Target(SMPL-H 3D运动)
划分原则:
- ✅ 按照数据形态变化划分
- ✅ 上层必须极简(文本,人可读)
- ✅ 底层必须具体(SMPL-H参数,可观测)
层级定义表
| 层级 | 名称 | 数据形态 | 示例 | 可观测性 |
|---|---|---|---|---|
| 层级1 | 用户输入层 | 自然语言文本 | “A person kicks a ball” | ✅ 用户输入 |
| 层级2 | LLM优化层 | 优化提示 + 预测时长 | “A person kicks a ball, extending their leg forward.” + “3.2秒” | ✅ Qwen3-30B-A3B输出 |
| 层级3 | 文本编码层 | 双重嵌入向量 | Qwen3-8B: (768维 × N_tokens) CLIP-L: (768维全局) | ✅ 模型前向传播 |
| 层级4 | Flow Matching层 | 概率路径与速度场 | xₜ = (1-t)x₀ + tx₁ v_θ(xₜ, c, t) | ✅ ODE求解器追踪 |
| 层级5 | DiT架构层 | 双流-单流注意力 | 双流: 独立QKV-MLP 单流: 拼接序列注意力 | ✅ 中间特征可视化 |
| 层级6 | 运动表示层 | SMPL-H 201维向量 | root位移(3) + 全局方向(6) + 关节旋转(126) + 关节位置(66) | ✅ 输出向量 |
| 层级7 | 3D渲染层 | 可视化人体网格 | SMPL-H mesh + 纹理 | ✅ 渲染视频 |
为什么要分层?
因为每一层都是数据的一次"形态转换":
文本 → 优化文本 → 嵌入向量 → 速度场 → 运动潜在表示 → SMPL-H参数 → 3D网格
只有追踪形态的变化,才能回答:
"我的文本如何变成运动?"
"Flow Matching如何生成连续运动?"
"为什么需要三阶段训练?"
"双流-单流架构如何工作?"
🚧 第二步:定关卡
目标:找到数据流转的必经之路
关卡特征:
- ✅ 数据必经:所有数据必须通过这个点
- ✅ 形态突变:数据在这里发生形态转换
- ✅ 可观测:有工具可以拦截/观察
关卡表格
| 关卡 | 数据转换对象 | 关键问题 | 主要观测工具 |
|---|---|---|---|
| 关卡1 LLM优化 | 用户提示 → 优化提示 + 预测时长 | 如何预测运动时长? | Qwen3-30B-A3B前向传播 |
| 关卡2 双重编码 | 优化提示 → Qwen3-8B token嵌入 + CLIP-L全局嵌入 | 为什么要两个编码器? | 嵌入向量可视化 |
| 关卡3 双流注意力 | 文本/运动token → 独立QKV-MLP + 联合注意力 | 如何保护文本语义不被噪声污染? | 注意力权重可视化 |
| 关卡4 Flow Matching初始化 | 噪声x₀ → 线性插值xₜ | 如何建立最优传输路径? | xₜ轨迹追踪 |
| 关卡5 速度场预测 | xₜ + c + t → v_θ(xₜ, c, t) | 模型预测什么? | 速度向量可视化 |
| 关卡6 ODE求解 | 速度场 → 干净运动x₁ | 如何从噪声恢复运动? | Euler solver中间状态 |
| 关卡7 SMPL-H解码 | 201维向量 → 3D网格 | 如何解释向量各维度? | SMPL-H参数映射 |
关卡因果链路(横向展示)
用户提示 "A person kicks a ball" ━━━━━━━━━━━━━━━━━━━━━━━━━┓
↓
LLM优化模块
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┓
↓ ↓
成功优化 优化失败
↓ ↓
优化提示 + 预测时长 降级到原始提示
"A person kicks a ball,
extending their leg forward."
预测时长: "3.2秒"
↓
双重编码器
├─ Qwen3-8B (细粒度token)
└─ CLIP-L (全局语义)
↓
Bidirectional Token Refiner
(因果注意 → 双向表示)
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
↓ ↓
双流模块 (1/3层) 单流模块 (2/3层)
文本流 & 运动流独立处理 拼接序列 + 深度融合
非对称注意力掩码: 并行空间+通道注意力
- 运动→文本: 允许
- 文本→运动: 禁止
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
↓
Flow Matching初始化
x₀ ~ N(0, I) # 高斯噪声
xₜ = (1-t)x₀ + t·x₁ # 线性插值
↓
速度场预测
v_θ(xₜ, c, t) → v_target = x₁ - x₀
↓
ODE求解器 (推理)
dx/dt = v_θ(xₜ, c, t)
Euler积分: x_{t+1} = x_t + v·dt
↓
┏━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┓
↓ ↓
训练阶段 推理阶段
监督速度预测 数值积分求解
MSE(v_pred, v_target) 从x₀→x₁
↓ ↓
SMPL-H解码 (201维向量)
├─ root_translation (3维)
├─ body_orientation (6维)
├─ joint_rotations (126维: 21关节 × 6)
└─ joint_positions (66维: 22关节 × 3)
↓
3D人体运动渲染
"踢球" 动作 @ 30fps
总时长 3.2秒 (96帧)
排查问题的链路
现象:生成的运动不遵循提示词
↓
检查关卡1(LLM优化)→ 优化提示是否保留语义?
↓ 否
→ 调整LLM温度参数
↓ 是
检查关卡2(双重编码)→ 嵌入向量是否正确?
↓ 否
→ 检查Qwen3-8B和CLIP-L输出
↓ 是
检查关卡3(双流注意力)→ 掩码是否正确应用?
↓ 否
→ 检查非对称注意力配置
↓ 是
检查关卡5(速度场预测)→ 模型是否学习到正确映射?
↓ 否
→ 增加训练数据或调整学习率
↓ 是
找到问题根源
🛠️ 第三步:架工具
目标:推荐在该关卡截获数据的工具
工具选择标准:
- ✅ 能让时间静止(Snapshot)
- ✅ 能留下痕迹(Log)
- ✅ 可重复执行
关卡1:LLM优化层工具
| 工具 | 解决痛点 | 命令/方法 | 功能 |
|---|---|---|---|
| 前向传播Hook | 查看LLM中间输出 | register_forward_hook | 捕获Qwen3-30B的隐藏状态 |
| 温度采样 | 控制生成随机性 | temperature=0.7 | 调整输出多样性 |
| 对比解码 | 验证优化效果 | 对比原始提示vs优化提示 | 评估语义保真度 |
关卡2:文本编码层工具
| 工具 | 解决痛点 | 命令/方法 | 功能 |
|---|---|---|---|
| Qwen3-8B Hook | 查看细粒度token嵌入 | 模型中间层输出 | 捕获每个token的768维向量 |
| CLIP-L编码 | 查看全局语义嵌入 | clip_model.encode_text() | 捕获768维全局向量 |
| Token Refiner追踪 | 理解双向转换 | 对比因果vs双向注意力 | 查看形态变化 |
| 嵌入可视化 | 理解语义空间 | t-SNE/UMAP降维 | 可视化token聚类 |
关卡3:DiT架构层工具
| 工具 | 解决痛点 | 命令/方法 | 功能 |
|---|---|---|---|
| 注意力权重Hook | 查看双流交互 | attn_map = model.attn_weights | 可视化注意力图 |
| 掩码验证 | 确认非对称性 | 检查attention_mask | 验证运动→文本单向流动 |
| RoPE位置编码 | 理解跨模态坐标 | apply_rotary_pos_emb | 查看相对位置编码 |
| 局部窗口掩码 | 验证121帧窗口 | 检查temporal_mask | 确认时序局部性 |
关卡4-5:Flow Matching层工具
| 工具 | 解决痛点 | 命令/方法 | 功能 |
|---|---|---|---|
| 轨迹追踪 | 观察xₜ插值过程 | 保存每个时间步的xₜ | 可视化噪声→清晰路径 |
| 速度场可视化 | 理解v_θ预测 | 保存v_θ(xₜ, c, t) | 查看向量场方向 |
| ODE求解器Log | 追踪Euler积分 | 记录每步的x和v | 验证数值稳定性 |
| 损失曲线 | 监控训练收敛 | MSE(v_pred, v_target) | 评估训练质量 |
关卡6-7:输出层工具
| 工具 | 解决痛点 | 命令/方法 | 功能 |
|---|---|---|---|
| SMPL-H参数解码 | 理解向量各维度 | smpl_model.decode(201维向量) | 转换为3D网格 |
| 运动可视化 | 直观查看生成结果 | 渲染为MP4视频 | 评估运动质量 |
| VLM评估 | 自动评分 | Gemini-2.5-Pro问答 | 计算SSAE指标 |
| 人工评分 | 主观质量评估 | 人类打分1-5 | Pass Rate统计 |
💉 第四步:投示踪
目标:设计示踪剂,追踪数据在各层级的转换
示踪剂设计原则:
❌ 不要:普通提示词(难以追踪)
✅ 推荐:
- 独特性:包含罕见动作组合(一眼能认出)
- 语义化:明确的解剖学描述(知道映射关系)
- 多样性:覆盖不同运动类别(广度测试)
- 高熵值:不会自然出现的组合
🧪 完整实验流程
下面通过HY-Motion 1.0的文本→运动生成的完整追踪,展示罗塞塔石碑实验法的应用。
每个实验都按照以下结构展开:
- 源输入:用户提供的文本
- 埋示踪:需要追踪的关键标记
- 观察分析:如何观察结果,建立映射
🧪 实验1:LLM优化层(理解提示词转换)
🎯 为什么要学这个?
现实痛点:
- 用户输入模糊,生成结果不准确
- 不知道如何控制运动时长
- 提示词不遵循模型期望的格式
学习目标:理解"用户随意输入"如何变成"结构化提示"
📝 源输入
用户输入(示踪剂设计):
"踢球"
设计原则:
- ✅ 极简性:只有两个中文字符
- ✅ 语义化:明确的动作类型
- ✅ 可扩展:可以测试不同复杂度
💉 埋示踪
执行步骤:
# 伪代码:追踪LLM优化过程
user_prompt = "踢球"
# 步骤1: LLM前向传播
optimized_prompt, duration = llm_module(user_prompt)
# 步骤2: Hook中间层
with hook(llm_model.layers[-1]):
hidden_states = llm_model.forward(user_prompt)
# 步骤3: 保存输出
print(f"优化提示: {optimized_prompt}")
print(f"预测时长: {duration}秒")
# 步骤4: 追踪token转换
tokens = tokenizer.encode(user_prompt)
optimized_tokens = tokenizer.encode(optimized_prompt)
👀 观察分析:建立映射关系
🔷 观察1:提示词优化
👁️ 观测结果
输入: "踢球"
输出:
优化提示: "A person kicks a ball, extending their leg forward."
预测时长: "3.2秒"
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| LLM优化 | 利用Qwen3-30B-A3B常识 | 补充缺失细节 | 模糊输入→精确输出 |
| 时长预测 | 基于动作类型推断 | 匹配运动数据分布 | 避免生成过长/过短 |
| 结构化输出 | 符合训练数据格式 | 提高DiT理解度 | 提升生成质量 |
🔗 认知映射
用户输入 LLM优化后
"踢球" → "A person kicks a ball,
extending their leg forward."
+ "3.2秒"
↓
理解:LLM充当"翻译器"
1. 将模糊输入转化为详细的英文描述
2. 预测合理的运动时长
3. 使输入符合DiT训练时见过的格式
🔷 观察2:Token转换
👁️ 观测结果
原始token: ["踢", "球"] (2个token)
优化token: ["A", " person", " kicks", " a", " ball", ",", " extending",
" their", " leg", " forward", "."] (11个token)
隐藏状态维度: [11, 4096] (Qwen3-30B的hidden_size)
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| Token膨胀 | 2→11个token | 英文比中文更详细 | 理解语言差异影响 |
| 隐藏状态 | 4096维向量 | 捕获丰富语义 | 理解LLM表示能力 |
| 上下文窗口 | 最多32K tokens | 处理长提示 | 理解长度限制 |
🔗 认知映射
输入长度 语义丰富度
2 tokens (中文) → 低(缺少细节)
11 tokens (英文) → 高(补充细节)
↓
理解:优化不只是翻译,更是语义增强
补充了"extending leg"等运动学细节
🎯 这个实验能解决什么问题?
问题1:提示词不遵循
现象:生成运动不符合用户意图
原因:用户输入太模糊
解决:LLM优化层自动补充细节
问题2:时长不合理
现象:生成的运动太长或太短
原因:没有时长达标
解决:LLM基于常识预测合理时长
问题3:语言差异
现象:中文输入效果不如英文
原因:训练数据主要是英文
解决:LLM自动翻译为优化英文
🧪 实验2:双重编码层(理解语义提取)
🎯 为什么要学这个?
现实痛点:
- 不知道为什么需要两个编码器
- 理解细粒度vs全局语义的区别
- 语义不遵循时不知道排查哪一层
学习目标:理解"文本"如何变成"嵌入向量"
📝 源输入
优化提示(示踪剂):
"A person kicks a ball with their right foot,
while swinging their arms backward for balance."
设计原则:
- ✅ 多关节:右脚(主要)+ 手臂(辅助)
- ✅ 明确侧边:right foot, backward
- ✅ 语义细节:for balance(解释性)
💉 埋示踪
执行步骤:
# 伪代码:追踪双重编码
prompt = "A person kicks a ball with their right foot..."
# 编码器1: Qwen3-8B (细粒度)
qwen_embeddings = qwen_model.encode(prompt)
# 输出: [N_tokens, 768]
# 示例: [14, 768] (14个token)
# Hook: 查看每个token的嵌入
for i, token in enumerate(tokens):
print(f"Token {i}: {token} → {qwen_embeddings[i]}")
# 编码器2: CLIP-L (全局)
clip_embeddings = clip_model.encode_text(prompt)
# 输出: [768]
# Bidirectional Token Refiner
bidirectional_embeddings = token_refiner(qwen_embeddings)
# 输出: [N_tokens, 768] (因果→双向)
👀 观察分析:建立映射关系
🔷 观察1:细粒度token嵌入
👁️ 观测结果
Token 0: "A" → vec[0] (768维)
Token 1: " person" → vec[1] (768维)
Token 2: " kicks" → vec[2] (768维) ← 关键动作
Token 3: " a" → vec[3] (768维)
Token 4: " ball" → vec[4] (768维) ← 关键对象
Token 5: " with" → vec[5] (768维)
Token 6: " their" → vec[6] (768维)
Token 7: " right" → vec[7] (768维) ← 关键属性
Token 8: " foot" → vec[8] (768维) ← 关键身体部位
...
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| Token级嵌入 | 每个词独立向量 | 保留细粒度语义 | 区分left/right等细节 |
| 768维空间 | 高维语义空间 | 捕获复杂语义关系 | 理解语义相似度 |
| 位置敏感 | token[i] ≠ token[j] | 保留词序信息 | 理解"kicks ball" vs “ball kicks” |
🔗 认知映射
文本 细粒度嵌入
"kicks" → vec[2] ≈ [0.2, -0.5, ..., 0.8]
"right foot" → vec[7:9] (两个token)
↓
理解:每个词都有自己的向量表示
DiT可以在生成时attend到特定token
例如:第50帧attend到"kicks" token触发踢球动作
🔷 观察2:全局语义嵌入
👁️ 观测结果
CLIP-L输出: [768] 全局向量
语义: "全身运动、球类运动、下肢主导、单腿动作"
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| 全局嵌入 | 整句压缩为一个向量 | 捕获整体语义 | AdaLN调制全局风格 |
| 768维固定 | 不随token数变化 | 适配不同长度输入 | 统一维度便于融合 |
| 与timestep拼接 | [768] + [timestep_emb] | 时间条件化 | 控制去噪进度 |
🔗 认知映射
整句文本 全局嵌入
"A person kicks..." → vec_global[768]
↓
理解:CLIP-L提取的是"整体氛围"
类似于"这是一段运动场景"的标签
通过AdaLN调制DiT的所有层
🔷 观察3:双向Token Refiner
👁️ 观测结果
输入(因果注意):
Token 5 ("with")只能attend到Token 0-4
输出(双向注意):
Token 5可以attend到所有token(包括"right foot"在后面)
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| 因果注意 | 只能看前面token | GPT类模型的设计 | 限制语义理解 |
| 双向注意 | 可以看所有token | BERT类交互式理解 | 更好的上下文 |
| Token Refiner | 转换模块 | 适配DiT的非自回归 | 解决因果限制 |
🔗 认知映射
Qwen3-8B(因果) Token Refiner DiT(双向)
"with"看后面 ❌ → 转换 → "with"可以看"right foot" ✅
↓
理解:Refiner是桥梁
将LLM的因果表示转换为DiT需要的双向表示
类似于"回头看"的能力
🎯 这个实验能解决什么问题?
问题1:细粒度控制失败
现象:无法区分左右手
原因:token嵌入没有捕获细节
解决:检查Qwen3-8B是否输出正确token向量
问题2:整体风格不对
现象:动作对但风格怪异
原因:CLIP-L全局嵌入偏离
解决:检查CLIP-L编码是否正确
问题3:长文本理解差
现象:复杂提示词后半部分被忽略
原因:因果注意限制
解决:Token Refiner双向化后应该解决
🧪 实验3:DiT双流架构(理解跨模态交互)
🎯 为什么要学这个?
现实痛点:
- 不知道为什么要双流-单流混合
- 不理解非对称注意力掩码
- 语义污染问题不知道怎么解决
学习目标:理解"文本"和"运动"如何交互
📝 源输入
文本: "A person raises their right hand."
运动噪声: xₜ (96帧 × 201维 = 19296维噪声向量)
💉 埋示踪
执行步骤:
# 伪代码:追踪双流交互
text_tokens = qwen_embeddings # [N, 768]
motion_tokens = xₜ # [96, 201]
# === 双流阶段 ===
# 文本流(独立处理)
text_QKV = text_QKV_proj(text_tokens)
text_MLP = text_MLP_proj(text_tokens)
# 运动流(独立处理)
motion_QKV = motion_QKV_proj(motion_tokens)
motion_MLP = motion_MLP_proj(motion_tokens)
# 联合注意力(非对称)
# motion_tokens可以attend到text_tokens
# text_tokens被mask,不能attend到motion_tokens
joint_attn = asymmetric_attention(
query=motion_QKV.Q,
key=text_QKV.K,
value=text_QKV.V,
mask=text_to_motion_mask # 允许motion→text
)
# === 单流阶段 ===
# 拼接文本和运动
unified_tokens = concat([text_tokens, motion_tokens])
# 应用RoPE位置编码
unified_tokens = apply_rope(unified_tokens)
# 并行空间+通道注意力
output = parallel_spatial_channel_attn(unified_tokens)
👀 观察分析:建立映射关系
🔷 观察1:非对称注意力掩码
👁️ 观测结果
# 注意力掩码矩阵 (motion_tokens=96, text_tokens=14)
attn_mask = [
[0, 0, ..., 0], # motion token 0 可以attend到所有text
[0, 0, ..., 0], # motion token 1 可以attend到所有text
...
[0, 0, ..., 0], # motion token 95 可以attend到所有text
]
# 反向(text→motion)被mask
text_attn_mask = [
[-inf, -inf, ..., -inf], # text token 0 不能attend到motion
[-inf, -inf, ..., -inf], # text token 1 不能attend到motion
...
]
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| 非对称掩码 | motion→text允许,text→motion禁止 | 防止噪声污染语义 | 保持文本纯净性 |
| 扩散噪声 | motion_tokens包含高斯噪声 | Flow Matching的特性 | 噪声会反向传播 |
| 语义保护 | text_tokens作为条件 | 指导生成方向 | 避免条件漂移 |
🔗 认知映射
双流交互逻辑:
运动帧 (含噪声) ━━→ attend to ━━→ 文本tokens (纯净)
✅ 允许 ✅ 纯净
文本tokens (纯净) ━━→ attend to ━━→ 运动帧 (含噪声)
❌ 禁止 ❌ 污染源头
↓
理解:单向信息流
运动可以"问"文本"怎么做"
文本不会"被"运动污染
类似于"学生问老师,老师不被学生带偏"
🔷 观察2:RoPE位置编码
👁️ 观测结果
# 统一序列: [text_tokens, motion_tokens]
unified_seq = concat([text_emb, motion_emb])
# shape: [14 + 96, 768] = [110, 768]
# 应用RoPE
rope_seq = apply_rotary_pos_emb(unified_seq)
# 相对位置可解析
text_token[5]与motion_token[20]的相对距离 = 15
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| RoPE | 旋转位置编码 | 编码相对位置 | 建立跨模态对应 |
| 统一序列 | text+motion拼接 | 共享坐标系 | 文本第i词对应运动第j帧 |
| 连续性 | 相对位置平滑 | 适应变长输入 | 泛化到不同时长 |
🔗 认知映射
文本token "right" (位置5)
↓ (RoPE建立坐标系)
运动帧 第20帧 (位置5+15=20)
↓
理解:RoPE让模型知道
"文本第5个词对应运动第20帧"
类似于"时间戳对齐"
🔷 观察3:局部窗口掩码(121帧)
👁️ 观测结果
# 121帧窗口 = @30fps约4秒
window_size = 121
# 对于第i帧,只能attend到[i-60, i+60]
temporal_mask[i, j] = 0 if abs(i-j) <= 60 else -inf
# 复杂度: O(N × window_size) 而非 O(N²)
# 对于96帧: 96 × 121 ≈ 11.6K (vs 96² = 9.2K,长序列时更优)
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| 局部窗口 | 限制时序注意力范围 | 运动学局部连续性 | 降低复杂度 |
| 归纳偏置 | “运动主要依赖相邻帧” | 物理规律约束 | 提高泛化 |
| 线性复杂度 | O(N × window_size) | 扩展到长序列 | 处理更长运动 |
🔗 认知映射
长序列(12秒=360帧):
全局注意力: 360² = 129.6K
局部窗口: 360 × 121 = 43.6K (节省66%)
假设:
第i帧的姿态主要由[i-2秒, i+2秒]决定
↓
理解:局部性是合理的
人体运动是连续的,不需要看很远
类似于"短期记忆"
🎯 这个实验能解决什么问题?
问题1:语义漂移
现象:生成到一半忘记提示词
原因:噪声反向污染文本
解决:非对称掩码保护文本
问题2:生成质量差
现象:运动不连贯
原因:窗口太小或全局注意力太稀疏
解决:调整121帧窗口大小
问题3:长序列生成失败
现象:超过4秒就崩溃
原因:复杂度爆炸
解决:局部窗口降低复杂度
🧪 实验4:Flow Matching(理解生成过程)
🎯 为什么要学这个?
现实痛点:
- 不理解Flow Matching vs DDPM区别
- 不知道为什么要最优传输路径
- 推理步数不会调整
学习目标:理解"噪声"如何变成"运动"
📝 源输入
文本: "A person waves their left hand."
目标运动: x₁ (96帧 × 201维干净运动)
💉 埋示踪
执行步骤:
# === 训练阶段 ===
def training_step(x₁, c):
# x₁: 干净运动
# c: 文本条件
# 1. 采样噪声
x₀ = torch.randn_like(x₁) # 高斯噪声
# 2. 采样时间步
t = torch.rand(1) # [0, 1]均匀分布
# 3. 构造最优传输路径
xₜ = (1 - t) * x₀ + t * x₁ # 线性插值
# 4. 计算目标速度
v_target = x₁ - x₀ # 常数向量
# 5. 模型预测
v_pred = model(xₜ, c, t)
# 6. 计算损失
loss = MSE(v_pred, v_target)
return loss
# === 推理阶段 ===
def inference_step(c, num_steps=50):
# 1. 初始化噪声
x = torch.randn([96, 201])
# 2. Euler积分
dt = 1.0 / num_steps
for i in range(num_steps):
t = torch.tensor([i / num_steps])
v = model(x, c, t)
x = x + v * dt
# Hook: 保存中间状态
save_intermediate(x, i)
return x # ≈ x₁
👀 观察分析:建立映射关系
🔷 观察1:最优传输路径
👁️ 观测结果
t=0.0: x₀ = 纯噪声 (高斯分布)
t=0.2: x₀.₂ = 0.8x₀ + 0.2x₁ (80%噪声 + 20%信号)
t=0.5: x₀.₅ = 0.5x₀ + 0.5x₁ (50%噪声 + 50%信号)
t=0.8: x₀.₈ = 0.2x₀ + 0.8x₁ (20%噪声 + 80%信号)
t=1.0: x₁ = 干净运动 (0%噪声 + 100%信号)
关键: xₜ的路径是直线!
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| 最优传输 | x₀→x₁的最短路径 | 最小化计算量 | 快速收敛 |
| 线性插值 | xₜ = (1-t)x₀ + tx₁ | 常数速度场 | 简化学习目标 |
| 常数速度 | v_target = x₁ - x₀ | 与时间无关 | 模型更容易学习 |
🔗 认知映射
DDPM (随机路径):
x₀ ──[弯曲路径]──→ x₁
速度场变化复杂,需要数百步
Flow Matching (直线):
x₀ ──[直线]──→ x₁
速度场是常数,只需几十步
↓
理解:最优传输 = 最短路径
Flow Matching比DDPM快5-10倍
🔷 观察2:速度场预测
👁️ 观测结果
# 时间步 t=0.5
xₜ = (1-0.5)*x₀ + 0.5*x₁ # 50%噪声
# 模型预测
v_pred = model(xₜ, c, t=0.5)
# shape: [96, 201]
# 目标
v_target = x₁ - x₀
# shape: [96, 201]
# 可视化
plt.quiver(v_pred[:, 0], v_pred[:, 1]) # 速度向量场
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| 速度场 v_θ | 每个点的移动方向和大小 | 指导去噪方向 | 逐步逼近目标 |
| 条件c | 文本嵌入 | 语义指导 | 确保运动符合提示 |
| 时间步t | 去噪进度 | 适配不同噪声级别 | 从粗到细细化 |
🔗 认知映射
速度场的作用:
第50帧的当前状态 xₜ[50]
↓
查询速度场 v_θ(xₜ[50], "waves hand", t=0.5)
↓
得到移动方向 v[50]
↓
更新: xₜ+1[50] = xₜ[50] + v[50] * dt
↓
理解:速度场是"导航图"
告诉每个像素点"往哪个方向走"
类似于"梯度下降"
🔷 观察3:Euler积分过程
👁️ 观测结果
# 50步Euler求解
step=0: x是纯噪声, PSNR=5dB
step=10: x有模糊轮廓, PSNR=12dB
step=25: x有清晰动作, PSNR=20dB
step=40: x接近真实, PSNR=28dB
step=50: x≈真实运动, PSNR=32dB
可视化: 噪声逐渐消失, 动作逐渐清晰
📊 冰山下的知识
| 概念 | 是什么 | 为什么需要 | 能解决什么问题 |
|---|---|---|---|
| ODE求解 | dx/dt = v_θ(xₜ, c, t) | 数值积分 | 从初值到终值 |
| Euler方法 | x_{t+1} = x_t + v·dt | 一阶近似 | 简单有效 |
| 步长dt | 1/num_steps | 精度vs速度权衡 | 调整质量/速度 |
🔗 认知映射
推理步数 vs 质量:
10步: 快但质量低 (实时?)
25步: 平衡 (推荐)
50步: 慢但质量高 (论文设置)
100步: 过慢 (边际收益递减)
↓
理解:步数选择是质量-速度的trade-off
Flow Matching: 50步 ≈ DDPM: 500步
🎯 这个实验能解决什么问题?
问题1:生成太慢
原因:推理步数太多
解决:减少到25步,质量下降不多但速度翻倍
问题2:生成质量差
原因:步数太少或ODE solver不稳定
解决:增加步数或改用Runge-Kutta
问题3:训练不收敛
原因:学习率太高导致速度场预测不准
解决:降低学习率或使用梯度裁剪
🧪 实验5:破坏性验证(边界测试)
🎯 为什么要破坏?
建立模型后,必须验证:
- 模型正确吗?
- 边界在哪里?
- 异常情况会怎样?
破坏性测试 = 找到系统的真实边界
📝 源输入
# 破坏1: 超长提示
long_prompt = "A person performs a complex sequence involving " * 100
# 破坏2: 矛盾提示
contradictory_prompt = "A person stands still while running fast"
# 破坏3: 超长时长
extreme_duration = "3600 seconds" # 1小时
# 破坏4: 未知语言
unknown_lang = "一个人踢球(古文)"
💉 埋示踪
执行步骤:
# 测试边界情况
test_cases = [
("超长提示", long_prompt),
("矛盾提示", contradictory_prompt),
("超长时长", extreme_duration),
("未知语言", unknown_lang)
]
for name, prompt in test_cases:
try:
result = model.generate(prompt)
print(f"[{name}] 成功: {result}")
except Exception as e:
print(f"[{name}] 失败: {e}")
👀 观察分析:建立映射关系
🔷 观察1:超长提示破坏
👁️ 观测结果
输入: 1000个token的提示
现象: 生成失败或质量严重下降
原因: 超过Qwen3-30B的上下文窗口(32K)或DiT的处理能力
📊 冰山下的知识
| 异常场景 | 观察现象 | 根本原因 | 应对策略 |
|---|---|---|---|
| 超长提示 | OOM或截断 | 超过上下文窗口 | 截断或摘要 |
| 超短提示 | 生成随机 | 缺少语义指导 | 使用默认提示 |
🔗 认知映射
提示长度 生成质量
1-10 tokens → 差(缺少信息)
10-50 tokens → 好(适中)
50-200 tokens → 好(详细)
>200 tokens → 下降(过载)
>1000 tokens → 失败(超出能力)
🔷 观察2:矛盾提示破坏
👁️ 观测结果
输入: "stands still while running"
输出: 要么站着不动,要么跑步,不会同时存在
原因: 模型学习到的是真实运动分布
📊 冰山下的知识
| 异常场景 | 观察现象 | 根本原因 | 应对策略 |
|---|---|---|---|
| 矛盾提示 | 选择其中一个 | 训练数据无矛盾示例 | 模型倾向更可能的动作 |
🔗 认知映射
矛盾提示:
"stands still" (低能量)
"running" (高能量)
↓
模型选择: "running" (更常见)
↓
理解:模型倾向于训练数据中更常见的模式
🔷 观察3:时长预测破坏
👁️ 观测结果
输入: "3600 seconds"
输出: 模型预测为"5-10秒"
原因: LLM基于常识判断,不会盲目相信
📊 冰山下的知识
| 异常场景 | 观察现象 | 根本原因 | 应对策略 |
|---|---|---|---|
| 不合理时长 | 覆写为合理值 | 训练数据范围1-12秒 | 限制预测范围 |
🔗 认知映射
时长预测:
用户输入: "1 hour"
LLM常识: "踢球不会持续1小时"
LLM输出: "3.2 seconds" (基于训练数据)
↓
理解:LLM的常识校准是保护机制
💡 验证认知模型
实验前预测:
"超长提示会失败"
"矛盾提示会选择其中一个"
"极端时长会被修正"
实验验证:
✅ 预测正确
✅ 理解了边界条件
模型修正:
如果预测错误,说明模型有问题,需要修正认知
📚 推荐学习路径
根据你的技术背景和兴趣,选择适合的学习路径:
路径1:AI研究工程师
目标:理解DiT+Flow Matching,设计新模型
推荐顺序:
文本编码层 → 实验2(双重编码)
- 学习Qwen3-8B, CLIP-L
- 理解细粒度vs全局语义
DiT架构层 → 实验3(双流-单流)
- 学习注意力掩码, RoPE
- 理解跨模态交互
Flow Matching层 → 实验4(最优传输)
- 学习ODE求解, 速度场
- 理解为什么比DDPM快
训练范式 → 三阶段训练
- 学习预训练, 微调, RL
- 理解数据质量vs规模
工具箱:
# 必备工具
model.forward(inputs) # 前向传播
register_forward_hook(module) # Hook中间层
torch.save(checkpoint) # 保存状态
attention_weights.matmul() # 注意力权重
路径2:机器学习工程师
目标:理解训练流程,优化模型性能
推荐顺序:
数据层 → 数据处理管道
- 学习运动清洗, 标注过滤
- 理解3000h vs 400h数据
训练层 → 三阶段训练
- 学习学习率衰减, GRPO
- 理解收敛曲线
推理层 → Flow Matching推理
- 学习Euler solver, 步数调整
- 理解质量-速度trade-off
评估层 → VLM+人工评估
- 学习SSAE, Pass Rate
- 理解指标含义
工具箱:
# 必备工具
python train.py --stage pretrain # 预训练
python train.py --stage finetune # 微调
python train.py --stage rl # 强化学习
python eval.py --metric ssae # 评估
路径3:3D动画工程师
目标:理解SMPL-H表示,集成到工作流
推荐顺序:
SMPL-H表示 → 实验5(运动解码)
- 学习201维向量含义
- 理解关节旋转vs位置
运动渲染 → 3D可视化
- 学习SMPL-H建模
- 理解mesh渲染
质量控制 → 足部滑动, 抖动
- 学习物理约束
- 理解数据清洗影响
工作流集成 → API调用
- 学习推理脚本
- 理解批处理
工具箱:
# 必备工具
smpl_model.decode(motion_vector) # SMPL-H解码
renderer.render(motion_sequence) # 渲染视频
video_editor.postprocess(video) # 后处理
🔄 可迁移性评估(跨领域应用)
罗塞塔石碑实验法的核心是可迁移的,可以应用到任何"输入→转换→输出"的技术栈。
案例1:文本生成视频 (Sora)
| 关卡 | 数据转换对象 | 关键问题 | 主要观测工具 |
|---|---|---|---|
| 关卡1 文本编码 | Text → CLIP嵌入 | 如何理解长文本? | CLIP模型 |
| 关卡2 Spacetime Patch | Video → 3D Patches | 如何划分时空? | Patch可视化 |
| 关卡3 DiT | Patches → Latents | 如何预测视频? | 中间状态Hook |
| 关卡4 Decoder | Latents → Video | 如何解码? | VAE Decoder |
案例2:语音合成 (TTS)
| 关卡 | 数据转换对象 | 关键问题 | 主要观测工具 |
|---|---|---|---|
| 关卡1 文本分析 | Text → Phonemes | 如何发音? | G2P模型 |
| 关卡2 声学模型 | Phonemes → Mel谱 | 如何生成音频? | Mel谱可视化 |
| 关卡3 声码器 | Mel → Waveform | 如何合成声音? | Waveform绘图 |
案例3:图像生成 (FLUX)
| 关卡 | 数据转换对象 | 关键问题 | 主要观测工具 |
|---|---|---|---|
| 关卡1 文本编码 | Text → T5嵌入 | 如何理解提示? | T5模型 |
| 关卡2 Flow Matching | Noise → Image | 如何去噪? | 中间图像可视化 |
| 关卡3 VAEDecoder | Latent → RGB | 如何解码? | 图像输出 |
⚡ 快速启动检查清单
开始前(5分钟)
- 选择我想理解的层级(参考学习路径)
- 明确我的输入(文本提示)
- 明确我想理解的输出(SMPL-H运动)
第1步:定层级(10分钟)
- 列出数据流经的所有层级
- 确认每个层级的数据形态
- 选择最容易理解的关卡开始
第2步:定关卡(5分钟)
- 找到数据必经之路
- 确认关卡可观测
- 记录关卡的作用
第3步:架工具(10分钟)
- 选择观测工具(参考工具速查表)
- 准备Hook代码
- 测试工具可用
第4步:投示踪(20分钟)
- 设计独特示踪剂(罕见动作组合)
- 执行模型推理
- 保存中间状态
- 记录观察结果
分析总结(15分钟)
- 建立认知映射表
- 理解因果逻辑链
- 记录冰山下的知识
- 设计破坏性测试验证
💡 核心原则
主动观测 > 被动阅读 论文告诉你"应该是什么",实验展示"实际是什么"
示踪剂必须独特 ❌ “A person walks” ✅ “A person kicks a ball with their left foot while waving their right hand”
一次只探一层 不要试图一步到位理解整个栈 Input → 关卡 → Target,专注一个数据突变点
相信观测,质疑假设 如果现象和论文矛盾,以观测为准
必须破坏性验证 正常场景看不出边界,异常场景才能暴露真实行为
🎯 关键架构洞察
通过罗塞塔石碑实验法,我们破译了HY-Motion 1.0的五个核心秘密:
洞察1:为什么Flow Matching比DDPM快?
DDPM: 随机路径 → 需要数百步
Flow Matching: 直线路径 → 只需50步
关键: 最优传输路径 xₜ = (1-t)x₀ + t·x₁
→ 常数目标速度 vₜ = x₁ - x₀
→ 模型只需学习线性映射
洞察2:为什么需要三阶段训练?
预训练(广度) ←→ 微调(精度) ←→ 强化学习(对齐)
类比:
学生先读百科全书(广度),
再精读经典(精度),
最后通过考试反馈修正理解(对齐)
洞察3:双流-单流混合设计
双流阶段:
- 文本和运动独立处理,保留模态特异性
- 通过非对称注意力交换信息(单向保护)
单流阶段:
- 融合为统一序列
- 并行空间+通道注意力深度融合
权衡: 语义保真度 vs 运动自然度
洞察4:为什么需要双重编码器?
Qwen3-8B (细粒度):
- Token级语义
- 区分left/right, hand/foot
- 专注细节
CLIP-L (全局):
- 整体风格
- 场景类别(运动/舞蹈/格斗)
- 专注整体
互补: 细节 + 整体 = 完整语义
洞察5:数据质量 vs 规模的trade-off
3000小时数据:
- 优点: 广度大,语义泛化强
- 缺点: 继承噪声,质量不稳定
400小时精选:
- 优点: 高质量,运动平滑
- 缺点: 覆盖有限
解决: 先规模后精度的"coarse-to-fine"训练
📊 完整数据流路径
用户输入 "踢球"
↓ [关卡1: LLM优化]
优化提示 "A person kicks a ball, extending their leg forward."
预测时长 "3.2秒"
↓ [关卡2: 双重编码]
Qwen3-8B → [14, 768] token嵌入
CLIP-L → [768] 全局嵌入
Token Refiner → 双向表示
↓ [关卡3: 双流DiT]
双流阶段: 非对称注意力
- 运动→文本: 允许
- 文本→运动: 禁止
单流阶段: RoPE + 拼接注意力
↓ [关卡4: Flow Matching]
初始化: x₀ ~ N(0, I)
插值: xₜ = (1-t)x₀ + t·x₁
速度预测: v_θ(xₜ, c, t)
↓ [关卡5: ODE求解]
Euler积分: x_{t+1} = x_t + v·dt
迭代50步: x₀ → x₁
↓ [关卡6: SMPL-H解码]
201维向量:
- root_translation (3)
- body_orientation (6)
- joint_rotations (126)
- joint_positions (66)
↓ [关卡7: 3D渲染]
SMPL-H mesh + 纹理
↓
输出: "踢球" 动作视频 @ 30fps, 3.2秒
🏆 总结
通过罗塞塔石碑实验法,我们穿透了从文本提示到SMPL-H运动的完整路径:
- 用户层:“踢球” → LLM预测时长+优化提示
- 编码层:双编码器(Qwen3-8B + CLIP-L)→细粒度+全局语义
- 生成层:Flow Matching + DiT → ODE求解器逐步去噪
- 输出层:201维向量 → SMPL-H参数 → 3D人体运动
核心架构公式:
文本语义 → 文本嵌入 → 速度场 v_θ(xₜ, c, t)
↓
噪声 x₀ ──[ODE积分]──→ 干净运动 x₁
dx/dt = v_θ(xₜ, c, t)
这个架构的成功在于:规模化(1B参数)+ 数据质量(三阶段训练)+ 架构创新(DiT+Flow Matching) 的协同作用。
记住:罗塞塔石碑方法的核心是对照。通过已知的输入和未知的底层输出,建立映射关系,就能破译任何黑盒系统。
现在,选择一个你想理解的层级,开始你的第一个示踪实验吧!