EltwiseProductLayer -> EltwiseLayer for generality
authorEvan Shelhamer <shelhamer@imaginarynumber.net>
Thu, 22 May 2014 01:38:19 +0000 (18:38 -0700)
committerEvan Shelhamer <shelhamer@imaginarynumber.net>
Thu, 22 May 2014 02:48:29 +0000 (19:48 -0700)
Reproduce elementwise product layer in more generality.
Add elementwise operation parameter.
Prepare for elementwise sum operation choice.

include/caffe/vision_layers.hpp
src/caffe/layer_factory.cpp
src/caffe/layers/eltwise_layer.cpp [moved from src/caffe/layers/eltwise_product_layer.cpp with 63% similarity]
src/caffe/layers/eltwise_layer.cu [moved from src/caffe/layers/eltwise_product_layer.cu with 52% similarity]
src/caffe/layers/lrn_layer.cpp
src/caffe/proto/caffe.proto
src/caffe/test/test_eltwise_layer.cpp [moved from src/caffe/test/test_eltwise_product_layer.cpp with 69% similarity]

index 29daf09..1b5da11 100644 (file)
@@ -112,12 +112,12 @@ class ConvolutionLayer : public Layer<Dtype> {
   int N_;
 };
 
-/* EltwiseProductLayer
+/* EltwiseLayer
 */
 template <typename Dtype>
