caffe初探4:对训练得到的模型进行测试

  • Post author:
  • Post category:其他




请各位读者朋友们注意,

鉴于大家比较关心测试caffe训练完毕的模型的步骤

,笔者特地将在c++程序中调用caffe训练完毕的模型与classification.cpp解析整理成了完整的博客:




classification.cpp解析与使用cmake编译classification详见:


在c++程序中调用caffe训练完毕的模型进行分类





工程化classification,将检测接口用cmake编译为.so文件并在程序中调用详见:


caffe工程化实例:将caffe分类程序编译成.so文件并在程序中调用





下面是之前的原文:


续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


实践是检验真理的唯一标准!



版权声明:本文为jiongnima原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。