caffe阅读记录(2)

[TOC]

layer类

  • 顾名思义这里的layer即神经网络中的每一”层”, layer和layer之间通过blob类传输数据连接起来,
    从上一层(bottom)得到数据(input),计算输出,称为前向传播(forward), 当然, 这里的具体forward function需要自己定义
    同理也有反向传播(backward)

具体解析

类定义

  • 和 Blob类似

成员变量

boost::mutex 是 boost中的互斥锁

构造函数和虚构函数

  • 在初始化列表初始化layer_param_, 之后设置Phase(当前网络是train还是test), 再将layer_param_中的Blob拷贝到blobs_中

SetUp函数

  • 这里首先初始化互斥锁(如果需要的话), 然后检查这个bottom和top的blob(个数)是否正确,再调用Layersetup对每一具体的层做进一步设置,之后再做reshape来设置top blobs和internal buffer。最后再设置loss weight multiplier 的blob对每一个非零的loss和weight,一般这个方法被继承之后是不会被重写的

其他函数

  • ShareInParallel, IsShared用于获取并行状态和这一layer是否被多个nets共享,默认情况下,除了data_layer都是关闭的。
  • SetShared似乎只能用于设置为不能共享

  • Reshape为纯虚函数, 用于调节top和internal buffer

Forward 和 Backward

  • Forward.这其实是一个装饰器,继承之后在调用的调用其相应的forward_cpu或者forward_gpu,根据输入的input data blob计算相应的output data blob,同时会返回这一层layer的total loss.
  • BackWard,实现的是反向传播,也就是给定top blob的error gradient 计算得到bottom的error gradient。其输入时 output blobs ,在Ouput blobs里面的diff存储的就是其相应的error gradients。其中propagate_down这个参数跟Bottom的长度是一样的,每一个Index用来指定是否需要反向传播error gradients 到对应的bottom blob。而bottom 这里面的diff 区域存放的就是BackWard计算出来相应的gradient error.

  • 可以看出Layer作为一个基类,其中有很多虚函数,其主要用于被具体的『layer』继承,当我们需要自定义一个Layer时,只需要填充setUp, Forward, Backward等就行了。

data layer

DataTransformer类

  • DataTransformer类主要负责对数据进行预处理, 比如减去均值、进行crop,镜像,强制设置为彩色强制设置为灰度图像以及像素值的缩放,此外该类还将Datum、const vector、cv::Mat&、vector 、Blob*类型的数据变换到目标大小的blob。负责对上述类型的数据推断其shape。

  • caffe.pb.h是由protobuf生成的, 其中的类定义可以直接看caffe.proto

类定义和成员变量

caffe.proto一些用到的类

  • TransformationParameter

  • BlobProto

  • Dantum

成员函数及实现

  • caffe_sub 目的是矩阵相减 y = a – b;

BaseDataLayer

  • LayerParameter

  • BaseDataLayer继承自Layer

类定义及成员变量和成员函数

Batch 类

  • Batch类可以看做对data和label的简单封装

BasePrefetchingDataLayer类

  • BasePrefetchingDataLayer继承自BaseDataLayer和InternalThread

类定义及成员变量和成员函数

ImageDataLayer

  • 根据继承关系, 这里仅以ImageDataLayer为例

类定义及成员变量和成员函数

其他一些Layer

  • 这里就不贴具体实现了

激励层(NeuronLayer)

  • 一般来说,激励层是element-wise的操作,输入和输出的大小相同,一般情况下就是一个非线性函数。
  • 输入
    • n * c * h * w
  • 输出
    • n * c * h * w

ReLU / Rectified-Linear and Leaky-ReLU

  • 定义示例

  • 可选参数
    • negative_slope [default 0]: 指定输入值小于零时的输出。
  • ReLU是目前使用做多的激励函数,主要因为其收敛更快,并且能保持同样效果。标准的ReLU函数为max(x, 0),而一般为当x > 0时输出x,但x <= 0时输出negative_slope。RELU层支持in-place计算,这意味着bottom的输出和输入相同以避免内存的消耗。

  • ReLU(x)=max{0,x}

Sigmoid

  • 定义示例

  • σ(x)=11+exp−x

TanH / Hyperbolic Tangent

  • 定义示例

  • tanh(x)=expx−exp−xexpx+exp−x

