svm分类问题

SVM三宝:间隔、对偶、核技巧,SVM通过超平面对物体进行分类。

对SVM进行整理

参考代码

参考代码,实现功能是二分类

#include <opencv2/core.hpp>
#include "opencv2/opencv.hpp"
#include <opencv2/ml.hpp>
#include <string>
#include <iostream>

//1、描述
//打乱数据集
//2、输入
//matrix:打乱前数据集
//3、输出
//返回:打乱后数据集,也就是行与行互换
cv::Mat shuffleRows(const cv::Mat &matrix)
{
    std::vector <int> seeds;
    for (int cont = 0; cont < matrix.rows; cont++)        //将0~7存入到seeds里面
        seeds.push_back(cont);

//作用:将原数组(矩阵)打乱;
//参数:输入输出数组(一维),决定交换数值的行列的位置的一个系数,(可选)随机数产生器,0表示使用默认的随机数产生器,即seed=-1。rng决定了打乱的方法
    cv::randShuffle(seeds, 7);
    cv::Mat output;
    for (int cont = 0; cont < matrix.rows; cont++)  //打乱后的数据重新存入矩阵中
        output.push_back(matrix.row(seeds[cont]));

    return output;
}

//1、描述
//从文件夹中读取图像并标记,获得数据集hog特征
//2、输入
//type:文件夹路径
//3、输出
//des:数据集包含hog特征
//labels:数据集的标记
void readData(cv::Mat &des, cv::Mat &labels, int num, std::string type)
{
    for(int j = 0; j < 6; j++)
    {
        std::string path0 = type, temp0;
        std::stringstream ss1;              //类型转换
        ss1<<j;                             //向流中传值
        ss1>>temp0;                         //向temp0中写值
        path0.append(temp0);                //添加temp0
        path0.append("/");               //在temp0后面添加 /

        for (int i = 0; i < num; ++i)
        {
            //生成每张jpg图片的路径
            std::string path1 = path0, temp;
            std::stringstream ss2;
            ss2<<i;
            ss2>>temp;
            path1.append(temp);
            path1.append(".bmp");
            std::vector<float> des_temp;                      //存放结果,为HOG描述子向量
            std::cout<<path1<<std::endl;                       //输出读取图片的路径
            //图像预处理,方式必须和自动瞄准预处理相同
            cv::Mat img = cv::imread(path1, 1);        //读取图片
            cvtColor(img, img, cv::COLOR_BGR2GRAY);    //转为灰度图像
            medianBlur(img, img, 3);                 //中值滤波

            //HOG计算方法
            //检测窗口(40,40),块尺寸(16,16),块步长(8,8),cell尺寸(8,8),直方图bin个数9  ,需要修改
            cv::HOGDescriptor *hog = new cv::HOGDescriptor(cv::Size(40, 40), cv::Size(16, 16), cv::Size(8, 8), cv::Size(8, 8), 9);
            //Hog特征计算,检测窗口移动步长(1,1)
            hog->compute(img, des_temp, cv::Size(1, 1), cv::Size(0, 0)); 
            
            //一行数据
            cv::Mat des_temp1(1, int(des_temp.size()), CV_32FC1, des_temp.data());     
            des.push_back(des_temp1);                                    //将数据压缩至des中

            //对数据集进行标记
            if (j == 0)                                                  
                labels.push_back(float(0));
            else
              labels.push_back(float(1));                                 
//            labels.push_back(float(j));
        }
    }
}

