↓ Skip to main content
  1. Posts/

caffe 源码学习笔记(10) eltwise layer

·988 words·2 mins
Note: This article is available in Chinese only. 本文暂无英文版本。 View original

背景
#

这个 layer 和 reduce layer 有一些相似,就干脆一起看了。 作用是输入至少两个 blob,然后对每个 blob 中的元素做一些运算,最后得到一个 blob。

caffe 支持的运算有 “PROD”、“SUM”、“MAX” 三种。

顺便提一句,TensorRT 支持的要多一些:

 1
 2enum class ElementWiseOperation : int
 3{
 4    kSUM = 0,  //!< Sum of the two elements.
 5    kPROD = 1, //!< Product of the two elements.
 6    kMAX = 2,  //!< Maximum of the two elements.
 7    kMIN = 3,  //!< Minimum of the two elements.
 8    kSUB = 4,  //!< Substract the second element from the first.
 9    kDIV = 5,  //!< Divide the first element by the second.
10    kPOW = 6   //!< The first element to the power of the second element.
11};

proto
#

 1
 2message EltwiseParameter {
 3  enum EltwiseOp {
 4    PROD = 0;
 5    SUM = 1;
 6    MAX = 2;
 7  }
 8  optional EltwiseOp operation = 1 [default = SUM]; // element-wise operation
 9  repeated float coeff = 2; // blob-wise coefficient for SUM operation
10
11  // Whether to use an asymptotically slower (for >2 inputs) but stabler method
12  // of computing the gradient for the PROD operation. (No effect for SUM op.)
13  optional bool stable_prod_grad = 3 [default = true];
14}

proto 里面的 coeff 是针对 SUM 操作的,可以给每一个 bottom blob 一个加权系数;stable_prod_grad 是 backward 用的,不用管。

c++ 实现
#

代码比较容易看懂,加了一些注释。有两个地方可以提一下:一个是 PROD 和 MAX 的做法,都是先求前两个,再把得到的结果和后面的 blob 进行运算(其实是很自然的操作…似乎也没什么可说的 orz)。

另外一个是 mask 这个变量,是在 MAX 操作时用来标记在哪个 bottom blob 取到了最大值,反向传播时要用。

详细代码
 1
 2template <typename Dtype>
 3void EltwiseLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
 4      const vector<Blob<Dtype>*>& top) {
 5  for (int i = 1; i < bottom.size(); ++i) {
 6    CHECK(bottom[i]->shape() == bottom[0]->shape());
 7  }
 8  //  check所有的bottom blob的shape都一样. 至少存在两个bottom blob
 9  top[0]->ReshapeLike(*bottom[0]);
10  // If max operation, we will initialize the vector index part.
11  if (this->layer_param_.eltwise_param().operation() ==
12      EltwiseParameter_EltwiseOp_MAX && top.size() == 1) {
13    max_idx_.Reshape(bottom[0]->shape());
14  }
15}
16
17template <typename Dtype>
18void EltwiseLayer<Dtype>::Forward_cpu(
19    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
20  int* mask = NULL;
21  const Dtype* bottom_data_a = NULL;
22  const Dtype* bottom_data_b = NULL;
23  const int count = top[0]->count();
24  Dtype* top_data = top[0]->mutable_cpu_data();
25  switch (op_) {
26  case EltwiseParameter_EltwiseOp_PROD:
27    caffe_mul(count, bottom[0]->cpu_data(), bottom[1]->cpu_data(), top_data);
28    for (int i = 2; i < bottom.size(); ++i) {
29      caffe_mul(count, top_data, bottom[i]->cpu_data(), top_data);
30    }
31    //  先算前两个,然后把结果和后面的每一个blob(如果还有的话)做运算
32    break;
33  case EltwiseParameter_EltwiseOp_SUM:
34    caffe_set(count, Dtype(0), top_data);
35    // 初始化top data为0
36    // TODO(shelhamer) does BLAS optimize to sum for coeff = 1?
37    for (int i = 0; i < bottom.size(); ++i) {
38      caffe_axpy(count, coeffs_[i], bottom[i]->cpu_data(), top_data);
39    }
40    break;
41  //  mask干啥用的???
42  //  forward应该用不到,是backward求梯度需要知道在哪个位置得到了最大值
43  case EltwiseParameter_EltwiseOp_MAX:
44    // Initialize
45    mask = max_idx_.mutable_cpu_data();
46    caffe_set(count, -1, mask);
47    caffe_set(count, Dtype(-FLT_MAX), top_data);
48    // bottom 0 & 1
49    bottom_data_a = bottom[0]->cpu_data();
50    bottom_data_b = bottom[1]->cpu_data();
51    for (int idx = 0; idx < count; ++idx) {
52      if (bottom_data_a[idx] > bottom_data_b[idx]) {
53        top_data[idx] = bottom_data_a[idx];  // maxval
54        mask[idx] = 0;  // maxid
55      } else {
56        top_data[idx] = bottom_data_b[idx];  // maxval
57        mask[idx] = 1;  // maxid
58      }
59    }
60    // bottom 2++
61    for (int blob_idx = 2; blob_idx < bottom.size(); ++blob_idx) {
62      bottom_data_b = bottom[blob_idx]->cpu_data();
63      for (int idx = 0; idx < count; ++idx) {
64        if (bottom_data_b[idx] > top_data[idx]) {
65          top_data[idx] = bottom_data_b[idx];  // maxval
66          mask[idx] = blob_idx;  // maxid
67        }
68      }
69    }
70    break;
71  default:
72    LOG(FATAL) << "Unknown elementwise operation.";
73  }
74}

Related

caffe 源码学习笔记(9) reduce layer

·1251 words·3 mins
背景 # 其实没什么背景,继续啃 caffe 代码而已 2333。 reduce layer 其实就是做 reduce 操作,把一个任意 shape 的 blob 通过某种运算变成一个 scalar。 caffe 目前支持求和(SUM)、绝对值的和(ASUM)、平方和(SUMSQ),以及对得到的 scalar 的总数求平均的求和(MEAN)。

caffe 源码学习笔记(8) loss function

·1095 words·3 mins
背景 # 虽然不太care 训练的过程,但是由于容易看懂的layer都看得差不多了 所以打算看一下这些loss function. Euclidean Loss (L2 loss) # 一般用于 “real-valued regression tasks”。比如之前项目里用的人脸年龄模型,就是用了这个 Loss。

caffe 源码学习笔记(7) slice layer

·1121 words·3 mins
背景 # OCR 组那边有个 shufflenet 的网络,里面有个 pytorch op 叫 chunk,转成的 onnx 对应的 op 是 split。 作用是: Split a tensor into a list of tensors, along the specified ‘axis’. Lengths of the parts can be specified using argument ‘split’. Otherwise, the tensor is split to equal sized parts.

caffe 源码学习笔记(6) reshape layer

·1851 words·4 mins
背景 # 最近在魔改 TensorRT 的 caffe parser。 之前 caffe 模型转到 trt 模型时,有一个修改需要将 reshape layer 的 param 末尾补 1,比较繁琐,于是看了下 caffe 的 reshape layer 的实现。

caffe 源码学习笔记(5) 卷积

·2756 words·6 mins
caffe中卷积运算的实现 # 暴力实现的卷积大概是这样子的 1 2for w in 1..W 3 for h in 1..H 4 for x in 1..K 5 for y in 1..K 6 for m in 1..M 7 for d in 1..D 8 output(w, h, m) += input(w+x, h+y, d) * filter(m, x, y, d) 9 end 10 end 11 end 12 end 13 end 14end 这种方式的效率显然很低,不意外地,caffe中并不是这样实现的.