OpenCV-6-ANN_MLP神經網絡模型訓練

OpenCV-6-ANN_MLP神經網絡模型訓練

使用語言:Java 1.8
操作系統:windows x64
OpenCV:4.1.1


ANN_MLP神經網絡理論介紹

神經網絡介紹

神經網絡是當前機器學習領域普遍所應用的,例如可利用神經網絡進行圖像識別、語音識別等,從而將其拓展應用於自動駕駛汽車。

目前在機器學習領域火爆的開源軟件庫中,TensorFlow算其中一個,裏面有大量的機器學習和深度神經網絡方面的研究,畢竟由Google 大腦小組開發出來。

神經網絡的變種目前有很多,如誤差反向傳播(Back Propagation,BP)神經網路概率神經網絡卷積神經網絡(Convolutional Neural Network ,CNN-適用於圖像識別,GOOGLetNet用的就是這個)、時間遞歸神經網絡(Long short-term Memory Network ,LSTM-適用於語音識別)等。但最簡單的神經網絡則是多層感知器(Muti-Layer Perception ,MLP)

MLP多層感知器神經網絡

MLP是基於生物神經元的模擬和簡化,得到多層感知器MLP的基本機構,就是輸入層、隱層和輸出層MLP神經網絡不同層之間是全連接的(全連接的意思就是:上一層的任何一個神經元與下一層的所有神經元都有連接)。

大概就是這個樣子的:

img

這裏的箭頭啥的,表示對應的映射。然後這些映射的抉擇用的是:權重、偏置和激活函數 (具體算法怎麼搞定的,我不會算法,大學沒有認真學數學啊 ε=(´ο`*)))唉)

MLP的最經典例子就是數字識別,即我們隨便給出一張上面寫有數字的圖片並作爲輸入,由它最終給出圖片上的數字到底是幾。

在這裏插入圖片描述
在這裏插入圖片描述

視頻請觀看:B站的3Brown1Blue, B站視頻號:av15532370 ,https://www.bilibili.com/video/av15532370/

理論小結:

MLP的處理方式就是,輸入圖片的數據,然後在中間好幾次的隱藏層中依據訓練好的權重偏移等參數,最終映射到結果。

這裏的輸入圖像爲圖像的N xN需要被轉換爲1x(N x N)的數據入參,輸出則爲對應數字(爲訓練集名稱下標)

在接下來,用代碼實現的時候,可以很好的理解這個訓練的出入參數。至於中間的算法過程,那是大神們考慮的╮(╯_╰)╭

理論參考


訓練一個ANN_MLP模型

由於玩的都是人臉識別,所以也想試一試自己訓練個識別來預測特定的人。

雖然使用MLP在數據量不多的情況下,訓練個數字就差不多了。不過代碼是一樣的,訓練集大家可以瞎搞的。

代碼上,有一個嚴重的問題沒有考慮到,就是樣本不規則的情況,這裏使用的每個種類樣本數量必須是一樣的。是使用循環直接來構建數據標籤的。。。後續有空會進行更新調整的 (恩有空的時候 罒ω罒

訓練代碼

public class AnnMlpTest {
    public static void main(String[] args) {
        //加載opencv庫
        System.load("D:\\OpenCV\\opencv\\build\\java\\x64\\opencv_java411.dll");
        //訓練
       training();
        //識別測試
        //annTest(null);
    }

    public static void annTest( Mat img_src){/*使用測試分開說明*/}

   /**
     * 訓練的方法
     * 內容:加載讀取的圖片訓練數據,加載構建的數據標籤,然後調用方法生成模型
     * 搞定,就是這麼簡單的意思 (⊙o⊙)…
     */
    public static void training(){
        //默認訓練集已近是人臉識別之後的的圖像 最終我轉成 40*40 的進行訓練 像素太大參數過多了
        Mat lables = outLables();  // 數據標籤 
        Mat trainData = prepareTrainingData("");  //讀取訓練數據
        //遍歷所有數據集
        //進行訓練
        ANN_MLP mlp  = ANN_MLP.create();
        // 這裏就是把layer 這5個數字參數 轉成Mat類型 作爲訓練的入參
        int[] layer = {40*40,128,128,128,3};
        Mat layerSizes = new Mat(1,layer.length, CvType.CV_32FC1);
        for(int i = 0;i<layer.length;i++){
            layerSizes.put(0,i,layer[i]);
        }
        mlp.setLayerSizes(layerSizes);
        mlp.setActivationFunction(ANN_MLP.SIGMOID_SYM);
        mlp.train(trainData, Ml.ROW_SAMPLE ,lables);
        //訓練並保存訓練後的模型,這個就是結果了
        mlp.save("D:\\ijworkspace\\meaen_test\\data\\mlptest.xml");
    }

    /**
     * 讀取訓練數據
     * Mat(sample_num_perclass*class_num,image_rows*image_cols,CvType.CV_32FC1),
     * sample_num_perclass爲每種類型的圖片數量,class_num爲圖片種類
     */
    public static Mat prepareTrainingData(String path){
        path = "D:\\ijworkspace\\meaen_test\\data\\training_data";
        Mat trainData = new Mat();
        //獲取其file對象
        File file = new File(path);
        //遍歷path下的文件和目錄,放在File數組中
        File[] fs = file.listFiles();
        //遍歷File[]數組 【對應的是 訓練集每一個種類的文件夾】
        for(int i =0;i<fs.length;i++){
            if(fs[i].isDirectory())	{
                //若是目錄(即文件)
                File file2 = new File(path+"\\"+fs[i].getName());
                //遍歷path下的文件和目錄,放在File數組中  
                //【對應訓練接各個類型下的文件  我這裏是每個訓練集57個文件】
                File[] fs2 = file2.listFiles();
                for(int j =0;j<fs2.length;j++){
                    // 此循環內爲 每一個訓練的圖片了
                    Mat srcImage = Imgcodecs.imread(fs2[j].toString());
                    Mat resizeImage = new Mat();
                    Mat trainImage = new Mat();
                    int image_col = 40;
                    int image_rows = 40;
                    // 將圖片大小調整爲40*40 並且轉變圖片的色彩格式 灰色圖
                    Imgproc.resize(srcImage,resizeImage,new Size(image_col,image_rows));
                    Imgproc.cvtColor(resizeImage,trainImage,Imgproc.COLOR_BGR2GRAY);
                    // 接下來這一段就是最麻煩的一段了 處理圖片數據集
                    if(j==0&&i==0) {trainData = new Mat(fs2.length*fs.length,image_rows*image_col,CvType.CV_32FC1);}
                    for(int row = 0;row<image_rows;row++){
                        for(int col = 0;col<image_col;col++){
                            double[] d = trainImage.get(row,col);
                            trainData.put(i*fs2.length+j,row*image_col+col,d);
                        }
                    }
                    // 這一段的意思是:就是構建輸入的數據集合
                    // 已我的數據爲例3個文件,每個文件57個圖片,每個圖片大小爲40*40
                    // 結果就是:一個矩陣 像這個樣子  57*1600 的數據矩陣
                    // ⎡  12,31,32,12,12,2,3,11........................22,12 ⎤  第一個圖片
                    // ⎢  ........這裏的數據爲圖片每一個像素的值.............. ⎥  一共有2*57 行
                    // ⎣  ........一共是有40*40 個像素(數據)................. ⎦  第3*57個圖片
                }
            }
        }
        // 這裏把這個數據矩陣以圖片形式保存下來 查看(圖片每一個像素即使數據)
        Imgcodecs.imwrite("D:\\ijworkspace\\meaen_test\\data\\trainData.jpg", trainData);
        return trainData;
    }

   /**
     * 數據標籤 也是個矩陣 57*3 的矩陣  【與上面的圖片數據矩陣對應的】【每一行的1 表示此行圖片數據屬於哪個分類】
     *  ⎡1,0,0⎤   第1個圖片
     *  ⎢1,0,0⎥	  第2個圖片
     *  ⎣.....⎦   一共有2*57 行
     *  ⎣0,0,1⎦   第3*57個圖片
     * @return
     * 由於寫法問題,這裏需要每個類型的訓練圖片數量一致
     */
    public static Mat outLables(){
        int sample_num_perclass = 57 ;
        int class_num = 3;
        Mat  lables = new Mat(sample_num_perclass*class_num,class_num,CvType.CV_32FC1);
        for(int i = 0 ;i<class_num;i++){
            for(int j = 0 ;j<sample_num_perclass;j++){
                for(int k = 0 ;k<class_num;k++){
                    if(k==i){
                        lables.put(i*sample_num_perclass+j,k,1);
                    }else{
                        lables.put(i*sample_num_perclass+j,k,0);
                    }
                }
            }
        }
        return lables;
    }

}

這裏給出一下,以上代碼使用的入參已經中間的數據集序列成二維矩陣之後的圖片:

在這裏插入圖片描述

這個是訓練用的圖片

在這裏插入圖片描述

這個就是 上面代碼中的輸出的中間處理的訓練數據的矩陣,把一張圖片變成一行的像素了

測試代碼

模型搞出來了,來試一下吧

public static void annTest( Mat img_src){
        ANN_MLP ann = ANN_MLP.load("D:\\ijworkspace\\meaen_test\\data\\mlptest.xml");
        //識別
        if(img_src==null){
            img_src = Imgcodecs.imread("D:\\ijworkspace\\meaen_test\\data\\1332.jpg");
        }
        Mat img_tmp = new Mat();
        Imgproc.resize(img_src,img_tmp,new Size(40,40));
        Imgproc.cvtColor(img_tmp,img_tmp,Imgproc.COLOR_BGR2GRAY);
        //將測試圖像轉化
        Mat sample = new Mat(1,1600,CvType.CV_32FC1);
        for(int row = 0;row<40;row++){
            for(int col = 0;col<40;col++){
                double[] d = img_tmp.get(row,col);
                sample.put(0,row*40+col,d);
            }
        }
        Mat predict = new Mat();
        ann.predict(sample,predict,ANN_MLP.UPDATE_MODEL);
        System.out.println("sample--"+sample.dump());
        System.out.println("outputs--"+predict.dump());
        Core.MinMaxLocResult maxLoc = Core.minMaxLoc(predict);
        System.out.println("zzzzzz  "+maxLoc.maxVal+":"+maxLoc.minVal);
        System.out.println("結果:"+maxLoc.maxLoc.x+"置信度"+maxLoc.maxVal*100+"%");
    }

這裏測試的結果,簡直不能看ε=(´ο`*)))唉

