1、交叉验证
算法性能评估
cross_val_score是一种评估模型性能的简单方法,但是它并不直接支持参数筛选。
1.1、交叉验证指标计算
回归常见的评估指标是:决定系数、mean_squared_error
分类常见的评估指标是:准确率、精确率、召回率、ROC-AUC 分类指标
聚类常见的评价指标是:轮廓系数、调整兰德指数
常见的指标见官网 更多指标
1.2、交叉验证迭代器(Fold)
交叉验证迭代器的作用是将数据合理的分割,常见的迭代器如下:
KFold、 StratifiedKFold(分层分割训练数据和测试数据,维持原数据的比例)

1.3、数据重排优化
在交叉验证中,数据重排(reshuffling)可以通过设置交叉验证划分器(如 KFold, StratifiedKFold)的 shuffle 参数为 True 来实现。
- **消除数据集内的偏序性:**在某些情况下,数据集可能按照某种特定的顺序进行排列。例如,患者的医疗记录可能按照时间顺序进行排列,或者图像可能按照它们的类别进行排列。在这种情况下,如果你直接对数据进行划分(例如,前80%为训练集,后20%为测试集),那么可能导致训练集和测试集的分布不同。这可能导致模型在测试集上的性能不佳,因为它可能没有在训练集中看到测试集的某些特性。
- **确保交叉验证的公平性:**在交叉验证中,我们需要多次划分数据集,并在每次迭代中对模型进行训练和验证。如果数据集按照某种特定的顺序排列,那么可能导致某些样本总是出现在训练集中,而其他样本总是出现在验证集中。通过重排数据,我们可以确保所有的样本都有相等的机会出现在训练集和验证集中,从而使得交叉验证的结果更加公平。
- **提高模型的健壮性:**如果一个模型对于训练数据的排列顺序很敏感,那么它可能不是一个很健壮的模型。通过在训练过程中对数据进行重排,我们可以检查模型是否对数据的排列顺序过于敏感,从而评估模型的健壮性。
1.4、交叉验证和模型选择
回归算法交叉验证举例
from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression,ElasticNet,Ridge,Lasso
from sklearn.model_selection import cross_val_score, KFold
# 加载数据
diabetes = load_diabetes()
# 定义模型
model = LinearRegression()
# 定义交叉验证策略
kfold = KFold(n_splits=10,shuffle=True)
# 进行交叉验证
scores = cross_val_score(model,
diabetes.data,
diabetes.target,
cv=kfold,
scoring='neg_mean_squared_error')
# 输出结果
print(f'Mean Squared Error: {-scores.mean()}')分类算法交叉验证举例
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score, StratifiedKFold
# 加载数据
iris = load_iris()
# 定义模型
model = LogisticRegression(max_iter=2000)
# model = SVC()
# model = RandomForestClassifier()
# model = KNeighborsClassifier()
# 定义交叉验证策略
skfold = StratifiedKFold(n_splits=5,shuffle=True)
# 进行交叉验证
scores = cross_val_score(model,
iris.data,
iris.target,
cv=skfold,
scoring='accuracy')
# 输出结果
print(f'Accuracy: {scores.mean()}')2、超参数调优
2.1、网格搜索
GridSearchCV和RandomizedSearchCV都是用来自动进行模型参数调整的方法。他们在交叉验证的过程中尝试了多种参数组合,以找到在验证集上表现最好的参数组合。
下面是一个使用GridSearchCV在SVM分类器上进行参数调整的例子:
from sklearn import datasets
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型
svc = SVC()
# 定义需要尝试的参数网格
param_grid = {'C': [0.1, 1, 10, 100], 'gamma': [1, 0.1, 0.01, 0.001], 'kernel': ['rbf', 'poly', 'sigmoid']}
# 初始化 GridSearchCV 对象
grid_search = GridSearchCV(svc, param_grid, cv=5)
# 进行交叉验证和参数调整
grid_search.fit(X_train, y_train)
# 输出最优参数
print(f'Best parameters: {grid_search.best_params_}')
# 输出在测试集上的准确性
print(f'Test set score: {grid_search.score(X_test, y_test)}')2.2、随机参数优化
当我们需要在很大的参数空间中寻找最优参数时,RandomizedSearchCV是一个很有用的工具,因为它能在参数空间中随机地选择一些参数组合。这种随机的搜索方法对于大型数据集和复杂模型来说,可能比网格搜索GridSearchCV更加有效率。
以下是一个使用RandomizedSearchCV在XGBoost分类器上进行参数调整的例子:
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import train_test_split
from xgboost import XGBClassifier
from sklearn.datasets import load_iris
import numpy as np
# 加载数据
iris = load_iris()
X = iris.data
y = iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型
model = XGBClassifier(use_label_encoder=False)
# 定义参数分布
param_dist = {
'n_estimators': range(50, 200, 50),
'learning_rate': np.linspace(0.01, 1, 10),
'max_depth': range(3, 10),
'colsample_bytree': np.linspace(0.1, 1, 10),
'subsample': np.linspace(0.1, 1, 10)
}
# 初始化 RandomizedSearchCV 对象
random_search = RandomizedSearchCV(model, param_dist, n_iter=20, cv=5, random_state=0)
# 进行交叉验证和参数调整
random_search.fit(X_train, y_train)
# 输出最优参数
print(f'Best parameters: {random_search.best_params_}')
# 输出在测试集上的准确性
print(f'Test set score: {random_search.score(X_test, y_test)}')2.3、连续减半搜索最佳参数
HalvingGridSearchCV 是一种新的超参数优化方法,它使用连续减半的方式来搜索最优参数。该方法的基本思想是在初步搜索阶段,使用较小的数据集进行训练,从而快速剔除不合适的参数,然后在每一轮迭代中,将剩余的参数使用更多的数据进行训练,直到找到最优参数。这种方法相比传统的网格搜索和随机搜索在大型数据集和复杂模型上可能更加高效。
以下是使用 HalvingGridSearchCV 在 AdaBoost 回归模型上进行参数搜索的例子:
%%time
from sklearn.experimental import enable_halving_search_cv # 需要首先导入这个模块
from sklearn.model_selection import HalvingGridSearchCV
from sklearn.ensemble import AdaBoostRegressor
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
model = AdaBoostRegressor()
model.fit(X_train,y_train)
print('未进行参数筛选,模型表现:',model.score(X_test,y_test))
# 加载数据
data = fetch_california_housing(data_home='./')
X = data.data
y = data.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型
model = AdaBoostRegressor()
# 定义参数网格
param_grid = {'n_estimators': [10, 50, 100, 200],
'learning_rate': [0.01, 0.1, 1]}
# 初始化 HalvingGridSearchCV 对象
halving_grid_search = HalvingGridSearchCV(model, param_grid, cv=5)
# 进行交叉验证和参数搜索
halving_grid_search.fit(X_train, y_train)
# 输出最优参数
print(f'Best parameters: {halving_grid_search.best_params_}')
# 输出在测试集上的 R2 score
print(f'Test set score: {halving_grid_search.score(X_test, y_test)}')2.4、蛮力参数搜索的替代方案
2.4.1、模型CV
RidgeCV 和 LogisticRegressionCV 都是使用内置交叉验证来找到最优正则化参数的模型。RidgeCV 用于回归问题,LogisticRegressionCV 用于分类问题。
下面是使用 RidgeCV 的例子:
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.linear_model import RidgeCV
import warnings
warnings.filterwarnings('ignore')
# 加载数据
boston = load_boston()
X = boston.data
y = boston.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型
ridge = RidgeCV(alphas=[0.1, 1.0, 10.0], cv=5)
# 训练模型
ridge.fit(X_train, y_train)
# 输出最优参数
print(f'Best alpha: {ridge.alpha_}')
# 输出在测试集上的 R2 score
print(f'Test set score: {ridge.score(X_test, y_test)}')下面是使用 LogisticRegressionCV 的例子:
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
# 加载数据
iris = load_iris()
X = iris.data
y = iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型
logistic = LogisticRegressionCV(Cs=[0.1, 1.0, 10.0],
cv=5,
max_iter=1000)
# 训练模型
logistic.fit(X_train, y_train)
# 输出最优参数
print(f'Best C: {logistic.C_}')
# 输出在测试集上的准确率
print(f'Test set score: {logistic.score(X_test, y_test)}')2.4.2、袋外估计
当使用基于装袋的集成方法时,即使用有放回采样生成新的训练集时,部分训练集未被使用。对于集成中的每个分类器,训练集中的不同部分被遗漏了。
这个遗漏的部分可用于估计泛化误差,而无需依赖单独的验证集。这个估计是“免费”的,因为不需要额外的数据并且可以用于模型选择。

