Caffe 的 C++接口学习(一)

Segnet-Caffe C++

学习笔记

从之前的Segnet-Slam代码中设置断点进行调试:

// OpenCV
#include 
#include 
#include 
#include 
#include "opencv2/imgproc/imgproc.hpp"
//...............此处省略若干代码
// Semantic now
cv::Mat new_frame;
cv::resize(frame->rgb, new_frame, cv::Size(480,360));
cv::resize(frame->rgb,frame->rgb,cv::Size(480,360));
std::vector predictions = classifier.Classify(new_frame);
cv::Mat segnet(new_frame.size(), CV_8UC3, cv::Scalar(0,0,0));
/* Return the top N predictions. */
//预测函数,输入一张图片img,希望预测的前N种概率最大的,我们一般取N等于1  
//输入预测结果为std::make_pair,每个对包含这个物体的名字,及其相对于的概率
class Classifier 
{
public:
    Classifier();
    std::vector Classify(const cv::Mat& img, int N = 1);
    //..............省略
};//在.h文件中N已经定义为1
std::vector Classifier::Classify(const cv::Mat& img, int N) 
{
    std::vector<float> output = Predict(img);//将图片给Predict函数
    //【1】Predict函数进行网络的前向传输,得到输入属于每一类的概率,存储在output中
    std::vector<int> maxN = Argmax(output, N);
    std::vector predictions;
    predictions.reserve(input_geometry_.height * input_geometry_.width);
    for (int i = 0; i < input_geometry_.height * input_geometry_.width; ++i)
    {
        int idx = maxN[i];
        predictions.push_back(std::make_pair(labels_[idx], idx));
    }

    return predictions;
}
断点调试时GDB窗口所示图像数据:
img = {const cv::Mat &} {
 flags = {int} 1124024336
 dims = {int} 2 //dims:Mat所代表的矩阵的维度,如3*4的矩阵为2维,3*4*5的为3维
 rows = {int} 360
 cols = {int} 480
 data = {uchar * | 0x7fffafa8e020} "\363\265\203\355\262\........
 //data:Mat对象中的一个指针,指向内存中存放矩阵数据的一块内存 (uchar* data)
 refcount = {int * | 0x7fffafb0c920} 0x7fffafb0c920 //引用计数器
 datastart = {uchar * | 0x7fffafa8e020} "\363\265\203\355\262\.....
 dataend = {uchar * | 0x7fffafb0c920} "\001"
 datalimit = {uchar * | 0x7fffafb0c920} "\001"
 allocator = {cv::MatAllocator * | 0x0} NULL
 size = {cv::Mat::MSize} 
 step = {cv::Mat::MStep} 
 }

Predict()函数

net类数据结构调试 【net类的一些说明】
Net类

