本文基于别人博客的整理,原文:
https://mp.weixin.qq.com/s/rY29GWzKOY0n73lD2M9dug
一、简介
1、导入feature和label txt数据↔OpenCV Mat类型
2、数据乱序处理
3、准备训练集和测试集
4、SVR训练
5、选择SVR最优模型
二、详介
1、导入feature和label txt数据↔OpenCV Mat类型
(1)txt文件数据格式如图所示:数据与数据之间以空格隔开,每一行以\r\n结尾
(2)具体代码如下:
void ReadTxtFileToMat(const std::string strFilePath, cv::Mat& mat)
{
std::ifstream infile;
infile.open(strFilePath, std::ios::in);
if (!infile.is_open())
{
std::cerr << "Open " << strFilePath << " failed!" << std::endl;
return;
}
for (int i = 0; i < mat.rows; i++)
{
for (int j = 0; j < mat.cols; j++)
{
infile >> mat.at<float>(i, j);
}
}
infile.close();
}
void ReadMatToTxtFile(const cv::Mat& mat, std::string strFilePath)
{
std::ofstream outfile;
outfile.open(strFilePath, std::ios::out);
for (int i = 0; i < mat.rows; i++) {
for (int j = 0; j < mat.cols; j++) {
outfile << mat.at<float>(i, j) << " ";
}
outfile << "\n";
}
outfile.close();
}
2、数据乱序处理
(1)代码如下:生成一个有序vector类型seeds,调用OpenCV randShuffle函数将其打乱,最后根据乱序的seeds去原数据取值
void ShuffleData(const cv::Mat& matFeature, const cv::Mat& matLabel, cv::Mat& matFeatureShuffled, cv::Mat& matLabelShuffled)
{
if (matFeature.rows != matLabel.rows)
{
std::cerr << "WARNING: The feature length unequals the label length!" << std::endl;
return;
}
std::vector<int> seeds;
for (int i = 0; i < matFeature.rows; i++)
{
seeds.push_back(i);
}
cv::randShuffle(seeds);
for (int i = 0; i < matFeature.rows; i++)
{
matFeatureShuffled.push_back(matFeature.row(seeds[i]));
matLabelShuffled.push_back(matLabel.row(seeds[i]));
}
}
3、准备训练集和测试集
(1)这个很简单,直接上代码:
void SplitData(cv::Mat& matData, cv::Mat& matDataTrain, cv::Mat& matDataTest, float fRate)
{
int train_num = int(fRate * matData.rows);
for (int i = 0; i < train_num; i++)
{
matDataTrain.push_back(matData.row(i));
}
for (int i = train_num; i < matData.rows; i++)
{
matDataTest.push_back(matData.row(i));
}
}
4、SVR训练
(1)Nu-SVR算法里面比较重要的两个参数:C和Nu;代码中给定了一系列的C和Nu值,依次进行训练
float C[11] = { 0.01, 0.05, 0.1, 0.3, 0.5, 0.8, 1, 2, 5, 10, 50 };
float Nu[8] = { 0.01, 0.02, 0.05, 0.08, 0.1, 0.3, 0.5, 0.8 };
// 训练模型:pitch
std::cout << "Pitch: 训练模型初始化...\n";
cv::Ptr<cv::ml::SVM> svr_model_pitch = cv::ml::SVM::create();
svr_model_pitch->setKernel(cv::ml::SVM::LINEAR);
svr_model_pitch->setC(C[i]);
svr_model_pitch->setNu(Nu[j]);
svr_model_pitch->setType(cv::ml::SVM::NU_SVR);
svr_model_pitch->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 10000, 1e-5));
std::cout << "Pitch: 开始训练...\n";
// 分别对pitch, yaw, roll进行训练,所以是label_train.col(0)
svr_model_pitch->train(data_train, cv::ml::ROW_SAMPLE, label_train.col(0));
std::cout << "Pitch: 训练结束...\n";
// 保存模型
std::cout << "保存模型: start...\n";
// 路径格式
sprintf_s(pitch_xml, "%s_C%1.2f_Nu%1.2f.xml", xml[0], C[i], Nu[j]);
svr_model_pitch->save(pitch_xml);
std::cout << "保存模型: end...\n";
// 测试模型
std::cout << "测试模型:start...\n";
cv::Mat predict_pitch;
svr_model_pitch->predict(data_test, predict_pitch);
std::cout << "测试模型:end...\n";
std::cout << "保存预测数据到txt文档:start...\n";
// 路径格式
sprintf_s(predict_pitch_txt, "%s_C%1.2f_Nu%1.2f.txt", predict[1], C[i], Nu[j]);
ReadMatToTxtFile(predict_pitch, predict_pitch_txt);
std::cout << "保存预测数据到txt文档:end...\n\n";
5、选择SVR最优模型
(1)通过计算测试数据与预测数据的误差均值与方差来选择最优的C和Nu组合
// 通过OpenCV absdiff函数计算测试数据和预测数据差值的绝对值
cv::absdiff(predict_pose, label_test, error_pose);
// 计算均值和方差
cv::Scalar mean;
cv::Scalar dev;
cv::meanStdDev(error_pose.col(0), mean, dev);
float mean_pitch = mean.val[0];
float sde_pitch = dev.val[0];
cv::meanStdDev(error_pose.col(1), mean, dev);
float mean_yaw = mean.val[0];
float sde_yaw = dev.val[0];
cv::meanStdDev(error_pose.col(2), mean, dev);
float mean_roll = mean.val[0];
float sde_roll = dev.val[0];
(2)基于均值和方差越小越好的原则选择最优模型