Skip to content

Commit

Permalink
Merge pull request BVLC#755 from jeffdonahue/bhack-split_dim
Browse files Browse the repository at this point in the history
Finishing touches on @bhack's SliceLayer (BVLC#680)
  • Loading branch information
jeffdonahue committed Jul 22, 2014
2 parents dc8bcf3 + f7bdf1a commit 1d56e23
Show file tree
Hide file tree
Showing 6 changed files with 455 additions and 2 deletions.
38 changes: 38 additions & 0 deletions include/caffe/common_layers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,44 @@ class SplitLayer : public Layer<Dtype> {
int count_;
};

/* SliceLayer
Takes one blobs and slices it along either num or channel dim,
outputting the result into as many top blobs as needed.
*/
template <typename Dtype>
class SliceLayer : public Layer<Dtype> {
public:
explicit SliceLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
virtual void SetUp(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top);

virtual inline LayerParameter_LayerType type() const {
return LayerParameter_LayerType_SLICE;
}
virtual inline int ExactNumBottomBlobs() const { return 1; }
virtual inline int MinTopBlobs() const { return 2; }

protected:
virtual Dtype Forward_cpu(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top);
virtual Dtype Forward_gpu(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom);

Blob<Dtype> col_bob_;
int count_;
int num_;
int channels_;
int height_;
int width_;
int slice_dim_;
vector<int> slice_point_;
};

} // namespace caffe

#endif // CAFFE_COMMON_LAYERS_HPP_
2 changes: 2 additions & 0 deletions src/caffe/layer_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ Layer<Dtype>* GetLayer(const LayerParameter& param) {
return new SigmoidLayer<Dtype>(param);
case LayerParameter_LayerType_SIGMOID_CROSS_ENTROPY_LOSS:
return new SigmoidCrossEntropyLossLayer<Dtype>(param);
case LayerParameter_LayerType_SLICE:
return new SliceLayer<Dtype>(param);
case LayerParameter_LayerType_SOFTMAX:
return new SoftmaxLayer<Dtype>(param);
case LayerParameter_LayerType_SOFTMAX_LOSS:
Expand Down
140 changes: 140 additions & 0 deletions src/caffe/layers/slice_layer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2014 BVLC and contributors.

#include <vector>

#include "caffe/layer.hpp"
#include "caffe/vision_layers.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void SliceLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top) {
Layer<Dtype>::SetUp(bottom, top);
const SliceParameter& slice_param = this->layer_param_.slice_param();
slice_dim_ = slice_param.slice_dim();
CHECK_GE(slice_dim_, 0);
CHECK_LE(slice_dim_, 1) << "Can only slice num and channels";
slice_point_.clear();
for (int i = 0; i < slice_param.slice_point_size(); ++i) {
slice_point_.push_back(slice_param.slice_point(i));
}
count_ = 0;
num_ = bottom[0]->num();
channels_ = bottom[0]->channels();
height_ = bottom[0]->height();
width_ = bottom[0]->width();
if (slice_point_.size() != 0) {
CHECK_EQ(slice_point_.size(), top->size() - 1);
if (slice_dim_ == 0) {
CHECK_LE(top->size(), num_);
} else {
CHECK_LE(top->size(), channels_);
}
int prev = 0;
vector<int> slices;
for (int i = 0; i < slice_point_.size(); ++i) {
CHECK_GT(slice_point_[i], prev);
slices.push_back(slice_point_[i] - prev);
prev = slice_point_[i];
}
if (slice_dim_ == 0) {
slices.push_back(num_ - prev);
for (int i = 0; i < top->size(); ++i) {
(*top)[i]->Reshape(slices[i], channels_, height_, width_);
count_ += (*top)[i]->count();
}
} else {
slices.push_back(channels_ - prev);
for (int i = 0; i < top->size(); ++i) {
(*top)[i]->Reshape(num_, slices[i], height_, width_);
count_ += (*top)[i]->count();
}
}

} else {
if (slice_dim_ == 0) {
CHECK_EQ(num_ % top->size(), 0)
<< "Number of top blobs (" << top->size() << ") "
<< "should evenly divide input num ( " << num_ << ")";
num_ = num_ / top->size();
} else {
CHECK_EQ(channels_ % top->size(), 0)
<< "Number of top blobs (" << top->size() << ") "
<< "should evenly divide input channels ( " << channels_ << ")";
channels_ = channels_ / top->size();
}
for (int i = 0; i < top->size(); ++i) {
(*top)[i]->Reshape(num_, channels_, height_, width_);
count_ += (*top)[i]->count();
}
}
CHECK_EQ(count_, bottom[0]->count());
}

