4. 数据生成与预处理实战

DeepONet 的预测能力上限,很大程度上由训练数据决定。与图像任务可以直接采集现成样本不同,动力系统算子学习的数据必须由我们自己"制造":给定一族初值条件、系统参数和时间域,用数值求解器逐条积分出轨迹,再整理成网络可消费的张量。本节围绕这条数据生产线展开,重点解决三个问题——采样空间怎么设计、数值求解怎么保证质量、数据怎么归一化与对齐并封装成可复用管道。

一、采样策略:在算子输入空间上布点

DeepONet 学习的是"输入函数 → 输出函数"的映射。以阻尼振子 ẍ + 2ζωₙẋ + ωₙ²x = 0 为例,分支网络的输入可以取初始状态 (x₀, v₀),主干网络的输入是时间坐标 t,输出是位移轨迹 x(t)。因此采样要覆盖三类维度:初始条件(x₀、v₀ 的取值区间)、系统参数(阻尼比 ζ、固有频率 ωₙ)、时间域(积分区间 [0, T] 与采样步长 Δt)。

采样方式上,均匀网格适合低维且边界重要的场景,但维度升高后样本数指数爆炸;拉丁超立方(LHS)在每个维度上分层抽样,能以较少样本获得较好的空间填充性,是参数采样的首选。对初值这类连续函数输入,还可采用高斯随机场(GRF)生成平滑的随机初值曲线,使分支网络见到更丰富的函数形态。一个可参考的示例配置如下(数值为教学假设,非实测结果):

import numpy as np
from scipy.stats import qmc

N_SAMPLES = 2000
sampler = qmc.LatinHypercube(d=4, seed=42)
u = sampler.random(N_SAMPLES)          # [0,1]^4

# 映射到物理区间
x0  = -1.0 + 2.0 * u[:, 0]             # 初始位移 [-1, 1]
v0  = -1.0 + 2.0 * u[:, 1]             # 初始速度 [-1, 1]
zeta = 0.05 + 0.45 * u[:, 2]           # 阻尼比 [0.05, 0.5]
wn   = 1.0 + 4.0 * u[:, 3]             # 固有频率 [1, 5] rad/s

时间域的采样需要与求解器精度匹配。若用四阶 Runge–Kutta 以 Δt=0.01 s 积分到 T=10 s,则每条轨迹有 1001 个时间点;主干网络若直接吃全部点,计算量偏大,通常再从中抽取 100~200 个配点作为主干输入,而监督标签仍保留全分辨率轨迹用于评估。

二、数值求解与数据质量校验

数据生成的第二步是逐样本积分。对刚性较强的参数区域(如 ζ 很小、ωₙ 很大),固定步长 RK4 可能失稳,此时应改用自适应步长的 solve_ivp(如 RK45 或 Radau),并设置 rtol=1e-8, atol=1e-10。求解完成后必须做质量校验,否则错误样本会悄悄污染训练集:检查轨迹是否出现 NaN/Inf、能量是否单调不增(无外力阻尼系统)、末端幅值是否已衰减到数值噪声以下。对不满足条件的样本应剔除并记录剔除率,若剔除率超过 5%,说明采样区间或求解器设置需要调整。

动力系统数据管道:从采样到可复用数据集 ① 采样空间设计 初值 (x₀, v₀) 参数 ζ, ωₙ 时间域 [0, T], Δt 方法:LHS / GRF ② 数值求解 RK4 / solve_ivp rtol=1e-8 输出轨迹 x(t) 自适应步长防失稳 ③ 质量校验 NaN / Inf 检查 能量单调性 剔除率 < 5% 不合格样本丢弃 ④ 归一化 Z-score 统计量存盘 训练/验证同源 避免信息泄漏 ⑤ 序列对齐与张量组装 分支输入: (N, 4) 初值+参数 主干输入: (N, M) 时间配点 标签: (N, M) 位移值 统一截断/插值到 M 点 变长轨迹 → 定长张量 ⑥ 批处理与划分 batch_size = 64 训练 70% / 验证 15% 测试 15% shuffle + 固定 seed 按样本划分,不按时间点 ⑦ 可复用数据管道 config.yaml 驱动 缓存 .npz 数据集 一键重生成 版本号 + 校验和 实验可复现

三、归一化:统计量必须"只来自训练集"

动力系统数据常跨越多个数量级(位移可能从 1e-3 到 1e0),不归一化会让损失被大幅值样本主导。推荐对分支输入、主干输入和标签分别做 Z-score:x̂ = (x − μ) / σ。关键在于 μ 和 σ 只能由训练集计算,验证集和测试集直接套用同一组统计量,否则会造成信息泄漏,使验证误差虚低。归一化参数应与数据集一起序列化保存,推理时原样加载,保证训练与部署的输入分布一致。

四、序列对齐与批处理

不同参数下轨迹的衰减速度差异很大,若按"末端幅值降到阈值"截断,轨迹长度会参差不齐。工程上通常统一在固定时间网格上重采样:对每条轨迹用线性或三次插值映射到 M 个等距时间点,得到形状一致的 (N, M) 标签矩阵。批处理时按样本维度切分,而不是按时间点切分——同一 batch 内应包含完整的轨迹,否则主干网络的配点会跨样本混叠。划分训练/验证/测试集时同样以样本为单位,并固定随机种子。

五、封装为可复用管道

把上述步骤写成一个由配置文件驱动的生成器,是让实验可复现的关键。建议的目录与接口约定:config.yaml 记录采样区间、样本数、求解器参数、归一化方式与随机种子;生成脚本读取配置后依次执行采样、求解、校验、归一化、对齐、划分,最终落盘为 dataset_v1.npz,同时写入配置哈希与样本数。下游训练脚本只依赖这个 npz 文件,不再关心数据如何产生。这样当需要扩大样本量或调整参数区间时,只需改配置并重新生成,训练代码零改动。

实践建议:先用 200 条样本跑通整条管道并训练一个极小模型,确认张量形状、归一化统计量和损失下降都正常,再放大到数千条样本。数据管道的 bug 往往在形状不匹配和统计量泄漏上,早发现成本最低。
常见陷阱:一是用全体数据计算归一化统计量;二是对变长轨迹直接 padding 而不做掩码,导致填充值参与损失;三是求解器步长过大使高频参数样本失真,却未做质量校验。三者都会让模型在测试集上表现异常,且难以从损失曲线定位。