跳至主要内容

Kaggle 教程:您的第一个机器学习模型

学习使用 Python 的 scikit-learn 包构建您的第一个机器学习模型——决策树分类器,提交到 Kaggle 并查看其表现!
· 11分钟 阅读

使用 AI 探索

构建您的第一个机器学习模型

在完成探索性数据分析(EDA)和基线模型之后,您就可以开始着手构建第一个真正的机器学习模型了。

注意 本教程基于一次 Facebook Live 的跟码会话;您可以在此回看:

在开始之前,先导入所有必要的库:

# Import modules
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import re
import numpy as np
from sklearn import tree
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV

# Figures inline and set visualization style
%matplotlib inline
sns.set()

# Import data
df_train = pd.read_csv('data/train.csv')
df_test = pd.read_csv('data/test.csv')
  • 下面,您将从训练数据集中删除目标列“Survived”,并创建一个新的数据框 data,它由训练集和测试集合并而成。这样做是因为您想先对数据做一些预处理,并确保对训练集执行的任何操作也同样作用于测试集。
  • 但首先,先把训练数据的目标变量单独存放起来以防丢失。
# Store target variable of training data in a safe place
survived_train = df_train.Survived

# Concatenate training and test sets
data = pd.concat([df_train.drop(['Survived'], axis=1), df_test])
  • 使用 info() 方法查看新的数据框 data。
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1309 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    1309 non-null int64
Pclass         1309 non-null int64
Name           1309 non-null object
Sex            1309 non-null object
Age            1046 non-null float64
SibSp          1309 non-null int64
Parch          1309 non-null int64
Ticket         1309 non-null object
Fare           1308 non-null float64
Cabin          295 non-null object
Embarked       1307 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 122.7+ KB

有 2 个数值变量存在缺失值。

它们是什么?

没错,'Age' 和 'Fare' 列有缺失值!从上面的 .info() 结果可以看到,第一列缺了 263 个值,因为在 1309 条记录中只有 1046 个非空值。理想情况下,当然希望这 1309 条都没有缺失,但这里并非如此!

幸运的是,Fare 列只缺了 1 个。同时注意到 'Cabin' 和 'Embarked' 也有缺失,稍后您也需要处理它们。

不过现在先专注于修复数值变量:您将为 'Age' 和 'Fare' 两列填补缺失值,使用这些变量已知值的中位数进行填充。

注意 在这里使用中位数是因为它非常适合处理离群值。换句话说,当数据分布偏斜时,中位数更合适。其他填补方法包括使用均值(将所有数据点相加再除以数量)或众数(出现次数最多的数)。

# Impute missing numerical variables
data['Age'] = data.Age.fillna(data.Age.median())
data['Fare'] = data.Fare.fillna(data.Fare.median())

# Check out info of data
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1309 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    1309 non-null int64
Pclass         1309 non-null int64
Name           1309 non-null object
Sex            1309 non-null object
Age            1309 non-null float64
SibSp          1309 non-null int64
Parch          1309 non-null int64
Ticket         1309 non-null object
Fare           1309 non-null float64
Cabin          295 non-null object
Embarked       1307 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 122.7+ KB

这看起来已经好多了,对吧!

另外请注意,在上一个教程和笔记本中,您有一组打算用于预测的特征,因为您从探索性分析中发现它们对 'Survived' 有重要影响。其中一个是 'Fare',另外还有 'Age' 和 'Sex'!

但您希望用数字来编码数据,因此需要把“male”和“female”转换为数值。这样做是因为大多数机器学习模型都要求输入特征是数值型。您可以使用 pandas 的 .get_dummies() 来实现:

data = pd.get_dummies(data, columns=['Sex'], drop_first=True)
data.head()
 PassengerIdPclassNameAgeSibSpParchTicketFareCabinEmbarkedSex_male