int main()
{
    //训练SVM分类器
    cv::Mat train_data;                  //数据
    cv::Mat train_labels;                //标签
    int num_0 = 3731;                    //数据总量
    int hog_num = 576;                  //hog长度
    //读取数据并且标记标签
    readData(train_data, train_labels, num_0, "/home/demon/CLionProjects/zhang_new_SVM/TrainData/");   //读取训练集图片,并提取HOG特征

    std::cout << train_data.size << std::endl;   //每一副图像形成一行,3731*6=22386,22386*576
    std::cout << train_labels.size << std::endl; //22386*1,一列
    cv::hconcat(train_data, train_labels, train_data);  //矩阵拼接,水平拼接
    train_data = shuffleRows(train_data);       //打乱数据集
    train_labels = train_data.rowRange(0, num_0*6).colRange(hog_num, hog_num+1);  //取特定行,包括最后,不包括前面

    train_labels.convertTo(train_labels, 4);  //矩阵数据类型转换
 
   train_data = train_data.rowRange(0, num_0*6).colRange(0, hog_num);
       //训练参数 关于SVM参数挺详细的解释:https://blog.csdn.net/computerme/article/details/38677599
       // 创建分类器并设置参数
            cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::create(); // 创建分类器
            svm->setType(cv::ml::SVM::C_SVC);             //C_SVC用于分类,C_SVR用于回归
            svm->setKernel(cv::ml::SVM::KernelTypes::RBF); //选用的核函数
            svm->setGamma(0.01);//核函数中的参数gamma,针对多项式/RBF/SIGMOID核函数;
            svm->setC(10.0);   //SVM最优问题参数,设置C-SVC,EPS_SVR和NU_SVR的参数;
           //设置终止条件,训练次数最多3000次,或者误差小于1e-6
            svm->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 3000, 0.1,1e-6));
           //  
           //该类(TermCriteria)变量需要3个参数,一个是类型,第二个参数为迭代的最大次数,最后一个是特定的阈值。
          //类型有COUNT, EPS or COUNT + EPS
          //分别代表着迭代终止条件为达到最大迭代次数终止,迭代到阈值终止,或者两者都作为迭代终止条件。
            std::cout << "参数设置完成" << std::endl;
            std::cout << "开始训练分类器" << std::endl;
           //训练数据和标签的结合
            cv::Ptr<cv::ml::TrainData>Inputdata = cv::ml::TrainData::create(train_data,cv::ml::ROW_SAMPLE,train_labels);
            svm->train( Inputdata);//layout:ROW_SAMPLE 为数据排列的方式,即行为一个样本
            std::cout << "分类器训练完成" << std::endl;

            //保存训练器
            svm->save("my_11_1_autohit.xml");
            std::cout << "my_11_1_autohit.xml" << std::endl;
            
/*********************************************以下属于测试部分******************************/

//载入分类器并测试
std::cout << "开始导入分类器文件...\n";
cv::Ptr<cv::ml::SVM> svm1 = cv::ml::StatModel::load<cv::ml::SVM>("my_11_1_autohit.xml");
std::cout << "成功导入分类器文件...\n";
// 测试分类器准确率
    int num =12;                                                                     //每类测试集有12张图片
    int num_img = 0;
    float count = 0;
    for(int j = 0; j < 6; j++)                                                       //测试的种类
    {
        std::string type = "/home/demon/CLionProjects/zhang_new_SVM/testimage/";       //测试集路径
        std::string path0 = type, temp0;                                        
        std::stringstream ss1;                                                       //数据类型转换
        ss1<<j;
        ss1>>temp0;
        path0.append(temp0);                                                 
        path0.append("/");
                                                                     
        for (int i = 0; i < num; ++i)
        {
            std::string temp1 = temp0;
            int result;
            if (j == 0)                                              //测试集实际结果
                result = 0;
            else
                result = 1;

            std::string path1 = path0, id;
            std::stringstream ss2;                               //每类测试集的图片名字是0~11.bmp
            ss2<<i;
            ss2>>id;
            temp1.append(id);
            path1.append(id);
            path1.append(".bmp");
            temp1.append(".bmp");
            std::vector<float> des_temp;                         //存储特征子向量
            std::cout<<path1<< std::endl;
            cv::Mat img = cv::imread(path1, 1);
            cvtColor(img, img, cv::COLOR_BGR2GRAY);              //灰度转换
            medianBlur(img, img, 3);
            //同上面解释
            cv::HOGDescriptor *hog = new cv::HOGDescriptor(cv::Size(40, 40), cv::Size(16, 16), cv::Size(8, 8), cv::Size(8, 8), 9);
            hog->compute(img, des_temp, cv::Size(1, 1), cv::Size(0, 0));
            std::cout<<i<<std::endl;

            float pre;
            //图片存储路径
            std::string path2 = "/home/demon/CLionProjects/zhang_new_SVM/True",path3 = "/home/demon/CLionProjects/zhang_new_SVM/Flase", labels, pres;
            ss2.clear();
            ss2<<result;
            ss2>>labels;
            path3.append(labels + "_" );
            path3.append(id + "_");
            pre = svm1->predict(des_temp);
            ss1.clear();
            ss1<<pre;
            ss1>>pres;

            path3.append(pres + ".jpg");

            std::cout<<"True labels: "<<float(result)<<"  "<<"Predicted labels: "<<pre<<std::endl;
            count += (pre == float(result));

            if (pre == float(result))
            {
//                imwrite(path2, img);
            } else
            {
                imwrite(path3, img);
            }
            num_img++;
        }
    }
    std::cout << "正确的识别个数: " << count <<  std::endl;
    std::cout << "正确率为:" << (count / num_img) * 100 << "%\n";
    std::cout<<num_img;
    return 0;
}

