SKlearn使用教程 - 1:基础操作

564 阅读9分钟

SKlearn使用教程 - 1:基础操作

一、进阶引言

我们将在教程中绘制几个图。 我们激活 matplotlib 以显示内联图。

%matplotlib inline
import matplotlib.pyplot as plt

scikit-learn 提供最先进的机器学习算法。 然而,这些算法不能直接用于原始数据。 原始数据需要事先进行预处理。 因此,除了机器学习算法,scikit-learn 还提供了一套预处理方法。 此外,scikit-learn 提供了用于流水线化这些估计器(即转换器、回归器、分类器、聚类器等)的连接器。

在本教程中,我们将展示一组 scikit-learn 功能,允许对估计器进行流水线化、评估这些流水线、使用超参数优化调整这些流水线以及创建复杂的预处理步骤。

二、具体操作

1. 基础用例:训练并测试一个分类器

对于第一个示例,我们将在数据集上训练和测试分类器。 我们将使用这个例子来回忆 scikit-learn 的 API。

我们将使用数字数据集,它是手写数字的数据集。

from sklearn.datasets import load_digits

X, y = load_digits(return_X_y=True)

X 中的每一行包含 64 个图像像素的灰度。 对于 X 中的每个样本,我们得到表示所写数字的真实值 y(具体标签,即64个像素组成的数字)。

# 展示第一个样本的灰度图
plt.imshow(X[0].reshape(8, 8), cmap='gray');
# 关闭坐标轴
plt.axis('off')
# 打印第一个样本的灰度图所代表的标签
print('The digit in the image is {}'.format(y[0]))
The digit in the image is 0



download.png

# 查看X中一共有多少个样本
len(X),len(X) == len(y)
(1797, True)

在机器学习中,我们应该通过对不同的数据集进行训练和测试(后面还有可能加入验证集)来评估我们的模型。 train_test_split 是一个实用函数,用于将数据分成两个独立的集合。 分层参数强制训练和测试数据集的类分布与整个数据集的类分布相同。

  • train_test_split 具体用法:
  1. train_test_split是交叉验证中常用的函数,功能是从样本中随机的按比例选取train_data和test_data。
  2. 常见形式为:X_train,X_test, y_train, y_test=cross_validation.train_test_split(train_data,train_target,test_size=0.4, random_state=0),cross_validatio为交叉验证
  3. 参数解释:
    • train_data:所要划分的样本特征集
    • train_target:所要划分的样本结果
    • test_size:样本占比,如果是整数的话就是样本的数量
    • random_state:是随机数的种子。
    • 随机数种子:其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如你每次都填1,其他参数一样的情况下你得到的随机数组是一样的。但填0或不填,每次都会不一样。
    • stratify:stratify是为了保持split之前类的分布,例如训练集和测试集数量的比例是 A:B= 4:1,等同于split前的比例(80:20)。通常在这种类分布不平衡的情况下会用到stratify。
from sklearn.model_selection import train_test_split
# 分离训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
# 查看训练集的像素灰度
X_train[:5,:5]
array([[ 0.,  2., 12., 16., 12.],
       [ 0.,  0.,  0., 13., 14.],
       [ 0.,  2., 11., 16., 12.],
       [ 0.,  0., 11., 10.,  0.],
       [ 0.,  0.,  3., 12., 16.]])
y_train[:5]
array([2, 1, 2, 1, 7])

一旦我们有了独立的训练和测试集,我们就可以使用 fit 方法学习机器学习模型。 我们将使用 score 方法来测试这个方法,依赖于默认的准确度指标。

