Skip to content

Commit a123334

Browse files
committed
add div layer with broadcasting
1 parent 6e254ae commit a123334

File tree

5 files changed

+244
-0
lines changed

5 files changed

+244
-0
lines changed

FEATURES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ atan_layer
1010
batch_to_space_nd_layer
1111
crop_and_resize
1212
depth_to_space_layer
13+
div_layer
1314
expand_dims_nd_layer
1415
gather_layer
1516
gather_nd_layer

include/caffe/layers/div_layer.hpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef CAFFE_DIV_LAYER_HPP_
2+
#define CAFFE_DIV_LAYER_HPP_
3+
4+
#include <vector>
5+
6+
#include "caffe/blob.hpp"
7+
#include "caffe/layer.hpp"
8+
#include "caffe/proto/caffe.pb.h"
9+
10+
namespace caffe {
11+
12+
/*
13+
* @brief Compute elementwise div. Support broadcasting as numpy and TensorFlow do.
14+
*
15+
* Note: Two dimensions are compatible for broadcasting if both are the same or either is 1.
16+
* The rule starts with the right-most dimension, and works towards the left-most dimension.
17+
*/
18+
19+
template <typename Dtype>
20+
class DivLayer : public Layer<Dtype> {
21+
public:
22+
explicit DivLayer(const LayerParameter& param)
23+
: Layer<Dtype>(param) {}
24+
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
25+
const vector<Blob<Dtype>*>& top);
26+
27+
virtual inline const char* type() const { return "Div"; }
28+
virtual inline int MinBottomBlobs() const { return 1; }
29+
virtual inline int ExactNumTopBlobs() const { return 1; }
30+
31+
protected:
32+
33+
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
34+
const vector<Blob<Dtype>*>& top);
35+
36+
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
37+
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom){
38+
NOT_IMPLEMENTED;
39+
};
40+
41+
int dim_diff_;
42+
int dim_;
43+
bool is_scalar_;
44+
};
45+
46+
} // namespace caffe
47+
48+
#endif // CAFFE_DIV_LAYER_HPP_

