From a2bac02bf48b8f5630ea2f45ef5e288c1cc59acd Mon Sep 17 00:00:00 2001 From: Jeff Donahue Date: Wed, 27 Nov 2013 21:29:05 -0800 Subject: [PATCH] add flatten layer --- include/caffe/vision_layers.hpp | 21 ++++++ src/caffe/layer_factory.cpp | 2 + src/caffe/layers/flatten_layer.cpp | 57 ++++++++++++++++ src/caffe/test/test_flatten_layer.cpp | 93 +++++++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 src/caffe/layers/flatten_layer.cpp create mode 100644 src/caffe/test/test_flatten_layer.cpp diff --git a/include/caffe/vision_layers.hpp b/include/caffe/vision_layers.hpp index 2290f0f53af..111d56e7269 100644 --- a/include/caffe/vision_layers.hpp +++ b/include/caffe/vision_layers.hpp @@ -89,6 +89,27 @@ class DropoutLayer : public NeuronLayer { }; +template +class FlattenLayer : public Layer { + public: + explicit FlattenLayer(const LayerParameter& param) + : Layer(param) {} + virtual void SetUp(const vector*>& bottom, + vector*>* top); + + protected: + virtual void Forward_cpu(const vector*>& bottom, + vector*>* top); + virtual void Forward_gpu(const vector*>& bottom, + vector*>* top); + virtual Dtype Backward_cpu(const vector*>& top, + const bool propagate_down, vector*>* bottom); + virtual Dtype Backward_gpu(const vector*>& top, + const bool propagate_down, vector*>* bottom); + int channels_out_; +}; + + template class InnerProductLayer : public Layer { public: diff --git a/src/caffe/layer_factory.cpp b/src/caffe/layer_factory.cpp index b663cb2c7ac..f4fb5e54055 100644 --- a/src/caffe/layer_factory.cpp +++ b/src/caffe/layer_factory.cpp @@ -31,6 +31,8 @@ Layer* GetLayer(const LayerParameter& param) { return new DropoutLayer(param); } else if (type == "euclidean_loss") { return new EuclideanLossLayer(param); + } else if (type == "flatten") { + return new FlattenLayer(param); } else if (type == "im2col") { return new Im2colLayer(param); } else if (type == "infogain_loss") { diff --git a/src/caffe/layers/flatten_layer.cpp b/src/caffe/layers/flatten_layer.cpp new file mode 100644 index 00000000000..9ffe4d24a07 --- /dev/null +++ b/src/caffe/layers/flatten_layer.cpp @@ -0,0 +1,57 @@ +// Copyright 2013 Yangqing Jia + +#include + +#include "caffe/layer.hpp" +#include "caffe/vision_layers.hpp" +#include "caffe/util/math_functions.hpp" + +namespace caffe { + +template +void FlattenLayer::SetUp(const vector*>& bottom, + vector*>* top) { + CHECK_EQ(bottom.size(), 1) << "Flatten Layer takes a single blob as input."; + CHECK_EQ(top->size(), 1) << "Flatten Layer takes a single blob as output."; + channels_out_ = bottom[0]->channels() * bottom[0]->height() + * bottom[0]->width(); + (*top)[0]->Reshape(bottom[0]->num(), channels_out_, 1, 1); +}; + +template +void FlattenLayer::Forward_cpu(const vector*>& bottom, + vector*>* top) { + + const Dtype* bottom_data = bottom[0]->cpu_data(); + Dtype* top_data = (*top)[0]->mutable_cpu_data(); + caffe_copy(channels_out_, bottom_data, top_data); +} + +template +void FlattenLayer::Forward_gpu(const vector*>& bottom, + vector*>* top) { + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* top_data = (*top)[0]->mutable_gpu_data(); + caffe_gpu_copy(channels_out_, bottom_data, top_data); +} + +template +Dtype FlattenLayer::Backward_cpu(const vector*>& top, + const bool propagate_down, vector*>* bottom) { + const Dtype* top_diff = top[0]->cpu_diff(); + Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + caffe_copy(channels_out_, top_diff, bottom_diff); +} + + +template +Dtype FlattenLayer::Backward_gpu(const vector*>& top, + const bool propagate_down, vector*>* bottom) { + const Dtype* top_diff = top[0]->gpu_diff(); + Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + caffe_gpu_copy(channels_out_, top_diff, bottom_diff); +} + +INSTANTIATE_CLASS(FlattenLayer); + +} // namespace caffe diff --git a/src/caffe/test/test_flatten_layer.cpp b/src/caffe/test/test_flatten_layer.cpp new file mode 100644 index 00000000000..23bce9d9675 --- /dev/null +++ b/src/caffe/test/test_flatten_layer.cpp @@ -0,0 +1,93 @@ +// Copyright 2013 Yangqing Jia + +#include +#include + +#include "gtest/gtest.h" +#include "caffe/blob.hpp" +#include "caffe/common.hpp" +#include "caffe/filler.hpp" +#include "caffe/vision_layers.hpp" +#include "caffe/test/test_gradient_check_util.hpp" + +#include "caffe/test/test_caffe_main.hpp" + +namespace caffe { + +extern cudaDeviceProp CAFFE_TEST_CUDA_PROP; + +template +class FlattenLayerTest : public ::testing::Test { + protected: + FlattenLayerTest() + : blob_bottom_(new Blob(2, 3, 6, 5)), + blob_top_(new Blob()) { + // fill the values + FillerParameter filler_param; + GaussianFiller filler(filler_param); + filler.Fill(this->blob_bottom_); + blob_bottom_vec_.push_back(blob_bottom_); + blob_top_vec_.push_back(blob_top_); + }; + virtual ~FlattenLayerTest() { delete blob_bottom_; delete blob_top_; } + Blob* const blob_bottom_; + Blob* const blob_top_; + vector*> blob_bottom_vec_; + vector*> blob_top_vec_; +}; + +typedef ::testing::Types Dtypes; +TYPED_TEST_CASE(FlattenLayerTest, Dtypes); + +TYPED_TEST(FlattenLayerTest, TestSetup) { + LayerParameter layer_param; + FlattenLayer layer(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 * 6 * 5); + EXPECT_EQ(this->blob_top_->height(), 1); + EXPECT_EQ(this->blob_top_->width(), 1); +} + +TYPED_TEST(FlattenLayerTest, TestCPU) { + LayerParameter layer_param; + FlattenLayer layer(layer_param); + Caffe::set_mode(Caffe::CPU); + layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + for (int c = 0; c < 3 * 6 * 5; ++c) { + EXPECT_EQ(this->blob_top_->data_at(0, c, 0, 0), + this->blob_bottom_->data_at(0, c / (6 * 5), (c / 5) % 6, c % 5)); + } +} + +TYPED_TEST(FlattenLayerTest, TestGPU) { + LayerParameter layer_param; + FlattenLayer layer(layer_param); + Caffe::set_mode(Caffe::GPU); + layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + for (int c = 0; c < 3 * 6 * 5; ++c) { + EXPECT_EQ(this->blob_top_->data_at(0, c, 0, 0), + this->blob_bottom_->data_at(0, c / (6 * 5), (c / 5) % 6, c % 5)); + } +} + +TYPED_TEST(FlattenLayerTest, TestCPUGradient) { + LayerParameter layer_param; + Caffe::set_mode(Caffe::CPU); + FlattenLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-2); + checker.CheckGradientExhaustive(layer, this->blob_bottom_vec_, this->blob_top_vec_); +} + +TYPED_TEST(FlattenLayerTest, TestGPUGradient) { + LayerParameter layer_param; + Caffe::set_mode(Caffe::GPU); + FlattenLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-2); + checker.CheckGradientExhaustive(layer, this->blob_bottom_vec_, this->blob_top_vec_); +} + + +}