资讯动态

扩散策略模型Diffuser:从轨迹生成到离线决策的全解析

发布时间:2026/10/13 17:06:37 来源:尧图企业网站定制
接到这个标题的第一反应我想大多数人会以为“扩散策略模型”是拿扩散模型做数据增强或者生成点仿真场景。但“Diffuser”这个叫法在决策智能领域里指的是一个非常特别的方向把扩散模型直接当成策略本身用“去噪”的方式规划动作序列。它是把图像生成领域那套前向加噪、逆向去噪的思路整套搬到了序列决策问题上。这个项目我前前后后拆过不少次也复现过几版今天把里面的门道一次性说透。简单说扩散策略模型解决的是“离线强化学习里策略表达力不足”的问题。传统做法是让策略直接输出动作或者输出动作的概率分布但遇到多模态的最优行为、长程稀疏回报任务这种表达方式会严重限制策略上限。Diffuser 的思路则完全不同它把一段轨迹状态、动作、回报的序列当成一张“图像”把决策问题转成“条件轨迹生成”问题——先学习轨迹的结构分布再通过引导条件采样出符合高回报偏好的轨迹。这个思路对刚接触的人来说有点绕但对做决策智能、机器人控制、推荐系统长期规划的人来说它打开了一扇新门。这篇文章适合三类读者一是做强化学习研究、想找离线 RL 新思路的二是做机器人控制被稀疏奖励和多目标策略折磨的三是在做序列预测、时间序列生成想看看扩散模型还能怎么用的。我会从设计思路、数学机理、实现细节、训练采样、踩坑实录五个层面把它展开尽量做到能直接照着复现。1. 扩散策略模型的设计思路拆解1.1 从“预测动作”到“生成轨迹”的范式转换传统强化学习策略的建模无论在线还是离线本质都在做同一件事给定当前状态 s拟合一个映射函数输出动作 a。哪怕用上神经网络这个映射关系依然是“单状态-单动作”的即时决策。这种范式有个隐藏问题它默认最优决策是状态的唯一函数但现实中最优行为往往不是唯一的。举个例子一个机械臂要把杯子从 A 点搬到 B 点它可以走弧线、走折线、甚至先抬高再平移这些轨迹在回报维度上可能都是最优的。传统策略网络面对这种多模态分布只能学一个平均结果就是生成一条“四不像”的中间轨迹既不是弧线也不是折线物理上往往不可行。扩散策略模型把这个问题直接绕开了。它不再建模样例状态到动作的映射而是建模整条轨迹的分布 p(τ)其中 τ (s₀, a₀, s₁, a₁, ..., s_T, a_T)。只要能从分布里采样出完整轨迹策略自然就蕴含其中。这里面有个关键认知决策不是“给定当前看一步”而是“给定一个片段补全整条路径”。就像你玩拼图传统策略是从已知格子推下一个格子扩散策略是先看完整张图该长什么样再一块一块还原。这个范式转换带来的直接好处策略表达能力不受限于网络结构而受限于轨迹分布拟合的精度。扩散模型恰好是当前分布拟合能力最强的生成模型之一它天然擅长表达多模态分布。1.2 为什么偏偏是扩散模型可能有人会问生成轨迹用 VAE、用 GAN、用自回归模型不行吗在 Diffuser 之前确实有人试过各有各的硬伤。VAE 虽然训练稳定但它的隐变量分布假设太强生成质量偏“糊”生成的轨迹多样性不足。GAN 在图像上效果炸裂但训练对抗不稳定用在轨迹生成上模式坍塌问题尤其严重——因为轨迹数据本身模式就多判别器很难学。自回归模型按时间步逐个生成误差会一步步累积长程轨迹生成到后半段基本放飞自我。扩散模型在这几个维度的表现比较均衡。它的训练目标简单就是去噪没有对抗博弈训练稳定性好生成质量在图像领域已经验证过细节还原度高于 VAE采样时可以通过控制初始噪声和引导条件天然输出多样化的轨迹。还有一个很容易被忽略的优势扩散模型的逆向过程本身就包含“从模糊到清晰”的多步迭代这跟轨迹优化的“从粗略到精细”天然同构。你在第 1 步反向采样时得到的是一个大方向第 10 步得到的是粗略形状最后几步才抠细节这跟人类规划复杂任务的层级感很像。1.3 Diffuser 的完整架构全景整个模型框架分三块轨迹表示、扩散主网络、引导条件机制。轨迹表示解决“轨迹怎么编码成类图像数据”的问题。Diffuser 的做法很直接把一段长度为 H 的轨迹段每个时间步包含观测 s_t、动作 a_t、回报 r_t组合成一个二维矩阵。矩阵的行是时间步列是状态和动作的维度拼接。这个矩阵就是“图”扩散模型直接在上面做加噪去噪。扩散主网络是标准的 DDPM 架构包含前向加噪过程和逆向去噪过程。前向过程把真实轨迹逐步加噪成纯高斯噪声逆向过程学习一步步还原。网络结构用 U-Net 或者类似的时间序列模型支持以时间步 embedding 为条件。引导条件机制是这套方案里最有价值的部分。Diffuser 用“分类器引导”思路额外训练一个回报预测模型或者说价值函数在采样时把回报梯度注入去噪过程让生成轨迹不仅“像数据”而且“像高回报的数据”。这等于把强化学习的优化目标直接焊进了生成过程里。2. 核心原理与数学机制2.1 扩散模型前向与逆向过程还原扩散模型的前向过程不涉及学习它直接定义一条从数据到噪声的退化链。给定真实轨迹 τ₀前向过程按预设的噪声调度 β₁, β₂, ..., β_N逐步叠加高斯噪声生成 τ₁, τ₂, ..., τ_N。到第 N 步时τ_N 基本就是纯高斯噪声了。这个过程用公式表达是 q(τ_n | τ_{n-1}) N(τ_n; √(1-β_n) τ_{n-1}, β_n I)。实际工程里不用真的逐步迭代加噪因为高斯分布的叠加性可以直接从 τ₀ 一步跳到第 n 步的分布τ_n √(ᾱ_n) τ₀ √(1-ᾱ_n) ε其中 ᾱ_n 是前 n 步噪声系数的累积乘积ε 是标准高斯噪声。这个“重参数化”技巧是整个扩散模型训练高效的关键——你不用跑完整个前向链随机取一个时间步用公式直接算加噪结果就行。逆向过程是学习目标。我们不知道从 τ_n 还原到 τ₀ 的真实逆分布所以用一个神经网络 ε_θ(τ_n, n) 去预测叠加的噪声 ε。训练损失就是预测噪声和真实噪声的均方误差L E[||ε - ε_θ(τ_n, n)||²]。这就是目标。你可能会觉得这跟决策无关但本质上预测噪声等于学会了一个轨迹的“结构”信息——知道哪个位置偏离正常轨迹多少自然也就知道正常轨迹长什么样。2.2 条件生成把回报期望写进采样过程如果只学习轨迹分布那生成的只是“常见的轨迹”不一定是“高回报的轨迹”。要往目标方向上引导Diffuser 在逆向采样的每一轮迭代里都对轨迹的估计加入回报模型的梯度。具体做法训练一个回报预测模型 R_φ(τ)输入轨迹片段输出预测回报。然后利用扩散模型逆向过程的“分数”特性——在连续时间视角下去噪过程实际上在沿着数据分布的对数密度梯度方向移动。我们不需要修改数据分布而是修改采样轨迹的分布让它的对数密度加上一个回报加权项log p(τ) λ·R_φ(τ)。这样采样时轨迹会偏向高回报区域。数学上这等价于在逆向去噪的每一步把 τ 的均值估计沿着 ∇R_φ 的方向推一步。实现时就是在原有的噪声预测结果上给轨迹估计加上一项回报梯度和引导系数 λ 的乘积。λ 越大轨迹越激进地追求高回报但风险是偏离数据分布太远生成不合理的轨迹λ 太小轨迹接近模仿学习回报提升有限。这个系数需要调后面的实操部分我会给参考范围。2.3 Diffuser 和传统离线强化学习的本质区别拿最经典的离线 RL 算法来对比理解会更直观。比如 CQL它学一个 Q 函数然后通过约束 Q 值在数据分布内的策略更新来提取策略。整个过程是“先估值再改进策略”两个环节互相依赖价值函数估计稍有偏差策略就容易崩。IQL 的思路是用分位数回归缓解过估计本质还是在做“价值驱动”。Diffuser 完全不走“价值函数策略提取”的老路。它只有两个组件轨迹生成模型和回报预测模型。轨迹生成模型负责生成合理轨迹回报模型只在采样时做引导不参与训练主网络的梯度传播。这意味着回报模型的误差不会直接污染策略——它的位置更像“裁判”而不是“教练”。这个解耦带来一个质变训练稳定性显著提升并且在数据质量参差不齐的离线数据集上Diffuser 的表现常常优于传统方法。还有一点值得注意传统方法处理稀疏奖励任务时报函数几乎全是 0学习信号极度稀疏。而 Diffuser 的轨迹模型在没有回报引导的情况下也能生成结构合理的轨迹因为它在学“轨迹长什么样”这是无监督学习奖励只是锦上添花的引导。这也是它在稀疏奖励任务上表现突出的根本原因。3. 实操过程从零复现 Diffuser 的全流程3.1 环境准备与数据构造我复现时用的硬件是单张消费级显卡显存 12 GB 左右就够跑小规模任务。环境依赖建议PyTorch 2.0 以上、numpy、gym以及一个能提供离线轨迹数据集的环境。如果只想验证算法直接用经典的 mujoco 环境再配合现成的离线数据接口是最省事的。离线数据是整套方案的命脉。你的数据集格式需要整理成“轨迹片段”的形式而不是“单步样本”。我在处理数据时会把原始轨迹切成固定长度 H 的片段一般取 H64 或者 H128。片段与片段之间允许重叠这样数据量能扩充好几倍。每个片段包含三个矩阵状态序列、动作序列、回报序列。切完之后把状态和动作在特征维度拼接形成模型的输入张量形状是 (H, dim_state dim_action)。这里有个很多人会忽略的点回报序列的处理。Diffuser 不是把回报当成训练目标而是把它作为条件信息或引导信号。我在实现里会把回报序列也拼进轨迹矩阵作为额外的一列或几列让模型在去噪时“看到”回报结构。但这会引入一个问题回报的数值尺度跟状态动作差异巨大必须做归一化不然噪声预测会被回报维度主导。我一般用数据集的回报均值和标准差做标准化并且对极端回报值做截断。3.2 轨迹扩散模型的结构选择官方原始实现用的是 U-Net 结构它原本是给图像设计的直接套到轨迹上有点浪费。我自己试了几种替代方案效果都不错。最简单的是多层 Transformer 编码器输入是加噪的轨迹矩阵加时间步 embedding输出是预测噪声。Transformer 的优势在于时间维度的注意力天然适合轨迹数据——轨迹中部和尾部的依赖关系都能捕捉到。我当时用两层 4 头注意力、隐藏维度 256在小规模任务上训练速度比 U-Net 快不少。另一种方案是 Temporal Convolutional NetworkTCN感受野受卷积核大小和层数限制长距离信息传递稍弱但胜在轻量、推理快。如果你的任务轨迹长度比较短H64TCN 是性价比很好的选择。我最终实践的方案是状态维度加动作维度小于 50 的小规模任务用两层 Transformer高维输入比如图像观测才考虑 U-Net 或者 Conv 1D 堆叠。结构选择的逻辑很简单——先保证输入输出维度匹配再用最小的模型把基线跑通不要一上来就堆复杂结构调试难度会成倍增加。3.3 引导回报模型的设计与训练引导回报模型输入是轨迹片段输出是标量回报预测。这里的挑战是回报可能来自轨迹末端稀疏奖励也可能分布在中间每一步密集奖励模型得自己学会汇总。我实现时采用一个简单做法把轨迹矩阵展平过两层 MLP输出预测回报。训练目标就是最小化预测回报和实际回报的均方误差。但实际回报的尺度问题又来了——不同任务的回报范围可能差几个量级。我把回报除以整个数据集的绝对值的均值再强制 clip 到 [-10, 10]训练会稳定很多。有个很实用的技巧回报模型并不需要非常准确。它的作用是提供“梯度方向”而不是精确估值。所以在训练时可以用一个很轻量的网络哪怕验证集误差偏高也没关系只要预测和真实回报的相关性不要太差引导就有效。这跟传统 RL 里价值函数必须尽量精确的思路完全不同。3.4 训练流程的完整代码骨架下面给一份我在实际项目中用过的训练核心代码去掉了具体环境相关的部分保留了完整逻辑。这是训练轨迹扩散模型的部分import torch import torch.nn as nn class TrajectoryDiffusion(nn.Module): def __init__(self, input_dim, hidden_dim256, num_layers2, horizon64): super().__init__() self.input_dim input_dim self.horizon horizon # 时间步 embedding扩散模型需要知道当前处在加噪/去噪的哪一步 self.time_mlp nn.Sequential( nn.Linear(128, hidden_dim), nn.SiLU(), nn.Linear(hidden_dim, hidden_dim) ) # 用 Transformer Encoder 处理轨迹序列 encoder_layer nn.TransformerEncoderLayer( d_modelinput_dim hidden_dim, nhead4, dim_feedforwardhidden_dim * 2, batch_firstTrue ) self.transformer nn.TransformerEncoder(encoder_layer, num_layersnum_layers) # 输出层预测噪声 self.output_layer nn.Sequential( nn.Linear(input_dim hidden_dim, hidden_dim), nn.SiLU(), nn.Linear(hidden_dim, input_dim) ) def forward(self, x, t): # x: (batch, horizon, input_dim) # t: (batch,) 时间步这里转成 embedding 并广播到每个时间步 t_emb self.time_mlp(self._timestep_embedding(t, 128)) t_emb t_emb.unsqueeze(1).repeat(1, self.horizon, 1) x torch.cat([x, t_emb], dim-1) x self.transformer(x) noise_pred self.output_layer(x) return noise_pred def _timestep_embedding(self, t, dim): # 标准的 sinusoidal embedding类似 Transformer 里的位置编码 half dim // 2 freqs torch.exp(-torch.log(torch.tensor(10000.0)) * torch.arange(half) / half).to(t.device) args t[:, None] * freqs[None, :] return torch.cat([torch.cos(args), torch.sin(args)], dim-1)训练主循环def train_diffusion(model, dataloader, optimizer, schedule, device): model.train() total_loss 0 for batch in dataloader: trajectory batch[trajectory].to(device) # (batch, horizon, input_dim) batch_size trajectory.shape[0] # 随机采样时间步让模型学会不同噪声程度下的去噪 t torch.randint(0, schedule.num_steps, (batch_size,), devicedevice) # 生成噪声并对轨迹加噪 noise torch.randn_like(trajectory) noisy_trajectory schedule.add_noise(trajectory, noise, t) # 预测噪声并计算损失 noise_pred model(noisy_trajectory, t) loss nn.functional.mse_loss(noise_pred, noise) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() return total_loss / len(dataloader)噪声调度 schedule 一般用余弦调度比线性调度在轨迹数据上表现更稳定。它定义了每个时间步的 α 和 σ 值决定加噪强度。加噪函数的具体实现就是前面提到的重参数化公式noisy sqrt(ᾱ) * original sqrt(1-ᾱ) * noise。3.5 采样生成与引导控制的实现采样推理阶段是扩散模型最直观体现决策能力的环节。我每次要生成一条轨迹时先用标准高斯分布初始化一个纯噪声矩阵然后从最大时间步逆向迭代到第 0 步。在每一步里模型预测噪声我用它估算出当前轨迹的“干净版本”再对这个干净版本施加回报梯度引导最后按照噪声调度重新加回一部分噪声进入下一步。这个流程我贴一段伪代码风格的实现方便理解每一步在做什么def sample_trajectory(model, guide_model, schedule, horizon, input_dim, use_guideTrue, guide_scale1.0): # 1. 从纯高斯噪声开始 x torch.randn(1, horizon, input_dim, devicedevice) # 2. 从最后一步纯噪声往第 0 步干净轨迹迭代 for step in reversed(range(schedule.num_steps)): t torch.full((1,), step, devicedevice) # 2.1 模型预测噪声 noise_pred model(x, t) # 2.2 根据噪声预测估算干净轨迹 alpha_bar schedule.alpha_bar[step] x_clean_estimate (x - torch.sqrt(1 - alpha_bar) * noise_pred) / torch.sqrt(alpha_bar) # 2.3 如果启用回报引导把干净轨迹推向高回报方向 if use_guide and guide_model is not None: x_clean_estimate.requires_grad_(True) reward_pred guide_model(x_clean_estimate) grad torch.autograd.grad(reward_pred, x_clean_estimate)[0] x_clean_estimate x_clean_estimate guide_scale * grad # 2.4 重新参数化生成进入下一步的样本 if step 0: sigma schedule.sigma[step] x x_clean_estimate * torch.sqrt(alpha_bar) sigma * torch.randn_like(x) else: x x_clean_estimate # 返回生成的干净轨迹取动作序列部分用于执行 return x这段代码里的第 2.3 步就是引导机制的核心。我的经验是引导系数 guide_scale 从 0 开始按 0.1、0.5、1.0、2.0 的档位逐步往上试观察生成的轨迹是否还在合理范围内。大于 2 的时候很多任务上轨迹会开始发散出现不切实际的加速度或者位置跳跃。另外回报模型前向传播会让计算图变大如果显存吃紧可以在前向后detach()掉一部分只保留梯度的必要路径。4. 常见问题与排查技巧实录4.1 生成轨迹不连贯或出现跳变这是最常遇到的问题生成的轨迹前半段正常后半段突然跑到不可行区域。大多数情况下罪魁祸首是时间步 embedding 的维度不够或者 Transformer 层数太浅模型没学到长程依赖。我排查时先看训练损失有没有收敛。如果损失已经很低但生成还是断裂我会把轨迹长度 H 缩短一半试试。有一次我处理机械臂任务H128 时轨迹经常漂移改成 H64 后基本正常——后来发现长轨迹中后段数据本身就稀疏模型在这部分的去噪能力不足。另一个解法是把数据集里的轨迹按长度分组串行训练多个模型每个模型负责一种长度范围但这属于工程手段加速明显但会多耗训练时间。还要检查加噪调度是不是太激进。余弦调度的好处是中间步骤的噪声变化比较平缓给模型更多的“修正机会”。如果用线性调度且步数少于 50轨迹细节往往恢复得不够充分。4.2 回报引导完全无效或起反作用引导无效通常不是主模型的问题而是回报模型质量不够或者引导梯度方向噪音太大。我调试时先做一个对比实验把引导系数设为 0看生成的轨迹平均水平再把引导系数设为一个中间值看平均水平是否提升。如果没提升优先怀疑回报模型。回报模型训练数据里有一个隐藏陷阱回报的分布通常高度偏斜少数的极高回报样本和大量中低回报样本。均方误差损失会被极端值带偏。我的建议是训练回报模型时对回报做分位数裁剪只保留 5%~95% 之间的样本参与训练。有种更稳妥的变体是训练集内排序把回报预测任务改成“排序学习”——预测回报的相对大小而不是绝对值。这样引导方向更鲁棒。还有一个常见问题使用引导时轨迹生成质量反而下降。这是因为梯度直接推在轨迹矩阵的原始维度上可能会把状态推出物理可行域。我的解法是不直接对原始轨迹加梯度而把梯度先投影到数据分布的主成分空间。具体实现上训练阶段对轨迹做一次 PCA保存主成分矩阵采样时把梯度投影到主成分空间再加回去。这个方法不改变训练过程只在采样时多一步矩阵运算但效果提升很明显。4.3 训练 loss 降不下去或过拟合loss 降不下去先检查数据是否过度拼接导致相邻轨迹之间断层明显。拼接处如果状态不连续模型相当于在学习“从一个状态跳到另一个不相关状态”的模式噪声预测自然学不好。我记得第一次构造数据集时用了重叠滑窗理论上没问题但实现时没对齐状态维度的单位导致拼接段有虚假的跳跃折腾了一整天才排查出来。解决方法是可视化训练数据把轨迹画成状态随时间变化的曲线肉眼检查拼接处是否平滑。这个问题在图像领域不存在但在轨迹领域极易发生因为时序数据的单位不一致很隐蔽。过拟合的表现是训练损失降得很低但生成轨迹质量一塌糊涂。往往是模型容量太大而数据不足。我发现轨迹数据集扩充其实有限尤其真实机器人数据采集成本很高。这种情况下用早停或者加 dropout 会立竿见影。Transformer 里我一般加 0.1 的 dropout对生成质量影响不大但对过拟合抑制非常有效。4.4 推理太慢实时控制跑不起来扩散模型的逆向过程动辄需要几十步迭代这对实时控制是个负担。我在实测中发现减少去噪步数的效果最直接。训练时用 100 步采样时只用 10 步质量虽然下降但很多时候仍然可用。但注意去噪步数不能太少——我用 5 步测试过轨迹会丢失高频细节动作序列显得僵硬。还有两个优化角度。第一个是缓存如果任务允许离线预生成一批轨迹片段存起来在线阶段直接查表适合周期性任务。第二个是蒸馏训练一个一步生成模型把多步去噪过程蒸馏进去。这相当于把扩散策略模型当作教师让学生网络学会“一步到位”近似结果。蒸馏后的模型会损失一些多样性但推理速度可以提升一个量级对于倒立摆、推箱子这类毫秒级决策任务很实用。5. 实践心得与经验总结做完整套复现我的一个强烈体感是扩散策略模型的最大价值不在“刷高分”在于策略的可解释性和可控性。传统 RL 策略是个黑盒你只能看它输出什么动作难以理解它为什么这样规划。而 Diffuser 生成的是整条轨迹你可以把轨迹展开看成期望路径可以基于回报梯度调整行为倾向甚至可以手工干预轨迹局部形状——这在机器人安全控制里意义很大。在部署层面也有个容易被忽略的好处Diffuser 天然适合“先规划后执行”的控制架构它可以提前生成参考轨迹传统控制器再跟踪执行。这样既保证了轨迹的全局合理性又保留了底层控制的实时性和安全性。这种两段式架构比端到端策略网络在真实系统里更容易获得工程师信任。如果后续想在这个方向继续深入我个人觉得有几个扩展点很值得关注一是把 Diffuser 嵌入到在线强化学习框架里让轨迹生成模型持续吸收新数据自我进化二是把引导条件从回报扩展到多目标比如同时约束安全距离和能耗三是大规模预训练轨迹生成模型类似语言模型的预训练范式在通用离线数据上先学到状态转移的通用规律再在具体任务上微调。这些探索现在都还处于很早期的阶段从工业应用前景看天花板很高。作为整个复现旅程的最后一句提醒做扩散策略模型开头不要追求完美先让模型学会生成任意一条合理轨迹再引导它走向高回报。分开验证逐步加码这个项目最怕的就是把“生成质量”和“回报优化”绑在一起调试互相干扰会让人心态崩溃。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