尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Flow Matching实战:从生成模型到Diffusion Policy的路径设计与训练优化

发布时间:2026/9/26 15:03:33

资讯中心
01
ARTICLE

Flow Matching实战:从生成模型到Diffusion Policy的路径设计与训练优化

Flow Matching实战:从生成模型到Diffusion Policy的路径设计与训练优化
1. 从生成模型到Flow Matching为什么我们需要新的范式1.1 生成模型的核心矛盾从噪声到数据的路径设计生成模型要解决的根本问题说穿了就一句话如何把一个简单的先验分布通常是标准正态分布变换成复杂的数据分布。这个问题听起来简单但真正做起来路径怎么设计、代价怎么算、训练稳不稳每一步都是坑。早期的生成模型走了两条截然不同的路。GAN靠对抗博弈生成器和判别器互相拉扯训练起来像走钢丝稍有不慎就模式崩塌。VAE走的是变分推断路线理论优雅但生成质量总是差那么一口气模糊感挥之不去。到了扩散模型Diffusion Model大家终于找到了一条相对稳定的路先把数据一步步加噪变成纯噪声再学着一步步去噪还原。这条路走通了图像生成的质量直接上了一个台阶。但扩散模型有个让人头疼的问题采样太慢。从纯噪声到一张图往往需要几十甚至上千步的迭代去噪。每一步都要跑一次网络前向传播计算量摆在那里。虽然后来有DDIM、DPM-Solver这类加速采样方法把步数压到了20到50步但本质上还是在解一个随机微分方程或者常微分方程步数少了精度就掉。Flow Matching的思路正是冲着这个痛点来的。它不跟你绕弯子直接去学一个速度场让样本沿着一条确定的路径从噪声流向数据。这条路径可以是直线可以是曲线但关键是你可以在训练时就把路径定好推理时用很少的步数就能走到终点。1.2 Flow Matching与CFM条件流匹配的直觉理解Flow Matching的核心思想可以用一个物理类比来理解。想象一滴墨水滴入水中墨水分子会从高浓度区域向低浓度区域扩散这个过程是随机的、不可逆的。但如果我们反过来看假设我们想让墨水分子沿着某条特定路径回到初始位置那我们需要给每个分子指定一个速度向量告诉它“往哪个方向走、走多快”。这个速度场就是Flow Matching要学的东西。具体来说Flow Matching定义了一个时间相关的概率路径 $p_t(x)$其中 $t \in [0,1]$$p_0$ 是噪声分布$p_1$ 是数据分布。我们希望找到一个速度场 $v_t(x)$使得沿着这个速度场演化的样本能够从 $p_0$ 流向 $p_1$。这个演化过程由常微分方程描述$$\frac{dx}{dt} v_t(x)$$训练的目标就是让网络学到的速度场 $v_\theta(t, x)$ 尽可能接近真实的速度场。但问题来了真实的速度场我们不知道因为概率路径 $p_t$ 本身是未知的。CFMConditional Flow Matching的巧妙之处在于它把问题条件化。我们不直接去匹配边缘速度场而是先构造条件概率路径 $p_t(x|x_1)$其中 $x_1$ 是某个具体的数据样本。对于每个数据样本我们可以显式地定义一条从噪声到该样本的路径比如最简单的直线路径$$x_t (1-t)x_0 t x_1$$其中 $x_0 \sim \mathcal{N}(0, I)$ 是噪声$x_1$ 是数据。沿着这条路径速度就是常数$$\frac{dx_t}{dt} x_1 - x_0$$这个速度是我们可以直接算出来的。CFM的损失函数就变得非常简洁$$\mathcal{L} \mathbb{E}{t, x_0, x_1} |v\theta(t, x_t) - (x_1 - x_0)|^2$$这个损失函数的美妙之处在于它不需要知道边缘分布 $p_t$ 的显式形式只需要从配对样本 $(x_0, x_1)$ 中采样就能得到无偏的梯度估计。训练过程变成了一个简单的回归问题给定时间 $t$ 和当前位置 $x_t$预测从 $x_0$ 到 $x_1$ 的方向。1.3 为什么Flow Matching在Diffusion Policy中受到关注Diffusion Policy是近年来机器人领域的一个热门方向它把扩散模型用在策略学习上让机器人能够生成多模态的动作序列。但扩散模型的慢采样问题在机器人控制中尤为致命——机器人需要实时响应你不可能让它在每个控制周期都跑50步去噪。Flow Matching在这里的优势就体现出来了。由于它学的是一个确定性的速度场推理时可以用很少的步数甚至1到5步就能生成高质量的动作。而且Flow Matching的训练目标比扩散模型更直接不需要预测噪声或者得分函数直接回归速度场梯度信号更清晰。另外Flow Matching的路径设计非常灵活。在机器人动作生成中我们可以根据任务特点设计不同的路径。比如对于需要平滑轨迹的任务可以用最优传输路径对于需要快速响应的任务可以用直线路径。这种灵活性是扩散模型难以比拟的。提示Flow Matching并不是要完全取代扩散模型。在图像生成等对采样速度要求不高的场景扩散模型依然有优势。但在需要实时决策的机器人控制、自动驾驶等场景Flow Matching的低步数采样特性是刚需。2. 代价函数与梯度Flow Matching训练的核心细节2.1 从概率路径到速度场的数学推导要真正理解Flow Matching的训练过程我们需要把数学推导捋清楚。假设我们有一个时间相关的概率路径 $p_t(x)$它满足连续性方程$$\frac{\partial p_t(x)}{\partial t} \nabla \cdot (p_t(x) v_t(x)) 0$$这个方程说的是概率密度的变化必须与速度场引起的概率流守恒。给定 $p_t$我们可以解出 $v_t$但 $p_t$ 本身是未知的。CFM的做法是引入条件路径 $p_t(x|x_1)$然后利用一个关键性质边缘速度场是条件速度场的期望。具体来说如果我们定义条件速度场 $v_t(x|x_1)$ 为条件路径的速度那么边缘速度场可以写成$$v_t(x) \mathbb{E}{x_1 \sim p{1|t}(x_1|x)} [v_t(x|x_1)]$$这个性质是CFM训练可行性的理论基础。它告诉我们虽然我们不知道边缘速度场但我们可以通过采样条件速度场来得到它的无偏估计。在实际训练中我们每次只采样一个 $x_1$用对应的条件速度作为目标虽然单次估计有方差但期望上是对的。对于直线路径 $x_t (1-t)x_0 t x_1$条件速度是 $x_1 - x_0$与 $t$ 无关。这意味着训练目标在整个时间区间上都是一致的不会出现某些时间段梯度特别大或者特别小的情况。这也是直线路径在Flow Matching中受欢迎的原因之一。2.2 代价函数的设计选择与梯度行为Flow Matching的代价函数看似简单但实际训练中有几个关键选择会显著影响效果。第一个选择是路径类型。直线路径最简单但未必最优。如果数据分布和噪声分布之间的最优传输路径是弯曲的直线路径会导致速度场在某些区域变化剧烈网络需要更强的表达能力才能拟合。最优传输路径OT路径通过求解一个静态最优传输问题找到从噪声到数据的“最省力”路径通常能让速度场更平滑训练更稳定。第二个选择是时间采样策略。均匀采样 $t \sim \mathcal{U}[0,1]$ 是最直接的做法但实际中往往更倾向于对中间时间段多采样。原因在于当 $t$ 接近0或1时$x_t$ 分别接近纯噪声和纯数据速度场的预测相对容易而在中间区域$x_t$ 混合了噪声和信号预测难度最大。通过调整时间采样分布可以让网络在困难区域获得更多梯度信号。第三个选择是损失函数的形式。标准的MSE损失 $|v_\theta - v|^2$ 是最常用的但也可以考虑其他形式。比如有些工作使用加权MSE对某些时间段或某些样本赋予更高权重。还有工作探索了基于似然的损失但实现复杂度较高。梯度行为方面Flow Matching的梯度通常比扩散模型更稳定。扩散模型训练时需要预测噪声 $\epsilon$ 或者得分函数 $\nabla \log p_t$这些量在 $t$ 接近0时数值可能非常大导致梯度爆炸。Flow Matching直接回归速度速度的量级通常与数据尺度相当不会出现极端值。但这也意味着如果数据尺度很大速度也会很大可能需要适当归一化。2.3 梯度累积与学习率调度的实操经验在实际训练Flow Matching模型时梯度累积和梯度下降的配置对最终效果影响很大。我试过几种不同的配置这里分享一些实测经验。梯度累积Gradient Accumulation在显存受限时特别有用。Flow Matching的批大小对训练稳定性有影响——批太小条件速度的方差大梯度噪声重批太大显存吃不消。梯度累积允许你用多个小批的梯度平均来模拟大批效果。具体操作是在前向传播和反向传播时不清空梯度累积若干步后再更新参数。# 梯度累积示例 accumulation_steps 4 optimizer.zero_grad() for i, (x0, x1) in enumerate(dataloader): t torch.rand(batch_size) xt (1 - t) * x0 t * x1 target x1 - x0 pred model(xt, t) loss F.mse_loss(pred, target) / accumulation_steps loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()学习率方面Flow Matching对学习率比较敏感。太大容易震荡太小收敛慢。我一般从 $1e-4$ 开始试配合余弦退火或者线性预热。预热阶段很重要因为训练初期速度场的预测误差很大直接上大学习率容易把网络带偏。通常预热500到1000步然后逐步升到目标学习率。还有一个细节是权重衰减。Flow Matching的速度场通常不需要太强的正则化权重衰减设 $1e-5$ 到 $1e-6$ 就够了。太强会限制速度场的表达能力导致生成样本多样性下降。注意梯度裁剪在Flow Matching中通常不是必须的但如果发现训练不稳定可以加一个适度的裁剪阈值比如1.0。不要设得太小否则会截断有用的梯度信号。3. 实操过程从零搭建一个Flow Matching训练流程3.1 数据准备与路径构造搭建Flow Matching训练流程的第一步是准备数据。假设我们处理的是图像数据每个样本是 $x_1 \in \mathbb{R}^{C \times H \times W}$。噪声样本 $x_0$ 从标准正态分布采样形状与 $x_1$ 相同。路径构造是Flow Matching的核心。对于直线路径$x_t (1-t)x_0 t x_1$实现起来非常简单。但如果你想要OT路径就需要先求解一个最优传输问题。对于图像数据精确求解OT问题计算量很大通常用Sinkhorn算法近似或者用mini-batch OT。import torch import torch.nn.functional as F def sample_conditional_path(x0, x1, t): 直线路径采样 t t.view(-1, 1, 1, 1) # 适配图像维度 xt (1 - t) * x0 t * x1 target_velocity x1 - x0 return xt, target_velocity def sample_ot_path(x0, x1, t): 简化版OT路径通过排序近似 # 对每个样本展平后排序构造单调映射 x0_flat x0.view(x0.size(0), -1) x1_flat x1.view(x1.size(0), -1) # 这里简化处理实际OT需要更复杂的求解 # 假设已经得到了配对 t t.view(-1, 1, 1, 1) xt (1 - t) * x0 t * x1 target_velocity x1 - x0 return xt, target_velocity时间采样方面我通常用Beta分布或者混合分布来增加中间时间段的采样概率。比如def sample_time(batch_size, device): # 使用Beta分布偏向中间区域 t torch.distributions.Beta(2.0, 2.0).sample((batch_size,)).to(device) return tBeta(2,2)的密度在0.5附近最高两端较低正好符合我们对中间区域多采样的需求。3.2 网络架构选择与速度场参数化Flow Matching的网络架构需要接收两个输入时间 $t$ 和当前位置 $x_t$输出速度场 $v_\theta(t, x_t)$。对于图像数据常用的架构是U-Net或者DiTDiffusion Transformer。U-Net的优势在于多尺度特征提取适合捕捉图像的局部和全局结构。DiT则更适合大规模训练扩展性好。时间 $t$ 的嵌入方式很关键。最简单的是用正弦位置编码把标量 $t$ 映射成高维向量然后通过MLP注入到网络中。更高级的做法是用FiLMFeature-wise Linear Modulation或者AdaGNAdaptive Group Normalization让时间信息调制网络中间层的特征。class TimeEmbedding(nn.Module): def __init__(self, dim): super().__init__() self.dim dim self.mlp nn.Sequential( nn.Linear(dim, dim * 4), nn.SiLU(), nn.Linear(dim * 4, dim) ) def forward(self, t): # 正弦位置编码 half_dim self.dim // 2 emb torch.log(torch.tensor(10000.0)) / (half_dim - 1) emb torch.exp(torch.arange(half_dim) * -emb).to(t.device) emb t[:, None] * emb[None, :] emb torch.cat([torch.sin(emb), torch.cos(emb)], dim-1) return self.mlp(emb)速度场的参数化有一个细节需要注意输出的尺度。如果数据经过了归一化速度的尺度通常在1左右网络输出不需要额外的缩放。但如果数据尺度很大最好在输出层加一个可学习的缩放因子或者对目标速度做归一化。3.3 训练循环与关键超参数配置完整的训练循环包括数据加载、路径采样、前向传播、损失计算、反向传播和参数更新。下面是一个典型的训练循环def train_flow_matching(model, dataloader, optimizer, device, epochs): model.train() for epoch in range(epochs): total_loss 0 for batch_idx, x1 in enumerate(dataloader): x1 x1.to(device) batch_size x1.size(0) # 采样噪声和时间 x0 torch.randn_like(x1) t sample_time(batch_size, device) # 构造路径 xt, target_v sample_conditional_path(x0, x1, t) # 前向传播 pred_v model(xt, t) # 计算损失 loss F.mse_loss(pred_v, target_v) # 反向传播 optimizer.zero_grad() loss.backward() # 梯度裁剪可选 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() total_loss loss.item() if batch_idx % 100 0: print(fEpoch {epoch}, Batch {batch_idx}, Loss: {loss.item():.4f}) avg_loss total_loss / len(dataloader) print(fEpoch {epoch} completed, Average Loss: {avg_loss:.4f})关键超参数配置方面我整理了一个参考表超参数推荐范围说明批大小64-256太小梯度噪声大太大显存不够学习率1e-4 - 5e-4配合预热和余弦退火预热步数500-2000防止初期震荡权重衰减1e-6 - 1e-5轻度正则化时间采样Beta(2,2)偏向中间区域梯度裁剪1.0可选训练不稳定时启用EMA衰减0.999-0.9999用于推理时稳定输出EMA指数移动平均在Flow Matching中很有用。训练时维护一份模型参数的滑动平均推理时用EMA参数生成的样本通常更平滑、更稳定。实现起来也简单class EMA: def __init__(self, model, decay0.999): self.model model self.decay decay self.shadow {k: v.clone().detach() for k, v in model.state_dict().items()} def update(self): for k, v in self.model.state_dict().items(): if v.dtype.is_floating_point: self.shadow[k] self.decay * self.shadow[k] (1 - self.decay) * v else: self.shadow[k] v.clone() def apply(self): self.model.load_state_dict(self.shadow)3.4 推理采样从ODE求解到少步生成训练完成后推理采样就是解一个常微分方程初值问题$$\frac{dx}{dt} v_\theta(t, x), \quad x(0) x_0 \sim \mathcal{N}(0, I)$$最简单的求解方法是欧拉法torch.no_grad() def sample_euler(model, x0, num_steps10): model.eval() dt 1.0 / num_steps x x0 for i in range(num_steps): t torch.full((x.size(0),), i * dt, devicex.device) v model(x, t) x x v * dt return x欧拉法简单但精度有限步数少时误差较大。更好的选择是RK4或者DPM-Solver这类高阶求解器。但在Flow Matching中由于路径通常比较直欧拉法在10步左右就能给出不错的结果。如果路径是OT路径甚至5步就够了。提示推理步数不是越多越好。对于直线路径速度场在整个区间上变化不大多步带来的收益递减。我实测下来10步和50步的视觉差异很小但计算时间差了5倍。4. 常见问题与排查技巧实录4.1 训练不收敛或损失震荡怎么办Flow Matching训练不收敛最常见的原因有三个学习率太大、时间采样不合理、网络容量不足。学习率太大时损失会在某个值附近剧烈震荡降不下去。这时候先检查学习率试着降一个数量级。如果降了之后收敛但很慢再逐步往上调。预热阶段也很关键如果一开始就用大学习率网络可能直接跑偏。时间采样不合理的情况比较隐蔽。如果只用均匀采样中间时间段的梯度信号可能不够导致速度场在中间区域拟合不好。表现是生成的样本在中间步骤出现明显的伪影或者模糊。换成Beta(2,2)或者对中间时间段加权通常能改善。网络容量不足时损失会稳定在一个较高的值降不下去。这时候需要增加网络深度或者宽度。但也要注意容量太大而数据量不够时容易过拟合验证损失会先降后升。还有一个容易被忽略的问题是数据归一化。如果数据没有归一化到合理范围速度的量级会很大网络需要输出很大的值训练难度增加。我一般把数据归一化到[-1, 1]或者[0, 1]速度的量级就控制在1左右。4.2 生成样本质量差的排查思路生成样本质量差可能表现为模糊、伪影、模式崩塌等。排查思路可以按以下顺序来先看训练损失。如果训练损失本身就很高说明模型没学好问题在训练阶段。检查学习率、网络容量、训练轮数。如果训练损失很低但生成质量差说明过拟合了需要加正则化或者更多数据。再看推理步数。步数太少时ODE求解误差大生成样本可能偏离数据分布。试着增加步数如果质量明显提升说明步数不够。但也要注意如果增加步数后质量反而下降可能是速度场在某些区域预测不准多步累积了误差。然后检查路径类型。直线路径在某些数据分布下可能不是最优的速度场变化剧烈网络难以拟合。换成OT路径或者VP路径方差保持路径试试。最后检查EMA。如果用了EMA但衰减率设得太小EMA参数和训练参数差异大推理时可能不稳定。衰减率一般设0.999以上训练步数越多衰减率可以越大。4.3 梯度计算中的数值稳定性问题Flow Matching的梯度计算通常比扩散模型稳定但仍有几个数值稳定性的坑需要注意。第一个坑是 $t$ 接近0或1时的数值精度。当 $t$ 非常接近0时$x_t$ 几乎等于 $x_0$速度目标 $x_1 - x_0$ 可能很大。如果数据尺度大这个值可能溢出。解决办法是对数据做归一化或者在计算损失时对目标速度做裁剪。第二个坑是时间嵌入的数值范围。正弦位置编码在 $t$ 很小时高频分量的值可能非常小导致梯度消失。可以在时间嵌入后加一个LayerNorm稳定数值范围。第三个坑是混合精度训练。用FP16训练时速度场的值如果超出FP16的表示范围会出现NaN。解决办法是用BF16代替FP16或者对损失做缩放loss scaling。# 混合精度训练示例 from torch.cuda.amp import autocast, GradScaler scaler GradScaler() for x1 in dataloader: x1 x1.to(device) x0 torch.randn_like(x1) t sample_time(x1.size(0), device) xt, target_v sample_conditional_path(x0, x1, t) with autocast(): pred_v model(xt, t) loss F.mse_loss(pred_v, target_v) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() optimizer.zero_grad()4.4 常见问题速查表问题现象可能原因排查方法解决方案损失震荡不降学习率太大降低学习率10倍加预热用余弦退火损失降不下去网络容量不足增加层数或通道数换更大架构生成样本模糊推理步数不够增加步数测试换高阶求解器生成样本有伪影时间采样不均检查时间分布用Beta分布采样训练后期过拟合数据量不足看验证损失加正则化或数据增强混合精度NaNFP16溢出检查损失值换BF16或加loss scaling速度场预测偏差大数据未归一化检查数据范围归一化到[-1,1]推理不稳定EMA衰减太小检查EMA参数增大衰减率到0.999注意排查问题时一次只改一个变量。同时改多个超参数即使效果变好了你也不知道是哪个起了作用。这是我在实际项目里踩过的最大的坑。5. 从Flow Matching到Diffusion Policy的迁移实践5.1 动作序列生成中的路径设计把Flow Matching用到Diffusion Policy上核心变化在于数据的形式。图像生成中$x_1$ 是一张图机器人策略中$x_1$ 是一段动作序列通常形状是 $[T, A]$$T$ 是预测步长$A$ 是动作维度。路径设计在动作生成中有额外的考量。机器人动作通常需要平滑相邻时间步的动作不能跳变。如果直接用直线路径生成的动作可能在时间维度上不够平滑。一种改进是设计时间相关的路径让不同时间步的路径略有差异鼓励网络学到时间一致的速度场。另一种思路是用动作序列的频域表示。先把动作序列做DCT变换在频域上构造路径生成后再逆变换回时域。这样生成的轨迹天然平滑因为高频分量被抑制了。5.2 实时推理的优化策略机器人控制对推理速度要求很高通常需要在10ms以内完成一次动作生成。Flow Matching的少步采样特性在这里是巨大优势但仍需进一步优化。第一个优化是减少推理步数。对于动作生成3到5步通常就够了。如果路径是OT路径甚至1步就能给出可用的动作。1步采样时Flow Matching退化成类似行为克隆的确定性策略但训练时学到的速度场提供了更好的泛化能力。第二个优化是网络轻量化。动作序列的维度通常远小于图像网络可以做得更小。用MLP或者轻量Transformer参数量控制在百万级别推理时间可以压到1ms以内。第三个优化是缓存。如果相邻控制周期的动作序列有重叠可以缓存上一周期的速度场预测只对新增的部分重新计算。这种增量式推理能进一步降低计算量。5.3 多模态动作分布的处理机器人策略面临的一个核心挑战是多模态性同一个状态下可能有多种合理的动作。比如抓取一个杯子可以从左边抓也可以从右边抓。扩散模型天然能建模多模态分布Flow Matching同样可以。Flow Matching处理多模态的关键在于路径的多样性。如果所有样本都用同一条直线路径网络学到的速度场是条件速度的期望多模态信息会被平均掉。解决办法是让路径本身具有随机性。比如在构造 $x_t$ 时对不同的 $x_1$ 使用不同的 $x_0$ 采样或者对路径加一点随机扰动。另一种方法是条件Flow Matching把模态信息作为条件输入。比如用一个离散的模态标签 $c$ 表示抓取方向网络学习 $v_\theta(t, x_t, c)$。推理时采样不同的 $c$就能生成不同模态的动作。提示多模态动作生成中模态崩塌是常见问题。如果发现生成的动作用远是同一模式检查路径随机性是否足够或者增加模态标签的多样性。6. 一些实操中的个人体会Flow Matching这个方向我从去年开始跟进前后在图像生成和机器人策略上都做过实验。最大的感受是它的简洁性既是优点也是陷阱。优点在于训练目标简单直接不需要像扩散模型那样推导复杂的变分下界或者得分匹配。陷阱在于简洁的代价函数背后路径设计、时间采样、网络架构这些选择对最终效果影响巨大而这些选择往往没有理论指导需要靠实验试出来。我试过在图像数据上用直线路径训练FID能到3左右但换成OT路径后降到了2.5。差异不大但训练稳定性明显提升学习率可以设得更大收敛更快。在机器人动作生成上直线路径的1步采样效果已经够用但多模态任务上路径随机性必不可少。梯度方面Flow Matching的梯度确实比扩散模型干净。扩散模型训练时得分函数在 $t$ 接近0时的数值可能非常大需要各种技巧来稳定。Flow Matching的速度目标通常在一个合理的范围内梯度裁剪很多时候都不需要。但这也意味着如果数据尺度没控制好速度目标可能很大这时候归一化就很重要。最后分享一个小技巧训练初期先用少量数据快速过拟合确认模型有能力拟合速度场。如果连过拟合都做不到说明网络架构或者训练配置有问题。过拟合之后再逐步增加数据量调整正则化。这个流程能帮你快速定位问题避免在错误的方向上浪费时间。这个方向后续还可以往几个方向扩展。一是路径设计的自动化用可学习的路径参数代替手工设计。二是与强化学习结合用Flow Matching生成动作候选再用价值函数筛选。三是扩展到更复杂的动作空间比如带约束的轨迹优化。这些方向都有不少空白值得深入探索。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。