解释

(二分类)以上这段代码实现的是识别是杂乱无章的图片还是正常图片,也就是杂乱无章图片返回为0,1~5的数字返回是1,从而判断正确率。也可以进行多分类问题

本人测试集采用的是0文件下存储垃圾图片,15文件夹下存储的是15的数字,每类文件下存储3731张图片。测试集则是每类文件下存储12张图片。

整体流程是:1,对图片进行预处理(转为灰度图像、中值滤波等)

​ 2,提取HOG特征,得到HOG描述子向量

​ 3,对训练集进行遍历操作,完成以上两个步骤

​ 4,打乱数据集、矩阵拼接,重新得到训练数据和标签(存疑)

​ 5,创建分类器并设置SVM参数,包括:

(1)设置SVM是用于分类还是回归 ---------------------------------------------------- svm->setType

(2)选用的核函数,以上选用的是径向基函数------------------------------------svm->setKernel

(3)核函数中的参数gamma,针对多项式/RBF/SIGMOID核函数;----------------svm->setGamma

(4)最优问题参数,设置C-SVC,EPS_SVR和NU_SVR的参数---------------- svm->setC

(5)设置终止条件,比如通过训练的次数和误差来判断---------------------------svm->setTermCriteria

​ (6) 将训练数据和标签的结合 -----------------------------------------------------------cv::ml::TrainData::create

​ (7) //保存训练器----------------------------------------------------------------------------- svm->save

​ 6,测试分类器准确率

​ (1)预处理图像,提取HOG特征,得到描述子向量

​ (2) pre = svm1->predict(des_temp); 得到预测结果

​ (3)通过与实际结果进行比对得到准确率

二、对于设置SVM参数部分也可以使用svm->trainAuto的方式进行训练,其优势是自动帮忙优化参数。

// 创建分类器并设置参数    
cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::create();    
svm->setType(cv::ml::SVM::C_SVC); //C_SVC用于分类,C_SVR用于回归    
svm->setKernel(cv::ml::SVM::KernelTypes::LINEAR); //选用的核函数     
std::cout << "参数设置完成" << std::endl; //训练分类器    
std::cout << "开始训练分类器" << std::endl;    
//svm自动优化参数,同时将标签和数据进行结合    
svm->trainAuto(train_data, cv::ml::ROW_SAMPLE, train_labels);
std::cout << "分类器训练完成" << std::endl; //保存训练器    
svm->save("my_11_1_autohit.xml");   
std::cout << "my_11_1_autohit.xml" << std::endl; 

核函数包含以下类型:

undefined

注意:

SVM的训练函数是ROW_SAMPLE类型的,也就是说,送入SVM训练的特征需要reshape成一个行向量,所有训练数据全部保存在一个Mat中,一个训练样本就是Mat中的一行,最后还要讲这个Mat转换成CV_32F类型,例如,如果有𝑘个样本,每个样本原本维度是(ℎ,𝑤),则转换后Mat的维度为(𝑘,ℎ∗𝑤)

对于多分类问题,label矩阵的行数要与样本数量一致,也就是每个样本要在label矩阵中有一个对应的标签,label的列数为1,因为对于一个样本,SVM输出一个值,我们在训练前需要做的就是设计这个值与样本的对应关系。对于有𝑘个样本的情况,label的维度是(𝑘,1)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值