NOAI 2024 复赛题解|第4题:不懂微分方程,连题目都读不完

NOAI 2024 复赛题解|第4题:不懂微分方程,连题目都读不完

本文核心观点
NOAI 2024复赛第4题要求用PyTorch自动求导回归阻尼单摆的物理参数,不是训练神经网络。核心思路:分段差分回归α和β,再用ODE数值模拟求解时刻参数。

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 的自动求导 + 梯度下降)解一个物理问题。

微信二维码

扫码备注【NOAI】加交流群