1. 先把话说透:GBDT和LLM在表格数据上的真实处境聊到表格数据建模,业内一直有个心照不宣的结论:凡是结构化数据,GBDT系模型(LightGBM、XGBoost、CatBoost)依然是性价比最高、最稳的默认选择。我做了几年机器学习项目,从风控评分卡到营销响应预测,绕过一圈之后,最终回到底层还是梯度提升树。但这两年LLM绕不开,恰好Titanic又是一个所有人入门时都跑过的数据集,拿它来验证LLM和GBDT在表格任务上的表现差异、顺便把Scaling Law落个地,这个选题我觉得挺有意思。先说清楚这篇内容解决什么问题。如果你手头有一个结构化数据集,正在纠结“要不要上大模型”,或者单纯想系统搞懂GBDT的调参逻辑,顺便看看LLM在处理表格数据时到底有几斤几两——这篇基本能覆盖。我们会用Titanic这个体量极小但五脏俱全的数据集,跑完特征工程、GBDT训练调优、LLM推理对比三件事,并把“模型性能随数据量/容量变化的规律”这个Scaling Law核心思想用实验串起来。不夸张地说,Titanic这个数据集虽然老,但它包含了缺失值、类别特征、数值特征、文本型特征(姓名、舱位)几乎所有表格数据常见难点,特别适合做这种横向对比。而且它足够小,意味着你可以反复跑实验、试参数、验证各种猜想,成本几乎为零。这一篇的核心不是教你调出一个99%准确率的模型——说实话在Titanic上那没意义——而是让你把GBDT和LLM这两条技术路线在同一个数据集上“同时跑通”,并理解背后的取舍逻辑。2. Titanic数据集处理的四个关键细节,大多数人第一步就错了2.1 先摸清字段的血型再动手Titanic数据集一共12个字段,但真正能进模型的通常只有8到9个。PassengerId和Name不会直接作为特征,但要特别注意,Name里的Title(称谓)是信息量极高的特征,我后面单独讲。Survived是标签列。Pclass(舱位等级)虽然是整数,但它的取值只有1、2、3,本质是序数类别特征,不要直接当连续值扔进去。我处理这类表格数据的习惯是,先做一轮EDA,把每个字段的类型、缺失率、取值分布列成一张表,然后才决定特征工程的方向。这一步看起来很基础,但很多人跳过之后,后面各种隐蔽问题都会冒出来。比如Age有177个缺失值,Cabin有687个缺失值,如果不去想清楚这两列的处理策略,后面的模型效果一定受影响。关键决策点有两个:Age缺失值的填充方式。均值填充虽然简单,但会降低特征方差,影响树模型的分裂质量。我试过用性别+Pclass分组中位数填充,效果比全局均值填充要好。因为不同舱位、不同性别的乘客年龄分布差异很大,头部舱位的中年男性乘客和底层舱位的年轻男性乘客,年龄中位数能差出十岁。用组内统计量去填,信息保留度明显更高。Cabin缺失值占比太高,直接作为类别特征会引入大量噪声。常见做法是把它变成“有无Cabin记录”的二值特征,或者提取甲板层级的首字母。我实测下来,在Titanic上“有无Cabin记录”这个二值特征比提取首字母更好用,因为缺失本身就和获救概率有相关性——有舱房记录的乘客,大概率是优先级更高的舱位。2.2 从Name里提取Title,是Titanic特征工程里性价比最高的一步这是个老技巧,但每次都要强调。Name字段本身不能直接进模型,但从中提取的Title能反映社会身份、年龄结构、甚至舱位等级。比如“Mr.”“Mrs.”的中位数年龄差异很大,“Master.”几乎可以确定是未成年男性,而“Dr.”“Rev.”“Lady”这类稀有称谓的人群,获救比例显著偏高。具体做法就是正则提取逗号后的称谓部分:import re def extract_title(name): title_search = re.search(r' ([A-Za-z]+)\.', name) if title_search: return title_search.group(1) return 'Unknown' train['Title'] = train['Name'].apply(extract_title)提取出来后,低频称谓要合并成一个大类,否则树模型会把它们当成噪声。常见的做法是把出现次数少于5的Title统一归为“Rare”。这一步做完,特征维度不会增加太多,但对模型准确率的提升非常直接,我实测能带来2%到3%的AUC提升。2.3 家庭维度特征构造:FamilySize和IsAloneSibSp(兄弟姐妹/配偶数量)和Parch(父母/子女数量)两个字段单独看区分度一般,但组合成FamilySize之后,信息量会明显增强。家庭规模为1的旅客(独行者)获救概率偏低,家庭规模2到4的旅客获救概率偏高,而家庭规模超过4的旅客获救概率又开始下降。这个V型关系在树模型里可以自动学到,但如果你提前构造出来,模型训练会更高效。train['FamilySize'] = train['SibSp'] + train['Parch'] + 1 train['IsAlone'] = 1 train.loc[train['FamilySize'] > 1, 'IsAlone'] = 0还有一个容易被忽略的字段是Ticket。同一个Ticket编号下的乘客属于同一组,组内生存情况高度相关。可以用Ticket的频次构造一个“团体规模”特征,效果比单纯用SibSp和Parch更接近真实情况,因为同票号的人可能是朋友或随行人员,并不一定在亲属字段里体现。2.4 编码策略:树模型其实不需要One-Hot那么激进GBDT系的树模型处理类别特征时,基于叶子节点分裂的机制本身就能自动找到类别间的最优切分,One-Hot编码会显著增加稀疏性,拖慢训练速度,但不会带来精度提升。Pclass、Sex这种低基数类别特征,直接用Label Encoding或者本身就保留整数取值就够了。Embarked(登船港口)有3个缺失值,直接删除极小量样本或填众数都可以。我习惯填众数“S”,因为缺失量占比不到0.5%,基本不影响结果。Sex编码成0/1,Pclass保持1/2/3原样,这样就完成了GBDT输入的准备工作。这一整套特征工程做完之后,我通常会得到一个与业务逻辑强相关的特征集合,而不是盲目堆几十个Embedding向量。这也是表格数据和图像、文本数据的本质区别:特征是人工构造的、可解释的、带有先验知识的。3. GBDT建模全流程:从基准参数到精调细节3.1 先搭一套baseline,再谈优化我见过太多人在Titanic上一上来就疯狂调参,实际上先跑通一套默认参数的baseline最重要。LightGBM在sklearn接口下起步代码非常短:import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score X_train, X_val, y_train, y_val = train_test_split( features, labels, test_size=0.2, random_state=42, stratify=labels ) model = lgb.LGBMClassifier( learning_rate=0.1, n_estimators=100, num_leaves=31, max_depth=-1, min_child_samples=20, random_state=42 ) model.fit(X_train, y_train) y_pred = model.predict(X_val) print(accuracy_score(y_val, y_pred))跑完这个baseline,在合理特征工程的前提下,Titanic的验证集准确率应该能到0.80到0.82之间。如果你的结果低于0.78,先别急着调参,回去检查特征工程和数据清洗,大概率是Age填充方式或者Cabin处理出了问题。3.2 核心参数逐个拆解,别只靠grid search硬搜LightGBM参数很多,但实际需要精调的其实就那么几个,我这里逐个讲清楚每个参数背后的逻辑。learning_rate(学习率)决定每棵树对最终预测的贡献权重。调小它通常能提升精度,但需要同步增加n_estimators,训练时间线性增长。常见做法是先固定learning_rate=0.05,然后用早停机制自动确定树的数量。num_leaves是LightGBM的核心复杂度控制参数,它控制单棵树的最大叶子节点数。默认31在中小数据集上够用,但我在Titanic上实测把num_leaves调小到15左右,配合max_depth=3,验证集准确率略有提升。原因是数据集本身只有几百个样本,过深的叶子节点容易记住噪声。min_child_samples控制叶子节点最少样本数,默认20比较安全。调大可以抑制过拟合,调小可以捕捉更细的模式。在Titanic上我试过调到40,准确率略有下降,说明数据量的限制让模型更需要细节模式。max_depth设为-1表示不限制,依靠num_leaves来约束树的复杂度。这种组合比单独限制max_depth更灵活。如果设为3,加上较小的num_leaves,模型偏向高偏差低方差,适合小数据场景。还有一个容易被忽视的参数是feature_fraction(特征抽样比例),默认1.0即全部特征参与分裂。由于Titanic特征维度不高,我一般保持默认,如果特征工程后维度超过50,可以考虑设到0.8左右,既加速又减弱特征间的相关性影响。注意:学习率的调整一定要和树的数量联动考虑。只调小的learning_rate而不增加n_estimators,模型会欠拟合;只增加n_estimators而不调小learning_rate,很快就会过拟合。最佳实践是先设定一个较小的learning_rate,用早停自动找到最优树数量。3.3 早停策略的用法,以及一个容易踩的时间顺序坑早停(early stopping)是最省心的正则化手段,核心逻辑是:每训练一轮就在验证集上评估一次,如果连续N轮没有提升,就停止训练并回滚到最优迭代轮数。model = lgb.LGBMClassifier( learning_rate=0.05, n_estimators=1000, num_leaves=15, max_depth=3, min_child_samples=20, random_state=42 ) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], callbacks=[lgb.early_stopping(stopping_rounds=50)] )早停里有个容易被忽略的坑:如果你在划分训练集和验证集时不加stratify参数,验证集里的生存标签分布可能和训练集差异较大,导致早停时机偏差。在这个只有几百条样本的数据集上,随机划分很可能把某个罕见类别的样本全部抽走,所以要始终使用分层划分。早停还有一个实际好处是帮你反推学习率是否合理。如果早停发生在20轮以内,说明learning_rate设太大,模型在快速拟合噪声;如果1000轮都没触发早停且验证集分数稳定,说明learning_rate太小,训练收益微乎其微,可以适当调大。3.4 从特征重要性反推业务结论LightGBM训练完成后,特征重要性提供了模型决策逻辑的一扇观察窗。feature_imp = pd.DataFrame({ 'feature': model.feature_name_, 'importance': model.feature_importances_ }).sort_values('importance', ascending=False)在合理特征工程之后,重要性排序通常符合这样的规律:Sex大概率排第一,Pclass紧随其后,然后是Age和Fare,Title提取后的特征也排在前列,FamilySize通常在中间区域。如果某个明显无关的特征排进了前三,比如PassengerId这种完全随机的ID,那说明特征工程环节出了问题,或者模型过拟合了训练集的噪声。这里插一句,这个反推过程就是GBDT优于黑盒模型的最大优势。金融风控、医疗诊断等场景要求模型可解释,GBDT能告诉你每个特征对每个样本的预测贡献了多少,这是神经网络系模型很难直接给出的。我做过不少需要向业务方解释模型逻辑的项目,GBDT的feature importance和SHAP值几乎是必备材料。4. LLM参与表格建模的三种姿势与Scaling Law验证4.1 姿势一:零样本推理,把表格转成Prompt让LLM直接预测LLM处理表格数据最基本的做法,是将每一行样本的字段构造为自然语言描述,然后让模型直接输出生存与否的预测。这里的关键在于Prompt怎么设计。对Titanic,我会把每行样本写成一个描述句:你是数据分析师,请根据以下乘客信息判断该乘客是否能在泰坦尼克号沉船事故中幸存。 乘客信息:男性,3等舱,25岁,票价8.05美元,独自一人,无亲属同行,无舱房记录。 只回答“幸存”或“不幸”,不要输出其他内容。零样本模式下,LLM的准确率通常在0.72到0.76之间,明显低于GBDT的0.80以上。原因很好理解:LLM能捕获的常识性规律(女性、头等舱、儿童更可能获救)在Titanic数据里确实存在,但它无法精确学习到具体数字边界,比如票价37美元这一分界线上下的差异、不同登船港口的细微生存差别。这里就引出一个问题:零样本LLM的本质,是“用预训练知识进行推理”,不是“用你的数据学习规律”。对通用的、符合常识规律的数据,它表现尚可;但对领域特定的隐蔽模式,它无能为力。这就是为什么在表格数据上,纯粹的零样本LLM很难取代GBDT。4.2 姿势二:少样本学习,塞几条示例进Prompt零样本不行,那就塞几条示例进Prompt。少样本学习利用的是LLM的in-context learning能力——给它看几个输入输出对,它就能模仿这个模式进行推断。操作上很简单,从训练集中挑几条不同类别的样本,把特征描述和正确答案拼进Prompt:请根据以下示例学习规律,然后预测新乘客的生存情况。 示例1:女性,1等舱,35岁,票价75.9美元,与丈夫同行,无舱房记录 -> 幸存 示例2:男性,3等舱,28岁,票价8.05美元,独自一人,无舱房记录 -> 不幸 示例3:女性,2等舱,45岁,票价26.5美元,与两个孩子同行,无舱房记录 -> 幸存 新乘客:男性,2等舱,30岁,票价23.5美元,与一位亲属同行,有舱房记录 请只回答“幸存”或“不幸”。在几组实验里,少样本(5到10条示例)能把LLM准确率提升到0.78左右,仍然略低于GBDT,但差距明显缩小了。示例的选择有讲究,全挑简单样本会导致LLM学不到边界情况,应该覆盖不同舱位等级、不同性别和不同家庭规模组合,让类别的分布与原始数据集大致一致。4.3 姿势三:LLM做自动特征工程,把生成结果喂给GBDT这是我认为更具实用价值的玩法。既然GBDT擅长处理人工构造的特征,而LLM擅长语义理解,那为什么不结合一下:让LLM从原始字段中生成新特征,再喂给GBDT。比如,把乘客的姓名、舱位、票价信息发给LLM,让它推断“乘客的社会经济地位等级”并编码为1到5的整数;让它根据家庭规模和年龄推断“逃生时获得帮助的可能性”并编码为0到1的分数。LLM输出的这些语义特征作为额外的输入列,加到原有特征矩阵中,继续交给GBDT训练。这个思路在几个模拟项目里验证过,生成特征对验证集准确率的提升因任务而异。Titanic上提升幅度不太大(0.5%左右),因为原本的特征工程已经很充分了。但对一些文本和数值混合的表格任务,比如客服工单的解决率预测、用户评价的星级推断,LLM生成的特征往往能带来1%到2%的明确提升。我的建议是,别把LLM当成最终的预测模型,而是当成特征提取器。它在表格任务上的正确生态位是“理解语义、补全特征”,而不是“精确计算、拟合边界”。4.4 用Titanic验证Scaling Law的三个维度Scaling Law的核心是一句话:模型性能随训练数据量、模型容量、计算量的增大而按特定规律提升。在Titanic上做不了大模型训练,但可以验证三个简化维度。维度一是数据量的缩放。我分别用100%、80%、60%、40%、20%的训练数据训练GBDT,记录验证集准确率。结果很典型:数据从100%降到60%时,准确率下降不明显,但降到40%以下就开始掉得厉害。这与Scaling Law中“模型性能随数据量对数增长”的描述一致——数据量翻倍带来的准确率提升幅度逐级递减。维度二是模型容量的缩放。对GBDT来说,n_estimators就是容量的表征。我把n_estimators从10逐步增加到1000,观察验证集准确率的变化。结果是先上升后下降,出现了明显的过拟合拐点。这揭示了一个重要差异:GBDT的Scaling Law不是单调的,因为树模型的容量提升会同时带来过拟合风险;而LLM的Scaling Law在大规模训练中更多表现为单调提升,因为它有海量数据和正则化手段支撑。维度三是LLM模型大小的缩放。在Titanic上,我更倾向于把“上下文长度”或“示例数量”看作容量维度。实验结果是:少样本示例数量从1条增加到10条时,准确率提升明显;超过10条后提升趋缓,甚至略有下降(因为过长的上下文会引入噪声干扰)。综合三个维度的观察,可以得出一个实践结论:在小表格数据场景里,GBDT仍是最优选择;当数据量增大到一定规模(我自己经验上的阈值是几万行以上),神经网络或基于深度学习的表格模型才会逐步追近甚至反超。LLM在表格数据上的意义更多体现在特征理解和生成上,而不是终点预测。5. 常见问题与排错技巧实录5.1 类别分布不均衡:Titanic其实还算温和Titanic的Survived分布大约是38%幸存对62%不幸,虽然存在轻度不均衡,但不够成严重问题。如果在其他失衡数据集上建模,GBDT的默认策略会对多数类倾斜,解决办法是通过class_weight参数调整或使用scale_pos_weight。我建议在Titanic上不用做特殊处理,用accuracy评分就够了。但要注意的是,当测试集和训练集的类别分布差异较大时,直接套用accuracy会严重失真。更稳妥的评估是同时看AUC和F1-score,AUC对不平衡不敏感,能更真实地反映模型的排序能力。5.2 Age缺失值填好后,验证集分数反而下降了?这是个我踩过的坑。有次我用sklearn的SimpleImputer做均值填充,结果验证集分数比完全删除缺失行还低。排查后发现,原因是均值填充引入了数据分布以外的中间值,而树模型的切分点对这一点很敏感——大量样本被填成同一个常数,模型分不清哪些是原始值哪些是填充值。解决思路是加一个“AgeWasMissing”的布尔特征,标记每个样本的Age是否为填充值,让模型自己判断是否要利用这个区分度。这种做法在缺失率较高的特征上几乎总是有效,不只是Titanic,风控数据里的收入字段也经常这么处理。5.3 LLM推理结果不稳定:答案是概率性的LLM不是确定性系统,同样的Prompt多次推理可能给出不同结果。我在少样本实验中就遇到过同一份测试集两次推理,准确率相差2%的情况。排查方式是设置temperature=0,让输出尽可能确定;如果业务场景允许,多次采样取多数票也能显著降低方差。另外,Prompt中的示例顺序会影响结果。把与测试样本更相似的示例放在Prompt末尾,推理准确率通常更高,这是in-context learning的已知特性。5.4 训练集和验证集效果差异过大的排查路径如果模型训练集上0.99、验证集上0.78,不要急着加正则化,先按顺序排查四件事:第一,是否在特征工程阶段不小心引入了与Survived直接相关的标签泄露,比如把生存预测中本不该出现的字段参与了特征计算;第二,数据划分是否真的随机且分层;第三,是否有重复样本同时出现在训练集和验证集;第四,特征中是否存在取值跨度极大的数值列(如Fare),导致某些树的切分只对当前数据有效。这个排查顺序我习惯固定下来用,省去了很多无效调参的时间。表格数据从业者的日常,一大半时间其实不是在训练模型,而是在和数据质量搏斗。6. 这一整套流程跑完之后,我的真实体会Titanic这个数据集我少说跑过几十遍,每次用不同的方法重做都会有新收获。这次把GBDT和LLM放在同一个实验框架里对比,最大的体会是:模型选型本质上是在“数据量、任务复杂度、可解释性约束、成本预算”四个约束条件下做权衡。在小表格数据上,GBDT依然是最可靠的答案;而LLM的价值不在于取代GBDT,而在于它给表格建模带来了新的特征生成方式和先验知识的注入途径。最后分享一个小技巧:当你拿到任何新数据集,先用这套流程跑通GBDT baseline,再考虑是否引入LLM或其他复杂模型。如果连baseline都达不到预期,问题几乎一定在数据质量或特征工程上,换再强的模型也没用。这个经验支撑了我过去很多项目,也推荐你把它作为自己的默认工作流起点。
1. 先把话说透:GBDT和LLM在表格数据上的真实处境聊到表格数据建模,业内一直有个心照不宣的结论:凡是结构化数据,GBDT系模型(LightGBM、XGBoost、CatBoost)依然是性价比最高、最稳的默认选择。我做了几年机器学习项目,从风控评分卡到营销响应预测,绕过一圈之后,最终回到底层还是梯度提升树。但这两年LLM绕不开,恰好Titanic又是一个所有人入门时都跑过的数据集,拿它来验证LLM和GBDT在表格任务上的表现差异、顺便把Scaling Law落个地,这个选题我觉得挺有意思。先说清楚这篇内容解决什么问题。如果你手头有一个结构化数据集,正在纠结“要不要上大模型”,或者单纯想系统搞懂GBDT的调参逻辑,顺便看看LLM在处理表格数据时到底有几斤几两——这篇基本能覆盖。我们会用Titanic这个体量极小但五脏俱全的数据集,跑完特征工程、GBDT训练调优、LLM推理对比三件事,并把“模型性能随数据量/容量变化的规律”这个Scaling Law核心思想用实验串起来。不夸张地说,Titanic这个数据集虽然老,但它包含了缺失值、类别特征、数值特征、文本型特征(姓名、舱位)几乎所有表格数据常见难点,特别适合做这种横向对比。而且它足够小,意味着你可以反复跑实验、试参数、验证各种猜想,成本几乎为零。这一篇的核心不是教你调出一个99%准确率的模型——说实话在Titanic上那没意义——而是让你把GBDT和LLM这两条技术路线在同一个数据集上“同时跑通”,并理解背后的取舍逻辑。2. Titanic数据集处理的四个关键细节,大多数人第一步就错了2.1 先摸清字段的血型再动手Titanic数据集一共12个字段,但真正能进模型的通常只有8到9个。PassengerId和Name不会直接作为特征,但要特别注意,Name里的Title(称谓)是信息量极高的特征,我后面单独讲。Survived是标签列。Pclass(舱位等级)虽然是整数,但它的取值只有1、2、3,本质是序数类别特征,不要直接当连续值扔进去。我处理这类表格数据的习惯是,先做一轮EDA,把每个字段的类型、缺失率、取值分布列成一张表,然后才决定特征工程的方向。这一步看起来很基础,但很多人跳过之后,后面各种隐蔽问题都会冒出来。比如Age有177个缺失值,Cabin有687个缺失值,如果不去想清楚这两列的处理策略,后面的模型效果一定受影响。关键决策点有两个:Age缺失值的填充方式。均值填充虽然简单,但会降低特征方差,影响树模型的分裂质量。我试过用性别+Pclass分组中位数填充,效果比全局均值填充要好。因为不同舱位、不同性别的乘客年龄分布差异很大,头部舱位的中年男性乘客和底层舱位的年轻男性乘客,年龄中位数能差出十岁。用组内统计量去填,信息保留度明显更高。Cabin缺失值占比太高,直接作为类别特征会引入大量噪声。常见做法是把它变成“有无Cabin记录”的二值特征,或者提取甲板层级的首字母。我实测下来,在Titanic上“有无Cabin记录”这个二值特征比提取首字母更好用,因为缺失本身就和获救概率有相关性——有舱房记录的乘客,大概率是优先级更高的舱位。2.2 从Name里提取Title,是Titanic特征工程里性价比最高的一步这是个老技巧,但每次都要强调。Name字段本身不能直接进模型,但从中提取的Title能反映社会身份、年龄结构、甚至舱位等级。比如“Mr.”“Mrs.”的中位数年龄差异很大,“Master.”几乎可以确定是未成年男性,而“Dr.”“Rev.”“Lady”这类稀有称谓的人群,获救比例显著偏高。具体做法就是正则提取逗号后的称谓部分:import re def extract_title(name): title_search = re.search(r' ([A-Za-z]+)\.', name) if title_search: return title_search.group(1) return 'Unknown' train['Title'] = train['Name'].apply(extract_title)提取出来后,低频称谓要合并成一个大类,否则树模型会把它们当成噪声。常见的做法是把出现次数少于5的Title统一归为“Rare”。这一步做完,特征维度不会增加太多,但对模型准确率的提升非常直接,我实测能带来2%到3%的AUC提升。2.3 家庭维度特征构造:FamilySize和IsAloneSibSp(兄弟姐妹/配偶数量)和Parch(父母/子女数量)两个字段单独看区分度一般,但组合成FamilySize之后,信息量会明显增强。家庭规模为1的旅客(独行者)获救概率偏低,家庭规模2到4的旅客获救概率偏高,而家庭规模超过4的旅客获救概率又开始下降。这个V型关系在树模型里可以自动学到,但如果你提前构造出来,模型训练会更高效。train['FamilySize'] = train['SibSp'] + train['Parch'] + 1 train['IsAlone'] = 1 train.loc[train['FamilySize'] > 1, 'IsAlone'] = 0还有一个容易被忽略的字段是Ticket。同一个Ticket编号下的乘客属于同一组,组内生存情况高度相关。可以用Ticket的频次构造一个“团体规模”特征,效果比单纯用SibSp和Parch更接近真实情况,因为同票号的人可能是朋友或随行人员,并不一定在亲属字段里体现。2.4 编码策略:树模型其实不需要One-Hot那么激进GBDT系的树模型处理类别特征时,基于叶子节点分裂的机制本身就能自动找到类别间的最优切分,One-Hot编码会显著增加稀疏性,拖慢训练速度,但不会带来精度提升。Pclass、Sex这种低基数类别特征,直接用Label Encoding或者本身就保留整数取值就够了。Embarked(登船港口)有3个缺失值,直接删除极小量样本或填众数都可以。我习惯填众数“S”,因为缺失量占比不到0.5%,基本不影响结果。Sex编码成0/1,Pclass保持1/2/3原样,这样就完成了GBDT输入的准备工作。这一整套特征工程做完之后,我通常会得到一个与业务逻辑强相关的特征集合,而不是盲目堆几十个Embedding向量。这也是表格数据和图像、文本数据的本质区别:特征是人工构造的、可解释的、带有先验知识的。3. GBDT建模全流程:从基准参数到精调细节3.1 先搭一套baseline,再谈优化我见过太多人在Titanic上一上来就疯狂调参,实际上先跑通一套默认参数的baseline最重要。LightGBM在sklearn接口下起步代码非常短:import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score X_train, X_val, y_train, y_val = train_test_split( features, labels, test_size=0.2, random_state=42, stratify=labels ) model = lgb.LGBMClassifier( learning_rate=0.1, n_estimators=100, num_leaves=31, max_depth=-1, min_child_samples=20, random_state=42 ) model.fit(X_train, y_train) y_pred = model.predict(X_val) print(accuracy_score(y_val, y_pred))跑完这个baseline,在合理特征工程的前提下,Titanic的验证集准确率应该能到0.80到0.82之间。如果你的结果低于0.78,先别急着调参,回去检查特征工程和数据清洗,大概率是Age填充方式或者Cabin处理出了问题。3.2 核心参数逐个拆解,别只靠grid search硬搜LightGBM参数很多,但实际需要精调的其实就那么几个,我这里逐个讲清楚每个参数背后的逻辑。learning_rate(学习率)决定每棵树对最终预测的贡献权重。调小它通常能提升精度,但需要同步增加n_estimators,训练时间线性增长。常见做法是先固定learning_rate=0.05,然后用早停机制自动确定树的数量。num_leaves是LightGBM的核心复杂度控制参数,它控制单棵树的最大叶子节点数。默认31在中小数据集上够用,但我在Titanic上实测把num_leaves调小到15左右,配合max_depth=3,验证集准确率略有提升。原因是数据集本身只有几百个样本,过深的叶子节点容易记住噪声。min_child_samples控制叶子节点最少样本数,默认20比较安全。调大可以抑制过拟合,调小可以捕捉更细的模式。在Titanic上我试过调到40,准确率略有下降,说明数据量的限制让模型更需要细节模式。max_depth设为-1表示不限制,依靠num_leaves来约束树的复杂度。这种组合比单独限制max_depth更灵活。如果设为3,加上较小的num_leaves,模型偏向高偏差低方差,适合小数据场景。还有一个容易被忽视的参数是feature_fraction(特征抽样比例),默认1.0即全部特征参与分裂。由于Titanic特征维度不高,我一般保持默认,如果特征工程后维度超过50,可以考虑设到0.8左右,既加速又减弱特征间的相关性影响。注意:学习率的调整一定要和树的数量联动考虑。只调小的learning_rate而不增加n_estimators,模型会欠拟合;只增加n_estimators而不调小learning_rate,很快就会过拟合。最佳实践是先设定一个较小的learning_rate,用早停自动找到最优树数量。3.3 早停策略的用法,以及一个容易踩的时间顺序坑早停(early stopping)是最省心的正则化手段,核心逻辑是:每训练一轮就在验证集上评估一次,如果连续N轮没有提升,就停止训练并回滚到最优迭代轮数。model = lgb.LGBMClassifier( learning_rate=0.05, n_estimators=1000, num_leaves=15, max_depth=3, min_child_samples=20, random_state=42 ) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], callbacks=[lgb.early_stopping(stopping_rounds=50)] )早停里有个容易被忽略的坑:如果你在划分训练集和验证集时不加stratify参数,验证集里的生存标签分布可能和训练集差异较大,导致早停时机偏差。在这个只有几百条样本的数据集上,随机划分很可能把某个罕见类别的样本全部抽走,所以要始终使用分层划分。早停还有一个实际好处是帮你反推学习率是否合理。如果早停发生在20轮以内,说明learning_rate设太大,模型在快速拟合噪声;如果1000轮都没触发早停且验证集分数稳定,说明learning_rate太小,训练收益微乎其微,可以适当调大。3.4 从特征重要性反推业务结论LightGBM训练完成后,特征重要性提供了模型决策逻辑的一扇观察窗。feature_imp = pd.DataFrame({ 'feature': model.feature_name_, 'importance': model.feature_importances_ }).sort_values('importance', ascending=False)在合理特征工程之后,重要性排序通常符合这样的规律:Sex大概率排第一,Pclass紧随其后,然后是Age和Fare,Title提取后的特征也排在前列,FamilySize通常在中间区域。如果某个明显无关的特征排进了前三,比如PassengerId这种完全随机的ID,那说明特征工程环节出了问题,或者模型过拟合了训练集的噪声。这里插一句,这个反推过程就是GBDT优于黑盒模型的最大优势。金融风控、医疗诊断等场景要求模型可解释,GBDT能告诉你每个特征对每个样本的预测贡献了多少,这是神经网络系模型很难直接给出的。我做过不少需要向业务方解释模型逻辑的项目,GBDT的feature importance和SHAP值几乎是必备材料。4. LLM参与表格建模的三种姿势与Scaling Law验证4.1 姿势一:零样本推理,把表格转成Prompt让LLM直接预测LLM处理表格数据最基本的做法,是将每一行样本的字段构造为自然语言描述,然后让模型直接输出生存与否的预测。这里的关键在于Prompt怎么设计。对Titanic,我会把每行样本写成一个描述句:你是数据分析师,请根据以下乘客信息判断该乘客是否能在泰坦尼克号沉船事故中幸存。 乘客信息:男性,3等舱,25岁,票价8.05美元,独自一人,无亲属同行,无舱房记录。 只回答“幸存”或“不幸”,不要输出其他内容。零样本模式下,LLM的准确率通常在0.72到0.76之间,明显低于GBDT的0.80以上。原因很好理解:LLM能捕获的常识性规律(女性、头等舱、儿童更可能获救)在Titanic数据里确实存在,但它无法精确学习到具体数字边界,比如票价37美元这一分界线上下的差异、不同登船港口的细微生存差别。这里就引出一个问题:零样本LLM的本质,是“用预训练知识进行推理”,不是“用你的数据学习规律”。对通用的、符合常识规律的数据,它表现尚可;但对领域特定的隐蔽模式,它无能为力。这就是为什么在表格数据上,纯粹的零样本LLM很难取代GBDT。4.2 姿势二:少样本学习,塞几条示例进Prompt零样本不行,那就塞几条示例进Prompt。少样本学习利用的是LLM的in-context learning能力——给它看几个输入输出对,它就能模仿这个模式进行推断。操作上很简单,从训练集中挑几条不同类别的样本,把特征描述和正确答案拼进Prompt:请根据以下示例学习规律,然后预测新乘客的生存情况。 示例1:女性,1等舱,35岁,票价75.9美元,与丈夫同行,无舱房记录 -> 幸存 示例2:男性,3等舱,28岁,票价8.05美元,独自一人,无舱房记录 -> 不幸 示例3:女性,2等舱,45岁,票价26.5美元,与两个孩子同行,无舱房记录 -> 幸存 新乘客:男性,2等舱,30岁,票价23.5美元,与一位亲属同行,有舱房记录 请只回答“幸存”或“不幸”。在几组实验里,少样本(5到10条示例)能把LLM准确率提升到0.78左右,仍然略低于GBDT,但差距明显缩小了。示例的选择有讲究,全挑简单样本会导致LLM学不到边界情况,应该覆盖不同舱位等级、不同性别和不同家庭规模组合,让类别的分布与原始数据集大致一致。4.3 姿势三:LLM做自动特征工程,把生成结果喂给GBDT这是我认为更具实用价值的玩法。既然GBDT擅长处理人工构造的特征,而LLM擅长语义理解,那为什么不结合一下:让LLM从原始字段中生成新特征,再喂给GBDT。比如,把乘客的姓名、舱位、票价信息发给LLM,让它推断“乘客的社会经济地位等级”并编码为1到5的整数;让它根据家庭规模和年龄推断“逃生时获得帮助的可能性”并编码为0到1的分数。LLM输出的这些语义特征作为额外的输入列,加到原有特征矩阵中,继续交给GBDT训练。这个思路在几个模拟项目里验证过,生成特征对验证集准确率的提升因任务而异。Titanic上提升幅度不太大(0.5%左右),因为原本的特征工程已经很充分了。但对一些文本和数值混合的表格任务,比如客服工单的解决率预测、用户评价的星级推断,LLM生成的特征往往能带来1%到2%的明确提升。我的建议是,别把LLM当成最终的预测模型,而是当成特征提取器。它在表格任务上的正确生态位是“理解语义、补全特征”,而不是“精确计算、拟合边界”。4.4 用Titanic验证Scaling Law的三个维度Scaling Law的核心是一句话:模型性能随训练数据量、模型容量、计算量的增大而按特定规律提升。在Titanic上做不了大模型训练,但可以验证三个简化维度。维度一是数据量的缩放。我分别用100%、80%、60%、40%、20%的训练数据训练GBDT,记录验证集准确率。结果很典型:数据从100%降到60%时,准确率下降不明显,但降到40%以下就开始掉得厉害。这与Scaling Law中“模型性能随数据量对数增长”的描述一致——数据量翻倍带来的准确率提升幅度逐级递减。维度二是模型容量的缩放。对GBDT来说,n_estimators就是容量的表征。我把n_estimators从10逐步增加到1000,观察验证集准确率的变化。结果是先上升后下降,出现了明显的过拟合拐点。这揭示了一个重要差异:GBDT的Scaling Law不是单调的,因为树模型的容量提升会同时带来过拟合风险;而LLM的Scaling Law在大规模训练中更多表现为单调提升,因为它有海量数据和正则化手段支撑。维度三是LLM模型大小的缩放。在Titanic上,我更倾向于把“上下文长度”或“示例数量”看作容量维度。实验结果是:少样本示例数量从1条增加到10条时,准确率提升明显;超过10条后提升趋缓,甚至略有下降(因为过长的上下文会引入噪声干扰)。综合三个维度的观察,可以得出一个实践结论:在小表格数据场景里,GBDT仍是最优选择;当数据量增大到一定规模(我自己经验上的阈值是几万行以上),神经网络或基于深度学习的表格模型才会逐步追近甚至反超。LLM在表格数据上的意义更多体现在特征理解和生成上,而不是终点预测。5. 常见问题与排错技巧实录5.1 类别分布不均衡:Titanic其实还算温和Titanic的Survived分布大约是38%幸存对62%不幸,虽然存在轻度不均衡,但不够成严重问题。如果在其他失衡数据集上建模,GBDT的默认策略会对多数类倾斜,解决办法是通过class_weight参数调整或使用scale_pos_weight。我建议在Titanic上不用做特殊处理,用accuracy评分就够了。但要注意的是,当测试集和训练集的类别分布差异较大时,直接套用accuracy会严重失真。更稳妥的评估是同时看AUC和F1-score,AUC对不平衡不敏感,能更真实地反映模型的排序能力。5.2 Age缺失值填好后,验证集分数反而下降了?这是个我踩过的坑。有次我用sklearn的SimpleImputer做均值填充,结果验证集分数比完全删除缺失行还低。排查后发现,原因是均值填充引入了数据分布以外的中间值,而树模型的切分点对这一点很敏感——大量样本被填成同一个常数,模型分不清哪些是原始值哪些是填充值。解决思路是加一个“AgeWasMissing”的布尔特征,标记每个样本的Age是否为填充值,让模型自己判断是否要利用这个区分度。这种做法在缺失率较高的特征上几乎总是有效,不只是Titanic,风控数据里的收入字段也经常这么处理。5.3 LLM推理结果不稳定:答案是概率性的LLM不是确定性系统,同样的Prompt多次推理可能给出不同结果。我在少样本实验中就遇到过同一份测试集两次推理,准确率相差2%的情况。排查方式是设置temperature=0,让输出尽可能确定;如果业务场景允许,多次采样取多数票也能显著降低方差。另外,Prompt中的示例顺序会影响结果。把与测试样本更相似的示例放在Prompt末尾,推理准确率通常更高,这是in-context learning的已知特性。5.4 训练集和验证集效果差异过大的排查路径如果模型训练集上0.99、验证集上0.78,不要急着加正则化,先按顺序排查四件事:第一,是否在特征工程阶段不小心引入了与Survived直接相关的标签泄露,比如把生存预测中本不该出现的字段参与了特征计算;第二,数据划分是否真的随机且分层;第三,是否有重复样本同时出现在训练集和验证集;第四,特征中是否存在取值跨度极大的数值列(如Fare),导致某些树的切分只对当前数据有效。这个排查顺序我习惯固定下来用,省去了很多无效调参的时间。表格数据从业者的日常,一大半时间其实不是在训练模型,而是在和数据质量搏斗。6. 这一整套流程跑完之后,我的真实体会Titanic这个数据集我少说跑过几十遍,每次用不同的方法重做都会有新收获。这次把GBDT和LLM放在同一个实验框架里对比,最大的体会是:模型选型本质上是在“数据量、任务复杂度、可解释性约束、成本预算”四个约束条件下做权衡。在小表格数据上,GBDT依然是最可靠的答案;而LLM的价值不在于取代GBDT,而在于它给表格建模带来了新的特征生成方式和先验知识的注入途径。最后分享一个小技巧:当你拿到任何新数据集,先用这套流程跑通GBDT baseline,再考虑是否引入LLM或其他复杂模型。如果连baseline都达不到预期,问题几乎一定在数据质量或特征工程上,换再强的模型也没用。这个经验支撑了我过去很多项目,也推荐你把它作为自己的默认工作流起点。