泰坦尼克号生存预测:数据挖掘入门实战与特征工程全解析

📅 发布时间:2026/9/9 18:53:00
泰坦尼克号生存预测:数据挖掘入门实战与特征工程全解析
泰坦尼克号生存预测这个项目说它是数据挖掘入门的第一课基本没什么争议。国内外的数据科学课程、Kaggle新手赛、培训班毕业设计几乎都拿它当标准案例。原因很简单数据集不大、字段不复杂、但该有的数据挖掘流程一个都不少——数据清洗、特征工程、建模调参、结果评估全都涉及。尤其适合用Jupyter Notebook来做一边写代码一边跑结果整个过程清清楚楚。这篇内容我结合自己带项目、写报告的经验把一个完整的泰坦尼克号生存预测项目从头到尾拆开讲包括思路、代码、参数选择的原因以及那些你不在实际环境里踩一遍很难发现的坑。1. 项目整体设计与分析思路1.1 为什么泰坦尼克号是数据挖掘入门的经典题目很多人会觉得泰坦尼克号这个项目太老了、太简单了还有必要拿来做那么多文章吗但我说句实在话这个项目考验的东西恰恰是数据挖掘里面最核心、最容易忽略的基本功。先说数据集本身。泰坦尼克号数据集包含12个字段左右乘客ID、舱位等级、姓名、性别、年龄、船票信息、票价、客舱号、登船港口、是否存活。这个数据量不大891条训练数据、418条测试数据跑起来非常快几秒钟就能出结果特别适合在Jupyter Notebook里反复试验。但别小看这些字段每一个字段背后都藏着问题。比如缺失值Age字段有177个缺失Cabin字段有687个缺失Embarked有2个缺失。这就是真实世界的数据不可能整整齐齐给你。数据挖掘的第一步就是跟这些脏数据、缺数据打交道这比模型选型重要得多。我见过太多人一上来就跑XGBoost、神经网络结果分数还不如隔壁用逻辑回归的老哥问题就出在数据没处理干净。再说这个问题的业务背景。泰坦尼克号沉没是真实历史事件不同群体生存概率差异很大。妇女儿童优先上救生艇头等舱乘客离救生艇更近三等舱乘客被锁在底层。这些背景知识直接转化成特征工程的思路Sex、Age、Pclass这仨字段是预测生存的核心变量。这不是瞎猜是有业务逻辑支撑的判断。所以这个项目虽然小但它把一个完整数据挖掘项目的所有环节都囊括了能让你练到真正的“数据思维”而不仅仅是跑通一个模型。1.2 从项目标题看你拿到的这个交付物到底包含什么这个项目的标题是“基于机器学习的泰坦尼克号对生存者的预测”后面还标注了“自动发货”“Jupyter环境”“设计源文件万字报告讲解”“支持资料、图片参考、相关定制”。我帮你拆一下这里面的信息量。首先是“Jupyter环境”。这意味着整套代码是运行在Jupyter Notebook里的不是纯.py脚本。Jupyter的好处是能分单元格执行每一步都能看到中间结果特别适合教学和报告展示。你打开Notebook从上往下依次运行可以看到数据长什么样、缺失值怎么处理、每个特征跟生存率的关系整个分析过程都是可见的。然后是“设计源文件万字报告讲解”。这说明交付物分成三块源代码文件、详细的设计报告、配合报告的视频或文字讲解。这种交付结构对应的是数据挖掘项目的标准流程代码负责实现报告负责呈现分析思路和结论讲解负责把逻辑讲透。你拿到的不是一堆孤立的代码而是一套完整的项目展示方案。“支持资料、图片参考、相关定制”则是让你在写自己的报告或做二次开发的时候可以基于这个交付物调整。比如你的数据集不同或者你想换一个模型试试都可以在这个基础上改。这个交付物的价值不是“跑通”两个字而是它把从数据到结论的完整逻辑链给你铺好了。1.3 技术栈选择为什么选Python Jupyter而不是其他工具这个项目如果放在三年前很多人可能会用SPSS Modeler或者R语言来做。但现在Python已经成为数据挖掘的事实标准Jupyter Notebook则是交互式分析的默认环境这两个组合几乎没有争议。Python的优势在于它的机器学习生态太完整了。Pandas处理表格数据NumPy做数值计算Matplotlib和Seaborn画图Scikit-learn提供几乎所有的经典机器学习算法。更关键的是Python的代码可读性强语法接近自然语言哪怕你不写代码光看别人的Notebook也能猜出个大概意思。这就非常符合数据挖掘教学项目的要求。Jupyter Notebook本身的价值我多说两句。对于初学者来说Jupyter最大的好处是“试错成本低”。你可以只运行一个单元格看看输出结果再改改参数重新运行。这种即时反馈对理解数据非常重要。比如你画一张生存率跟年龄的关系图想看不同年龄段的分布几秒钟就能改出来这种效率是传统IDE没法比的。不过Jupyter也有坑最大的是“单元格执行顺序混乱”。你可以在第5个单元格修改了一个变量但第5个单元格是在第3个单元格之前运行的这就会导致变量状态不一致。所以我提一个建议每次跑完一轮分析最好点击“Kernel - Restart Run All”全部重跑一遍确保代码从上到下执行的结果是一致的。这个习惯能避免非常多莫名其妙的错误。1.4 搭建环境Anaconda一步到位我建议直接安装Anaconda而不是单独装Python再装Jupyter。Anaconda自带Python解释器、Jupyter Notebook、Spyder以及Pandas、NumPy、Scikit-learn这些数据科学常用的库一次性装好省去大量踩坑时间。装好之后在终端Mac/Linux或Anaconda PromptWindows里输入jupyter notebook就能启动Notebook服务浏览器会自动打开本地地址。如果你用的是Anaconda Navigator也可以直接在界面里点击Launch启动Jupyter Notebook效果一样。关于环境这块我有几个经验建议在项目目录下新建一个专门的文件夹启动Jupyter时直接进入这个文件夹避免在根目录下新建Notebook导致文件到处乱飞。可以用cd命令先进入目录再启动Jupyter或者在Jupyter主界面里切换路径。依赖库的版本要统一。不同版本的Pandas、Scikit-learn在API上有些差别代码可能在一个版本上跑通换到另一个版本就报错。最好固定版本比如用conda list查看当前环境版本或者在项目里配一个requirements.txt方便复现环境。如果你装了多套Python环境比如系统自带一个、Anaconda一个、虚拟环境一个启动Jupyter前先确认当前用的是哪个Python。终端里执行which pythonMac/Linux或where pythonWindows看一眼避免装错库、跑错环境这种低级错误。装好环境代码基本就能顺畅跑起来。下面进入正题数据分析的核心流程。2. 数据分析与特征工程这个项目的灵魂2.1 数据探索先搞懂数据长什么样再动手拿到数据的第一件事不是建模而是“看数据”。我通常会从几个维度去观察这决定了后面所有处理的思路。用Pandas加载数据先看前几行import pandas as pd train_data pd.read_csv(train.csv) test_data pd.read_csv(test.csv) print(train_data.shape) print(train_data.head())然后查看数据的基本信息train_data.info()info()会列出每个字段的非空数量、数据类型、内存占用。这一步可以快速定位缺失值。泰坦尼克号数据集里Age有177条缺失、Cabin有687条缺失、Embarked有2条缺失。缺失量和字段含义直接决定处理方式。再看统计描述train_data.describe()describe()输出的是数值型字段的统计信息包括均值、标准差、最小值、最大值、四分位数。这一步能让你快速发现异常值。比如Fare票价字段最小值为0而泰坦尼克号二等舱票价至少也要10英镑以上所以票价0大概率是数据缺失或者录入异常需要进一步排查。关于探索性数据分析我多说几句。EDA的核心是“质疑数据”。每一个异常值、每一个缺失值都要问一个为什么。不要急着填数先把数据背后的逻辑搞清楚。比如年龄缺失可能是因为船员登记信息不完整票价异常可能是因为家属乘员合并购票。这些背景知识能帮你做出更合理的处理决策。2.2 缺失值处理不是简单填空而是讲逻辑缺失值处理有几种常用方法删除、填充均值/中位数/众数、用模型预测、或者干脆一个单独的“缺失”类别。选择哪种方法取决于字段的业务含义和缺失比例。先说Age字段。年龄缺失177条占比约20%不算特别高但直接删除会损失信息。我见过两种常见处理方案用均值填充简单粗暴但会把年龄分布往中间推导致模型学不到真实的年龄分布特征。用中位数填充比均值稍微好一点但对极端值不敏感。泰坦尼克号这个项目里我比较推荐“基于其他特征分组填充”。具体做法是用Title称呼字段来推断年龄。比如“Mr.”的乘客平均年龄32岁“Miss.”的平均年龄22岁“Master.”男孩的平均年龄5岁。这个方法背后是有逻辑的不同社会身份、不同年龄段的人称呼方式本身就不同。用称呼分组填充年龄比全局均值填充更接近真实分布。# 提取称呼 train_data[Title] train_data[Name].str.extract( ([A-Za-z])\., expandFalse) # 按Title分组填充年龄 train_data[Age] train_data.groupby(Title)[Age].transform(lambda x: x.fillna(x.median()))再说Cabin字段。Cabin缺失率高达77%所以这个字段直接作为数值特征是不现实的。但Cabin也不是完全没用——它可以直接转化为一个布尔特征Cabin是否缺失。这个逻辑是有客舱号的乘客说明登记信息更完整可能是有组织地登船生存概率或许更高。实际验证下来这个特征确实有一定区分度。2.3 特征工程从原始字段中创造新特征特征工程是数据挖掘中最能体现功力的地方。同样的原始数据特征工程做得好模型精度能提升好几个百分点。泰坦尼克号项目里我常用这么几个特征工程思路。第一个是家庭成员数量。数据里有SibSp同行的兄弟姐妹/配偶数量和Parch同行的父母/子女数量。把两个字段加起来再减1就得到“家庭人数”train_data[FamilySize] train_data[SibSp] train_data[Parch] 1这个特征背后的业务逻辑是孤身一人出行的人在灾难来临时没有同伴帮助或约束生存率相对较低家庭人数太多也不行因为寻找家人会耽误逃生时间。实际分析发现FamilySize1的生存率最低FamilySize2到4的生存率最高FamilySize4的生存率又下降。这就能拆成一个新特征“是否小家庭”。第二个是称呼提取。从姓名中提取称呼不仅用于填充年龄本身也是一个好特征。可以把Mr、Mrs、Miss、Master这些称呼映射为数字或者进一步降维归类普通男性Mr、贵族男性Rare Male、已婚女性Mrs、贵族女性Rare Female、儿童Master、未婚女性Miss。第三个是客舱区域提取。Cabin字段虽然缺失多但非缺失部分的客舱号第一个字母代表区域比如C代表C区域E代表E区域。可以提取首字母作为特征或者干脆直接用“Cabin缺失/不缺失”这个二值特征。train_data[CabinKnown] train_data[Cabin].notna().astype(int)这几个特征各有用处实际做下来FamilySize和Title对模型精度的提升是最明显的。2.4 数据可视化用图说话快速验证特征有效性在Jupyter Notebook里做数据探索可视化是必不可少的一环。一张图比一堆数字更直观能快速验证你的假设还能在报告里直接当素材使用。常用的图包括import matplotlib.pyplot as plt import seaborn as sns # 不同性别下的生存率 sns.barplot(xSex, ySurvived, datatrain_data) plt.show() # 不同舱位等级下的生存率 sns.barplot(xPclass, ySurvived, datatrain_data) plt.show() # 年龄分布与生存状态 plt.figure(figsize(12, 6)) plt.hist([train_data[train_data[Survived]1][Age].dropna(), train_data[train_data[Survived]0][Age].dropna()], bins20, label[Survived, Not Survived]) plt.legend() plt.show()这些图的作用是让你直观看到女性生存率远高于男性头等舱生存率远高于三等舱小孩和中老年人在年龄分布上有明显差异。有了这些可视化的依据你在报告里写特征选择理由时就有了“看图说话”的底气而不是空口无凭。3. 模型训练与调优从基线模型到精度提升3.1 数据集划分与预处理在建模之前数据的预处理环节不能少。需要做几件事对类别特征做编码。Sex是字符串需要变成数值Embarked也是类别需要处理。对数值特征做标准化或归一化。虽然树模型不敏感但逻辑回归、SVM这类基于距离的模型对尺度敏感。编码的实现# 性别编码 train_data[Sex] train_data[Sex].map({male: 0, female: 1}) # 登船港口填充用众数 train_data[Embarked] train_data[Embarked].fillna(S) train_data[Embarked] train_data[Embarked].map({S: 0, C: 1, Q: 2})把特征和标签分开features [Pclass, Sex, Age, Fare, FamilySize, CabinKnown, Title] X train_data[features] y train_data[Survived]接下来划分训练集和验证集。这里要注意很多新手会直接把全部训练数据拿去训练最后用测试集提交。但这样你无法评估模型的泛化能力。正确的做法是从训练数据中再切出一部分作为验证集用验证集评估模型效果。我习惯用Scikit-learn自带的train_test_splitfrom sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val train_test_split(X, y, test_size0.2, random_state42, stratifyy)random_state固定为固定值保证实验可复现stratifyy 按标签比例分层抽样保证训练集和验证集中存活/未存活的比例一致避免因抽样不均匀导致评估偏差。3.2 基线模型选择先从逻辑回归开始不要一上来就上随机森林、XGBoost。一个合格的建模流程是先建立一个简单的基线模型看基准精度再慢慢叠加复杂度。基线模型选逻辑回归最合适原因有几个逻辑回归是线性模型解释性强你能看每个特征的系数知道模型关注什么。训练速度快几毫秒就出结果。逻辑回归的精度在这个数据集上其实不低通常能达到78%-80%左右已经是一个不错的基线。from sklearn.linear_model import LogisticRegression model LogisticRegression(max_iter1000) model.fit(X_train, y_train) train_acc model.score(X_train, y_train) val_acc model.score(X_val, y_val) print(f训练集精度: {train_acc:.4f}) print(f验证集精度: {val_acc:.4f})如果你的逻辑回归验证集精度低于75%说明前面的特征工程或数据清洗还有问题不建议继续往下走先回头检查数据。3.3 模型对比与选择不是越复杂越好有了基线模型之后再跑几组不同算法做对比。常用的候选算法包括随机森林Random Forest梯度提升树Gradient Boosting支持向量机SVMK近邻KNN在Notebook里可以一次跑完对比from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier from sklearn.svm import SVC from sklearn.neighbors import KNeighborsClassifier models { Logistic Regression: LogisticRegression(max_iter1000), Random Forest: RandomForestClassifier(random_state42), Gradient Boosting: GradientBoostingClassifier(random_state42), SVM: SVC(), KNN: KNeighborsClassifier() } for name, model in models.items(): model.fit(X_train, y_train) y_pred model.predict(X_val) val_score accuracy_score(y_val, y_pred) print(f{name}: {val_score:.4f})从实际经验来看泰坦尼克号数据集上随机森林和梯度提升树通常表现最好验证集精度能到80%-84%线性模型大概78%-80%KNN则不太稳定跟特征量纲和距离计算方式有很大关系。但这里要提醒一个事不同算法在同一个随机种子下表现不同单次对比有运气成分。建议用交叉验证而不是只跑一次划分。交叉验证把数据分成K份轮流用K-1份训练、1份验证最终取平均分结果要稳定得多。from sklearn.model_selection import cross_val_score scores cross_val_score(RandomForestClassifier(random_state42), X, y, cv5) print(f平均精度: {scores.mean():.4f} (±{scores.std():.4f}))3.4 超参数调优GridSearchCV和RandomizedSearchCV选定模型之后就到了超参数调优环节。这一步也是提分的关键但要注意控制度超参数调得过狠容易过拟合。随机森林有几个关键超参数n_estimators树的数量越大越稳定但训练时间也越长。max_depth树的最大深度控制模型复杂度。min_samples_split节点分裂所需最小样本数防止过拟合。max_features每次分裂时考虑的特征数量。调参用GridSearchCVfrom sklearn.model_selection import GridSearchCV param_grid { n_estimators: [100, 200, 300], max_depth: [5, 10, 15], min_samples_split: [2, 5, 10] } grid_search GridSearchCV(RandomForestClassifier(random_state42), param_grid, cv5, scoringaccuracy) grid_search.fit(X_train, y_train) print(最佳参数:, grid_search.best_params_) print(最佳精度:, grid_search.best_score_)我提三个实战经验不要只盯验证集精度还要看训练集精度和验证集精度的差距。如果训练集98%、验证集81%说明过拟合了需要限制模型复杂度。调参逻辑是在保证验证集精度不掉的条件下尽量压缩训练集精度和验证集精度之间的差距。网格搜索的计算量是组合增长的。n_estimators有3个选项、max_depth有3个、min_samples_split有3个那就是27组参数组合每组还要跑5折交叉验证也就是135次训练。泰坦尼克号数据量小还扛得住换大数据集就得用RandomizedSearchCV随机搜索或者Optuna去优化。随机种子要固定。调参过程中如果你每轮都换random_state那你没法判断精度变化到底是因为参数调优有效还是随机数变化带来的噪声。3.5 模型集成简单有效的提分手段调完超参数如果还想进一步提升精度可以试试集成学习。最简单有效的是投票法把多种模型的结果综合起来少数服从多数。from sklearn.ensemble import VotingClassifier voting_clf VotingClassifier( estimators[ (lr, LogisticRegression(max_iter1000)), (rf, RandomForestClassifier(random_state42)), (gb, GradientBoostingClassifier(random_state42)) ], votingsoft ) voting_clf.fit(X_train, y_train) y_pred voting_clf.predict(X_val)votingsoft是软投票会考虑每个类别的预测概率votinghard是硬投票只看最终类别。实际效果来看软投票通常更稳定因为保有更多信息。泰坦尼克号这个项目有个特点数据量小模型能力上限有限。Kaggle上顶尖选手的成绩大概在81%-82%左右公开数据集再往上就非常难了。所以不要纠结从82%提到83%那个边际收益很低了。更重要的是理解整个流程。4. 常见问题与排查技巧实录4.1 Jupyter Notebook 环境问题这个项目是在Jupyter Notebook里运行的环境问题是最容易卡住的。我列几个高频问题。问题一Jupyter启动后页面是空白的大概率是端口被占用或者缓存问题。先关掉所有浏览器标签页重新打开Jupyter如果还不行在终端里执行jupyter notebook --port8889换个端口试试。还有可能是Windows防火墙弹窗没有允许Python通过局域网如果本机能开、局域网其他电脑不能访问就去防火墙配置里开放端口。问题二Pandas、Sklearn导入失败报错ModuleNotFoundError: No module named pandas。这说明当前Jupyter内核使用的Python环境不是你安装库的那个环境。特别是用Anaconda 系统Python混装时这种问题非常常见。解决办法是在Notebook里执行以下代码查看当前Python路径import sys print(sys.executable)确认一下是不是Anaconda的Python路径。如果不是在启动Jupyter之前先激活对应的conda环境conda activate your_env_name jupyter notebook4.2 数据预处理常见的坑问题一特征编码后训练报错比如报错could not convert string to float: male。原因很简单字符串特征没有转成数值。需要注意Pandas里的category类型有时候不会自动转换需要显式调用map()或LabelEncoder。问题二训练集和测试集特征不一致这个我也踩过。处理训练集时新建了列但测试集没有同步处理最后在测试集上预测时特征数量对不上直接报错。解决办法是保证训练集和测试集做相同的特征工程操作最好把特征工程封装成函数训练集和测试集都调用同一个函数。比如def feature_engineering(df): df df.copy() df[FamilySize] df[SibSp] df[Parch] 1 df[CabinKnown] df[Cabin].notna().astype(int) df[Title] df[Name].str.extract( ([A-Za-z])\., expandFalse) return df train_data feature_engineering(train_data) test_data feature_engineering(test_data)问题三K折交叉验证时目标列泄漏目标列泄漏指的是训练和验证数据之间特征信息跨越了边界让模型“偷看”了答案。最常见的情况是在全量数据上做了填充然后再划分训练集和验证集。规范化流程是先划分数据再在训练集中的一部分拟合填充值然后对验证集用同样的填充值填充。4.3 模型过拟合的判断与处理泰坦尼克号这个项目过拟合其实是很容易发生的。因为特征数量不多但模型复杂度可以拉得很高。判断方法很简单训练集精度明显高于验证集精度差值超过5个百分点就要警惕。处理过拟合的方法有限制模型复杂度降低树的深度、增大min_samples_split、增加正则化参数。增加数据泰坦尼克号数据集就这么多可以通过交叉验证来充分利用数据。特征选择删掉一些噪声特征。有些特征比如PassengerId是纯ID对预测毫无帮助还可能干扰模型。需要注意不是所有特征都要进模型。每次新加一个特征先看它在验证集上的效果是否提升。如果没提升果断去掉。5. 报告撰写与项目讲解让交付物“有料”5.1 万字报告怎么写结构重于文采一个数据挖掘项目的报告不需要华丽的辞藻但结构必须清晰。我建议按这个顺序组织摘要项目背景、数据来源、核心方法、主要结论。半页纸说清楚。数据探索与分析数据字典、缺失值统计、特征分布、可视化图表。特征工程每个特征是怎么构造的依据是什么对应代码。模型选择与评估不同模型的实验结果表格、交叉验证得分、最终选型理由。结论与优化方向项目结论、局限性、后续改进空间。写报告最忌讳只贴代码和结果而缺少“为什么这么做”的说明。比如你选随机森林不能只说“因为效果最好”你应该写逻辑回归作为基线达到78%随机森林通过交叉验证平均达到82%且在相同验证集划分下表现稳定因此选择随机森林作为最终模型。这样读者才能跟着你的思路走。5.2 讲解时的核心表达技巧如果你拿这个项目去做答辩或者讲解我建议重点讲三个地方。一是处理缺失值的逻辑。面试官或老师最爱问的就是为什么年龄缺失用均值填充而不是删除你如果回答“因为删除会损失信息”这是标准答案但要再往深一层为什么用Title分组填充因为不同社会身份年龄差异大直接用全局均值会缩小年龄的差异性影响模型对年龄特征的敏感度。二是特征工程的思路。讲你怎么从原始字段中想到FamilySize、Title这些新特征。这里体现的是数据敏感度也是项目里最能加分的地方。三是模型的泛化能力。讲清楚你的方案在训练集和验证集上的表现差异以及你如何防止过拟合。然后说明虽然最终精度是82%但这个项目的核心价值不在于精度指标本身而在于你建立了一套完整的数据处理和分析方法论这套方法论可以迁移到其他项目上。5.3 后续扩展方向项目还能怎么改我已经把泰坦尼克号这个项目跑通了很多遍但每次再做还是会发现新东西。如果你想给自己加点挑战可以往这几个方向扩展使用更复杂的特征工程从姓名中提取更多社会身份信息从客舱号中提取位置信息。尝试深度学习模型用简单的多层感知机MLP看看能否超过树模型。这个项目比较小深度模型的优势不太明显但可以作为一个对比实验。换成其他经典数据集比如Titanic的“进阶版”——房价预测House Prices或者银行营销数据集Bank Marketing套路一样但特征更丰富、数据处理难度更大。6. 实操总结与个人经验泰坦尼克号生存预测这个项目每次带人做、每次写报告我都觉得它不是一个“做完就扔”的项目。它简单但外延很深几乎涵盖了数据挖掘流程中的所有核心环节。我个人的建议是这个项目做完之后不要急着炫耀你的精度数字而是花时间把每一个步骤的“为什么”想清楚。论文式报告和随笔式报告的区别就在这里。有几个实际经验再啰嗦一遍Jupyter Notebook里要养成“Restart Run All”的习惯确保结果可复现。特征工程是提分的最关键环节花在特征上的时间至少和建模调参一样多。不要只看单次划分的精度用交叉验证评估模型结果更可靠。模型不是越复杂越好泰坦尼克号这个项目随机森林已经接近上限暴力调参的边际收益很低。最后务必保证代码的随机种子固定让实验结果可复现。这个习惯会帮你省下巨大的解释成本。最后再分享一个小技巧。写报告的时候把Notebook里每个关键单元格的输出截图整理到报告里比如缺失值统计图、生存率对比图、模型精度对比表。图比文字更有说服力答辩时能让评委一眼看出你用数据思考而不是空谈。数据挖掘项目做得好不好很大程度上取决于你有没有把“数据的故事”讲清楚而不仅仅是模型的准确率数字有多高。