1. 程式人生 > >如何利用caffe訓練自己資料集

如何利用caffe訓練自己資料集

這篇博文主要參考了另一位博主https://blog.csdn.net/hellohaibo,在此向他表示感謝

首先,博主今天的caffe崩了,毫無徵兆的崩了,具體表現為博主想做一個更大的資料集,但是在生成lmbd檔案時永遠生成的是一個沒有名字的資料夾,可是博主已經在指定的example目錄裡寫了檔名,百度,沒有答案,初步推測caffe崩了。。。

然後重新安裝,真的坑,以前沒有的報錯都出現了,網上尋求外國友人幫助。。。真的難受這玩意。。。

重新安裝好後,就可以訓練自己的資料集了,前一章講過如何製作自己的資料集,這章就忽略了,我們來講均值檔案的生成,很簡單,去caffe裡尋找一個make_imagenet_mean.sh的指令碼檔案,然後放入自己測試的目錄,博主的目錄如下:

修改指令碼檔案如下

#!/usr/bin/env sh
# Compute the mean image from the imagenet training lmdb
# N.B. this is available in data/ilsvrc12

EXAMPLE=/home/f/Downloads/test
DATA=/home/f/Downloads/test
TOOLS=/home/f/Downloads/caffe/build/tools

$TOOLS/compute_image_mean $EXAMPLE/train_lmdb \
  $DATA/train_mean.binaryproto

echo "Done."

然後終端執行該檔案,這樣就會產生均值檔案了。

將caffe/model下隨便拷貝一個檔案到我們的test目錄,修改以下引數,首先對於train_val的prototxt檔案,將一開始的兩層修改為

name: "CaffeNet"
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 227
    mean_file: "/home/f/Downloads/test/train_mean.binaryproto"
  }
# mean pixel / channel-wise mean instead of mean image
#  transform_param {
#    crop_size: 227
#    mean_value: 104
#    mean_value: 117
#    mean_value: 123
#    mirror: true
#  }
  data_param {
    source: "/home/f/Downloads/test/train_lmdb"
    batch_size: 10
    backend: LMDB
  }
}
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mirror: false
    crop_size: 227
    mean_file: "/home/f/Downloads/test/train_mean.binaryproto"
  }
# mean pixel / channel-wise mean instead of mean image
#  transform_param {
#    crop_size: 227
#    mean_value: 104
#    mean_value: 117
#    mean_value: 123
#    mirror: false
#  }
  data_param {
    source: "/home/f/Downloads/test/val_lmdb"
    batch_size: 5
    backend: LMDB
  }
}

這裡面是對source的路徑修改,它為lmdb的檔案所在路徑,mean file為均值檔案所在路徑

batchsize根據你的數量集來,博主由於只有幾百張圖片,所以size設定得很小

然後修該該檔案的最後輸出層

inner_product_param {
    num_output: 3
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "fc8"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "fc8"
  bottom: "label"
  top: "loss"
}

將max——output改為你的分類數

對於deploy的prototxt檔案,只有修改最後一層的輸出,同上。

layer {
  name: "fc8"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  inner_product_param {
    num_output: 3
  }
}
layer {
  name: "prob"
  type: "Softmax"
  bottom: "fc8"
  top: "prob"
}

然後修改solve檔案,我的形式為

net: "/home/f/Downloads/test/make_model/train_val.prototxt"
test_iter: 24
test_interval: 50
base_lr: 0.001
lr_policy: "step"
gamma: 0.1
stepsize: 100
display: 20
max_iter: 500
momentum: 0.9
weight_decay: 0.0005
snapshot: 50
snapshot_prefix: "/home/f/Downloads/test/final_model"
solver_mode: CPU

然後cd出來執行命令

./caffe/build/tools/caffe train   --solver=/home/f/Downloads/test/make_model/solver.prototxt  

然後就可以愉快地等待了。。。

注意,這裡的目錄最好都是絕對目錄,caffe經常會報目錄的問題,所以一定要細心。。。

注:如果訓練量太大我們想中斷並恢復,可以執行如下的命令,從某一snapshot開始:

    ./caffe/buiid/tools/caffe train 
    --solver=test/make_model/solver.prototxt  
    --snapshot=test/final_model/cifar10_full_iter_100.solverstate  

可以通過caffe的介面進行測試:

