Titanic - Machine Learning from Disaster - KG-0x000

Baobao0824

泰坦尼克号——灾难中的机器学习是kaggle中最基本的比赛。我们需要构建一个预测模型,来回答“哪些类型的人更有可能存活”。我们会获得两个数据集,包括乘客信息,例如姓名、年龄、性别和社会经济阶层等。训练集为train.csv,测试集为test.csv,训练集包括了乘客的幸存情况而测试集没有。最后我们要提交的csv文件有两列,第一列是PassengerId,第二列是Survived,如果幸存就是1否则就是0。

交代完这些信息之后我们可以开始了。先在main/1中把对应的内容下载好。然后我们在main/2中可以查看对应的数据。我们可以看到,每个乘客除了PassengerIdSurvived之外,还有很多其他的变量。

1
2
3
4
5
6
7
8
# main/1
import kagglehub

# Download latest version
path = kagglehub.competition_download('titanic')

print("Path to competition files:", path)
# Path to competition files: /Users/baohan/.cache/kagglehub/competitions/titanic
1
2
3
4
5
6
7
8
9
10
11
# main/2
from pathlib import Path
import pandas as pd

train_path = Path(path) / 'train.csv'
test_path = Path(path) / 'test.csv'

train_dataset = pd.read_csv(train_path)
test_dataset = pd.read_csv(test_path)

train_dataset

  • Pclass,票务等级,1-3分别代表1到3级。
  • Name,姓名。
  • Sex,性别,分别用malefemale来表示。
  • Age,年龄。
  • SibSp,在船上的兄弟姐妹或配偶数量。
  • Parch,在船上的父母或子女数量。
  • Ticket,票据编号。
  • Fare,乘客票价。
  • Cabin,仓号。
  • Embarked,登船港口,可能为C(谢尔堡)、Q(昆斯顿)和S(南安普顿)

我们准备选用决策树模型来解决问题。什么是决策树呢?假设我们要判断一个水果是不是苹果,那么我们很容易画出来一棵这样的树来进行判断。

1
2
3
4
5
6
7
8
9
          颜色?
/ \
红色 其他
/
大小?
/ \
大 小

苹果

这就是一棵决策树。但是这个变量太多了。我们先问什么呢?这就是机器要做的事情。我们尝试所有的特征,从中选择效果最好的那一个作为判断特征。那么如何判断效果是否最好呢?只要这一刀切下去,分的两边很纯(一边全是苹果、另一边全不是苹果),就说明这个特征判断的效果好。然后在第二层继续选择所有的特征判断,以此类推,直到能分出所有的元素为止。这里的“纯不纯”用的是基尼指数进行判断,这个指数越小那么分的两边就越纯。

那么有人就会问了,为什么第二层不把第一层已经切过的那一刀排除掉呢?这个就和你选择的算法有关了。如果是ID3算法的话,一个离散特征用了之后就不能再用了,而如果是scilearn默认的CART算法,就会重复使用。但是由于每一个下一层的节点所接收的全都是父节点分完的元素,所以大多数时候离散的特征再分是没有意义的,而一些连续的特征就是可以再分的,比如我第一层问工资大于3000,第二层还可以接着问工资大于5000。

那么如果我们要用决策树来进行这个数据集的训练,那么我们需要先挑选到底要用什么特征吗?其实并不用,因为决策树天然就具备筛选特征的能力,毕竟如果拿姓名当特征什么都分不开,那么自然他就不用了。但是我们这个数据不是很干净,里面有很多需要清洗的,因为甚至你能看到有年龄为NaN的,这个可不行。main/3展示了Age列一共有177个NaN。这个特征很重要,必须保留,所以我们可以直接用中位数来填充(见main/4)。除此之外,main/5展示了Cabin的缺失数量,一共有687个缺失,实在是太多了,因此考虑直接将这一列删掉(见main/6),接下来,在main/7中我们发现Embarked只有两个缺失,我们只需要将最多次数的港口填进来就可以了(见main/8)。

1
2
3
# main/3
train_dataset['Age'].isna().sum()
# np.int64(177)
1
2
3
# main/4
train_dataset['Age'] = train_dataset['Age'].fillna(train_dataset['Age'].median())
train_dataset

1
2
3
# main/5
train_dataset['Cabin'].isna().sum()
# np.int64(687)
1
2
3
# main/6
train_dataset = train_dataset.drop(columns=['Cabin'])
train_dataset

1
2
# main/7
train_dataset.isna().sum()
1
2
3
4
5
6
7
8
9
10
11
12
PassengerId    0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Embarked 2
dtype: int64
1
2
3
# main/8
train_dataset['Embarked'] = train_dataset['Embarked'].fillna(train_dataset['Embarked'].mode()[0])
train_dataset.isna().sum()
1
2
3
4
5
6
7
8
9
10
11
12
PassengerId    0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Embarked 0
dtype: int64

