Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Very simple version of ReshapeLayer #2088

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions include/caffe/common_layers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,55 @@ class MVNLayer : public Layer<Dtype> {
Blob<Dtype> sum_multiplier_;
};

/**
* @brief Reshapes an input Blob.
*/
template <typename Dtype>
class ReshapeLayer : public Layer<Dtype> {
public:
explicit ReshapeLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);

virtual inline const char* type() const { return "Reshape"; }
virtual inline int ExactNumBottomBlobs() const { return 1; }
virtual inline int ExactNumTopBlobs() const { return 1; }

protected:
/**
* @param bottom input Blob vector (length 1)
* -# @f$ (D_1 \times D_2 \times ... \times D_m) @f$
* the inputs
* @param top output Blob vector (length 1)
* -# @f$ (d_1 \times d_2 \times ... \times d_n) @f$,
* the outputs -- i.e., the (virtually) copied inputs.
* The shape is specified by <code>reshape_param.shape()</code>, and the
* product of the dimensions in the new shape must match that of the
* input shape; i.e., @f$ d_1 d_2 ... d_n = D_1 D_2 ... D_m @f$.
*/
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {}
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {}

/**
* @brief Computes the error gradient w.r.t. the concatenate inputs.
*
* @param top output Blob vector (length 1), providing the error gradient with
* respect to the outputs
* @param propagate_down see Layer::Backward.
* @param bottom input Blob vector (length K), into which the top error
* gradient is (virtually) copied
*/
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {}
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {}
};

