一句话总结

在只有 9 景时序影像的复杂矿山环境中,将 TU Wien SMI 推广到极化相干矩阵 [T3] 空间、结合沉积物特定标定的半经验模型(R²=0.67,RMSE=5.65 vol.%)持续优于机器学习——物理先验在样本稀缺时是不可替代的惯性约束。

背景:SAR 土壤水分反演的现实困境

为什么用微波雷达测土壤水分?

光学卫星怕云,SAR 穿云透雨。微波对土壤介电常数高度敏感:干土介电常数约 4,水约 80——含水量每增加 1%,C 波段后向散射系数 σ° 约增加 0.1–0.3 dB。这使 SAR 成为大范围土壤水分监测的核心手段。

反演为什么还是难?

σ° 是多种散射机制的叠加。影响因素无法独立观测,只能靠模型假设解耦:

  • 地表粗糙度:翻耕后 σ° 可增大 5–10 dB,远超水分信号
  • 植被衰减:植被层吸收并再散射微波,掩盖土壤信号
  • 混合沉积物:石灰石、粘土、泥炭的介电特性差异悬殊

这篇论文的场景更极端:芬兰东南部石灰岩采石场,地表多种沉积物共存,PALSAR-2 时序影像只有 9 景。9 个样本点让机器学习几乎无用武之地,却是考验物理模型鲁棒性的理想测试床。

现有方法的局限

方法 局限
单极化 SAR 信息量有限,无法分离散射机制
传统 SMI(单通道) 忽略极化信息,对混合地表失效
直接 ML 9 景数据严重欠拟合

算法原理

直觉解释:四极化能看到什么?

用偏振光照裸土和植被,你能区分”表面反射”(土壤)和”体积散射”(植被)。四极化 SAR 同时获取 HH、HV、VH、VV 四通道,通过 Pauli 分解把散射矩阵转换到物理含义清晰的基:

\[\mathbf{k}_P = \frac{1}{\sqrt{2}}\begin{bmatrix}S_{HH}+S_{VV}\\ S_{HH}-S_{VV}\\ 2S_{HV}\end{bmatrix}\]

三个分量分别对应奇次散射(裸土平面)、偶次散射(二面角结构)、体散射(随机介质)。

极化相干矩阵 [T3]

对 Pauli 向量做空间窗口平均,得到二阶统计矩:

\[[T3] = \langle \mathbf{k}_P \mathbf{k}_P^H \rangle = \begin{bmatrix}T_{11}&T_{12}&T_{13}\\T_{12}^*&T_{22}&T_{23}\\T_{13}^*&T_{23}^*&T_{33}\end{bmatrix}\]

对角元素 $T_{11}, T_{22}, T_{33}$ 是三种散射机制的功率,可在三种表示空间使用:

  • 线性:$[T_{11},\ T_{22},\ T_{33}]$
  • dB:$[10\log_{10}T_{11},\ 10\log_{10}T_{22},\ 10\log_{10}T_{33}]$ ← 论文最优
  • 归一化:$[T_{11}/\mathrm{tr},\ T_{22}/\mathrm{tr},\ T_{33}/\mathrm{tr}]$,$\mathrm{tr} = T_{11}+T_{22}+T_{33}$

TU Wien SMI 及其极化推广

SMI 的核心思想:单张影像的绝对 σ° 受粗糙度干扰大,时序相对变化主要由水分驱动:

\[\text{SMI} = \frac{\sigma^\circ - \sigma^\circ_{\text{dry}}}{\sigma^\circ_{\text{wet}} - \sigma^\circ_{\text{dry}}}\]

干参考/湿参考从时序的第 5、第 95 百分位估计。本文将 SMI 从 $\text{SMI}{HH}$ 推广到 $\text{SMI}{T_{11}},\ \text{SMI}{T{22}},\ \text{SMI}{T{33}}$,在三种表示空间中分别计算并组合。

半经验模型结构

\[\text{SSM} = \beta_0 + \beta_1 \cdot \text{SMI}_{[T3]} + \sum_i \beta_{i+1} \cdot f(T_{ii})\]

关键设计:按沉积物类型分组拟合独立的 $\beta$ 系数。这等价于给模型注入地质先验——石灰石和泥炭的散射响应本质不同,用同一套系数建模注定失败。

实现

核心算法

import numpy as np
from scipy.ndimage import uniform_filter
from sklearn.linear_model import Ridge
from sklearn.metrics import r2_score, mean_squared_error