ConvolutionLayer

  • 输入
    • n * ci * hi * wi
  • 输出
    • n * co * ho * wo
    • ho=(hi+2×padh−kernelh)/strideh+1
  • 定义示例

  • 必要参数
    • num_output (c_o):过滤器的个数
    • kernel_size (or kernel_h and kernel_w):过滤器的大小(也就是所谓“核”的大小)
  • 可选参数
    • bias_filler:偏置的初始化方法
    • bias_term [default true]:指定是否是否开启偏置项
    • pad (or pad_h and pad_w) [default 0]:指定在输入的每一边加上多少个像素
    • stride (or stride_h and stride_w) [default 1]:指定过滤器的步长
    • group (g) [default 1]: 如果g>1,那么将每个滤波器都限定只与某个输入的子集有关联。换句话说,将输入分为g组,同时将输出也分为g组。那么第i组输出只与第i组输入有关

PoolingLayer

  • 输入
    • n * ci * hi * wi
  • 输出
    • n * co * ho * wo
    • ho=(hi+2×padh−kernelh)/strideh+1
  • 定义示例

  • 必要参数
    • kernel_size (or kernel_h and kernel_w):过滤器的大小
  • 可选参数
    • pool [default MAX]:pooling的方法,目前有MAX, AVE, 和STOCHASTIC三种方法
    • pad (or pad_h and pad_w) [default 0]:指定在输入的每一遍加上多少个像素
    • stride (or stride_h and stride_w) [default 1]:指定过滤器的步长

LRNLayer

LossLayer

InnerProductLayer

  • 输入
    • n * ci * hi * wi
  • 输出
    • n * co * 1 * 1
  • 定义示例

  • 更多Layer请参考官方文档

layer_factory 及 工厂模式

  • layer_factory采用工厂模式
  • Layer_factory的主要作用是负责Layer的注册,已经注册完事的Layer在运行时可以通过传递一个 LayerParameter 给 CreaterLayer 函数的方式来调用

LayerResistry

LayerRegisterer

以convolution layer为例

Net类

参考资料: http://blog.csdn.net/qq_16055159/article/details/45057297

  • Net类是Solve类的一个成员,在net.cpp中定义了对Net的所有操作,其中包括:
    • Init
    • GetLearningRateAndWeightDecay
    • ForwardPrefilled
    • Backward
    • ShareTrainedLayersWith
    • CopyTrainedLayersFrom
    • ToProto
    • Update
    • has_blob
    • blob_by_name
    • has_layer
    • layer_by_name
  • Net(const NetParameter& param)
    • 功能: 调用Init初始化网络
  • Net(const string& param_file)
    • 功能同上
  • Init(const NetParameter& in_param)
    • 功能: 初始化网络
  • GetLearningRateAndWeightDecay()
    • 功能:收集学习速率和权重衰减,即更新params_、params_lr_和params_weight_decay_
  • ForwardPrefilled(Dtype* loss)
    • 功能:前馈预先填满,即预先进行一次前馈
  • Forward(const vector<Blob> & bottom, Dtype loss)
    • 功能:把网络输入层的blob读到net_input_blobs_,然后进行前馈,计算出loss
    • 输入:整个网络输入层的blob
    • 输出:整个网络输出层的blob
  • Forward(const string& input_blob_protos, Dtype* loss)
    • 功能:Forward的重载,只是输入层的blob以string的格式传入
  • Backward()
    • 功能:对整个网络进行反向传播
  • ShareTrainedLayersWith(Net* other)
    • 功能:从Other网络复制某些层
  • CopyTrainedLayersFrom(const NetParameter& param)
    • 功能:和ShareTrainedLayersWith一样
  • CopyTrainedLayersFrom(const string trained_filename)
    • 功能:从文件中读入NetParameter param,然后调用CopyTrainedLayersFrom()
  • ToProto(NetParameter* param, bool write_diff)
    • 功能:把网络的参数存入prototxt中
  • Update()
    • 功能:更新params_中blob的值
  • has_blob(const string& blob_name)
    • 功能:判断是否存在名字为blob_name的blob
  • blob_by_name(const string& blob_name)
    • 功能:给一个blob的名字,返回这个blob的指针
  • has_layer(const string& layer_name)
    • 功能:判断是否存在名字为layer_name的layer
  • layer_by_name(const string& layer_name)
    • 功能:给一个layer的名字,返回这个layer的指针

Solver类

