NOAI 2024 复赛题解|第4题:不懂微分方程,连题目都读不完
NOAI复赛 机器学习 / 物理建模
需要真题、资料,请拉到文末添加艾斯老师微信。
题目回顾
一个带空气阻力的单摆,传感器记录了摆角 θ(t) 随时间变化的数据。但传感器中途坏了若干秒,断档期间有人施加了竖直向下的恒力 F。
运动满足微分方程:a(t) = -α·ω(t) - β·sin(θ(t))
其中 α = μ/m,施力前 β₁ = g/l,施力后 β₂ = g/l + F/(ml)。质量 m=1,g=9.8。
需要求解的 5 个参数
1绳长 l
2空气阻力 μ
3外力 F
4施力后下一次 θ(t)=0 的时刻
5施加外力的时刻
训练集、测试集A、测试集B的参数全不一样。你提交的是一种通用求解方法,不是针对某组数据调出来的参数。
这道题在考什么
不是训练一个神经网络去"预测"参数。是用 PyTorch 的自动求导机制,回归微分方程的参数。
前三道题是"给数据、训模型、做预测",这道题是"给数据、建物理方程、用梯度下降拟合方程参数"。思路完全不同。
第一步:理解数据的结构
θ(t) 曲线分三段:
• 前半段:正常的阻尼振荡(振幅逐渐减小),对应 β₁
• 中间:数据缺失(传感器断了),某时刻施加了外力
• 后半段:振荡规律变了(施加了外力),对应 β₂
第一件事是把数据分成两段:找相邻时间戳间隔的突变。
dt = np.diff(t) gap_idx = np.argmax(dt) # 间隔最大的位置就是断档 t1, theta1 = t[:gap_idx+1], theta[:gap_idx+1] # phase 1 t2, theta2 = t[gap_idx+1:], theta[gap_idx+1:] # phase 2
第二步:用差分代替微分
微分方程里有角速度 ω 和角加速度 a,但数据只给了 θ。用差分近似导数:
ω = Δθ/Δt # 从 θ 算角速度,数组长度 n-1 a = Δω/Δt # 从 ω 算角加速度,数组长度 n-2
每求一次差分,数组长度减 1。三个数组要截断到相同长度,这是最容易出 bug 的地方 ⚠️
思路一:分段回归 α 和 β
Phase 1:用断档前的数据回归 α 和 β₁
class PendulumPhase1(nn.Module): def __init__(self): super().__init__() self.alpha = nn.Parameter(torch.tensor([0.5])) self.beta1 = nn.Parameter(torch.tensor([10.0])) def forward(self, omega, theta): return -self.alpha * omega - self.beta1 * torch.sin(theta)
训练目标:让预测的 a 尽量接近从数据差分算出来的真实 a。回归出 α 和 β₁ 后:
• l = g / β₁ = 9.8 / β₁
• μ = α × m = α(因为 m=1)
Phase 2:固定 α(已知),只回归 β₂。然后算 F:
• F = (β₂ - β₁) × l
思路二:联合回归
把两段数据一起训练,共享 α,分别回归 β₁ 和 β₂。好处是 α 同时受两段数据约束,估计更准。
第三步:用 ODE 求 t_Fput 和 t_nextzerotheta
这两个参数不能直接从差分回归得到,需要用微分方程做正向数值模拟。
求 t_Fput
从 phase 1 末尾用 β₁ 向前模拟,从 phase 2 开头用 β₂ 向后模拟,两条曲线在断档区间内最接近的时刻就是 t_Fput。用 scipy.integrate.odeint 做数值积分。
求 t_nextzerotheta
从 phase 2 最后一个数据点继续用 β₂ 向前模拟,找到 θ(t) 第一次过零的时刻。
# 找过零点 theta_sol = sol[:, 0] zero_crossings = np.where(np.diff(np.sign(theta_sol)))[0] t_nextzerotheta = t_future[zero_crossings[0]]
容易踩的坑
坑 1差分数组长度对不齐
θ 有 n 个点 → ω 有 n-1 个 → a 有 n-2 个。维度不匹配但 PyTorch 广播不报错,算出来的 loss 是错的。
坑 2参数初始化离真实值太远
用物理常识给初始值。单摆绳长一般 0.5~5 米,β₁ = g/l 大概在 2~20 之间,比 torch.randn 的随机值靠谱得多。"用物理直觉缩小搜索范围"是 AI for Science 领域的常见做法,平时做过物理建模的同学不会在这里卡住。
坑 3评分公式的容错差异
l 和 μ 的评分是 exp(-10|误差|),偏差 0.1 分数就从 1 掉到 0.37。F 的评分是 exp(-|误差|),宽松 10 倍。先把 l 和 μ 搞准 👈
坑 4手动调参在测试集上不工作
三个数据集参数全不同。如果你的 epoch 数、学习率是针对训练集调的,测试集上大概率不收敛。用 early stopping 或收敛判断,让方法自适应。
区分度在哪
这道题的门槛是物理和数学背景。不懂微分方程、不理解阻尼振荡,连题目都没法下手。
过了门槛之后:
• 能分段回归 α、β₁、β₂ → 拿到 l、μ、F 三个参数的基础分
• 能用 odeint 正向模拟 → 再拿 t_Fput 和 t_nextzerotheta 的分
• 初始化合理、收敛自动化、差分对齐 → 方法在测试集上也稳定
这道题不是 AI 题,是用 AI 的工具(PyTorch 的自动求导 + 梯度下降)解一个物理问题。