【Svm機器學習篇】Opencv3.4.1與C++實現對分類問題的訓練與預測】

樹莓派派酒發表於2018-08-19

     支援向量機(SVM)中最核心的是4個字——“支援向量”,一旦在兩類或多累樣本集中定位到某些特定的點作為支援向量,就可以依據這些支援向量計算出來分類超平面,再依據超平面對類別進行歸類劃分就是水到渠成的事了。有必要回顧一下什麼是支援向量機中的支援向量。 上圖中需要對紅色和藍色的兩類訓練樣本進行區分,實現綠線是決策面(超平面),最靠近決策面的2個實心紅色樣本和1個實心藍色樣本分別是兩類訓練樣本的支援向量,決策面所在的位置是使得兩類支援向量與決策面之間的間隔都達到最大時決策面所處的位置。

     一般情況下,訓練樣本都會存在噪聲,這就導致其中一類樣本的一個或多個樣本跑到了決策面的另一邊,摻雜到另一類樣本中。針對這種情況,SVM加入了鬆弛變數(懲罰變數)來應對,確保這些噪聲樣本不會被作為支援向量,而不管它們離超平面的距離有多近。包括SVM中的另一個重要概念“核函式”,也是為訓練樣本支援向量的確定提供支援的。 在OpenCV中,SVM的訓練、歸類流程如下: 1. 獲取訓練樣本 SVM是一種有監督的學習分類方法,所以對於給出的訓練樣本,要明確每個樣本的歸類是0還是1,即每個樣本都需要標註一個確切的類別標籤,提供給SVM訓練使用。對於樣本的特徵,以及特徵的維度,SVM並沒有限定,可以使用如Haar、角點、Sift、Surf、直方圖等各種特徵作為訓練樣本的表述參與SVM的訓練。Opencv要求訓練資料儲存在float型別的Mat結構中。

1. SVM的核心思想

SVM的分類思想本質上和線性迴歸LR分類方法類似,就是求出一組權重係數,線上性表示之後可以分類。我們先使用一組trainging set來訓練SVM中的權重係數,然後可以對testingset進行分類。

說的更加更大上一些:SVM就是先訓練出一個分割超平面separation hyperplane, 然後該平面就是分類的決策邊界,分在平面兩邊的就是兩類。顯然,經典的SVM演算法只適用於兩類分類問題,當然,經過改進之後,SVM也可以適用於多類分類問題。

我們希望找到離分隔超平面最近的點,確保它們離分隔面的距離儘可能遠。這裡點到分隔面的距離被稱為間隔margin. 我們希望這個margin儘可能的大。支援向量support vector就是離分隔超平面最近的那些點,我們要最大化支援向量到分隔面的距離。

那麼為了達到上面的目的,我們就要解決這樣的一個問題:如何計算一個點到分隔面的距離?這裡我們可以借鑑幾何學中點到直線的距離,需要變動的是我們這裡是點到超平面的距離。具體轉換過程如下:

 

程式碼:

 

#include<sstream>
#include<opencv2\opencv.hpp>
#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include<opencv2/ml/ml.hpp>

using namespace std;
using namespace cv;
using namespace ml;


cv::Mat trainData;
cv::Mat trainLabel;



void get_data1();
void get_data2();



int main() {
	get_data1();
	get_data2();
	trainData.convertTo(trainData, CV_32FC1);
	trainLabel.convertTo(trainLabel, CV_32SC1);
	//vector<Mat> test_set;

	//get_test(io, test_set);




	Ptr<SVM> model = SVM::create();
	model->setType(SVM::C_SVC);     //SVM型別
	model->setKernel(SVM::LINEAR);  //核函式,這裡使用線性核
	model->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER, 100, 1e-6));
	//model->train(trainData, ROW_SAMPLE, trainLabel);//只訓練不生成xml
	Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabel);
	//訓練生成xml

	std::cout << "SVM: start train ..." << endl;
	model->train(tData);
	std::cout << "SVM: train success ..." << endl;
	model->save("svm.xml");


	waitKey();
	getchar();
	return 0;

}





void get_data1() {

	ifstream fin("alist.txt", ios::in);

	string s;
	while (getline(fin, s)) {

		if (s.length() != 0) {
			string ss;
			ss = "C:\\Users\\wangz\\Desktop\\\apple\\" + s;
			Mat m2 = imread(ss, 0);
			if (m2.empty()) {
				cout << "fail" << endl;
			}
			Mat temp;
			resize(m2, temp, Size(256, 256));
			//imshow("12", m2);
			trainData.push_back(temp.reshape(0, 1));  //連續放入Mat容器中 
			trainLabel.push_back(1);
			//trainLabel.push_back(Mat(1, 1, CV_32SC1, 0));									  //trainData.convertTo(trainData, CV_32FC1);
		}

	}
}
void get_data2() {

	ifstream fin("mlist.txt", ios::in);
	string s;

	//Mat mm(29, 256 * 256, CV_8UC1);
	while (getline(fin, s)) {
		string ss;
		if (s.length() != 0) {

			ss = "C:\\Users\\wangz\\Desktop\\svm\\microsoft\\" + s;
			Mat m2 = imread(ss, 0);
			if (m2.empty()) {
				cout << "fail" << endl;
			}
			Mat temp;
			resize(m2, temp, Size(256, 256));
			//imshow("12", m2);
			trainData.push_back(temp.reshape(0, 1));  //連續放入Mat容器中 
			trainLabel.push_back(-1);
			//trainLabel.push_back(Mat(1, 1, CV_32SC1, 1));										  //trainData.convertTo(trainData, CV_32FC1);
		}

	}
}
/*
ifstream fin1(path1, ios::in);
string s1;
while (getline(fin1, s1)) {

if (s1.length() != 0) {

string slable;
slable = s1.substr(s1.find(" "));
int index = atoi(slable.c_str());  //slable - '0';
cout << index << endl;
trainLabels.push_back(Mat(1, 1, CV_32SC1, &index));

}

}
}

*/
//樣本資料必須是CV_32FC1型別。opencv3版本決定的
//樣本標籤必須是CV_32SC1,opencv3後從int陣列轉換為CV_32SC1型別,而opencv2是從float資料轉換。


  程式碼二:

