一句话总结

将大气廓线反演(辐射→状态)和辐射前向模拟(状态→辐射)视为一对互逆映射,通过循环一致性约束联合训练,在 FY-4A GIIRS 高光谱数据上同时提升反演精度和辐射重建质量。

问题设定

两个方向的物理问题

高光谱红外探测仪(如 FY-4A GIIRS,约 1700 个通道)测量大气层顶的辐射亮温 $\mathbf{r} \in \mathbb{R}^C$,其物理本质是大气状态 $\mathbf{a} \in \mathbb{R}^{L \times K}$($L$ 个气压层,$K$ 个变量如温度、湿度)的积分响应:

\[r_c = \int_0^\infty B_c(T(p)) \cdot \frac{\partial \tau_c(p)}{\partial p} \, dp\]

其中 $\tau_c(p)$ 是通道 $c$ 在气压 $p$ 处的透过率。这产生了两类任务:

  • 正问题(前向模拟):$\mathbf{a} \to \mathbf{r}$,有精确物理模型(如 RTTOV),耗时但可靠
  • 逆问题(廓线反演):$\mathbf{r} \to \mathbf{a}$,病态——不同廓线可能产生极相近的辐射值

现有方法的两个缺陷

缺陷 1:单向性。大多数深度学习方法只做反演,完全忽略了两者之间的物理对称性。

缺陷 2:解耦性。即使同时训练两个方向的网络,各自独立优化就无法保证互相自洽——反演得到的廓线,再经前向模拟后可能无法还原原始辐射观测。

SIMBA 的核心思路

设反演网络 $R_\theta: \mathbb{R}^C \to \mathbb{R}^{L \times K}$,前向模拟网络 $F_\phi: \mathbb{R}^{L \times K} \to \mathbb{R}^C$,联合优化:

\[\mathcal{L}(\theta, \phi) = \mathcal{L}_{\text{ret}}(\theta) + \mathcal{L}_{\text{sim}}(\phi) + \lambda_{\text{cyc}} \cdot \mathcal{L}_{\text{cyc}}(\theta, \phi)\]

其中循环一致性损失强制双向自洽:

\[\mathcal{L}_{\text{cyc}} = \mathbb{E}\left[\|F_\phi(R_\theta(\mathbf{r})) - \mathbf{r}\|^2\right] + \mathbb{E}\left[\|R_\theta(F_\phi(\mathbf{a})) - \mathbf{a}\|^2\right]\]

算法原理

直觉解释

类比双语互译的训练场景:同时训练”中→英”和”英→中”两个翻译器,并要求”中→英→中”的循环翻译结果尽量接近原中文。

这个约束的好处不仅仅是提供额外监督信号——循环损失的梯度会同时流向 $\theta$ 和 $\phi$,强制两个网络在优化过程中”配合”。从优化结构看,这是一个耦合最小化问题,而非两个独立子问题的叠加。

诚实的局限:循环一致性隐含假设 $R$ 和 $F$ 构成双射,但大气反演本质上是病态的(一个辐射值可对应多个廓线)。因此这是一个软约束,有助于但不能根治病态性。

为什么要用双向 Mamba?

大气廓线在垂直方向有强烈的非局部依赖:高层云的辐射效应会影响整个气柱的传输。Transformer 对气压层序列建模的复杂度是 $O(L^2)$,而 Mamba 的选择性状态空间模型(S6)将扫描复杂度降至 $O(L)$。

核心方程(输入依赖的离散化状态更新):

\[h_t = \underbrace{e^{\Delta_t A}}_{\bar{A}_t} h_{t-1} + \underbrace{\Delta_t B(x_t)}_{\bar{B}_t} x_t, \quad y_t = C(x_t) h_t\]

其中 $\Delta_t, B, C$ 均依赖输入——这是与经典 SSM 的关键区别,赋予了模型”选择性”记忆能力。

双向设计的必要性:气压层序列没有时间方向,从地面向上和从对流层向下的信息流同等重要:

\[\mathbf{h}_{\text{bi}} = \text{Merge}\left(\text{SSM}_{\text{fwd}}(\mathbf{x}),\ \text{SSM}_{\text{bwd}}(\text{flip}(\mathbf{x}))\right)\]

收敛性的诚实评估

  • 循环损失涉及两次前向传播,梯度路径加倍,梯度爆炸风险显著增大
  • $\lambda_{\text{cyc}}$ 过大会使两个网络”相互妥协”,反演精度反而下降
  • 推荐策略:先令 $\lambda_{\text{cyc}} = 0$ 让两个分支独立收敛,再逐步引入循环约束(课程式增大)

实现

核心算法:双向 Mamba 块

import torch
import torch.nn as nn
import torch.nn.functional as F

class SelectiveSSM(nn.Module):
    """简化版选择性 SSM,输入依赖的 A/B/C 参数"""
    def __init__(self, d_model, d_state=16):
        super().__init__()
        self.d_state = d_state
        self.x_proj  = nn.Linear(d_model, d_state * 2 + d_model)  # B, C, Δ
        A = torch.arange(1, d_state + 1).float().repeat(d_model, 1)
        self.log_A = nn.Parameter(torch.log(A))  # 固定对角结构,学习幅度
        self.D     = nn.Parameter(torch.ones(d_model))  # 跳跃连接系数

    def forward(self, x):
        # x: [B, L, d_model],L = 气压层序列长度
        B, L, D = x.shape
        BCdt = self.x_proj(x)
        B_p  = BCdt[..., :self.d_state]
        C_p  = BCdt[..., self.d_state:2 * self.d_state]
        dt   = F.softplus(BCdt[..., 2 * self.d_state:])  # Δ > 0
        A    = -torch.exp(self.log_A)  # 稳定性:要求 A < 0

        h, ys = torch.zeros(B, D, self.d_state, device=x.device), []
        for t in range(L):  # 沿气压层顺序扫描(可替换为并行前缀扫描)
            dA = torch.exp(dt[:, t, :, None] * A[None])
            dB = dt[:, t, :, None] * B_p[:, t, None, :]
            h  = h * dA + dB * x[:, t, :, None]
            ys.append((h * C_p[:, t, None, :]).sum(-1))

        return torch.stack(ys, dim=1) + self.D[None, None] * x  # 残差

class BiMambaBlock(nn.Module):
    """双向 Mamba 块:同时捕获低层→高层和高层→低层的依赖"""
    def __init__(self, d_model, d_state=16):
        super().__init__()
        self.fwd = SelectiveSSM(d_model, d_state)
        self.bwd = SelectiveSSM(d_model, d_state)
        self.norm  = nn.LayerNorm(d_model)
        self.merge = nn.Linear(d_model * 2, d_model)

    def forward(self, x):
        h = self.norm(x)
        fwd = self.fwd(h)                          # 正向扫描
        bwd = self.bwd(h.flip(1)).flip(1)          # 反向扫描后翻转对齐
        return x + self.merge(torch.cat([fwd, bwd], dim=-1))

SIMBA 完整架构

class AtmosphericEncoder(nn.Module):
    def __init__(self, d_model=128, n_layers=4, d_state=16):
        super().__init__()
        self.blocks = nn.ModuleList(
            [BiMambaBlock(d_model, d_state) for _ in range(n_layers)]
        )
    def forward(self, x):
        for blk in self.blocks:
            x = blk(x)
        return x

class SIMBA(nn.Module):
    """
    联合反演-模拟框架(教学简化版,省略位置编码等细节)
    C_rad=1700(GIIRS 通道数),L=37(ERA5 气压层),K=2(温度+湿度)
    """
    def __init__(self, C_rad=1700, L=37, K=2, d_model=128, n_layers=4):
        super().__init__()
        # 反演分支:辐射(C_rad 通道序列)→ 廓线(L 气压层)
        self.rad_embed   = nn.Linear(1, d_model)
        self.ret_encoder = AtmosphericEncoder(d_model, n_layers)
        self.ret_head    = nn.Linear(d_model, K)
        self.ret_proj    = nn.Linear(C_rad, L)   # 通道维度 → 气压层维度
        # 前向模拟分支:廓线(L 气压层序列)→ 辐射(C_rad 通道)
        self.prof_embed  = nn.Linear(K, d_model)
        self.sim_encoder = AtmosphericEncoder(d_model, n_layers)
        self.sim_head    = nn.Linear(d_model, 1)
        self.sim_proj    = nn.Linear(L, C_rad)   # 气压层维度 → 通道维度

    def retrieve(self, r):
        """r: [B, C_rad] → profile: [B, L, K]"""
        x = self.rad_embed(r.unsqueeze(-1))      # [B, C_rad, d_model]
        x = self.ret_encoder(x)
        x = self.ret_head(x)                      # [B, C_rad, K]
        return self.ret_proj(x.transpose(1, 2)).transpose(1, 2)  # [B, L, K]

    def simulate(self, a):
        """a: [B, L, K] → radiance: [B, C_rad]"""
        x = self.prof_embed(a)                    # [B, L, d_model]
        x = self.sim_encoder(x)
        return self.sim_proj(self.sim_head(x).squeeze(-1))  # [B, C_rad]

循环一致性训练

def simba_loss(model, r_obs, a_obs, lambda_cyc=0.1):
    """
    r_obs: 卫星观测辐射亮温 [B, C_rad]
    a_obs: ERA5 廓线真值    [B, L, K]
    """
    # 有监督损失
    a_pred = model.retrieve(r_obs)
    r_pred = model.simulate(a_obs)
    loss_ret = F.mse_loss(a_pred, a_obs)
    loss_sim = F.mse_loss(r_pred, r_obs)

    # 循环一致性:梯度同时流向两个分支的参数
    r_cycle = model.simulate(a_pred)       # 辐射 → 廓线 → 辐射
    a_cycle = model.retrieve(r_pred)       # 廓线 → 辐射 → 廓线
    loss_cyc = F.mse_loss(r_cycle, r_obs) + F.mse_loss(a_cycle, a_obs)

    return loss_ret + loss_sim + lambda_cyc * loss_cyc

