首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
泰坦尼克号生存预测实战:特征工程、交叉验证与模型调参全记录
📅 2026/10/21 23:16:09
✍️ 爱科研究院
👁 阅读 3,247
Kaggle上的泰坦尼克号生存预测几乎每个学机器学习的人都会碰一次。这项目从2012年上线到现在一直是很多人提交的第一个Kaggle内核也是很多课程里拿来演示分类问题的固定案例。我把它作为自己的第02个完整实验来跑目的倒不是为了刷一个多高的分而是想完整走一遍从原始CSV到最终预测结果的全流程特征工程、交叉验证、调参、提交。这中间有几个地方表面看很简单实际动手做才会发现坑比想象中多。这个项目我用的数据就是Kaggle官方给的train.csv和test.csv训练集891条测试集418条字段包括乘客ID、舱位等级Pclass、姓名Name、性别Sex、年龄Age、同乘兄弟姐妹/配偶数SibSp、同乘父母/子女数Parch、票号Ticket、票价Fare、客舱号Cabin、登船港口Embarked目标列是Survived。整个任务就是根据这些信息判断乘客是否生还。今天这篇记录就按我实际操作的顺序来还原整个实验过程包括每一处改动的理由、每个参数选择的依据以及踩过的一些坑。1. 项目整体设计与思路拆解1.1 为什么这个项目值得认真做一遍泰坦尼克号这个题从模型角度说并不是什么高难度挑战它没有特别复杂的高维数据也没有图像文本这种非结构化输入纯粹就是一张二维表格。但恰恰是这种“看起来简单”的数据集最适合用来检验你对机器学习流程的掌握程度。我见过不少人跑这个项目上来就调库读数据喂给随机森林提交分数0.75左右结束。这样做当然也算学会了调包但没太大意义。这个项目的价值在于891条训练数据里真正决定生死的特征隐藏在一些需要自己加工的字段中。比如乘客姓名里的头衔Title船票号码Ticket客舱号Cabin这些字段原始状态下没法直接进模型但经过处理后往往比原始数值特征更有区分度。换句话说这个项目真正考察的是特征工程能力而不是模型复杂度。从我个人的实践经验来看这个项目最适合三类人刚学完Python和pandas、想练手数据处理的初学者已经会建模但想系统梳理特征工程方法的中级学习者以及准备参加更复杂Kaggle比赛、想拿这个简单项目练熟交叉验证和提交流程的人。1.2 数据核心逻辑哪些信息真正影响生存率在动手写代码之前我先对数据做了一次整体审视。这里有个关键经验不要一上来就急着处理缺失值先搞清楚每列的本质含义以及它和目标之间的关系。我先把训练集里最重要的几个字段做了分布对比很快就能看到几个非常明显的规律。性别是决定性因素女性存活率明显偏高这符合泰坦尼克号“妇孺优先”的撤离原则。舱位等级Pclass同样影响显著头等舱存活比例远高于三等舱这背后对应的是船舱位置离救生艇甲板的距离以及疏散优先级。年龄和票价也有影响但不像前两者那么直观需要进一步分箱观察。这里有一个很容易被忽略的点很多人在分析数据时只关注单个字段但有些信息是藏在多个字段组合里的。比如SibSp和Parch这两个字段单看都不算强特征但如果合成一个“家庭成员数量FamilySize”就能反映出“一个人独行”还是“和家人一起”的状态。在真实海难中独行乘客往往更难被统计到、更容易被遗漏这一点在数据里也确实有体现。这些组合特征的构造才是这个项目能拉开分数差距的核心所在。2. 数据清洗与特征工程实操2.1 缺失值处理的几个选择及理由训练集里最明显的缺失项是Age、Cabin和Embarked。处理方式我逐个说明每个都记录了当时的考虑。Age字段分布比较集中缺失比例约为19.8%不算特别严重但直接删掉显然浪费信息。常见的做法有三种用全局中位数填充、用均值填充、按其他维度分组后填充。我测试下来效果最好的是按乘客头衔Title分组填充。原因是不同头衔对应的人群年龄区间差别很大Master通常是男孩Mr通常是成年男性Miss和Mrs分别是未婚和已婚女性用全局中位数会把不同群体的年龄特征抹平。我实际操作时用了groupby(Title)[Age].transform(median)这种方法既简单又合理避免引入额外复杂度。Cabin缺失量很大训练集里超过77%的乘客没有客舱号。这种情况下直接用中位数或众数填充都不可取因为“缺失”本身就是信息。我在实验里把Cabin处理成一个二元特征Has_Cabin表示是否有客舱记录同时保留了首个字母代表的舱区信息。这个思路的出发点是有客舱记录通常意味着乘客的舱位信息被完整登记这和乘客在船上的位置以及疏散过程中的状态可能有关系。Embarked只有2个缺失值用众数S填充就够了这里不需要过度设计。补充一个我在第一次做这个项目时踩过的坑最开始我直接把Age填充为全数据的中位数结果模型的交叉验证分数反而有所下降。原因并不难理解891条数据里缺失的年龄如果全填同一个值等于人为制造了一个集中的假象模型会倾向于认为这个值附近有大量样本进而扭曲对年龄这个特征的判断。后来改成按Title分组填充交叉验证分数有了小幅但稳定的提升。2.2 从姓名、票号和客舱里挖出的有效特征这是整个实验里我认为最值得详细记录的一部分也是从“只会调包”到“会做特征”的分水岭。先说Name字段。单纯看字符串没什么用但可以从里面提取头衔Title。我用正则表达式从逗号后、句点前的位置提取了乘客的头衔然后做了一个映射把少数罕见头衔归类到常见类别里。提取后统计可以发现Mr、Mrs、Miss、Master占了绝大多数还有少量Don、Lady、Sir这类非常见头衔样本极少我直接归入Rare类别。得到的Title特征和生还率有非常明显的相关Mrs和Miss存活率较高Mr的存活率明显偏低Master作为儿童头衔存活率也比较高。这个特征在后续模型中的重要性排名稳居前几位。再说Ticket字段。这是很多人忽略的地方。表面看票号是一堆乱码但有一个很重要的信息同一票号往往代表同一组同行乘客。这类信息无法从SibSp和Parch中完全看出因为朋友、同事、随行人员不一定在亲属关系字段里体现。我把票号里非数字部分清洗掉留下纯数字部分然后统计每个票号出现的次数构造出TicketFrequency特征。这里有个很直接的逻辑如果同票号的乘客人数多说明这批人可能是团体出行团体成员之间在疏散时更容易互相通知和协助。最后是Cabin字段的进一步处理。除了前面说的Has_Cabin之外我还提取了客舱区字母作为特征。泰坦尼克号的客舱编号首位字母对应不同甲板层这个字母能在一定程度上反映乘客在船上的活动区域。我把这些字母直接作为类别特征使用配合Pclass和Fare比单纯用Pclass单一特征更能描绘乘客的船舱位置。2.3 数值特征的分布处理与编码方式Age和Fare这两个连续数值特征原始值分布都偏斜。Age相对好一些但Fare的分布很极端少数高额票价把数据拉得很开直接喂给线性模型会导致数值范围差异过大。我做了两种处理方式对比一种是对Fare取对数log1p另一种是分箱成类别特征。两种都能提升模型稳定性最终我选择保留对数变换后的连续特征因为分箱会丢失区间内的细微差异。还有一个很多教程里不会提的问题Pclass虽然数值是1、2、3但它是有序类别变量用数值直接进树模型没问题但如果做线性模型需要one-hot编码。我在实验里统一把它当成类别特征做编码处理避免后续更换模型时还要回改。Sex这个字段最简单直接映射成0和1。Embarked同理做类别编码。这里我记录一个细节类别编码方式的选择会影响模型表现对于树模型来说标签编码和one-hot都可以对于线性模型则倾向one-hot。我在最终管道里用了one-hot编码因为后续基线模型中包含逻辑回归这样能在不同模型间保持一致的分值对比口径。还有一个值得记录的特征把SibSp和Parch合并成FamilySize之后我还进一步构造了IsAlone这个二值特征判断乘客是否一个人出行。单独一个FamilySize也能用但IsAlone把问题转化为“是否独行”在实际预测中更直观。从数据统计结果来看独行乘客的存活率低于结伴出行的乘客这个差值在训练集里是真实存在的。3. 模型选型与参数调优实录3.1 基线模型的建立与复盘我的习惯是任何项目先跑通一个最简单的基线用这个分数作为后续所有优化的基准参照。对这个数据集我先用逻辑回归做了基线模型只喂原始数值特征不做任何特征工程得到的交叉验证准确率大约是0.78。这个分数可以作为一个基准锚点。然后逐步加上特征工程结果同样是逻辑回归交叉验证分数提升到0.80附近。这个提升幅度看起来不大但考虑到逻辑回归对特征表达的要求比较高特征工程的价值已经体现出来了。之后换成随机森林和梯度提升树分数还会有进一步提升。这里有个非常重要的原则不要一上来就堆模型复杂度。先用简单的线性模型跑通整个流程再用树模型做对照这样你能分别看出特征工程和模型选择各自贡献了多少提升。如果直接上XGBoost你会得到一个看似不错的分数但你根本不知道这个分数里哪些来自特征工程哪些来自模型本身的拟合能力。3.2 交叉验证为何选择5折与分层抽样泰坦尼克号数据集样本量很小只有891条训练数据。如果只用一次train_test_split来评估模型结果会非常不稳定因为划分方式不同分数波动可以达到0.02甚至更多。所以我采用K折交叉验证这里我用的是5折。为什么选5折而不是10折样本量本身不大如果做10折每折只有89条验证数据验证集上的指标方差会偏大。5折时每折约178条样本量相对够用。同时5折的计算成本低便于在调参过程中反复运行节省了很多尝试时间。交叉验证时我还做了分层抽样。由于训练集中死亡人数多于生存人数比例大约是62%对38%如果不做分层某些折里可能出现生存样本过少的情况导致评估结果出现不必要的偏差。用StratifiedKFold可以保持每一折中Survived的正负比例与全量数据一致这样每个折的评估都是相对公平的。这一段的经验直接来自于一次对比实验我用普通K折跑出的分数波动接近0.025换成StratifiedKFold之后波动缩小到0.01以内。在小样本数据集上这一点差别非常影响你判断模型改进是否有效。3.3 调参思路从网格搜索到针对性微调网格搜索我实际用下来效率只能说一般。4个超参组合出几百种情况在小数据集上跑还能接受但时间成本高而且很多搜索结果在交叉验证分数上的差别其实在误差范围内。我的做法是先用粗网格定范围再手工微调关键参数。以随机森林为例我先把n_estimators固定在300搜索max_depth和min_samples_split的合理范围锁定到某个区间后再单独看max_features。等到各项参数都大致确定了最后一次微调n_estimators。这种做法的思路是随机森林的n_estimators只要足够大模型表现就会趋于稳定不需要花太多时间在上面精细搜索真正影响大的是树的深度和分裂条件。梯度提升类模型我同时测了XGBoost和LightGBM在小样本上两者差距不大。我最终选择保留XGBoost作为主模型之一和随机森林做集成投票。这里有个记录在泰坦尼克这个数据集上集成两个模型比单模型能稳定提升0.005到0.01幅度不大但对于追求排名的人来说已经值得保留。最终模型我用的是RandomForest和XGBoost的软投票集成逻辑回归作为对照组。软投票的含义是取每个模型预测的概率均值作为最终判断依据而不是简单按投票数——这在概率校准较好时通常效果更好。用5折交叉验证评估本地最终分数稳定在0.815左右。3.4 特征重要性排序的解读模型训练完之后我导出了随机森林的特征重要性。排名靠前的是Sex、Title、Fare、Age、Pclass这些和探索性分析的结论是一致的。值得注意的是Title的特征重要性高于Pclass这说明从Name里提取的头衔确实比原始舱位信息包含更多判别力。很多初学者容易忽略一个现象特征重要性高的特征不等于因果性强的特征。比如Title这个特征在泰坦尼克数据集里它隐含了性别、年龄和婚姻状况的综合信息所以它重要性高是合理的。但在做业务决策时不能直接认为“头衔决定了生存”这个逻辑要拎清楚。特征重要性只是给我们一个加工线索提示哪些原始信息值得保留而不是理解现实的依据。4. 常见问题与坑点排查实录4.1 过拟合的典型表现与应对手段小数据集最容易犯的错就是过拟合。我在实验中观察到一个很典型的现象当我把特征数量加得比较多比如把Ticket处理成几百个独立编号的类别特征时训练集上的准确率可以到0.95以上但交叉验证分数反而掉到0.78以下。这是因为模型把训练集里的个案规律记住了而不是学会了一般模式。解决方式有三个一是减少特征维度去掉区分度过低的特征二是限制树模型的深度比如把max_depth限制在5以内三是加强交叉验证的纪律每次特征改动后用同一组折来评估保证分数可比。我最终保留的特征数量是12个左右这个维度对891条样本来说比较安全既能表达信息又不至于引入过多噪声。4.2 提交格式与预测概率的坑这是我第一次提交时踩过最不值得的坑。Kaggle要求提交的CSV文件只有PassengerId和Survived两列且Survived必须是整数0或1。我当时直接用model.predict_proba(x_test)[:, 1]的结果写进CSV结果生成的是连续概率值提交后直接报错。正确做法是将概率大于等于0.5的映射为1其余为0。这里要提一点0.5作为阈值并不总是最优的。我从交叉验证的ROC曲线上观察最佳阈值大约在0.48左右但差距很小最终我还是用0.5作为阈值方便复现和解释。如果想把阈值调整得更细其实需要更多数据支撑在这个数据集上去做微调收益有限。4.3 本地交叉验证分数与公共排行榜分数不一致的原因这是一个非常多人会遇到的现象本地交叉验证分数0.815提交到Kaggle公共榜后分数可能是0.79也可能更高到0.83差别是正常的。原因是本地交叉验证只是用训练集的一部分来做评估而公共榜分数是在真正的测试集上算出来的两者的数据分布有一定差异样本量又小分数波动天然存在。我的做法是本地分数的主要目的是帮助对比不同特征和不同参数的相对优劣而不是预测公共榜的绝对分数。只要某个改动在本地能稳定提升交叉验证分数就值得保留如果本地分数没变即使公共榜分数波动也不应该当作有效提升。4.4 避免数据泄漏的几个禁区这个话题值得单独说。数据泄漏的含义是在训练阶段使用了本不该出现的信息在泰坦尼克这种小数据集上尤其容易犯这个错。最典型的例子是用全部数据包括test.csv的分布去做预处理参数拟合。比如你用全量数据的Age中位数去做填充那其实已经让测试集的信息渗透进了训练过程。正确的姿势是对训练集做fit再用训练集得到的参数去transform测试集。具体到实现上就是先用训练集的Age中位数填充训练集缺失值再用同一个值填充测试集缺失值。另一个容易被忽略的泄漏源是在做交叉验证时特征工程过程放错了位置。比如你先对全部训练数据做了Fare分箱或标准化再做5折交叉验证这其实已经有轻微泄漏了因为每一折的验证数据在特征工程阶段已经参与过统计。正确做法是把特征工程放进交叉验证的每一折里训练时只基于训练折的统计量再变换验证折。这个方法在sklearn里可以通过Pipeline实现也是我在这版代码里最终采用的结构。5. 实验总结与几点个人经验这个项目做完后我最大的感受是决定模型上限的不是模型本身而是你对数据里每个字段背后信息的理解程度。泰坦尼克号的891条训练数据并不算多但通过合理的特征加工可以让一个普通的树模型达到0.80以上的本地准确率而如果只是为了跑通流程直接把所有原始字段塞进模型可能只能停在0.75左右。最后记录几条我后续再做类似比赛时会继续使用的经验。第一建模之前先花充足时间看数据分布不要跳步。很多有效的特征工程思路来源于对字段的深入观察比如从票号里找同组人这一招如果不对Ticket做逐条排查很难想到。第二交叉验证的分层和固定随机种子必须从一开始就设置好否则每次实验的分数都没有可比性你根本没法判断某个改动是否有效。第三不要迷信公共排行榜分数。以Kaggle这个项目为例公共榜的418条测试数据占约50%的评估权重但由于样本量小本地CV和公共榜分数之间本来就有波动。专注于重复可验证的本地评估比盯着公共榜的一两分之差更有意义。第四如果你准备用集成方法一定要确认各个基模型的错误模式有一定差异性否则集成带来的提升很不稳定——这个在泰坦尼克实验里我用随机森林和XGBoost得到的效果就是要好于随机森林和额外随机树的组合。这个项目本身虽然已经很老了但它作为机器学习的入门流程完全不过时。后面如果有时间我计划在这个基底上再尝试一下特征选择的方法对比以及概率校准对提交结果的影响。每次做这个题都能发现新的可以改进的角落这也是经典项目有意思的地方。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/21 23:11:03
软件测试年终总结:用可验证数据构建质量价值表达框架
2026/10/21 23:11:03
技能盘点与迭代:把个人能力当作资产组合来经营
2026/10/21 23:11:03
PTES七阶段实战指南:渗透测试流程落地与避坑
2026/10/22 3:51:46
SpringBoot高并发抢购系统:防超卖与分布式锁实战
2026/10/22 3:51:46
端到端不是终点:自动驾驶的隐形主线在数据与验证
2026/10/22 3:51:46
基于DDPG的电力市场报价策略复现与训练调参实战
2026/10/22 3:51:46
实时网络同步技术实战:消息模型与协同编辑完整方案
2026/10/22 3:51:46
RTSP转HLS实战:.NET进程化稳定推流方案
2026/10/22 3:46:46
AI Agent搭建师进阶:从提示词调优到工程化能力跃迁
2026/10/22 0:01:24
电商智能运营体系从0到1:架构设计、落地步骤与避坑指南
2026/10/22 0:01:24
压缩空气储能与热网热惯性联合优化调度:热电联产系统建模与求解
2026/10/22 0:01:24
若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定
2026/10/21 19:05:16
OpenResearch实战:Claude Code、Codex、OpenCode、Cursor 工作流搭建与故障排查
2026/10/21 14:47:26
知识管理核弹:用Obsidian构建第二大脑的工程化方案(TaoToken 统一 Key 接入版)
2026/10/21 19:05:04
金融AI落地指南:从调研报告到技术选型与工程实践
2026/10/20 2:24:03
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/22 2:07:18
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/21 19:01:08
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)