龙空技术网

图像识别之初探SVM分类器

萌萌哒程序猴 54

前言:

现时同学们对“sift算法的缺点和改进”都比较注意,姐妹们都想要了解一些“sift算法的缺点和改进”的相关资讯。那么小编同时在网摘上搜集了一些关于“sift算法的缺点和改进””的相关文章,希望同学们能喜欢,朋友们一起来学习一下吧!

SVM的中文名为支持向量机,是一种非常经典的有监督数据分类算法,也即该算法首先需要训练,训练得到分类模型之后,再使用分类模型对待分类数据进行分类。有监督数据分类算法的大致过程如下图所示:

上图中,训练数据与待分类数据通常为n维向量,n可以是1,2,3,4,5,......

对于图像,一般有两种方法把其所有像素点的像素值转换为n维向量:

方法一:图像数据属于二维矩阵,可以直接把二维矩阵的多行数据按行进行首尾拼接,组成只有一行的n维向量。这种做法通常是在图像尺寸很小的情况,如果图像很大,那么得到的n维向量长度会很长,不仅严重影响训练和分类的速度,有时候数据量过多反而对分类起干扰作用,影响分类的准确性。

方法二:提取图像的特征,包括Sift、Surf特征和Hog特征等。

(1) 检测Hog特征,对于相同尺寸的图像和相同的输入参数,检测得到Hog特征的维数是一样的, 因此Hog特征可直接作为n维向量输入分类器。

(2) 检测Sift特征,Sift特征是对图像上特殊点(可以理解为具有明显特征的角点)的向量描述,每个特殊点的Sift特征为一个128维向量。然而使用Sift算法在不同图像上检测到的特殊点的个数往往是不一样的,此时不同图像的128维向量的个数也不一样,因此不同图像中所有128维向量首尾拼接组成的n维向量的长度也不一样了。如果向量长度不一样,是不能输入分类器的,为了保证每张图像的特征向量长度一致,PCA降维就派上用场了,通常使用PCA降维算法把多个128维向量降维成1个128维向量。

(3) 检测Surf特征,Surf算法是由Sift算法改进和演变而来的,Surf算法主要比Sift算法更快,一个Surf特征为一个64维向量,检测到多个Surf特征之后,也需要像检测Sift特征那样,进行PCA降维,确保每张图像的特征向量维度一致。

最近本人一直在琢磨SVM的数学原理,还有不少地方没能理解,比如求最优解的数学原理、怎么做到多类的分类、核函数等。下面首先以二维向量为例,来讲讲我对SVN分类器的初步理解(扩展到n维也是一样的原理),然后再讲解怎么使用Opencv的SVM模块对手写数字图片和Cifar-10数据集进行分类。

假设有多个二维向量如下:

以上的每个二维向量在x1-x2平面坐标系中表现为一个点,我们的目标是使用一条直线把这些点分成两类,两类中距离最近的点分别为Xi和Xj,我们要寻找的直线在Xi和Xj的中间,也即Xi和Xj到直线的距离都为d,当d取得最大值的时候,这条直线就是我们要找的分类界限啦~

在我们最常见的x-y坐标系中,直线的一般形式为Ax+By+c=0。同理,在x1-x2坐标系中,我们分别使用w1代替A,w2代替B,x1代替x,x2代替y,b代替C,得到直线的一般表达式为w1x1+w2x2+b=0,写成向量形式:

根据点到直线的距离公式,有d的计算式:

由于d是最短距离,对于所有点均满足:

也即有:

对上式,不等号两边都除以d,则有:

我们记:

于是有:

为了方便分类,我们通常给每个点(二维向量)贴上一个标签y,属于红点则y=1,属于蓝点则y=-1,从几何原理来看,有:

1. 当WTX+b>0时点位于直线上方,属于红点类型,此时y=1;

2. 当WTX+b<0时点位于直线下方,属于蓝点类型,此时y=-1。

所以上式又可以转换为:

y的绝对值为1,因此y相当于正负号的作用,由此上式可以合并为:

我们注意到,在x1-x2坐标系中,由于d|W|是一个大于零的实数,以下两个解析式表示的是同一条直线:

因此,问题可以等效转换为求使d取得最大值的以上的解析式二。

此时同样由点到直线的距离公式可得d的计算式为:

又由以上的(1)式可得:

由上式可知当|Wd|取得最小值时,d取得最大值,因此问题又可以等效转换为求下式的最小值,之所以这样转换,使为了后续方便通过求导来求得最优解:

推导到这里,由以上的(2)式和(3)式,我们就可以得到SVM最优化问题的数学表达了:

