Scikit-Learn 教程:如何安装和 Scikit-Learn 示例
什么是 Scikit-learn?
Scikit学习 是开源的 Python 机器学习库。它支持最先进的算法,例如 KNN、XGBoost、随机森林和 SVM。它建立在 NumPy 之上。Scikit-learn 广泛应用于 Kaggle 竞赛以及知名科技公司。它有助于预处理、降维(参数选择)、分类、回归、聚类和模型选择。
Scikit-learn 拥有所有开源库中最好的文档。它为您提供了一个交互式图表 https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html.

Scikit-learn 使用起来并不难,而且效果也很好。不过,scikit-learn 不支持并行计算。虽然可以用它运行深度学习算法,但这并不是最佳解决方案,尤其是如果你知道如何使用 TensorFlow 的话。
如何下载和安装 Scikit-learn
现在在这个 Python Scikit-learn 教程,我们将学习如何下载和安装 Scikit-learn:
选项1: AWS
scikit-learn 可以在 AWS 上使用。请 参考 已预先安装 scikit-learn 的 docker 镜像。
要使用开发者版本,请使用以下命令 Jupyter
import sys !{sys.executable} -m pip install git+git://github.com/scikit-learn/scikit-learn.git
选项2: Mac或 Windows 使用 Anaconda
要了解有关 Anaconda 安装的信息,请参阅 https://www.guru99.com/download-install-tensorflow.html
最近,scikit 的开发人员发布了一个开发版本,解决了当前版本面临的常见问题。我们发现使用开发版本比使用当前版本更加方便。
如何使用 Conda 环境安装 scikit-learn
如果你使用 conda 环境安装了 scikit-learn,请按照步骤更新至版本 0.20
步骤1) 激活 TensorFlow 环境
source activate hello-tf
步骤2) 使用 conda 命令删除 scikit lean
conda remove scikit-learn
步骤3) 安装开发者版本。
安装 scikit learn 开发版本以及必要的库。
conda install -c anaconda git pip install Cython pip install h5py pip install git+git://github.com/scikit-learn/scikit-learn.git
注意: Windows 用户需要安装 Microsoft 视觉 C++ 14. 你可以从 点击这里
机器学习的 Scikit-Learn 示例
本 Scikit 教程分为两部分:
- 使用 scikit-learn 进行机器学习
- 如何通过 LIME 信任你的模型
第一部分详细介绍了如何构建管道、创建模型和调整超参数,而第二部分提供了模型选择方面的最新技术。
步骤1)导入数据
在本 Scikit 学习教程中,您将使用成人数据集。
有关此数据集的背景信息,请参阅如果您有兴趣了解更多有关描述性统计数据的信息,请使用 Dive 和 Overview 工具。
参考 本教程 了解更多关于潜水和概览的信息
使用 Pandas 导入数据集。请注意,您需要将连续变量的类型转换为浮点格式。
该数据集包括八个分类变量:
分类变量在 CATE_FEATURES 中列出
- 工人阶级
- 教育
- 婚姻
- 占用
- 关系
- 种族
- 性别
- 祖国
此外,还有六个连续变量:
连续变量在 CONTI_FEATURES 中列出
- 年龄
- 风车
- 教育编号
- 资本收益
- 资本损失
- hours_week
请注意,我们手动填写列表,以便您更好地了解我们正在使用的列。构建分类或连续列表的更快方法是使用:
## List Categorical CATE_FEATURES = df_train.iloc[:,:-1].select_dtypes('object').columns print(CATE_FEATURES) ## List continuous CONTI_FEATURES = df_train._get_numeric_data() print(CONTI_FEATURES)
以下是导入数据的代码:
# Import dataset import pandas as pd ## Define path data COLUMNS = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country', 'label'] ### Define continuous list CONTI_FEATURES = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week'] ### Define categorical list CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country'] ## Prepare the data features = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country'] PATH = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data" df_train = pd.read_csv(PATH, skipinitialspace=True, names = COLUMNS, index_col=False) df_train[CONTI_FEATURES] =df_train[CONTI_FEATURES].astype('float64') df_train.describe()
年龄 | 风车 | 教育编号 | 资本收益 | 资本损失 | hours_week | |
---|---|---|---|---|---|---|
数 | 32561.000000 | 3.256100e + 04 | 32561.000000 | 32561.000000 | 32561.000000 | 32561.000000 |
意味着 | 38.581647 | 1.897784e + 05 | 10.080679 | 1077.648844 | 87.303830 | 40.437456 |
STD | 13.640433 | 1.055500e + 05 | 2.572720 | 7385.292085 | 402.960219 | 12.347429 |
分钟 | 17.000000 | 1.228500e + 04 | 1.000000 | 0.000000 | 0.000000 | 1.000000 |
25% | 28.000000 | 1.178270e + 05 | 9.000000 | 0.000000 | 0.000000 | 40.000000 |
50% | 37.000000 | 1.783560e + 05 | 10.000000 | 0.000000 | 0.000000 | 40.000000 |
75% | 48.000000 | 2.370510e + 05 | 12.000000 | 0.000000 | 0.000000 | 45.000000 |
最大 | 90.000000 | 1.484705e + 06 | 16.000000 | 99999.000000 | 4356.000000 | 99.000000 |
您可以检查 native_country 特征的唯一值计数。您可以看到只有一个家庭来自荷兰。这个家庭不会给我们带来任何信息,但会在训练过程中通过错误带来。
df_train.native_country.value_counts()
United-States 29170 Mexico 643 ? 583 Philippines 198 Germany 137 Canada 121 Puerto-Rico 114 El-Salvador 106 India 100 Cuba 95 England 90 Jamaica 81 South 80 China 75 Italy 73 Dominican-Republic 70 Vietnam 67 Guatemala 64 Japan 62 Poland 60 Columbia 59 Taiwan 51 Haiti 44 Iran 43 Portugal 37 Nicaragua 34 Peru 31 France 29 Greece 29 Ecuador 28 Ireland 24 Hong 20 Cambodia 19 Trinadad&Tobago 19 Thailand 18 Laos 18 Yugoslavia 16 Outlying-US(Guam-USVI-etc) 14 Honduras 13 Hungary 13 Scotland 12 Holand-Netherlands 1 Name: native_country, dtype: int64
您可以从数据集中排除此无信息的行
## Drop Netherland, because only one row df_train = df_train[df_train.native_country != "Holand-Netherlands"]
接下来,将连续特征的位置存储在列表中。在下一步构建管道时,您将需要它。
下面的代码将循环遍历 CONTI_FEATURES 中的所有列名称并获取其位置(即其编号),然后将其附加到名为 conti_features 的列表中
## Get the column index of the categorical features conti_features = [] for i in CONTI_FEATURES: position = df_train.columns.get_loc(i) conti_features.append(position) print(conti_features)
[0, 2, 10, 4, 11, 12]
以下代码的作用与上述相同,但针对的是分类变量。以下代码重复了您之前所做的事情,但针对的是分类特征。
## Get the column index of the categorical features categorical_features = [] for i in CATE_FEATURES: position = df_train.columns.get_loc(i) categorical_features.append(position) print(categorical_features)
[1, 3, 5, 6, 7, 8, 9, 13]
您可以查看数据集。请注意,每个分类特征都是一个字符串。您不能向模型提供字符串值。您需要使用虚拟变量来转换数据集。
df_train.head(5)
实际上,您需要为特征中的每个组创建一列。首先,您可以运行以下代码来计算所需的列总数。
print(df_train[CATE_FEATURES].nunique(), 'There are',sum(df_train[CATE_FEATURES].nunique()), 'groups in the whole dataset')
workclass 9 education 16 marital 7 occupation 15 relationship 6 race 5 sex 2 native_country 41 dtype: int64 There are 101 groups in the whole dataset
整个数据集包含 101 个组,如上所示。例如,workclass 的特征有 XNUMX 个组。您可以使用以下代码可视化组的名称
unique() 返回分类特征的唯一值。
for i in CATE_FEATURES: print(df_train[i].unique())
['State-gov' 'Self-emp-not-inc' 'Private' 'Federal-gov' 'Local-gov' '?' 'Self-emp-inc' 'Without-pay' 'Never-worked'] ['Bachelors' 'HS-grad' '11th' 'Masters' '9th' 'Some-college' 'Assoc-acdm' 'Assoc-voc' '7th-8th' 'Doctorate' 'Prof-school' '5th-6th' '10th' '1st-4th' 'Preschool' '12th'] ['Never-married' 'Married-civ-spouse' 'Divorced' 'Married-spouse-absent' 'Separated' 'Married-AF-spouse' 'Widowed'] ['Adm-clerical' 'Exec-managerial' 'Handlers-cleaners' 'Prof-specialty' 'Other-service' 'Sales' 'Craft-repair' 'Transport-moving' 'Farming-fishing' 'Machine-op-inspct' 'Tech-support' '?' 'Protective-serv' 'Armed-Forces' 'Priv-house-serv'] ['Not-in-family' 'Husband' 'Wife' 'Own-child' 'Unmarried' 'Other-relative'] ['White' 'Black' 'Asian-Pac-Islander' 'Amer-Indian-Eskimo' 'Other'] ['Male' 'Female'] ['United-States' 'Cuba' 'Jamaica' 'India' '?' 'Mexico' 'South' 'Puerto-Rico' 'Honduras' 'England' 'Canada' 'Germany' 'Iran' 'Philippines' 'Italy' 'Poland' 'Columbia' 'Cambodia' 'Thailand' 'Ecuador' 'Laos' 'Taiwan' 'Haiti' 'Portugal' 'Dominican-Republic' 'El-Salvador' 'France' 'Guatemala' 'China' 'Japan' 'Yugoslavia' 'Peru' 'Outlying-US(Guam-USVI-etc)' 'Scotland' 'Trinadad&Tobago' 'Greece' 'Nicaragua' 'Vietnam' 'Hong' 'Ireland' 'Hungary']
因此,训练数据集将包含 101 + 7 列。最后七列是连续特征。
Scikit-learn 可以处理转换。它分两步完成:
- 首先,您需要将字符串转换为 ID。例如,State-gov 的 ID 为 1,Self-emp-not-inc 的 ID 为 2,依此类推。LabelEncoder 函数会为您完成此操作
- 将每个 ID 转置为一个新列。如前所述,数据集有 101 个组的 ID。因此将有 101 列捕获所有分类特征的组。Scikit-learn 有一个名为 OneHotEncoder 的函数可以执行此操作
步骤2)创建训练/测试集
现在数据集已经准备好了,我们可以将其按 80/20 进行分割。
80%用于训练集,20%用于测试集。
您可以使用 train_test_split。第一个参数是数据框,即特征,第二个参数是标签数据框。您可以使用 test_size 指定测试集的大小。
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(df_train[features], df_train.label, test_size = 0.2, random_state=0) X_train.head(5) print(X_train.shape, X_test.shape)
(26048, 14) (6512, 14)
步骤 3)构建管道
管道使得向模型提供一致的数据变得更加容易。
背后的想法是将原始数据放入“管道”中执行操作。
例如,对于当前数据集,您需要标准化连续变量并转换分类数据。请注意,您可以在管道内执行任何操作。例如,如果数据集中有“NA”,您可以用平均值或中位数替换它们。您还可以创建新变量。
您可以选择:对两个进程进行硬编码或创建管道。第一种选择可能会导致数据泄露,并随着时间的推移造成不一致。更好的选择是使用管道。
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder from sklearn.compose import ColumnTransformer, make_column_transformer from sklearn.pipeline import make_pipeline from sklearn.linear_model import LogisticRegression
在输入逻辑分类器之前,管道将执行两项操作:
- 标准化变量:`StandardScaler()“
- 转换分类特征:OneHotEncoder(sparse=False)
您可以使用 make_column_transformer 执行这两个步骤。此功能在当前版本的 scikit-learn (0.19) 中不可用。当前版本无法在管道中执行标签编码器和独热编码器。这是我们决定使用开发人员版本的原因之一。
make_column_transformer 使用起来很简单。你需要定义要应用转换的列以及要操作的转换。例如,要标准化连续特征,你可以执行以下操作:
- conti_features,make_column_transformer 里面的 StandardScaler()。
- conti_features:包含连续变量的列表
- StandardScaler:对变量进行标准化
make_column_transformer 内部的对象 OneHotEncoder 自动对标签进行编码。
preprocess = make_column_transformer( (conti_features, StandardScaler()), ### Need to be numeric not string to specify columns name (categorical_features, OneHotEncoder(sparse=False)) )
您可以测试管道是否与 fit_transform 配合使用。数据集应具有以下形状:26048, 107
preprocess.fit_transform(X_train).shape
(26048, 107)
数据转换器已准备就绪。您可以使用 make_pipeline 创建管道。数据转换完成后,您就可以输入逻辑回归了。
model = make_pipeline( preprocess, LogisticRegression())
使用 scikit-learn 训练模型很简单。您需要使用管道前面的对象 fit,即模型。您可以使用 scikit-learn 库中的分数对象打印准确率
model.fit(X_train, y_train) print("logistic regression score: %f" % model.score(X_test, y_test))
logistic regression score: 0.850891
最后,你可以使用 predict_proba 预测类别。它返回每个类别的概率。请注意,它的总和为 1。
model.predict_proba(X_test)
array([[0.83576663, 0.16423337], [0.94582765, 0.05417235], [0.64760587, 0.35239413], ..., [0.99639252, 0.00360748], [0.02072181, 0.97927819], [0.56781353, 0.43218647]])
步骤 4)在网格搜索中使用我们的管道
调整超参数(决定网络结构的变量,如隐藏单元)可能很繁琐且令人精疲力尽。
评估模型的一种方法是改变训练集的大小并评估性能。
您可以重复此方法十次来查看得分指标。但是,这太费力了。
相反,scikit-learn 提供了执行参数调整和交叉验证的功能。
交叉验证
交叉验证是指在训练过程中,将训练集分批滑行 n 次,然后对模型进行 n 次评估。例如,如果 cv 设置为 10,则训练集将进行 10 次训练和评估。在每一轮中,分类器会随机选择 XNUMX 次来训练模型,第 XNUMX 次用于评估。
网格搜索
每个分类器都有超参数需要调整。您可以尝试不同的值,也可以设置参数网格。如果您访问 scikit-learn 官方网站,您会看到逻辑分类器有不同的参数需要调整。为了加快训练速度,您可以选择调整 C 参数。它控制正则化参数。它应该是正的。较小的值会为正则化器提供更大的权重。
您可以使用对象 GridSearchCV。您需要创建一个包含要调整的超参数的字典。
列出超参数,然后列出要尝试的值。例如,要调整 C 参数,请使用:
- 'logisticregression__C':[0.1, 1.0, 1.0]:该参数前面是分类器的名称(小写)和两个下划线。
模型将尝试四个不同的值:0.001、0.01、0.1 和 1。
使用 10 倍训练模型:cv=10
from sklearn.model_selection import GridSearchCV # Construct the parameter grid param_grid = { 'logisticregression__C': [0.001, 0.01,0.1, 1.0], }
您可以使用带有参数 gri 和 cv 的 GridSearchCV 来训练模型。
# Train the model grid_clf = GridSearchCV(model, param_grid, cv=10, iid=False) grid_clf.fit(X_train, y_train)
OUTPUT
GridSearchCV(cv=10, error_score='raise-deprecating', estimator=Pipeline(memory=None, steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None, transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...ty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False))]), fit_params=None, iid=False, n_jobs=1, param_grid={'logisticregression__C': [0.001, 0.01, 0.1, 1.0]}, pre_dispatch='2*n_jobs', refit=True, return_train_score='warn', scoring=None, verbose=0)
要访问最佳参数,可以使用 best_params_
grid_clf.best_params_
OUTPUT
{'logisticregression__C': 1.0}
用四种不同的正则化值训练模型后,最佳参数为
print("best logistic regression from grid search: %f" % grid_clf.best_estimator_.score(X_test, y_test))
网格搜索的最佳逻辑回归:0.850891
要访问预测概率:
grid_clf.best_estimator_.predict_proba(X_test)
array([[0.83576677, 0.16423323], [0.9458291 , 0.0541709 ], [0.64760416, 0.35239584], ..., [0.99639224, 0.00360776], [0.02072033, 0.97927967], [0.56782222, 0.43217778]])
使用 scikit-learn 的 XGBoost 模型
让我们尝试使用 Scikit-learn 示例来训练市场上最好的分类器之一。XGBoost 是对随机森林的改进。分类器的理论背景超出了本文的范围 Python Scikit 教程。请记住,XGBoost 赢得了许多 kaggle 比赛。在平均数据集大小的情况下,它的表现可以与深度学习算法一样好,甚至更好。
分类器训练起来很有挑战性,因为它有大量参数需要调整。当然,您可以使用 GridSearchCV 来选择参数。
相反,让我们看看如何使用更好的方法来找到最佳参数。如果传递许多值,GridSearchCV 可能会很繁琐,而且训练起来非常漫长。搜索空间会随着参数数量的增加而增长。一个更好的解决方案是使用 RandomizedSearchCV。此方法包括在每次迭代后随机选择每个超参数的值。例如,如果分类器在 1000 次迭代中训练,那么将评估 1000 个组合。它的工作原理或多或少类似于。GridSearchCV
您需要导入 xgboost。如果未安装该库,请使用 pip3 install xgboost 或
use import sys !{sys.executable} -m pip install xgboost
In Jupyter 环境
接下来,
import xgboost from sklearn.model_selection import RandomizedSearchCV from sklearn.model_selection import StratifiedKFold
Scikit 的下一步 Python 本教程包括指定要调整的参数。您可以参考官方文档查看所有要调整的参数。为了 Python Sklearn 教程中,你只需选择两个超参数,每个超参数有两个值。XGBoost 需要大量时间来训练,网格中的超参数越多,你需要等待的时间就越长。
params = { 'xgbclassifier__gamma': [0.5, 1], 'xgbclassifier__max_depth': [3, 4] }
您使用 XGBoost 分类器构建了一个新的管道。您选择定义 600 个估算器。请注意,n_estimators 是您可以调整的参数。高值可能会导致过度拟合。您可以自己尝试不同的值,但请注意这可能需要几个小时。您对其他参数使用默认值
model_xgb = make_pipeline( preprocess, xgboost.XGBClassifier( n_estimators=600, objective='binary:logistic', silent=True, nthread=1) )
您可以使用分层 K-Folds 交叉验证器改进交叉验证。您在这里只构建三个折叠以加快计算速度,但会降低质量。在家中将此值增加到 5 或 10 以改善结果。
您选择通过四次迭代来训练模型。
skf = StratifiedKFold(n_splits=3, shuffle = True, random_state = 1001) random_search = RandomizedSearchCV(model_xgb, param_distributions=params, n_iter=4, scoring='accuracy', n_jobs=4, cv=skf.split(X_train, y_train), verbose=3, random_state=1001)
随机搜索已准备就绪,您可以训练模型
#grid_xgb = GridSearchCV(model_xgb, params, cv=10, iid=False) random_search.fit(X_train, y_train)
Fitting 3 folds for each of 4 candidates, totalling 12 fits [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............ [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............ [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............ [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............ [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8759645283888057, total= 1.0min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............ [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8729701715996775, total= 1.0min [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8706519235199263, total= 1.0min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............ [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 .............. [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8735460094437406, total= 1.3min [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 .............. [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8722791661868018, total= 57.7s [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 .............. [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8753886905447426, total= 1.0min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 .............. [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8697304768486523, total= 1.3min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 .............. [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8740066797189912, total= 1.4min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 .............. [CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8707671043538355, total= 1.0min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8729701715996775, total= 1.2min [Parallel(n_jobs=4)]: Done 10 out of 12 | elapsed: 3.6min remaining: 43.5s [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8736611770125533, total= 1.2min [CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8692697535130154, total= 1.2min
[Parallel(n_jobs=4)]: Done 12 out of 12 | elapsed: 3.6min finished /Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/model_selection/_search.py:737: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal. DeprecationWarning)
RandomizedSearchCV(cv=<generator object _BaseKFold.split at 0x1101eb830>, error_score='raise-deprecating', estimator=Pipeline(memory=None, steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None, transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,... reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None, silent=True, subsample=1))]), fit_params=None, iid='warn', n_iter=4, n_jobs=4, param_distributions={'xgbclassifier__gamma': [0.5, 1], 'xgbclassifier__max_depth': [3, 4]}, pre_dispatch='2*n_jobs', random_state=1001, refit=True, return_train_score='warn', scoring='accuracy', verbose=3)
可以看出,XGBoost 的得分比之前的逻辑回归要好。
print("Best parameter", random_search.best_params_) print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
Best parameter {'xgbclassifier__max_depth': 3, 'xgbclassifier__gamma': 0.5} best logistic regression from grid search: 0.873157
random_search.best_estimator_.predict(X_test)
array(['<=50K', '<=50K', '<=50K', ..., '<=50K', '>50K', '<=50K'], dtype=object)
在 scikit-learn 中使用 MLPClassifier 创建 DNN
最后,您可以使用 scikit-learn 训练深度学习算法。方法与其他分类器相同。分类器可在 MLPClassifier 获得。
from sklearn.neural_network import MLPClassifier
您定义以下深度学习算法:
- Adam 求解器
- Relu 激活函数
- Alpha = 0.0001
- 批次大小为 150
- 两个隐藏层分别有 100 个和 50 个神经元
model_dnn = make_pipeline( preprocess, MLPClassifier(solver='adam', alpha=0.0001, activation='relu', batch_size=150, hidden_layer_sizes=(200, 100), random_state=1))
你可以改变层数来改进模型
model_dnn.fit(X_train, y_train) print("DNN regression score: %f" % model_dnn.score(X_test, y_test))
DNN 回归分数:0.821253
LIME:相信你的模型
现在您有了一个好的模型,您需要一个工具来信任它。 机器识别 算法,尤其是随机森林和神经网络,都被认为是黑箱算法。换句话说,它有效,但没人知道为什么。
三位研究人员发明了一种很棒的工具来观察计算机如何进行预测。这篇论文的题目是《我为什么要相信你?》。
他们开发了一种算法,名为 本地可解释模型不可知的解释(LIME).
举个例子:
有时你不知道是否可以相信机器学习的预测:
例如,医生不能仅仅因为计算机的诊断结果就相信它的诊断结果。在将模型投入生产之前,你还需要知道是否可以信任该模型。
想象一下,我们可以理解为什么任何分类器都会做出预测,即使是非常复杂的模型,如神经网络、随机森林或任何核的 svm
如果我们能够理解预测背后的原因,就会更容易相信预测。从医生的例子来看,如果模型告诉他哪些症状是关键的,你就会相信它,同样,也更容易弄清楚你是否不应该相信这个模型。
Lime 可以告诉你哪些特征会影响分类器的决策
资料准备
你需要改变以下几点才能运行 LIME 蟒蛇。首先需要在终端中安装lime,可以使用pip install lime
Lime 利用 LimeTabularExplainer 对象在本地近似模型。此对象需要:
- numpy 格式的数据集
- 特征的名称:feature_names
- 课程名称:class_names
- 分类特征列的索引:categorical_features
- 每个分类特征的组名称:categorical_names
创建 numpy 训练集
您可以将 df_train 从 pandas 复制并转换为 麻木 非常简单地
df_train.head(5) # Create numpy data df_lime = df_train df_lime.head(3)
获取班级名称 可以使用对象 unique() 访问标签。您应该看到:
- '<= 50K'
- '> 50K'
# Get the class name class_names = df_lime.label.unique() class_names
array(['<=50K', '>50K'], dtype=object)
分类特征列的索引
您可以使用之前学习的方法获取组的名称。使用 LabelEncoder 对标签进行编码。对所有分类特征重复此操作。
## import sklearn.preprocessing as preprocessing categorical_names = {} for feature in CATE_FEATURES: le = preprocessing.LabelEncoder() le.fit(df_lime[feature]) df_lime[feature] = le.transform(df_lime[feature]) categorical_names[feature] = le.classes_ print(categorical_names)
{'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private', 'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'], dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th', 'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad', 'Masters', 'Preschool', 'Prof-school', 'Some-college'], dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse', 'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'], dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair', 'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners', 'Machine-op-inspct', 'Other-service', 'Priv-house-serv', 'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support', 'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child', 'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other', 'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba', 'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England', 'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras', 'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica', 'Japan', 'Laos', 'Mexico', 'Nicaragua', 'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan', 'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam', 'Yugoslavia'], dtype=object)} df_lime.dtypes
age float64 workclass int64 fnlwgt float64 education int64 education_num float64 marital int64 occupation int64 relationship int64 race int64 sex int64 capital_gain float64 capital_loss float64 hours_week float64 native_country int64 label object dtype: object
现在数据集已准备就绪,您可以构建不同的数据集,如下面的 Scikit 学习示例所示。您实际上在管道之外转换数据,以避免 LIME 出现错误。LimeTabularExplainer 中的训练集应该是没有字符串的 numpy 数组。使用上述方法,您已经有一个转换后的训练数据集。
from sklearn.model_selection import train_test_split X_train_lime, X_test_lime, y_train_lime, y_test_lime = train_test_split(df_lime[features], df_lime.label, test_size = 0.2, random_state=0) X_train_lime.head(5)
您可以使用 XGBoost 中的最佳参数来创建管道
model_xgb = make_pipeline( preprocess, xgboost.XGBClassifier(max_depth = 3, gamma = 0.5, n_estimators=600, objective='binary:logistic', silent=True, nthread=1)) model_xgb.fit(X_train_lime, y_train_lime)
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/preprocessing/_encoders.py:351: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values. If you want the future behavior and silence this warning, you can specify "categories='auto'."In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly. warnings.warn(msg, FutureWarning)
Pipeline(memory=None, steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None, transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,... reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None, silent=True, subsample=1))])
您会收到一条警告。警告说明您不需要在管道之前创建标签编码器。如果您不想使用 LIME,则可以使用 Scikit-learn 机器学习教程第一部分中的方法。否则,您可以继续使用此方法,首先创建一个编码数据集,然后在管道中设置获取热门编码器。
print("best logistic regression from grid search: %f" % model_xgb.score(X_test_lime, y_test_lime))
best logistic regression from grid search: 0.873157
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01], [9.5173013e-01, 4.8269872e-02], [7.9344827e-01, 2.0655173e-01], ..., [9.9031430e-01, 9.6856682e-03], [6.4581633e-04, 9.9935418e-01], [9.7104281e-01, 2.8957171e-02]], dtype=float32)
在实际使用 LIME 之前,让我们创建一个包含错误分类特征的 numpy 数组。稍后您可以使用该列表来了解哪些因素误导了分类器。
temp = pd.concat([X_test_lime, y_test_lime], axis= 1) temp['predicted'] = model_xgb.predict(X_test_lime) temp['wrong']= temp['label'] != temp['predicted'] temp = temp.query('wrong==True').drop('wrong', axis=1) temp= temp.sort_values(by=['label']) temp.shape
(826,16)
您创建一个 lambda 函数来使用新数据从模型中检索预测。您很快就会需要它。
predict_fn = lambda x: model_xgb.predict_proba(x).astype(float) X_test_lime.dtypes
age float64 workclass int64 fnlwgt float64 education int64 education_num float64 marital int64 occupation int64 relationship int64 race int64 sex int64 capital_gain float64 capital_loss float64 hours_week float64 native_country int64 dtype: object
predict_fn(X_test_lime)
array([[7.96461046e-01, 2.03538969e-01], [9.51730132e-01, 4.82698716e-02], [7.93448269e-01, 2.06551731e-01], ..., [9.90314305e-01, 9.68566816e-03], [6.45816326e-04, 9.99354184e-01], [9.71042812e-01, 2.89571714e-02]])
将 pandas 数据框转换为 numpy 数组
X_train_lime = X_train_lime.values X_test_lime = X_test_lime.values X_test_lime
array([[4.00000e+01, 5.00000e+00, 1.93524e+05, ..., 0.00000e+00, 4.00000e+01, 3.80000e+01], [2.70000e+01, 4.00000e+00, 2.16481e+05, ..., 0.00000e+00, 4.00000e+01, 3.80000e+01], [2.50000e+01, 4.00000e+00, 2.56263e+05, ..., 0.00000e+00, 4.00000e+01, 3.80000e+01], ..., [2.80000e+01, 6.00000e+00, 2.11032e+05, ..., 0.00000e+00, 4.00000e+01, 2.50000e+01], [4.40000e+01, 4.00000e+00, 1.67005e+05, ..., 0.00000e+00, 6.00000e+01, 3.80000e+01], [5.30000e+01, 4.00000e+00, 2.57940e+05, ..., 0.00000e+00, 4.00000e+01, 3.80000e+01]])
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01], [9.5173013e-01, 4.8269872e-02], [7.9344827e-01, 2.0655173e-01], ..., [9.9031430e-01, 9.6856682e-03], [6.4581633e-04, 9.9935418e-01], [9.7104281e-01, 2.8957171e-02]], dtype=float32)
print(features, class_names, categorical_features, categorical_names)
['age', 'workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country'] ['<=50K' '>50K'] [1, 3, 5, 6, 7, 8, 9, 13] {'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private', 'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'], dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th', 'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad', 'Masters', 'Preschool', 'Prof-school', 'Some-college'], dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse', 'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'], dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair', 'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners', 'Machine-op-inspct', 'Other-service', 'Priv-house-serv', 'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support', 'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child', 'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other', 'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba', 'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England', 'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras', 'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica', 'Japan', 'Laos', 'Mexico', 'Nicaragua', 'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan', 'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam', 'Yugoslavia'], dtype=object)}
import lime import lime.lime_tabular ### Train should be label encoded not one hot encoded explainer = lime.lime_tabular.LimeTabularExplainer(X_train_lime , feature_names = features, class_names=class_names, categorical_features=categorical_features, categorical_names=categorical_names, kernel_width=3)
让我们从测试集中随机选择一个家庭,看看模型预测以及计算机如何做出选择。
import numpy as np np.random.seed(1) i = 100 print(y_test_lime.iloc[i]) >50K
X_test_lime[i]
array([4.20000e+01, 4.00000e+00, 1.76286e+05, 7.00000e+00, 1.20000e+01, 2.00000e+00, 4.00000e+00, 0.00000e+00, 4.00000e+00, 1.00000e+00, 0.00000e+00, 0.00000e+00, 4.00000e+01, 3.80000e+01])
您可以使用 explain_instance 来使用解释器来检查模型背后的解释
exp = explainer.explain_instance(X_test_lime[i], predict_fn, num_features=6) exp.show_in_notebook(show_all=False)
我们可以看到分类器正确地预测了家庭收入。收入确实在 50k 以上。
我们首先要说的是,分类器对预测的概率不太确定。机器预测家庭收入超过 50k 的概率为 64%。这 64% 由资本收益和婚姻收入组成。蓝色对正类有负贡献,橙色线则有正贡献。
分类器很困惑,因为这个家庭的资本收益为零,而资本收益通常是财富的良好预测指标。此外,该家庭每周工作时间不到 40 小时。年龄、职业和性别对分类器有正向贡献。
如果婚姻状况为单身,分类器会预测收入低于 50 万(0.64-0.18 = 0.46)
我们可以尝试用另一个被错误分类的家庭
temp.head(3) temp.iloc[1,:-2]
age 58 workclass 4 fnlwgt 68624 education 11 education_num 9 marital 2 occupation 4 relationship 0 race 4 sex 1 capital_gain 0 capital_loss 0 hours_week 45 native_country 38 Name: 20931, dtype: object
i = 1 print('This observation is', temp.iloc[i,-2:])
This observation is label <=50K predicted >50K Name: 20931, dtype: object
exp = explainer.explain_instance(temp.iloc[1,:-2], predict_fn, num_features=6) exp.show_in_notebook(show_all=False)
分类器预测收入低于 50k,但事实并非如此。这个家庭看起来很奇怪。它没有资本收益,也没有资本损失。他离婚了,60 岁,受过教育,即 education_num > 12。根据整体模式,这个家庭的收入应该像分类器解释的那样,低于 50k。
你试着使用 LIME。你会发现分类器存在严重错误。
您可以查看库所有者的 GitHub。他们为图像和文本分类提供了额外的文档。
结语
以下是 scikit learn 版本 >=0.20 的一些有用命令列表
创建训练/测试数据集 | 练习生分手 |
构建管道 | |
选择列并应用转换 | 制作柱变压器 |
转型类型 | |
规范 | 标准缩放器 |
最小最大 | 最小最大缩放器 |
归一化 | 归一化器 |
估算缺失值 | 估算 |
转换分类 | 热编码器 |
拟合和变换数据 | 拟合变换 |
建立管道 | make_pipeline |
基本型号 | |
逻辑回归 | Logistic回归 |
XGBoost | XGB分类器 |
神经网络 | MLP分类器 |
网格搜索 | 网格搜索CV |
随机搜索 | 随机搜索CV |