outputs--[1.4029182, -0.40310526, -0.0076343976]
zzzzzz  1.4029182195663452:-0.4031052589416504
結果:0.0置信度140.29182195663452%

先說明一下我的參數:這裏的57張的每個類型的人臉圖片,基本上是隻有10張左右的原始數據,其他是我copy出來的。

導致的結果就是,我用訓練的圖片進行測試識別,要麼是極度匹配,要麼就是不知道匹配到哪國去了 w(゚Д゚)w

比如,訓練集裏面1和2 是兩個眼鏡男生,然後測試的時候,只有有個眼鏡都匹配到1了 ε=(´ο`*)))唉

後續優化代碼

額。。。想想有空的時候再說吧(改成支持隨便數據量的圖片訓練集,各個類型的圖片數量可以不同)

代碼參考


小結一下

使用ANN_MLP神經網絡來訓練自己的模型,對於訓練集的選擇還是要注意纔行的啊(包括數據量,數據類型)

最好還是用數字的圖片來測試,每個數字幾十張應該就可以有不錯的識別率了。

至於我所使用的,直接十幾張的訓練集圖片弄人臉識別,只能是試試水,玩一下的。不要期待準確度的 ε=(´ο`*)))唉

雖說如此,用MLP來了解神經網絡還是很合適的,可以很好的簡單瞭解,訓練的出入參數,和模糊的算法匹配模式。(大佬的可以忽略啊,那不是普通人的級別了


2019-11-07 小杭

一天一篇,好累。。。ψ(*`ー´)ψ


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章