-class EltwiseProductLayer : public Layer<Dtype> {
+class EltwiseLayer : public Layer<Dtype> {
  public:
-  explicit EltwiseProductLayer(const LayerParameter& param)
+  explicit EltwiseLayer(const LayerParameter& param)
       : Layer<Dtype>(param) {}
   virtual void SetUp(const vector<Blob<Dtype>*>& bottom,
       vector<Blob<Dtype>*>* top);
@@ -131,6 +131,8 @@ class EltwiseProductLayer : public Layer<Dtype> {
       const bool propagate_down, vector<Blob<Dtype>*>* bottom);
   virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
       const bool propagate_down, vector<Blob<Dtype>*>* bottom);
+
+  EltwiseParameter_EltwiseOp op_;
 };
 
 /* FlattenLayer
@@ -276,7 +278,7 @@ class LRNLayer : public Layer<Dtype> {
   shared_ptr<PowerLayer<Dtype> > power_layer_;
   Blob<Dtype> power_output_;
   vector<Blob<Dtype>*> power_top_vec_;
-  shared_ptr<EltwiseProductLayer<Dtype> > product_layer_;
+  shared_ptr<EltwiseLayer<Dtype> > product_layer_;
   Blob<Dtype> product_data_input_;
   vector<Blob<Dtype>*> product_bottom_vec_;
 };
index ae15ba5..58c20b1 100644 (file)
@@ -38,8 +38,8 @@ Layer<Dtype>* GetLayer(const LayerParameter& param) {
     return new DropoutLayer<Dtype>(param);
   case LayerParameter_LayerType_EUCLIDEAN_LOSS:
     return new EuclideanLossLayer<Dtype>(param);
-  case LayerParameter_LayerType_ELTWISE_PRODUCT:
-    return new EltwiseProductLayer<Dtype>(param);
+  case LayerParameter_LayerType_ELTWISE:
+    return new EltwiseLayer<Dtype>(param);
   case LayerParameter_LayerType_FLATTEN:
     return new FlattenLayer<Dtype>(param);
   case LayerParameter_LayerType_HDF5_DATA:
similarity index 63%
rename from src/caffe/layers/eltwise_product_layer.cpp
rename to src/caffe/layers/eltwise_layer.cpp
index b394450..dd46618 100644 (file)
@@ -9,7 +9,7 @@
 namespace caffe {
 
 template <typename Dtype>
-void EltwiseProductLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
+void EltwiseLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
       vector<Blob<Dtype>*>* top) {
   CHECK_GE(bottom.size(), 2) <<
       "Eltwise Product Layer takes at least 2 blobs as input.";
@@ -26,22 +26,29 @@ void EltwiseProductLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
     CHECK_EQ(width, bottom[i]->width());
   }
   (*top)[0]->Reshape(num, channels, height, width);
+  op_ = this->layer_param_.eltwise_param().operation();
 }
 
 template <typename Dtype>
-Dtype EltwiseProductLayer<Dtype>::Forward_cpu(
+Dtype EltwiseLayer<Dtype>::Forward_cpu(
     const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
   const int count = (*top)[0]->count();
   Dtype* top_data = (*top)[0]->mutable_cpu_data();
-  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);
+  switch (op_) {
+  case EltwiseParameter_EltwiseOp_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;
+  default:
+    LOG(FATAL) << "Unknown elementwise operation.";
   }
   return Dtype(0.);
 }
 
 template <typename Dtype>
-void EltwiseProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
+void EltwiseLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
     const bool propagate_down, vector<Blob<Dtype>*>* bottom) {
   if (propagate_down) {
     const int count = top[0]->count();
@@ -50,13 +57,19 @@ void EltwiseProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
     for (int i = 0; i < bottom->size(); ++i) {
       const Dtype* bottom_data = (*bottom)[i]->cpu_data();
       Dtype* bottom_diff = (*bottom)[i]->mutable_cpu_diff();
-      caffe_div(count, top_data, bottom_data, bottom_diff);
-      caffe_mul(count, bottom_diff, top_diff, bottom_diff);
+      switch (op_) {
+      case EltwiseParameter_EltwiseOp_PROD:
+        caffe_div(count, top_data, bottom_data, bottom_diff);
+        caffe_mul(count, bottom_diff, top_diff, bottom_diff);
+        break;
+      default:
+        LOG(FATAL) << "Unknown elementwise operation.";
+      }
     }
   }
 }
 
-INSTANTIATE_CLASS(EltwiseProductLayer);
+INSTANTIATE_CLASS(EltwiseLayer);
 
 
 }  // namespace caffe
similarity index 52%
rename from src/caffe/layers/eltwise_product_layer.cu
rename to src/caffe/layers/eltwise_layer.cu
index 9c66033..9072f88 100644 (file)
@@ -9,19 +9,25 @@
 namespace caffe {
 
 template <typename Dtype>
-Dtype EltwiseProductLayer<Dtype>::Forward_gpu(
+Dtype EltwiseLayer<Dtype>::Forward_gpu(
     const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
   const int count = (*top)[0]->count();
   Dtype* top_data = (*top)[0]->mutable_gpu_data();
-  caffe_gpu_mul(count, bottom[0]->gpu_data(), bottom[1]->gpu_data(), top_data);
-  for (int i = 2; i < bottom.size(); ++i) {
-    caffe_gpu_mul(count, top_data, bottom[i]->gpu_data(), top_data);
+  switch (op_) {
+  case EltwiseParameter_EltwiseOp_PROD:
+    caffe_gpu_mul(count, bottom[0]->gpu_data(), bottom[1]->gpu_data(), top_data);
+    for (int i = 2; i < bottom.size(); ++i) {
+      caffe_gpu_mul(count, top_data, bottom[i]->gpu_data(), top_data);
+    }
+    break;
+  default:
+    LOG(FATAL) << "Unknown elementwise operation.";
   }
   return Dtype(0.);
 }
 
 template <typename Dtype>
-void EltwiseProductLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
+void EltwiseLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
     const bool propagate_down, vector<Blob<Dtype>*>* bottom) {
   if (propagate_down) {
     const int count = top[0]->count();
@@ -30,13 +36,19 @@ void EltwiseProductLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
     for (int i = 0; i < bottom->size(); ++i) {
       const Dtype* bottom_data = (*bottom)[i]->gpu_data();
       Dtype* bottom_diff = (*bottom)[i]->mutable_gpu_diff();
-      caffe_gpu_div(count, top_data, bottom_data, bottom_diff);
-      caffe_gpu_mul(count, bottom_diff, top_diff, bottom_diff);
+      switch (op_) {
+      case EltwiseParameter_EltwiseOp_PROD:
+        caffe_gpu_div(count, top_data, bottom_data, bottom_diff);
+        caffe_gpu_mul(count, bottom_diff, top_diff, bottom_diff);
+        break;
+      default:
+        LOG(FATAL) << "Unknown elementwise operation.";
+      }
     }
   }
 }
 
-INSTANTIATE_CLASS(EltwiseProductLayer);
+INSTANTIATE_CLASS(EltwiseLayer);
 
 
 }  // namespace caffe
index cfcc59c..6f7af75 100644 (file)
@@ -85,7 +85,9 @@ void LRNLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
       product_bottom_vec_.push_back(bottom[0]);
       product_bottom_vec_.push_back(&power_output_);
       LayerParameter product_param;
-      product_layer_.reset(new EltwiseProductLayer<Dtype>(product_param));
+      EltwiseParameter* eltwise_param = product_param.mutable_eltwise_param();
+      eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);
+      product_layer_.reset(new EltwiseLayer<Dtype>(product_param));
       product_layer_->SetUp(product_bottom_vec_, top);
       CHECK_EQ((*top)[0]->num(), num_);
       CHECK_EQ((*top)[0]->channels(), channels_);
index 2d9a1aa..a0a855e 100644 (file)
@@ -115,7 +115,7 @@ message SolverState {
 // NOTE
 // Update the next available ID when you add a new LayerParameter field.
 //
-// LayerParameter next available ID: 24 (last added: argmax_param)
+// LayerParameter next available ID: 25 (last added: eltwise_param)
 message LayerParameter {
   repeated string bottom = 2; // the name of the bottom blobs
   repeated string top = 3; // the name of the top blobs
@@ -141,7 +141,7 @@ message LayerParameter {
     DATA = 5;
     DROPOUT = 6;
     EUCLIDEAN_LOSS = 7;
-    ELTWISE_PRODUCT = 25;
+    ELTWISE = 25;
     FLATTEN = 8;
     HDF5_DATA = 9;
     HDF5_OUTPUT = 10;
@@ -180,6 +180,7 @@ message LayerParameter {
   optional ConvolutionParameter convolution_param = 10;
   optional DataParameter data_param = 11;
   optional DropoutParameter dropout_param = 12;
+  optional EltwiseParameter eltwise_param = 24;
   optional HDF5DataParameter hdf5_data_param = 13;
   optional HDF5OutputParameter hdf5_output_param = 14;
   optional ImageDataParameter image_data_param = 15;
@@ -250,6 +251,14 @@ message DropoutParameter {
   optional float dropout_ratio = 1 [default = 0.5]; // dropout ratio
 }
 
+// Message that stores parameters used by EltwiseLayer
+message EltwiseParameter {
+  enum EltwiseOp {
+    PROD = 0;
+  }
+  optional EltwiseOp operation = 1; // element-wise operation to compute
+}
+
 // Message that stores parameters used by HDF5DataLayer
 message HDF5DataParameter {
   // Specify the data source.
similarity index 69%
rename from src/caffe/test/test_eltwise_product_layer.cpp
rename to src/caffe/test/test_eltwise_layer.cpp
index 86d6fdc..86e4751 100644 (file)
@@ -17,9 +17,9 @@ namespace caffe {
 extern cudaDeviceProp CAFFE_TEST_CUDA_PROP;
 
 template <typename Dtype>
-class EltwiseProductLayerTest : public ::testing::Test {
+class EltwiseLayerTest : public ::testing::Test {
  protected:
-  EltwiseProductLayerTest()
+  EltwiseLayerTest()
       : blob_bottom_a_(new Blob<Dtype>(2, 3, 4, 5)),
         blob_bottom_b_(new Blob<Dtype>(2, 3, 4, 5)),
         blob_bottom_c_(new Blob<Dtype>(2, 3, 4, 5)),
@@ -35,7 +35,7 @@ class EltwiseProductLayerTest : public ::testing::Test {
     blob_bottom_vec_.push_back(blob_bottom_c_);
     blob_top_vec_.push_back(blob_top_);
   }
-  virtual ~EltwiseProductLayerTest() {
+  virtual ~EltwiseLayerTest() {
     delete blob_bottom_a_;
     delete blob_bottom_b_;
     delete blob_bottom_c_;
@@ -50,12 +50,14 @@ class EltwiseProductLayerTest : public ::testing::Test {
 };
 
 typedef ::testing::Types<float, double> Dtypes;
-TYPED_TEST_CASE(EltwiseProductLayerTest, Dtypes);
+TYPED_TEST_CASE(EltwiseLayerTest, Dtypes);
 
-TYPED_TEST(EltwiseProductLayerTest, TestSetUp) {
+TYPED_TEST(EltwiseLayerTest, TestSetUp) {
   LayerParameter layer_param;
-  shared_ptr<EltwiseProductLayer<TypeParam> > layer(
-      new EltwiseProductLayer<TypeParam>(layer_param));
+  EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param();
+  eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);
+  shared_ptr<EltwiseLayer<TypeParam> > layer(
+      new EltwiseLayer<TypeParam>(layer_param));
   layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_));
   EXPECT_EQ(this->blob_top_->num(), 2);
   EXPECT_EQ(this->blob_top_->channels(), 3);
@@ -63,11 +65,13 @@ TYPED_TEST(EltwiseProductLayerTest, TestSetUp) {
   EXPECT_EQ(this->blob_top_->width(), 5);
 }
 
-TYPED_TEST(EltwiseProductLayerTest, TestCPU) {
+TYPED_TEST(EltwiseLayerTest, TestCPU) {
   Caffe::set_mode(Caffe::CPU);
   LayerParameter layer_param;
-  shared_ptr<EltwiseProductLayer<TypeParam> > layer(
-      new EltwiseProductLayer<TypeParam>(layer_param));
+  EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param();
+  eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);
+  shared_ptr<EltwiseLayer<TypeParam> > layer(
+      new EltwiseLayer<TypeParam>(layer_param));
   layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_));
   layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_));
   const TypeParam* data = this->blob_top_->cpu_data();
@@ -80,11 +84,13 @@ TYPED_TEST(EltwiseProductLayerTest, TestCPU) {
   }
 }
 
-TYPED_TEST(EltwiseProductLayerTest, TestGPU) {
+TYPED_TEST(EltwiseLayerTest, TestGPU) {
   Caffe::set_mode(Caffe::GPU);
   LayerParameter layer_param;
-  shared_ptr<EltwiseProductLayer<TypeParam> > layer(
-      new EltwiseProductLayer<TypeParam>(layer_param));
+  EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param();
+  eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);
+  shared_ptr<EltwiseLayer<TypeParam> > layer(
+      new EltwiseLayer<TypeParam>(layer_param));
   layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_));
   layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_));
   const TypeParam* data = this->blob_top_->cpu_data();
@@ -97,19 +103,23 @@ TYPED_TEST(EltwiseProductLayerTest, TestGPU) {
   }
 }
 
-TYPED_TEST(EltwiseProductLayerTest, TestCPUGradient) {
+TYPED_TEST(EltwiseLayerTest, TestCPUGradient) {
   Caffe::set_mode(Caffe::CPU);
   LayerParameter layer_param;
-  EltwiseProductLayer<TypeParam> layer(layer_param);
+  EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param();
+  eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);
+  EltwiseLayer<TypeParam> layer(layer_param);
   GradientChecker<TypeParam> checker(1e-2, 1e-3);
   checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_),
       &(this->blob_top_vec_));
 }
 
-TYPED_TEST(EltwiseProductLayerTest, TestGPUGradient) {
+TYPED_TEST(EltwiseLayerTest, TestGPUGradient) {
   Caffe::set_mode(Caffe::GPU);
   LayerParameter layer_param;
-  EltwiseProductLayer<TypeParam> layer(layer_param);
+  EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param();
+  eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);
+  EltwiseLayer<TypeParam> layer(layer_param);
   GradientChecker<TypeParam> checker(1e-2, 1e-2);
   checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_),
       &(this->blob_top_vec_));