**交叉验证(Cross Validation)**是机器学习领域、分类器算法等十分重要的模型性能检测方法。交叉验证是将数据集随机分为训练集和测试集,重复这一过程,直至每一个样本都做过测试集。交叉验证可以分为简单随机交叉验证、留一法、留P法、K折法。
上述部分交叉验证在Matlab中有3个函数可以实现这些功能,为帮助读者厘清其中的不同,选择适用的函数,本文拟对Matlab中的3个交叉验证函数做一个系统介绍。需要说明的是,Matlab中的交叉验证实质是帮助你划分好数据集,划分后,自己还需要写代码添加自己的模型。此外,本文给出自己写的留P法交叉验证代码,供读者择用。
crossvalind是cross validation的缩写,该函数的输出结果有两种形式,会对后续代码书写带不变,因此本人不太喜欢这一点:
语法1:indices = crossvalind(‘KFlod’,n,k)
说明:k折法,在n个样本中将数据集平均划分k组,分组方式是用数字1至k进行标记,例如分成3组,indices结果如下:
>indices = crossvalind('KFold',10,3)
indices =
3
1
1
2
3
2
3
1
3
2
因此这个函数后续一般会跟有如下代码串,用于获取训练集和测试集数据。
for i=1:k
test=(indices==k); %结果为逻辑值,每次循环选取一个组作为测试集
train=~test; %取test的补集即为训练集
TrainData=Data(trian,:); %提取训练集数据
TestData=Data(test,:); %提取测试集数据
...%训练模型代码
end
语法2:[Train,Test] = crossvalind(‘HoldOut’,n,p)
说明:简单随机交叉验证,在n个样本中选择n*p个样本作为测试集,p只能取(0,1)之间,Train和Test是n×1的逻辑数组。
语法3:[Train, Test] = crossvalind(‘LeaveMOut’,n, m)
说明:留一法和留P法,但是数据划分不完整,仅仅划分一次,并不是定义中的交叉验证
小结:crossvalind()函数使用起来给代码书写增加新的问题,比如不同交叉验证输出结果不一致需要处理,比如留一法和留P法数据划分只有一种需要补全,这些都导致crossvalind()函数不怎么实用。
cvpartition()是本人比较喜欢使用的交叉验证函数,比crossvalind()函数好很多,输出结果统一,贴合原有交叉验证方法的定义。用法如下:
语法1:c = cvpartition(n,‘KFold’,k);
说明:k折法,在n个样本中将数据集平均划分k组,分组是用逻辑数组表示。
语法2:c = cvpartition(n,‘HoldOut’,p);
说明:简单随机交叉验证,在n个样本中选择n*p个样本作为测试集(0
语法3:c = cvpartition(n,‘LeaveOut’,);
说明:留一法交叉验证,在n个样本中选择1个样本作为测试集,重复n次。
之所以很喜欢cvpartition()函数,是因为该函数将输出结果统一为cvpartition类型变量,虽然是没见过这个变量,但这个类型变量很简单,有两个关键的成员函数,很好用于交叉验证的后续代码编写。
首先看一下c是什么
>c = cvpartition(10,'HoldOut',0.2)
c =
Hold-out cross validation partition
NumObservations: 10
NumTestSets: 1
TrainSize: 8
TestSize: 2
>c = cvpartition(10,'KFold',4)
c =
K-fold cross validation partition
NumObservations: 10
NumTestSets: 4
TrainSize: 8 7 7 8
TestSize: 2 3 3 2
可以看出cvpartition类型的成员变量有交叉验证类型(eg:Hold-out cross validation partition)、样本量(NumObservations)、测试集个数(NumTestSets)、TrainSize(训练集样本量)、TestSize(测试集样本量)。
cvpartition类型的成员函数有如下两个最常用:
函数1:TrainIndex = training(c,TestIndex)
函数2:TestIndex = test(c,TestIndex)
函数training()获取第TestIndex个训练集样本的逻辑值,函数test()获取第TestIndex测试集样本的逻辑值,举例如下。后续就可以获取训练集数据训练模型,很方便。
>TrainIndex = training(c,2)
TrainIndex =
1
1
1
0
0
0
1
1
1
1
利用cvpartition()实现K折交叉验证的数据划分代码示例如下:
% Data定义为数据集
PointCount = size(Data,1);%获取样本量
c = cvpartition(PointCount,'KFold',4);%k-fold法
for ModelIndex = 1:c.NumTestSets
TrainIndex = training(c,ModelIndex);
TestIndex = test(c,ModelIndex);
TrainData = Data(TrainIndex,:);%提取训练数据
TestData = Data(TestIndex,:);%提取验证数据
...%训练模型代码
end
小结:cvpartition()函数将交叉验证的输出做成cvpartition类,十分有助于后续代码的一致性书写,总体十分友好。
注意:cvpartition()函数包含留一法交叉验证,但是没有留P法交叉验证。
crossval()函数是将交叉验证和模型训练聚合在一起,是对cvpartition()的又一次升级。用法如下:
语法1:vals = crossval(fun,X,Y);
说明:fun是个函数句柄,使用类似于‘fx = @(x) f(x)’的形式定义;X和Y分别是输入和输出变量;此时,要求fun至少有四个输入,分别是(XTRAIN,YTRAIN,…,XTEST,YTEST,…),输出为评价指标,默认使用10折交叉验证
语法2:mse = crossval(‘mse’,X,Y,‘Predfun’,predfun);
说明:该语法表明模型评价指标采用MSE(均方误差),除了可以用‘mse’,还可以用‘mcr’(错分类率);predfun的定义是三个输入,分别是(XTRAIN,ytrain,XTEST),输出为XTEST的模型计算结果。
语法3:vals = crossval(…,‘name’,value)
说明:'name’表示使用什么类型的交叉验证,crossval支持简单随机、k折、留一法、cvpartition类型(没有留P法),参数及定义见下表:
小结:crossval()可以将模型和交叉验证结合在一起来做,真的很不错。模型可以先按crosscal的输入输出要求写好函数,然后在函数句柄中调用该函数,就可以实现模型与交叉验证的完美结合了!
根据对crossvalind()等函数的学习,留P交叉验证也可以使用逻辑数组的形式实现,本人利用Matlab中自带的**nchoosek(n,P)**函数实现获取从n个样本中选取P个样本作为测试集的所有情形,代码如下,仅供参考:
PointCount = size(Data,1);%数据集的样本数
p=3;%以p=3为例
TestAll = nchoosek(1:PointCount,p);%nchoosek函数返回选取样本的序号,构成C_n^p×p矩阵
TrainGroup = logical(ones(size(TestAll,1),PointCount));%逻辑矩阵
TestGroup = logical(zeros(size(TestAll,1),PointCount));%逻辑矩阵
for row=1:size(TestAll,1)
TestGroup(row,TestAll(row,:)) = true;%训练集
TrainGroup(row,:) = ~TestGroup(row,:);%验证集
end
for ModelIndex = 1:size(TestAll,1)
TrainData = PData(TrainGroup(ModelIndex,:),:);
TrainTime = PTime(TrainGroup(ModelIndex,:));%提取训练数据
TestData = PData(TestGroup(ModelIndex,:),:);
TestTime = PTime(TestGroup(ModelIndex,:));%提取验证数据
...%训练模型代码
end
本文对Matlab的三种交叉验证函数做了详细介绍,并补充自己书写的留P法交叉验证代码,希望对读者有帮助。