返回列表

25th Place - Polynomnial Fitting / Nelder-Mead

670. NeurIPS - Ariel Data Challenge 2025 | ariel-data-challenge-2025

开始: 2025-06-26 结束: 2025-09-24 物理与天文 数据算法赛
第 25 名 - 多项式拟合 / Nelder-Mead 算法

第 25 名 - 多项式拟合 / Nelder-Mead 算法

作者:Adrian Wiśniewski

发布时间:2025-09-27

竞赛排名:第 25 名

竞赛:ARIEL Data Challenge 2025

1. 数据预处理

数据校准是按照组织者建议的解决方案完成的。唯一的修改是使用传感器数据像素的子集。因此,数组切片 [:, 10:22, 39:321], [:, 10:22, :] 分别用于 AIRS-CH0 和 FGS1 传感器。

2. 检测凌星进入/离开 (Ingress / Egress)

凌星进入和离开是使用基于梯度方法的算法计算的。在进行多项式拟合之前,排除了从 phase_a_startphase_a_end 以及从 phase_b_startphase_b_end 的窗口。如果 phase_a 检测无效,我仅使用 egress 部分来估计深度,反之亦然。如果 phase_aphase_b 检测均无效,则不进行任何预测。

def detect_breakpoints(flux, verbose=False):
    """
    通过计算去趋势信号的梯度来检测 airs 通量信号中的相位。
    """

    # 均值和滤波信号
    flux = flux.mean(axis=-1)

    # 查找凌星中间点
    min_index = np.argmin(flux)
    in_a = flux[:min_index]
    in_b = flux[min_index:]

    # 计算两半部分的梯度
    gradient_a = np.gradient(in_a, edge_order=1)

    gradient_2a = np.gradient(gradient_a, edge_order=1)
    gradient_2b = np.gradient(gradient_b, edge_order=1)

    # 基于梯度识别相位索引
    phase_a = gradient_a[1:].argmin() + 1
    phase_b = gradient_b[:-1].argmax() + len(gradient_a)

    phase_a_start = gradient_2a.argmin()
    phase_a_end = gradient_2a.argmax()

    phase_b_start = gradient_2b.argmax() + len(gradient_a)
    phase_b_end = gradient_2b.argmin() + len(gradient_a)

    breakpoints = {'center': min_index,
                   'phase_a_start': phase_a_start - 1,
                   'phase_a': phase_a,
                   'phase_a_end': phase_a_end + 1,
                   'phase_b_start': phase_b_start - 1,
                   'phase_b': phase_b,
                   'phase_b_end': phase_b_end + 1}

    return breakpoints
梯度检测示意图

3. 凌星深度估计

凌星深度是使用多项式拟合和 Nelder-Mead 优化确定的。

def F(depth, phase_a_data, phase_x_data, phase_b_data, polyorder):
    y = np.concatenate((
        phase_a_data,
        phase_x_data * depth,
        phase_b_data
    ))

    X = np.arange(len(y))

    z = np.polyfit(X, y, polyorder)
    p = np.poly1d(z)
    score = np.abs(p(X) - y).mean()
    return score

3.1 动态光谱估计

凌星深度是使用多项式拟合(最高 3 阶多项式)确定的。

动态光谱估计示例

3.2 平坦光谱

我开始该方法时仅预测平坦输出,因为数据中的大多数光谱具有相对较小的振荡。

最初使用多项式拟合(最高 18 阶多项式)仅在平均波长上确定一次凌星深度。使用高达 18 阶的多项式拟合可能看起来违反直觉;然而,与拟合高达 3/4 阶相比,它产生了更好的结果。

多项式拟合示例 1 多项式拟合示例 2 多项式拟合示例 3

3.3 凌星深度波动

为了准确预测最终光谱,确定了深度测量中的动态量。测量是在未平滑的时间序列上计算的。

过零率 (Zero Crossings Rate - ZCR) 是一个用于测量信号穿过零电平(即改变其符号)次数的特征。

def compute_zcr(signal, burn_in=20):
    return librosa.zero_crossings(signal - signal.mean())[:-burn_in].sum()
ZCR 示例 1 ZCR 示例 2

4. 最终解决方案 - 结合平坦光谱/动态光谱及超参数 - 过零率/轨道周期/凌星宽度/凌星正确性

简而言之:

  • 如果计算的凌星深度波动最小(高 ZCR),则使用低 sigma 的平坦光谱作为解决方案,因为预测错误的可能性很小。
  • 由于存在显著的预测错误可能性,如果计算的凌星深度表现出显著摆动(低 ZCR),则使用具有较大 sigma 的非平坦(动态)光谱作为解决方案。
  • 预测的凌星深度建模为缩放信号的三次多项式函数,使用系数 a0, a1, a2 和 a3。
    signal = a0 + a1 * signal + a2 * signal ** 2 + a3 * signal ** 3

Nelder-Mead 算法和以下代码用于确定 sigma/信号系数的值以及要使用的非平坦深度的量。

def operator(signal, zcr, additional_features, dynamic_depths, args):
    """
    简化版本:
    - 结合基础参数与 ZCR 及特征以修改 sigma。
    - 根据特征调整信号。
    - 如果在 'airs' 模式下,基于 zcr 添加残差深度以进行平坦化。
    - 最后,使用多项式系数缩放信号。
    """

    # 解包参数
    a0, a1, a2, a3, cross_sigma, sigma, residual_coef, post_sigma = args[:8]
    feature_coeffs = args[8:]  # 额外特征的系数

    # 使用基础值和 ZCR 影响初始化 sigma
    sigma = sigma + cross_sigma * zcr

    # 将额外特征的影响纳入 sigma 和信号
    for i, (feature_name, feature_value) in enumerate(additional_features.items()):
        sigma += feature_coeffs[i] * feature_value
        signal += feature_coeffs[i] * feature_value

    # 如果模式为 'airs' 则进行额外修正
    if ScalerSigma.mode == 'airs':
        # 计算残差(与均值的差异)并将其平坦化
        residual = dynamic_depths - np.mean(dynamic_depths, axis=-1, keepdims=True)
        residual = savgol_filter(residual, window_length=10, polyorder=1)

        # 将通过 zcr 缩放的残差添加到信号
        signal += zcr[:, None] * residual

    # 信号的最终缩放
    scaled_signal = a0 + a1 * signal + a2 * signal ** 2 + a3 * signal ** 3

    return scaled_signal, sigma

4.1 预测示例

预测示例 1 预测示例 2
同比赛其他方案