用机器学习判断泰坦尼克上的乘客是否幸存?

这里我们以泰坦尼克号幸存者数据集进行第一个机器学习项目

数据集的下载地址为:

titanic_train.csv

导包,导入数据集并分割数据集

import pandas
from sklearn.model_selection import train_test_split

titanic = pandas.read_csv('./titanic_train.csv')

输出输出的前几行,查看各列属性等

titanic.head()
数据前5行

下面简单介绍一下各列属性的含义,详细信息请见链接:https://www.kaggle.com/c/titanic/data

VariableDefinitionKey
survival Survival 0 = No, 1 = Yes
pclass Ticket class 1 = 1st, 2 = 2nd, 3 = 3rd
sex Sex
Age Age in years
sibsp # of siblings / spouses aboard the Titanic
parch # of parents / children aboard the Titanic
ticket Ticket number
fare Passenger fare
cabin Cabin number
embarked Port of Embarkation C = Cherbourg, Q = Queenstown, S = Southampton

下一步分离数据特征和预测目标

y = titanic['Survived']
x = titanic.drop(['Survived'], axis=1)

print(x.shape)
print(y.shape)
(891, 11)
(891,)

统计缺失数据,数据类型等情况。

print(x.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 11 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Pclass       891 non-null    int64  
 2   Name         891 non-null    object 
 3   Sex          891 non-null    object 
 4   Age          714 non-null    float64
 5   SibSp        891 non-null    int64  
 6   Parch        891 non-null    int64  
 7   Ticket       891 non-null    object 
 8   Fare         891 non-null    float64
 9   Cabin        204 non-null    object 
 10  Embarked     889 non-null    object 
dtypes: float64(2), int64(4), object(5)
memory usage: 76.7+ KB
None

从上面的输出信息可以看到:

  1. 数据中一共有11个属性列,891个训练样本。
  2. 这些数据类型中有2个属性的数据类型为float,4个属性的数据类型为int,5个属性的数据类型为字符串类型,即为分类变量。
  3. 这些属性列中,Age属性,Cabin属性和Embarked属性列有空值,需要进一步处理。

下面对空值进行处理,数据类型和文本类型的处理手段不同。
Age属性使用已有的年龄属性的平均值进行填充。
字符串类型的空值统一标记为Unknown

x['Age'].fillna(x['Age'].mean(), inplace=True)
x.fillna('Unknown', inplace=True)

print(x.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 11 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Pclass       891 non-null    int64  
 2   Name         891 non-null    object 
 3   Sex          891 non-null    object 
 4   Age          891 non-null    float64
 5   SibSp        891 non-null    int64  
 6   Parch        891 non-null    int64  
 7   Ticket       891 non-null    object 
 8   Fare         891 non-null    float64
 9   Cabin        891 non-null    object 
 10  Embarked     891 non-null    object 
dtypes: float64(2), int64(4), object(5)
memory usage: 76.7+ KB
None

可以看到数据被补全了,可以进行下一步操作。

下一步分割数据集,25%的数据用于测试集,75%的数据用于训练集。

X_train, X_test, y_train, y_test = train_test_split(x, y, train_size=.75, random_state=33)

下一步要处理的是分类变量,即建立分类变量的编码问题,这里选择使用DictVectorizer

DictVectorizer的处理对象是符号化(非数字化)的但是具有一定结构的特征数据,如字典等,将符号转成数字0/1表示。

from sklearn.feature_extraction import DictVectorizer
vec = DictVectorizer()
X_train = vec.fit_transform(X_train.to_dict(orient='record'))
X_test = vec.transform(X_test.to_dict(orient='record'))

print(len(vec.feature_names_))
1352

输出建立分类变量的数据

print(X_train.toarray()[:10, :5])
[[47.          0.          0.          0.          0.        ]
 [40.          0.          0.          0.          0.        ]
 [29.69911765  0.          0.          0.          0.        ]
 [22.          0.          0.          0.          0.        ]
 [23.5         0.          0.          0.          0.        ]
 [47.          0.          0.          0.          0.        ]
 [27.          0.          0.          0.          0.        ]
 [24.          0.          0.          0.          0.        ]
 [29.69911765  0.          0.          0.          0.        ]
 [29.69911765  0.          0.          0.          0.        ]]

我们不难发现,DictVectorizer对非数字化的处理方式是,借助原特征的名称,组合成新的特征,并采用0/1的方式进行量化,而数值型的特征转化比较方便,一般情况维持原值即可。

对数据处理完毕后,开始对模型进行训练。这里我们使用随机森林模型对所有特征进行预测,并作性能评估。这里使用循环的方法找到最佳的参数max_depth

from sklearn.ensemble import RandomForestClassifier

maxid, maxval = 0, 0
for i in range(10, 45):
    forest = RandomForestClassifier(max_depth=i, random_state=33)
    forest.fit(X_train, y_train)
    score = forest.score(X_test, y_test)
    if maxval < score:
        maxval = score
        maxid = i
print(maxid, maxval)
28 0.874439461883408

这里,我们选择max_depth=10为树的最大深度。开始训练模型:

best_forest = RandomForestClassifier(max_depth=10, random_state=33)
best_forest.fit(X_train, y_train)
score = best_forest.score(X_test, y_test)
print(score)
0.8026905829596412

总体来讲,良好的数据特征组合不需太多便可以使得模型的性能表现突出。比如,我们“良/恶性乳腺癌肿瘤预测”问题中,仅仅使用两个描述肿瘤形态的特征便可以取得很高的识别率。冗余的特征虽然不会影响到模型的性能,不过却使得CPU的计算做了无用功。比如,主成分分析主要用于去除多余的那些线性相关的特征组合,原因在于这些冗余的特征组合并不会对模型训练有更多贡献。而不良的特征自然会降低模型的精度。

特征筛选与PCA这类通过选择主成分对特征进行重建的方法略有区别:对于PCA而言,我们经常无法解释重建之后的特征;但是特征筛选不存在对特征值的修改,而更加侧重于寻找那些对模型的性能提升较大的少量特征。

这里我们在代码中继续沿用 Titanic据集,这次试图通过特征筛选来寻找最佳的特征组合,并且达到提高预测准确性的目标。

sklearn中导入特征筛选器

from sklearn import feature_selection
fs = feature_selection.SelectPercentile(feature_selection.chi2, percentile=20)
X_train_fs = fs.fit_transform(X_train, y_train)
best_forest.fit(X_train_fs, y_train)
fs_score = best_forest.score(fs.transform(X_test), y_test)
print(fs_score)
0.8295964125560538

可以看到结果,在选择了前20%的属性进行模型的训练得到了一个比全属性进行训练更好的结果。准确率达到了82.96%

这样,我们就完成了一个简单的模型,虽然没有对模型做过多的优化,后续的优化环节,在后续的博客中进行介绍。