013Braund, Mr. Owen Harris22.010A/5 211717.2500NaNS1
121Cumings, Mrs. John Bradley (Florence Briggs Th...38.010PC 1759971.2833C85C0
233Heikkinen, Miss. Laina26.000STON/O2. 31012827.9250NaNS0
341Futrelle, Mrs. Jacques Heath (Lily May Peel)35.01011380353.1000C123S0
453Allen, Mr. William Henry35.0003734508.0500NaNS1

.get_dummies() 会为 'Sex' 中的每个选项创建一个新列。因此它会为 female 创建一个名为 'Sex_female' 的新列,并为 'Sex_male' 创建一个新列,用以编码该行是男性还是女性。

现在,因为在代码中传入了 drop_first 参数,'Sex_female' 被丢弃了,因为本质上 'Sex_female' 和 'Sex_male' 这两个新列编码了相同的信息。

因此您所做的就是创建了一个新的 'Sex_male' 列,如果该行为男性则为 1,如果为女性则为 0。

在做机器学习的数据处理时,.get_dummies() 将会是您的好帮手!

  • 现在,从数据框中选择列 ['Sex_male', 'Fare', 'Age','Pclass', 'SibSp'] 来构建您的第一个机器学习模型:
# Select columns and view head
data = data[['Sex_male', 'Fare', 'Age','Pclass', 'SibSp']]
data.head()
 Sex_maleFareAgePclassSibSp
017.250022.031
1071.283338.011
207.925026.030
3053.100035.011
418.050035.030
  • 使用 .info() 查看 data:
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1309 entries, 0 to 417
Data columns (total 5 columns):
Sex_male    1309 non-null uint8
Fare        1309 non-null float64
Age         1309 non-null float64
Pclass      1309 non-null int64
SibSp       1309 non-null int64
dtypes: float64(2), int64(2), uint8(1)
memory usage: 52.4 KB

现在所有条目都为非空。干得好!

到目前为止,您已经把数据整理成可用于构建第一个机器学习模型的形式了。这意味着您终于可以开始构建第一个模型了!

想要进一步学习 pandas,请查看我们的Python 数据处理学习路径。

构建决策树分类器

什么是决策树分类器?它是一棵树,允许您基于特征变量来对数据点(也称目标变量)进行分类。

例如,下面这棵树的根节点会让您根据这样一个问题做出第一步决策:“'Sex_male' 是否小于 0.5?”换句话说,该数据点是否为女性。如果答案是 True,您沿左侧走,得到 'Survived'。如果是 False,沿右侧走,得到 'Dead'。

现在,您无需担心节点中看到的附加信息,如 gini、samples 或 value。这些可以稍后再看!

decision tree classifier

  • 首先,您要将这样的模型拟合到训练数据上,这意味着(基于训练数据)决定在树的每个分支点做出怎样的分裂。例如,第一层是否以“是否为男性”为分裂,并且 'Male' 会导致预测为 'Dead'。

注意 实际上是使用 Gini 系数来做这些决策的。此处我们不会更深入探讨。

  • 在将模型拟合到 data 之前,将其拆分回训练集和测试集:
data_train = data.iloc[:891]
data_test = data.iloc[891:]
  • 您将使用 scikit-learn,它需要数组而非 DataFrame,因此先进行转换:
X = data_train.values
test = data_test.values
y = survived_train.values
  • 现在就来构建您的决策树分类器!先用 max_depth=3 创建一个模型,然后将其拟合到数据上。注意 我们将模型命名为 clf,是 “Classifier” 的缩写。
# Instantiate model and fit to data
clf = tree.DecisionTreeClassifier(max_depth=3)
clf.fit(X, y)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=3,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')

特征变量 X 是传给 .fit() 方法的第一个参数,而目标变量 y 是第二个参数。

输出给出了刚构建的决策树分类器的关键信息:例如,最大深度被设定为 3。

  • 现在,您将在测试集上进行预测,创建一个新的 'Survived' 列并将预测结果存入其中。将 df_test 的 'PassengerId' 和 'Survived' 列保存为 .csv,然后提交到 Kaggle。
# Make predictions and store in 'Survived' column of df_test
Y_pred = clf.predict(test)
df_test['Survived'] = Y_pred
df_test[['PassengerId', 'Survived']].to_csv('data/predictions/1st_dec_tree.csv', index=False)
  • Kaggle 报告的模型准确率是多少?

kaggle screen

准确率是 78%。您的排名提升了 2000 多位!

恭喜,您已将数据整理成可用于构建第一个机器学习模型的形式。此外,您也已经构建了第一个模型:一个决策树分类器。

接下来,您将弄清 max_depth 参数是什么、为什么选择它,并探索 train_test_split。

想要进一步学习 scikit-learn,请查看我们的使用 scikit-learn 的监督学习课程。

什么是决策树分类器?

您刚刚构建的决策树分类器设置了 max_depth=3,它看起来像这样:

Decision Tree Classifier

从第一次决策到最后一次决策的最大距离是 3,这就是 max_depth=3。

注意:您可以使用 graphviz 来生成这样的图。有关详细信息,请参阅 scikit-learn 文档此处。

在构建这个模型时,本质上是在特征空间中创建决策边界,例如(图片来自这里):

decision boundary

为何选择 max_depth=3?

树的深度被称为超参数,即在将模型拟合到数据之前需要决定的参数。如果您选择更大的 max_depth,就会得到更复杂的决策边界。

  • 如果决策边界过于复杂,您可能会对数据过拟合,也就是模型不仅描述了信号,还描述了噪声。

  • 如果 max_depth 太小,您可能会欠拟合数据,即模型没有包含足够的信号。

但如何判断是过拟合还是欠拟合呢?

注意:这也涉及偏差-方差权衡;此处不展开,只为完整性提及!

一种方法是从训练数据中留出一部分作为测试集。您可以将模型拟合到训练集上,在测试集上进行预测,并查看预测在测试集上的效果如何。

  • 现在就来做这件事:将原始训练数据拆分为训练集和测试集:
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.33, random_state=42, stratify=y)
  • 接下来,您将遍历 max_depth 从 1 到 9 的取值,并绘制模型在训练集和测试集上的准确率:
