#include
#include "gtest/gtest.h"
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/layers/conv_layer.hpp"
#ifdef USE_CUDNN
#include "caffe/layers/cudnn_conv_layer.hpp"
#endif
#include "caffe/test/test_caffe_main.hpp"
#include "caffe/test/test_gradient_check_util.hpp"
namespace caffe {
template
static void dump_blob(const Blob * blob, const char * outfile)
{
std::ofstream os;
os.open(outfile);
for(int i=0;iLegacyShape(0);i++)
{
ospad_size() ? conv_param->pad(0) : 0;
}
int stride_h, stride_w;
if (conv_param->has_stride_h() || conv_param->has_stride_w()) {
stride_h = conv_param->stride_h();
stride_w = conv_param->stride_w();
} else {
stride_h = stride_w = conv_param->stride_size() ? conv_param->stride(0) : 1;
}
int dilation_h, dilation_w;
dilation_h = dilation_w = conv_param->dilation_size() ?
conv_param->dilation(0) : 1;
int kernel_d, pad_d, stride_d, dilation_d;
if (has_depth) {
kernel_d = kernel_h;
stride_d = stride_h;
pad_d = pad_h;
dilation_d = dilation_h;
} else {
kernel_d = stride_d = dilation_d = 1;
pad_d = 0;
}
// Groups
int groups = conv_param->group();
int o_g = out->shape(1) / groups;
int k_g = in->shape(1) / groups;
int o_head, k_head;
// Convolution
vector weight_offset(4 + has_depth);
vector in_offset(4 + has_depth);
vector out_offset(4 + has_depth);
Dtype* out_data = out->mutable_cpu_data();
for (int n = 0; n < out->shape(0); n++) {
for (int g = 0; g < groups; g++) {
o_head = o_g * g;
k_head = k_g * g;
for (int o = 0; o < o_g; o++) {
for (int k = 0; k < k_g; k++) {
for (int z = 0; z < (has_depth ? out->shape(2) : 1); z++) {
for (int y = 0; y < out->shape(2 + has_depth); y++) {
for (int x = 0; x < out->shape(3 + has_depth); x++) {
for (int r = 0; r < kernel_d; r++) {
for (int p = 0; p < kernel_h; p++) {
for (int q = 0; q < kernel_w; q++) {
int in_z = z * stride_d - pad_d + r * dilation_d;
int in_y = y * stride_h - pad_h + p * dilation_h;
int in_x = x * stride_w - pad_w + q * dilation_w;
if (in_z >= 0 && in_z < (has_depth ? in->shape(2) : 1)
&& in_y >= 0 && in_y < in->shape(2 + has_depth)
&& in_x >= 0 && in_x < in->shape(3 + has_depth)) {
weight_offset[0] = o + o_head;
weight_offset[1] = k;
if (has_depth) { weight_offset[2] = r; }
weight_offset[2 + has_depth] = p;
weight_offset[3 + has_depth] = q;
in_offset[0] = n;
in_offset[1] = k + k_head;
if (has_depth) { in_offset[2] = in_z; }
in_offset[2 + has_depth] = in_y;
in_offset[3 + has_depth] = in_x;
out_offset[0] = n;
out_offset[1] = o + o_head;
if (has_depth) { out_offset[2] = z; }
out_offset[2 + has_depth] = y;
out_offset[3 + has_depth] = x;
out_data[out->offset(out_offset)] +=
in->data_at(in_offset)
* weights[0]->data_at(weight_offset);
}
}
}
}
}
}
}
}
}
}
}
// Bias
if (conv_param->bias_term()) {
const Dtype* bias_data = weights[1]->cpu_data();
for (int n = 0; n < out->shape(0); n++) {
for (int o = 0; o < out->shape(1); o++) {
for (int z = 0; z < (has_depth ? out->shape(2) : 1); z++) {
for (int y = 0; y < out->shape(2 + has_depth); y++) {
for (int x = 0; x < out->shape(3 + has_depth); x++) {
out_offset[0] = n;
out_offset[1] = o;
if (has_depth) { out_offset[2] = z; }
out_offset[2 + has_depth] = y;
out_offset[3 + has_depth] = x;
out_data[out->offset(out_offset)] += bias_data[o];
}
}
}
}
}
}
}
template void caffe_conv(const Blob* in,
ConvolutionParameter* conv_param,
const vector& weights,
Blob* out);
template void caffe_conv(const Blob* in,
ConvolutionParameter* conv_param,
const vector& weights,
Blob* out);
template
class ConvolutionLayerTest : public MultiDeviceTest {
typedef typename TypeParam::Dtype Dtype;
protected:
ConvolutionLayerTest()
: blob_bottom_(new Blob(2, 3, 6, 4)),
blob_bottom_2_(new Blob(2, 3, 6, 4)),
blob_top_(new Blob()),
blob_top_2_(new Blob()) {}
virtual void SetUp() {
// fill the values
FillerParameter filler_param;
filler_param.set_value(1.);
GaussianFiller filler(filler_param);
filler.Fill(this->blob_bottom_);
filler.Fill(this->blob_bottom_2_);
blob_bottom_vec_.push_back(blob_bottom_);
blob_top_vec_.push_back(blob_top_);
}
virtual ~ConvolutionLayerTest() {
delete blob_bottom_;
delete blob_bottom_2_;
delete blob_top_;
delete blob_top_2_;
}
virtual Blob* MakeReferenceTop(Blob* top) {
this->ref_blob_top_.reset(new Blob());
this->ref_blob_top_->ReshapeLike(*top);
return this->ref_blob_top_.get();
}
Blob* const blob_bottom_;
Blob* const blob_bottom_2_;
Blob* const blob_top_;
Blob* const blob_top_2_;
shared_ptr ref_blob_top_;
vector blob_bottom_vec_;
vector blob_top_vec_;
};
typedef ::testing::Types float_only;
#define TestDtypesAndDevices float_only
TYPED_TEST_CASE(ConvolutionLayerTest, TestDtypesAndDevices);
TYPED_TEST(ConvolutionLayerTest, TestSetup) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->add_kernel_size(3);
convolution_param->add_stride(2);
convolution_param->set_num_output(4);
this->blob_bottom_vec_.push_back(this->blob_bottom_2_);
this->blob_top_vec_.push_back(this->blob_top_2_);
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(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(), 4);
EXPECT_EQ(this->blob_top_->height(), 2);
EXPECT_EQ(this->blob_top_->width(), 1);
EXPECT_EQ(this->blob_top_2_->num(), 2);
EXPECT_EQ(this->blob_top_2_->channels(), 4);
EXPECT_EQ(this->blob_top_2_->height(), 2);
EXPECT_EQ(this->blob_top_2_->width(), 1);
// setting group should not change the shape
convolution_param->set_num_output(3);
convolution_param->set_group(3);
layer.reset(new ConvolutionLayer(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);
EXPECT_EQ(this->blob_top_->height(), 2);
EXPECT_EQ(this->blob_top_->width(), 1);
EXPECT_EQ(this->blob_top_2_->num(), 2);
EXPECT_EQ(this->blob_top_2_->channels(), 3);
EXPECT_EQ(this->blob_top_2_->height(), 2);
EXPECT_EQ(this->blob_top_2_->width(), 1);
}
TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolution) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->add_kernel_size(3);
convolution_param->add_stride(2);
convolution_param->set_num_output(3);
convolution_param->mutable_weight_filler()->set_type("gaussian");
convolution_param->mutable_bias_filler()->set_type("constant");
convolution_param->mutable_bias_filler()->set_value(0.1);
vector bottom_shape;
bottom_shape.push_back(2);
bottom_shape.push_back(3);
bottom_shape.push_back(5);
bottom_shape.push_back(5);
this->blob_bottom_->Reshape(bottom_shape);
this->blob_bottom_2_->Reshape(bottom_shape);
fill_blob_data(this->blob_bottom_,0,1);
fill_blob_data(this->blob_bottom_2_,1,1);
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
this->blob_bottom_vec_.push_back(this->blob_bottom_2_);
this->blob_top_vec_.push_back(this->blob_top_2_);
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
//fill_blob_data(layer->blobs()[0].get(),1,1);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
#ifdef LAYER_PERF_STAT
perf_stat * p_time_stat;
p_time_stat=layer->get_time_stat();
std::coutblobs(),
this->MakeReferenceTop(this->blob_top_2_));
top_data = this->blob_top_2_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
#endif
}
#if 0
TYPED_TEST(ConvolutionLayerTest, TestDilatedConvolution) {
typedef typename TypeParam::Dtype Dtype;
vector bottom_shape;
bottom_shape.push_back(2);
bottom_shape.push_back(3);
bottom_shape.push_back(8);
bottom_shape.push_back(7);
this->blob_bottom_vec_.push_back(this->blob_bottom_2_);
this->blob_top_vec_.push_back(this->blob_top_2_);
for (int i = 0; i < this->blob_bottom_vec_.size(); ++i) {
this->blob_bottom_vec_[i]->Reshape(bottom_shape);
}
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->add_kernel_size(3);
convolution_param->add_dilation(2);
convolution_param->set_num_output(4);
convolution_param->mutable_weight_filler()->set_type("gaussian");
convolution_param->mutable_bias_filler()->set_type("constant");
convolution_param->mutable_bias_filler()->set_value(0.1);
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Check against reference convolution.
const Dtype* top_data;
const Dtype* ref_top_data;
caffe_conv(this->blob_bottom_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_));
top_data = this->blob_top_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
caffe_conv(this->blob_bottom_2_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_2_));
top_data = this->blob_top_2_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
}
TYPED_TEST(ConvolutionLayerTest, Test0DConvolution) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
const int kNumOutput = 3;
convolution_param->set_num_output(kNumOutput);
convolution_param->set_axis(3);
convolution_param->mutable_weight_filler()->set_type("gaussian");
convolution_param->mutable_bias_filler()->set_type("gaussian");
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
vector top_shape = this->blob_bottom_->shape();
top_shape[3] = kNumOutput;
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
EXPECT_EQ(top_shape, this->blob_top_->shape());
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Check against reference convolution.
vector weight_offset(2);
const Blob* weight = layer->blobs()[0].get();
const Blob* bias = layer->blobs()[1].get();
const int num = this->blob_top_->count(3);
const int dim = this->blob_top_->shape(3);
const int bottom_dim = this->blob_bottom_->shape(3);
for (int n = 0; n < num; ++n) {
for (int d = 0; d < dim; ++d) {
weight_offset[0] = d;
Dtype value = bias->cpu_data()[d];
for (int bottom_d = 0; bottom_d < bottom_dim; ++bottom_d) {
weight_offset[1] = bottom_d;
value += weight->data_at(weight_offset) *
this->blob_bottom_->cpu_data()[n * bottom_dim + bottom_d];
}
EXPECT_NEAR(value, this->blob_top_->cpu_data()[n * dim + d], 1e-4);
}
}
}
TYPED_TEST(ConvolutionLayerTest, TestSimple3DConvolution) {
typedef typename TypeParam::Dtype Dtype;
this->blob_bottom_vec_.push_back(this->blob_bottom_2_);
this->blob_top_vec_.push_back(this->blob_top_2_);
vector bottom_shape(5);
bottom_shape[0] = this->blob_bottom_vec_[0]->shape(0);
bottom_shape[1] = this->blob_bottom_vec_[0]->shape(1);
bottom_shape[2] = 5;
bottom_shape[3] = this->blob_bottom_vec_[0]->shape(2);
bottom_shape[4] = this->blob_bottom_vec_[0]->shape(3);
FillerParameter filler_param;
GaussianFiller filler(filler_param);
for (int i = 0; i < this->blob_bottom_vec_.size(); ++i) {
this->blob_bottom_vec_[i]->Reshape(bottom_shape);
filler.Fill(this->blob_bottom_vec_[i]);
}
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->add_kernel_size(3);
convolution_param->add_stride(2);
convolution_param->set_num_output(4);
convolution_param->mutable_weight_filler()->set_type("gaussian");
convolution_param->mutable_bias_filler()->set_type("gaussian");
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Check against reference convolution.
const Dtype* top_data;
const Dtype* ref_top_data;
caffe_conv(this->blob_bottom_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_));
top_data = this->blob_top_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
caffe_conv(this->blob_bottom_2_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_2_));
top_data = this->blob_top_2_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
}
TYPED_TEST(ConvolutionLayerTest, TestDilated3DConvolution) {
typedef typename TypeParam::Dtype Dtype;
this->blob_bottom_vec_.push_back(this->blob_bottom_2_);
this->blob_top_vec_.push_back(this->blob_top_2_);
vector bottom_shape(5);
bottom_shape[0] = this->blob_bottom_vec_[0]->shape(0);
bottom_shape[1] = this->blob_bottom_vec_[0]->shape(1);
bottom_shape[2] = 6;
bottom_shape[3] = 7;
bottom_shape[4] = 8;
FillerParameter filler_param;
GaussianFiller filler(filler_param);
for (int i = 0; i < this->blob_bottom_vec_.size(); ++i) {
this->blob_bottom_vec_[i]->Reshape(bottom_shape);
filler.Fill(this->blob_bottom_vec_[i]);
}
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->add_kernel_size(3);
convolution_param->add_dilation(2);
convolution_param->set_num_output(4);
convolution_param->mutable_weight_filler()->set_type("gaussian");
convolution_param->mutable_bias_filler()->set_type("gaussian");
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Check against reference convolution.
const Dtype* top_data;
const Dtype* ref_top_data;
caffe_conv(this->blob_bottom_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_));
top_data = this->blob_top_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
caffe_conv(this->blob_bottom_2_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_2_));
top_data = this->blob_top_2_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
}
#endif
TYPED_TEST(ConvolutionLayerTest, Test1x1Convolution) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
#if 0
convolution_param->add_kernel_size(1);
convolution_param->set_num_output(2);
vector bottom_shape;
bottom_shape.push_back(1);
bottom_shape.push_back(32);
bottom_shape.push_back(133);
bottom_shape.push_back(98);
this->blob_bottom_vec_[0]->Reshape(bottom_shape);
#else
convolution_param->add_kernel_size(1);
convolution_param->add_stride(1);
convolution_param->set_num_output(4);
#endif
convolution_param->mutable_weight_filler()->set_type("gaussian");
convolution_param->mutable_bias_filler()->set_type("constant");
convolution_param->mutable_bias_filler()->set_value(1);
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
fill_blob_data(this->blob_bottom_,1,1);
fill_blob_data(layer->blobs()[0].get(),1,1);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
fill_blob_data(this->blob_bottom_,1,3);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Check against reference convolution.
const Dtype* top_data;
const Dtype* ref_top_data;
caffe_conv(this->blob_bottom_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_));
top_data = this->blob_top_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
// std::coutSetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Check against reference convolution.
const Dtype* top_data;
const Dtype* ref_top_data;
caffe_conv(this->blob_bottom_, convolution_param, layer->blobs(),
this->MakeReferenceTop(this->blob_top_));
top_data = this->blob_top_->cpu_data();
ref_top_data = this->ref_blob_top_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4);
}
}
TYPED_TEST(ConvolutionLayerTest, TestSobelConvolution) {
// Test separable convolution by computing the Sobel operator
// as a single filter then comparing the result
// as the convolution of two rectangular filters.
typedef typename TypeParam::Dtype Dtype;
// Fill bottoms with identical Gaussian noise.
shared_ptr filler;
FillerParameter filler_param;
filler_param.set_value(1.);
filler.reset(new GaussianFiller(filler_param));
filler->Fill(this->blob_bottom_);
this->blob_bottom_2_->CopyFrom(*this->blob_bottom_);
// Compute Sobel G_x operator as 3 x 3 convolution.
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->add_kernel_size(3);
convolution_param->add_stride(2);
convolution_param->set_num_output(1);
convolution_param->set_bias_term(false);
layer_param.set_type("Convolution");
shared_ptr layer=
LayerRegistry::CreateLayer(layer_param);
layer->blobs().resize(1);
layer->blobs()[0].reset(new Blob(1, 3, 3, 3));
Dtype* weights = layer->blobs()[0]->mutable_cpu_data();
for (int c = 0; c < 3; ++c) {
int i = c * 9; // 3 x 3 filter
weights[i + 0] = -1;
weights[i + 1] = 0;
weights[i + 2] = 1;
weights[i + 3] = -2;
weights[i + 4] = 0;
weights[i + 5] = 2;
weights[i + 6] = -1;
weights[i + 7] = 0;
weights[i + 8] = 1;
}
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Compute Sobel G_x operator as separable 3 x 1 and 1 x 3 convolutions.
// (1) the [1 2 1] column filter
vector sep_blob_bottom_vec;
vector sep_blob_top_vec;
shared_ptr blob_sep(new Blob());
sep_blob_bottom_vec.push_back(this->blob_bottom_2_);
sep_blob_top_vec.push_back(this->blob_top_2_);
convolution_param->clear_kernel_size();
convolution_param->clear_stride();
convolution_param->set_kernel_h(3);
convolution_param->set_kernel_w(1);
convolution_param->set_stride_h(2);
convolution_param->set_stride_w(1);
convolution_param->set_num_output(1);
convolution_param->set_bias_term(false);
layer.reset(new ConvolutionLayer(layer_param));
layer->blobs().resize(1);
layer->blobs()[0].reset(new Blob(1, 3, 3, 1));
Dtype* weights_1 = layer->blobs()[0]->mutable_cpu_data();
for (int c = 0; c < 3; ++c) {
int i = c * 3; // 3 x 1 filter
weights_1[i + 0] = 1;
weights_1[i + 1] = 2;
weights_1[i + 2] = 1;
}
layer->SetUp(sep_blob_bottom_vec, sep_blob_top_vec);
layer->Forward(sep_blob_bottom_vec, sep_blob_top_vec);
// (2) the [-1 0 1] row filter
blob_sep->CopyFrom(*this->blob_top_2_, false, true);
sep_blob_bottom_vec.clear();
sep_blob_bottom_vec.push_back(blob_sep.get());
convolution_param->set_kernel_h(1);
convolution_param->set_kernel_w(3);
convolution_param->set_stride_h(1);
convolution_param->set_stride_w(2);
convolution_param->set_num_output(1);
convolution_param->set_bias_term(false);
layer.reset(new ConvolutionLayer(layer_param));
layer->blobs().resize(1);
layer->blobs()[0].reset(new Blob(1, 1, 1, 3));
Dtype* weights_2 = layer->blobs()[0]->mutable_cpu_data();
weights_2[0] = -1;
weights_2[1] = 0;
weights_2[2] = 1;
layer->SetUp(sep_blob_bottom_vec, sep_blob_top_vec);
layer->Forward(sep_blob_bottom_vec, sep_blob_top_vec);
// Test equivalence of full and separable filters.
const Dtype* top_data = this->blob_top_->cpu_data();
const Dtype* sep_top_data = this->blob_top_2_->cpu_data();
for (int i = 0; i < this->blob_top_->count(); ++i) {
EXPECT_NEAR(top_data[i], sep_top_data[i], 1e-4);
}
}
TYPED_TEST(ConvolutionLayerTest, TestNDAgainst2D) {
typedef typename TypeParam::Dtype Dtype;
const int kernel_h = 11;
const int kernel_w = 13;
vector bottom_shape(4);
bottom_shape[0] = 15;
bottom_shape[1] = 18;
bottom_shape[2] = kernel_h * 2;
bottom_shape[3] = kernel_w * 2;
FillerParameter filler_param;
GaussianFiller filler(filler_param);
for (int i = 0; i < this->blob_bottom_vec_.size(); ++i) {
this->blob_bottom_vec_[i]->Reshape(bottom_shape);
filler.Fill(this->blob_bottom_vec_[i]);
}
LayerParameter layer_param;
ConvolutionParameter* convolution_param =
layer_param.mutable_convolution_param();
convolution_param->set_num_output(12);
convolution_param->set_bias_term(false);
convolution_param->set_group(6);
convolution_param->set_kernel_h(kernel_h);
convolution_param->set_kernel_w(kernel_w);
convolution_param->mutable_weight_filler()->set_type("gaussian");
Blob weights;
Blob top_diff;
// Shape and fill weights and top_diff.
bool copy_diff;
bool reshape;
{
ConvolutionLayer layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
top_diff.ReshapeLike(*this->blob_top_);
filler.Fill(&top_diff);
ASSERT_EQ(1, layer.blobs().size());
copy_diff = false; reshape = true;
weights.CopyFrom(*layer.blobs()[0], copy_diff, reshape);
}
vector propagate_down(1, true);
Blob result_2d;
Blob backward_result_2d;
Blob backward_weight_result_2d;
// Test with 2D im2col
{
caffe_set(this->blob_top_->count(), Dtype(0),
this->blob_top_->mutable_cpu_data());
caffe_set(this->blob_bottom_->count(), Dtype(0),
this->blob_bottom_->mutable_cpu_diff());
caffe_set(weights.count(), Dtype(0), weights.mutable_cpu_diff());
// Do SetUp and Forward; save Forward result in result_2d.
convolution_param->set_force_nd_im2col(false);
layer_param.set_type("Convolution");
Layer & layer_2d=*LayerRegistry::CreateLayer(layer_param);
layer_2d.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
ASSERT_EQ(1, layer_2d.blobs().size());
copy_diff = false; reshape = false;
layer_2d.blobs()[0]->CopyFrom(weights, copy_diff, reshape);
layer_2d.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
copy_diff = false; reshape = true;
result_2d.CopyFrom(*this->blob_top_, copy_diff, reshape);
// Copy pre-generated top diff into actual top diff;
// do Backward and save result in backward_result_2d.
ASSERT_EQ(this->blob_top_->shape(), top_diff.shape());
caffe_copy(top_diff.count(), top_diff.cpu_data(),
this->blob_top_->mutable_cpu_diff());
layer_2d.Backward(this->blob_top_vec_, propagate_down,
this->blob_bottom_vec_);
copy_diff = true; reshape = true;
backward_result_2d.CopyFrom(*this->blob_bottom_, copy_diff, reshape);
backward_weight_result_2d.CopyFrom(weights, copy_diff, reshape);
}
Blob result_nd;
Blob backward_result_nd;
Blob backward_weight_result_nd;
// Test with ND im2col
{
caffe_set(this->blob_top_->count(), Dtype(0),
this->blob_top_->mutable_cpu_data());
caffe_set(this->blob_bottom_->count(), Dtype(0),
this->blob_bottom_->mutable_cpu_diff());
caffe_set(weights.count(), Dtype(0), weights.mutable_cpu_diff());
// Do SetUp and Forward; save Forward result in result_nd.
convolution_param->set_force_nd_im2col(true);
layer_param.set_type("Convolution");
Layer& layer_nd=*LayerRegistry::CreateLayer(layer_param);
layer_nd.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
ASSERT_EQ(1, layer_nd.blobs().size());
copy_diff = false; reshape = false;
layer_nd.blobs()[0]->CopyFrom(weights, copy_diff, reshape);
layer_nd.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
copy_diff = false; reshape = true;
result_nd.CopyFrom(*this->blob_top_, copy_diff, reshape);
// Copy pre-generated top diff into actual top diff;
// do Backward and save result in backward_result_nd.
ASSERT_EQ(this->blob_top_->shape(), top_diff.shape());
caffe_copy(top_diff.count(), top_diff.cpu_data(),
this->blob_top_->mutable_cpu_diff());
layer_nd.Backward(this->blob_top_vec_, propagate_down,
this->blob_bottom_vec_);
copy_diff = true; reshape = true;
backward_result_nd.CopyFrom(*this->blob_bottom_, copy_diff, reshape);
backward_weight_result_nd.CopyFrom(weights, copy_diff, reshape);
}
ASSERT_EQ(result_nd.count(), result_2d.count());
for (int i = 0; i < result_2d.count(); ++i) {
EXPECT_EQ(result_2d.cpu_data()[i], result_nd.cpu_data()[i]);
}
ASSERT_EQ(backward_result_nd.count(), backward_result_2d.count());
for (int i = 0; i < backward_result_2d.count(); ++i) {
EXPECT_EQ(backward_result_2d.cpu_diff()[i],
backward_result_nd.cpu_diff()[i]);
}
ASSERT_EQ(backward_weight_result_nd.count(),
backward_weight_result_2d.count());
for (int i = 0; i < backward_weight_result_2d.count(); ++i) {
EXPECT_EQ(backward_weight_result_2d.cpu_diff()[i],
backward_weight_result_nd.cpu_diff()[i]);
}
}
#endif
} // namespace caffe