template <typename Dtype>
Dtype SliceLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top) {
const Dtype* bottom_data = bottom[0]->mutable_cpu_data();
if (slice_dim_ == 0) {
int offset_num = 0;
for (int i = 0; i < top->size(); ++i) {
Blob<Dtype>* blob = (*top)[i];
Dtype* top_data = blob->mutable_cpu_data();
caffe_copy(blob->count(), bottom_data + bottom[0]->offset(offset_num),
top_data);
offset_num += blob->num();
}
} else if (slice_dim_ == 1) {
int offset_channel = 0;
for (int i = 0; i < top->size(); ++i) {
Blob<Dtype>* blob = (*top)[i];
Dtype* top_data = blob->mutable_cpu_data();
const int num_elem = blob->channels() * blob->height() * blob->width();
for (int n = 0; n < num_; ++n) {
caffe_copy(num_elem, bottom_data + bottom[0]->offset(n, offset_channel),
top_data + blob->offset(n));
}
offset_channel += blob->channels();
}
} // slice_dim_ is guaranteed to be 0 or 1 by SetUp.
return Dtype(0.);
}

template <typename Dtype>
void SliceLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom) {
if (!propagate_down[0]) { return; }
Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff();
if (slice_dim_ == 0) {
int offset_num = 0;
for (int i = 0; i < top.size(); ++i) {
Blob<Dtype>* blob = top[i];
const Dtype* top_diff = blob->cpu_diff();
caffe_copy(blob->count(), top_diff,
bottom_diff + (*bottom)[0]->offset(offset_num));
offset_num += blob->num();
}
} else if (slice_dim_ == 1) {
int offset_channel = 0;
for (int i = 0; i < top.size(); ++i) {
Blob<Dtype>* blob = top[i];
const Dtype* top_diff = blob->cpu_diff();
const int num_elem = blob->channels() * blob->height() * blob->width();
for (int n = 0; n < num_; ++n) {
caffe_copy(num_elem, top_diff + blob->offset(n),
bottom_diff + (*bottom)[0]->offset(n, offset_channel));
}
offset_channel += blob->channels();
}
} // slice_dim_ is guaranteed to be 0 or 1 by SetUp.
}

#ifdef CPU_ONLY
STUB_GPU(SliceLayer);
#endif

INSTANTIATE_CLASS(SliceLayer);

} // namespace caffe
71 changes: 71 additions & 0 deletions src/caffe/layers/slice_layer.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2014 BVLC and contributors.

#include <vector>

#include "caffe/layer.hpp"
#include "caffe/vision_layers.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
Dtype SliceLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top) {
const Dtype* bottom_data = bottom[0]->mutable_gpu_data();
if (slice_dim_ == 0) {
int offset_num = 0;
for (int i = 0; i < top->size(); ++i) {
Blob<Dtype>* blob = (*top)[i];
Dtype* top_data = blob->mutable_gpu_data();
caffe_copy(blob->count(), bottom_data + bottom[0]->offset(offset_num),
top_data);
offset_num += blob->num();
}
} else if (slice_dim_ == 1) {
int offset_channel = 0;
for (int i = 0; i < top->size(); ++i) {
Blob<Dtype>* blob = (*top)[i];
Dtype* top_data = blob->mutable_gpu_data();
const int num_elem = blob->channels() * blob->height() * blob->width();
for (int n = 0; n < num_; ++n) {
caffe_copy(num_elem, bottom_data + bottom[0]->offset(n, offset_channel),
top_data + blob->offset(n));
}
offset_channel += blob->channels();
}
} // slice_dim_ is guaranteed to be 0 or 1 by SetUp.
return Dtype(0.);
}