# Setup arrays to store train and test accuracies
dep = np.arange(1, 9)
train_accuracy = np.empty(len(dep))
test_accuracy = np.empty(len(dep))

# Loop over different values of k
for i, k in enumerate(dep):
    # Setup a Decision Tree Classifier
    clf = tree.DecisionTreeClassifier(max_depth=k)

    # Fit the classifier to the training data
    clf.fit(X_train, y_train)

    #Compute accuracy on the training set
    train_accuracy[i] = clf.score(X_train, y_train)

    #Compute accuracy on the testing set
    test_accuracy[i] = clf.score(X_test, y_test)

# Generate plot
plt.title('clf: Varying depth of tree')
plt.plot(dep, test_accuracy, label = 'Testing Accuracy')
plt.plot(dep, train_accuracy, label = 'Training Accuracy')
plt.legend()
plt.xlabel('Depth of tree')
plt.ylabel('Accuracy')
plt.show()

line graph

随着您增加 max_depth,模型会越来越贴合训练数据,因为您会做出能够描述训练数据的分裂。训练集上的准确率会不断上升,但您会看到测试集并非如此:这是过拟合。

这就是为什么您选择了 max_depth=3。

总结

在本教程中,您已将数据整理成可用于构建第一个机器学习模型的形式。接着,您也构建了第一个机器学习模型:一个决策树分类器。最后,您了解了 train_test_split,以及它如何帮助我们选择机器学习模型的超参数。

在下一篇将于 2018 年 1 月 10 日在 DataCamp Community 发布的教程中,您将学习如何工程化一些新特征并构建一些新模型!

同时,如果您想进一步了解 scikit-learn,请查看 DataCamp 的使用 scikit-learn 的监督学习课程。

主题