泰坦尼克号生存预测 - MACHINE-LEARNING教程

泰坦尼克号生存预测

如果你刚开始学习机器学习,可能会觉得那些复杂的算法和数学公式离现实世界很遥远。但今天,我们将通过一个经典案例——泰坦尼克号生存预测,来亲手体验一次完整的机器学习项目流程。

泰坦尼克号数据集是机器学习领域最著名的入门项目之一,它基于 1912 年泰坦尼克号沉船事件中乘客的真实信息。我们的目标是: 根据乘客的年龄、性别、船票等级等信息,构建一个模型来预测他们是否能在灾难中幸存

这个项目之所以经典,是因为它完美地涵盖了机器学习项目的核心步骤:

  1. 数据理解与探索
  2. 数据清洗与预处理
  3. 特征工程
  4. 模型选择与训练
  5. 模型评估与优化

通过这个实战案例,你将不再只是阅读理论,而是真正理解如何将机器学习应用于解决实际问题。


第一步:理解我们的数据

在动手写任何代码之前,我们必须先了解手头的数据。泰坦尼克号数据集通常包含以下字段(特征):

字段名 说明 数据类型 备注
PassengerId 乘客ID 整数 唯一标识符,对预测无帮助
Survived 是否幸存 整数 (0/1) 目标变量 ,0=遇难,1=幸存
Pclass 船票等级 整数 (1,2,3) 1=头等舱,2=二等舱,3=三等舱
Name 乘客姓名 字符串 包含称谓(如 Mr., Miss.),可提取新特征
Sex 性别 字符串 male female
Age 年龄 浮点数 有部分缺失值
SibSp 同行的兄弟姐妹/配偶数量 整数
Parch 同行的父母/子女数量 整数
Ticket 船票编号 字符串 结构复杂,信息量可能有限
Fare 船票价格 浮点数
Cabin 船舱号 字符串 大量缺失值,但首字母可能代表船舱区域
Embarked 登船港口 字符串 C=Cherbourg, Q=Queenstown, S=Southampton

核心洞察 :从历史知识我们知道,当时奉行"妇女和儿童优先"的原则,且头等舱乘客有优先使用救生艇的权利。因此,我们预期 Sex , Age , Pclass 等特征将对预测结果产生重大影响。


第二步:数据清洗与预处理

原始数据几乎从不完美。

数据清洗就像为模型准备高质量的食材,这一步至关重要。

把以下数据存储到 train.csv 文件中:

PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked

1,0,3,"Braund, Mr. Owen Harris",male,22,1,0,A/5 21171,7.25,,S

2,1,1,"Cumings, Mrs. John Bradley",female,38,1,0,PC 17599,71.2833,C85,C

3,1,3,"Heikkinen, Miss. Laina",female,26,0,0,STON/O2. 3101282,7.925,,S

4,1,1,"Futrelle, Mrs. Jacques Heath",female,35,1,0,113803,53.1,C123,S

5,0,3,"Allen, Mr. William Henry",male,35,0,0,373450,8.05,,S

6,0,3,"Moran, Mr. James",male,,0,0,330877,8.4583,,Q

7,0,1,"McCarthy, Mr. Timothy J",male,54,0,0,17463,51.8625,E46,S

8,0,3,"Palsson, Master. Gosta Leonard",male,2,3,1,349909,21.075,,S

9,1,3,"Johnson, Mrs. Oscar W",female,27,0,2,347742,11.1333,,S

10,1,2,"Nasser, Mrs. Nicholas",female,14,1,0,237736,30.0708,,C

把以下数据存储到 test.csv 文件中:

PassengerId,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked

11,3,"Kelly, Mr. James",male,34.5,0,0,330911,7.8292,,Q

12,3,"Wilkes, Mrs. James",female,47,1,0,363272,7,,S

13,2,"Myles, Mr. Thomas Francis",male,62,0,0,240276,9.6875,,Q

14,3,"Dwyer, Miss. Ellen",female,18,0,0,330959,7.75,,Q

15,1,"Jones, Mr. Charles",male,,1,0,PC 17603,82.1708,B28,C

我们将使用 Python 的 pandas numpy 库来完成这项工作。

示例代码
# 导入必要的库importpandasaspdimportnumpyasnp# 加载数据train_data=pd.read_csv('train.csv')# 训练集,包含目标变量 Survivedtest_data=pd.read_csv('test.csv')# 测试集,不包含 Survived,用于最终评估# 1. 初步查看数据print("训练集形状:",train_data.shape)print(train_data.info())# 查看数据类型和缺失情况print(train_data.head())# 查看前几行数据

输出:

训练集形状: (10, 12)

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 10 entries, 0 to 9

