TrainData類型,拆分數據集setTrainTestSplitRatio(),計算准確率calcError()(OpenCV案例源碼letter_recog.cpp解讀3)


機器學習中,需要總樣本集,拆分成訓練集、測試集,計算訓練、測試、整體的准確率。

OpenCV在ml.hpp中為我們准備了特有格式TrainData,它會把標簽、特征集融合到其中,方便操作。

針對TrainData類型,提供了非常完美的函數,具體介紹如下:

1、拆分TrainData類型總樣本集,注意默認是會打亂行順序的。

setTrainTestSplitRatio(double ratio, bool shuffle=true); //比例方式,前ratio(0~1)行是訓練集,推薦使用此函數

setTrainTestSplit(int count, bool shuffle=true); //具體指定方式,前count行是訓練集
第二個參數默認為true,即打亂行后再拆分。

2、計算准確率,此函數計算的是錯誤率,准確率用100減下即可

float calcError( const Ptr<TrainData>& data, bool test, OutputArray resp )
  data——融合了標簽、特征的總樣本集,TrainData類型
  test——計算訓練集准確率設為false,測試集准確率設為true,總的准確率設為false同時data不可以使用拆分函數setTrainTestSplitRatio或setTrainTestSplit。
  resp——預測的標簽結果,內部調用了predict函數

3、獲取訓練集、測試集

Mat getTrainSamples()
Mat getTestSamples()

【TrainData類型的解釋】

下圖是letter-recognition.data文件的內容,第1列是標簽,后16列是特征,需要分別讀入到Mat格式的responses和samples中。之后用create函數創建TrainData類型對象(融合了標簽、特征)。

此解釋針對的是分類問題,而不是回歸擬合。samples特征集中每1行為1個樣本,每1列為1個特征。
Ptr<TrainData> create(InputArray samples, int layout, InputArray responses, InputArray varIdx=noArray(), InputArray sampleIdx=noArray(),InputArray sampleWeights=noArray(), InputArray varType=noArray());
  samples——特征集(特征變量組成的集合),必須是32FC1(32位浮點單通道)類型
  layout——樣本布局,ROW_SAMPLE = 0,COL_SAMPLE = 1,此處只用前者
  responses——標簽集,必須是CV_32S類型(即int型)。一維向量,與每一行樣本對應。
  varIdx——參與訓練的特征(默認都參與),元素為0、非0的一維向量(行列都可),大小為特征數(列數)。1對應的樣本參與訓練(非0有效),格式用CV_8U即可
  sampleIdx——參與訓練的樣本(默認都參與),同上,大小為總樣本數(行數)
  sampleWeights——樣本權重,忽略不用
  varType——特征變量的類型,忽略不用

總樣本集(上圖)中responses在哪一列都可以,只要保證samples(行為樣本,列為特征)、responses(行、列都可以)即可。

將總樣本集拆分為訓練集和測試集的方法有兩種:用自帶函數(推薦),用參數sampleWeights(元素為0、非0的一維向量(行列都可))。

方法一:使用加粗部分代碼即可,setTrainTestSplit或setTrainTestSplitRatio、getTrainSamples、getTestSamples

Ptr<TrainData> tdata=TrainData::create(samples, ROW_SAMPLE, responses);//其他參數默認,即所有樣本包含在TrainData類對象tdata中 //tdata->setTrainTestSplit(16000);//前16000行為訓練集 tdata->setTrainTestSplitRatio(0.8);//比例方式,前80%行作為訓練集,推薦 Mat trainSet = tdata->getTrainSamples();//獲取訓練集 Mat testSet = tdata->getTestSamples();//獲取測試集 //Mat res = tdata->getResponses();//所有標簽 //Mat classNum = tdata->getClassLabels();//26類(分26類問題),65~90

tdata是TrainData類型(包含了標簽、特征),trainSet 、testSet 是Mat類型,只有特征。

方法二:

Mat sample_idx = Mat::zeros(1, samples.rows, CV_8U); Mat train_samples = sample_idx.colRange(0, (int)(data.rows*0.8)); //操作train_samples就是操作sample_idx,淺拷貝。sample_idx中前80%變為1 
train_samples.setTo(Scalar::all(1)); Ptr<TrainData> tdata=TrainData::create(samples, ROW_SAMPLE, responses, noArray(), sample_idx);//sample_idx中1對應的樣本參與訓練(非0有效)

此時tdata中共有前80%的樣本(既有特征又有標簽)。

 【樣本集注意點】

如果樣本集不同類別間是雜亂的,那么上述函數可以隨意用。不過,一般我們會自己歸類,如2分類問題。正樣本、負樣本各自獨立有序。為了保證正負樣本都會參與訓練,所以最好不要拆分TrainData類型總樣本集。否則可能會出現前80%行一種類別過多的情況。

如果可以保證拆分后,訓練集不同類別樣本均衡,那可以使用訓練集、測試集這種我們習慣的方式。否則,不推薦拆分。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM