caffe源码阅读3-blob.cpp

1 第一个方法:Reshape()

template <typename Dtype>
void Blob<Dtype>::Reshape(const int num, const int channels, const int height,
    const int width) {
  CHECK_GE(num, 0);
  CHECK_GE(channels, 0);
  CHECK_GE(height, 0);
  CHECK_GE(width, 0);
  num_ = num;
  channels_ = channels;
  height_ = height;
  width_ = width;
  count_ = num_ * channels_ * height_ * width_;
  if (count_ > capacity_) {
    capacity_ = count_;
    data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
    diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
  }
}

从源码中,可以看到,这个函数感觉不能随便乱用啊,直接根据给定的输入参数,直接修改了元数据的大小,并且如果count_超过了capacity_就重新开辟空间,注意,这里是重新开辟空间!!并没有将原来的数据放进去。就算count_没有超过capacity_,原来的数据不是就已经改变了吗?所以,这个函数不要轻易使用。

至于推荐使用的ReshapeLike(),其实也调用的Reshape(),如果对数据变换以及数据存储结构不是特别清楚,最要不要随便使用这两个函数!

然后再来看看构造函数:

在hpp中的声明是:

  Blob()
       : data_(), diff_(), num_(0), channels_(0), height_(0), width_(0),
       count_(0), capacity_(0) {}
  explicit Blob(const int num, const int channels, const int height,
    const int width);

第一个构造函数应该说比较好理解,依次调用各种方法。第二个构造函数在cpp中有具体的实现:

template <typename Dtype>
Blob<Dtype>::Blob(const int num, const int channels, const int height,
    const int width)
  // capacity_ must be initialized before calling Reshape
  : capacity_(0) {
  Reshape(num, channels, height, width);
}

还是调用那个 Reshape(),不过这里相当于是初始化的过程,所以可以理解。

2 还记得在blob.hpp中定义了各种关于data的方法吗:

  const Dtype* cpu_data() const;
  void set_cpu_data(Dtype* data);
  const Dtype* gpu_data() const;
  const Dtype* cpu_diff() const;
  const Dtype* gpu_diff() const;
  Dtype* mutable_cpu_data();
  Dtype* mutable_gpu_data();
  Dtype* mutable_cpu_diff();
  Dtype* mutable_gpu_diff();

根据上一篇博客( caffe源码阅读2)的介绍,现在再来看这些就应该很好理解了,除了 set_cpu_data()外,这里的方法都是返回数据,而且都是调用syncedmem里面的方法。

例如cpu_diff()的源码如下:

template <typename Dtype>
const Dtype* Blob<Dtype>::cpu_diff() const {
  CHECK(data_);
  return (const Dtype*)diff_->cpu_data();
}

3 在blob.cpp中接着的方法是 ShareData()ShareDiff(),在( caffe源码阅读1)中说得很不详细。这里再具体说一下,以 ShareDiff()的源码来进行说明:

template <typename Dtype>
void Blob<Dtype>::ShareDiff(const Blob& other) {
  CHECK_EQ(count_, other.count());
  diff_ = other.diff();
}

从源码中可以看到,也就是将参数(const Blob& other)中的数据共享(赋值)给自己。

4 呃,然后Updata(),源码如下:

// The "update" method is used for parameter blobs in a Net, which are stored
// as Blob<float> or Blob<double> -- hence we do not define it for
// Blob<int> or Blob<unsigned int>.
template <> void Blob<unsigned int>::Update() { NOT_IMPLEMENTED; }
template <> void Blob<int>::Update() { NOT_IMPLEMENTED; }

template <typename Dtype>
void Blob<Dtype>::Update() {
  // We will perform update based on where the data is located.
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    // perform computation on CPU
    caffe_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->cpu_data()),
        static_cast<Dtype*>(data_->mutable_cpu_data()));
    break;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    // perform computation on GPU
    caffe_gpu_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->gpu_data()),
        static_cast<Dtype*>(data_->mutable_gpu_data()));
#else
    NO_GPU;
#endif
    break;
  default:
    LOG(FATAL) << "Syncedmem not initialized.";
  }
}

这里懵的原因如下:函数这样子重载真的可以吗?尽管注释得很详细,但是caffe_axpy()是什么啊?好吧,这个函数就先放放,后面读到了,再继续解读。

解释这里的问题,caffe_axpy()可以在caffe源码阅读-插曲-math_function.cpp中可以看到,用在updata中表达的意思就是:data = -1*diff + data;也就是权重更新的过程。

5 继续源码中的asum_data()

