在这篇文章中,我将介绍“交叉验证”这一评估泛化性能的统计学方法,它比起单次划分训练集与测试集的方法更稳定与全面。首先我会简单介绍它的概念,然后是分层k折交叉验证和其他策略,最后是一些代码实现。

一、 概念

交叉验证(k-fold cross-validation),是指将数据集进行多次划分,分为几个互斥的子集来进行模型的训练与验证。其中k是用户指定的数字,通常取5或10。

在进行5次的交叉验证时,我们首先将数据大致划分为均等的5份,其中每一份都叫一折,然后先用第一折作为测试集,其余折作为训练集来训练模型,然后当这一次的训练完后在1折上评估精度,再之后便构建另一模型,此时就用第二折作为测试集,其余来作为训练集,重复方才的操作,完成后剩下的3、4、5折都与刚才的1、2折进行一样的行为。因为每一次用不同的折作为测试集时都要计算精度,所以在完成后我们会得到5个精度值。

(这里我用我自学的书籍之一的《Introduction to Machine Learning with Python》中的图像来展示交叉验证中的数据划分)

二、 分层k折交叉验证和其他策略

2.1 分层k折交叉验证

如果当数据集有些特殊时,我们直接使用交叉验证并非一个好的选择,事实胜于雄辩,看下面这个例子:

iris labels:
[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 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
 2 2]

在这个数据集中我们发现其前三分之一是类别0,中间三分之一是1,最后三分之一是类别2,那么当我们进行交叉验证时,就会发现我们选取的第一折中将全是类别0的存在,至于训练集则全是类别1与类别2,那么最后我们发现在这一折的交叉验证中得到的精度就会有大问题,所以这时就需要分层k折交叉验证了。

在分层k折交叉验证中,我们让不同类别之间的比例在每一折中都与在整个数据集中的比例相同,就是说,在上述的数据集中,0占整体的1/3,那么在每一折中我们也同样保证0的占比为1/3。

(同样用书中的图来展示)

2.2 交叉验证分离器

在函数cross_val_score中,我们可以通过调整cv参数来改变所用的折数,而实现对于数据集更精准的划分。同时,它本身并不直接评估模型,而是生成用于训练和验证的数据索引。

2.3 留一法交叉验证

留一法交叉验证其实就是每折只包含单个样本的k折交叉验证。简单说,就是选择单个数据点作为测试集。

2.4 打乱交叉验证

在打乱交叉验证中,通过随机打乱数据集后再进行分割,从而确保每次分割都是随机的。

(同样,如下图)

2.5 分组交叉验证

分组交叉验证是一种特殊的交叉验证方法,它在数据集中考虑了一种特殊的结构——即样本之间的“组”关系。这种交叉验证方法适用于数据集中样本间存在某种依赖或相关性的情况,例如数据来自不同的个体、不同的时间段或不同的地理区域等。

三、 python代码实现

3.1 交叉分离器

首先先给出最原本的交叉验证的代码:

from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris

iris = load_iris()
logreg = LogisticRegression()
scores = cross_val_score(logreg,iris.data,iris.target)
print("score:{}".format(scores))

关于它,我们会得到这样一个结果:

并且输出结果为:score:[0.96666667 1.         0.93333333 0.96666667 1.        ]

这是因为优化器未能在预定的最大迭代次数内达到收敛标准。

然后我们略作修改,如下:

from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler

iris = load_iris()
X = iris.data
y = iris.target

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 创建逻辑回归模型,并增加最大迭代次数
logreg = LogisticRegression(max_iter=1000)

# 使用交叉验证计算得分
scores = cross_val_score(logreg, X_scaled, y)
分
print("Scores:", scores)
print("Mean Score:", scores.mean())

这样结果就是:

Scores: [0.96666667 1.         0.93333333 0.9        1.        ]
Mean Score: 0.9600000000000002

在默认情况下,cross_val_score将执行3折交叉验证,并返回三个精度值,我们可以通过修改cv来改变参数。

3.2 分层k折交叉验证

接下来是分层k折交叉验证的代码:

from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris

iris = load_iris()
logreg = LogisticRegression()

# 使用分层k折交叉验证
stratified_kfold = StratifiedKFold(n_splits=5)

scores = cross_val_score(logreg, iris.data, iris.target, cv=stratified_kfold)

print("Scores:{}".format(scores))

结果为:

Scores:[0.96666667 1.         0.93333333 0.96666667 1.        ]

3.3 交叉分离器

然后是交叉分离器的代码,在这里我们会发现使用交叉分离器后,是直接将之索引带入到了cv参数里。

from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold

iris = load_iris()
logreg = LogisticRegression()

#获得分离器,并分5折
kfold = KFold(n_splits=5)

scores = cross_val_score(logreg, iris.data, iris.target, cv=kfold)

print("Scores:{}".format(scores))

结果为:

Scores:[1.         1.         0.86666667 0.93333333 0.83333333]

3.4 留一法交叉验证

这是留一法交叉验证的代码:

from sklearn.model_selection import cross_val_score, LeaveOneOut
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris

iris = load_iris()
logreg = LogisticRegression()

# 实例化留一
loo = LeaveOneOut()

scores = cross_val_score(logreg, iris.data, iris.target, cv=loo)

print("Scores:{}".format(scores))
print("Number of cv iterations:{}".format(len(scores)))
print("Mean accuracy:{}".format(format(scores.mean())))

 其结果为:

Scores:[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. 0. 1.
 1. 1. 1. 1. 1. 0. 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. 0. 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.]
Number of cv iterations:150
Mean accuracy:0.9666666666666667

3.5 打乱交叉验证

代码如下:

from sklearn.model_selection import cross_val_score, ShuffleSplit
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris

iris = load_iris()
logreg = LogisticRegression()

# 实例打乱交叉验证
shuffl_s = ShuffleSplit(test_size=.5,train_size=.5,n_splits=10)

scores = cross_val_score(logreg, iris.data, iris.target, cv=shuffl_s)

print("Number of cv iterations:{}".format(len(scores)))
print("Mean accuracy:{}".format(format(scores.mean())))

结果为:

Number of cv iterations:10
Mean accuracy:0.9426666666666668

3.6 分组交叉验证

最后是分组交叉验证,代码如下:

from sklearn.model_selection import cross_val_score, GroupKFold
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_blobs

#创建模拟数据集
X, y = make_blobs(n_samples=12, random_state=0)
logreg = LogisticRegression()
group = [0,0,0,1,1,1,1,2,2,3,3,3]

scores = cross_val_score(logreg, X, y, groups=group, cv=GroupKFold(n_splits=3))

print("Number of cv iterations:{}".format(len(scores)))
print("Mean accuracy:{}".format(format(scores.mean())))

结果如下:

Number of cv iterations:3
Mean accuracy:0.6722222222222222
 

此上

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