Eltwise : element-wise
eltwise layer是caffe提供的按元素操作层。它支持3种基本操作:
1. PROD:按元素乘积
2. SUM:按元素求和(默认)
3. MAX:保存元素大者
进行何种操作可以在layer里面通过定义EltwiseOp : x #x:=0,1,2
除此之外,该层还定义了
coeff
参数,该参数只对SUM操作起作用。
最后,caffe还设定了stable_prod_grad #[default = true ]
来选择是否渐进较慢的梯度计算方法,该方法只适用于PROD操作,对SUM操作无效。
更多细节参见下面的源码。
eltwise_layer 源码
#include <cfloat>
#include <vector>
#include "caffe/layers/eltwise_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
void EltwiseLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
CHECK(this->layer_param().eltwise_param().coeff_size() == 0
|| this->layer_param().eltwise_param().coeff_size() == bottom.size()) <<
"Eltwise Layer takes one coefficient per bottom blob.";
CHECK(!(this->layer_param().eltwise_param().operation()
== EltwiseParameter_EltwiseOp_PROD
&& this->layer_param().eltwise_param().coeff_size())) <<
"Eltwise layer only takes coefficients for summation.";
op_ = this->layer_param_.eltwise_param().operation();
// Blob-wise coefficients for the elementwise operation.
coeffs_ = vector<Dtype>(bottom.size(), 1);
if (this->layer_param().eltwise_param().coeff_size()) {
for (int i = 0; i < bottom.size(); ++i) {
coeffs_[i] = this->layer_param().eltwise_param().coeff(i);
}
}
stable_prod_grad_ = this->layer_param_.eltwise_param().stable_prod_grad();
}
template <typename Dtype>
void EltwiseLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
for (int i = 1; i < bottom.size(); ++i) {
CHECK(bottom[i]->shape() == bottom[0]->shape());
}
top[0]->ReshapeLike(*bottom[0]);
// If max operation, we will initialize the vector index part.
if (this->layer_param_.eltwise_param().operation() ==
EltwiseParameter_EltwiseOp_MAX && top.size() == 1) {
max_idx_.Reshape(bottom[0]->shape());
}
}
template <typename Dtype>
void EltwiseLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
int* mask = NULL;
const Dtype* bottom_data_a = NULL;
const Dtype* bottom_data_b = NULL;
const int count = top[0]->count();
Dtype* top_data = top[0]->mutable_cpu_data();
switch (op_) { //choose different operations according to op_
case EltwiseParameter_EltwiseOp_PROD: //PROD,按位乘
caffe_mul(count, bottom[0]->cpu_data(), bottom[1]->cpu_data(), top_data);
for (int i = 2; i < bottom.size(); ++i) {
caffe_mul(count, top_data, bottom[i]->cpu_data(), top_data);
}
break;
case EltwiseParameter_EltwiseOp_SUM: //SUM 按位和,这里有coffs_为系数,可以通过指定某一输入的系数来完成按位加和按位减
caffe_set(count, Dtype(0), top_data);
// TODO(shelhamer) does BLAS optimize to sum for coeff = 1?
for (int i = 0; i < bottom.size(); ++i) {
caffe_axpy(count, coeffs_[i], bottom[i]->cpu_data(), top_data);
}
break;
case EltwiseParameter_EltwiseOp_MAX: //按位取大数
// Initialize
mask = max_idx_.mutable_cpu_data();
caffe_set(count, -1, mask);
caffe_set(count, Dtype(-FLT_MAX), top_data);
// bottom 0 & 1
bottom_data_a = bottom[0]->cpu_data();
bottom_data_b = bottom[1]->cpu_data();
for (int idx = 0; idx < count; ++idx) {
if (bottom_data_a[idx] > bottom_data_b[idx]) {
top_data[idx] = bottom_data_a[idx]; // maxval
mask[idx] = 0; // maxid
} else {
top_data[idx] = bottom_data_b[idx]; // maxval
mask[idx] = 1; // maxid
}
}
// bottom 2++
for (int blob_idx = 2; blob_idx < bottom.size(); ++blob_idx) {
bottom_data_b = bottom[blob_idx]->cpu_data();
for (int idx = 0; idx < count; ++idx) {
if (bottom_data_b[idx] > top_data[idx]) {
top_data[idx] = bottom_data_b[idx]; // maxval
mask[idx] = blob_idx; // maxid
}
}
}
break;
default:
LOG(FATAL) << "Unknown elementwise operation.";
}
}
template <typename Dtype>
void EltwiseLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
const int* mask = NULL;
const int count = top[0]->count();
const Dtype* top_data = top[0]->cpu_data();
const Dtype* top_diff = top[0]->cpu_diff();
for (int i = 0; i < bottom.size(); ++i) {
if (propagate_down[i]) {
const Dtype* bottom_data = bottom[i]->cpu_data();
Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();
switch (op_) {
case EltwiseParameter_EltwiseOp_PROD:
if (stable_prod_grad_) {
bool initialized = false;
for (int j = 0; j < bottom.size(); ++j) {
if (i == j) { continue; }
if (!initialized) {
caffe_copy(count, bottom[j]->cpu_data(), bottom_diff);
initialized = true;
} else {
caffe_mul(count, bottom[j]->cpu_data(), bottom_diff,
bottom_diff);
}
}
} else {
caffe_div(count, top_data, bottom_data, bottom_diff);
}
caffe_mul(count, bottom_diff, top_diff, bottom_diff);
break;
case EltwiseParameter_EltwiseOp_SUM:
if (coeffs_[i] == Dtype(1)) {
caffe_copy(count, top_diff, bottom_diff);
} else {
caffe_cpu_scale(count, coeffs_[i], top_diff, bottom_diff);
}
break;
case EltwiseParameter_EltwiseOp_MAX:
mask = max_idx_.cpu_data();
for (int index = 0; index < count; ++index) {
Dtype gradient = 0;
if (mask[index] == i) {
gradient += top_diff[index];
}
bottom_diff[index] = gradient;
}
break;
default:
LOG(FATAL) << "Unknown elementwise operation.";
}
}
}
}
#ifdef CPU_ONLY
STUB_GPU(EltwiseLayer);
#endif
INSTANTIATE_CLASS(EltwiseLayer);
REGISTER_LAYER_CLASS(Eltwise);
} // namespace caffe
目录
简单介绍
eltwise_layer 实现多个blobs element-wise 的相加,相乘或者取最大值。
主要函数
1.LayerSetUp 函数:
template <typename Dtype>
void EltwiseLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
CHECK(this->layer_param().eltwise_param().coeff_size() == 0
|| this->layer_param().eltwise_param().coeff_size() == bottom.size()) <<
"Eltwise Layer takes one coefficient per bottom blob.";
// 如果存在系数,则每个blob 对应一个系数
CHECK(!(this->layer_param().eltwise_param().operation()
== EltwiseParameter_EltwiseOp_PROD
&& this->layer_param().eltwise_param().coeff_size())) <<
"Eltwise layer only takes coefficients for summation.";
op_ = this->layer_param_.eltwise_param().operation();//默认为SUM
// Blob-wise coefficients for the elementwise operation.
coeffs_ = vector<Dtype>(bottom.size(), 1);//将系数初始化为1
if (this->layer_param().eltwise_param().coeff_size()) {
for (int i = 0; i < bottom.size(); ++i) {
coeffs_[i] = this->layer_param().eltwise_param().coeff(i);
}//coeffs_ 存放从protobuf 传入的每个系数的值
}
stable_prod_grad_ = this->layer_param_.eltwise_param().stable_prod_grad();//默认true
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
2.Reshape 函数:
template <typename Dtype>
void EltwiseLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
for (int i = 1; i < bottom.size(); ++i) {
CHECK(bottom[i]->shape() == bottom[0]->shape());
}//输入的每个bottom要有相同的shape
top[0]->ReshapeLike(*bottom[0]); // 输入和输出的shape相同
// If max operation, we will initialize the vector index part.
if (this->layer_param_.eltwise_param().operation() ==
EltwiseParameter_EltwiseOp_MAX && top.size() == 1) {
max_idx_.Reshape(bottom[0]->shape());//存放取最大值时的index
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
3.Forward_cpu 函数:
template <typename Dtype>
void EltwiseLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
int* mask = NULL;
const Dtype* bottom_data_a = NULL;
const Dtype* bottom_data_b = NULL;
const int count = top[0]->count();
Dtype* top_data = top[0]->mutable_cpu_data();
switch (op_) {
case EltwiseParameter_EltwiseOp_PROD:
caffe_mul(count, bottom[0]->cpu_data(), bottom[1]->cpu_data(), top_data);
//输出top为输入的两个bottom的element-wise乘积
for (int i = 2; i < bottom.size(); ++i) {
caffe_mul(count, top_data, bottom[i]->cpu_data(), top_data);
} //将所有输入的bottom做element-wise乘积
break;
case EltwiseParameter_EltwiseOp_SUM:
caffe_set(count, Dtype(0), top_data);
// TODO(shelhamer) does BLAS optimize to sum for coeff = 1?
for (int i = 0; i < bottom.size(); ++i) {
caffe_axpy(count, coeffs_[i], bottom[i]->cpu_data(), top_data);
}// 输出top 为 所有输入bottom乘以对应系数之和
break;
case EltwiseParameter_EltwiseOp_MAX:
// Initialize
mask = max_idx_.mutable_cpu_data();
caffe_set(count, -1, mask);//初始化mask所有elements 为-1
caffe_set(count, Dtype(-FLT_MAX), top_data);
// 初始化top所有element 为负无穷
// bottom 0 & 1
bottom_data_a = bottom[0]->cpu_data();
bottom_data_b = bottom[1]->cpu_data();
for (int idx = 0; idx < count; ++idx) {
if (bottom_data_a[idx] > bottom_data_b[idx]) {
top_data[idx] = bottom_data_a[idx]; // maxval
mask[idx] = 0; // maxid
} else {
top_data[idx] = bottom_data_b[idx]; // maxval
mask[idx] = 1; // maxid
}
}//bottom 0 和bottom 1 做比较
// bottom 2++
for (int blob_idx = 2; blob_idx < bottom.size(); ++blob_idx) {
bottom_data_b = bottom[blob_idx]->cpu_data();
for (int idx = 0; idx < count; ++idx) {
if (bottom_data_b[idx] > top_data[idx]) {
top_data[idx] = bottom_data_b[idx]; // maxval
mask[idx] = blob_idx; // maxid 更新为当前最大值的bottom id
}//
}
}
break;
default:
LOG(FATAL) << "Unknown elementwise operation.";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
4.Backward_cpu 函数:
template <typename Dtype>
void EltwiseLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
const int* mask = NULL;
const int count = top[0]->count();
const Dtype* top_data = top[0]->cpu_data();
const Dtype* top_diff = top[0]->cpu_diff();
for (int i = 0; i < bottom.size(); ++i) {
if (propagate_down[i]) {
const Dtype* bottom_data = bottom[i]->cpu_data();
Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();
switch (op_) {
case EltwiseParameter_EltwiseOp_PROD:
if (stable_prod_grad_) {
bool initialized = false;
for (int j = 0; j < bottom.size(); ++j) {
if (i == j) { continue; }
if (!initialized) {
caffe_copy(count, bottom[j]->cpu_data(), bottom_diff);
initialized = true;
} else {
caffe_mul(count, bottom[j]->cpu_data(), bottom_diff,
bottom_diff);
}//对于bottom[i]导数为除了自己外输入bottom的element-wise 乘积
}
} else {5
caffe_div(count, top_data, bottom_data, bottom_diff);
}
caffe_mul(count, bottom_diff, top_diff, bottom_diff);
//乘以上一层传下来的导数top_diff
break;
case EltwiseParameter_EltwiseOp_SUM:
if (coeffs_[i] == Dtype(1)) {
caffe_copy(count, top_diff, bottom_diff);
} else {
caffe_cpu_scale(count, coeffs_[i], top_diff, bottom_diff);
}
break;
case EltwiseParameter_EltwiseOp_MAX:
mask = max_idx_.cpu_data();
for (int index = 0; index < count; ++index) {
Dtype gradient = 0;
if (mask[index] == i) {
gradient += top_diff[index];
}
bottom_diff[index] = gradient;
}//当该bottom的index值取为最大时,继承top_diff在该index的值,其他位置为0;
break;
default:
LOG(FATAL) << "Unknown elementwise operation.";
}
}
}
}