template <typename Dtype>
void SliceLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom) {
if (!propagate_down[0]) { return; }
Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff();
if (slice_dim_ == 0) {
int offset_num = 0;
for (int i = 0; i < top.size(); ++i) {
Blob<Dtype>* blob = top[i];
const Dtype* top_diff = blob->gpu_diff();
caffe_copy(blob->count(), top_diff,
bottom_diff + (*bottom)[0]->offset(offset_num));
offset_num += blob->num();
}
} else if (slice_dim_ == 1) {
int offset_channel = 0;
for (int i = 0; i < top.size(); ++i) {
Blob<Dtype>* blob = top[i];
const Dtype* top_diff = blob->gpu_diff();
const int num_elem = blob->channels() * blob->height() * blob->width();
for (int n = 0; n < num_; ++n) {
caffe_copy(num_elem, top_diff + blob->offset(n),
bottom_diff + (*bottom)[0]->offset(n, offset_channel));
}
offset_channel += blob->channels();
}
} // slice_dim_ is guaranteed to be 0 or 1 by SetUp.
}

INSTANTIATE_CLASS(SliceLayer);

} // namespace caffe
16 changes: 14 additions & 2 deletions src/caffe/proto/caffe.proto
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ message SolverState {
// NOTE
// Update the next available ID when you add a new LayerParameter field.
//
// LayerParameter next available ID: 31 (last added: relu_param)
// LayerParameter next available ID: 32 (last added: slice_param)
message LayerParameter {
repeated string bottom = 2; // the name of the bottom blobs
repeated string top = 3; // the name of the top blobs
Expand All @@ -129,7 +129,7 @@ message LayerParameter {
// line above the enum. Update the next available ID when you add a new
// LayerType.
//
// LayerType next available ID: 33 (last added: DUMMY_DATA)
// LayerType next available ID: 34 (last added: SLICE)
enum LayerType {
// "NONE" layer type is 0th enum element so that we don't cause confusion
// by defaulting to an existent LayerType (instead, should usually error if
Expand Down Expand Up @@ -164,6 +164,7 @@ message LayerParameter {
SOFTMAX = 20;
SOFTMAX_LOSS = 21;
SPLIT = 22;
SLICE = 33;
TANH = 23;
WINDOW_DATA = 24;
THRESHOLD = 31;
Expand Down Expand Up @@ -211,6 +212,7 @@ message LayerParameter {
optional WindowDataParameter window_data_param = 20;
optional ThresholdParameter threshold_param = 25;
optional HingeLossParameter hinge_loss_param = 29;
optional SliceParameter slice_param = 31;

// DEPRECATED: The layer parameters specified as a V0LayerParameter.
// This should never be used by any code except to upgrade to the new
Expand Down Expand Up @@ -440,6 +442,16 @@ message ReLUParameter {
optional float negative_slope = 1 [default = 0];
}

// Message that stores parameters used by SliceLayer
message SliceParameter {
// SliceLayer needs to know which dimension to slice across.
// Currently, SliceLayer only supports slicing across num (dim 0)
// and channels (dim 1).
// By default, SliceLayer slices across channels.
optional uint32 slice_dim = 1 [default = 1];
repeated uint32 slice_point = 2;
}

// Message that stores parameters used by WindowDataLayer
message WindowDataParameter {
// Specify the data source.
Expand Down
Loading

0 comments on commit 1d56e23

Please sign in to comment.