在opencv3中實現機器學習算法之:利用最近鄰算法(knn)實現手寫數字分類


手寫數字digits分類,這可是深度學習算法的入門練習。而且還有專門的手寫數字MINIST庫。opencv提供了一張手寫數字圖片給我們,先來看看

這是一張密密麻麻的手寫數字圖:圖片大小為1000*2000,有0-9的10個數字,每5行為一個數字,總共50行,共有5000個手寫數字。在opencv3.0版本中,圖片存放位置為

/opencv/sources/samples/data/digits.png

我們首先要做的,就是把這5000個手寫數字,一個個截取出來,每個數字塊大小為20*20。直接將每個小圖塊進行序列化,因此最終得到一個5000*400的特征矩陣。樣本數為5000,維度為400維。取其中前3000個樣本進行訓練。

注意:截取的時候,是按列截取。不然取前3000個樣本進行訓練就會出現后幾個數字訓練不到。

具體代碼:

#include "stdafx.h"
#include "opencv2\opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;
using namespace cv::ml;

int main()
{
    Mat img = imread("E:/opencv/opencv/sources/samples/data/digits.png");
    Mat gray;
    cvtColor(img, gray, CV_BGR2GRAY);
    int b = 20;
    int m = gray.rows / b;   //原圖為1000*2000
    int n = gray.cols / b;   //裁剪為5000個20*20的小圖塊
    Mat data,labels;   //特征矩陣
    for (int i = 0; i < n; i++)
    {
        int offsetCol = i*b; //列上的偏移量
        for (int j = 0; j < m; j++)
        {
            int offsetRow = j*b;  //行上的偏移量
            //截取20*20的小塊
            Mat tmp;
            gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
            data.push_back(tmp.reshape(0,1));  //序列化后放入特征矩陣
            labels.push_back((int)j / 5);  //對應的標注
        }

    }
    data.convertTo(data, CV_32F); //uchar型轉換為cv_32f
    int samplesNum = data.rows;
    int trainNum = 3000;
    Mat trainData, trainLabels;
    trainData = data(Range(0, trainNum), Range::all());   //前3000個樣本為訓練數據
    trainLabels = labels(Range(0, trainNum), Range::all());

    //使用KNN算法
    int K = 5;
    Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabels);
    Ptr<KNearest> model = KNearest::create();
    model->setDefaultK(K);
    model->setIsClassifier(true);
    model->train(tData);

    //預測分類
    double train_hr = 0, test_hr = 0;
    Mat response;
    // compute prediction error on train and test data
    for (int i = 0; i < samplesNum; i++)
    {
        Mat sample = data.row(i);
        float r = model->predict(sample);   //對所有行進行預測
        //預測結果與原結果相比,相等為1,不等為0
        r = std::abs(r - labels.at<int>(i)) <= FLT_EPSILON ? 1.f : 0.f;          

        if (i < trainNum)
            train_hr += r;  //累積正確數
        else
            test_hr += r;
    }

    test_hr /= samplesNum - trainNum;
    train_hr = trainNum > 0 ? train_hr / trainNum : 1.;

    printf("accuracy: train = %.1f%%, test = %.1f%%\n",
        train_hr*100., test_hr*100.);
    waitKey(0);
    return 0;
}

根據經驗,利用最近鄰算法對手寫數字進行分類,會有很高的精度,因此在本文中我們采用的是knn算法。

最終結果:

訓練精度為95.9%, 測試精度為92.6%。如果對手寫數字識別准確率達不到90%以上,就沒有什么實際作用了。如果調整訓練樣本數,這個精度應該會有所改變。


免責聲明!

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



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