From d1a0345eaaff501d11a1705986cac8f124dd2545 Mon Sep 17 00:00:00 2001 From: Jeff Donahue Date: Tue, 7 Oct 2014 11:55:54 -0700 Subject: [PATCH 1/2] SliceLayer: allow trivial operation with single top Blob --- include/caffe/common_layers.hpp | 2 +- src/caffe/layers/slice_layer.cpp | 7 ++++++- src/caffe/layers/slice_layer.cu | 3 ++- src/caffe/test/test_slice_layer.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/include/caffe/common_layers.hpp b/include/caffe/common_layers.hpp index 8e64b3e5dc5..6d4a9e3cb81 100644 --- a/include/caffe/common_layers.hpp +++ b/include/caffe/common_layers.hpp @@ -625,7 +625,7 @@ class SliceLayer : public Layer { virtual inline const char* type() const { return "Slice"; } virtual inline int ExactNumBottomBlobs() const { return 1; } - virtual inline int MinTopBlobs() const { return 2; } + virtual inline int MinTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, diff --git a/src/caffe/layers/slice_layer.cpp b/src/caffe/layers/slice_layer.cpp index e4418c9cf9c..0a059ae88fe 100644 --- a/src/caffe/layers/slice_layer.cpp +++ b/src/caffe/layers/slice_layer.cpp @@ -67,11 +67,16 @@ void SliceLayer::Reshape(const vector*>& bottom, } } CHECK_EQ(count, bottom[0]->count()); + if (top.size() == 1) { + top[0]->ShareData(*bottom[0]); + top[0]->ShareDiff(*bottom[0]); + } } template void SliceLayer::Forward_cpu(const vector*>& bottom, const vector*>& top) { + if (top.size() == 1) { return; } int offset_slice_axis = 0; const Dtype* bottom_data = bottom[0]->cpu_data(); const int bottom_slice_axis = bottom[0]->shape(slice_axis_); @@ -92,7 +97,7 @@ void SliceLayer::Forward_cpu(const vector*>& bottom, template void SliceLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, const vector*>& bottom) { - if (!propagate_down[0]) { return; } + if (!propagate_down[0] || top.size() == 1) { return; } int offset_slice_axis = 0; Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); const int bottom_slice_axis = bottom[0]->shape(slice_axis_); diff --git a/src/caffe/layers/slice_layer.cu b/src/caffe/layers/slice_layer.cu index 796841d3f52..e8dc6cd98fc 100644 --- a/src/caffe/layers/slice_layer.cu +++ b/src/caffe/layers/slice_layer.cu @@ -28,6 +28,7 @@ __global__ void Slice(const int nthreads, const Dtype* in_data, template void SliceLayer::Forward_gpu(const vector*>& bottom, const vector*>& top) { + if (top.size() == 1) { return; } int offset_slice_axis = 0; const Dtype* bottom_data = bottom[0]->gpu_data(); const int bottom_slice_axis = bottom[0]->shape(slice_axis_); @@ -48,7 +49,7 @@ void SliceLayer::Forward_gpu(const vector*>& bottom, template void SliceLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, const vector*>& bottom) { - if (!propagate_down[0]) { return; } + if (!propagate_down[0] || top.size() == 1) { return; } int offset_slice_axis = 0; Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); const int bottom_slice_axis = bottom[0]->shape(slice_axis_); diff --git a/src/caffe/test/test_slice_layer.cpp b/src/caffe/test/test_slice_layer.cpp index ccd03646d19..2d2d0fdc005 100644 --- a/src/caffe/test/test_slice_layer.cpp +++ b/src/caffe/test/test_slice_layer.cpp @@ -88,6 +88,21 @@ TYPED_TEST(SliceLayerTest, TestSetupChannels) { EXPECT_EQ(this->blob_bottom_->width(), this->blob_top_0_->width()); } +TYPED_TEST(SliceLayerTest, TestTrivialSlice) { + // Test the trivial (single output) "slice" operation -- + // should be the identity. + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + SliceLayer layer(layer_param); + this->blob_top_vec_0_.resize(1); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_0_); + ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_0_->shape()); + for (int i = 0; i < this->blob_bottom_->count(); ++i) { + EXPECT_EQ(this->blob_bottom_->cpu_data()[i], + this->blob_top_0_->cpu_data()[i]); + } +} + TYPED_TEST(SliceLayerTest, TestSliceAcrossNum) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; @@ -161,6 +176,18 @@ TYPED_TEST(SliceLayerTest, TestSliceAcrossChannels) { } } +TYPED_TEST(SliceLayerTest, TestGradientTrivial) { + // Test the trivial (single output) "slice" operation -- + // should be the identity. + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + SliceLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-3); + this->blob_top_vec_0_.resize(1); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_0_); +} + TYPED_TEST(SliceLayerTest, TestGradientAcrossNum) { typedef typename TypeParam::Dtype Dtype; // Gradient checks are slow; reduce blob size. From 784dfddd42cc787fd9661a954f2a360990867df2 Mon Sep 17 00:00:00 2001 From: Jeff Donahue Date: Fri, 23 Jan 2015 12:52:44 -0800 Subject: [PATCH 2/2] ConcatLayer: allow trivial operation with single bottom Blob --- include/caffe/common_layers.hpp | 2 +- src/caffe/layers/concat_layer.cpp | 6 ++++++ src/caffe/layers/concat_layer.cu | 2 ++ src/caffe/test/test_concat_layer.cpp | 23 +++++++++++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/caffe/common_layers.hpp b/include/caffe/common_layers.hpp index 6d4a9e3cb81..89bab8d6f3a 100644 --- a/include/caffe/common_layers.hpp +++ b/include/caffe/common_layers.hpp @@ -85,7 +85,7 @@ class ConcatLayer : public Layer { const vector*>& top); virtual inline const char* type() const { return "Concat"; } - virtual inline int MinBottomBlobs() const { return 2; } + virtual inline int MinBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: diff --git a/src/caffe/layers/concat_layer.cpp b/src/caffe/layers/concat_layer.cpp index 95fba105b9a..86b500de859 100644 --- a/src/caffe/layers/concat_layer.cpp +++ b/src/caffe/layers/concat_layer.cpp @@ -48,11 +48,16 @@ void ConcatLayer::Reshape(const vector*>& bottom, } top[0]->Reshape(top_shape); CHECK_EQ(bottom_count_sum, top[0]->count()); + if (bottom.size() == 1) { + top[0]->ShareData(*bottom[0]); + top[0]->ShareDiff(*bottom[0]); + } } template void ConcatLayer::Forward_cpu(const vector*>& bottom, const vector*>& top) { + if (bottom.size() == 1) { return; } Dtype* top_data = top[0]->mutable_cpu_data(); int offset_concat_axis = 0; const int top_concat_axis = top[0]->shape(concat_axis_); @@ -72,6 +77,7 @@ void ConcatLayer::Forward_cpu(const vector*>& bottom, template void ConcatLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, const vector*>& bottom) { + if (bottom.size() == 1) { return; } const Dtype* top_diff = top[0]->cpu_diff(); int offset_concat_axis = 0; const int top_concat_axis = top[0]->shape(concat_axis_); diff --git a/src/caffe/layers/concat_layer.cu b/src/caffe/layers/concat_layer.cu index 3c64c7ef224..617701e2621 100644 --- a/src/caffe/layers/concat_layer.cu +++ b/src/caffe/layers/concat_layer.cu @@ -28,6 +28,7 @@ __global__ void Concat(const int nthreads, const Dtype* in_data, template void ConcatLayer::Forward_gpu(const vector*>& bottom, const vector*>& top) { + if (bottom.size() == 1) { return; } Dtype* top_data = top[0]->mutable_gpu_data(); int offset_concat_axis = 0; const int top_concat_axis = top[0]->shape(concat_axis_); @@ -48,6 +49,7 @@ void ConcatLayer::Forward_gpu(const vector*>& bottom, template void ConcatLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, const vector*>& bottom) { + if (bottom.size() == 1) { return; } const Dtype* top_diff = top[0]->gpu_diff(); int offset_concat_axis = 0; const int top_concat_axis = top[0]->shape(concat_axis_); diff --git a/src/caffe/test/test_concat_layer.cpp b/src/caffe/test/test_concat_layer.cpp index 088e0a41685..ccd97eb1d66 100644 --- a/src/caffe/test/test_concat_layer.cpp +++ b/src/caffe/test/test_concat_layer.cpp @@ -99,6 +99,19 @@ TYPED_TEST(ConcatLayerTest, TestSetupChannelsNegativeIndexing) { EXPECT_EQ(this->blob_top_->width(), this->blob_bottom_0_->width()); } +TYPED_TEST(ConcatLayerTest, TestForwardTrivial) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + ConcatLayer layer(layer_param); + this->blob_bottom_vec_0_.resize(1); + layer.SetUp(this->blob_bottom_vec_0_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_0_, this->blob_top_vec_); + for (int i = 0; i < this->blob_bottom_0_->count(); ++i) { + EXPECT_EQ(this->blob_bottom_0_->cpu_data()[i], + this->blob_top_->cpu_data()[i]); + } +} + TYPED_TEST(ConcatLayerTest, TestForwardNum) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; @@ -154,6 +167,16 @@ TYPED_TEST(ConcatLayerTest, TestForwardChannels) { } } +TYPED_TEST(ConcatLayerTest, TestGradientTrivial) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + ConcatLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-2); + this->blob_bottom_vec_0_.resize(1); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_0_, + this->blob_top_vec_); +} + TYPED_TEST(ConcatLayerTest, TestGradientNum) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param;