//通过合成和自动微分,网络同时定义了一个函数和其对应的梯度。通过合成各层的输出来计算这个函数,来执行给定的任务,
//并通过合成各层的向后传播过程来计算来自损失函数的梯度,从而学习任务。Caffe模型是端到端的机器学习引擎。
//Net是由一系列层组成的有向五环(DAG)计算图,Caffe保留了计算图中所有的中间值以确保前向和反向迭代的准确性。
//一个典型的Net开始于data layer--从磁盘中加载数据,终止于loss layer--计算分类和重构这些任务的目标函数。
//Net由一系列层和它们之间的相互连接构成,用的是一种文本建模语言(protobuf)。
//Net是通过protobuf文件来描述整个Net是怎么由layer组成的。
//Caffe中网络的构建与设备无关。网络构建完之后,通过设置Caffe::mode()函数中的Caffe::set_mode()即可
//实现在CPU或GPU上的运行。CPU与GPU无缝切换并且独立于模型定义。
//前传(forward)过程为给定的待推断的输入计算输出。在前传过程中,Caffe组合每一层的计算以得到整个模型的计算”函数”。
//本过程自底向上进行。
//反传(backward)过程根据损失来计算梯度从而进行学习。在反传过程中,Caffe通过自动求导并反向组合每一层的梯度
//来计算整个网络的梯度。这就是反传过程的本质。本过程自顶向下进行。
//反传过程以损失开始,然后根据输出计算梯度。根据链式准则,逐层计算出模型其余部分的梯度。
//有参数的层,会在反传过程中根据参数计算梯度。
//只要定义好了模型,Caffe中前传和反传的计算就可以立即进行,Caffe已经准备好了前传和反传的实现方法。
////实现方法:
//(1)、Net::Forward()和Net::Backward()方法实现网络的前传和反传,而Layer::Forward()和Layer::Backward()计算每一层的前传和后传。
//(2)、每一层都有forward_{cpu,gpu}()和backward_{cpu,gpu}方法来适应不同的计算模式。由于条件限制
//或者为了使用便利,一个层可能仅实现了CPU或者GPU模式。
//与大多数的机器学习模型一样,在Caffe中,学习是由一个损失函数驱动的(通常也被称为误差、代价或者目标函数)。
//因此,学习的目的是找到一个网络权重的集合,使得损失函数最小。
//在Caffe中,损失是通过网络的前向计算得到的。每一层由一系列的输入blobs(bottom),
//典型的一对多分类任务的损失函数是softMaxWithLoss函数。
//Loss weights:对于含有多个损失层的网络(例如,一个网络使用一个softMaxWithLoss输入分类
//并使用EuclideanLoss层进行重构),损失权值可以被用来指定它们之间的相对重要性。
//按照惯例,有着Loss后缀的Caffe层对损失函数有贡献,其它层被假定仅仅用于中间计算。
//对于带后缀Loss的层来说,其对于该层的第一个top blob含有一个隐式的loss_weight:1;其它层对应于所有top blob有一个隐式的loss_weight: 0。
//然而,任何可以反向传播的层,可允许给予一个非0的loss_weight,例如,如果需要,
//对网络的某些中间层所产生的激活进行正则化。对于具有相关非0损失的非单输出,损失函数可以通过对所有blob求和来进行简单地计算。
//那么,在Caffe中最终的损失函数可以通过对整个网络中所有的权值损失进行求和计算获得。
//为了创建一个Caffe模型,需要在一个protobuf(.prototxt)文件中定义模型的结构。
//在Caffe中,层和相应的参数都定义在caffe.proto文件里。

深度网络是组成模型,自然地表示为在大量数据上工作的互连层集合。 Caffe在其自己的模型模式中定义了一个网络层。 网络将整个模型从输入数据自下而上地定义为损失。 随着数据和派生物在前向和后向流经网络,Caffe存储,传达和操纵信息为blobblobCaffe框架的标准阵列和统一内存接口。 该层次是模型和计算的基础。 网络作为图层的收集和连接。 blob的详细信息描述了信息在层和网络中如何存储和传输。


Blob存储和通信

Blob是Caffe处理和传递的实际数据的封装,并且还提供CPU和GPU之间的同步功能。 在数学上,Blob是以C语言风格的连续方式存储的N维数组。
Caffe使用Blob存储和传输数据。 Blob提供了保存数据的统一的存储器接口; 例如批量的图像,模型参数以及用于优化的衍生物。
Blob通过根据需要从CPU主机同步到GPU设备来隐藏混合CPU / GPU操作的计算和心理开销。 主机和设备上的内存按需(懒散地)分配以提高内存使用率。
批量图像数据的常规Blob尺寸是

数量N×通道K×高度H×宽度W

Blob在布局中主要以行进行存储,所以最后/最右维度变化最快。 例如,在4D blob中,index(n,k,h,w)处的值在物理上位于索引

((n * K + k)* H + h)* W + w

数量N是数据的批量大小。 批处理为通信和设备处理实现更好的吞吐量。 对于256个图像的N = 256的ImageNet训练批次。
通道K是特征维度,例如对于RGB图像K = 3


Layer(层)计算和通信

Layer是模型的本质和计算的基本单位。

net_ = {boost::shared_ptr}  //net类:一个Net由多个Layer组成。一个典型的网络
                                       //从data layer(从磁盘中载入数据)出发到loss layer结束。
  name_ = {std::string} "VGG_ILSVRC_16_layer"//网络的名称,从.prototxt文件中载入
  phase_ = {caffe::Phase} caffe::TEST
  layer_names_ =  //layer是Caffe的基本计算单元
   [0] = {std::basic_string<char, std::char_traits, std::allocator>} "input"   
   [1] =  "conv1_1"
   [2] =  "conv1_1_bn"
//..................省略
   [86] = "conv1_1_D"
   [87] = "argmax"
