13.4 scikit-learn介绍

scikit-learn是一个广泛使用、用途多样的Python机器学习库。它包含多种标准监督和非监督机器学习方法和模型选择和评估、数据转换、数据加载和模型持久化工具。这些模型可以用于分类、聚合、预测和其它任务。

机器学习方面的学习和应用scikit-learn和TensorFlow解决实际问题的线上和纸质资料很多。本节中,我会简要介绍scikit-learn API的风格。

写作此书的时候,scikit-learn并没有和pandas深度结合,但是有些第三方包在开发中。尽管如此,pandas非常适合在模型拟合前处理数据集。

举个例子,我用一个Kaggle竞赛的经典数据集,关于泰坦尼克号乘客的生还率。我们用pandas加载测试和训练数据集:

  1. In [86]: train = pd.read_csv('datasets/titanic/train.csv')
  2. In [87]: test = pd.read_csv('datasets/titanic/test.csv')
  3. In [88]: train[:4]
  4. Out[88]:
  5. PassengerId Survived Pclass \
  6. 0 1 0 3
  7. 1 2 1 1
  8. 2 3 1 3
  9. 3 4 1 1
  10. Name Sex Age SibSp \
  11. 0 Braund, Mr. Owen Harris male 22.0 1
  12. 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1
  13. 2 Heikkinen, Miss. Laina female 26.0 0
  14. 3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1
  15. Parch Ticket Fare Cabin Embarked
  16. 0 0 A/5 21171 7.2500 NaN S
  17. 1 0 PC 17599 71.2833 C85 C
  18. 2 0 STON/O2. 3101282 7.9250 NaN S
  19. 3 0 113803 53.1000 C123 S

statsmodels和scikit-learn通常不能接收缺失数据,因此我们要查看列是否包含缺失值:

  1. In [89]: train.isnull().sum()
  2. Out[89]:
  3. PassengerId 0
  4. Survived 0
  5. Pclass 0
  6. Name 0
  7. Sex 0
  8. Age 177
  9. SibSp 0
  10. Parch 0
  11. Ticket 0
  12. Fare 0
  13. Cabin 687
  14. Embarked 2
  15. dtype: int64
  16. In [90]: test.isnull().sum()
  17. Out[90]:
  18. PassengerId 0
  19. Pclass 0
  20. Name 0
  21. Sex 0
  22. Age 86
  23. SibSp 0
  24. Parch 0
  25. Ticket 0
  26. Fare 1
  27. Cabin 327
  28. Embarked 0
  29. dtype: int64

在统计和机器学习的例子中,根据数据中的特征,一个典型的任务是预测乘客能否生还。模型现在训练数据集中拟合,然后用样本外测试数据集评估。

我想用年龄作为预测值,但是它包含缺失值。缺失数据补全的方法有多种,我用的是一种简单方法,用训练数据集的中位数补全两个表的空值:

  1. In [91]: impute_value = train['Age'].median()
  2. In [92]: train['Age'] = train['Age'].fillna(impute_value)
  3. In [93]: test['Age'] = test['Age'].fillna(impute_value)

现在我们需要指定模型。我增加了一个列IsFemale,作为“Sex”列的编码:

  1. In [94]: train['IsFemale'] = (train['Sex'] == 'female').astype(int)
  2. In [95]: test['IsFemale'] = (test['Sex'] == 'female').astype(int)

然后,我们确定一些模型变量,并创建NumPy数组:

  1. In [96]: predictors = ['Pclass', 'IsFemale', 'Age']
  2. In [97]: X_train = train[predictors].values
  3. In [98]: X_test = test[predictors].values
  4. In [99]: y_train = train['Survived'].values
  5. In [100]: X_train[:5]
  6. Out[100]:
  7. array([[ 3., 0., 22.],
  8. [ 1., 1., 38.],
  9. [ 3., 1., 26.],
  10. [ 1., 1., 35.],
  11. [ 3., 0., 35.]])
  12. In [101]: y_train[:5]
  13. Out[101]: array([0, 1, 1, 1, 0])

我不能保证这是一个好模型,但它的特征都符合。我们用scikit-learn的LogisticRegression模型,创建一个模型实例:

  1. In [102]: from sklearn.linear_model import LogisticRegression
  2. In [103]: model = LogisticRegression()

与statsmodels类似,我们可以用模型的fit方法,将它拟合到训练数据:

  1. In [104]: model.fit(X_train, y_train)
  2. Out[104]:
  3. LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
  4. intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
  5. penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
  6. verbose=0, warm_start=False)

现在,我们可以用model.predict,对测试数据进行预测:

  1. In [105]: y_predict = model.predict(X_test)
  2. In [106]: y_predict[:10]
  3. Out[106]: array([0, 0, 0, 0, 1, 0, 1, 0, 1, 0])

如果你有测试数据集的真是值,你可以计算准确率或其它错误度量值:

  1. (y_true == y_predict).mean()

在实际中,模型训练经常有许多额外的复杂因素。许多模型有可以调节的参数,有些方法(比如交叉验证)可以用来进行参数调节,避免对训练数据过拟合。这通常可以提高预测性或对新数据的健壮性。

交叉验证通过分割训练数据来模拟样本外预测。基于模型的精度得分(比如均方差),可以对模型参数进行网格搜索。有些模型,如logistic回归,有内置的交叉验证的估计类。例如,logisticregressioncv类可以用一个参数指定网格搜索对模型的正则化参数C的粒度:

  1. In [107]: from sklearn.linear_model import LogisticRegressionCV
  2. In [108]: model_cv = LogisticRegressionCV(10)
  3. In [109]: model_cv.fit(X_train, y_train)
  4. Out[109]:
  5. LogisticRegressionCV(Cs=10, class_weight=None, cv=None, dual=False,
  6. fit_intercept=True, intercept_scaling=1.0, max_iter=100,
  7. multi_class='ovr', n_jobs=1, penalty='l2', random_state=None,
  8. refit=True, scoring=None, solver='lbfgs', tol=0.0001, verbose=0)

要手动进行交叉验证,你可以使用cross_val_score帮助函数,它可以处理数据分割。例如,要交叉验证我们的带有四个不重叠训练数据的模型,可以这样做:

  1. In [110]: from sklearn.model_selection import cross_val_score
  2. In [111]: model = LogisticRegression(C=10)
  3. In [112]: scores = cross_val_score(model, X_train, y_train, cv=4)
  4. In [113]: scores
  5. Out[113]: array([ 0.7723, 0.8027, 0.7703, 0.7883])

默认的评分指标取决于模型本身,但是可以明确指定一个评分。交叉验证过的模型需要更长时间来训练,但会有更高的模型性能。