线性不可分情况下的支持向量机测试
本部分设置了线性不可分的训练集情况,采用不同类型的支持向量机核进行测试,由于opencv3的ml模块支持trainAuto,可以不必预先对核函数参数进行确定,大大提高了可用性。
代码如下
bool rbfSVMExample() {
//支持向量机设置
cv::Ptr<SVM> rbfSVM=SVM::create();
rbfSVM->setType(SVM::C_SVC);
rbfSVM->setKernel(SVM::RBF);
//rbfSVM->setGamma(5);//设置rbf核参数
//rbfSVM->setC(100);//设置惩罚因子
rbfSVM->setTermCriteria(TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,(int)1e7, 1e-6));
//训练数据集
const int width = 512, height = 512;
int samplesNum = 100;//样本数
float tPercent = 0.2;//正样本比例
int tSampleNum = int(samplesNum * tPercent);
Mat trainData(2 * samplesNum, 2, CV_32FC1);//32FC1
Mat labelData(2 * samplesNum, 1, CV_32SC1);//32SC1
RNG rng(unsigned(time(NULL)));//以当前系统时间为种子点,设置随机数生成器rng
Mat trainClass = trainData.rowRange(0, tSampleNum);//ROI操作,左闭右开区间
Mat c = trainClass.colRange(0, 1);
rng.fill(c, RNG::UNIFORM, Scalar(width/3), Scalar(width*2/3));//采用均匀分布填充,范围为1/3-2/3宽度
c = trainClass.colRange(1, 2);//坐标y
rng.fill(c, RNG::UNIFORM, Scalar(height/3), Scalar(height*2/3));
labelData.rowRange(0, tSampleNum).setTo(1);//将labelData的对应范围设置1
//类2进行同样的处理
trainClass = trainData.rowRange(tSampleNum,(samplesNum-tSampleNum)*1/4+tSampleNum);
c = trainClass.colRange(0, 1);
rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(width*2/3));
c = trainClass.colRange(1, 2);
rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(height*1/3));
labelData.rowRange(tSampleNum, (samplesNum - tSampleNum) * 1 / 4 + tSampleNum).setTo(2);
trainClass = trainData.rowRange((samplesNum-tSampleNum)*1/4+tSampleNum,(samplesNum - tSampleNum) * 2 / 4 + tSampleNum);
c = trainClass.colRange(0, 1);
rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(width * 1 / 3));
c = trainClass.colRange(1, 2);
rng.fill(c, RNG::UNIFORM, Scalar(height * 1 / 3), Scalar(height));
labelData.rowRange((samplesNum - tSampleNum) * 1 / 4 + tSampleNum, (samplesNum - tSampleNum) * 2 / 4 + tSampleNum).setTo(2);
trainClass = trainData.rowRange((samplesNum-tSampleNum)*2/4+tSampleNum,(samplesNum - tSampleNum) * 3 / 4 + tSampleNum);
c = trainClass.colRange(0, 1);
rng.fill(c, RNG::UNIFORM, Scalar(width * 1 / 3), Scalar(width));
c = trainClass.colRange(1, 2);
rng.fill(c, RNG::UNIFORM, Scalar(height * 2 / 3), Scalar(height));
labelData.rowRange((samplesNum - tSampleNum) * 2 / 4 + tSampleNum, (samplesNum - tSampleNum) * 3 / 4 + tSampleNum).setTo(2);
trainClass = trainData.rowRange((samplesNum-tSampleNum)*3/4+tSampleNum, samplesNum);
c = trainClass.colRange(0, 1);
rng.fill(c, RNG::UNIFORM, Scalar(width * 2 / 3), Scalar(width));
c = trainClass.colRange(1, 2);
rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(height * 2 / 3));
labelData.rowRange((samplesNum-tSampleNum)*3/4+tSampleNum,samplesNum).setTo(2);
//整理样本
Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, labelData);
//训练SVM
cout << "开始训练" << endl;
rbfSVM->trainAuto(tData);//自动确定rbf模型参数
cout << "结束训练" << endl;
cout << "C" <<rbfSVM->getC()<< endl;
cout << "Gamma" <<rbfSVM->getGamma()<< endl;
//显示训练分区
Vec3b green1(0, 100, 0), blue1(100, 0, 0);
Mat imgShow = Mat::zeros(height, width, CV_8UC3);
for (int i = 0; i < imgShow.rows; ++i) // 遍历画布上的每个点
{//妙啊
for (int j = 0; j < imgShow.cols; ++j)
{
cv::Mat SampleMat = (cv::Mat_<float>(1, 2) << j, i);//遍历并预测所有像素点
auto Response = rbfSVM->predict(SampleMat);
if (Response == 1)
{
imgShow.at<cv::Vec3b>(i, j) = green1;
}
else if (Response==2)
{
imgShow.at<cv::Vec3b>(i, j) = blue1;
}
//SVM默认将类别分为+1和-1
}
}
// 在画布上画上训练数据所表示的点
int Thinckness = -1;
float px, py;
for (int i = 0; i < tSampleNum; i++) {
px = trainData.at<float>(i, 0);
py = trainData.at<float>(i, 1);
circle(imgShow, Point((int)px, (int)py),2,Scalar(0,255,0));
}
for (int i = tSampleNum; i < samplesNum; i++) {
px = trainData.at<float>(i, 0);
py = trainData.at<float>(i, 1);
circle(imgShow, Point((int)px, (int)py), 2, Scalar(255, 0, 0));
}
Thinckness = 2;
//显示支持向量,rbf模式下好像显示不了...不显示支持向量了?
Mat supportVectors = rbfSVM->getUncompressedSupportVectors();//获取未压缩支持向量
for (int i = 0; i < supportVectors.rows; i++) {//注意是<rows
auto vecTmp = supportVectors.ptr<float>(i);//读取supportVectors Mat中第i个数据坐标,数据类型为float(?),因为样本label给的是32FC1,也就是32bit的float,channel1,所以支持向量也是float格式
//??auto换成float*反而变卡了
cv::circle(imgShow, Point(static_cast<int>(vecTmp[0]), static_cast<int>(vecTmp[1])), 6, Scalar(128, 128, 128), Thinckness);
}
cv::imshow("svmExample", imgShow);
cv::waitKey();
return true;
}
trainAuto()
貌似trainAuto只能正确地得出LINEAR、RBF和INTER,POLY要预先设定参数,SIGMOID也不行,CHI2不能进行分类
3. Poly核不能直接trainAuto出来;
4. INTER核
多类型分类
对应修改对应测试样本集标签序号即可;