opencv机器学习线性回归_(四十四)OpenCV中的机器学习-SVM

时间为友,记录点滴。

本来还想在特征点检测的道路上再走一走,学习一下Harr级联或者HOG什么的,但总有些概念感觉绕不过去。择日不如撞日,撞日不如今日。我们先揭开机器学习中的一个小角,看看大热的机器学习到底是什么东西。

啥是机器学习?

如今机器学习在中国的大热,还是要感谢2016年Alpha Go和李世石的那场人机围棋对决。外行人人谈AI,内行深度神经网络大红大紫。人工智能,机器学习,深度学习的关系

从关系上看,机器学习仅仅是实现人工智能的其中一种方式。所谓机器学习,我更愿意把机器拟人化,跟人类学习一样,可以从经验(数据)中去成长(学习),然后可以解决实际的问题。这个过程就是学习。人的过程就是人的学习,计算机的过程就是机器学习。

我们可以用这么定义:

机器学习=数据+算法+硬件

机器学习的分类

硬件:

我们结合上面的公式,先来看看硬件:CPU GPU 专用芯片最开始的机器学习当然是写成程序运行在CPU通用处理器上了;

但是随着深度学习的发展,人们发现GPU的架构更适合做深度神经网络这种大量的简单运算(要么NVIDIA的股票biubiu地飞);

后来,人们发现可以为某个算法单独设计一款ASIC,这样的运算能效比比GPU更高(要么Bitcoin能挖这么快);

数据和算法:

为什么把数据和算法放一块呢?因为他们总是相辅相成,特定的数据就需要有更优的算法来匹配。

在机器学习的算法世界里,如OpenCV的传统算法一样,浩如烟海,如果真的有人想要搞懂大部分的算法,一定是惊为天人了。听说下面这个哥们用numpy手写了好多。ddbourgin/numpy-ml​github.com

OpenCV中的机器学习

OpenCV的ml模块实现了很多算法,包括朴素贝叶斯、K近邻、SVM、决策树、Boosting、GBT、随机森林、神经网络等。其大多继承自同一基类,训练和预测的接口都是train(),predict(),使用较为方便。

我们进入到代码里面瞅一瞅:

在源码的\opencv-4.1.1\opencv-4.1.1\modules\ml\include\opencv2\ml.hpp文件中有提到:ml.hpp里面包含的内容

SVM(Support Vector Machines)

如果之前没接触过机器学习,那么越说多越迷糊;如果之前就听过NG大神的课,那多说就是废话。我们既然不是以机器学习算法研究推导为主题,那么就让我们在OpenCV中感受一个比较简单的算法,这样可以对机器学习的认识再向前前进一小步。

拿谁开刀呢?SVM!

SVM(Support Vector Machines)中文翻译是支持向量机,SVM算法在在1995年正式发表,在针对中小型数据规模的分类任务上有着卓越的效果,同时有着完整的理论证明,在20世纪末的几年和21世纪初的10年完胜神经网络,直到深度学习的兴起。但即便这样,现在SVM算法依旧被广泛的使用。

1、SVM要解决什么问题?

如果要搞清楚SVM是什么东西,还是要从我们为什么要引入SVM说起。

SVM主要用于解决模式识别领域中的数据分类问题,属于有监督学习算法的一种。SVM要解决的问题可以用一个经典的二分类问题加以描述。

我们把需求简化一下,两个数据在笛卡尔直角平面坐标系中分别被标定成红色点和蓝色点,那么我显然可以通过一条直线把两个数据区分开(在数字图像中,可以把两个点理解成特征点和背景,所以区分数据就是提取特征点的过程)

那么问题来了,这条线该怎么画?SVM就是来解决如何划线的问题。监督:人为规定此球(数据)为红色,彼球(数据)为蓝色的过程。

分类:就是把红球和篮球分开;

2、SVM中如何画线?

如何判断上图中的三条线,哪条比较好?直觉是红色的那条,为什么呢?因为当数据变多时,红色的线是最大可能分区数据的那条。可以画一条虚线连接两个圆的圆心作为辅助线,红线距离两个圆的圆心都比较远

嗯,感觉好有道理,这和SVM有什么关系呢?

我们知道,在计算机的世界里,所有的点都是离散的,比如下图:

如何区分上图的众多红球和篮球的过程中,你可以画一条A这样的线,也可以画一条B这样的线,他们都可以把红球和篮球区分开,但是哪个是最优的呢?

可不可以借鉴两个圆连接圆心求最大距离的思路呢?当然可以!

我们取(b)图放大,此时A就是此时的分界线,专业称为“决策线”。我们沿着决策线做向两边的平行线,直到碰到两边的第一个有效数据,画两条虚线。

