chapter 11 模型评估与优化
除了之前的 train_test_split 函数外,还有更自动化的拆分测试工具。就是交叉验证法(Cross Validation)。
K折叠交叉验证法(k-fold cross validation):
# 导入红酒数据集
from sklearn.datasets import load_wine
# 导入交叉验证工具
from sklearn.model_selection import cross_val_score
# 导入用于分类的支持向量机模型
from sklearn.svm import SVC
# 载入数据
wine = load_wine()
# 设置 SVC 的核函数为 linear
svc = SVC(kernel='linear')
# 使用交叉验证法对SVC进行评分
scores=cross_val_score(svc, wine.data, wine.target, cv=5) #默认分成5份
print(scores, "\nMean score:{:0.3f}".format(scores.mean()) )
[0.88888889 0.94444444 0.97222222 1. 1. ] Mean score:0.961
# sklearn对分类模型,默认使用的是 分层k交叉验证法。
print(wine.target) #可见酒分了3类。
# 所谓的分层,就是如果目标是80%男,20%女,则拆分数据后每一份中男女也是这个比例。
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
# 原理:先从数据中随机抽取一部分作为训练集,再从其余部分随机抽取一部分作为测试集,进行评分后再迭代,
# 直到达到我们希望的迭代次数。
from sklearn.model_selection import ShuffleSplit
# 设置拆分为10份
shuffle_split = ShuffleSplit(train_size=0.7, test_size=0.2, n_splits=10)
# 交叉验证
scores=cross_val_score(svc, wine.data, wine.target, cv=shuffle_split)
print(scores, "\nMean score:{:0.3f}".format(scores.mean()) )
[0.91666667 0.97222222 0.94444444 0.97222222 0.97222222 0.97222222 0.91666667 0.91666667 0.97222222 1. ] Mean score:0.956
# 原理:每次取一个作为测试集,其他作为训练集。缺点是耗时,特别是对大数据集。
from sklearn.model_selection import LeaveOneOut
loo = LeaveOneOut()
# 交叉验证
scores=cross_val_score(svc, wine.data, wine.target, cv=loo)
print(scores, "\nMean score:{:0.3f}".format(scores.mean()) )
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] Mean score:0.955
除了逐个尝试看打分外,还可以使用网络搜索法一次性找到更优的参数设置。
# 目的: 尝试2个参数的组合 max_iter=100,1000,5000,10000, alpha=10,1,0.1,0.01;
from sklearn.datasets import load_wine
wine = load_wine()
# 导入套索回归模型
from sklearn.linear_model import Lasso
# 导入数据集拆分工具
from sklearn.model_selection import train_test_split
# 将数据集拆分为训练集与测试集
X_train, X_test, y_train, y_test = train_test_split(wine.data, wine.target, random_state=38)
# 设置初试分数0
best_score=0
# 设置 alpha 参数4个遍历
for alpha in [0.01, 0.1, 1, 10]:
#设置最大迭代次数, 4个遍历
for max_iter in [100, 1000, 5000, 10000]:
lasso=Lasso(alpha=alpha, max_iter=max_iter)
lasso.fit(X_train, y_train)
score=lasso.score(X_test, y_test)
# print(alpha, max_iter, score)
# 最高分数
if score > best_score:
best_score=score
best_parameters={"alpha":alpha, "max_iter":max_iter}
# 输出
print("best_score:{:0.3f}".format(best_score))
print("best_parameters:{}".format(best_parameters))
best_score:0.889 best_parameters:{'alpha': 0.01, 'max_iter': 100}
# 上述做法是有缺陷的,因为16次评分使用的是同一个训练集和测试集。不能反映出新数据及的情况。
# 比如换一个拆分方法,打分也会变 random_state=0
X_train, X_test, y_train, y_test = train_test_split(wine.data, wine.target, random_state=0)
best_score=0
for alpha in [0.01, 0.1, 1, 10]:
for max_iter in [100, 1000, 5000, 10000]:
lasso=Lasso(alpha=alpha, max_iter=max_iter)
lasso.fit(X_train, y_train)
score=lasso.score(X_test, y_test)
if score > best_score:
best_score=score
best_parameters={"alpha":alpha, "max_iter":max_iter}
# 输出
print("best_score:{:0.3f}".format(best_score))
print("best_parameters:{}".format(best_parameters))
# 改变拆分方法后,最佳参数变为 alpha=0.1, 最佳打分也下降了
best_score:0.830 best_parameters:{'alpha': 0.1, 'max_iter': 100}
import numpy as np
from sklearn.model_selection import cross_val_score
best_score=0
for alpha in [0.01, 0.1, 1, 10]:
for max_iter in [100, 1000, 5000, 10000]:
lasso=Lasso(alpha=alpha, max_iter=max_iter)
scores = cross_val_score(lasso, X_train, y_train, cv=6)
score=np.mean(scores)
if score > best_score:
best_score=score
best_parameters={"alpha":alpha, "max_iter":max_iter}
# 输出
print("best_score:{:0.3f}".format(best_score))
print("best_parameters:{}".format(best_parameters))
best_score:0.865 best_parameters:{'alpha': 0.01, 'max_iter': 100}
# 再试试验证集上,使用最优参数
lasso=Lasso(alpha=0.01, max_iter=100).fit(X_train, y_train)
print("test score:{:0.3f}".format(lasso.score(X_test, y_test)))
# 这个打分有点低。因为lasso会舍弃特征,而我们的特征本来就不多,说明舍弃的列不那么冗余。
test score:0.819
# 使用内置的 GridSearchCV 简化上述循环
#导入网络搜索工具
from sklearn.model_selection import GridSearchCV
params = {'alpha':[0.01, 0.1, 1, 10], 'max_iter':[100,1000,5000,10000]}
# 定义模型和参数
grid_search = GridSearchCV(lasso, params, cv=6)
grid_search.fit(X_train, y_train)
# 打分
print("test score:{:0.3f}".format(grid_search.score(X_test, y_test)))
print("best params:", grid_search.best_params_)
# 最佳条件和上面的for循环一样。
# 注意: grid_search.best_score_ 中存储的是交叉验证的最高分,而不是在测试集上的得分。
print("\nbest_score_:{:0.3f}".format(grid_search.best_score_))
test score:0.819 best params: {'alpha': 0.01, 'max_iter': 100} best_score_:0.865
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
X, y= make_blobs(n_samples=200, random_state=1, centers=2, cluster_std=5)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.cool, edgecolor='k')
plt.show()
# 假设深红色 1,浅色0
# 导入高斯贝叶斯模型
from sklearn.naive_bayes import GaussianNB
# 数据集拆分
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=68)
# 训练高斯贝叶斯模型
gnb=GaussianNB()
gnb.fit(X_train, y_train)
predict_prob = gnb.predict_proba(X_test)
print(predict_prob.shape) #(50, 2)
predict_prob[:3]
# 第一行是归为2个分类的概率 98.8% vs 1.2%
(50, 2)
array([[0.98849996, 0.01150004], [0.0495985 , 0.9504015 ], [0.01648034, 0.98351966]])
# 可视化分类过程中的表现
x_min, x_max= X[:,0].min()-0.5, X[:,0].max()+0.5
y_min, y_max= X[:,1].min()-0.5, X[:,1].max()+0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z=gnb.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:,1]
Z=Z.reshape(xx.shape)
#绘制等高线
plt.contourf(xx, yy, Z, cmap=plt.cm.summer, alpha=0.8)
plt.scatter(X_train[:,0], X_train[:,1], c=y_train, cmap=plt.cm.cool, edgecolor='k')
plt.scatter(X_test[:,0], X_test[:,1], c=y_test, cmap=plt.cm.cool, edgecolor='k', alpha=0.6)
# 设置坐标轴范围
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
# 设置横纵轴的单位
plt.xticks(())
plt.yticks(())
plt.title("GaussianNB predict_proba")
plt.show()
# 背景颜色代表概率值。半透明的点是测试集。
# 并不是每个分类算法都有 predict_proba 属性。
# 不过我们还可以使用另一种方法检查分类的可信度,就是决定系数 decision_function.
决定系数只返回一个值,正数代表属于分类1,负数代表属于分类2.
由于高斯朴素贝叶斯没有 decision_function,我们换成 支持向量机SVC 算法来进行建模。
from sklearn.svm import SVC
svc=SVC().fit(X_train, y_train)
dec_func= svc.decision_function(X_test)
# 打印决定系数的前5个
print(dec_func[:5])
# 负数一类,正数一类。
[-1.36071347 1.53694862 1.78825594 -0.96133081 1.81826853]
# 可视化工作原理
Z=svc.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z=Z.reshape(xx.shape)
# 绘制等高线
plt.contourf(xx, yy, Z, cmap=plt.cm.summer, alpha=0.8)
# 绘制散点图
plt.scatter(X_train[:,0], X_train[:,1], c=y_train, cmap=plt.cm.cool, edgecolor='k')
plt.scatter(X_test[:,0], X_test[:,1], c=y_test, cmap=plt.cm.cool, edgecolor='k', alpha=0.6)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("SVC decision_function")
# 设置横纵轴的单位
plt.xticks(())
plt.yticks(())
plt.show()
# 和上图类似,有一个斜对角线分界线。
# 但是深背景色的核心位置不同。处于区域中心的是高度可信的,而边缘区域和交界区域的,则是“模棱两可”区域。
本例汇总使用的是二元分类任务,但是 predict_proba 和 decision_function 同样适用于多元分类任务。
之前一直使用的 .score(),
R^2 = 1 - 求和(y-y_hat)^2/求和(y-y_bar)^2
其它指标
这些指标经常和 网格搜索算法 配合使用,选择合适的模型和参数。