template <typename Dtype>
Dtype Blob<Dtype>::asum_data() const {
  if (!data_) { return 0; }
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    return caffe_cpu_asum(count_, cpu_data());
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
  {
    Dtype asum;
    caffe_gpu_asum(count_, gpu_data(), &asum);
    return asum;
  }
#else
    NO_GPU;
#endif
  case SyncedMemory::UNINITIALIZED:
    return 0;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
  }
  return 0;
}

尽管这里也涉及到一个不认识的函数 caffe_cpu_asum(),不过在hpp中其实介绍的很清楚, asum_data()就是计算数据(data)的绝对值之和,或者说一阶范数。所以我想 caffe_cpu_asum()才是 一阶范数计算的具体实现吧。那么 asum_diff()也是一样的道理。

6 CopyFrom()

函数原型:void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape)

应该可以知道,是从数据source中将数据拷贝到自己。

后面的两个参数:

copy_diff如果为true,则从source中拷贝diff数据,否则拷贝data数据;

reshape如果为true,则强制拷贝,否则当source的数据与自己的数据尺寸不同时,报错。

源码如下:

template <typename Dtype>
void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape) {
  if (num_ != source.num() || channels_ != source.channels() ||
      height_ != source.height() || width_ != source.width()) {
    if (reshape) {
      Reshape(source.num(), source.channels(), source.height(), source.width());
    } else {
      LOG(FATAL) << "Trying to copy blobs of different sizes.";
    }
  }
  switch (Caffe::mode()) {
  case Caffe::GPU:
    if (copy_diff) {
      caffe_copy(count_, source.gpu_diff(),
          static_cast<Dtype*>(diff_->mutable_gpu_data()));
    } else {
      caffe_copy(count_, source.gpu_data(),
          static_cast<Dtype*>(data_->mutable_gpu_data()));
    }
    break;
  case Caffe::CPU:
    if (copy_diff) {
      caffe_copy(count_, source.cpu_diff(),
          static_cast<Dtype*>(diff_->mutable_cpu_data()));
    } else {
      caffe_copy(count_, source.cpu_data(),
          static_cast<Dtype*>(data_->mutable_cpu_data()));
    }
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode.";
  }
}

虽然这里涉及到函数caffe_copy,以及caffe::mode(),不过不影响阅读,使用过caffe的人应该都知道caffe::GPU和caffe::CPU是什么意思。

7 FromProto() ToProto()

源码如下:

template <typename Dtype>
void Blob<Dtype>::FromProto(const BlobProto& proto) {
  Reshape(proto.num(), proto.channels(), proto.height(), proto.width());
  // copy data
  Dtype* data_vec = mutable_cpu_data();
  for (int i = 0; i < count_; ++i) {
    data_vec[i] = proto.data(i);
  }
  if (proto.diff_size() > 0) {
    Dtype* diff_vec = mutable_cpu_diff();
    for (int i = 0; i < count_; ++i) {
      diff_vec[i] = proto.diff(i);
    }
  }
}

template <typename Dtype>
void Blob<Dtype>::ToProto(BlobProto* proto, bool write_diff) const {
  proto->set_num(num_);
  proto->set_channels(channels_);
  proto->set_height(height_);
  proto->set_width(width_);
  proto->clear_data();
  proto->clear_diff();
  const Dtype* data_vec = cpu_data();
  for (int i = 0; i < count_; ++i) {
    proto->add_data(data_vec[i]);
  }
  if (write_diff) {
    const Dtype* diff_vec = cpu_diff();
    for (int i = 0; i < count_; ++i) {
      proto->add_diff(diff_vec[i]);
    }
  }
}

这两个方法阅读起来,理解上应该不会有什么问题, FromProto()就是从proto中拷贝数据给自己, ToProto()就是将自己的数据拷贝出去给proto。但是对我产生了几个疑问:

·BlobProto长什么样呢?当然估计和Blob差不多的。但是注意它的下标访问是用()而不是[]。

回答这个问题:BlobProto定义在caffe.proto中,源码为:

message BlobProto {
  optional int32 num = 1 [default = 0];
  optional int32 channels = 2 [default = 0];
  optional int32 height = 3 [default = 0];
  optional int32 width = 4 [default = 0];
  repeated float data = 5 [packed = true];
  repeated float diff = 6 [packed = true];
}

至于为什么可以使用()来访问数据,应该与google protocol buffer的协议有关系吧。

·在FromProto()中为什么需要调用mutable_cpu_data()来获得数据,而不是直接存入自己的私有变量data_中呢?难道没有访问权限吗?不能啊,自己类的方法应该是可以访问自己的数据的吧?在ToProto()中也是同样的问题,不过这里调用的是cpu_data(),而不是mutable_cpu_data(),可能都还好理解一点,因为数据刚拷贝过来,当然知道数据的位置,所以调用mutable_cpu_data()也就同时设置数据头的位置。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值