include/caffe/util/math_functions.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ inline void caffe_memset(const size_t N, const int alpha, void* X) {
4646
template <typename Dtype>
4747
void caffe_add_scalar(const int N, const Dtype alpha, Dtype *X);
4848

49+
template <typename Dtype>
50+
void caffe_div_scalar(const int N, const Dtype alpha, Dtype *X);
51+
4952
template <typename Dtype>
5053
void caffe_scal(const int N, const Dtype alpha, Dtype *X);
5154

src/caffe/layers/div_layer.cpp

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#include <algorithm>
2+
#include <functional>
3+
#include <utility>
4+
#include <vector>
5+
6+
#include "caffe/layers/div_layer.hpp"
7+
8+
namespace caffe {
9+
template <typename Dtype>
10+
void DivLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
11+
const vector<Blob<Dtype>*>& top) {
12+
CHECK_NE(top[0], bottom[0]) << this->type() << " Layer does not allow in-place computation.";
13+
is_scalar_=false;
14+
Blob<Dtype>* divisor = (bottom.size() > 1) ? bottom[1] : this->blobs_[0].get();
15+
16+
if(bottom[0]->num_axes()==0 || divisor->num_axes()==0)
17+
is_scalar_=true;
18+
dim_diff_ = bottom[0]->num_axes() - divisor->num_axes();
19+
dim_ = bottom[0]->num_axes() >= divisor->num_axes() ? bottom[0]->num_axes() : divisor->num_axes();
20+
vector<int> top_shape(dim_, 1);
21+
if(dim_diff_ == 0)
22+
{
23+
if(!is_scalar_)
24+
{
25+
for(int i=0;i<dim_;i++)
26+
{
27+
CHECK(bottom[0]->shape(i)==divisor->shape(i) || bottom[0]->shape(i)==1 || divisor->shape(i)==1)
28+
<< "Dimensions must be equal or 1 in the bottoms!";
29+
top_shape[i] = bottom[0]->shape(i) >= divisor->shape(i) ? bottom[0]->shape(i): divisor->shape(i);
30+
}
31+
}
32+
}
33+
else if(dim_diff_ > 0) //bottom0 has more axes than bottom1
34+
{
35+
if(!is_scalar_)
36+
{
37+
for(int i=0;i<dim_diff_;i++)
38+
top_shape[i] = bottom[0]->shape(i);
39+
for(int i=dim_diff_; i<dim_; i++)
40+
top_shape[i] = bottom[0]->shape(i) >= divisor->shape(i-dim_diff_) ? bottom[0]->shape(i): divisor->shape(i-dim_diff_);
41+
}
42+
else //bottom1 is a scalar
43+
{
44+
for(int i=0;i<dim_;i++)
45+
top_shape[i] = bottom[0]->shape(i);
46+
}
47+
}
48+
else //dim_diff_<0, bottom1 has more axes than bottom0
49+
{
50+
if(!is_scalar_)
51+
{
52+
for(int i=0;i<-dim_diff_;i++)
53+
top_shape[i] = divisor->shape(i);
54+
for(int i=-dim_diff_; i<dim_; i++)
55+
top_shape[i] = bottom[0]->shape(i+dim_diff_) >= divisor->shape(i) ? bottom[0]->shape(i+dim_diff_): divisor->shape(i);
56+
}
57+
else //bottom0 is a scalar
58+
{
59+
for(int i=0;i<dim_;i++)
60+
top_shape[i] = divisor->shape(i);
61+
}
62+
}
63+
64+
top[0]->Reshape(top_shape);
65+
}
66+
67+
template <typename Dtype>
68+
void DivLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
69+
const vector<Blob<Dtype>*>& top) {
70+
const Dtype* bottom0_data = bottom[0]->cpu_data();
71+
Blob<Dtype>* divisor = (bottom.size() > 1) ? bottom[1] : this->blobs_[0].get();
72+
const Dtype* bottom1_data = divisor->cpu_data();
73+
Dtype* top_data = top[0]->mutable_cpu_data();
74+
int count = top[0]->count();
75+
76+
// Assume top index (x,y,z) with top shape (A, B, C)
77+
// top offset d = xBC + yC + z
78+
// So to count the bottom index, should first figure out x, y, z
79+
// x = d / BC
80+
// y = (d % BC) / C
81+
// z = d % C
82+
// Then consider bottom shape (A', B', C'), while A = A' or 1
83+
// So bottom offset = x'B'C' + y'C' + z', while x' = x or 0
84+
if(!is_scalar_)
85+
{
86+
for(int d=0; d<count; d++)
87+
{
88+
int offset0 = 0;
89+
int offset1 = 0;
90+
91+
if(dim_diff_ == 0)
92+
{
93+
for(int i=0;i<dim_-1;i++)
94+
{
95+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
96+
int n0 = 1 == bottom[0]->shape(i) ? 0 : num;
97+
int n1 = 1 == divisor->shape(i) ? 0 : num;
98+
offset0 += n0 * bottom[0]->count(i+1);
99+
offset1 += n1 * divisor->count(i+1);
100+
}
101+
int z = d % top[0]->shape(dim_-1);
102+
int z0 = 1 == bottom[0]->shape(dim_-1) ? 0 : z;
103+
int z1 = 1 == divisor->shape(dim_-1) ? 0 : z;
104+
offset0 += z0;
105+
offset1 += z1;
106+
}
107+
else if(dim_diff_ > 0) //bottom0 has more axes than bottom1
108+
{
109+
for(int i=0;i<dim_diff_;i++)
110+
{
111+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
112+
int n0 = 1 == bottom[0]->shape(i) ? 0 : num;
113+
offset0 += n0 * bottom[0]->count(i+1);
114+
}
115+
for(int i=dim_diff_;i<dim_-1;i++)
116+
{
117+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
118+
int n0 = 1 == bottom[0]->shape(i) ? 0 : num;
119+
int n1 = 1 == divisor->shape(i-dim_diff_) ? 0 : num;
120+
offset0 += n0 * bottom[0]->count(i+1);
121+
offset1 += n1 * divisor->count(i-dim_diff_+1);
122+
}
123+
int z = d % top[0]->shape(dim_-1);
124+
int z0 = 1 == bottom[0]->shape(dim_-1) ? 0 : z;
125+
int z1 = 1 == divisor->shape(dim_-dim_diff_-1) ? 0 : z;
126+
offset0 += z0;
127+
offset1 += z1;
128+
}
129+
else //dim_diff_<0, bottom1 has more axes than bottom0
130+
{
131+
for(int i=0;i<-dim_diff_;i++)
132+
{
133+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
134+
int n1 = 1 == divisor->shape(i) ? 0 : num;
135+
offset1 += n1 * divisor->count(i+1);
136+
}
137+
for(int i=-dim_diff_;i<dim_-1;i++)
138+
{
139+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
140+
int n0 = 1 == bottom[0]->shape(i+dim_diff_) ? 0 : num;
141+
int n1 = 1 == divisor->shape(i) ? 0 : num;
142+
offset0 += n0 * bottom[0]->count(i+dim_diff_+1);
143+
offset1 += n1 * divisor->count(i+1);
144+
}
145+
int z = d % top[0]->shape(dim_-1);
146+
int z0 = 1 == bottom[0]->shape(dim_+dim_diff_-1) ? 0 : z;
147+
int z1 = 1 == divisor->shape(dim_-1) ? 0 : z;
148+
offset0 += z0;
149+
offset1 += z1;
150+
}
151+
152+
top_data[d] = bottom0_data[offset0] / bottom1_data[offset1];
153+
}
154+
}
155+
else //is scalar with shape ()
156+
{
157+
if(divisor->num_axes()==0) //bottom1 is a scalar
158+
{
159+
caffe_copy(count, bottom0_data, top_data);
160+
Dtype scalar = bottom1_data[0];
161+
caffe_div_scalar(count, scalar, top_data);
162+
}
163+
else //bottom0 is a scalar
164+
{
165+
Dtype scalar = bottom0_data[0];
166+
for(int d=0; d<count; d++)
167+
{
168+
top_data[d] = scalar / bottom1_data[d];
169+
}
170+
}
171+
}
172+
}
173+
174+
175+
INSTANTIATE_CLASS(DivLayer);
176+
REGISTER_LAYER_CLASS(Div);
177+
178+
} // namespace caffe

src/caffe/util/math_functions.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ void caffe_add_scalar(const int N, const double alpha, double* Y) {
8282
}
8383
}
8484

85+
template <>
86+
void caffe_div_scalar(const int N, const float alpha, float* Y) {
87+
for (int i = 0; i < N; ++i) {
88+
Y[i] /= alpha;
89+
}
90+
}
91+
92+
template <>
93+
void caffe_div_scalar(const int N, const double alpha, double* Y) {
94+
for (int i = 0; i < N; ++i) {
95+
Y[i] /= alpha;
96+
}
97+
}
98+
8599
template <typename Dtype>
86100
void caffe_copy(const int N, const Dtype* X, Dtype* Y) {
87101
if (X != Y) {

0 commit comments

Comments
 (0)