//网络中大部分功能都是以Layer的形式去展开的,如convolute,pooling,loss等等。
//在创建一个Caffe模型的时候,也是以Layer为基础进行的,需按照src/caffe/proto/caffe.proto中定义的网络及参数格式定义网络的.prototxt文件
  blobs_ = {std::vectorshared_ptr, std::allocator>} 
   [0] = {boost::shared_ptr} 
    px = {boost::shared_ptr::element_type * | 0x8d4fb80} 0x8d4fb80
     data_ = {boost::shared_ptr} 
     diff_ = {boost::shared_ptr} 
     shape_data_ = {boost::shared_ptr} 
     shape_ = {std::vector<int, std::allocator>} 
     count_ = {int} 518400
     capacity_ = {int} 518400
    pn = {boost::detail::shared_count} 
     pi_ = {boost::detail::sp_counted_base * | 0x8d4fbe0} 0x8d4fbe0
   [1] = {boost::shared_ptr} 
  blob_names_ = {std::vector<std::basic_string, std::allocator>} 
   [0] = {std::basic_string<char, std::char_traits, std::allocator>} "data"
   [1] = {std::basic_string<char, std::char_traits, std::allocator>} "conv1_1"
   [2] = {std::basic_string<char, std::char_traits, std::allocator>} "conv1_2"
   [42] = {std::basic_string<char, std::char_traits, std::allocator>} "argmax"
   [41] = {std::basic_string<char, std::char_traits, std::allocator>} "conv1_1_D"
  net_input_blobs_ = {std::vectorstd::allocator>} 
   [0] = {caffe::Blob<float> * | 0x8d4fb80} 0x8d4fb80
    data_ = {boost::shared_ptr} 
     px = {boost::shared_ptr::element_type * | 0x9230300} 0x9230300
      cpu_ptr_ = {void * | 0x0} NULL
      gpu_ptr_ = {void * | 0x0} NULL
      size_ = {size_t} 2073600
      head_ = {caffe::SyncedMemory::SyncedHead} caffe::SyncedMemory::UNINITIALIZED
      own_cpu_data_ = {bool} false
      cpu_malloc_use_cuda_ = {bool} false
      own_gpu_data_ = {bool} false
      gpu_device_ = {int} -1
     pn = {boost::detail::shared_count} 
      pi_ = {boost::detail::sp_counted_base * | 0x922f950} 0x922f950
       use_count_ = {int} 1
       weak_count_ = {int} 1
    diff_ = {boost::shared_ptr} 
     px = {boost::shared_ptr::element_type * | 0x9230240} 0x9230240
      cpu_ptr_ = {void * | 0x0} NULL
      gpu_ptr_ = {void * | 0x0} NULL
      size_ = {size_t} 2073600
      head_ = {caffe::SyncedMemory::SyncedHead} caffe::SyncedMemory::UNINITIALIZED
      own_cpu_data_ = {bool} false
      cpu_malloc_use_cuda_ = {bool} false
      own_gpu_data_ = {bool} false
      gpu_device_ = {int} -1
     pn = {boost::detail::shared_count} 
      pi_ = {boost::detail::sp_counted_base * | 0x8d5c240} 0x8d5c240
       use_count_ = {int} 1
       weak_count_ = {int} 1
    shape_data_ = {boost::shared_ptr} 
     pn = {boost::detail::shared_count} 
    shape_ = {std::vector<int, std::allocator>} 
     [0] = {int} 1
     [1] = {int} 3
     [2] = {int} 360
     [3] = {int} 480
    count_ = {int} 518400
    capacity_ = {int} 518400
std::vector<float> Classifier::Predict(const cv::Mat& img) 
{
    Blob<float>* input_layer = net_->input_blobs()[0];
    input_layer->Reshape(1, num_channels_, input_geometry_.height, input_geometry_.width);
    /* Forward dimension change to all layers. */
    net_->Reshape();

    std::vector input_channels;
    WrapInputLayer(&input_channels);

    Preprocess(img, &input_channels);

    net_->ForwardPrefilled();

    /* Copy the output layer to a std::vector */
    Blob<float>* output_layer = net_->output_blobs()[0];

    const float* begin = output_layer->cpu_data();
    const float* end = begin + output_layer->height() * output_layer->width() * output_layer->channels();

    return std::vector<float>(begin, end);
}

你可能感兴趣的:(learning,journal)