./caffe/build/examples/cpp_classification/classification.bin /home/f/Downloads/test/make_model/deploy.prototxt /home/f/Downloads/test/final_model/solver_iter_500.caffemodel /home/f/Downloads/test/train_mean.binaryproto /home/f/Downloads/test/label.txt /home/f/Downloads/test/val/101.jpg

輸出結果:

接著我們在opencv上測試

將deploy檔案和caffemodel檔案和我們自己做的標籤檔案放入測試程式目錄中,修改deploy的第一層為

    name: "CaffeNet"  
    input: "data"  
    input_dim: 10   
    input_dim: 3   
    input_dim: 227   
    input_dim: 227  

測試程式為

 #include <opencv2/dnn.hpp>  
    #include <opencv2/imgproc.hpp>  
    #include <opencv2/highgui.hpp>  
    #include <opencv2/core/utils/trace.hpp>  
    using namespace cv;  
    using namespace cv::dnn;  
      
    #include <fstream>  
    #include <iostream>  
    #include <cstdlib>  
    using namespace std;  
      
    //尋找出概率最高的一類  
    static void getMaxClass(const Mat &probBlob, int *classId, double *classProb)  
    {  
        Mat probMat = probBlob.reshape(1, 1);  
        Point classNumber;  
      
        minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);  
        *classId = classNumber.x;  
    }  
    //從標籤檔案讀取分類 空格為標誌  
    static std::vector<String> readClassNames(const char *filename = "label.txt")  
    {  
        std::vector<String> classNames;  
      
        std::ifstream fp(filename);  
        if (!fp.is_open())  
        {  
            std::cerr << "File with classes labels not found: " << filename << std::endl;  
            exit(-1);  
        }  
      
        std::string name;  
        while (!fp.eof())  
        {  
            std::getline(fp, name);  
            if (name.length())  
                classNames.push_back(name.substr(name.find(' ') + 1));  
        }  
        fp.close();  
        return classNames;  
    }  
    //主程式  
    int main(int argc, char **argv)  
    {  
        //初始化  
        CV_TRACE_FUNCTION();  
        //讀取模型引數和模型結構檔案  
        String modelTxt = "deploy.prototxt";  
        String modelBin = "caffenet.caffemodel";  
        //讀取圖片  
        String imageFile = (argc > 1) ? argv[1] : "1.jpg";  
      
        //合成網路  
        Net net = dnn::readNetFromCaffe(modelTxt, modelBin);  
        //判斷網路是否生成成功  
        if (net.empty())  
        {  
            std::cerr << "Can't load network by using the following files: " << std::endl;  
            exit(-1);  
        }  
        cerr << "net read successfully" << endl;  
      
        //讀取圖片  
        Mat img = imread(imageFile);  
        imshow("image", img);  
        if (img.empty())  
        {  
            std::cerr << "Can't read image from the file: " << imageFile << std::endl;  
            exit(-1);  
        }  
        cerr << "image read sucessfully" << endl;  
      
    /*  Mat inputBlob = blobFromImage(img, 1, Size(224, 224), 
                                        Scalar(104, 117, 123)); */   
      
        //構造blob,為傳入網路做準備,圖片不能直接進入網路  
        Mat inputBlob = blobFromImage(img, 1, Size(227, 227));  
          
        Mat prob;  
        cv::TickMeter t;  
        for (int i = 0; i < 10; i++)  
        {  
            CV_TRACE_REGION("forward");  
            //將構建的blob傳入網路data層  
            net.setInput(inputBlob,"data");   
            //計時  
            t.start();  
            //前向預測  
            prob = net.forward("prob");    
            //停止計時  
            t.stop();  
        }  
      
        int classId;  
        double classProb;  
        //找出最高的概率ID儲存在classId,對應的標籤在classProb中  
        getMaxClass(prob, &classId, &classProb);  
      
        //打印出結果  
        std::vector<String> classNames = readClassNames();  
        std::cout << "Best class: #" << classId << " '" << classNames.at(classId) << "'" << std::endl;  
        std::cout << "Probability: " << classProb * 100 << "%" << std::endl;  
        //打印出花費時間  
        std::cout << "Time: " << (double)t.getTimeMilli() / t.getCounter() << " ms (average from " << t.getCounter() << " iterations)" << std::endl;  
      
        //便於觀察結果  
        waitKey(0);  
        return 0;  
    }   

然後觀察結果

我由於實驗的資料很不好,導致結果出現了問題,我放了貓,結果是狗。。。。但至少證明了實驗可以成功,也說明了資料集的重要。。。