7.2 KiB
7.2 KiB
ASMR++ 奖励计算完整分析
默认配置使用 reward_type: spatial_max + error_metric: maximum。整个奖励信号链分以下步骤:
Step 1: 误差估计 — 精细网格参考解
参考网格 (初始网格细化6次)
↓ FEM求解
参考解 u_ref (视为"真值")
↓
粗网格解 u_coarse 在每个积分点(参考网格元素中点)与 u_ref 比较
↓
绝对误差 |u_ref - u_coarse| per 积分点
↓ scatter_max per 粗元素
error_per_element: 每个粗网格元素内的最大误差 (num_elements, solution_dim)
用精细网格做数值积分 (error_integrator.py:86-169),支持三种积分方式:mean(积分平均值)、squared(积分平方误差)、maximum(元素内最大误差)。默认Poisson 是标量 PDE,solution_dim=1。
Step 2: spatial_max 奖励计算
核心代码在 mesh_refinement.py:657-714,以下是逐步推导:
奖励基准 (reward_per_agent_and_dim)
= previous_error_per_element ← 细分前该元素的误差
┌─────┬─────┐
│ │ │
├─────┼─────┤
│ │ │
└─────┴─────┘
父元素 i (error=0.8) 子元素: j1(0.3), j2(0.5), j3(0.6), j4(0.1)
↓ scatter_max per agent_mapping
max_mapped_error[i] = max(0.3, 0.5, 0.6, 0.1) = 0.6
↓
reward_raw[i] = 0.8 - 0.6 = +0.2 ✅ 误差最大的子元素也比父元素好
关键:spatial_max 只奖励"所有子元素误差都下降"的情况。如果有任一子元素误差仍等于原父元素误差,reward=0。
父元素 j (error=0.5) 子元素: k1(0.5), k2(0.1), k3(0.2), k4(0.05)
↓
max_mapped_error[j] = max(0.5, 0.1, 0.2, 0.05) = 0.5
↓
reward_raw[j] = 0.5 - 0.5 = 0 ❌ 有一个子元素仍未改善
对比 spatial (非 max) 模式:
reward_raw[i] = previous_error[i] - Σ_j φ_ij * error[j]
= 标量加法 (np.add.at) 把所有子元素误差从父元素误差中减去
这种模式下即使部分子元素没有改善,整体仍有正奖励。
Step 3: 归一化 + 降维到标量
# 除以初始网格的误差 → 把误差改善量归一化到 [0, ~1] 区间
reward_per_agent_and_dim = reward_per_agent_and_dim / initial_approximation_error
# 多维 PDE 降维: dot product with solution_dimension_weights
# Poisson 是标量PDE, weights=[1.0], 即恒等变换
reward_per_agent = project_to_scalar(reward_per_agent_and_dim)
# = np.dot(reward_per_agent_and_dim, [1.0]) = reward_per_agent_and_dim
Step 4: 元素惩罚 (Element Penalty)
# 统计每个父元素产生了多少子元素
element_counts = unique(agent_mapping, return_counts=True)[1] # 每个父元素→子元素的数量
element_counts = element_counts - 1 # 减1因为是"新增的"子元素数
# 默认 λ ~ 0.01 (loguniform 采样于 [1e-3, 1e-1])
element_penalty = λ * element_counts
┌──────────────────────────┬────────────────┬──────────────────┐
│ 场景 │ element_counts │ penalty (λ=0.01) │
├──────────────────────────┼────────────────┼──────────────────┤
│ 未细分元素 │ 0 │ 0 │
├──────────────────────────┼────────────────┼──────────────────┤
│ 分裂为 4 个子三角 │ 3 │ 0.03 │
├──────────────────────────┼────────────────┼──────────────────┤
│ 被波及细分 (Rivara 平滑) │ 1-3 │ 0.01-0.03 │
└──────────────────────────┴────────────────┴──────────────────┘
作用: 惩罚是正则化项,防止策略无节制细分所有元素。只在"误差改善 > 细分代价"时细分才有利。
Step 5: 元素上限惩罚 (Element Limit Penalty)
if num_elements > maximum_elements (20000):
element_limit_penalty = 1000 / previous_num_elements # ≈ 0.05~0.5 per agent
else:
element_limit_penalty = 0
Step 6: 最终每 Agent 奖励
r_i = error_improvement_i / initial_error
- λ * new_elements_created_by_i
- limit_penalty
形状为 (num_agents_t,) — 每个 agent(父元素)一个标量奖励。
Step 7: 奖励到 TD 误差 — 与论文公式 (3) 的对应
Buffer 存储:
r_i(s_t, a_t) ← 父元素 i 的奖励 (num_agents_t,)
V_i(s_t) ← 父元素 i 的价值 (num_agents_t,)
φ_ij = agent_mapping ← 子元素j → 父元素i 的映射
V_j(s_{t+1}) ← 子元素的价值 (num_agents_{t+1},)
GAE Delta 计算:
projected_V = scatter_sum(V_j(s_{t+1}), index=φ_ij) ← Σ_j φ_ij·V_j(s_{t+1})
δ_i = r_i + γ * projected_V_i - V_i(s_t)
对应论文 (3): δ_i^t = r(s^t, a^t)_i + γ·Σ_j φ_ij^t·V_j(s^{t+1}) - V_i(s^t)
Step 8: 混合奖励 (Mixed Return, global_weight=0.5)
在 MixedOnPolicyBuffer 中额外计算:
# 全局奖励 (均值)
r_global = mean(r_i) # 所有agent的平均奖励
# 全局价值 (均值)
V_global = mean(V_i) # 所有agent的平均价值
# 全局 GAE
δ_global = r_global + γ·V_global' - V_global
# 局部 GAE
δ_local_i = 上述 per-agent GAE
# 混合 Advantage
A_i = (1 - 0.5) * A_local_i + 0.5 * A_global
完整奖励流总结
FEM求解 → 逐元素误差估计 (±积分 vs 参考网格)
↓
spatial_max: error_before - max_error_of_children
↓
归一化 (/ initial_error)
↓
- λ * new_elements + limit_penalty
↓
r_i (per agent) ────────────→ 局部 GAE → A_local_i
│ ↓
└→ r_global = mean(r_i) → 全局 GAE → A_global
↓
A_i = 0.5·A_local_i + 0.5·A_global
↓
送入 PPO policy_loss
设计精巧之处:
1. 空间奖励 + agent_mapping:每个元素独立计算误差改善,通过 agent_mapping φ_ij 追踪父→子关系
2. spatial_max 语义:reward 表示"最差子元素的误差下降量"——驱动策略优先细分误差最大的区域
3. 元素惩罚:防止盲目细分,精确到每个 agent 独立计算代价
4. 混合奖励:局部信号指导细粒度决策 + 全局信号稳定整体训练