from sklearn.linear_model import LogisticRegression
# 创建逻辑回归模型
clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=5000, random_state=42)
# 训练
clf.fit(X_train, y_train)
# 测试并返回精度
accuracy = clf.score(X_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
Accuracy score of the LogisticRegression is 0.95

scikit-learn 的 API 在分类器中是一致的。 因此,我们可以轻松地将 LogisticRegression 分类器替换为 RandomForestClassifier。 这些更改很小,仅与分类器实例的创建有关。

from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(n_estimators=100, n_jobs=-1, random_state=42)
clf.fit(X_train, y_train)
accuracy = clf.score(X_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
Accuracy score of the RandomForestClassifier is 0.96

A. 练习

上面已经给出了基本分类器的步骤,下面我们做一些练习来巩固一下。

  • 导入乳腺癌数据集. 从 sklearn.datasets 中导入 load_breast_cancer 函数,便于导入整个数据集
from sklearn.datasets import load_breast_cancer 
X,y = load_breast_cancer(return_X_y = True)
  • 使用 sklearn.model_selection.train_test_split 拆分数据集以保留 30% 用于测试。 确保对数据进行分层(即使用 stratify 参数)并将 random_state 设置为 0。
from sklearn.model_selection import train_test_split
# 注意此处的返回值顺序:X_train,X_test, y_train,y_test
X_train,X_test,y_train,y_test = train_test_split(X, y, random_state = 0, stratify  = y)
  • 使用训练数据训练监督分类器。
# 使用逻辑回归用于预测
from sklearn.linear_model import LogisticRegression
# 创建逻辑回归模型
clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=5000, random_state=0)
  • 使用拟合分类器预测测试集的分类标签。
clf.fit(X_train,y_train)
LogisticRegression(max_iter=5000, random_state=0)
  • 计算测试集上的平衡精度。 您需要从 sklearn.metrics 导入balanced_accuracy_score,

  • 函数用法:balanced_accuracy_score(y_true, y_pred)

from sklearn.metrics import balanced_accuracy_score
acc = balanced_accuracy_score(y_test,clf.predict(X_test))
acc
0.9344863731656184

可以看出正确率不错,我们可以尝试一下修改逻辑回归的solver,修改为梯度下降——liblinear(之前是lbfgs —— 拟牛顿法)

# 导入模型
clf_liblin = LogisticRegression(solver='liblinear', multi_class='auto', max_iter=5000, random_state=0)
# 训练
clf_liblin.fit(X_train,y_train)
# 测试性能
balanced_accuracy_score(y_test,clf_liblin.predict(X_test))
0.9344863731656184

B. 性能可视化

from sklearn.metrics import accuracy_score
# 训练集表现
l1_train_predict = []
l2_train_predict = []

# 测试集表现
l1_test_predict = []
l2_test_predict = []

for c in np.linspace(0.01, 2, 50) :
    # 拟牛顿法在1000次时难以收敛
    lr_l1 = LogisticRegression( C=c, solver='lbfgs', max_iter=5000)
    lr_l2 = LogisticRegression( C=c, solver='liblinear', max_iter=5000)
    
    # 训练模型,记录L1正则化模型在训练集测试集上的表现
    lr_l1.fit(X_train, y_train)
    l1_train_predict.append(accuracy_score(lr_l1.predict(X_train), y_train))
    l1_test_predict.append(accuracy_score(lr_l1.predict(X_test), y_test))
    
    # 记录L2正则化模型的表现
    lr_l2.fit(X_train, y_train)
    l2_train_predict.append(accuracy_score(lr_l2.predict(X_train), y_train))
    l2_test_predict.append(accuracy_score(lr_l2.predict(X_test), y_test))
    
data = [l1_train_predict, l2_train_predict, l1_test_predict, l2_test_predict]
label = ['lbfgs_train', 'liblin_train', 'lbfgs_test', "liblin_test"]
color = ['red', 'green', 'orange', 'blue']

plt.figure(figsize=(12, 6))
for i in range(4) :
    plt.plot(np.linspace(0.01, 2, 50), data[i], label=label[i], color=color[i])

plt.legend(loc="best")
plt.show()

download.png

  • 注意:解决jupyter botebook 输出超出范围 limit_output extension: Maximum message size of 10000:利用Nbextensions 修改LimitOutput

可以看出拟牛顿法的性能更优(但难以收敛)

2. 高级用例:在训练和测试分类器之前预处理数据

A. 标准化数据

在学习模型之前可能需要进行预处理。 例如,用户可能对创建手工制作的特征感兴趣,或者算法可能会对数据做出一些先验假设。

在我们的例子中,LogisticRegression 使用的求解器期望数据被标准化。 因此,我们需要在训练模型之前对数据进行标准化。 为了观察这个必要条件,我们将检查训练模型所需的迭代次数。

from sklearn.linear_model import LogisticRegression

clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=5000, random_state=42)
clf.fit(X_train, y_train)