def train_epoch(model, loader, optimizer, lambda_cyc=0.1):
    model.train()
    for r_obs, a_obs in loader:
        optimizer.zero_grad()
        loss = simba_loss(model, r_obs, a_obs, lambda_cyc)
        loss.backward()
        # 循环损失路径长,梯度裁剪是必须项
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        optimizer.step()

超参数指南

参数 推荐范围 敏感度 调参建议
lambda_cyc 0.05 ~ 0.3 从 0 逐步增大,监视验证集反演误差
d_model 64 ~ 256 GIIRS 通道信息量大,推荐 128+
n_layers 3 ~ 6 超过 6 层梯度消失风险增加
d_state 8 ~ 32 默认 16,气压层不长无需太大
学习率 1e-4 ~ 3e-4 AdamW + cosine 衰减

实验

温度/湿度廓线反演(基线对比)

基于论文相对改善幅度的近似对比(仅供参考,具体数值见原文):

方法 温度 RMSE (K) 湿度 RMSE (g/kg)
物理 1D-Var (RTTOV) ~1.8 ~0.80
单向 CNN ~1.52 ~0.68
单向 Transformer ~1.38 ~0.61
单向 Mamba ~1.31 ~0.58
SIMBA(双向 + 循环一致) ~1.21 ~0.53

改善最显著的区域是中对流层(300–700 hPa),正是辐射传输非局部效应最强的区间。

消融实验

# 四种配置对比
configs = {
    "单向 Mamba,无循环":  dict(bidirectional=False, lambda_cyc=0.0),
    "双向 Mamba,无循环":  dict(bidirectional=True,  lambda_cyc=0.0),
    "单向 Mamba,有循环":  dict(bidirectional=False, lambda_cyc=0.1),
    "SIMBA(完整)":       dict(bidirectional=True,  lambda_cyc=0.1),
}
# 结论:
# 双向设计对反演精度的贡献 > 循环一致性(约 0.07K vs 0.03K RMSE)
# 循环一致性对辐射重建的贡献 > 双向设计(约 15% vs 6% RMSE)
# 两者结合的改善 > 各自单独改善之和(协同效应)

失败案例:对流降水区域

SIMBA 在热带辐合带(ITCZ)附近表现明显下降,温度误差高出平均值约 40%。原因:

  1. 深对流引发的剧烈垂直混合,打破了辐射传输方程的层流假设
  2. ERA5 再分析数据本身在对流参数化上存在系统偏差
  3. 循环一致性无法修复训练数据的偏差——如果 ERA5 廓线本身不准,学出的 $R$ 和 $F$ 都会带偏差,且循环一致性会把这个偏差”固化”

什么时候用 / 不用?

适用场景 不适用场景
物理过程存在明确互逆结构 只需要单向映射
有足够的配对 (观测, 状态) 数据 数据极少(循环损失需要批量稳定)
垂直/时序方向有长程依赖 问题本质是单射(循环约束退化为恒等)
希望模型内部物理自洽 计算预算极紧(双编码器 + 循环损失 ≈ 2× 开销)

与其他方法对比

方法 优点 缺点 适用场景
物理 1D-Var 可解释,无需大量数据 慢(迭代求解),依赖背景场先验 业务精度要求,小数据
单向 DL 快速,精度高 无物理约束,解耦两方向 纯反演或纯模拟
CycleGAN 式 可处理无配对数据 不保证物理量纲正确性 无标签场景
SIMBA 双向联合自洽,精度最优 训练复杂,超参敏感 有配对数据的联合任务

实践建议

调试流程(三步走)

  1. 先令 lambda_cyc=0,独立训练反演和模拟分支,确认各自验证损失正常下降
  2. 打开循环一致性后,同时监控循环误差和反演误差——循环误差下降而反演误差上升 = lambda_cyc 过大
  3. 确认稳定后,再微调 lambda_cyc 到最优值

常见陷阱

  • 梯度爆炸:循环损失的计算图跨越两个网络,路径加倍,clip_grad_norm 不可省略
  • 通道冗余:GIIRS 1700 个通道高度相关,直接全量输入易过拟合,先做 PCA 降维至 200~300 个主成分是常见预处理
  • 归一化不当:温度廓线(单位 K)和辐射亮温(单位 mW/m²/sr/cm⁻¹)量纲差异极大,必须各自独立归一化,不能混用同一个 BatchNorm

何时放弃:如果 lambda_cyc=0 的单向基线在验证集上反演 RMSE 已低于 1.0K,引入循环一致性带来的边际收益很可能不超过额外的训练成本。SIMBA 的优势在中等精度区间最为显著。