山东大学机器学习(实验七解读)—— PCA

1. 一对多SVM分类器

对于多分类来说,一对多的SVM分类器思想是给定一类标上正标签,其余剩下的类标上负标签,对应的将训练40次。我们用细胞元组来将训练数据和测试数据存储起来。

  • divdata.m函数负责将数据重新分配,即按上述我所说的
function data = divdata(index,X,n)
    len = size(X,1);
    if index == 1
        data = [[X(1:n,:),ones(n,1)];[X(n+1:len,:),-ones(len-n,1)]];
    elseif index == 80
        data = [[X(1:len-n,:),-ones(len-n,1)];[X(len-n+1:len,:),ones(n,1)]];
    else
        data = [[X(1:n*(index-1),:),-ones(n*(index-1),1)];[X(n*index-n+1:n*index,:),ones(n,1)];[X(n*index+1:len,:),-ones(len-n*index,1)]];
    end
end
  • svmTrain.m是训练函数(这是实验五中的函数)
function svm = svmTrain(X,Y,C)
    % Options是用来控制算法的选项参数的向量,optimset无参时,
    % 创建一个选项结构所有字段为默认值的选项
    options = optimset;
    options.largeScale = 'off'; %LargeScale指大规模搜索,off表示在规模搜索模式关闭
    options.Display = 'off'; %表示无输出
    
    %二次规划用来求解问题,使用quadprog
    n = length(Y);  
    %使用线性核   
    H = (Y.*X)*(Y.*X)';
    H=(H+H')/2;
    f = -ones(n,1); %f'为1*n个-1
    A = [];
    b = [];
    Aeq = Y'; 
    beq = 0;
    lb = zeros(n,1); 
    ub = C*ones(n,1);
    a0 = zeros(n,1);  % a0是解的初始近似值
    a = quadprog(H,f,A,b,Aeq,beq,lb,ub,a0,options);
    epsilon = 1e-9;
    sv_label = find(abs(a)> epsilon);
    svm.sva = a(sv_label);
    svm.Xsv = X(sv_label,:);
    svm.Ysv = Y(sv_label);
    svm.svnum = length(sv_label);
    svm.a = a;
end
  • test1.m主函数
%从人脸照片中随选择7张作为训练集
train_data = zeros(40*7,112*92);
test_data = zeros(40*3,112*92);
for i = 1:40
   file_name = ['s',num2str(i)];
   %随机选取同一文件夹下的7个图片
   index = randperm(10);
   for j = 1:7
       %文件名拼接
       name = ['att_faces\',file_name,'\',num2str(index(j)),'.pgm'];
       image = double(imread(name));
       %将矩阵展开为一行
       train_data(7*(i-1)+j,:) = reshape(image,1,112*92);
   end
   for j = 1:3
       %文件名拼接
       name = ['att_faces\',file_name,'\',num2str(index(7+j)),'.pgm'];
       image = double(imread(name));
       test_data(3*(i-1)+j,:) = reshape(image,1,112*92);
   end
end
%================PCA算法开始================
%()Step one: 中心化数据
numData = [train_data;test_data];
X = numData - repmat(mean(numData),40*10,1);

%()Step two: 求中心化数据的协方差矩阵
C = X'*X/size(X,1);

%()Step three: 计算特征向量和特征值
[V,D] = eig(C);

%()Step four: 将特征值和特征向量按降序排序
[dummy,order] = sort(diag(-D));
d=diag(D);      %将特征值取出,构成一个列向量
E=V(:,order);   %将特征向量按照特征值大小进行降序排列
newd=d(order);  %将特征值构成的列向量按降序排列

%()Step five: 取前k行组成矩阵P,构成变换矩阵
%选取5个特征贡献率值来当作测试对象
Crate = [0.75 0.80 0.85 0.90 0.95];
sumd = sum(newd);
for ii = 1:length(Crate)
    k = 0;
    for j = 1:length(newd)
        i = sum(newd(1:j,1),1)/sumd; %计算贡献率=前k个特征值之和/总特征值之和 
        if i > Crate(ii) %当贡献率大于95%时循环结束,并记下取多少个特征值
            k = j;
            break;
        end
    end
    P = E(:,1:k);
    newX = X*P;
    %================SVM 实现多分类================
    %================一对多SVM分类器===============
    %()Step one: 重新分配数据
    train_X = newX(1:280,:);
    test_X = newX(281:400,:);
    num = 40;
    data1 = cell(num,1);
    data2 = cell(num,1);
    for i = 1:num
       data1{i,1} = divdata(i,train_X,7);
       data2{i,1} = divdata(i,test_X,3);
    end

    %()Step two: 训练数据
    a = zeros(num,size(train_X,1));
    w = zeros(num,size(train_X,2));
    b = zeros(num,1);
    for i = 1:num
       svm = svmTrain(data1{i,1}(:,1:k),data1{i,1}(:,k+1),1);
       a(i,:) =  svm.a';
       for j = 1:size(train_X,2)
          w(i,j) = sum(a(i,:)'.*data1{i,1}(:,k+1).*data1{i,1}(:,j));
       end
       b(i,1) = sum(svm.Ysv-svm.Xsv*w(i,:)')/svm.svnum;
    end

    %()Step three: 测试数据
    labels_num = zeros(size(test_X,1),num);
    for i = 1:num
       labels_num(:,i) = (test_X*w(i,:)')'+b(i,1); 
    end

    %()Step four: 得出分类结果
    labels = zeros(size(test_X,1),1);
    for i = 1:size(test_X,1)
        [~,index] = max(labels_num(i,:));
        labels(i,1) = index;
    end

    %()Step five: 分析分类结果
    compare = zeros(size(test_X,1),1);
    for i = 1:num
       compare((i-1)*3+1:(i-1)*3+3,1) = i; 
    end
    success_num = length(find(compare==labels));
    success_rate = success_num/size(test_X,1);
    %打印分类正确率情况
    fprintf('==========one-to-all==========\n');
    fprintf(' k = %d\n',k);
    fprintf(' Crate:%f\n test_data_num: %d\n',Crate(ii),size(test_X,1));
    fprintf(' success_num: %f\n success_rate: %f\n',success_num,success_rate);
end

  test1.m中的特征贡献率是决定最终维数的标准,因为在对矩阵求解特征值和特征向量时,有些特征值太小了,我们可以不考虑它们,因此可以用特征贡献率来决定我们选取的特征数,也即维数。
  对于如何知道测试集某一数据是属于哪一类是这样判断的,因为40组数据在SVM训练器上得到的 ω \omega ω b b b是不一样的,因此对应的 ω T x + b \omega^Tx+b ωTx+b的值不同,若这个值是小于0的则不考虑,若这个值是大于0的,我们要找出这40组 ω \omega ω b b b中使得该值最大的 ω \omega ω b b b,其对应的训练集的正标签即为该 ω T x + b \omega^Tx+b ωTx+b最大嘛。

  • 测试结果
    测试数据集总数为120组。
特征贡献率 维数 正确组数 正确率
0.75 30 75 0.6250
0.80 44 66 0.5500
0.85 68 74 0.6167
0.90 111 80 0.6667
0.95 190 81 0.6750

2. 一对一SVM分类器

采用一对一的SVM的思想是对于40个类,我们任意选出两个类作为二分类的SVM训练集,将选定的两个类分别标上正标签和负标签,使用实验五的svmTrain训练训练集,这时我们将训练40×(40-1)/2 = 780次。

  • test2.m主函数,这个函数还用到了上述的svmTrain.m
%从人脸照片中随选择7张作为训练集
train_data = zeros(40*7,112*92);
test_data = zeros(40*3,112*92);
for i = 1:40
   file_name = ['s',num2str(i)];
   %随机选取同一文件夹下的7个图片
   index = randperm(10);
   for j = 1:7
       %文件名拼接
       name = ['att_faces\',file_name,'\',num2str(index(j)),'.pgm'];
       image = double(imread(name));
       %将矩阵展开为一行
       train_data(7*(i-1)+j,:) = reshape(image,1,112*92);
   end
   for j = 1:3
       %文件名拼接
       name = ['att_faces\',file_name,'\',num2str(index(7+j)),'.pgm'];
       image = double(imread(name));
       test_data(3*(i-1)+j,:) = reshape(image,1,112*92);
   end
end
%================PCA算法开始================
%()Step one: 中心化数据
numData = [train_data;test_data];
X = numData - repmat(mean(numData),40*10,1);

%()Step two: 求中心化数据的协方差矩阵
C = X'*X/size(X,1);

%()Step three: 计算特征向量和特征值
[V,D] = eig(C);

%()Step four: 将特征值和特征向量按降序排序
[dummy,order] = sort(diag(-D));
d=diag(D);      %将特征值取出,构成一个列向量
E=V(:,order);   %将特征向量按照特征值大小进行降序排列
newd=d(order);  %将特征值构成的列向量按降序排列

%()Step five: 取前k行组成矩阵P,构成变换矩阵
%选取5个特征贡献率值来当作测试对象
Crate = [0.75 0.80 0.85 0.90 0.95];
sumd = sum(newd);
for ii = 1:length(Crate)
    k = 0;
    for j = 1:length(newd)
        i = sum(newd(1:j,1),1)/sumd; %计算贡献率=前k个特征值之和/总特征值之和 
        if i > Crate(ii) %当贡献率大于95%时循环结束,并记下取多少个特征值
            k = j;
            break;
        end
    end
    P = E(:,1:k);
    newX = X*P;
    %================SVM 实现多分类================
    %这里采用一对一方法
    %()Step one: 重新分配数据
    train_X = newX(1:280,:);
    test_X = newX(281:400,:);
    num = 40*(40-1)/2;
    data1 = cell(num,1);
    data2 = cell(num,1);
    index = 1;
    two_labels = zeros(num,2);
    for i = 1:39
        for j = i+1:40
            data1{index,1} = [[train_X(i*7-6:i*7,:),ones(7,1)];[train_X(j*7-6:j*7,:),-ones(7,1)]];
            data2{index,1} = [[test_X(i*3-2:i*3,:),ones(3,1)];[test_X(j*3-2:j*3,:),-ones(3,1)]];
            two_labels(index,1) = i;
            two_labels(index,2) = j;
            index = index + 1;
        end
    end

    %()Step two: 训练数据
    a = zeros(num,14);
    w = zeros(num,size(train_X,2));
    b = zeros(num,1);
    for i = 1:num
       svm = svmTrain(data1{i,1}(:,1:k),data1{i,1}(:,k+1),1);
       a(i,:) =  svm.a';
       for j = 1:size(train_X,2)
          w(i,j) = sum(a(i,:)'.*data1{i,1}(:,k+1).*data1{i,1}(:,j));
       end
       b(i,1) = sum(svm.Ysv-svm.Xsv*w(i,:)')/svm.svnum;
    end

    %()Step three: 测试数据
    labels_num = zeros(size(test_X,1),num);
    for i = 1:num
       labels_num(:,i) = (test_X*w(i,:)')'+b(i,1); 
    end

    %()Step four: 得出分类结果
    %找出每个类别所在的分类器
    labels_svm = zeros(40,39);
    for i = 1:40
         labels_svm(i,:) = find(two_labels(:,1)==i | two_labels(:,2)==i);
    end
    %统计每一类在各个分类器分类正确率
    labels = zeros(size(test_X,1),1);
    for i = 1:size(test_X,1)
        pro = zeros(40,1);
        for j = 1:40
           for m = 1:39
               if labels_num(i,labels_svm(j,m)) > 0 && two_labels(labels_svm(j,m),1) == j
                   pro(j,1) = pro(j,1) + 1;
               elseif labels_num(i,labels_svm(j,m)) < 0 && two_labels(labels_svm(j,m),2) == j
                   pro(j,1) = pro(j,1) + 1;
               end
           end  
        end
        [~,index] = max(pro);
        labels(i,1) = index;
    end

    %()Step five: 分析分类结果
    compare = zeros(size(test_X,1),1);
    for i = 1:40
       compare((i-1)*3+1:(i-1)*3+3,1) = i; 
    end
    success_num = length(find(compare==labels));
    success_rate = success_num/size(test_X,1);
    %打印分类正确率情况
    fprintf('==========one-to-all==========\n');
    fprintf(' k = %d\n',k);
    fprintf(' Crate:%f\n test_data_num: %d\n',Crate(ii),size(test_X,1));
    fprintf(' success_num: %f\n success_rate: %f\n',success_num,success_rate);
end

  对于一对一的SVM训练模型,如何划分每一个测试集属于哪一类思路是这样的,因为关于某一个类的训练次数会有39次(以第一个人脸为例,它会和剩下的39个不同的人脸进行组合训练),而对于每一个测试集,我们的780次训练将会得到相应的 ω \omega ω b b b,每个测试集的 ω T x + b \omega^Tx+b ωTx+b的值将会是不一样的。比较这40个类中,测试集在每个类对应的39次训练中 ω T x + b \omega^Tx+b ωTx+b为正数的次数最多的即为该类。例如某一测试集在第一类人脸中对应的39次训练对应的 ω T x + b \omega^Tx+b ωTx+b为正数的次数是30次,而在其余的人脸中对应的39次训练对应的 ω T x + b \omega^Tx+b ωTx+b为正数的次数少于30次,那么可以判断该测试集的最终结果就是第一类人脸。(讲的有点out口,需要自行体会和理解)

  • 测试结果
特征贡献率 维数 正确组数 正确率
0.75 30 115 0.9583
0.80 44 115 0.9583
0.85 68 115 0.9583
0.90 111 115 0.9583
0.95 190 115 0.9583

3. 两者模型对比

从上述结果看来一对一模型比一对多模型的效果较好些,但总的来说这两者的效果都不是很理想的,高级教程中会有其它解决多分类的更高校的算法。一对多的不理想在于它的正负标签比例不均衡,而一对一的不理想在于它的训练次数大大增加了,因为目前只学了二分类,往后研究还得学习关于多分类的算法。

你可能感兴趣的:(机器学习)