Hello
Mình sử dụng SVM trong OpenCV để kiểm nghiệm thuật toán SVM chạy ntn như sau
Đây là chương trình phân lớp nhị phân 2 tập dữ liệu tách biệt tuyến tính. Chạy ổn
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/ml/ml.hpp>
using namespace std;
using namespace cv;
using namespace cv::ml;
int main(int argc, char *argv[]) {
Mat image = Mat::zeros(512, 512, CV_8UC3);
int labels[6] = {1, -1, 1, -1, -1, 1};
float trainingData[6][2] =
{
{501, 10 },
{255, 10 },
{501, 255},
{10 , 501},
{310, 300},
{300, 350}
};
Mat labelsMat(6, 1, CV_32SC1, labels);
Mat trainingDataMat(6, 2, CV_32FC1, trainingData);
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);
Vec3b blueRegion(254,195,189), greenRegion(189,254,198);
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
Mat sampleMat = (Mat_<float>(1,2) << j,i); // [j,i] in matrix == (x,y) in 2d
float response = svm->predict(sampleMat);
if (response == 1) image.at<Vec3b>(i,j) = blueRegion;
else if (response == -1) image.at<Vec3b>(i,j) = greenRegion;
}
}
int thickness = -1;
int lineType = 4;
Scalar bluePoint(255, 0, 0), greenPoint(0,100,0);
float x1, x2;
for (int i = 0; i < trainingDataMat.rows; i++) {
x1 = trainingDataMat.at<float>(i, 0);
x2 = trainingDataMat.at<float>(i, 1);
if (labelsMat.at<int>(i, 0) == 1) circle(image, Point(x1,x2), 5, bluePoint, thickness, lineType);
else if (labelsMat.at<int>(i, 0) == -1) circle(image, Point(x1,x2), 5, greenPoint, thickness, lineType);
}
thickness = 2;
Mat sv = svm->getUncompressedSupportVectors();
for (int i = 0; i < sv.rows; ++i) {
const float *v = sv.ptr<float>(i);
circle(image,Point((int)v[0],(int)v[1]),6,Scalar(0,0,255),thickness,lineType);
}
imshow("Linear SVM Binary Classifier", image);
waitKey(0);
system("pause");
return 0;
}
model được cấu hình các tham số sau
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC); // phân loại n lớp (n>2), cho phép sử dụng softmargin với độ lỗi C
svm->setKernel(SVM::LINEAR); // hàm sử dụng để chuyển dữ liệu từ không gian giả thiết sang không gian đặc trưng là hàm tuyến tính
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); // điều kiện dừng khi thuật toán lặp là 100 lần và độ chính xác của model đạt 1e-6 ~ 0.000001
Kết quả
[DEAD IMAGE]
1 số kết quả khác
[DEAD IMAGE]
Bây giờ vẫn chương trình trên nhưng mình sửa 1 chút dữ liệu cho 2 tập phi tuyến tính như sau
int labels[6] = {1, -1, 1, -1, -1, 1};
float trainingData[6][2] =
{
{501, 10 },
{255, 10 },
{501, 255},
{10 , 501},
{310, 300}, // phi tuyến
{300, 350} // phi tuyến
};
Vẫn để cấu hình tham số model như cũ và thử chạy thì có kêt quả
Có thể thấy siêu phẳng tối ưu tìm được đã bị sai. Giờ mình sẽ cấu hình thêm tham số độ lỗi C để chương trình sử dụng softmargin
svm->setC(0.1);
Thì kết quả vẫn như cũ
Ở đây có 1 chương trình phân lớp nhị phân khi dữ liệu phi tuyến nhưng có thể sử dụng được softmargin của trang chủ OpenCV.
https://docs.opencv.org/3.4.1/d0/dcc/tutorial_non_linear_svms.html
Họ cũng làm như chương trình trên, chỉ có là dữ liệu của họ nhiều hơn , tạo bằng cách random, và điều kiện dừng khi thuật toán lặp của họ khác. Mình cũng đã thử điện kiện dừng của họ vào chương trình trên nhưng vẫn không được.
Còn 1 vấn đề nữa. Chương trình của họ chạy sẽ ra ntn
Ở đây các điểm được vòng màu đỏ là support vector. Mà theo định nghĩa, support vector là tập các điểm hỗ trợ xây đựng siêu phẳng tối ưu, chính là các điểm mà 2 siêu phẳng lề song song đi quá = các điểm gần siêu phẳng tối ưu nhất. Nhưng với softmargin, như ảnh trên có rât nhiều điểm được vòng màu đỏ. Vậy các điểm bị bỏ đi (nằm trong vùng k an toàn, vùng lề) có được coi là 1 support không?
Thank you.