from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
# 加载数据
boston = load_boston()
X = boston.data
y = boston.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型,设置 oob_score=True 开启袋外评估
rf = RandomForestRegressor(n_estimators=100, oob_score=True, random_state=42)
# 训练模型
rf.fit(X_train, y_train)
# 输出袋外评估得分
print(f'OOB score: {rf.oob_score_}')
# 输出在测试集上的 R2 score
print(f'Test set score: {rf.score(X_test, y_test)}')在这个例子中,我们设置 oob_score=True 开启袋外评估,训练完模型后,可以通过 oob_score_ 属性获取袋外评估得分。
需要注意的是,只有当随机森林中的决策树数量足够多时,袋外评估才会比较准确,因为这时每一棵树的训练样本都足够多样化,袋外数据也足够大。
3、指标和评分
量化预测的质量
3.1、 定义模型评估规则
回归模型评估
线性回归模型是统计学和机器学习中最基础和最常见的预测模型。我们通常使用 R2 分数(决定系数)来评估线性回归模型的性能。
下面是一个使用 scikit-learn 库中的线性回归模型和 R2 分数的例子:
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
# 加载波士顿房价数据集
boston = load_boston()
X = boston.data
y = boston.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建线性回归模型
model = LinearRegression()
# 使用训练数据训练模型
model.fit(X_train, y_train)
# 对测试集进行预测
y_pred = model.predict(X_test)
# 计算 R2 分数
r2 = r2_score(y_test, y_pred)
print(f'R2 score: {r2}')其中,
具体来说,
其中,n 是样本数,
以下是一个自定义
import numpy as np
def r2_score(y_true, y_pred):
ss_res = np.sum((y_true - y_pred) ** 2)
ss_tot = np.sum((y_true - np.mean(y_true)) ** 2)
r2 = 1 - ss_res / ss_tot
return r2
print(f'R2 score: {r2_score(y_test, y_pred)}')3.2、评估指标
分类评估、回归评估、聚类评估、多标签排名评估
多标签排名度量(Multilabel ranking metrics)是用于评估多标签分类问题的性能指标。这类问题的特点是每个样本可以属于多个类别,而且这些类别之间可能有某种顺序或重要性的关系。
这里,我们会使用 scikit-learn 的 label_ranking_average_precision_score 来作为多标签排名的指标。这个指标衡量的是,对于每个标签,将其预测为正类的样本的排名(即预测概率)是否靠前。得分越高,说明模型越能够将真正的正类样本排在前面。
下面是一个示例:
from sklearn.metrics import label_ranking_average_precision_score
# 假设我们有以下真实标签和预测的概率
y_true = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
y_score1 = [[0.75, 0.15, 0.1], [0.8, 0.1, 0.1], [0, 0.4, 0.6]]
y_score2 = [[0.85, 0.05, 0.1], [0.1, 0.8, 0.1], [0, 0.4, 0.6]]
# 计算平均排名精度
score = label_ranking_average_precision_score(y_true, y_score1)
print(f'Label ranking average precision score: {score}')
score = label_ranking_average_precision_score(y_true, y_score2)
print(f'Label ranking average precision score: {score}')4、验证曲线
绘制分数曲线评估模型
4.1.、验证曲线
验证曲线是一种工具,用于找到模型复杂度与训练得分和验证得分之间的关系,这有助于我们理解模型是否过拟合或欠拟合。在 scikit-learn 中,可以使用 validation_curve 函数绘制验证曲线。
以下是一个使用支持向量机(SVM)的示例,我们将查看不同的 gamma 参数(核参数)如何影响模型的训练得分和验证得分:
import numpy as np
from sklearn.datasets import load_digits
from sklearn.svm import SVC
from sklearn.model_selection import validation_curve
import matplotlib.pyplot as plt
# 加载数据
digits = load_digits()
X, y = digits.data, digits.target
# 设定要测试的参数范围
param_range = np.logspace(-6, -1, 5)
# 使用 validation_curve 得到训练得分和测试得分
train_scores, test_scores = validation_curve(SVC(),
X,
y,
param_name="gamma",
param_range=param_range,
cv=10,
scoring="accuracy",
n_jobs=-1)
# 计算得分的平均值和标准差
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
# 绘制验证曲线
plt.title("Validation Curve with SVM")
plt.xlabel(r"$\gamma$")
plt.ylabel("Score")
plt.ylim(0.0, 1.1)
lw = 2
# 在 X 轴上绘制一个对数缩放比例的图
plt.semilogx(param_range, train_scores_mean, label="Training score",
color="darkorange", lw=lw)
plt.fill_between(param_range, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.2,
color="darkorange", lw=lw)
plt.semilogx(param_range, test_scores_mean, label="Cross-validation score",
color="navy", lw=lw)
plt.fill_between(param_range, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.2,
color="navy", lw=lw)
plt.legend(loc="best")
在此图中,您可以看到支持向量机针对不同核参数伽玛值的训练分数和验证分数。 对于非常低的 gamma 值,您可以看到训练分数和验证分数都很低。 这称为欠拟合。 gamma 的中等值将导致两个分数的高值,即分类器表现相当好。 如果 gamma 太高,分类器会过拟合,这意味着训练分数很好,但验证分数很差。
4.2、 学习曲线
学习曲线是一种工具,用于可视化模型随着训练样本数量的增加而学习的程度。这有助于我们理解模型是否可能从更多的训练数据中受益,或者模型是否更可能受益于更复杂的模型。
在 scikit-learn 中,可以使用 learning_curve 函数绘制学习曲线。以下是一个使用支持向量机(SVM)的例子:
import numpy as np
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve
from sklearn.svm import SVC
import matplotlib.pyplot as plt
# 加载数据
digits = load_digits()
X, y = digits.data, digits.target
# 设定交叉验证的次数
cv = 10
# 使用 learning_curve 得到训练样本数量、训练得分和测试得分
train_sizes, train_scores, test_scores = learning_curve(SVC(gamma=0.001),
X, y,
cv=cv,
n_jobs=-1,
train_sizes=np.linspace(.1, 1.0, 5))
# 计算得分的平均值和标准差
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
# 绘制学习曲线
plt.figure()
plt.title("Learning Curve")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1, color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Cross-validation score")
plt.legend(loc="best")
plt.show()
在这个示例中,我们首先加载了一个数字识别的数据集,然后我们使用 learning_curve 计算在不同大小的训练集上的训练得分和测试得分。最后我们将这两个得分以及它们的标准差绘制在图上。图中的红线表示训练得分,绿线表示交叉验证得分。从图中我们可以看出,随着训练样本数量的增加,训练得分和测试得分都有一定的提高。
4.3、受试者特征曲线
受试者工作特征曲线(Receiver Operating Characteristic curve,简称 ROC 曲线)和准确率(Accuracy)都是评估分类器性能的重要工具,但它们评估的侧重点和应用场景有所不同。
准确率是最直接的评估指标,它等于正确分类的样本数除以总样本数。然而,在不平衡数据集中,准确率可能会产生误导。例如,在一个数据集中,正样本占 1%,负样本占 99%。一个总是预测负样本的模型,其准确率也能达到 99%,但这显然是不可接受的,因为它没有正确找出任何一个正样本。
相比之下,ROC 曲线描绘了在不同阈值下分类器的真阳率(True Positive Rate,TPR)和假阳率(False Positive Rate,FPR)的变化。ROC 曲线下的面积(Area Under the Curve,AUC)可以量化分类器的整体性能,不受类别不平衡的影响。AUC 等于 1 是完美分类器,0.5 是随机猜测。
总的来说,准确率对于平衡数据集来说是一个很好的指标,但在处理不平衡数据时,ROC 曲线和 AUC 通常是更好的选择。具体选择哪种度量方法,还需要根据实际问题和业务需求来决定。
4.3.1、ROC曲线
准确率评估
import numpy as np
from sklearn.metrics import roc_curve,auc
import matplotlib.pyplot as plt
y_true = [1,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0]#真实类别
proba = np.array([0.42,0.73,0.55,0.37,0.57,0.70,0.25,0.23,0.46,0.62,0.76,
0.46,0.55,0.56,0.56,0.38,0.37,0.73,0.77,0.21,0.39])
# 概率转化为类别,阈值是0.3
y_ = (proba >= 0.3).astype(np.int8)
print('阈值设为0.3准确率是:',(y_ == y_true).mean())
# 概率转化为类别,阈值是0.5
y_ = (proba >= 0.5).astype(np.int8)
print('阈值设为0.5准确率是:',(y_ == y_true).mean())
# 概率转化为类别,阈值是0.7
y_ = (proba >= 0.7).astype(np.int8)
print('阈值设为0.7准确率是:',(y_ == y_true).mean())
# 概率转化为类别,阈值是0.9
y_ = (proba >= 0.9).astype(np.int8)
print('阈值设为0.9准确率是:',(y_ == y_true).mean())ROC曲线评估
import numpy as np
from sklearn.metrics import roc_curve,auc
import matplotlib.pyplot as plt
y_true = [1,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0]#真实类别
proba = np.array([0.42,0.73,0.55,0.37,0.57,0.70,0.25,0.23,0.46,0.62,0.76,
0.46,0.55,0.56,0.56,0.38,0.37,0.73,0.77,0.21,0.39])
fpr,tpr,thresholds = roc_curve(y_true,proba)#调用这个方法,返回3个值,fpr(横坐标),tpr(纵坐标)
plt.plot(fpr,tpr,color = 'red')
plt.xlabel('FPR',fontsize = 15)
plt.ylabel('TPR',fontsize = 15)
plt.fill_between(fpr,tpr,color = 'green',alpha = 0.3)
auc_ = auc(fpr,tpr)
plt.title('AUC-score: %0.2f'%(auc_))
4.3.2、ROC与交叉验证
构造数据
import numpy as np
from sklearn.datasets import load_iris
# 筛选数据二分类
iris = load_iris()
target_names = iris.target_names
X, y = iris.data, iris.target
X, y = X[y != 2], y[y != 2]
n_samples, n_features = X.shape
# 增加噪声,使问题复杂化
random_state = np.random.RandomState(0)
X = np.concatenate([X, random_state.randn(n_samples, 200 * n_features)], axis=1)交叉验证ROC曲线
线性差值演示
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(9,3))
a = np.linspace(0,2*np.pi,15)
b = np.sin(a)
plt.subplot(1,2,1)
plt.scatter(a,b)
c = np.linspace(0,2*np.pi,40)
d = np.interp(c,a,b) # 线性插值,根据a和b的关系,将c对应的值,线性方式插值创造。
plt.subplot(1,2,2)
plt.scatter(c,d,color = 'red')import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.metrics import auc
from sklearn.metrics import RocCurveDisplay
from sklearn.model_selection import StratifiedKFold
cv = StratifiedKFold(n_splits=6)
classifier = svm.SVC(kernel="linear", probability=True, random_state=random_state)
tprs = []
aucs = []
mean_fpr = np.linspace(0, 1, 100)
fig, ax = plt.subplots(figsize=(9, 9))
for fold, (train, test) in enumerate(cv.split(X, y)):
classifier.fit(X[train], y[train])
viz = RocCurveDisplay.from_estimator(classifier,
X[test],
y[test],
name=f"ROC fold {fold}",
alpha=0.3,ax = ax)
interp_tpr = np.interp(mean_fpr, viz.fpr, viz.tpr) # 线性插值
interp_tpr[0] = 0.0
tprs.append(interp_tpr)
aucs.append(viz.roc_auc)
ax.plot([0, 1], [0, 1], "k--", label="chance level (AUC = 0.5)")
mean_tpr = np.mean(tprs, axis=0)
mean_tpr[-1] = 1.0
mean_auc = auc(mean_fpr, mean_tpr)
std_auc = np.std(aucs)
ax.plot(mean_fpr,
mean_tpr,
color="b",
label=r"Mean ROC (AUC = %0.2f $\pm$ %0.2f)" % (mean_auc, std_auc),
lw=2,
alpha=0.8)
ax.set(xlim=[-0.05, 1.05],
ylim=[-0.05, 1.05],
xlabel="False Positive Rate",
ylabel="True Positive Rate",
title=f"Mean ROC curve with variability\n(Positive label '{target_names[1]}')")
ax.legend(loc="lower right")