accuracy = clf.score(X_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
print('{} required {} iterations to be fitted'.format(clf.__class__.__name__, clf.n_iter_[0]))
Accuracy score of the LogisticRegression is 0.95
LogisticRegression required 3086 iterations to be fitted

MinMaxScaler 转换器用于规范化数据。 该缩放器应以下列方式应用:学习(即拟合方法)训练集的统计数据,并标准化(即变换方法)训练集和测试集。 最后,我们将训练和测试模型和缩放的数据集。

from sklearn.preprocessing import MinMaxScaler

# 实例化正规器对象
scaler = MinMaxScaler()
# 规范训练数据集
X_train_scaled = scaler.fit_transform(X_train)
# 规范测试数据集
X_test_scaled = scaler.transform(X_test)

# 注意此处的最大迭代次数设为了 1000
clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000, random_state=42)
clf.fit(X_train_scaled, y_train)
accuracy = clf.score(X_test_scaled, y_test)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
print('{} required {} iterations to be fitted'.format(clf.__class__.__name__, clf.n_iter_[0]))
Accuracy score of the LogisticRegression is 0.96
LogisticRegression required 189 iterations to be fitted

通过缩放数据,模型的收敛速度比未缩放的数据快得多。并且准确率也提高了一丢丢。

B. 错误的预处理模式

我们强调了如何预处理和充分训练机器学习模型。 发现预处理数据的错误方式也很有趣。 有两个潜在的错误很容易犯,但很容易发现。

第一种错误模式是在将完整集拆分为训练集和测试集之前对数据进行标准化。

scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)
X_train_prescaled, X_test_prescaled, y_train_prescaled, y_test_prescaled = train_test_split(
    X_scaled, y, stratify=y, random_state=42)

clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000, random_state=42)
clf.fit(X_train_prescaled, y_train_prescaled)
accuracy = clf.score(X_test_prescaled, y_test_prescaled)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
Accuracy score of the LogisticRegression is 0.96

第二种错误模式是独立标准化训练集和测试集。 它会在训练集和测试集上调用 fit 方法。 因此,训练集和测试集的标准化是不同的。

scaler = MinMaxScaler()
X_train_prescaled = scaler.fit_transform(X_train)
X_test_prescaled = scaler.fit_transform(X_test)

clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000, random_state=42)
clf.fit(X_train_prescaled, y_train)
accuracy = clf.score(X_test_prescaled, y_test)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
Accuracy score of the LogisticRegression is 0.96

C. 保持简单,蠢拙:使用 scikit-learn 的管道连接器

前两种模式是数据泄漏的问题。 然而,当必须手动进行预处理时,这很难防止这种错误。 因此,scikit-learn 引入了 Pipeline 对象。 它顺序连接几个转换器和一个分类器(或回归器)。 我们可以创建一个管道:

from sklearn.pipeline import Pipeline

pipe = Pipeline(steps=[('scaler', MinMaxScaler()),
                       ('clf', LogisticRegression(solver='lbfgs', multi_class='auto', random_state=42))])

我们看到这个管道包含缩放器和分类器的参数。 有时,为管道中的每个估算器命名可能很乏味。 make_pipeline 将自动为每个估计器命名,该名称是类名的小写。

from sklearn.pipeline import make_pipeline
pipe = make_pipeline(MinMaxScaler(),
                     LogisticRegression(solver='lbfgs', multi_class='auto', random_state=42, max_iter=1000))

管道将具有相同的 API。 我们使用 fit 来训练分类器并使用 score 来检查准确性。 但是,调用 fit 将调用管道中所有转换器的方法 fit_transform 。 调用 score(或 predict 和 predict_proba)将调用管道中所有转换器的内部转换。 它对应于 2.2.1小节中的规范化过程。

pipe.fit(X_train, y_train)
accuracy = pipe.score(X_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(pipe.__class__.__name__, accuracy))
Accuracy score of the Pipeline is 0.96

我们可以使用 get_params() 检查管道的所有参数。

