caffe的学习之路---Blob的基本用法

本文详细介绍Caffe中Blob类的使用方法,并提供从磁盘读取和写入Blob的具体实现示例。适合有一定C++基础的学习者进一步理解Caffe框架。

首先,参考书籍《21天实战caffe》

以前没有C++基础,所以第一次看这本书时候觉得好垃圾;

现在有了C++基础,再次看这本书,发现还是很值得学习。


在本文之前,我已经把官方tutorial看完。

要看懂blob.hpp建议将<C++ Primer>前七章看完,再学习caffe源码。

如果要看layer的相关文件,至少把前十五章看完,一起加油。

所以现在开始读源码,首先还是类模板Blob。

下面是自己将Blob写入磁盘,从磁盘读取内容存入Blob的程序,当然《21天实战caffe》的源码。主要写给自己看。

首先先说几个Member Function

1  void Reshape(const int num,const int channels,const int height,const int weight)

      void Reshape(const vector<int> &shape)

这2个函数的作用是重构blob的维数(Dim)

2     void FromProto(const BlobProto &proto, bool reshape=true)

      用法a.FromProto 将Blob类型的变量a写入一个类型为BlobProto的变量proto中

  这是数据从Blob到磁盘的中间处理层(作用就是序列化)

3    void ToProto(BlobProto *proto, bool write_diff=fasle ) 

     第二个参数输入true (将diff也序列化,默认是只把data序列化)


另外需要认识2个IO函数

1. void WriteProToBinaryFile(const Message &proto,const char *filename)

      void WriteProToBinaryFile(const Message &proto,const string filename)

  从名字就看出是将BlobPro写入二进制文件filename

2.  bool ReadProtoFromBinaryFile(const char *filename,Message *proto)

     bool ReadProtoFromBinaryFile(const string &filename,Message *proto)

#include <vector>
#include <iostream>
#include <caffe/blob.hpp>
#include <caffe/util/io.hpp>

using namespace caffe;
using namespace std;

int main(){
    Blob<float> a;
    a.Reshape(1,2,3,4);
    float *p=a.mutable_cpu_data();
    float *q=a.mutable_cpu_diff();
    for(int i=0;i<a.count();i++){
    	p[i]=i;
	q[i]=a.count()-i-1;
    }
    a.Update();
    for(int u=0;u<a.num();u++){
        for(int v=0;v<a.channels();v++){
            for(int w=0;w<a.height();w++){
                for(int x=0;x<a.width();x++){
                    cout<<"a["<<u<<"]["<<v<<"]["<<w<<"]["<<x<<"]="
                        <<a.data_at(u,v,w,x)<<endl;
                }
            }
        }
    }
    BlobProto bp1;
    a.ToProto(&bp1,true);
    WriteProtoToBinaryFile(bp1,"a.blob");
    BlobProto bp2;
    ReadProtoFromBinaryFile("a.blob",&bp2);
    Blob<float> b;
    b.FromProto(bp2,true);
    for(int u=0;u<b.num();u++){
        for(int v=0;v<b.channels();v++){
            for(int w=0;w<b.height();w++){
                for(int x=0;x<b.width();x++){
                    cout<<"b["<<u<<"]["<<v<<"]["<<w<<"]["<<x<<"]="
			<<b.data_at(u,v,w,x)<<endl;
                }           
            }
        }
    }
    return 0;

}



