【学习机器学习】模型评估与选择——matlab版

前言

周五实验课,可以说是不出所料的完成不了,因为之前用python写的时候就觉得内容太多了,好在老师先教matlab基本用法,后进行实验只要求做一个留出法就好。下面,我们使用鸢尾花(iris)数据集,先把数据划分的部分做了。

一、划分训练集与测试集

所有的原理介绍都在上次python实现时写过了,这次使用matlab重新实现,主要是为了更加熟悉这些方法的原理,因为python直接调用函数实际上并不能让你明白划分的具体操作是怎么回事,因此我们只讲代码。有一说一matlab处理矩阵确实有一手,相当的方便,即使是第一次接触我也很快就上手了。
如果你想只想了解有关模型评估与选择的内容,可以看这位写的模型评估与选择

1、留出法

留出法老师直接作为例子来给我们讲解matlab基本语法了,所以只有最后很少一部分是我写的用以完善。

function [Train_holdout,Test_holdout]=splittraintest(data1)
Train_holdout=[];
Test_holdout=[];

f=[50 50 50];

D1=data1(1:50,:);
D2=data1(51:100,:);
D3=data1(101:150,:);

D(:,:,1)=D1;	%此处将原数据集分成三维是可选操作
D(:,:,2)=D2;	%老师说这样可以确保数据随机抽取时分布均匀
D(:,:,3)=D3;	%但是实际上量够大时这个操作没有特别大的作用

[a,b]=size(f);

[d1,d2]=size(data1);

percent1=0.7;	%训练集比例

for j=1:100		%100次重复
    for i=1:b     
        x=round(f(i)*percent1);     %round表示取整
        s=randperm(f(i));	%生成随机序号,相当于随机抽取
        s1=s(1:x);
        s2=s((x+1):f(i));
        DD=D(:,:,i);
        train1=DD(s1,:);
        test1=DD(s2,:);
        Train_data(:,:,i)=train1;
        Test_data(:,:,i)=test1;
    end   
    Train1=reshape(Train_data,x*b,d2);    %将三维转换成二维
    Test1=reshape(Test_data,d1-x*b,d2);
    Train_holdout=cat(3,Train1,Train_holdout);   %100次的结果合并为一个三维矩阵
    Test_holdout=cat(3,Test1,Test_holdout);
end

这个代码里我写的部分其实只有最后四句话。就像上面注释中写到的,老师把数据分为三维来均匀选取,但是我们的数据最终要的是一个二维矩阵(对于某一次来说)。因此我们还需要把三维矩阵转换成二维,这里就用到了reshape函数。
reshape的用法可以参考reshape使用方法整理,特别需要注意,你转换完维度后要保持元素个数不变,也就是 x y z = m n xyz=mn (x、y、z为原三维,m、n为新二维),在此处我们保持列( n = y n=y )不变,要求二维矩阵的中行数 m = x z m=xz
其次就是,划分需要多次进行,最后分别进行预测后取平均来得到结果,因此我们还需要在最外层套一个循环来实现重复实验(上面的j循环)。每一次循环都会得到一对Train和Test的数据划分,我想到的一个保存方式就是把它们“摞起来”,即存储在三维中。那么就用到了cat函数(联结函数)。
cat函数的使用方法,具体内容我就不赘述了,需要注意联结的每一项必须有相同的行数与列数,这点后面会遇到。

2、p次k折交叉验证法

k折交叉验证法的关键就在于“划分每一折”,一旦将k个分组做完,之后的内容只是每次取k-1个做并集的问题。

function [Train_kfold,Test_kfold]=repeatedkfold(data1)
Train_kfold=[];
Test_kfold=[];
d_length=length(data1); %保存数据长度,鸢尾花数据为150[d1,d2]=size(data1);    %保存行数d1和列数d2