定义两条虚线之间的距离为W为“分类间隔”,两边被虚线划中的点叫做“支持向量”

那么最佳的决策线应该是所有的支持向量到决策线的垂直距离d都相等,且最大。

所以,找线的问题就转变成求解支持向量到决策线的垂直距离d最大的问题。

在SVM支持向量中,所有的这些数据在空间中的表达式就是一个向量,而用于直接支持确定决策线的这些点,就叫做支持向量。因此,这个算法被叫做支持向量机。

数据即向量;

支持确立决策线的数据即支持向量;

通过支持向量求决策线的算法即支持向量机;

好了,我们今天了解了支持向量机能做什么以及为什么叫他支持向量机。至于公式推导SVM如何做到这一切的就留给明天吧,我们今天先来看看OpenCV中官方的SVM怎么使用的。

官方文件目录:opencv-4.1.1\samples\cpp\tutorial_code\ml\introduction_to_svm\introduction_to_svm.cpp

C++

这里面的三个关键点:

1、数据和标签

int labels[4] = {1, 1, -1, -1};

float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };trainingData为四个数据点(可以认为是球的圆心坐标)。

labels为对应数据的标签(监督,可以认为1为红球,-1为篮球)

2、训练

svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);

如下是OpenCV中train的函数原型:

/** @brief Trains the statistical model@param samples training samples@param layout See ml::SampleTypes.@param responses vector of responses associated with the training samples.*/

CV_WRAP virtual bool train( InputArray samples, int layout, InputArray responses );

其中:Sample表示训练样本数据

Layout 有两种组织方式ROW_SAMPLE与COL_SAMPLE

Responses 每个输入样本的标签

这里其实就是寻找决策线的过程。

3、分类

float response = svm->predict(sampleMat);

如下是OpenCV的predict原型

@param samples The input samples, floating-point matrix

@param results The optional output matrix of results.

@param flags The optional flags, model-dependent. See cv::ml::StatModel::Flags.

*/

CV_WRAP virtual float predict( InputArray samples, OutputArray results=noArray(), int flags=0 ) const = 0;

其中:samples:表示输入样本,供SVM判定。

OutputArray :表示输出矩阵

flag:表示支持模式

返回值为设定的标签值(这里是1或者-1),由此可以判定当前输入应该被分到哪一类中(也可以理解成是分在决策线的哪一边。)

#include #include #include #include #include

using namespace cv;

using namespace cv::ml;

int main(int, char**)

{

// Set up training data //! [setup1] int labels[4] = {1, 1, -1, -1};

float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };

//! [setup1] //! [setup2] Mat trainingDataMat(4, 2, CV_32F, trainingData);

Mat labelsMat(4, 1, CV_32SC1, labels);

//! [setup2]

// Train the SVM //! [init] Ptr svm = SVM::create();

svm->setType(SVM::C_SVC);

svm->setKernel(SVM::LINEAR);

svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));

//! [init] //! [train] svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);

//! [train]

// Data for visual representation int width = 512, height = 512;

Mat image = Mat::zeros(height, width, CV_8UC3);

// Show the decision regions given by the SVM //! [show] Vec3b green(0,255,0), blue(255,0,0);

for (int i = 0; i < image.rows; i++)

{

for (int j = 0; j < image.cols; j++)

{

Mat sampleMat = (Mat_(1,2) << j,i);

float response = svm->predict(sampleMat);

if (response == 1)

image.at(i,j) = green;

else if (response == -1)

image.at(i,j) = blue;

}

}

//! [show]

// Show the training data //! [show_data] int thickness = -1;

circle( image, Point(501, 10), 5, Scalar( 0, 0, 0), thickness );

circle(image, Point(255, 10), 5, Scalar(0, 0, 0), thickness);

circle( image, Point(501, 255), 5, Scalar(255, 255, 255), thickness );

circle( image, Point( 10, 501), 5, Scalar(255, 255, 255), thickness );

//! [show_data]

// Show support vectors //! [show_vectors] thickness = 2;

Mat sv = svm->getUncompressedSupportVectors();

for (int i = 0; i < sv.rows; i++)

{

const float* v = sv.ptr(i);

circle(image, Point( (int) v[0], (int) v[1]), 6, Scalar(0, 0, 255), thickness);

}

//! [show_vectors]

imwrite("result.png", image); // save the image

imshow("SVM Simple Example", image); // show it to the user waitKey();

return 0;

}

有了上面的监督数据+训练+预测,代码就好理解多了。直接看结果吧:代码我稍作了修改,主要是修改了标签值。

读者也可以自己改一下标签纸,感受一下支持向量和决策边界的移动。


版权声明:本文为weixin_39658726原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。