第一次接触Kaggle的泰坦尼克号数据集时,我被这个看似简单却暗藏玄机的数据集深深吸引了。这份记录着1912年那场著名海难乘客信息的数据,包含了每位乘客的年龄、性别、舱位等级等基本信息。但真正让我着迷的是,如何从这些原始数据中挖掘出影响生存的关键因素。
记得刚开始分析时,我习惯性地先看整体数据分布。用pandas的info()和describe()快速扫描后,立刻发现了两个棘手问题:年龄字段有约20%的缺失值,而舱位信息缺失更严重,达到77%。这给我的第一个教训是:真实世界的数据从来不会完美,如何处理这些缺失值往往决定了模型的成败。
最初看到"Name"字段时,我差点直接把它删掉——毕竟名字看起来和生存率毫无关系。但仔细一看,每个名字前都有"Mr."、"Mrs."这样的称谓。灵机一动,我用正则表达式提取出这些称谓:
python复制data['Title'] = data['Name'].str.extract(' ([A-Za-z]+)\.', expand=False)
结果发现了有趣的现象:不同称谓对应的生存率差异巨大。"Master"(未成年男性)生存率高达57%,而"Mr."只有15%。这促使我创建了"称谓"这个新特征,用它来更精确地填充缺失的年龄值。
原始数据中有"SibSp"(兄弟姐妹/配偶数量)和"Parch"(父母/子女数量)两个字段。单独看它们与生存率的关系时,我发现一个矛盾现象:有1-2个家庭成员的人生存率较高,但大家庭(4人以上)生存率骤降。
于是我将两者相加创建了"FamilySize"特征,并衍生出"IsAlone"标志。可视化分析证实了我的猜想:中等规模家庭(2-4人)生存率最高,而单独旅行或大家庭生存率较低。这个发现完美印证了"互助但资源有限"的现实场景。
"Fare"(票价)字段的分布极不均匀,从0到512不等。直接使用原始值会导致模型过分关注极端值。我尝试了等宽分箱和等频分箱两种方法:
python复制# 等频分箱
data['FareBin'] = pd.qcut(data['Fare'], 4, labels=False)
# 等宽分箱
data['FareCat'] = pd.cut(data['Fare'], bins=[0, 10, 50, 100, 550], labels=False)
对比后发现等频分箱更能反映票价与生存率之间的非线性关系——不是票价越高生存率越高,而是存在明显的分界点。
在处理完所有特征后,我先用逻辑回归建立基线模型。这个选择很明智——逻辑回归简单直观,特征重要性一目了然:
python复制from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(max_iter=1000)
lr.fit(X_train, y_train)
模型准确率约80%,但查看系数时发现了问题:舱位等级(Pclass)和性别(Sex)的权重过高,其他特征几乎被忽略。这说明需要更好的特征缩放或正则化。
切换到GradientBoostingClassifier后,准确率提升到85%左右。更重要的是,通过feature_importances_看到了不同特征的真实贡献:
python复制gbc = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1)
gbc.fit(X_train, y_train)
出乎意料的是,"称谓"和"家庭规模"的重要性超过了原始年龄字段,验证了特征工程的价值。但调参过程中也踩过坑——过大的learning_rate导致早熟收敛,过深的树导致过拟合。
为了进一步提升,我尝试了简单的模型融合:
python复制from sklearn.ensemble import VotingClassifier
ensemble = VotingClassifier(estimators=[
('lr', LogisticRegression()),
('gbc', GradientBoostingClassifier())
], voting='soft')
融合模型在验证集上表现更好,但提交Kaggle后发现提升有限。这让我意识到:在特征工程足够好的情况下,单一强模型往往比简单融合更有效。
通过这个项目,我总结出特征工程的三个关键步骤:
新手常犯的几个错误我也没能幸免:
最后分享几个我反复使用的实用代码段:
python复制# 交叉验证查看特征重要性
from sklearn.inspection import permutation_importance
result = permutation_importance(gbc, X_test, y_test, n_repeats=10)
sorted_idx = result.importances_mean.argsort()
python复制# 可视化特征相关性
import seaborn as sns
corr = data.corr()
sns.heatmap(corr, annot=True, cmap='coolwarm')
泰坦尼克号项目最宝贵的不是最终的准确率,而是教会我如何像侦探一样从数据中寻找线索。每次当我发现一个新的特征组合能提升模型表现时,那种"啊哈"时刻的兴奋感,正是数据科学最迷人的地方。