选自 Python-Machine-Learning-Book On GitHub
python
做者:Sebastian Raschkagit
翻译&整理 By Samgithub
这个系列的文章写得也是够长时间的了,不过总算是写完了,今晚就把以前的内容作一次汇总,同时也把相关code打包share出来,后台回复“评估”获取吧。算法
因为文章较长,因此我仍是先把目录提早。数组
🚙 🚙 🚙微信
1、认识管道流网络
1.1 数据导入app
1.2 使用管道建立工做流dom
2、K折交叉验证机器学习
2.1 K折交叉验证原理
2.2 K折交叉验证明现
3、曲线调参
3.1 模型准确度
3.2 绘制学习曲线获得样本数与准确率的关系
3.3 绘制验证曲线获得超参和准确率关系
4、网格搜索
4.1 两层for循环暴力检索
4.2 构建字典暴力检索
5、嵌套交叉验证
6、相关评价指标
6.1 混淆矩阵及其实现
6.2 相关评价指标实现
6.3 ROC曲线及其实现
1、认识管道流 🐢
今天先介绍一下管道工做流的操做。
“管道工做流”这个概念可能有点陌生,其实能够理解为一个容器,而后把咱们须要进行的操做都封装在这个管道里面进行操做,好比数据标准化、特征降维、主成分分析、模型预测等等,下面仍是以一个实例来说解。
1.1 数据导入与预处理
本次咱们导入一个二分类数据集 Breast Cancer Wisconsin,它包含569个样本。首列为主键ID,第2列为类别值(M=恶性肿瘤,B=良性肿瘤),第3-32列是实数值的特征。
先导入数据集:
1# 导入相关数据集
2import pandas as pd
3import urllib
4try:
5 df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases'
6 '/breast-cancer-wisconsin/wdbc.data', header=None)
7except urllib.error.URLError:
8 df = pd.read_csv('https://raw.githubusercontent.com/rasbt/'
9 'python-machine-learning-book/master/code/'
10 'datasets/wdbc/wdbc.data', header=None)
11print('rows, columns:', df.shape)
12df.head()
使用咱们学习过的LabelEncoder来转化类别特征:
1from sklearn.preprocessing import LabelEncoder
2X = df.loc[:, 2:].values
3y = df.loc[:, 1].values
4le = LabelEncoder()
5# 将目标转为0-1变量
6y = le.fit_transform(y)
7le.transform(['M', 'B'])
划分训练验证集:
1## 建立训练集和测试集
2from sklearn.model_selection import train_test_split
3X_train, X_test, y_train, y_test = \
4 train_test_split(X, y, test_size=0.20, random_state=1)
1.2 使用管道建立工做流
不少机器学习算法要求特征取值范围要相同,所以须要对特征作标准化处理。此外,咱们还想将原始的30维度特征压缩至更少维度,这就须要用到主成分分析,要用PCA来完成,再接着就能够进行logistic回归预测了。
Pipeline对象接收元组构成的列表做为输入,每一个元组第一个值做为变量名,元组第二个元素是sklearn中的transformer或Estimator。管道中间每一步由sklearn中的transformer构成,最后一步是一个Estimator。
本次数据集中,管道包含两个中间步骤:StandardScaler和PCA,其都属于transformer,而逻辑斯蒂回归分类器属于Estimator。
本次实例,当管道pipe_lr执行fit方法时:
1)StandardScaler执行fit和transform方法;
2)将转换后的数据输入给PCA;
3)PCA一样执行fit和transform方法;
4)最后数据输入给LogisticRegression,训练一个LR模型。
对于管道来讲,中间有多少个transformer均可以。管道的工做方式能够用下图来展现(必定要注意管道执行fit方法,而transformer要执行fit_transform):
上面的代码实现以下:
1from sklearn.preprocessing import StandardScaler # 用于进行数据标准化
2from sklearn.decomposition import PCA # 用于进行特征降维
3from sklearn.linear_model import LogisticRegression # 用于模型预测
4from sklearn.pipeline import Pipeline
5pipe_lr = Pipeline([('scl', StandardScaler()),
6 ('pca', PCA(n_components=2)),
7 ('clf', LogisticRegression(random_state=1))])
8pipe_lr.fit(X_train, y_train)
9print('Test Accuracy: %.3f' % pipe_lr.score(X_test, y_test))
10y_pred = pipe_lr.predict(X_test)
Test Accuracy: 0.947
2、K折交叉验证 🔍
为何要评估模型的泛化能力,相信这个你们应该没有疑惑,一个模型若是性能很差,要么是由于模型过于复杂致使过拟合(高方差),要么是模型过于简单致使致使欠拟合(高误差)。如何评估它,用什么数据来评估它,成为了模型评估须要重点考虑的问题。
咱们常规作法,就是将数据集划分为3部分,分别是训练、测试和验证,彼此之间的数据不重叠。但,若是咱们碰见了数据量很少的时候,这种操做就显得不太现实,这个时候k折交叉验证就发挥优点了。
2.1 K折交叉验证原理
先很少说,先贴一张原理图(以10折交叉验证为例)。
k折交叉验证步骤:
Step 1:使用不重复抽样将原始数据随机分为k份;
Step 2:其中k-1份数据用于模型训练,剩下的那1份数据用于测试模型;
Step 3:重复Step 2 k次,获得k个模型和他的评估结果。
Step 4:计算k折交叉验证结果的平均值做为参数/模型的性能评估。
2.1 K折交叉验证明现
K折交叉验证,那么K的取值该如何确认呢?通常咱们默认10折,但根据实际状况有所调整。咱们要知道,当K很大的时候,你须要训练的模型就会不少,这样子对效率影响较大,并且每一个模型的训练集都差很少,效果也差很少。咱们经常使用的K值在5~12。
咱们根据k折交叉验证的原理步骤,在sklearn中进行10折交叉验证的代码实现:
1import numpy as np
2from sklearn.model_selection import StratifiedKFold
3kfold = StratifiedKFold(n_splits=10,
4 random_state=1).split(X_train, y_train)
5scores = []
6for k, (train, test) in enumerate(kfold):
7 pipe_lr.fit(X_train[train], y_train[train])
8 score = pipe_lr.score(X_train[test], y_train[test])
9 scores.append(score)
10 print('Fold: %s, Class dist.: %s, Acc: %.3f' % (k+1,
11 np.bincount(y_train[train]), score))
12print('\nCV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))
output:
固然,实际使用的时候不必这样子写,sklearn已经有现成封装好的方法,直接调用便可。
1from sklearn.model_selection import cross_val_score
2scores = cross_val_score(estimator=pipe_lr,
3 X=X_train,
4 y=y_train,
5 cv=10,
6 n_jobs=1)
7print('CV accuracy scores: %s' % scores)
8print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))
3、曲线调参 🌈
咱们讲到的曲线,具体指的是学习曲线(learning curve)和验证曲线(validation curve)。
3.1 模型准确率(Accuracy)
模型准确率反馈了模型的效果,你们看下图:
1)左上角子的模型误差很高。它的训练集和验证集准确率都很低,极可能是欠拟合。解决欠拟合的方法就是增长模型参数,好比,构建更多的特征,减少正则项。
2)右上角子的模型方差很高,表现就是训练集和验证集准确率相差太多。解决过拟合的方法有增大训练集或者下降模型复杂度,好比增大正则项,或者经过特征选择减小特征数。
3)右下角的模型就很好。
3.2 绘制学习曲线获得样本数与准确率的关系
直接上代码:
1import matplotlib.pyplot as plt
2from sklearn.model_selection import learning_curve
3pipe_lr = Pipeline([('scl', StandardScaler()),
4 ('clf', LogisticRegression(penalty='l2', random_state=0))])
5train_sizes, train_scores, test_scores =\
6 learning_curve(estimator=pipe_lr,
7 X=X_train,
8 y=y_train,
9 train_sizes=np.linspace(0.1, 1.0, 10), #在0.1和1间线性的取10个值
10 cv=10,
11 n_jobs=1)
12train_mean = np.mean(train_scores, axis=1)
13train_std = np.std(train_scores, axis=1)
14test_mean = np.mean(test_scores, axis=1)
15test_std = np.std(test_scores, axis=1)
16plt.plot(train_sizes, train_mean,
17 color='blue', marker='o',
18 markersize=5, label='training accuracy')
19plt.fill_between(train_sizes,
20 train_mean + train_std,
21 train_mean - train_std,
22 alpha=0.15, color='blue')
23plt.plot(train_sizes, test_mean,
24 color='green', linestyle='--',
25 marker='s', markersize=5,
26 label='validation accuracy')
27plt.fill_between(train_sizes,
28 test_mean + test_std,
29 test_mean - test_std,
30 alpha=0.15, color='green')
31plt.grid()
32plt.xlabel('Number of training samples')
33plt.ylabel('Accuracy')
34plt.legend(loc='lower right')
35plt.ylim([0.8, 1.0])
36plt.tight_layout()
37plt.show()
Learning_curve中的train_sizes参数控制产生学习曲线的训练样本的绝对/相对数量,此处,咱们设置的train_sizes=np.linspace(0.1, 1.0, 10),将训练集大小划分为10个相等的区间,在0.1和1之间线性的取10个值。learning_curve默认使用分层k折交叉验证计算交叉验证的准确率,咱们经过cv设置k。
下图能够看到,模型在测试集表现很好,不过训练集和测试集的准确率仍是有一段小间隔,多是模型有点过拟合。
3.3 绘制验证曲线获得超参和准确率关系
验证曲线是用来提升模型的性能,验证曲线和学习曲线很相近,不一样的是这里画出的是不一样参数下模型的准确率而不是不一样训练集大小下的准确率:
1from sklearn.model_selection import validation_curve
2param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]
3train_scores, test_scores = validation_curve(
4 estimator=pipe_lr,
5 X=X_train,
6 y=y_train,
7 param_name='clf__C',
8 param_range=param_range,
9 cv=10)
10train_mean = np.mean(train_scores, axis=1)
11train_std = np.std(train_scores, axis=1)
12test_mean = np.mean(test_scores, axis=1)
13test_std = np.std(test_scores, axis=1)
14plt.plot(param_range, train_mean,
15 color='blue', marker='o',
16 markersize=5, label='training accuracy')
17plt.fill_between(param_range, train_mean + train_std,
18 train_mean - train_std, alpha=0.15,
19 color='blue')
20plt.plot(param_range, test_mean,
21 color='green', linestyle='--',
22 marker='s', markersize=5,
23 label='validation accuracy')
24plt.fill_between(param_range,
25 test_mean + test_std,
26 test_mean - test_std,
27 alpha=0.15, color='green')
28plt.grid()
29plt.xscale('log')
30plt.legend(loc='lower right')
31plt.xlabel('Parameter C')
32plt.ylabel('Accuracy')
33plt.ylim([0.8, 1.0])
34plt.tight_layout()
35plt.show()
咱们获得了参数C的验证曲线。和learning_curve方法很像,validation_curve方法使用采样k折交叉验证来评估模型的性能。在validation_curve内部,咱们设定了用来评估的参数(这里咱们设置C做为观测)。
从下图能够看出,最好的C值是0.1。
4、网格搜索
网格搜索(grid search),做为调参很经常使用的方法,这边仍是要简单介绍一下。
在咱们的机器学习算法中,有一类参数,须要人工进行设定,咱们称之为“超参”,也就是算法中的参数,好比学习率、正则项系数或者决策树的深度等。
网格搜索就是要找到一个最优的参数,从而使得模型的效果最佳,而它实现的原理其实就是暴力搜索;即咱们事先为每一个参数设定一组值,而后穷举各类参数组合,找到最好的那一组。
4.1. 两层for循环暴力检索
网格搜索的结果得到了指定的最优参数值,c为100,gamma为0.001
1# naive grid search implementation
2from sklearn.datasets import load_iris
3from sklearn.svm import SVC
4from sklearn.model_selection import train_test_split
5iris = load_iris()
6X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=0)
7print("Size of training set: %d size of test set: %d" % (X_train.shape[0], X_test.shape[0]))
8best_score = 0
9for gamma in [0.001, 0.01, 0.1, 1, 10, 100]:
10 for C in [0.001, 0.01, 0.1, 1, 10, 100]:
11 # for each combination of parameters
12 # train an SVC
13 svm = SVC(gamma=gamma, C=C)
14 svm.fit(X_train, y_train)
15 # evaluate the SVC on the test set
16 score = svm.score(X_test, y_test)
17 # if we got a better score, store the score and parameters
18 if score > best_score:
19 best_score = score
20 best_parameters = {'C': C, 'gamma': gamma}
21print("best score: ", best_score)
22print("best parameters: ", best_parameters)
output:
Size of training set: 112 size of test set: 38
best score: 0.973684210526
best parameters: {'C': 100, 'gamma': 0.001}
4.2. 构建字典暴力检索
网格搜索的结果得到了指定的最优参数值,c为1
1from sklearn.svm import SVC
2from sklearn.model_selection import GridSearchCV
3pipe_svc = Pipeline([('scl', StandardScaler()),
4 ('clf', SVC(random_state=1))])
5param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]
6param_grid = [{'clf__C': param_range,
7 'clf__kernel': ['linear']},
8 {'clf__C': param_range,
9 'clf__gamma': param_range,
10 'clf__kernel': ['rbf']}]
11gs = GridSearchCV(estimator=pipe_svc,
12 param_grid=param_grid,
13 scoring='accuracy',
14 cv=10,
15 n_jobs=-1)
16gs = gs.fit(X_train, y_train)
17print(gs.best_score_)
18print(gs.best_params_)
output:
0.978021978022
{'clf__C': 0.1, 'clf__kernel': 'linear'}
GridSearchCV中param_grid参数是字典构成的列表。对于线性SVM,咱们只评估参数C;对于RBF核SVM,咱们评估C和gamma。最后, 咱们经过best_parmas_获得最优参数组合。
接着,咱们直接利用最优参数建模(best_estimator_):
1clf = gs.best_estimator_
2clf.fit(X_train, y_train)
3print('Test accuracy: %.3f' % clf.score(X_test, y_test))
网格搜索虽然不错,可是穷举过于耗时,sklearn中还实现了随机搜索,使用 RandomizedSearchCV类,随机采样出不一样的参数组合。
5、嵌套交叉验证 🎯
嵌套交叉验证(nested cross validation)选择算法(外循环经过k折等进行参数优化,内循环使用交叉验证),对特定数据集进行模型选择。Varma和Simon在论文Bias in Error Estimation When Using Cross-validation for Model Selection中指出使用嵌套交叉验证获得的测试集偏差几乎就是真实偏差。
嵌套交叉验证外部有一个k折交叉验证将数据分为训练集和测试集,内部交叉验证用于选择模型算法。
下图演示了一个5折外层交叉沿则和2折内部交叉验证组成的嵌套交叉验证,也被称为5*2交叉验证:
咱们仍是用到以前的数据集,相关包的导入操做这里就省略了。
SVM分类器的预测准确率代码实现:
1gs = GridSearchCV(estimator=pipe_svc,
2 param_grid=param_grid,
3 scoring='accuracy',
4 cv=2)
5
6# Note: Optionally, you could use cv=2
7# in the GridSearchCV above to produce
8# the 5 x 2 nested CV that is shown in the figure.
9
10scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5)
11print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))
CV accuracy: 0.965 +/- 0.025
决策树分类器的预测准确率代码实现:
1from sklearn.tree import DecisionTreeClassifier
2
3gs = GridSearchCV(estimator=DecisionTreeClassifier(random_state=0),
4 param_grid=[{'max_depth': [1, 2, 3, 4, 5, 6, 7, None]}],
5 scoring='accuracy',
6 cv=2)
7scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5)
8print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))
CV accuracy: 0.921 +/- 0.029
6、相关评价指标 🎲
6.1 混淆矩阵及其实现
混淆矩阵,你们应该都有据说过,大体就是长下面这样子的:
因此,有几个概念须要先说明:
TP(True Positive): 真实为0,预测也为0
FN(False Negative): 真实为0,预测为1
FP(False Positive): 真实为1,预测为0
TN(True Negative): 真实为1,预测也为1
因此,衍生了几个经常使用的指标:
: 分类模型整体判断的准确率(包括了全部class的整体准确率)
: 预测为0的准确率
: 真实为0的准确率
: 真实为1的准确率
: 预测为1的准确率
: 对于某个分类,综合了Precision和Recall的一个判断指标,F1-Score的值是从0到1的,1是最好,0是最差
: 另一个综合Precision和Recall的标准,F1-Score的变形
再举个例子:
混淆矩阵网络上有不少文章,也不用说刻意地去背去记,须要的时候百度一下你就知道,混淆矩阵实现代码:
1from sklearn.metrics import confusion_matrix
2
3pipe_svc.fit(X_train, y_train)
4y_pred = pipe_svc.predict(X_test)
5confmat = confusion_matrix(y_true=y_test, y_pred=y_pred)
6print(confmat)
output:
[[71 1]
[ 2 40]]
1fig, ax = plt.subplots(figsize=(2.5, 2.5))
2ax.matshow(confmat, cmap=plt.cm.Blues, alpha=0.3)
3for i in range(confmat.shape[0]):
4 for j in range(confmat.shape[1]):
5 ax.text(x=j, y=i, s=confmat[i, j], va='center', ha='center')
6
7plt.xlabel('predicted label')
8plt.ylabel('true label')
9
10plt.tight_layout()
11plt.show()
6.2 相关评价指标实现
分别是准确度、recall以及F1指标的实现。
1from sklearn.metrics import precision_score, recall_score, f1_score
2
3print('Precision: %.3f' % precision_score(y_true=y_test, y_pred=y_pred))
4print('Recall: %.3f' % recall_score(y_true=y_test, y_pred=y_pred))
5print('F1: %.3f' % f1_score(y_true=y_test, y_pred=y_pred))
Precision: 0.976
Recall: 0.952
F1: 0.964
指定评价指标自动选出最优模型:
能够经过在make_scorer中设定参数,肯定须要用来评价的指标(这里用了fl_score),这个函数能够直接输出结果。
1from sklearn.metrics import make_scorer
2
3scorer = make_scorer(f1_score, pos_label=0)
4
5c_gamma_range = [0.01, 0.1, 1.0, 10.0]
6
7param_grid = [{'clf__C': c_gamma_range,
8 'clf__kernel': ['linear']},
9 {'clf__C': c_gamma_range,
10 'clf__gamma': c_gamma_range,
11 'clf__kernel': ['rbf']}]
12
13gs = GridSearchCV(estimator=pipe_svc,
14 param_grid=param_grid,
15 scoring=scorer,
16 cv=10,
17 n_jobs=-1)
18gs = gs.fit(X_train, y_train)
19print(gs.best_score_)
20print(gs.best_params_)
0.982798668208
{'clf__C': 0.1, 'clf__kernel': 'linear'}
6.3 ROC曲线及其实现
若是须要理解ROC曲线,那你就须要先了解一下混淆矩阵了,具体的内容能够查看一下以前的文章,这里重点引入2个概念:
真正率(true positive rate,TPR),指的是被模型正确预测的正样本的比例:
假正率(false positive rate,FPR) ,指的是被模型错误预测的正样本的比例:
ROC曲线概念:
ROC(receiver operating characteristic)接受者操做特征,其显示的是分类器的真正率和假正率之间的关系,以下图所示:
ROC曲线有助于比较不一样分类器的相对性能,其曲线下方的面积为AUC(area under curve),其面积越大则分类的性能越好,理想的分类器auc=1。
ROC曲线绘制:
对于一个特定的分类器和测试数据集,显然只能获得一个分类结果,即一组FPR和TPR结果,而要获得一个曲线,咱们实际上须要一系列FPR和TPR的值。
那么如何处理?很简单,咱们能够根据模型预测的几率值,而且设置不一样的阈值来得到不一样的预测结果。什么意思?
好比说:
5个样本,真实的target(目标标签)是y=c(1,1,0,0,1)
模型分类器将预测样本为1的几率p=c(0.5,0.6,0.55,0.4,0.7)
咱们须要选定阈值才能把几率转化为类别,
若是咱们选定阈值为0.1,那么5个样本被分进1的类别
若是选定0.3,结果仍然同样
若是选了0.45做为阈值,那么只有样本4被分进0
以后把全部获得的全部分类结果计算FTR,PTR,并绘制成线,就能够获得ROC曲线了,当threshold(阈值)取值越多,ROC曲线越平滑。
ROC曲线代码实现:
1from sklearn.metrics import roc_curve, auc
2from scipy import interp
3
4pipe_lr = Pipeline([('scl', StandardScaler()),
5 ('pca', PCA(n_components=2)),
6 ('clf', LogisticRegression(penalty='l2',
7 random_state=0,
8 C=100.0))])
9
10X_train2 = X_train[:, [4, 14]]
11 # 由于所有特征丢进去的话,预测效果太好,画ROC曲线很差看哈哈哈,因此只是取了2个特征
12
13
14cv = list(StratifiedKFold(n_splits=3,
15 random_state=1).split(X_train, y_train))
16
17fig = plt.figure(figsize=(7, 5))
18
19mean_tpr = 0.0
20mean_fpr = np.linspace(0, 1, 100)
21all_tpr = []
22
23for i, (train, test) in enumerate(cv):
24 probas = pipe_lr.fit(X_train2[train],
25 y_train[train]).predict_proba(X_train2[test])
26
27 fpr, tpr, thresholds = roc_curve(y_train[test],
28 probas[:, 1],
29 pos_label=1)
30 mean_tpr += interp(mean_fpr, fpr, tpr)
31 mean_tpr[0] = 0.0
32 roc_auc = auc(fpr, tpr)
33 plt.plot(fpr,
34 tpr,
35 lw=1,
36 label='ROC fold %d (area = %0.2f)'
37 % (i+1, roc_auc))
38
39plt.plot([0, 1],
40 [0, 1],
41 linestyle='--',
42 color=(0.6, 0.6, 0.6),
43 label='random guessing')
44
45mean_tpr /= len(cv)
46mean_tpr[-1] = 1.0
47mean_auc = auc(mean_fpr, mean_tpr)
48plt.plot(mean_fpr, mean_tpr, 'k--',
49 label='mean ROC (area = %0.2f)' % mean_auc, lw=2)
50plt.plot([0, 0, 1],
51 [0, 1, 1],
52 lw=2,
53 linestyle=':',
54 color='black',
55 label='perfect performance')
56
57plt.xlim([-0.05, 1.05])
58plt.ylim([-0.05, 1.05])
59plt.xlabel('false positive rate')
60plt.ylabel('true positive rate')
61plt.title('Receiver Operator Characteristic')
62plt.legend(loc="lower right")
63
64plt.tight_layout()
65plt.show()
查看下AUC和准确率的结果:
1pipe_lr = pipe_lr.fit(X_train2, y_train)
2y_labels = pipe_lr.predict(X_test[:, [4, 14]])
3y_probas = pipe_lr.predict_proba(X_test[:, [4, 14]])[:, 1]
4# note that we use probabilities for roc_auc
5# the `[:, 1]` selects the positive class label only
1from sklearn.metrics import roc_auc_score, accuracy_score
2print('ROC AUC: %.3f' % roc_auc_score(y_true=y_test, y_score=y_probas))
3print('Accuracy: %.3f' % accuracy_score(y_true=y_test, y_pred=y_labels))
ROC AUC: 0.752
Accuracy: 0.711
本文分享自微信公众号 - Python与算法社区(alg-channel)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。