#include<sstream>
#include<opencv2\opencv.hpp>
#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include<opencv2/ml/ml.hpp>

using namespace std;
using namespace cv;
using namespace ml;




void get_data1(Mat& trainimage,vector<int>& trainlab);
void get_data2(Mat& trainimage,vector<int>& trainlab);

int main() {
	Mat classes;
	Mat traindata, trainimg;

	vector<int>trainlables;
	get_data1(trainimg,trainlables);
	get_data2(trainimg, trainlables);

	Mat(trainimg).copyTo(traindata);
	traindata.convertTo(traindata, CV_32FC1);

	Mat(trainlables).copyTo(classes);
	classes.convertTo(classes, CV_32SC1);
	//vector<Mat> test_set;

	//get_test(io, test_set);




	Ptr<SVM> model = SVM::create();
	model->setType(SVM::C_SVC);     //SVM型別
	model->setKernel(SVM::LINEAR);  //核函式,這裡使用線性核
	model->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER, 100, 1e-6));
	//model->train(trainData, ROW_SAMPLE, trainLabel);//只訓練不生成xml
	Ptr<TrainData> tData = TrainData::create(traindata, ROW_SAMPLE, classes);
	//訓練生成xml

	std::cout << "SVM: start train ..." << endl;
	model->train(tData);
	std::cout << "SVM: train success ..." << endl;
	Mat tset=imread("123.jpg",0);
	resize(tset, tset, Size(256, 256));
	tset.reshape(0, 1);
	double k= model->predict(tset);
	cout << k;
	//model->save("svm.xml");
//

	waitKey();
	getchar();
	return 0;

}





void get_data1(Mat& trainimage, vector<int>& trainlab) {

	ifstream fin("alist.txt", ios::in);

	string s;
	while (getline(fin, s)) {

		if (s.length() != 0) {
			string ss;
			ss = "C:\\Users\\wangz\\Desktop\\\apple\\" + s;
			Mat m2 = imread(ss, 0);
			if (m2.empty()) {
				cout << "fail" << endl;
			}
			Mat temp;
			resize(m2, temp, Size(256, 256));
			//imshow("12", m2);
			trainimage.push_back(temp.reshape(0, 1));  //連續放入Mat容器中 
			trainlab.push_back(1);
			//trainLabel.push_back(Mat(1, 1, CV_32SC1, 0));									  //trainData.convertTo(trainData, CV_32FC1);
		}

	}
	cout << trainimage.size() << endl;
	cout << trainlab.size() << endl;
}
void get_data2(Mat& trainimage,vector<int>& trainlab) {

	ifstream fin("mlist.txt", ios::in);
	string s;

	//Mat mm(29, 256 * 256, CV_8UC1);
	while (getline(fin, s)) {
		string ss;
		if (s.length() != 0) {

			ss = "C:\\Users\\wangz\\Desktop\\svm\\microsoft\\" + s;
			Mat m2 = imread(ss, 0);
			if (m2.empty()) {
				cout << "fail" << endl;
			}
			Mat temp;
			resize(m2, temp, Size(256, 256));
			//imshow("12", m2);
			trainimage.push_back(temp.reshape(0, 1));  //連續放入Mat容器中 
			trainlab.push_back(-1);
			//trainLabel.push_back(Mat(1, 1, CV_32SC1, 1));										  //trainData.convertTo(trainData, CV_32FC1);
		}


	}
	cout << trainimage.size() << endl;
	cout << trainlab.size() << endl;
}
/*
ifstream fin1(path1, ios::in);
string s1;
while (getline(fin1, s1)) {

if (s1.length() != 0) {

string slable;
slable = s1.substr(s1.find(" "));
int index = atoi(slable.c_str());  //slable - '0';
cout << index << endl;
trainLabels.push_back(Mat(1, 1, CV_32SC1, &index));

}

}
}

*/
//樣本資料必須是CV_32FC1型別。opencv3版本決定的
//樣本標籤必須是CV_32SC1,opencv3後從int陣列轉換為CV_32SC1型別,而opencv2是從float資料轉換。


  第一次用opencv訓練svm出問題是很正常的如下:

百度解決方法如下:

http://www.it1352.com/481183.html

https://blog.csdn.net/galileoyuyu/article/details/82083673

參考部落格:https://www.cnblogs.com/br170525/p/9236479.html

https://blog.csdn.net/mao_hui_fei/article/details/80455538

https://blog.csdn.net/im6520/article/details/75240435

https://blog.csdn.net/a1111h/article/details/72568970

https://blog.csdn.net/sinat_34474705/article/details/80502789?utm_source=blogxgwz0

https://blog.csdn.net/xuan_zizizi/article/details/71102018

hog訓練的:https://blog.csdn.net/u013419097/article/details/80253977

相關文章