#include #include #include "gtest/gtest.h" #include "caffe/blob.hpp" #include "caffe/common.hpp" #include "caffe/filler.hpp" #include "caffe/layers/scale_layer.hpp" #include "caffe/test/test_caffe_main.hpp" #include "caffe/test/test_gradient_check_util.hpp" namespace caffe { template class ScaleLayerTest : public MultiDeviceTest { typedef typename TypeParam::Dtype Dtype; protected: ScaleLayerTest() : blob_bottom_(new Blob(2, 3, 4, 5)), blob_bottom_eltwise_(new Blob(2, 3, 4, 5)), blob_bottom_broadcast_0_(new Blob()), blob_bottom_broadcast_1_(new Blob()), blob_bottom_broadcast_2_(new Blob()), blob_bottom_scale_(new Blob(vector())), blob_top_(new Blob()) { Caffe::set_random_seed(1701); vector broadcast_shape(2); broadcast_shape[0] = 2; broadcast_shape[1] = 3; this->blob_bottom_broadcast_0_->Reshape(broadcast_shape); broadcast_shape[0] = 3; broadcast_shape[1] = 4; this->blob_bottom_broadcast_1_->Reshape(broadcast_shape); broadcast_shape[0] = 4; broadcast_shape[1] = 5; this->blob_bottom_broadcast_2_->Reshape(broadcast_shape); FillerParameter filler_param; filler_param.set_min(1); filler_param.set_max(10); UniformFiller filler(filler_param); filler.Fill(this->blob_bottom_); filler.Fill(this->blob_bottom_eltwise_); filler.Fill(this->blob_bottom_broadcast_0_); filler.Fill(this->blob_bottom_broadcast_1_); filler.Fill(this->blob_bottom_broadcast_2_); filler.Fill(this->blob_bottom_scale_); blob_bottom_vec_.push_back(blob_bottom_); blob_top_vec_.push_back(blob_top_); } virtual ~ScaleLayerTest() { delete blob_bottom_; delete blob_bottom_eltwise_; delete blob_bottom_broadcast_0_; delete blob_bottom_broadcast_1_; delete blob_bottom_broadcast_2_; delete blob_bottom_scale_; delete blob_top_; } Blob* const blob_bottom_; Blob* const blob_bottom_eltwise_; Blob* const blob_bottom_broadcast_0_; Blob* const blob_bottom_broadcast_1_; Blob* const blob_bottom_broadcast_2_; Blob* const blob_bottom_scale_; Blob* const blob_top_; vector*> blob_bottom_vec_; vector*> blob_top_vec_; }; TYPED_TEST_CASE(ScaleLayerTest, TestDtypesAndDevices); TYPED_TEST(ScaleLayerTest, TestForwardEltwise) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_eltwise_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(0); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data_a = this->blob_bottom_->cpu_data(); const Dtype* in_data_b = this->blob_bottom_eltwise_->cpu_data(); for (int i = 0; i < count; ++i) { EXPECT_NEAR(data[i], in_data_a[i] * in_data_b[i], 1e-5); } } TYPED_TEST(ScaleLayerTest, TestForwardEltwiseInPlace) { typedef typename TypeParam::Dtype Dtype; this->blob_top_vec_[0] = this->blob_bottom_; // in-place computation Blob orig_bottom(this->blob_bottom_->shape()); orig_bottom.CopyFrom(*this->blob_bottom_); this->blob_bottom_vec_.push_back(this->blob_bottom_eltwise_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(0); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_bottom_->cpu_data(); const int count = this->blob_bottom_->count(); const Dtype* in_data_a = orig_bottom.cpu_data(); const Dtype* in_data_b = this->blob_bottom_eltwise_->cpu_data(); for (int i = 0; i < count; ++i) { EXPECT_NEAR(data[i], in_data_a[i] * in_data_b[i], 1e-5); } } TYPED_TEST(ScaleLayerTest, TestBackwardEltwiseInPlace) { typedef typename TypeParam::Dtype Dtype; Blob orig_bottom(this->blob_bottom_->shape()); orig_bottom.CopyFrom(*this->blob_bottom_); this->blob_bottom_vec_.push_back(this->blob_bottom_eltwise_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(0); shared_ptr > layer(new ScaleLayer(layer_param)); Blob top_diff(this->blob_bottom_->shape()); FillerParameter filler_param; filler_param.set_type("gaussian"); filler_param.set_std(1); GaussianFiller filler(filler_param); filler.Fill(&top_diff); vector propagate_down(2, true); // Run forward + backward without in-place computation; // save resulting bottom diffs. layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); caffe_copy(top_diff.count(), top_diff.cpu_data(), this->blob_top_->mutable_cpu_diff()); layer->Backward(this->blob_top_vec_, propagate_down, this->blob_bottom_vec_); const bool kReshape = true; const bool kCopyDiff = true; Blob orig_bottom_diff; orig_bottom_diff.CopyFrom(*this->blob_bottom_, kCopyDiff, kReshape); Blob orig_scale_diff; orig_scale_diff.CopyFrom(*this->blob_bottom_eltwise_, kCopyDiff, kReshape); // Rerun forward + backward with in-place computation; // check that resulting bottom diffs are the same. this->blob_top_vec_[0] = this->blob_bottom_; // in-place computation layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); caffe_copy(top_diff.count(), top_diff.cpu_data(), this->blob_bottom_->mutable_cpu_diff()); layer->Backward(this->blob_top_vec_, propagate_down, this->blob_bottom_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { EXPECT_NEAR(orig_bottom_diff.cpu_diff()[i], this->blob_bottom_->cpu_diff()[i], 1e-5); } for (int i = 0; i < this->blob_bottom_eltwise_->count(); ++i) { EXPECT_NEAR(orig_scale_diff.cpu_diff()[i], this->blob_bottom_eltwise_->cpu_diff()[i], 1e-5); } } TYPED_TEST(ScaleLayerTest, TestForwardEltwiseWithParam) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ScaleParameter* scale_param = layer_param.mutable_scale_param(); scale_param->set_axis(0); scale_param->set_num_axes(-1); scale_param->mutable_filler()->set_type("gaussian"); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data_a = this->blob_bottom_->cpu_data(); const Dtype* in_data_b = layer->blobs()[0]->cpu_data(); for (int i = 0; i < count; ++i) { EXPECT_NEAR(data[i], in_data_a[i] * in_data_b[i], 1e-5); } } TYPED_TEST(ScaleLayerTest, TestForwardBroadcastBegin) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_0_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(0); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_bottom_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { for (int w = 0; w < this->blob_bottom_->width(); ++w) { EXPECT_NEAR(this->blob_top_->data_at(n, c, h, w), this->blob_bottom_->data_at(n, c, h, w) * this->blob_bottom_broadcast_0_->data_at(n, c, 0, 0), 1e-5); } } } } } TYPED_TEST(ScaleLayerTest, TestForwardBroadcastMiddle) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_1_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(1); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_bottom_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { for (int w = 0; w < this->blob_bottom_->width(); ++w) { EXPECT_NEAR(this->blob_top_->data_at(n, c, h, w), this->blob_bottom_->data_at(n, c, h, w) * this->blob_bottom_broadcast_1_->data_at(c, h, 0, 0), 1e-5); } } } } } TYPED_TEST(ScaleLayerTest, TestForwardBroadcastMiddleInPlace) { typedef typename TypeParam::Dtype Dtype; this->blob_top_vec_[0] = this->blob_bottom_; // in-place computation Blob orig_bottom(this->blob_bottom_->shape()); orig_bottom.CopyFrom(*this->blob_bottom_); this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_1_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(1); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_bottom_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { for (int w = 0; w < this->blob_bottom_->width(); ++w) { EXPECT_NEAR(this->blob_bottom_->data_at(n, c, h, w), orig_bottom.data_at(n, c, h, w) * this->blob_bottom_broadcast_1_->data_at(c, h, 0, 0), 1e-5); } } } } } TYPED_TEST(ScaleLayerTest, TestBackwardBroadcastMiddleInPlace) { typedef typename TypeParam::Dtype Dtype; Blob orig_bottom(this->blob_bottom_->shape()); orig_bottom.CopyFrom(*this->blob_bottom_); this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_1_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(1); shared_ptr > layer(new ScaleLayer(layer_param)); Blob top_diff(this->blob_bottom_->shape()); FillerParameter filler_param; filler_param.set_type("gaussian"); filler_param.set_std(1); GaussianFiller filler(filler_param); filler.Fill(&top_diff); vector propagate_down(2, true); // Run forward + backward without in-place computation; // save resulting bottom diffs. layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); caffe_copy(top_diff.count(), top_diff.cpu_data(), this->blob_top_->mutable_cpu_diff()); layer->Backward(this->blob_top_vec_, propagate_down, this->blob_bottom_vec_); const bool kReshape = true; const bool kCopyDiff = true; Blob orig_bottom_diff; orig_bottom_diff.CopyFrom(*this->blob_bottom_, kCopyDiff, kReshape); Blob orig_scale_diff; orig_scale_diff.CopyFrom(*this->blob_bottom_broadcast_1_, kCopyDiff, kReshape); // Rerun forward + backward with in-place computation; // check that resulting bottom diffs are the same. this->blob_top_vec_[0] = this->blob_bottom_; // in-place computation layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); caffe_copy(top_diff.count(), top_diff.cpu_data(), this->blob_bottom_->mutable_cpu_diff()); layer->Backward(this->blob_top_vec_, propagate_down, this->blob_bottom_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { EXPECT_NEAR(orig_bottom_diff.cpu_diff()[i], this->blob_bottom_->cpu_diff()[i], 1e-5); } for (int i = 0; i < this->blob_bottom_broadcast_1_->count(); ++i) { EXPECT_NEAR(orig_scale_diff.cpu_diff()[i], this->blob_bottom_broadcast_1_->cpu_diff()[i], 1e-5); } } TYPED_TEST(ScaleLayerTest, TestForwardBroadcastMiddleWithParam) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ScaleParameter* scale_param = layer_param.mutable_scale_param(); scale_param->set_axis(1); scale_param->set_num_axes(2); scale_param->mutable_filler()->set_type("gaussian"); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_bottom_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { for (int w = 0; w < this->blob_bottom_->width(); ++w) { EXPECT_NEAR(this->blob_top_->data_at(n, c, h, w), this->blob_bottom_->data_at(n, c, h, w) * layer->blobs()[0]->data_at(c, h, 0, 0), 1e-5); } } } } } TYPED_TEST(ScaleLayerTest, TestForwardBroadcastMiddleWithParamAndBias) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ScaleParameter* scale_param = layer_param.mutable_scale_param(); scale_param->set_axis(1); scale_param->set_num_axes(2); scale_param->mutable_filler()->set_type("gaussian"); scale_param->set_bias_term(true); scale_param->mutable_bias_filler()->set_type("gaussian"); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_bottom_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { for (int w = 0; w < this->blob_bottom_->width(); ++w) { EXPECT_NEAR(this->blob_top_->data_at(n, c, h, w), this->blob_bottom_->data_at(n, c, h, w) * layer->blobs()[0]->data_at(c, h, 0, 0) + layer->blobs()[1]->data_at(c, h, 0, 0), 1e-5); } } } } } TYPED_TEST(ScaleLayerTest, TestForwardBroadcastEnd) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_2_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(2); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_bottom_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { for (int w = 0; w < this->blob_bottom_->width(); ++w) { EXPECT_NEAR(this->blob_top_->data_at(n, c, h, w), this->blob_bottom_->data_at(n, c, h, w) * this->blob_bottom_broadcast_2_->data_at(h, w, 0, 0), 1e-5); } } } } } TYPED_TEST(ScaleLayerTest, TestForwardScale) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_scale_); LayerParameter layer_param; shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data = this->blob_bottom_->cpu_data(); const Dtype scale = *this->blob_bottom_scale_->cpu_data(); for (int i = 0; i < count; ++i) { EXPECT_NEAR(data[i], in_data[i] * scale, 1e-5); } } TYPED_TEST(ScaleLayerTest, TestForwardScaleAxis2) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_scale_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(2); shared_ptr > layer(new ScaleLayer(layer_param)); layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); ASSERT_EQ(this->blob_bottom_->shape(), this->blob_top_->shape()); layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data = this->blob_bottom_->cpu_data(); const Dtype scale = *this->blob_bottom_scale_->cpu_data(); for (int i = 0; i < count; ++i) { EXPECT_NEAR(data[i], in_data[i] * scale, 1e-5); } } TYPED_TEST(ScaleLayerTest, TestGradientEltwise) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_eltwise_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(0); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientEltwiseWithParam) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ScaleParameter* scale_param = layer_param.mutable_scale_param(); scale_param->set_axis(0); scale_param->set_num_axes(-1); scale_param->mutable_filler()->set_type("gaussian"); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientBroadcastBegin) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_0_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(0); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientBroadcastMiddle) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_1_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(1); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientBroadcastMiddleWithParam) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_1_); LayerParameter layer_param; ScaleParameter* scale_param = layer_param.mutable_scale_param(); scale_param->set_axis(1); scale_param->set_num_axes(2); scale_param->mutable_filler()->set_type("gaussian"); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientBroadcastEnd) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_broadcast_2_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(2); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientScale) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_scale_); LayerParameter layer_param; ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientScaleAndBias) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_scale_); LayerParameter layer_param; ScaleParameter* scale_param = layer_param.mutable_scale_param(); scale_param->set_bias_term(true); scale_param->mutable_bias_filler()->set_type("gaussian"); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } TYPED_TEST(ScaleLayerTest, TestGradientScaleAxis2) { typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_scale_); LayerParameter layer_param; layer_param.mutable_scale_param()->set_axis(2); ScaleLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, this->blob_top_vec_); } } // namespace caffe