/**
* @brief Ignores bottom blobs while producing no top blobs. (This is useful
* to suppress outputs during testing.)
Expand Down
26 changes: 26 additions & 0 deletions src/caffe/layers/reshape_layer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <vector>

#include "caffe/common_layers.hpp"
#include "caffe/layer.hpp"

namespace caffe {

template <typename Dtype>
void ReshapeLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
top[0]->Reshape(this->layer_param_.reshape_param().shape());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@longjon I guess this should technically all be done in LayerSetUp since the top shape doesn't vary with the bottom shape? (But then if special options were added when certain dims of the shape are set to 0 or -1, the top would then vary with the bottom shape, so it would be moved to Reshape then? Maybe better to keep it in Reshape then?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, to me it feels like this should go in LayerSetUp. If special options were added so that top shape depended on bottom shape, then Reshape would be needed. But that isn't the case in this code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed. I kept the CHECK_EQ for counts in Reshape though, which still seems right.

top[0]->ShareData(*bottom[0]);
top[0]->ShareDiff(*bottom[0]);
}

template <typename Dtype>
void ReshapeLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
CHECK_EQ(top[0]->count(), bottom[0]->count())
<< "new shape must have the same count as input";
}

INSTANTIATE_CLASS(ReshapeLayer);
REGISTER_LAYER_CLASS(Reshape);

} // namespace caffe
10 changes: 9 additions & 1 deletion src/caffe/proto/caffe.proto
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ message ParamSpec {
// NOTE
// Update the next available ID when you add a new LayerParameter field.
//
// LayerParameter next available layer-specific ID: 132 (last added: prelu_param)
// LayerParameter next available layer-specific ID: 133 (last added: reshape_param)
message LayerParameter {
optional string name = 1; // the layer name
optional string type = 2; // the layer type
Expand Down Expand Up @@ -326,6 +326,7 @@ message LayerParameter {
optional PReLUParameter prelu_param = 131;
optional PythonParameter python_param = 130;
optional ReLUParameter relu_param = 123;
optional ReshapeParameter reshape_param = 132;
optional SigmoidParameter sigmoid_param = 124;
optional SoftmaxParameter softmax_param = 125;
optional SliceParameter slice_param = 126;
Expand Down Expand Up @@ -659,6 +660,13 @@ message PythonParameter {
optional string layer = 2;
}

// Message that stores parameters used by ReshapeLayer
message ReshapeParameter {
// The new shape of the Blob. Must have the same "count" (product of
// dimensions) as the input Blob.
optional BlobShape shape = 1;
}

// Message that stores parameters used by ReLULayer
message ReLUParameter {
// Allow non-zero slope for negative inputs to speed up optimization
Expand Down
138 changes: 138 additions & 0 deletions src/caffe/test/test_reshape_layer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <cstring>
#include <vector>

#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_caffe_main.hpp"
#include "caffe/test/test_gradient_check_util.hpp"

namespace caffe {

template <typename TypeParam>
class ReshapeLayerTest : public MultiDeviceTest<TypeParam> {
typedef typename TypeParam::Dtype Dtype;
protected:
ReshapeLayerTest()
: blob_bottom_(new Blob<Dtype>(2, 3, 6, 5)),
blob_top_(new Blob<Dtype>()) {
Caffe::set_random_seed(1701);
// fill the values
FillerParameter filler_param;
GaussianFiller<Dtype> filler(filler_param);
filler.Fill(this->blob_bottom_);
blob_bottom_vec_.push_back(blob_bottom_);
blob_top_vec_.push_back(blob_top_);
}
virtual ~ReshapeLayerTest() { delete blob_bottom_; delete blob_top_; }
Blob<Dtype>* const blob_bottom_;
Blob<Dtype>* const blob_top_;
vector<Blob<Dtype>*> blob_bottom_vec_;
vector<Blob<Dtype>*> blob_top_vec_;
};

TYPED_TEST_CASE(ReshapeLayerTest, TestDtypesAndDevices);

TYPED_TEST(ReshapeLayerTest, TestSetup) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
BlobShape* shape = layer_param.mutable_reshape_param()->mutable_shape();
shared_ptr<ReshapeLayer<Dtype> > layer;

shape->Clear();
shape->add_dim(2 * 3 * 6 * 5);
layer.reset(new ReshapeLayer<Dtype>(layer_param));
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
ASSERT_EQ(this->blob_top_->num_axes(), 1);
EXPECT_EQ(this->blob_top_->shape(0), 2 * 3 * 6 * 5);

shape->Clear();
shape->add_dim(2 * 3 * 6);
shape->add_dim(5);
layer.reset(new ReshapeLayer<Dtype>(layer_param));
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
ASSERT_EQ(this->blob_top_->num_axes(), 2);
EXPECT_EQ(this->blob_top_->shape(0), 2 * 3 * 6);
EXPECT_EQ(this->blob_top_->shape(1), 5);

shape->Clear();
shape->add_dim(6);
shape->add_dim(1);
shape->add_dim(2);
shape->add_dim(3);
shape->add_dim(1);
shape->add_dim(5);
layer.reset(new ReshapeLayer<Dtype>(layer_param));
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
ASSERT_EQ(this->blob_top_->num_axes(), 6);
EXPECT_EQ(this->blob_top_->shape(0), 6);
EXPECT_EQ(this->blob_top_->shape(1), 1);
EXPECT_EQ(this->blob_top_->shape(2), 2);
EXPECT_EQ(this->blob_top_->shape(3), 3);
EXPECT_EQ(this->blob_top_->shape(4), 1);
EXPECT_EQ(this->blob_top_->shape(5), 5);
}

TYPED_TEST(ReshapeLayerTest, TestForward) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
BlobShape* shape = layer_param.mutable_reshape_param()->mutable_shape();
shape->add_dim(6);
shape->add_dim(2);
shape->add_dim(3);
shape->add_dim(5);
ReshapeLayer<Dtype> layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
EXPECT_EQ(this->blob_top_->cpu_data()[i],
this->blob_bottom_->cpu_data()[i]);
}
}

TYPED_TEST(ReshapeLayerTest, TestForwardAfterReshape) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
BlobShape* shape = layer_param.mutable_reshape_param()->mutable_shape();
shape->add_dim(6);
shape->add_dim(2);
shape->add_dim(3);
shape->add_dim(5);
ReshapeLayer<Dtype> layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// We know the above produced the correct result from TestForward.
// Reshape the bottom and call layer.Reshape, then try again.
vector<int> new_bottom_shape(1, 2 * 3 * 6 * 5);
this->blob_bottom_->Reshape(new_bottom_shape);
layer.Reshape(this->blob_bottom_vec_, this->blob_top_vec_);
FillerParameter filler_param;
GaussianFiller<Dtype> filler(filler_param);
filler.Fill(this->blob_bottom_);
layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
EXPECT_EQ(this->blob_top_->cpu_data()[i],
this->blob_bottom_->cpu_data()[i]);
}
}

TYPED_TEST(ReshapeLayerTest, TestGradient) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
BlobShape* shape = layer_param.mutable_reshape_param()->mutable_shape();
shape->add_dim(6);
shape->add_dim(2);
shape->add_dim(3);
shape->add_dim(5);
ReshapeLayer<Dtype> layer(layer_param);
GradientChecker<Dtype> checker(1e-2, 1e-2);
checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_,
this->blob_top_vec_);
}


} // namespace caffe