Data columns (total 12 columns):

 #   Column       Non-Null Count  Dtype  

---  ------       --------------  -----  

 0   PassengerId  10 non-null     int64  

 1   Survived     10 non-null     int64  

 2   Pclass       10 non-null     int64  

 3   Name         10 non-null     object 

 4   Sex          10 non-null     object 

 5   Age          9 non-null      float64

 6   SibSp        10 non-null     int64  

 7   Parch        10 non-null     int64  

 8   Ticket       10 non-null     object 

 9   Fare         10 non-null     float64

 10  Cabin        3 non-null      object 

 11  Embarked     10 non-null     object 

dtypes: float64(2), int64(5), object(5)

memory usage: 1.1+ KB

None

   PassengerId  Survived  Pclass                          Name     Sex  ...  Parch            Ticket     Fare Cabin  Embarked

0            1         0       3       Braund, Mr. Owen Harris    male  ...      0         A/5 21171   7.2500   NaN         S

1            2         1       1    Cumings, Mrs. John Bradley  female  ...      0          PC 17599  71.2833   C85         C

2            3         1       3        Heikkinen, Miss. Laina  female  ...      0  STON/O2. 3101282   7.9250   NaN         S

3            4         1       1  Futrelle, Mrs. Jacques Heath  female  ...      0            113803  53.1000  C123         S

4            5         0       3      Allen, Mr. William Henry    male  ...      0            373450   8.0500   NaN         S



[5 rows x 12 columns]

运行上述代码后,你可能会发现两个主要问题: 缺失值 非数值型数据

处理缺失值

示例代码
# 检查各列缺失值的数量print(train_data.isnull().sum())# 处理 Age(年龄):中位数填充train_data['Age']=train_data['Age'].fillna(train_data['Age'].median())test_data['Age']=test_data['Age'].fillna(test_data['Age'].median())# 处理 Embarked(登船港口):众数填充most_common_port=train_data['Embarked'].mode()[0]train_data['Embarked']=train_data['Embarked'].fillna(most_common_port)test_data['Embarked']=test_data['Embarked'].fillna(most_common_port)# 处理 Fare(船票价格):测试集test_data['Fare']=test_data['Fare'].fillna(test_data['Fare'].median())# 处理 Cabin(船舱):直接删除train_data.drop(columns=['Cabin'],inplace=True)test_data.drop(columns=['Cabin'],inplace=True)

转换非数值型数据

机器学习模型通常只能处理数值。我们需要将 Sex Embarked 这样的文本列转换为数字。

示例代码
# 将 Sex 列转换为数值:female -> 0, male -> 1train_data['Sex']=train_data['Sex'].map({'female':0,'male':1})test_data['Sex']=test_data['Sex'].map({'female':0,'male':1})# 将 Embarked 列转换为数值(独热编码 One-Hot Encoding)# 因为港口之间没有大小顺序,所以不适合用 0,1,2 简单映射train_data=pd.get_dummies(train_data,columns=['Embarked'])test_data=pd.get_dummies(test_data,columns=['Embarked'])

第三步:特征工程

特征工程是机器学习中的"魔法",它通过创造或转换特征来帮助模型更好地学习。我们从 Name 列中提取"称谓"就是一个经典例子。

示例代码
# 从 Name 列中提取称谓(如 Mr., Mrs., Miss., Master.)# 称谓往往能反映年龄、社会地位和性别,可能影响获救优先级train_data['Title']=train_data['Name'].str.extract(' ([A-Za-z]+)\.',expand=False)test_data['Title']=test_data['Name'].str.extract(' ([A-Za-z]+)\.',expand=False)# 查看有哪些称谓print(pd.crosstab(train_data['Title'],train_data['Sex']))# 将不常见的称谓归类为'Rare'title_mapping={'Mr':'Mr','Miss':'Miss','Mrs':'Mrs','Master':'Master','Dr':'Rare','Rev':'Rare','Col':'Rare','Major':'Rare','Mlle':'Miss','Countess':'Rare','Ms':'Miss','Lady':'Rare','Jonkheer':'Rare','Don':'Rare','Dona':'Rare','Mme':'Mrs','Capt':'Rare','Sir':'Rare'}train_data['Title']=train_data['Title'].map(title_mapping)test_data['Title']=test_data['Title'].map(title_mapping)# 将处理后的 Title 列也进行独热编码train_data=pd.get_dummies(train_data,columns=['Title'])test_data=pd.get_dummies(test_data,columns=['Title'])# 创建新特征:家庭规模train_data['FamilySize']=train_data['SibSp']+ train_data['Parch']+1test_data['FamilySize']=test_data['SibSp']+ test_data['Parch']+1# 创建新特征:是否独自一人train_data['IsAlone']=(train_data['FamilySize']==1).astype(int)test_data['IsAlone']=(test_data['FamilySize']==1).astype(int)# 删除不再需要的原始列columns_to_drop=['PassengerId','Name','Ticket','SibSp','Parch']train_data.drop(columns_to_drop,axis=1,inplace=True)test_passenger_ids=test_data['PassengerId']# 保存测试集ID用于后续提交test_data.drop(columns_to_drop,axis=1,inplace=True)print("特征工程后的训练集列名:",train_data.columns.tolist())