那么我们就可以直接传入一个决策树进行训练了吗?实际上并不是。通常来讲决策树只能支持数值型的变量值,我们需要将值为字符串的特征值全部都改为数值。

  • 对于Name列,我们应该选择直接删掉,毕竟这个很难映射。
  • 对于Sex列,我们可以把male映射为1,female映射为0,当然反过来也是可以的。
  • 对于Ticket列,也可以直接删掉,Pclass已经能够代替他实现一部分功能了。
  • 对于Embarked列,我们也可以进行映射,例如S对应0,C对应1,Q对应2。
  • 对于PassengerId列,我们也应该删除,因为没有预测价值,这个我们在X_train中再去掉,在train_dataset中先保留。

main/9所示,我们使用DataFrame.map方法来将字符串映射数值中,同时删除掉一系列的字符串列。接下来就可以设定X_trainY_train并开始训练了。决策树这种基本的模型在sklearn.tree中,叫做DecisionTreeClassifier。在main/10中可以看到,我们把SurvivedPassengerId去掉之后的X_train和只有结果的Y_train,我们在main/11中构建一个决策树并开始训练(使用fit方法)。

1
2
3
4
5
6
7
8
9
10
11
12
# main/9
train_dataset = train_dataset.drop(columns=['Name','Ticket'])
train_dataset['Sex'] = train_dataset['Sex'].map({
'male': 1,
'female': 0
})
train_dataset['Embarked'] = train_dataset['Embarked'].map({
"S": 0,
"C": 1,
"Q": 2
})
train_dataset

1
2
3
4
5
6
7
# main/10
from sklearn.tree import DecisionTreeClassifier

X_train = train_dataset.drop(columns=['PassengerId','Survived'])
Y_train = train_dataset['Survived']

X_train, Y_train
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(     Pclass  Sex   Age  SibSp  Parch     Fare  Embarked
0 3 1 22.0 1 0 7.2500 0
1 1 0 38.0 1 0 71.2833 1
2 3 0 26.0 0 0 7.9250 0
3 1 0 35.0 1 0 53.1000 0
4 3 1 35.0 0 0 8.0500 0
.. ... ... ... ... ... ... ...
886 2 1 27.0 0 0 13.0000 0
887 1 0 19.0 0 0 30.0000 0
888 3 0 28.0 1 2 23.4500 0
889 1 1 26.0 0 0 30.0000 1
890 3 1 32.0 0 0 7.7500 2

[891 rows x 7 columns],
0 0
1 1
2 1
3 1
4 0
..
886 0
887 1
888 0
889 1
890 0
Name: Survived, Length: 891, dtype: int64)
1
2
3
# main/11
model = DecisionTreeClassifier()
model.fit(X=X_train,y=Y_train)

下次修订的时候就来解释一下这些参数的作用
下次修订的时候就来解释一下这些参数的作用

接下来,我们在main/12中查看test_dataset的样子,并在main/13中对其进行清洗,构建出X_test来。然后就可以在main/14中进行预测了。可以看到pred的值是一个0或者1的array。我们在main/15中查看需要提交的数据格式,然后在main/16中弄一个变量来符合提交格式。

1
2
# main/12
test_dataset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# main/13
test_dataset = test_dataset.drop(columns=['Name','Cabin','Ticket'])
test_dataset['Embarked'] = test_dataset['Embarked'].fillna(test_dataset['Embarked'].mode()[0])
test_dataset['Age'] = test_dataset['Age'].fillna(test_dataset['Age'].median())
test_dataset['Sex'] = test_dataset['Sex'].map({
'male': 1,
'female': 0
})
test_dataset['Embarked'] = test_dataset['Embarked'].map({
'S': 0,
'C': 1,
'Q': 2
})
X_test = test_dataset.drop(columns=['PassengerId'])
X_test

1
2
3
# main/14
pred = model.predict(X_test)
pred
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
array([0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0,
1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1,
1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,
0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0,
1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1])
1
2
3
4
# main/15
gender_submission_path = Path(path) / 'gender_submission.csv'
gender_submission = pd.read_csv(gender_submission_path)
gender_submission

1
2
3
4
5
6
# main/16
y_test = pd.DataFrame({
"PassengerId" : test_dataset['PassengerId'],
"Survived": pred
})
y_test

最后,我们在main/17中保存为result.csv文件进行提交。

1
2
# main/17
y_test.to_csv('result.csv',index=False)

这次的提交结果为0.70334,只能排到11972名。不过进一步优化也没什么动力了,因为这个模型太简单了,我们主要是为了了解整个Kaggle的流程,以及了解各种不同的机器学习方法。

  • 标题: Titanic - Machine Learning from Disaster - KG-0x000
  • 作者: Baobao0824
  • 创建于 : 2026-07-03 10:58:54
  • 更新于 : 2026-07-03 11:29:17
  • 链接: https://blog.baobao0824.top//Kaggle/KG-0x000/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
Titanic - Machine Learning from Disaster - KG-0x000