参考资料: http://blog.csdn.net/qq_16055159/article/details/45068147

  • Solver::Solver(const SolverParameter& param)
    • 功能:构造函数
    • 步骤:初始化两个Net类,net_和test_net_,并调用Init()函数
    • 输入:SolverParameter类型的param
    • 输出:无
  • Solver::Solver(const string& param_file)
    • 功能:构造函数
    • 步骤:初始化两个Net类,net_和test_net_,并调用Init()函数
    • 输入:string类型的param_file
    • 输出:无
  • void Solver::Init(const SolverParameter& param)
    • 功能:初始化网络
    • 步骤:
      1. 设置随机数种子
      2. 申请一块Net空间以下面的构造函数进行初始化param_file=train_net_,net_指向这块空间
      3. 如果有test_net,则申请一块Net空间,test_net_指向这块空间
    • 输入:SolverParameter类型的param
    • 输出:无
  • void Solver::Solve(const char* resume_file)
    • 功能:训练网络
    • 步骤:
      1. 设置Caffe的mode(GPU还是CPU)
      2. 如果是GPU且有GPU芯片的ID,则设置GPU
      3. 设置当前阶段(TRAIN还是TEST/TRAIN)
      4. 调用PreSolve函数:PreSolve()
      5. 调用Restore函数:Restore(resume_file)
      6. 调用一遍Test(),判断内存是否够
      7. 对于每一次训练时的迭代(遍历整个网络):while (iter_++ < param_.max_iter()):
        1. 计算loss:loss = net_->ForwardBackward(bottom_vec)
        2. 调用ComputeUpdateValue函数:ComputeUpdateValue()
        3. 输出loss
        4. 达到test_interval时调用Test()
        5. 达到snapshot时调用snapshot()
        6. 调用Snapshot函数:Snapshot()
    • 输入:char*类型的resume_file
    • 输出:无
  • void Solver::Test()
    • 功能:测试网络
    • 输入:无
    • 输出:无
    • 步骤:
      1. 设置当前阶段(TRAIN还是TEST/TEST)
      2. 将test_net_指向net_,即对同一个网络操作
      3. 对于每一次测试时的迭代:for (int i = 0; i < param_.test_iter(); ++i)
        1. 用下面语句给result赋值net_output_blobs_ //result是所有的输出层blob
          同时得到这次测试的iter_loss
          result = test_net_->Forward(bottom_vec, &iter_loss)
        2. 第一次测试时:
          1. 取每一个输出层的blob result_vec = result[j]->cpu_data()
          2. 把每一个blob的数据(降为一维)存入一个vector–“test_score”
        3. 不是第一次测试:
          1. 用 test_score[idx++] += result_vec[k], 而不是 test_score.push_back(result_vec[k])
          2. 把输出层对应位置的blob值累加 test_score[idx++] += result_vec[k]
        4. 是否要输出Test loss
        5. 是否要输出test_score
        6. 设置当前阶段(TRAIN还是TEST/TRAIN)
  • void Solver::Snapshot()
    • 功能:输出当前网络状态到一个文件中,不重要
    • 输入:无
    • 输出:无
  • void Solver::Restore(const char* state_file)
    • 功能:从一个文件中读入网络状态,并可以从那个状态恢复,不重要
    • 输入:文件名
    • 输出:无
  • Dtype SGDSolver::GetLearningRate()
    • 功能:得到学习率
    • 步骤:
      1. 得到学习率类型 const string& lr_policy = this->param_.lr_policy()
      2. 判断学习率类型(注释有介绍)
      3. 返回学习率
    • 输入:无
    • 输出:Dtype类型的rate
  • void SGDSolver::PreSolve()
    • 功能:提前训练
    • 步骤:
      1. 将训练网络net_的参数读到net_params net_params = this->net_->params()
        其中params_是一个存blob指针的vector
      2. 清空历史残留值
      3. 向history压入与网络的每一层blob相同大小的空间
    • 输入:无
    • 输出:无
  • void SGDSolver::ComputeUpdateValue()
    • 功能:用随机梯度下降法计算更新值
    • 输入:无
    • 输出:无

What I have learned

  • 注释的重要性, 代码中每个地方都有详细的注释, 方便维护的同时, 配合Doxygen可以自动生成文档
  • 测试的重要性, Caffe中有大量的测试
  • OO思想和设计模式, Caffe用多类, 继承(特别是在Layer), 以及工厂模式, 每处的紧密联系保证了庞大的系统的稳定性, 易用性, 可拓展性, 鲁棒性.

Leave a Comment

电子邮件地址不会被公开。 必填项已用*标注