- 路线图
http://blog.csdn.net/kkk584520/article/details/41681085
- (1)从CAFFE_ROOT/src/caffe/proto/caffe.proto开始,了解各类数据结构,主要是内存对象和序列化磁盘文件的一一对应关系,知道如何从磁盘Load一个对象到内存,以及如何将内存对象Save到磁盘,中间的过程实现都是由Protobuf自动完成的。
- (2)看头文件,先理解整个框架。Caffe中类数目众多,但脉络十分清晰。在Testing时,最外层的类是Caffe::Net,包含了多个Caffe::Layer对象,而Layer对象派生出神经网络多种不同层的类(DataLayer,ConvolutionLayer,InnerProductionLayer,AccurancyLayer等),每层会有相应的输入输出(Blob对象)以及层的参数(可选,Blob对象);Blob中包括了SyncedMemory对象,统一了CPU和GPU存储器。自顶向下去看这些类,结合理论知识很容易掌握使用方法。
- (3)有针对性地去看cpp和cu文件。一般而言,Caffe框架不需要修改,只需要增加新的层实现即可。例如想自己实现卷积层,只需从ConvolutionLayer派生一个新类MyConvolutionLayer,然后将几个虚函数改成自己的实现即可。所以这一阶段关注点在算法上,而不是源码本身。
- (4)可以自己尝试编写各类工具,集成到Caffe内部。在CAFFE_ROOT/tools/下面有很多实用工具,可以根据需要修改。例如从训练好的模型中抽取参数进行可视化可以用Python结合matplot实现。
Caffe 数据结构
protobuf详解
参考 http://blog.csdn.net/kkk584520/article/details/41046827
http://www.cnblogs.com/yymn/p/5167013.html
- protobuf用于caffe中的参数管理。在实际使用的时候主要是作为一个代码自动生成工具来使用,通过生成对所定义的数据结构的标准读写代码,用户可以通过标准的读写接口从文件中进行数据的读取,解析和存储。
- protobuf使用前,先编写proto文件,这是描述我们需要配置参数的数据结构。
- src/caffe/proto/caffe.proto:(以NetParameter为例)
message NetParameter {
optional string name = 1; // consider giving the network a name
// DEPRECATED. See InputParameter. The input blobs to the network.
repeated string input = 3;
// DEPRECATED. See InputParameter. The shape of the input blobs.
repeated BlobShape input_shape = 8;
// 4D input dimensions -- deprecated. Use "input_shape" instead.
// If specified, for each input blob there should be four
// values specifying the num, channels, height and width of the input blob.
// Thus, there should be a total of (4 * #input) numbers.
repeated int32 input_dim = 4;
// Whether the network will force every layer to carry out backward operation.
// If set False, then whether to carry out backward is determined
// automatically according to the net structure and learning rates.
optional bool force_backward = 5 [default = false];
// The current "state" of the network, including the phase, level, and stage.
// Some layers may be included/excluded depending on this state and the states
// specified in the layers' include and exclude fields.
optional NetState state = 6;
// Print debugging information about results while running Net::Forward,
// Net::Backward, and Net::Update.
optional bool debug_info = 7 [default = false];
// The layers that make up the net. Each of their configurations, including
// connectivity and behavior, is specified as a LayerParameter.
repeated LayerParameter layer = 100; // ID 100 so layers are printed last.
// DEPRECATED: use 'layer' instead.
repeated V1LayerParameter layers = 2;
解析
- 一个message称为一个消息,由至少一个字段组合而成,字段格式是:
限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
- 限定修饰符包含 required\optional\repeated:
- Required: 表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
- Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。—因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
- Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。
- 字段编码值用于使通信双方互相能识别对方的字段。相同的编码值,其限定修饰符和数据类型必须相同。编码值取值范围:1~2^32.编码值越大,其编码的时间和空间效率就越低。protobuf建议把经常要传递的值的字段编码设置为1~15.消息中的字段编码的值无需连续,只要是合法的,并且不能在同一个消息中有字段包含相同的编码值。
- 默认值:对required数据类型,如果用户没有设置值,则使用默认值传递;对optional数据类型,如果没有接收到optional字段,则设置为默认值。
- 在实际程序中只需要调用类中的接口如:set_***(), add_***(), mutable_***(), ***_size()等就能够操作相应的参数,最后将内存中的参数序列化为文件只需要执行SerializeToOstream()。相应的读取参数文件的操作为ParseFromIstream()。
- message支持嵌套消息,消息可以包含另一个消息作为其字段。也可以在消息内定义一个新的消息。
- NetParameter中的BlobShape、NetState、LayerParameter 、V1LayerParameter也是一个message,同样包含多个字段,只需要通过接口函数进行调用。
认识protobuf对后续学习源码很有好处,在源码里经常会出现一些参数操作,如果不了解protobuf的存在就会对这些操作无所适从不知出处。
核心类
- Blob类
- blob作为统一的内存接口在不同应用场景具有不同的含义,如可以是:batches of images, model parameters, derivatives for optimization等
- 源代码见以下:
- data_ 为原始数据
- diff_ 为梯度信息
- shape_data_ 为blob数据
- shape_ 为blob维度:
1.对于图像数据(n, k, h, w),blob数据维度为n*k*h*w,blob是row-major保存,因此在(n, k, h, w)图像位置的数据的物理位置为((n * K + k) * H + h) * W + w。注意这个保存方式,在用opencv读图传入caffe中时由于存储方式不同需要对数据进行转换
2.对于参数,维度根据该层的类型和配置来决定。如对于有3个输入96个输出的卷积层,Filter核 11*11,则blob为96*3*11*11;对于全连接层,1000个输出,1024个输入,则blob为1000*1024 - count_ 为该blob总容量(size)
- blob类源代码:
template <typename Dtype>
class Blob {
public:
Blob(): data_(), diff_(), count_(0), capacity_(0) {}
/// @brief Deprecated; use <code>Blob(const vector<int>& shape)</code>.
explicit Blob(const int num, const int channels, const int height, const int width);
explicit Blob(const vector<int>& shape);
/// @brief Deprecated; use <code>Reshape(const vector<int>& shape)</code>.
void Reshape(const int num, const int channels, const int height, const int width);
......
protected:
shared_ptr<SyncedMemory> data_;
shared_ptr<SyncedMemory> diff_;
shared_ptr<SyncedMemory> shape_data_;
vector<int> shape_;
int count_;
int capacity_;
DISABLE_COPY_AND_ASSIGN(Blob);
}; // class Blob
Layer类
- 主要定义了layer的初始化、前向传播、反向传播操作。
- layer_param_ 存储该层的参数(protobuf)
- phase_ Train or Test阶段
- blobs_存储每层网络的训练参数,通常有blobs_[0]存储该层网络的weight,blobs_[1]存储该层网络的bias。不管是weight还是bias都具有blob的结构
- param_propagate_down_ 标记该层网络做不做反向传播
- loss_ 存储每层网络的loss,但是除了loss layer外,基本上其他类型的层的loss都是0
- Forward:前向传播,根据bottom计算top,调用了Forward_cpu(必须实现)和Forward_gpu(可选,若未实现,则调用cpu的)
- Backward:反向传播,根据top计算bottom的梯度,其他同上
五大派生类
- Data Layers -data_layer,memory_data_layer,hdf5_data_layer,hdf5_output_layer,image_data_layer,window_data_layer,dummy_data_layer等等
- Vision Layers
- conv_layer,pooling_layer,lrn_layer等等 - Activation Neuron Layers -relu_layer,sigmoid_layer,tanh_layer,prelu_layer,power_layer,absval_layer,bnll_layer等等
- Loss Layers
-softmax_loss_layer,euclidean_loss_layer(EuclideanLoss),hinge_loss_layer,infogain_loss_layer,sigmoid_cross_entropy_loss_layer,infogain_loss_layer等等 - Common Layers
- 单个层与多个层的连接,concat_layer,eltwise_layer,mvn_layer(Mean-Variance Normalization),slice_layer,split_layer等等 - 每个layer都有对应的.cpp,.cu和.hpp文件声明和实现了各个类的接口
layer类源代码
- 主要定义了layer的初始化、前向传播、反向传播操作。
template <typename Dtype>
class Layer {
public:
explicit Layer(const LayerParameter& param) : layer_param_(param) {
// Set phase and copy blobs (if there are any).
phase_ = param.phase();
if (layer_param_.blobs_size() > 0) {
blobs_.resize(layer_param_.blobs_size());
for (int i = 0; i < layer_param_.blobs_size(); ++i) {
blobs_[i].reset(new Blob<Dtype>());
blobs_[i]->FromProto(layer_param_.blobs(i));
}
}
}
virtual ~Layer() {}
void SetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
CheckBlobCounts(bottom, top);
LayerSetUp(bottom, top);
Reshape(bottom, top);
SetLossWeights(top);
}
inline Dtype Forward(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top);
inline void Backward(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
......
protected:
/** The protobuf that stores the layer parameters */
LayerParameter layer_param_;
/** The phase: TRAIN or TEST */
Phase phase_;
/** The vector that stores the learnable parameters as a set of blobs. */
vector<shared_ptr<Blob<Dtype> > > blobs_;
/** Vector indicating whether to compute the diff of each param blob. */
vector<bool> param_propagate_down_;
/** The vector that indicates whether each top blob has a non-zero weight in the objective function. */
vector<Dtype> loss_;
/** @brief Using the CPU device, compute the layer output. */
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) = 0;
/**@brief Using the GPU device, compute the layer output. Fall back to Forward_cpu() if unavailable. */
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
// LOG(WARNING) << "Using CPU code as backup.";
return Forward_cpu(bottom, top);
}
/**@brief Using the CPU device, compute the gradients for any parameters and for the bottom blobs if propagate_down is true.*/
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) = 0;
/** @brief Using the GPU device, compute the gradients for any parameters and for the bottom blobs if propagate_down is true. Fall back to Backward_cpu() if unavailable. */
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
// LOG(WARNING) << "Using CPU code as backup.";
Backward_cpu(top, propagate_down, bottom);
}
......
private:
DISABLE_COPY_AND_ASSIGN(Layer);
}; // class Layer
- Net类
- Init,通过创建blob和layer搭建了整个网络框架,以及调用各层的SetUp函数
- blobs_ 存放这每一层产生的blobls的中间结果
- top_vecs_ 存放每一层的top blobs
- Net类源代码
template <typename Dtype>
class Net {
public:
explicit Net(const NetParameter& param);
explicit Net(const string& param_file, Phase phase, const int level = 0, const vector<string>* stages = NULL);
virtual ~Net() {}
/// @brief Initialize a network with a NetParameter.
void Init(const NetParameter& param);
......
const vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom, Dtype* loss = NULL);
......
void Backward();
Dtype ForwardBackward() {
Dtype loss;
Forward(&loss);
Backward();
return loss;
}
protected:
/// @brief The network name
string name_;
/// @brief The phase: TRAIN or TEST
Phase phase_;
/// @brief Individual layers in the net
vector<shared_ptr<Layer<Dtype> > > layers_;
vector<string> layer_names_;
map<string, int> layer_names_index_;
vector<bool> layer_need_backward_;
/// @brief the blobs storing intermediate results between the layer.
vector<shared_ptr<Blob<Dtype> > > blobs_;
......
vector<vector<Blob<Dtype>*> > bottom_vecs_;
......
vector<vector<Blob<Dtype>*> > top_vecs_;
......
DISABLE_COPY_AND_ASSIGN(Net);
};
} // namespace caffe
- Solver类
- 优化方法
参考
http://blog.csdn.net/mounty_fsc/article/details/51085654
http://blog.csdn.net/mounty_fsc/article/details/51088173