pipe.get_params()
{'memory': None,
 'steps': [('minmaxscaler', MinMaxScaler()),
  ('logisticregression', LogisticRegression(max_iter=1000, random_state=42))],
 'verbose': False,
 'minmaxscaler': MinMaxScaler(),
 'logisticregression': LogisticRegression(max_iter=1000, random_state=42),
 'minmaxscaler__clip': False,
 'minmaxscaler__copy': True,
 'minmaxscaler__feature_range': (0, 1),
 'logisticregression__C': 1.0,
 'logisticregression__class_weight': None,
 'logisticregression__dual': False,
 'logisticregression__fit_intercept': True,
 'logisticregression__intercept_scaling': 1,
 'logisticregression__l1_ratio': None,
 'logisticregression__max_iter': 1000,
 'logisticregression__multi_class': 'auto',
 'logisticregression__n_jobs': None,
 'logisticregression__penalty': 'l2',
 'logisticregression__random_state': 42,
 'logisticregression__solver': 'lbfgs',
 'logisticregression__tol': 0.0001,
 'logisticregression__verbose': 0,
 'logisticregression__warm_start': False}

D. 练习

重用第一个练习的乳腺癌数据集来训练可以从 linear_model 导入的 SGDClassifier。 使用此分类器和从 sklearn.preprocessing 导入的 StandardScaler 转换器制作管道。 训练和测试这个管道。

# 用于导入数据集
from sklearn.datasets import load_breast_cancer 
# 用于分离原始数据集为训练集和测试集
from sklearn.model_selection import train_test_split
# 导入StandardScaler 规范器
from sklearn.preprocessing import StandardScaler 
# 导入管道,避免上面的两种错误规范化
from sklearn.pipeline import Pipeline
# 导入SGDClassifier分类器
from sklearn.linear_model import SGDClassifier
  • SGDClassifier参数说明:
  1. loss function可以通过loss参数进行设置。SGDClassifier支持下面的loss函数:

    • loss=”hinge”: (soft-margin)线性SVM.
    • loss=”modified_huber”: 带平滑的hinge loss.
    • loss=”log”: logistic回归
  2. 可以通过penalty参数设置具体的惩罚。SGD支持以下处罚:

    • penalty=“l2”:L2正则化惩罚coef_。
    • penalty=“l1”:L1正则化惩罚coef_。
    • penalty=“elasticnet”:L2和L1的组合;
# 导入并分离数据
X, y = load_breast_cancer(return_X_y = True)
X_train,X_test,y_train,y_test = train_test_split(X,y ,random_state = 0, stratify  = y )
# 注意Pipeline 和 make_pipeline的初始化区别
pipe = make_pipeline(
    StandardScaler(),
    SGDClassifier(loss='log',penalty='l2', max_iter=1000)
)
# 每次执行都不同
pipe.fit(X_train,y_train)
accuracy = pipe.score(X_test, y_test)
print('Accuracy score of the %s is %f'%(pipe.__class__.__name__, accuracy))
Accuracy score of the Pipeline is 0.958042
# 查看管道参数:
pipe.get_params()
{'memory': None,
 'steps': [('standardscaler', StandardScaler()),
  ('sgdclassifier', SGDClassifier(loss='log'))],
 'verbose': False,
 'standardscaler': StandardScaler(),
 'sgdclassifier': SGDClassifier(loss='log'),
 'standardscaler__copy': True,
 'standardscaler__with_mean': True,
 'standardscaler__with_std': True,
 'sgdclassifier__alpha': 0.0001,
 'sgdclassifier__average': False,
 'sgdclassifier__class_weight': None,
 'sgdclassifier__early_stopping': False,
 'sgdclassifier__epsilon': 0.1,
 'sgdclassifier__eta0': 0.0,
 'sgdclassifier__fit_intercept': True,
 'sgdclassifier__l1_ratio': 0.15,
 'sgdclassifier__learning_rate': 'optimal',
 'sgdclassifier__loss': 'log',
 'sgdclassifier__max_iter': 1000,
 'sgdclassifier__n_iter_no_change': 5,
 'sgdclassifier__n_jobs': None,
 'sgdclassifier__penalty': 'l2',
 'sgdclassifier__power_t': 0.5,
 'sgdclassifier__random_state': None,
 'sgdclassifier__shuffle': True,
 'sgdclassifier__tol': 0.001,
 'sgdclassifier__validation_fraction': 0.1,
 'sgdclassifier__verbose': 0,
 'sgdclassifier__warm_start': False}