1. 程式人生 > >caffe初探4:對訓練得到的模型進行測試

caffe初探4:對訓練得到的模型進行測試

請各位讀者朋友們注意,鑑於大家比較關心測試caffe訓練完畢的模型的步驟,筆者特地將在c++程式中呼叫caffe訓練完畢的模型與classification.cpp解析整理成了完整的部落格:

classification.cpp解析與使用cmake編譯classification詳見:在c++程式中呼叫caffe訓練完畢的模型進行分類

下面是之前的原文:

續caffe初探1,2,3,在我們訓練出自己的模型之後,就可以測試,或者說使用我們的模型來分類了,在我們使用網路模型對單張圖片進行分類測試之前筆者還是列出測試所需物資清單:

茲測試所需物資清單如下:

(1)類名檔案,未準備。標定分類名稱的txt檔案。


(2)測試圖片,未準備。準備若干張供網路模型分類的圖片。

(3)字尾名稱為.caffemodel的網路模型檔案,已準備。筆者已經在caffe初探3中生成了若干網路模型檔案,在這裡我們可以選擇迭代10000次的模型檔案,裡面包含了網路引數。

(4)網路架構說明檔案,未準備。網路架構說明檔案是說明使用網路時的網路架構,該檔案與我們訓練模型時使用的./caffe/forkrecognition/train_val.prototxt檔案類似,後續會詳細說明。

(5)均值檔案,已準備。在這裡我們就使用在caffe初探2-3中訓練模型時使用的均值檔案。

(6)分類器,未準備。用來呼叫以上5類資源的檔案。

一目瞭然,我們還有四類檔案沒有準備,那麼,下面筆者就闡述一下怎麼準備這四樣東西。

1、有關類名檔案的準備

類名檔案為txt格式檔案,作用是規定分類號對應的類名稱,格式為:分類號:類名;一行對應一個類。因為我們是用來分類包含岔路口的圖片和不包含岔路口的圖片,因此就兩類。在./caffe/forkrecognition/目錄下面建立words.txt檔案,檔案內容如下所示。


2、測試圖片的準備

因為筆者訓練的模型是用來判斷圖片有沒有岔路口,因此筆者準備了7張包含岔路口的圖片和7張不包含岔路口的普通道路圖片,並把他們尺寸除錯成227*227的,放在./caffe/forkrecognition/test_images/目錄下面,如下圖所示。


3、網路架構說明檔案

在我們使用分類器結合訓練出的模型分類圖片時,同樣需要指定網路架構,有看過筆者前面的文章“caffe初探2:有關網路設計的探索”的讀者朋友應該記得,筆者在該文章中特意提到了我們在訓練模型時設計的網路結構中四個特殊的層,是訓練模型時特別需要的。而我們在使用模型時使用的網路架構與訓練模型時使用的網路架構的不同之處主要就在於這四個層。我們複製訓練時使用的網路架構檔案./caffe/forkrecognition/train_val.prototxt檔案到./caffe/forkrecognition/目錄下,新建名為deploy.prototxt的檔案,檔案內容如下所示。

name: "AlexNet"
layer {
  name: "forkdata"
  type: "Input"
  top: "data"
  input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }#我們只輸入一張圖片,因此第一個引數(批次)為1,第二個引數表示圖片為三通道的,第三地四個引數表示圖片的width與height。
}#之後部分和訓練網路時使用的./caffe/forkrecognition/train_val.prototxt檔案幾乎一樣,只是去掉了卷積層,全連線層的引數設定,詳見程式碼
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 96
    kernel_size: 11
    stride: 4
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}
layer {
  name: "norm1"
  type: "LRN"
  bottom: "conv1"
  top: "norm1"
  lrn_param {
    local_size: 5
    alpha: 0.0001
    beta: 0.75
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "norm1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 256
    pad: 2
    kernel_size: 5
    group: 2
  }
}
layer {
  name: "relu2"
  type: "ReLU"
  bottom: "conv2"
  top: "conv2"
}
layer {
  name: "norm2"
  type: "LRN"
  bottom: "conv2"
  top: "norm2"
  lrn_param {
    local_size: 5
    alpha: 0.0001
    beta: 0.75
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "norm2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}
layer {
  name: "conv3"
  type: "Convolution"
  bottom: "pool2"
  top: "conv3"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 384
    pad: 1
    kernel_size: 3
  }
}
layer {
  name: "relu3"
  type: "ReLU"
  bottom: "conv3"
  top: "conv3"
}
layer {
  name: "conv4"
  type: "Convolution"
  bottom: "conv3"
  top: "conv4"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 384
    pad: 1
    kernel_size: 3
    group: 2
  }
}
layer {
  name: "relu4"
  type: "ReLU"
  bottom: "conv4"
  top: "conv4"
}
layer {
  name: "conv5"
  type: "Convolution"
  bottom: "conv4"
  top: "conv5"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 256
    pad: 1
    kernel_size: 3
    group: 2
  }
}
layer {
  name: "relu5"
  type: "ReLU"
  bottom: "conv5"
  top: "conv5"
}
layer {
  name: "pool5"
  type: "Pooling"
  bottom: "conv5"
  top: "pool5"
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}
layer {
  name: "fc6"
  type: "InnerProduct"
  bottom: "pool5"
  top: "fc6"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 4096
  }
}
layer {
  name: "relu6"
  type: "ReLU"
  bottom: "fc6"
  top: "fc6"
}
layer {
  name: "drop6"
  type: "Dropout"
  bottom: "fc6"
  top: "fc6"
  dropout_param {
    dropout_ratio: 0.5
  }
}
layer {
  name: "fc7"
  type: "InnerProduct"
  bottom: "fc6"
  top: "fc7"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 4096
  }
}
layer {
  name: "relu7"
  type: "ReLU"
  bottom: "fc7"
  top: "fc7"
}
layer {
  name: "drop7"
  type: "Dropout"
  bottom: "fc7"
  top: "fc7"
  dropout_param {
    dropout_ratio: 0.5
  }
}
layer {
  name: "forkfc8"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 2
  }
}#之前的部分和訓練網路時使用的./caffe/forkrecognition/train_val.prototxt檔案幾乎一樣,只是去掉了卷積層,全連線層的引數設定
layer {  #在使用訓練好的模型時,不需要準確率層與損失層了,需要的是概率層,評估圖片位於各個分類的概率。
  name: "prob"
  type: "Softmax"
  bottom: "fc8"
  top: "prob"
}


