资讯详情

天文数据挖掘实战复盘:从特征工程到模型融合的三分类方案

📅 2026/10/19 8:26:27 | 华诺云谱 👁 阅读
天文数据挖掘实战复盘:从特征工程到模型融合的三分类方案
最近我把某竞赛平台的天文数据挖掘比赛完整走了一遍。这个赛题的核心是给定一批天体观测数据让模型区分三个类别恒星、星系、类星体。听起来挺浪漫真做起来却没那么轻松样本量大、噪声明显、还带着明显的类别不平衡。这类赛题在真实天文数据处理里也很有代表性不只是刷榜用的玩具它很考验你对脏数据的容忍度、对物理特征的理解以及对模型验证细节的把控。如果你正在准备入门数据挖掘比赛或者想找一条“从拿到表格数据到提交结果”的完整路线这篇复盘应该能当一份实用参考。我会沿着实际操作顺序写赛题理解、数据清洗、特征工程、模型验证、融合提交、问题排查尽量把每个关键步骤背后的理由也讲清楚。这种天文数据挖掘实验之所以值得写不只是因为分数高低而是它几乎覆盖了表格类竞赛的全部经典环节类别不均衡、缺失值、异常值、高相关性特征、线下线上分数偏移、模型融合。任何一个环节处理不好排名都会差出一截。下面我开始复盘所有经验都来自实际跑通流程后的总结你可以直接拿去对照自己的项目。1. 项目概述与整体思路1.1 赛题本质不只是“三分类”那么简单很多新手看到这个任务第一反应是“不就是分类嘛三个类别随便来个模型都能做”。但实际评估用的是 macro-F1而不是准确率这就让题目难度上升了一个档次。macro-F1 的意思是对每一个类别单独计算 F1 分数再取平均。这样一来样本量少的类星体和样本量大的恒星在最终得分里拥有同等的说话权利。打个比方如果班级里有 50 个学生其中 45 个擅长数学、5 个严重偏科老师评“综合表现”时把数学成绩和各科成绩平均拉在一起那偏科的那 5 个学生就会拖后腿。模型也一样哪怕恒星类识别得再准只要类星体的精确率或召回率偏低macro-F1 就上不去。我在实验前先统计了训练集类别比例大概是一半恒星、三分之一星系、剩下的才是类星体类星体占比不到两成。这种分布直接决定了后续一切处理策略不能盲目追求准确率不能随便做随机划分也不能用默认阈值一刀切。理解评价指标永远是做竞赛的第一步。1.2 先建立 baseline再谈精细优化我见过不少人拿到赛题就直奔特征工程甚至一上来就上深度神经网络结果花了很长时间线上分数反而不如一个简单的 LightGBM。正确的做法是先快速建一个可以提交的 baseline不管分数多难看先跑通全流程。我第一次用原始特征训练 LightGBM五折交叉验证的 macro-F1 大约在 0.78 左右。这个分数很低但它的价值不在分数本身而在于给了我一个参照系。后面每做一步特征工程或调参我都会拿它和这个 0.78 对比知道每一步到底带来多少收益。没有 baseline你根本不知道自己在瞎忙什么。另一个原因是baseline 能提前暴露数据问题。我记得第一次跑的时候就发现目标列里出现了两种形式相同的字符串只是大小写不一致如果不做归一会直接报错。这种问题如果等特征工程做完才发现排查成本会高很多。1.3 把时间预算花在刀刃上竞赛周期长但能集中精力做的时间其实有限我给自己做了一个粗粒度的时间分配表原则很朴素先保通路再保提分最后保稳定性。阶段核心动作期望产出数据探索字段理解、分布检查、缺失统计明确可用特征、排除身份列特征工程构造颜色、交互、缺失标志让模型看到物理含义更强的特征建模与调参树模型为主交叉验证调参稳定可靠的线下分数模型融合概率加权、Stacking比单模型再提 0.01~0.02提交复盘格式校验、结果归档可复现的最终方案这个表不是死的后面做的时候发现特征工程收益很高我就适当削减了调参时间。竞赛不是考试没有固定题量灵活调整节奏本身就是能力的一部分。2. 数据探索与清洗实操2.1 先搞懂每一列是什么再动手写代码天文赛题给的数据表一般不小每行代表一个天体样本列往往包含编号、不同波段星等、颜色指数、红移值、红移误差以及目标类别。第一次打开数据集时我先做三件事打印 shape 和数据类型检查目标类别分布统计每列缺失率。这三步几乎不耗时但能避免后面的所有返工。举个例子有些列表面上看是数值实际上却是占位符比如缺失值用 -999 表示。如果不处理模型会把 -999 当成一个有规律的特殊数值来学习线下可能出现莫名其妙的规律线上却不复现。因此我养成一个习惯任何特征在进模型之前先做一次 value_counts 或者 min/max 检查看看有没有类似 -999、9999 这种异常数值。目标类别部分我直接把字符串映射成 0、1、2 三个整数。映射关系单独保存成字典避免提交时把标签顺序搞错。这一步虽小但提交阶段返工都是从这里埋下的雷。2.2 编号和坐标这类“身份列”必须隔离天文数据里一定有类似 id、ra、dec 这样的坐标字段。id 是每条样本的编号训练它没有意义反而会让模型学习到“某类样本集中在某个 id 区间”的假规律。RA 和 Dec 表示天体在天空中的位置看起来很像有效特征实际上却很危险。为什么呢因为巡天数据的观测覆盖并不均匀某些天区可能观测得更多、更深另一些天区则相对稀疏。如果直接把坐标放进模型它很可能学到的不是天体物理规律而是“这个天区里的目标更容易被判成星系”这种伪相关。线下验证时可能因为数据划分问题显得很准一旦遇到分布不同的线上测试集分数就会崩塌。我在实验里把所有坐标类字段单独隔离不参与建模只在做相关性分析时用来观察样本分布。这是一个保守但安全的决定宁可少用一个可能有用的特征也不要让模型学到一个会炸的假规律。2.3 缺失值处理要区分“没采到”和“物理上不存在”天文数据的缺失非常常见比如红移字段缺失可能意味着目标太暗、光谱质量不够而不是目标本身没有红移。树模型本来能直接处理缺失值但为了让实验可控我通常会先统一填充并额外增加一个“是否缺失”的布尔特征。因为“缺失与否”本身可能携带观测质量的信息把它单独作为一个特征能让模型自己去决定这个信号有没有用。填充策略上我没有用 0 填充。连续特征我用中位数填充离散特征用众数填充。中位数相比均值更抗异常值这对天文数据尤其重要因为少量极亮或极暗的目标会把均值拉偏。对于缺失率超过 50% 的特征我不会急着删除而是先观察它的分布。有些特征虽然缺失多但缺失标志本身就有区分度直接扔掉反而可惜。还有一点必须强调所有填充值都只能从训练集里计算然后应用到训练集和测试集。如果把全量数据一起算统计量再做填充本质上就是小小的数据泄露会让线下分数虚高。2.4 异常值优先裁剪而不是直接删除天文数据里噪声点很多极端星等、极端红移都可能出现我用分位数裁剪代替删除。具体做法是对每个连续特征取 1% 和 99% 分位小于下界的压到下界大于上界的压到上界。这样既保留了样本数量和类别分布又限制了异常点对模型的影响。裁剪阈值需要用字典保存下来训练集和测试集使用同一套阈值这一点非常关键。如果测试集单独计算分位数就会导致特征分布不一致。我早期吃过这个亏训练时用训练集分位数提交前不小心在测试集上重新算了阈值结果线下分数没变化线上分数却掉了好几个点后来排查了很久才发现是分布不一致导致的。3. 特征工程让模型看到物理差异3.1 把原始星等转换成颜色和物理量单看某个波段的星等模型很难区分天体类型但不同波段的“星等差”却能反映出天体内部的物理差异。天文上不同波段对应不同的颜色的光类星体和恒星在颜色空间里的位置有明显差异所以我把 u、g、r、i、z 这几个波段做两两相减得到 u-g、g-r、r-i、i-z 这样的颜色指数。这一步实现上就是普通列运算但效果特别明显。除了颜色差我还加入了一些交互特征比如红移乘以颜色差。因为高红移天体的颜色会受到宇宙学红移的影响原本的观测颜色会向红色端移动单纯的颜色差可能不够加上红移交互后模型更容易捕捉这种变化。另外我构造了总星等和平均星等相当于给模型一个“这个天体整体亮度”的全局视角。做特征工程时要控制总特征数量不能无限制堆。我最终保留的特征在 20 到 30 个之间删掉了一些重要性极低的原始列。对天文赛题来说物理含义清晰的小特征集远好过堆了几百列却解释不了的“特征垃圾场”。3.2 连续值的分布需要仔细观察星等在物理上是通量的对数尺度这意味着星等的分布本身就带有压缩效果。如果某些特征动态范围特别大我可以对它们做指数变换恢复到通量尺度再和别的特征做比。但在这次实验里颜色指数已经相当于两个通量比的 log所以直接用就好不需要过度变换。我测试过对特征做 PCA线性和树模型效果差别很大。PCA 对线性模型有帮助但对树模型几乎没提升因为树模型本身就能处理特征间的非线性关系强行降维反而会丢失物理含义。如果你后续想用神经网络或者逻辑回归再做标准化和 PCA如果主力是 GBDT 系模型保留原始单位和物理构造特征更重要。当然如果某些特征跨度特别大比如红移从接近 0 到接近 5我建议做一次 log1p 变换压缩动态范围。这不会让树模型效果变差但对融合时的线性模型会友好很多。我实际试下来log1p 红移特征让最后的融合分数略有提升。3.3 用特征重要性反馈来精简特征集特征不是越多越好天文表格数据往往特征相关性很高加多了只会增加过拟合风险。我跑完一版模型后会输出 LightGBM 的 feature_importance重点看 gain 而不是 split 次数。原因很简单某个特征可能被频繁使用但每次都只是浅浅地分割它对最终结果的贡献未必高gain 更能反映特征带来的信息量。对于重要性偏低的特征我不会直接全删而是先看它和已有特征的相关性。如果相关性很高就删掉如果相关性低但重要性也低则可能是模型没充分利用可以先保留一段时间。特征工程应该是迭代的不是一锤子买卖。4. 模型构建、验证与调参4.1 分层五折交叉验证是线下评估的基本盘评估模型时我没有用简单的训练测试两段式而是采用 StratifiedKFold 分层五折交叉验证。之所以强调“分层”是因为目标类别不平衡如果随机划分某一折里可能类星体样本特别少分数波动会很大得到的结论也不可靠。分层五折的思路是确保每一折训练集和验证集的类别比例都与全局相近。这样算出的平均分方差小、参考价值高。我在代码里固定了随机种子random_state42保证每次跑出来的结果可复现。后续每次调参都基于同一套折方便横向对比。代码示意from sklearn.model_selection import StratifiedKFold from sklearn.metrics import f1_score skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for fold, (tr_idx, va_idx) in enumerate(skf.split(X, y)): X_tr, X_va X.iloc[tr_idx], X.iloc[va_idx] y_tr, y_va y.iloc[tr_idx], y.iloc[va_idx] # 训练模型计算 macro-F1这种验证方式有一个附带好处可以快速观察五折分数是否都在一个合理区间。如果某一折明显偏低说明数据里可能存在按天区或观测批次分布的不均匀性这时候我会考虑改用 GroupKFold。4.2 树模型为什么是主力而不是深度学习对于这种中小规模的表格型天文数据我首选 LightGBM、XGBoost 和 CatBoost 的组合。LightGBM 训练快、内存占用低XGBoost 稳定性好CatBoost 对类别特征有天然支持。我用 LightGBM 作为主力模型其他两个做差异化补充最后再融合。LightGBM 初始参数很关键不能全部用默认值。我通常先设置一个偏保守的配置再通过早停确定树数量import lightgbm as lgb params { objective: multiclass, num_class: 3, metric: multi_logloss, learning_rate: 0.05, num_leaves: 63, max_depth: 7, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, lambda_l1: 0.1, lambda_l2: 1.0, verbose: -1, }多分类任务里num_class必须与目标类别数一致否则训练直接报错。metric中的 logloss 只是训练参考最终排名看的还是 macro-F1所以我还会在回调里加自定义评估函数实时观察 F1 变化。早停轮数我设置为 100一般跑到 500 轮左右就稳定了。如果提前停止轮数太小模型可能没收敛太大则浪费时间。100 是一个工程上比较稳的折中值。4.3 调参顺序先结构参数后正则参数调参最忌讳上来就网格搜索几十组参数那是浪费算力。我的顺序是先确定树数量并调整learning_rate再调num_leaves、min_data_in_leaf这类结构参数最后调lambda_l1、lambda_l2做正则化。参数范围调整目标容易出现的问题learning_rate 0.01~0.1控制收敛速度太大容易过拟合太小训练慢num_leaves 31~127控制模型复杂度太大过拟合太小欠拟合min_data_in_leaf 20~100防止单叶样本过少太小噪声大feature_fraction 0.6~0.9控制特征采样太小欠拟合lambda_l1 / lambda_l2正则化通常不会剧烈影响分数实际上我发现最大收益来自特征工程调参只能带来小幅但稳定的提升。所以不要把调参当做救命稻草它更像最后的打磨。5. 模型融合与提交阶段的关键细节5.1 概率平均融合简单但有效单模型跑到一定阶段后我用 LightGBM、XGBoost、CatBoost 做概率融合。方法是对三个模型预测出的各类别概率做加权平均然后取概率最大的类别作为最终结果。权重不是随意定的。我在验证集上做了简单的网格搜索遍历不同权重组合找 macro-F1 最高的一组。实际结果大约在 LightGBM 0.5、XGBoost 0.3、CatBoost 0.2 时效果最好。代码示意import numpy as np best_weight None best_score 0 for w1 in np.arange(0.4, 0.9, 0.1): for w2 in np.arange(0.1, 0.4, 0.1): w3 1 - w1 - w2 if w3 0: continue pred w1 * p_lgb w2 * p_xgb w3 * p_cat score f1_score(y_valid, pred.argmax(axis1), averagemacro) if score best_score: best_score score best_weight (w1, w2, w3)融合能带来的提升通常是 0.01 到 0.02 个 macro-F1 点听起来不多但在竞赛里足以让名次前进不少。不过有一个前提三个模型之间要有差异性。如果都是同一个模型换几个参数融合基本没有意义。5.2 可选 Stacking 与阈值调整除概率平均外Stacking 也值得尝试。做法是把第一层模型的预测概率作为第二层模型的输入特征第二层用逻辑回归或简单的神经网络。但要注意第二层不要再用原始特征否则很容易过拟合。Stacking 在样本量充足、模型差异大的时候收益更明显。比赛如果以 F1 作为评价指标概率校准不是最优先的事阈值调整更直接。某些类别概率阈值默认是 0.5但可以针对每类单独调整。比如类星体类如果阈值降到 0.3召回率可能上升精确率可能下降需要看组合结果。不过验证集太小时强行调阈值容易过拟合要克制。5.3 提交格式与版本管理的坑提交阶段最容易出低级错误。平台要求 CSV 包含 id 和 label 两列label 是预测类别我提交前一定会检查三件事行数是否等于测试集行数label 是否包含全部三个类别id 列是否与测试集一致。我习惯先把提交模板做好每次只填充预测结果定义一个简单的断言脚本检查行数和类别。不要小看这一步线下跑出高分但提交失败的人每次比赛都有。模型文件也要做好版本管理我建议用“日期参数备注”的方式命名记录每版模型的线下分数和线上分数否则过几天你就会忘记之前做过什么改动。6. 常见问题与排查技巧实录6.1 线上分数和线下分数差很多怎么排查先看特征重要性如果某个特征重要性高得不正常优先怀疑是否用了测试集才有的信息。再看数据预处理所有统计量比如均值、中位数、分位数裁剪阈值是不是都只在训练集上计算过。如果这两点都没问题就要想测试集分布是否和训练集差异很大。天文数据中训练集和测试集可能来自不同观测区域这会直接导致分布偏移。解决办法是用坐标字段构造分组用 GroupKFold 做验证模拟这种区域差异。这样做出来的线下分数会更接近线上真实情况。6.2 类别不平衡该不该过采样我建议先不要急着做 SMOTE。对树模型而言简单过采样经常适得其反模型会对少数类样本过度学习线上遇到新数据反而泛化不好。我更喜欢用类别权重或直接调整阈值。在这个实验里类星体占比不足两成但 LightGBM 的表现并没有明显偏向多数类所以我没有用过采样。如果某个类别占比极低比如只有 5%可以优先尝试提高该类的损失权重再决定要不要做生成式过采样。不要一开始就背大锅。6.3 模型训练太慢、内存爆炸怎么办样本量大的时候LightGBM 的内存和训练速度可以通过几个参数快速优化max_bin从默认 255 降到 63显著减少内存feature_fraction和bagging_fraction适度调小训练更快。还有一个经验先用全部数据的 10% 跑通完整流程确认无误后再全量训练能节省大量排队时间。读取超大 CSV 时用pandas.read_csv的usecols参数只加载需要的列不要一次把全表塞进内存。遇到实在没法全量训练的极限情况可以考虑分批训练再做模型平均但这是最后手段通常优化后已经够用。6.4 预测概率全是 0.33模型等于没学这个问题我在调试早期遇到过。概率均匀分布通常意味着模型没有从特征里学到判别信息常见原因有三个标签没有正确编码、特征全是常数、数据预处理把不同类别的样本错误地混在一起。排查方法很简单按类别统计每个特征的均值和标准差如果差异几乎为零说明特征本身没有区分度或者列读取错了。再检查目标编码过程确保 shuffle 时没有把标签和特征张冠李戴。我自己复盘下来收益最大的动作依次是仔细理解每个字段的物理意义、构造颜色和红移交互特征、完成多模型融合。这三步加起来把 macro-F1 从 baseline 的 0.78 拉到了 0.87 左右。最后说一个处理天文数据时最有感触的点不要相信第一次读到的数据表一定要先问“每一行到底是什么”“每一列代表什么物理量”。很多看起来像玄学的问题最后都能追溯到最开始没有认真理解字段含义上。把这个基本功做扎实后面的建模才谈得上效率。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