From ffedfa6bd92ed982cfbfbb7335c79c3eee4bcd3f Mon Sep 17 00:00:00 2001 From: Evan Shelhamer Date: Wed, 21 May 2014 18:38:19 -0700 Subject: [PATCH] EltwiseProductLayer -> EltwiseLayer for generality Reproduce elementwise product layer in more generality. Add elementwise operation parameter. Prepare for elementwise sum operation choice. --- include/caffe/vision_layers.hpp | 10 +++-- src/caffe/layer_factory.cpp | 4 +- ...eltwise_product_layer.cpp => eltwise_layer.cpp} | 31 ++++++++++----- .../{eltwise_product_layer.cu => eltwise_layer.cu} | 28 ++++++++++---- src/caffe/layers/lrn_layer.cpp | 4 +- src/caffe/proto/caffe.proto | 13 ++++++- ...se_product_layer.cpp => test_eltwise_layer.cpp} | 44 +++++++++++++--------- 7 files changed, 91 insertions(+), 43 deletions(-) rename src/caffe/layers/{eltwise_product_layer.cpp => eltwise_layer.cpp} (63%) rename src/caffe/layers/{eltwise_product_layer.cu => eltwise_layer.cu} (52%) rename src/caffe/test/{test_eltwise_product_layer.cpp => test_eltwise_layer.cpp} (69%) diff --git a/include/caffe/vision_layers.hpp b/include/caffe/vision_layers.hpp index 29daf09..1b5da11 100644 --- a/include/caffe/vision_layers.hpp +++ b/include/caffe/vision_layers.hpp @@ -112,12 +112,12 @@ class ConvolutionLayer : public Layer { int N_; }; -/* EltwiseProductLayer +/* EltwiseLayer */ template -class EltwiseProductLayer : public Layer { +class EltwiseLayer : public Layer { public: - explicit EltwiseProductLayer(const LayerParameter& param) + explicit EltwiseLayer(const LayerParameter& param) : Layer(param) {} virtual void SetUp(const vector*>& bottom, vector*>* top); @@ -131,6 +131,8 @@ class EltwiseProductLayer : public Layer { const bool propagate_down, vector*>* bottom); virtual void Backward_gpu(const vector*>& top, const bool propagate_down, vector*>* bottom); + + EltwiseParameter_EltwiseOp op_; }; /* FlattenLayer @@ -276,7 +278,7 @@ class LRNLayer : public Layer { shared_ptr > power_layer_; Blob power_output_; vector*> power_top_vec_; - shared_ptr > product_layer_; + shared_ptr > product_layer_; Blob product_data_input_; vector*> product_bottom_vec_; }; diff --git a/src/caffe/layer_factory.cpp b/src/caffe/layer_factory.cpp index ae15ba5..58c20b1 100644 --- a/src/caffe/layer_factory.cpp +++ b/src/caffe/layer_factory.cpp @@ -38,8 +38,8 @@ Layer* GetLayer(const LayerParameter& param) { return new DropoutLayer(param); case LayerParameter_LayerType_EUCLIDEAN_LOSS: return new EuclideanLossLayer(param); - case LayerParameter_LayerType_ELTWISE_PRODUCT: - return new EltwiseProductLayer(param); + case LayerParameter_LayerType_ELTWISE: + return new EltwiseLayer(param); case LayerParameter_LayerType_FLATTEN: return new FlattenLayer(param); case LayerParameter_LayerType_HDF5_DATA: diff --git a/src/caffe/layers/eltwise_product_layer.cpp b/src/caffe/layers/eltwise_layer.cpp similarity index 63% rename from src/caffe/layers/eltwise_product_layer.cpp rename to src/caffe/layers/eltwise_layer.cpp index b394450..dd46618 100644 --- a/src/caffe/layers/eltwise_product_layer.cpp +++ b/src/caffe/layers/eltwise_layer.cpp @@ -9,7 +9,7 @@ namespace caffe { template -void EltwiseProductLayer::SetUp(const vector*>& bottom, +void EltwiseLayer::SetUp(const vector*>& bottom, vector*>* top) { CHECK_GE(bottom.size(), 2) << "Eltwise Product Layer takes at least 2 blobs as input."; @@ -26,22 +26,29 @@ void EltwiseProductLayer::SetUp(const vector*>& bottom, CHECK_EQ(width, bottom[i]->width()); } (*top)[0]->Reshape(num, channels, height, width); + op_ = this->layer_param_.eltwise_param().operation(); } template -Dtype EltwiseProductLayer::Forward_cpu( +Dtype EltwiseLayer::Forward_cpu( const vector*>& bottom, vector*>* 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 -void EltwiseProductLayer::Backward_cpu(const vector*>& top, +void EltwiseLayer::Backward_cpu(const vector*>& top, const bool propagate_down, vector*>* bottom) { if (propagate_down) { const int count = top[0]->count(); @@ -50,13 +57,19 @@ void EltwiseProductLayer::Backward_cpu(const vector*>& 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 diff --git a/src/caffe/layers/eltwise_product_layer.cu b/src/caffe/layers/eltwise_layer.cu similarity index 52% rename from src/caffe/layers/eltwise_product_layer.cu rename to src/caffe/layers/eltwise_layer.cu index 9c66033..9072f88 100644 --- a/src/caffe/layers/eltwise_product_layer.cu +++ b/src/caffe/layers/eltwise_layer.cu @@ -9,19 +9,25 @@ namespace caffe { template -Dtype EltwiseProductLayer::Forward_gpu( +Dtype EltwiseLayer::Forward_gpu( const vector*>& bottom, vector*>* 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 -void EltwiseProductLayer::Backward_gpu(const vector*>& top, +void EltwiseLayer::Backward_gpu(const vector*>& top, const bool propagate_down, vector*>* bottom) { if (propagate_down) { const int count = top[0]->count(); @@ -30,13 +36,19 @@ void EltwiseProductLayer::Backward_gpu(const vector*>& 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 diff --git a/src/caffe/layers/lrn_layer.cpp b/src/caffe/layers/lrn_layer.cpp index cfcc59c..6f7af75 100644 --- a/src/caffe/layers/lrn_layer.cpp +++ b/src/caffe/layers/lrn_layer.cpp @@ -85,7 +85,9 @@ void LRNLayer::SetUp(const vector*>& bottom, product_bottom_vec_.push_back(bottom[0]); product_bottom_vec_.push_back(&power_output_); LayerParameter product_param; - product_layer_.reset(new EltwiseProductLayer(product_param)); + EltwiseParameter* eltwise_param = product_param.mutable_eltwise_param(); + eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); + product_layer_.reset(new EltwiseLayer(product_param)); product_layer_->SetUp(product_bottom_vec_, top); CHECK_EQ((*top)[0]->num(), num_); CHECK_EQ((*top)[0]->channels(), channels_); diff --git a/src/caffe/proto/caffe.proto b/src/caffe/proto/caffe.proto index 2d9a1aa..a0a855e 100644 --- a/src/caffe/proto/caffe.proto +++ b/src/caffe/proto/caffe.proto @@ -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. diff --git a/src/caffe/test/test_eltwise_product_layer.cpp b/src/caffe/test/test_eltwise_layer.cpp 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 --- a/src/caffe/test/test_eltwise_product_layer.cpp +++ b/src/caffe/test/test_eltwise_layer.cpp @@ -17,9 +17,9 @@ namespace caffe { extern cudaDeviceProp CAFFE_TEST_CUDA_PROP; template -class EltwiseProductLayerTest : public ::testing::Test { +class EltwiseLayerTest : public ::testing::Test { protected: - EltwiseProductLayerTest() + EltwiseLayerTest() : blob_bottom_a_(new Blob(2, 3, 4, 5)), blob_bottom_b_(new Blob(2, 3, 4, 5)), blob_bottom_c_(new Blob(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 Dtypes; -TYPED_TEST_CASE(EltwiseProductLayerTest, Dtypes); +TYPED_TEST_CASE(EltwiseLayerTest, Dtypes); -TYPED_TEST(EltwiseProductLayerTest, TestSetUp) { +TYPED_TEST(EltwiseLayerTest, TestSetUp) { LayerParameter layer_param; - shared_ptr > layer( - new EltwiseProductLayer(layer_param)); + EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param(); + eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); + shared_ptr > layer( + new EltwiseLayer(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 > layer( - new EltwiseProductLayer(layer_param)); + EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param(); + eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); + shared_ptr > layer( + new EltwiseLayer(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 > layer( - new EltwiseProductLayer(layer_param)); + EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param(); + eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); + shared_ptr > layer( + new EltwiseLayer(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 layer(layer_param); + EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param(); + eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); + EltwiseLayer layer(layer_param); GradientChecker 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 layer(layer_param); + EltwiseParameter* eltwise_param = layer_param.mutable_eltwise_param(); + eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); + EltwiseLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), &(this->blob_top_vec_)); -- 2.7.4