4、分類器

現在萬事俱備,只欠東風,我們只需要構造分類器就能使用模型分類圖片了。我們可以使用官方提供的分類器分類圖片,官方提供了兩種分類器介面,一種是c++介面,一種是python介面;在這裡我們使用c++介面的分類器來分類圖片。點開目錄./caffe/examples/cpp_classification/目錄,可以看到其中有兩個檔案,如下圖所示。


其中classification.cpp就是分類器檔案。讓我們點開它旁邊的readme.md檔案檢視它的用法,readme.md檔案內容如下所示。

---
title: CaffeNet C++ Classification example
description: A simple example performing image classification using the low-level C++ API.
category: example
include_in_docs: true
priority: 10
---

# Classifying ImageNet: using the C++ API

Caffe, at its core, is written in C++. It is possible to use the C++
API of Caffe to implement an image classification application similar
to the Python code presented in one of the Notebook example. To look
at a more general-purpose example of the Caffe C++ API, you should
study the source code of the command line tool `caffe` in `tools/caffe.cpp`.

## Presentation

A simple C++ code is proposed in
`examples/cpp_classification/classification.cpp`. For the sake of
simplicity, this example does not support oversampling of a single
sample nor batching of multiple independant samples. This example is
not trying to reach the maximum possible classification throughput on
a system, but special care was given to avoid unnecessary
pessimization while keeping the code readable.

## Compiling

The C++ example is built automatically when compiling Caffe. To
compile Caffe you should follow the documented instructions. The
classification example will be built as `examples/classification.bin`   #classfication.cpp已經編譯好了,生成了classification.bin檔案
in your build directory.

## Usage

To use the pre-trained CaffeNet model with the classification example,
you need to download it from the "Model Zoo" using the following
script:
```
./scripts/download_model_binary.py models/bvlc_reference_caffenet
```
The ImageNet labels file (also called the *synset file*) is also
required in order to map a prediction to the name of the class:
```
./data/ilsvrc12/get_ilsvrc_aux.sh
```
Using the files that were downloaded, we can classify the provided cat
image (`examples/images/cat.jpg`) using this command:
```                                               
./build/examples/cpp_classification/classification.bin \              #在這裡規定了呼叫分類器的方法
  models/bvlc_reference_caffenet/deploy.prototxt \
  models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel \
  data/ilsvrc12/imagenet_mean.binaryproto \
  data/ilsvrc12/synset_words.txt \
  examples/images/cat.jpg
```
The output should look like this:
```
---------- Prediction for examples/images/cat.jpg ----------
0.3134 - "n02123045 tabby, tabby cat"
0.2380 - "n02123159 tiger cat"
0.1235 - "n02124075 Egyptian cat"
0.1003 - "n02119022 red fox, Vulpes vulpes"
0.0715 - "n02127052 lynx, catamount"
```

## Improving Performance

To further improve performance, you will need to leverage the GPU
more, here are some guidelines:

* Move the data on the GPU early and perform all preprocessing
operations there.
* If you have many images to classify simultaneously, you should use
batching (independent images are classified in a single forward pass).
* Use multiple classification threads to ensure the GPU is always fully
utilized and not waiting for an I/O blocked CPU thread.


可見檔案中詳細闡述了分類器的用法,呼叫./caffe/build/examples/cpp_classification/檔案目錄下的classification.bin檔案並傳入五個引數:網路架構檔案deploy.prototxt,訓練生成的.caffemodel檔案,均值檔案,類名檔案,測試圖片。現在我們就利用此規範來測試某一張圖片,如下圖。



可見分類結果了,圖中包含岔路口的概率為0.9627不包含岔路口的概率為0.0373。

大功告成!遺憾的是,筆者在整個訓練及分類的過程中,由於資料集過小,使得結果的魯棒性一般,有時會出錯。

到這裡為止,經過四期caffe初探,筆者已經完整地講述了一遍caffe的分類用法及操作過程,也很希望各位讀者朋友也能去了解與使用這一款強大的深度學習工具;同時,作為一個初涉機器學習領域的菜鳥,筆者也非常期待與各位讀者朋友一起學習與交流,一起探索機器學習無邊無際的知識海洋!

筆者在寫作部落格的時候,肯定會有一些謬誤,萬望各位讀者朋友海涵,並誠懇地期待各位讀者朋友能指出其中的不足,在此筆者表示最衷心的感謝。

讀萬卷書,行萬里路,在向終點馳騁的途中,千萬不要忽略一路上瑰麗的風景,祝各位讀者朋友學業工作無往而不利!

written by jiong

實踐是檢驗真理的唯一標準!