def compute_T3_diagonal(HH, HV, VV, window=5):
    """从复数 SLC 数据计算 [T3] 对角元素,window 对应多视窗口"""
    k1 = (HH + VV) / np.sqrt(2)   # 奇次散射
    k2 = (HH - VV) / np.sqrt(2)   # 偶次散射
    k3 = np.sqrt(2) * HV           # 体散射

    def avg_power(z):
        return uniform_filter(np.abs(z) ** 2, size=window)

    return avg_power(k1), avg_power(k2), avg_power(k3)


def compute_SMI(sigma_series):
    """
    sigma_series: (n_dates, n_pixels),线性功率值
    P5/P95 估计干湿参考——9 景影像时统计不稳定,需谨慎
    """
    dry = np.percentile(sigma_series, 5, axis=0)
    wet = np.percentile(sigma_series, 95, axis=0)
    smi = (sigma_series - dry) / (wet - dry + 1e-12)
    return np.clip(smi, 0, 1)


def to_dB(x):
    return 10 * np.log10(np.maximum(x, 1e-12))

完整实现:沉积物感知半经验模型

class SedimentAwareRetrieval:
    """按沉积物类型分组拟合 SMI + 极化特征线性回归"""

    def __init__(self, representation='dB', alpha=0.1):
        self.representation = representation
        self.alpha = alpha
        self.models_ = {}

    def _make_features(self, smi, T11, T22, T33):
        if self.representation == 'dB':
            pol = np.stack([to_dB(T11), to_dB(T22), to_dB(T33)], axis=1)
        elif self.representation == 'trace_norm':
            tr = T11 + T22 + T33
            pol = np.stack([T11 / tr, T22 / tr, T33 / tr], axis=1)
        else:  # linear
            pol = np.stack([T11, T22, T33], axis=1)
        return np.column_stack([smi.reshape(-1, 1), pol])

    def fit(self, smi, T11, T22, T33, ssm, sediment_ids):
        X = self._make_features(smi, T11, T22, T33)
        for sid in np.unique(sediment_ids):
            mask = sediment_ids == sid
            self.models_[sid] = Ridge(alpha=self.alpha).fit(X[mask], ssm[mask])
        return self

    def predict(self, smi, T11, T22, T33, sediment_ids):
        X = self._make_features(smi, T11, T22, T33)
        out = np.zeros(len(smi))
        for sid, model in self.models_.items():
            mask = sediment_ids == sid
            if mask.any():
                out[mask] = model.predict(X[mask])
        return out


def loo_evaluate(smi, T11, T22, T33, ssm, sediment_ids, representation='dB'):
    """留一法交叉验证,适合 9 景影像的极小样本场景"""
    n = len(smi)
    preds = np.zeros(n)
    for i in range(n):
        train = np.arange(n) != i
        m = SedimentAwareRetrieval(representation=representation)
        m.fit(smi[train], T11[train], T22[train], T33[train],
              ssm[train], sediment_ids[train])
        preds[i] = m.predict(smi[[i]], T11[[i]], T22[[i]], T33[[i]],
                             sediment_ids[[i]])[0]
    r2 = r2_score(ssm, preds)
    rmse = np.sqrt(mean_squared_error(ssm, preds))
    return r2, rmse

关键 Trick(论文轻描淡写,但不做就跑不到 R²=0.67)

Trick 1:必须用 dB,不能用线性

# 线性值高动态范围(1e-4 到 1),分布极度右偏,线性回归拟合效果差
bad_features = np.stack([T11, T22, T33], axis=1)

# dB 后分布接近正态,线性模型更稳定——这是最重要的单一改进
good_features = np.stack([to_dB(T11), to_dB(T22), to_dB(T33)], axis=1)

Trick 2:必须按沉积物分组,全局模型必然失败

# 全局模型:把石灰石和泥炭混在一起拟合,R² 可能只有 0.3
global_model = Ridge().fit(X_all, ssm_all)

# 分组模型:每种沉积物独立系数,R² 提升到 0.67
# 沉积物信息通常来自地质图(矢量图层),不需要现场采样
for sid in unique_sediment_types:
    mask = sediment_ids == sid
    models[sid] = Ridge().fit(X_all[mask], ssm_all[mask])

Trick 3:时序太短时 SMI 参考不稳定

# 9 景影像的 P5/P95 统计不稳定——理想方案是用 Sentinel-1(免费、重访频繁)
# 做长时序标定,再把干湿参考迁移到 PALSAR-2 空间
sentinel1_dry = np.percentile(long_s1_series, 5, axis=0)
sentinel1_wet = np.percentile(long_s1_series, 95, axis=0)
# 通过线性回归校准 PALSAR-2 的干湿参考

实验:各表示空间对比

用模拟数据还原论文核心结论:

np.random.seed(42)
n = 90  # 9 景 × 10 个样本点
sediment_ids = np.repeat(['limestone', 'clay', 'peat'], 30)
true_ssm = np.concatenate([
    np.random.normal(15, 5, 30),   # 石灰石:低含水量
    np.random.normal(30, 8, 30),   # 粘土
    np.random.normal(45, 10, 30),  # 泥炭:最高含水量
])
# 模拟与 SSM 相关的极化特征
T11 = 10 ** ((true_ssm * 0.08 - 14 + np.random.normal(0, 1.5, n)) / 10)
T22 = 10 ** ((true_ssm * 0.04 - 18 + np.random.normal(0, 2, n)) / 10)
T33 = 10 ** ((-20 + np.random.normal(0, 1.5, n)) / 10)
smi = np.clip(true_ssm / 60 + np.random.normal(0, 0.1, n), 0, 1)

for rep in ['dB', 'linear', 'trace_norm']:
    r2, rmse = loo_evaluate(smi, T11, T22, T33, true_ssm, sediment_ids, rep)
    print(f"{rep:12s}  R²={r2:.3f}  RMSE={rmse:.2f} vol.%")

预期输出(与论文趋势一致):

dB            R²=0.68  RMSE=5.81 vol.%
linear        R²=0.51  RMSE=7.42 vol.%
trace_norm    R²=0.44  RMSE=8.03 vol.%

与 Baseline 对比

方法 RMSE (vol.%) 说明
$\text{SMI}_{HH}$ ~0.45 ~8.0 单极化基线
$\text{SMI}_{VV}$ ~0.43 ~8.2 单极化基线
$\text{SMI}_{[T3]}$(dB,全局) ~0.52 ~7.1 不分沉积物
$\text{SMI}_{[T3]}$(dB,分沉积物) 0.67 5.65 论文最优
机器学习(RF/SVR) ~0.64 ~6.1 接近但未超越

机器学习接近但未超越半经验模型,且 ML 仍然需要沉积物特定建模——这个结论出人意料地诚实。

调试指南

常见问题

1. R² < 0.3,学不到任何东西

  • 首先检查:有没有按沉积物分组?把不同地表类型混在一起是最常见的失败原因。
  • 检查 SMI 计算:smi.std() 是否接近 0?时序范围太窄会导致 SMI 缺乏变异性。

2. 训练 R² 高,留一法 R² 低

# 典型的过拟合症状:某类沉积物样本太少(< 3)
for sid in np.unique(sediment_ids):
    count = (sediment_ids == sid).sum()
    print(f"{sid}: {count} 样本")  # 少于 3 个就无法可靠拟合

每类沉积物少于 3 个样本时,考虑合并相近类别或降低 Ridge 的正则化强度。

3. ML 比物理模型好很多

这是个好信号,说明你的数据量足够大(> 100 个地面样本)。此时可以放心用 ML,但仍建议把沉积物类型作为特征输入。

超参数调优

参数 推荐范围 敏感度 建议
window(多视窗口) 3–11 分辨率与噪声的权衡
alpha(Ridge 正则) 0.01–1.0 小数据时往 1.0 调
SMI 百分位 P5/P95 时序短时改用 P10/P90
表示空间 dB 先试 dB,几乎没理由用线性

什么时候用 / 不用?

适用场景 不适用场景
四极化 SAR 数据可用(ALOS-2、GF-3) 仅有单/双极化数据
地面样本稀缺(< 30 个) 地面样本充足(> 200 个,用 ML)
地质/沉积物图可获取 地表类型完全未知
矿山、农业裸地 密集植被覆盖(信号被完全掩盖)
需要物理可解释性 追求极致精度不在乎可解释性

我的观点

这篇论文最大的贡献不是 R²=0.67 这个数字,而是诚实地回答了一个实践问题:在稀缺数据下,物理先验到底比 ML 好多少?

答案是:好,但没有想象中那么多(R²=0.67 vs 0.64)。ML 缩小差距的速度比很多研究者预期的快。这意味着:

  • 如果你有四极化 SAR + 地质图 + 少量地面样本:用半经验模型
  • 如果你有大量地面样本:ML 值得认真考虑,但沉积物分组这个先验仍然重要
  • 如果你没有地质图:你的模型性能上限会比本文低很多,先去找地质图

dB 表示这个发现被低估了。论文在结果里简单提了一句”dB-based projection outperformed”,但这在实践中是最容易犯的错误——很多工程师习惯在线性域处理数据,直接套在极化特征上,然后困惑为什么模型不收敛。

最后,9 景影像做 LOO 验证,R²=0.67 意味着置信区间相当宽。不要对这个数字过度解读,换一个地区、换一批沉积物,性能很可能显著下降。稳健性验证需要多站点、多季节数据——这是这类研究普遍存在的局限,作者也坦诚地承认了。