返回列表

37th place solution

626. NeurIPS - Ariel Data Challenge 2024 | ariel-data-challenge-2024

开始: 2024-08-01 结束: 2024-10-31 物理与天文 数据算法赛
第 37 名解决方案

第 37 名解决方案

作者: Shamil Yagiyayev (@thegrey)
队友: @imayushsaxena, @octaviograu
发布时间: 2024-11-02

首先,我要感谢主办方的准备工作,感谢我的队友 @imayushsaxena@octaviograu,以及所有让这次比赛充满挑战的 competitors!

我们的顶级解决方案

该解决方案取得了以下成绩:

方案变体 公开榜 私有榜 Leaderboard 排名 最终排名
第 10 名 0.6152848 0.6198108 53 -
第 11 名 0.6128403 0.6293136 - 37

第 11 名方案总结

该解决方案仅使用了 AIRS 数据。

解决方案的基本流程:

  1. 标准校正,但不进行分箱/缩小通量 (binning / shrinking the flux)
  2. 标准化信号
  3. 使用 gaussian_filter1d 对信号去噪
  4. 检测凌日阶段,包括临边昏暗 (limb darkening)
  5. 计算整个信号的特征
  6. 通过分组最近的 6、10 和 25 个波长,计算分箱波长的特征
  7. 基于特征训练了 1000 个模型
  8. 模型预测的平均值给出凌日深度预测
  9. Sigma 基于每个波长模型预测的标准差

第 11 名解决方案链接

核心 Pipeline

我们尝试了许多去噪策略,因此需要一种简单的方法来进行迭代。我们构建了一个易于配置的功能性 pipeline:

from functools import reduce

full_pipeline = [
    lambda  **kwargs: bin_wavelengths(**kwargs),
    lambda  **kwargs: normalize_signal(**kwargs),
    lambda **kwargs: filter_signal_with_gaussian(sigma=25, **kwargs),
    lambda **kwargs: polynomial_detrend(**kwargs),
    lambda **kwargs: fit_transit_poly(**kwargs),
    lambda **kwargs: collect_features(**kwargs),
]

initial_kwargs = {'signal': None, 'margin': 1, 'from_wavelength':39, 'to_wavelength':321}
result = reduce(lambda kw, func: func(**kw), full_pipeline, initial_kwargs)

然后我们可以结合不同 pipeline 的 result['features']

Pipeline 中的每个步骤可以这样组织:

def filter_signal_with_median(window=5, **kwargs):
    signal = kwargs['signal']
    filtered_signal = median_filter(signal, size=window)
    kwargs['signal'] = filtered_signal
    return kwargs

凌日阶段检测 (Transit phase detection)

在实验了 BATMAN 库之后,我们发现即使完全去噪和去趋势后,凌日通量信号仍有几种可能的结构。临边昏暗会影响凌日深度的计算,进而影响我们计算凌日边缘的方式。

def get_transit_phases2(**kwargs):
    signal = kwargs['signal'] + 1

    grad1 = np.gradient(signal)
    mn = np.argmin(grad1)
    mx = np.argmax(grad1)
    
    peaks = find_peaks(grad1, width=25)[0]
    ingress_left = peaks[(peaks < mn) & (mn - peaks <= 250)][-1] if np.any((peaks < mn) & (mn - peaks <= 250)) else mn-110
    ingress_right = peaks[(peaks > mn) & (peaks - mn <= 250)][0] if np.any((peaks > mn) & (peaks - mn <= 250)) else mn+110
    
    dips = find_peaks(-grad1, width=25)[0]
    egress_left = dips[(dips < mx) & (mx - dips <= 250)][-1] if np.any((dips < mx) & (mx - dips <= 250)) else mx-110
    egress_right = dips[(dips > mx) & (dips - mx <= 250)][0] if np.any((dips > mx) & (dips - mx <= 250)) else mx+110

    kwargs['ingress_left'] = ingress_left
    kwargs['ingress_right'] = ingress_right
    kwargs['egress_left'] = egress_left
    kwargs['egress_right'] = egress_right

    return kwargs

特征计算

知道凌日的 ingressegress 后,我们将多项式拟合到通量信号中(忽略凌日),然后执行去趋势。

然后我们可以假设,一旦 plain 去趋势信号处于常数 1 的水平,我们就可以通过以下公式推导凌日深度:

def transit_formula(s):
    return 1/s - 1

这里 s 可以是凌日中的平均通量、中点通量等。我们在这里收集了不少特征。与 minimize 方法相比,这是一个巨大的速度改进。

此外,我们还收集了凌日通量的通用特征,如标准差。

模型训练

我们生成了 500 个 Ridge 模型和 500 个 PLSRegression 模型,形成了集成模型。

Ridge 的 Alpha 是随机的:

alpha = 10 ** (2 - 3 * np.random.rand())

PLSRegression 的 n_components 也是如此:

model = PLSRegression(n_components=n_components, tol=1e-8)

我们为每个模型使用了不同的训练数据子集:

subset_size = int(X_train.shape[0] * np.random.uniform(0.5, 1))

我们对每个波长的模型预测进行了排序,去除了最低和最高的 30%,并取剩余预测的平均值。

Sigma 计算

我们计算了每个波长排序预测的标准差:

std_preds = np.std(sorted_preds, axis=0)

然后将标准差乘以在 LB 上效果最好的系数:

COEFF_0 = 4 # 这个效果最好
sigma_response = sigma * COEFF_0

效果良好但未进入最终提交的内容

  1. 使用小波 (pywt) - 我们最喜欢的是 db29
  2. median_filter
  3. savgol_filter

无效的方法

  1. 高斯过程回归 (Gaussian Process Regression) - 我们在这里尝试了不同的核,但没有看到突破,同时处理时间大幅增加
  2. LinearRegresion, SVM, LGBM, CatBoost, RandomForest, XGBoost...
  3. BayesianRidge - 效果不错,但时间长(并且对于更多特征无法适应内存)
  4. 我们尝试训练一个具有 sigma 和凌日深度(比赛得分)损失函数的神经网络,但由于时间有限,运气不佳

我们来不及完成的改进

  1. 使用 BATMAN 生成训练数据
  2. 使用 FGS 数据
  3. 使用真实分子的数据生成额外的训练数据
  4. 收集更有意义的特征(并过滤掉那些没有帮助的特征)
  5. Sigma 方法 - 我们没有在这里利用所有的潜力
同比赛其他方案