第四步:选择与训练模型

现在,我们有了干净且富含信息的数值数据。接下来,我们将其分为 特征 (X) 目标变量 (y) ,然后选择一个模型进行训练。

我们将从简单且高效的 随机森林 (Random Forest) 模型开始。

示例代码
# 导入机器学习库fromsklearn.model_selectionimporttrain_test_splitfromsklearn.ensembleimportRandomForestClassifierfromsklearn.metricsimportaccuracy_score# 准备数据# X 是特征矩阵,y 是我们要预测的目标向量X=train_data.drop('Survived',axis=1)y=train_data['Survived']# 为了在训练过程中评估模型性能,我们将数据分为训练集和验证集# test_size=0.2 表示 20% 的数据用于验证,80% 用于训练# random_state 是一个随机种子,确保每次分割的结果一致X_train,X_val,y_train,y_val=train_test_split(X,y,test_size=0.2,random_state=42)# 初始化随机森林分类器# n_estimators: 森林中树的数量# max_depth: 树的最大深度,控制模型复杂度,防止过拟合# random_state: 确保结果可复现model=RandomForestClassifier(n_estimators=100,max_depth=5,random_state=42)# 训练模型(让模型从数据中学习规律)model.fit(X_train,y_train)# 在验证集上进行预测y_pred=model.predict(X_val)# 评估模型准确率accuracy=accuracy_score(y_val,y_pred)print(f"模型在验证集上的准确率为:{accuracy:.4f} (即 {accuracy*100:.2f}%)")

第五步:模型评估、优化与提交

评估与优化

一次训练的结果可能不是最优的。我们可以通过以下方式改进:

  1. 调整模型参数 :例如,尝试不同的 n_estimators max_depth
  2. 尝试其他模型 :比如逻辑回归、支持向量机、梯度提升树等。
  3. 进一步的特征工程 :比如对 Age Fare 进行分箱处理。
示例代码
# 示例:尝试不同的最大深度fordepthin[3,5,10,None]:# None 表示不限制深度model_temp=RandomForestClassifier(n_estimators=100,max_depth=depth,random_state=42)model_temp.fit(X_train,y_train)y_pred_temp=model_temp.predict(X_val)acc=accuracy_score(y_val,y_pred_temp)print(f"max_depth={depth} 时,验证集准确率:{acc:.4f}")

特征重要性分析

随机森林可以告诉我们哪些特征对预测贡献最大。

示例代码
# 获取特征重要性feature_importances=pd.DataFrame({'feature': X_train.columns,'importance': model.feature_importances_}).sort_values('importance',ascending=False)print("特征重要性排名:")print(feature_importances)

你可能会发现 Sex , Fare (关联 Pclass ), Age , Title 是最重要的特征,这与我们的历史直觉相符。

在测试集上生成最终预测

当我们对模型性能满意后,就用全部训练数据重新训练,并对真正的测试集进行预测。

示例代码
# 使用全部训练数据重新训练最终模型final_model=RandomForestClassifier(n_estimators=100,max_depth=5,random_state=42)final_model.fit(X,y)# 这次使用全部训练数据 X, y# 确保测试集的特征列与训练集完全一致(顺序和列数)# pd.get_dummies 可能导致训练集和测试集列数不同(如果某个类别只在一方出现)# 这里我们需要对齐列。一个简单的方法是先合并再分割,但更稳健的做法是确保编码一致性。# 为简化,假设我们处理后的测试集列已对齐。final_predictions=final_model.predict(test_data)# 创建提交文件submission=pd.DataFrame({'PassengerId': test_passenger_ids,'Survived': final_predictions})submission.to_csv('my_titanic_submission.csv',index=False)print("预测结果已保存至 'my_titanic_submission.csv',可以提交到Kaggle平台查看排名!")

总结与项目流程图

我们已经完成了一个完整的机器学习管道,用流程图回顾整个过程:

通过这个实战项目,你不仅学会了 pandas 进行数据处理、 sklearn 构建模型的技巧,更重要的是,你掌握了 解决一个机器学习问题的标准思路

这个流程——从数据理解到模型部署——是绝大多数数据科学项目的核心。