让我们看看如何训练支持向量机的模型,保存训练后的模型并测试模型以使用OpenCV检查其预测准确性的百分比。
数据组织:
使用imagenetscraper
和autocrop
,我们收集来自网络,农作物面临数据,并将其调整到批量较小的尺寸。收集的数据需要进行有意义的组织,以便我们可以通过编程方式和手动方式进行访问。使用以下文件夹结构-
FFR_dataset/
|-- Age
| |-- adult
| |-- child
| |-- old
| |-- teen
|-- Emotion
| |-- anger
| |-- contempt
| |-- happy
| |-- neutral
| |-- sad
| |-- surprise
|-- Gender
|-- female
|-- male
我们在代码中使用相同的目录名称来访问它们,以训练,保存和预测识别结果。每个文件夹至少需要50张图像来训练模型,以获得良好的预测结果。训练更多图像可以改善结果,但不建议这样做,因为执行该过程会花费很多时间,并且无法带来明显的改善。
执行
通过使用官方opencv存储库中提供的样本来训练带有HOG的SVM train_HOG.cpp,我们实现了C++代码来训练,保存和预测具有多张面孔的图像上的面部特征。
共有三种功能类型:年龄,情感和性别。四个年龄段,六种情绪和两种性别类型。因此,实现了n类分类器以识别面部数据上的每个特征。
步骤#1:对于每种功能类型,即(年龄,情感或性别)循环遍历“ n”个运行时间。
// CTrainTestHOG::Run(int run_times)
for (auto ft : m_FeatureList) {
DEBUGLW("\tFeature type=[%s]\n", ft.first.c_str());
std::vector predictionAccuracyList;
predictionAccuracyList.reserve(run_times);
for (int run = 0; run < run_times; ++run) {
DEBUGLW("\t\tRun=[%d]\n", run);
vector trainData, predData;
vector trainLabels, predLabels;
this->get_ft_dataset(ft.first, trainData, predData,
trainLabels, predLabels);
// ... train, predict and measure the SVM model.
}
}
步骤#2:在每次运行中,遍历要素类型中的要素值,并将图像获取到向量或数组中,即从“性别”->“男性”和“性别->”女性文件夹中获取所有图像。
// CTrainTestHOG::get_ft_dataset()
std::set& featureValueList = m_FeatureList.find(ft)->second;
for (auto fv : featureValueList) {
DEBUGLW("\t\t\tFeature value=[%s]\n", fv.c_str());
std::vector _trainData;
std::vector _predData;
std::vector _trainLabels;
std::vector _predLabels;
errCode = this->get_ftfv_dataset(ft, fv, _trainData, _predData,
_trainLabels, _predLabels);
if (errCode != EXIT_SUCCESS)
break;
trainData.insert(trainData.end(), _trainData.begin(), _trainData.end());
predData.insert(predData.end(), _predData.begin(), _predData.end());
trainLabels.insert(trainLabels.end(), _trainLabels.begin(), _trainLabels.end());
predLabels.insert(predLabels.end(), _predLabels.begin(), _predLabels.end());
}
步骤3至6:
- 将向量中的图像裁剪为脸部矩形,并使用新的脸部列表更新图像向量。
- 在脸部列表中的每个图像上执行任何预处理任务,例如将尺寸调整为较小的尺寸(64、64)。
- 随机将向量中经过预处理的人脸图像随机排列以引入随机输入数据。
- 将数据集分为训练(80%)和预测(20%)数据。
// CTrainTestHOG::get_ftfv_dataset()
std::vector imgList;
this->get_images(folderName, imgList);
this->get_cropped_faces(imgList);
this->get_preprocessed_faces(imgList);
//-- return on empty img list to prevent seg fault
if (imgList.empty()) {
errCode = EXIT_FAILURE;
DEBUGLE("Error img list is empty!\n");
break;
}
DEBUGLD("\t\t\timgList.size()=[%ld]\n", imgList.size());
std::random_shuffle(imgList.begin(), imgList.end());
// 80% for training
int trainPart = imgList.size() * 0.8;
// 20% for predicting
int predPart = imgList.size() - trainPart;
DEBUGLD("\t\t\ttrainPart=[%d], predPart=[%d]\n", trainPart, predPart);
trainData.reserve(trainPart);
predData.reserve(predPart);
ft_t::iterator ft_iter = m_FeatureList.find(ft);
fv_t::iterator fv_iter = ft_iter->second.find(fv);
int label = std::distance(ft_iter->second.begin(), fv_iter);
DEBUGLD("\t\t\tlabel=[%d]\n", label);
int i = 0;
for (; i < trainPart; ++i) {
trainData.push_back(imgList.at(i));
trainLabels.push_back(label);
}
DEBUGLD("\t\t\ti=[%d], trainData.size()=[%ld],
trainLabels.size()
= [% ld]\n ", i, trainData.size(),
trainLabels.size());
for (; i < imgList.size(); ++i) {
predData.push_back(imgList.at(i));
predLabels.push_back(label);
}
DEBUGLD("\t\t\ti=[%d], predData.size()=[%ld],
predLabels.size()
= [% ld]\n ", i, predData.size(), predLabels.size());
步骤#7:为训练数据中的每个图像计算HOG。
// CTrainTestHOG::computeHOGs()
HOGDescriptor hog;
vector hogMats;
vector descriptors;
for (auto img : imgHogList) {
hog.winSize = img.size() / 8 * 8;
hog.compute(img, descriptors);
cv::Mat descriptors_mat(Mat(descriptors).clone());
hogMats.push_back(descriptors_mat);
}
imgHogList.swap(hogMats);
步骤#8:将训练数据向量转换为opencv Mat对象以训练SVM。
// CTrainTestHOG::convert_to_ml()
for (size_t i = 0; i < train_samples.size(); ++i) {
CV_Assert(train_samples[i].cols == 1 || train_samples[i].rows == 1);
if (train_samples[i].cols == 1) {
cv::transpose(train_samples[i], tmp);
tmp.copyTo(trainData.row((int)i));
}
else if (train_samples[i].rows == 1) {
train_samples[i].copyTo(trainData.row((int)i));
}
}
步骤#9:将训练数据Mat对象与训练数据标签向量一起传递给svm train函数。
// CTrainTestHOG::Run()
trainLabels.resize(ml_train_data.rows);
// train svm
DEBUGLW("\t\tTraining SVM - begin\n");
m_pSVM->train(ml_train_data, ROW_SAMPLE, trainLabels);
DEBUGLW("\t\tTraining SVM - end\n");
步骤#10:保存经过训练的模型。
//-- step 10, CTrainTestHOG::Run()
cv::String svmModelFileName = cv::format("%s/cv4_svm_%s_model.xml",
getenv(FFR_DATASET_PATH),
ft.first.c_str());
m_pSVM->save(svmModelFileName.c_str());
DEBUGLW("\t\tSaved SVM model=[%s]\n",
svmModelFileName.c_str());
步骤#11:通过为每个预测图像计算HOG来预测模型,将预测数据集转换为opencv mat对象,并使用标签矢量调用svm预测来存储结果。
//-- step 11, CTrainTestHOG::Run()
// test the model
// compute HOG for each pre-processed face
errCode = this->computeHOGs(predData);
if (errCode != EXIT_SUCCESS) {
DEBUGLE("Error in computing HOGs for the feature "
"type=[%s]\n",
ft.first.c_str());
break;
}
// convert HOG feature vectors to SVM data
Mat ml_pred_data;
vector resultLabels;
errCode = this->convert_to_ml(predData, ml_pred_data);
if (errCode != EXIT_SUCCESS) {
DEBUGLE("Error in converting to ml for the "
"feature type=[%s]\n",
ft.first.c_str());
break;
}
predLabels.resize(ml_pred_data.rows);
// resultLabels.resize(ml_pred_data.rows);
// test svm
DEBUGLW("\t\tTesting SVM - begin\n");
Mat responses_mat;
m_pSVM->predict(ml_pred_data, responses_mat);
for (size_t i = 0; i < ml_pred_data.rows; ++i) {
resultLabels.push_back(responses_mat.at(i));
}
DEBUGLW("\t\tTesting SVM - end\n");
步骤#12和#13:通过将预期的预测标签与预测的标签进行比较,计算其准确性的百分比。
// CTrainTestHOG::Run()
// check the accuracy
float accuracy = 0.0f;
this->get_prediction_accuracy(predLabels, resultLabels, accuracy);
DEBUGLW("\t\tPrediction accuracy=[%lf]\n", accuracy);
predictionAccuracyList.push_back(accuracy);
//-- step 13, CTrainTestHOG::Run()
// check the mean accuracy of 'n' runs
float sum_of_accuracies = std::accumulate(
predictionAccuracyList.begin(),
predictionAccuracyList.end(), 0.0);
float mean_accuracy = sum_of_accuracies / predictionAccuracyList.size();
DEBUGLW("\t\tMean prediction accuracy=[%lf]\n",
mean_accuracy);
使用以下命令行参数运行可执行文件。
./train_hog --test --in= --out= --show
输入:
输出:
使用OpenCV 2.4的HOG SVM的结果日志
使用OpenCV 4.0的HOG SVM的结果日志
注意:由于图片中所有三个人的头发都很长,因此将性别检测为“女性”,这是假阳性。在机器学习算法中,由于输入样本图像具有歧义特征,误报总是很常见。