(1) 约束条件:对x1-x2坐标系中所有的点Xk (k=1,2,3,4,5,......),均满足:

(2) 在满足以上约束条件的前提下,求解(3)式的Wd

本文的原理就讲到这里,关于如何求得以上数学问题的最优解,我们后续再继续研究和讲解。下面我们来讲讲如何使用Opencv的SVM模块来对手写数字图像和Cifar-10图像进行分类。关于如何获取手写数字图像和Cifar-10图像,此处不再重复,读者如果感兴趣可以参考我之前的博文:

图像识别之KNN算法的理解与应用

图像识别之KNN算法的理解与应用(2)

首先是对手写数字图像进行训练和分类,由于手写数字图像特征相对简单,且尺寸为较小的20*20,因此我们使用以上提到的方法一把每张图像数据转换为一个n维向量,再把n维向量输入SVM模块中。代码如下:

void SVM_Hand_Digital_test(void){  char ad[128] = { 0 };  int testnum = 0, truenum = 0;  Ptr<SVM> model = SVM::create();   //创建一个SVM分类器  model->setType(SVM::C_SVC);     //设置SVM类型  model->setKernel(SVM::LINEAR);  //设置核函数,这里使用线性核  model->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, 1e-6));   //设置求SVM最优解的最大迭代次数和精度  Mat traindata, trainlabel;  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < 400; j++)    {      sprintf_s(ad, "%d/%d.jpg", i, j);      Mat srcimage = imread(ad);      srcimage = srcimage.reshape(1, 1);    //将多行数据转换为一行的向量      traindata.push_back(srcimage);   //将向量输入到训练矩阵中      trainlabel.push_back(i);      //将标签输入到标签矩阵中    }  }  traindata.convertTo(traindata, CV_32F);   //将训练数据转换为浮点型数据  model->train(traindata, ROW_SAMPLE, trainlabel);   //输入训练数据和标签,开始训练分类模型  for (int i = 0; i < 10; i++)  {    for (int j = 400; j < 500; j++)    {      testnum++;      sprintf_s(ad, "%d/%d.jpg", i, j);      Mat testdata = imread(ad);      testdata = testdata.reshape(1, 1);   //将多行数据转换为一行的向量      testdata.convertTo(testdata, CV_32F);  //将待分类数据转换为浮点型数据      Mat result;      int  response = model->predict(testdata);   //使用训练得到的分类模型对待分类数据进行预测,得到分类结果      if (response == i)   //如果预测的分类结果与标签一致,则认为分类成功      {        truenum++;      }    }  }  cout << "测试总数" << testnum << endl;  cout << "正确分类数" << truenum << endl;  cout << "准确率:" << (float)truenum / testnum * 100 << "%" << endl;}

运行以上代码,得到结果如下。训练之后,对1000张手写数字图像进行预测分类,准确率达到90.5%,这个分类结果还是比较理想的。

接下来,我们再使用Opencv的SVM模块对Cifar-10数据集进行分类。我们使用以上提到的方法二把每张图像转换为一个n维向量:提取每张图像的Hog特征,并把Hog特征输入SVM模块。首先在data_batch_1.bin~data_batch_5.bin这个5个文件中都取前900张图像对SVM模型进行训练,然后再对test_batch.bin中的前800张图像进行预测分类。代码如下:

void SVM_cifar_test_hog(void){  char ad[128] = { 0 };  int testnum = 0, truenum = 0;  Ptr<SVM> model = SVM::create();  //创建一个SVM分类器  model->setType(SVM::C_SVC);     //设置SVM类型  model->setKernel(SVM::RBF);  //设置核函数,这里使用非线性核RBF  model->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, 1e-6));  //设置求SVM最优解的最大迭代次数和精度  Mat traindata, trainlabel;  const int trainnum = 900;  //定义HOG检测器  HOGDescriptor *hog = new HOGDescriptor(cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), cvSize(2, 2), 9);   //特征提取滑动窗口, 块大小, 块滑动步长, 胞元(cell)大小  vector<float> descriptors;  //定义HOG特征数组  const int win_step = 16;    //加载data_batch_1.bin的训练数据  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < trainnum; j++)    {      printf("i=%d, j=%d\n", i, j);      sprintf_s(ad, "cifar/batch1/%d/%d.tif", i, j);      Mat srcimage = imread(ad, CV_LOAD_IMAGE_GRAYSCALE);                                  hog->compute(srcimage, descriptors, Size(win_step, win_step), Size(0, 0));  //计算图像的HOG特征      Mat hogg(descriptors); //将特征数组存入Mat矩阵中      srcimage = hogg.reshape(1, 1);    //将多行数据转换为一行的向量      traindata.push_back(srcimage);   //将HOG特征向量加载到训练矩阵中      trainlabel.push_back(i);      //将标签输入到标签矩阵中    }  }  //加载data_batch_2.bin的训练数据  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < trainnum; j++)    {      printf("i=%d, j=%d\n", i, j);      sprintf_s(ad, "cifar/batch2/%d/%d.tif", i, j);      Mat srcimage = imread(ad, CV_LOAD_IMAGE_GRAYSCALE);      hog->compute(srcimage, descriptors, Size(win_step, win_step), Size(0, 0));      Mat hogg(descriptors);      srcimage = hogg.reshape(1, 1);    //Mat Mat::reshape(int cn, int rows = 0),cn表示通道数,为0则通道不变,rows表示矩阵行数      traindata.push_back(srcimage);      trainlabel.push_back(i);    }  }  //加载data_batch_3.bin的训练数据  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < trainnum; j++)    {      printf("i=%d, j=%d\n", i, j);      sprintf_s(ad, "cifar/batch3/%d/%d.tif", i, j);      Mat srcimage = imread(ad, CV_LOAD_IMAGE_GRAYSCALE);      hog->compute(srcimage, descriptors, Size(win_step, win_step), Size(0, 0));      Mat hogg(descriptors);      srcimage = hogg.reshape(1, 1);    //Mat Mat::reshape(int cn, int rows = 0),cn表示通道数,为0则通道不变,rows表示矩阵行数      traindata.push_back(srcimage);      trainlabel.push_back(i);    }  }  //加载data_batch_4.bin的训练数据  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < trainnum; j++)    {      printf("i=%d, j=%d\n", i, j);      sprintf_s(ad, "cifar/batch4/%d/%d.tif", i, j);      Mat srcimage = imread(ad, CV_LOAD_IMAGE_GRAYSCALE);      hog->compute(srcimage, descriptors, Size(win_step, win_step), Size(0, 0));      Mat hogg(descriptors);      srcimage = hogg.reshape(1, 1);    //Mat Mat::reshape(int cn, int rows = 0),cn表示通道数,为0则通道不变,rows表示矩阵行数      traindata.push_back(srcimage);      trainlabel.push_back(i);    }  }  //加载data_batch_5.bin的训练数据  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < trainnum; j++)    {      printf("i=%d, j=%d\n", i, j);      sprintf_s(ad, "cifar/batch5/%d/%d.tif", i, j);      Mat srcimage = imread(ad, CV_LOAD_IMAGE_GRAYSCALE);      hog->compute(srcimage, descriptors, Size(win_step, win_step), Size(0, 0));      Mat hogg(descriptors);      srcimage = hogg.reshape(1, 1);    //Mat Mat::reshape(int cn, int rows = 0),cn表示通道数,为0则通道不变,rows表示矩阵行数      traindata.push_back(srcimage);      trainlabel.push_back(i);    }  }  traindata.convertTo(traindata, CV_32F);   //将训练数据转换为浮点型数据  model->train(traindata, ROW_SAMPLE, trainlabel);   //输入训练数据和标签,开始训练分类模型  //对test_batch.bin中的前800张图像进行预测分类  for (int i = 0; i < 10; i++)  {    for (int j = 0; j < 800; j++)    {      testnum++;      sprintf_s(ad, "cifar/test_batch/%d/%d.tif", i, j);      Mat testdata = imread(ad, CV_LOAD_IMAGE_GRAYSCALE);      hog->compute(testdata, descriptors, Size(win_step, win_step), Size(0, 0));   //计算待分类图像的HOG特征      Mat hogg(descriptors);      testdata = hogg.reshape(1, 1);      testdata.convertTo(testdata, CV_32F);      Mat result;      int  response = model->predict(testdata);   //对Hog特征进行预测分类      if (response == i)  //如果预测的分类结果与标签一致,则认为分类成功      {        truenum++;      }    }  }  cout << "测试总数" << testnum << endl;  cout << "正确分类数" << truenum << endl;  cout << "准确率:" << (float)truenum / testnum * 100 << "%" << endl;}

运行以上代码,得到结果如下。对8000张图像进行分类,准确率只有55.2375%,分类的准确率与前文中我们使用KNN算法的分类结果相比,并不没有提升多少。当图像复杂之后,这些传统的数据分类算法就显得有些无力了。因此在以后的文章中,我们将尝试卷积神经网络与深度学习来进行分类。

以上只是总结了一下本人对SVM算法的初步理解,本人远没有达到理解透的境界,但是学无止境,让我们继续加油吧~~

标签: #sift算法的缺点和改进 #svm算法实现多维数据图 #svm最优化问题