<think> 我们正在处理一个有趣的问题:如何使用Caffe框架实现1到100的求和。但是,首先需要明确一点:Caffe是一个深度学习框架,主要用于训练和部署深度神经网络。它并不是为通用的数值计算(如简单的求和)而设计的。 然而,我们可以尝试用Caffe的方式来解决这个问题。在Caffe中,我们可以通过构建一个网络来实现计算。这里的一个思路是:我们可以设计一个自定义层(Custom Layer)来执行求和操作,或者利用现有的层组合来实现。 但是,让我们重新审视问题:1到100的求和是一个等差数列求和,公式为:$S = \frac{n \times (n+1)}{2}$,其中$n=100$,所以$S=5050$。如果只是为了得到结果,直接计算即可,无需使用Caffe。然而,如果是为了学习Caffe或者有特殊需求(比如在神经网络中嵌入这个求和操作),我们可以尝试模拟。 考虑到Caffe的典型流程是定义网络(prototxt文件)和运行求解器,我们可以尝试以下思路: 1. 生成一个包含1到100的数据集(例如,一个只有一个样本的数据,包含100个特征,每个特征的值分别是1到100)。 2. 构建一个网络,该网络包含一个全连接层(InnerProductLayer),其权重设置为1,偏置设置为0,且输出神经元只有一个。这样,全连接层的输出就是所有输入特征的和(因为每个输入特征乘以权重1然后相加)。 3. 运行网络,得到输出。 但是,Caffe的全连接层要求权重是学习得到的,或者我们可以通过设置固定的权重(不更新)来实现。我们可以在网络定义中设置参数的学习率为0,并且初始化权重为1。然而,更直接的方法是使用一个自定义层。 由于Caffe本身没有提供直接求和的层,我们可以考虑其他方式: - 使用Eltwise层进行求和:但Eltwise层通常用于两个blob之间的逐元素操作(如求和、乘积),而我们的输入是100个特征(可以视为一个1x100的blob),我们希望将这100个数相加得到一个数。我们可以通过一系列操作(比如多次使用Eltwise层)来实现,但这样很繁琐。 - 自定义一个求和层:我们可以写一个简单的层,在Forward_cpu函数中,将输入blob的所有元素相加,然后输出。这种方法更直接。 由于问题要求使用Caffe,我们将采用自定义层的方法。下面我们将描述如何实现: 步骤1:创建自定义层 我们需要创建两个文件:`sum_layer.hpp`(头文件)和`sum_layer.cpp`(源文件)。同时,我们需要修改`caffe.proto`文件以添加新层的参数(虽然这个层不需要参数,但为了统一,我们仍然要添加)。 步骤2:实现自定义层的前向传播 在`Forward_cpu`函数中,我们对输入blob的所有元素求和,并输出到top blob。 步骤3:在prototxt网络定义中使用该层。 注意:由于反向传播在求和问题中不需要(因为我们只是求值,不训练),我们可以将反向传播函数设置为空(或直接设置为不执行任何操作)。 但是,请注意,用户要求实现1到100求和,我们可以假设输入数据就是1到100这100个数字。因此,我们可以通过一个数据层(比如HDF5Data层)来提供这个数据,然后通过我们的求和层得到结果。 下面我们逐步实现: 1. 创建自定义求和层: sum_layer.hpp: ```cpp #ifndef CAFFE_SUM_LAYER_HPP_ #define CAFFE_SUM_LAYER_HPP_ #include <vector> #include "caffe/blob.hpp" #include "caffe/layer.hpp" #include "caffe/proto/caffe.pb.h" namespace caffe { template <typename Dtype> class SumLayer : public Layer<Dtype> { public: explicit SumLayer(const LayerParameter& param) : Layer<Dtype>(param) {} virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); virtual void Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); virtual inline const char* type() const { return "Sum"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); virtual void Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { // 不需要反向传播 } }; } // namespace caffe #endif // CAFFE_SUM_LAYER_HPP_ ``` sum_layer.cpp: ```cpp #include <vector> #include "caffe/layers/sum_layer.hpp" #include "caffe/util/math_functions.hpp" namespace caffe { template <typename Dtype> void SumLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { // 无需特别设置 } template <typename Dtype> void SumLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { // 输入是1个blob,假设bottom[0]的形状为(N, C, H, W),我们将其所有元素求和,输出为一个标量(但为了统一,输出blob的形状为(N, 1, 1, 1)) vector<int> top_shape(4, 1); top_shape[0] = bottom[0]->num(); top[0]->Reshape(top_shape); } template <typename Dtype> void SumLayer<Dtype>::Forward_cpu( const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); // 对每个样本分别求和(注意:bottom[0]可能有多个样本,即batch_size>1) int num = bottom[0]->num(); int dim = count / num; for (int i = 0; i < num; ++i) { top_data[i] = caffe_cpu_asum<Dtype>(dim, bottom_data + i * dim); } } INSTANTIATE_CLASS(SumLayer); REGISTER_LAYER_CLASS(Sum); } // namespace caffe ``` 注意:这里我们使用了`caffe_cpu_asum`函数,它计算绝对值之和。但是我们的数据都是正数(1到100),所以直接求和等同于绝对值之和。如果是负数,则绝对值之和不是我们想要的,但在这个问题中,数据都是正数,所以没关系。如果为了通用,我们可以自己写一个循环求和(或者用BLAS的`caffe_cpu_strided_dot`来点乘一个全1向量?)。但是为了简单,我们直接用`caffe_cpu_asum`。 2. 将上述两个文件放入Caffe的相应目录: - `sum_layer.hpp`放到`include/caffe/layers/` - `sum_layer.cpp`放到`src/caffe/layers/` 3. 修改`src/caffe/proto/caffe.proto`: 在`message LayerParameter`中添加: ``` optional SumParameter sum_param = 151; // 选择一个未使用的ID,确保不重复 ``` 然后定义`SumParameter`消息,虽然我们不需要参数,但为了统一,可以定义一个空消息: ``` message SumParameter { } ``` 4. 编译Caffe:重新编译Caffe以包含新层。 5. 创建一个网络定义文件(prototxt): 假设我们使用一个HDF5数据层来读取数据,数据文件`data.h5`中包含一个数据blob,其值为1到100(形状为1×100,即一个样本,100个特征)。 网络定义`sum_net.prototxt`: ``` name: "SumNet" layer { name: "data" type: "HDF5Data" top: "data" hdf5_data_param { source: "data.txt" # data.txt文件中包含数据文件的路径,例如:/path/to/data.h5 batch_size: 1 } include { phase: TRAIN # 或者TEST,这里我们只用于前向 } } layer { name: "sum" type: "Sum" bottom: "data" top: "sum_result" } ``` 6. 创建一个数据文件: 使用Python生成一个HDF5文件,包含一个数据blob(1到100): ```python import h5py import numpy as np data = np.array([range(1,101)], dtype=np.float32) # 1行100列,值为1到100 with h5py.File('data.h5', 'w') as f: f.create_dataset('data', data=data) # 创建data.txt,里面写入data.h5的绝对路径 with open('data.txt', 'w') as f: f.write('/absolute/path/to/data.h5\n') ``` 7. 使用Caffe的`time`命令运行网络(不需要训练,只需要前向传播): ``` caffe time -model sum_net.prototxt ``` 或者写一个简单的程序加载网络并执行前向传播,然后输出结果。 但是,请注意,这个自定义层输出的结果是一个blob,我们可以通过读取输出的blob来得到结果。 然而,以上步骤较为复杂,特别是对于简单的1到100求和。实际上,如果只是需要这个结果,直接计算是最方便的。如果是为了学习Caffe的自定义层开发,那么这是一个很好的练习。 因此,我们可以给出两种方案: 方案一:使用Caffe的自定义层(如上所述)。 方案二:直接计算(不推荐使用Caffe,因为杀鸡用牛刀)。 由于问题明确要求使用Caffe,我们采用方案一。 但是,我们需要注意的是,上述自定义层在实现时,对每个样本进行求和(即对每个样本,将其所有特征求和)。所以,当我们输入一个样本(100个特征,值为1到100)时,输出就是5050。 总结步骤: 1. 创建自定义求和层并添加到Caffe。 2. 准备包含1到100的HDF5数据文件。 3. 定义网络prototxt,使用HDF5Data层和Sum层。 4. 运行网络,从输出的blob中读取结果。 注意:在实际运行中,可能需要使用Caffe的Python接口来读取输出结果,或者使用`caffe test`来查看输出。 相关问题: §§相关问题§§ 1. 如何在Caffe中实现自定义层? 2. Caffe支持哪些数据格式作为输入? 3. 除了自定义层,还有其他方法在Caffe中实现求和操作吗? 4. Caffe中的HDF5Data层如何使用? </think> ### 如何在 Caffe 中实现 1 到 100 的求和 Caffe 是深度学习框架,主要用于神经网络训练,不直接支持算术运算。但可通过以下方法实现求和: #### 方法 1:使用自定义 Python 层(推荐) 1. **创建 `sum_layer.py`** ```python import caffe import numpy as np class SumLayer(caffe.Layer): def setup(self, bottom, top): params = eval(self.param_str) # 解析参数 self.start = params.get('start', 1) self.end = params.get('end', 100) def forward(self, bottom, top): result = np.array([sum(range(self.start, self.end+1))], dtype=np.float32) top[0].reshape(*result.shape) top[0].data[...] = result ``` 2. **网络定义 `sum_net.prototxt`** ```protobuf layer { name: "sum" type: "Python" top: "result" python_param { module: "sum_layer" # 文件名 layer: "SumLayer" # 类名 param_str: "{'start':1, 'end':100}" # 参数 } } ``` 3. **执行** 通过 Caffe Python 接口加载网络即可获取结果 `result.data[0] = 5050` #### 方法 2:利用数学公式层(需第三方扩展) 若已集成公式解析层(如 `EvalLayer`): ```protobuf layer { type: "Eval" top: "result" eval_param { expr: "n*(n+1)/2" # 等差数列公式 variables: { name: "n" value: 100 } } } ``` #### 方法 3:矩阵乘法(仅教学演示) ```protobuf layer { name: "ones" type: "Constant" top: "ones" constant_param { shape: { dim: 100 } value: 1.0 } # 生成100个1 } layer { name: "sequence" type: "Input" top: "seq" input_param { shape: { dim: 100 dim: 1 } } # 数据需预先填充1-100 } layer { name: "dot_product" type: "InnerProduct" bottom: "ones" bottom: "seq" top: "sum" inner_product_param { num_output: 1 bias_term: false } } ``` > **关键说明** > - Caffe 原生不支持算术运算,推荐方法 1 的自定义层 > - 实际开发中应避免将 Caffe 用于数值计算 > - 等差数列求和公式 $S_n = \frac{n(n+1)}{2}$ 可直接计算 $S_{100} = 5050$[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值