k=input('请输入k折的k=');    %k和p共同决定最终Txxx_kfold的深度为k*p或(k+1*p
p=input('请输入重复次数p=');

divide=round(d_length/k);   %划分区间大小,注意取整

for n=1:p   %做p次
    data_temp=[];
    rowrank = randperm(size(data1, 1)); %随机打乱数据集,从而使p次结果不一样
    data_temp=data1(rowrank,:);

    division_matrix=[]; %划分矩阵,用来保留k折的每一折,三维数组

    left_index=1;   %循环时左右界限需要记录
    right_index=left_index+divide-1;

    while left_index<=d_length   %循环得到划分矩阵
        a=data_temp(left_index:right_index,:);
        left_index=right_index+1;
        right_index=right_index+divide;

        if right_index>=d_length       %最后一个划分处理方法
            right_index=d_length;
        end

        if length(a)<divide       %划分不能整除,最后一个矩阵补0
            a(divide,d2)=0;
        end
        division_matrix=cat(3,a,division_matrix);
    end

    [array,column,page]=size(division_matrix); %主要是为了得到深度信息page
    Train_temp=[];
    Test_temp=[];
    Train=[];   %保存每一次k折的结果,三维数组
    Test=[];
    
    for i=1:page
        Test_temp=division_matrix(:,:,i);
        for j=1:page
            if j~=i
                Train_temp=cat(3,division_matrix(:,:,j),Train_temp);
            end
        end
        Train1=reshape(Train_temp,array*(page-1),column);
        Train_temp=[];
        Train=cat(3,Train1,Train);
        Test=cat(3,Test_temp,Test);
    end
     Train_kfold=cat(4,Train,Train_kfold);  %保存所有p次k折结果,四维数组
     Test_kfold=cat(4,Test,Test_kfold);
end

上面的代码我们可以按照循环来切分,看看每一步都干了什么。
while循环。while循环做了整个交叉验证法的核心内容,分折。对于区间左右下标的处理,搭配注释不难理解,注意到最后一步有一个处理不足区间长度补0的操作,这主要是为了后面联结操作做准备。
for i=1:page循环。这个for循环实际上就是在做k-1个分组合并的操作。不过写博客的时候我突然发现这完全是一种浪费性能的做法,训练集的获取并不需要从头遍历至尾来一一联结,从划分矩阵中删除一份测试集即可。我考虑一下改进方法,但目前的这种显然可以完成任务。
for n=1:p。最外层的循环是为了实现p次重复划分的,可以看到循环之初我就打乱了原数据集的顺序,从而让多次重复起到效果。
整个p次k折执行完毕后会得到一个四维矩阵,第四维代表第n次k折,第三维代表一次k折中的第i次合并。

3、自助法

自助法实在没什么好说的,上次用python写几行就完事了,matlab思想也差不多,只不过这次我做了重复试验。

function [Train_bootstrap,Test_bootstrap]=bootstrap(data1)
Train_bootstrap=[];
Test_bootstrap=[];
d_length=length(data1);

for n=1:10  %重复n次
    bootstrap=[];
    Train_temp=[];
    Test_temp=[];

    for i=1:d_length
        bootstrap(end+1)=round(rand*d_length);  %对有m个元素的数据集随机抽取m次
    end

    for i=1:d_length
        if ismember(i,bootstrap)
            Train_temp(end+1,:)=data1(i,:);
        else
            Test_temp(end+1,:)=data1(i,:);
        end
    end
    Train_temp(d_length*0.7,:)=0;   %按照理论计算,大概有35%的数据不会被选到
    Test_temp(d_length*0.7,:)=0;    %因此为了合并数据,我们将数据补00.7*m大小
    Train_bootstrap=cat(3,Train_temp,Train_bootstrap);
    Test_bootstrap=cat(3,Test_temp,Test_bootstrap);
end

二、性能度量

老师说我们只需要随机生成一些数据就可以了,因此我们可以直接在命令行运行以下代码生成二分类的数据:

predict=rand(1,100);	%随机生成1000-1之间的预测值
ground_truth=randi([0,1],1,100); %生成10001组成的真实值

接下来我们只需要一一套公式就可以了

1、MSE

均方误差(MSE): E ( f ; D ) = 1 m i = 1 m ( f ( x i ) y i ) 2 E(f;D)=\cfrac{1}{m}\sum_{i=1}^m (f(xi)-yi)^2 x i xi 为测试数据, f ( x i ) f(xi) 即预测值。

function [MSE]=calMSE(data1,data2)
predict=data1;
truth=data2;
sum=0;
for i=1:length(data1)
    sum=sum+(predict(i)-truth(i))^2;
end
MSE=sum/length(data1);

2、错误率与精度

我们判断一个预测值表示正例还是反例需要设定一个阈值threshold,这里我们直接让用户输入阈值。
错误率:
E ( f ; D ) = 1 m i = 1 m ( f ( x i ) y i ) E(f;D)=\cfrac{1}{m}\sum_{i=1}^m Ⅱ(f(xi)\neq yi)
精度:
a c c ( f ; D ) = 1 m i = 1 m ( f ( x i ) = y i ) = 1 E ( f ; D ) acc(f;D)=\cfrac{1}{m}\sum_{i=1}^m Ⅱ(f(xi)= yi)=1-E(f;D)

function [error_rate,accuracy]=calErrorAndAccuracy(data1,data2,threshold)
predict=data1;
ground_truth=data2;
right=0;	%正确个数
wrong=0;	%错误个数
for i=1:length(data1)	%这里你可以将两个for循环合并
    if predict(i)>threshold
        predict(i)=1;
    else
        predict(i)=0;
    end
end

for i=1:length(data1)
    if predict(i)==ground_truth(i)
        right=right+1;
    else
        wrong=wrong+1;
    end
end

error_rate=wrong/length(data1);
accuracy=right/length(data1);

3、查准率与查全率

从这里开始我们需要一系列判断值TP(真正例)、TN(真反例)、FR(假正例)、FN(假反例)。所以我们干脆把这些值一并保留下来,免得重复运算。
查准率(Precision): T P T P + F P \cfrac{TP}{TP+FP}
查全率(Recall): T P T P + F N \cfrac{TP}{TP+FN}

function [TP,FP,TN,FN,precision,recall]=calPrecisionAndRecall(data1,data2,threshold)
predict=data1;
ground_truth=data2;
TP=0;   %真正例
FP=0;   %假正例
TN=0;   %真反例
FN=0;   %假反例
for i=1:length(data1)
    if((predict(i)>threshold)&&(ground_truth(i)==1))
        TP=TP+1;
    elseif((predict(i)>threshold)&&(ground_truth(i)==0))
        FP=FP+1;
    elseif((predict(i)<=threshold)&&(ground_truth(i)==0))
        TN=TN+1;
    elseif((predict(i)<=threshold)&&(ground_truth(i)==1))
        FN=FN+1;
    end
end

precision=TP/(TP+FP);
recall=TP/(TP+FN);

4、F1指数

F1指数是一个比P-R图平衡点更常用的度量值,因为它同时兼顾了精确率与召回率(查全率)。
F 1 = 2 P R P + R = 2 T P m + T P T N F1=\cfrac{2*P*R}{P+R}=\cfrac{2*TP}{m+TP-TN}

function [F1]=calF1(TP,TN,predict)
F1=(2*TP)/(length(predict)+TP-TN)

5、绘制ROC图,并计算AUC

关于ROC曲线的理解和绘制原理我在上一次用python写时已经说过了,这里不再多说。matlab的实现,大家直接去看这位博主写好的即可,这里也感谢这位博主的讲解。
不过可以对其代码做一些修改,因为这位博主的写法是从(1,1)开始递减的绘制,我们一般是从(0,0)开始递增的做图。

三、假设检验

不会,老师说先不做了。

参考资料:

1、模型评估与选择
2、matlab 绘制 ROC曲线
3、西瓜书

原创文章 8 获赞 13 访问量 2341

猜你喜欢

转载自blog.csdn.net/weixin_44151650/article/details/105321973
今日推荐