-
Notifications
You must be signed in to change notification settings - Fork 18.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ae4a5b1
commit b28e03d
Showing
4 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#include <algorithm> | ||
#include <vector> | ||
|
||
#include "caffe/layer.hpp" | ||
#include "caffe/util/math_functions.hpp" | ||
#include "caffe/vision_layers.hpp" | ||
|
||
namespace caffe { | ||
|
||
template <typename Dtype> | ||
void FilterLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom, | ||
const vector<Blob<Dtype>*>& top) { | ||
CHECK_EQ(top.size(), bottom.size()-1) << | ||
"Top.size() should be equal to bottom.size() - 1"; | ||
first_reshape_ = true; | ||
} | ||
|
||
template <typename Dtype> | ||
void FilterLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, | ||
const vector<Blob<Dtype>*>& top) { | ||
// bottom[0...k-1] are the blobs to filter | ||
// bottom[last] is the "selector_blob" | ||
int selector_index = bottom.size() - 1; | ||
for (int i = 1; i < bottom[selector_index]->num_axes(); ++i) { | ||
CHECK_EQ(bottom[selector_index]->shape(i), 1) | ||
<< "Selector blob must have all shapes == 1 (except the first one)"; | ||
} | ||
for (int i = 0; i < bottom.size()-1; i++) { | ||
CHECK_EQ(bottom[selector_index]->shape(0), bottom[i]->shape(0)) << | ||
"Each bottom should have the same dimension as the selector blob"; | ||
} | ||
|
||
const Dtype* bottom_data_selector = bottom[selector_index]->cpu_data(); | ||
indices_to_forward_.clear(); | ||
|
||
// look for non-zero elements in bottom[0]. Items of each bottom that | ||
// have the same index as the items in bottom[0] with value == non-zero | ||
// will be forwarded | ||
for (int item_id = 0; item_id < bottom[selector_index]->shape(0); ++item_id) { | ||
// we don't need an offset because item size == 1 | ||
const Dtype* tmp_data_selector = bottom_data_selector + item_id; | ||
if (*tmp_data_selector) { | ||
indices_to_forward_.push_back(item_id); | ||
} | ||
} | ||
// only filtered items will be forwarded | ||
int new_tops_num = indices_to_forward_.size(); | ||
// init | ||
if (first_reshape_) { | ||
new_tops_num = bottom[0]->shape(0); | ||
first_reshape_ = false; | ||
} | ||
for (int t = 0; t < top.size(); t++) { | ||
int num_axes = bottom[t]->num_axes(); | ||
vector<int> shape_top(num_axes); | ||
shape_top[0] = new_tops_num; | ||
for (int ts = 1; ts < num_axes; ts++) | ||
shape_top[ts] = bottom[t]->shape(ts); | ||
top[t]->Reshape(shape_top); | ||
} | ||
} | ||
|
||
template <typename Dtype> | ||
void FilterLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, | ||
const vector<Blob<Dtype>*>& top) { | ||
int new_tops_num = indices_to_forward_.size(); | ||
// forward all filtered items for all bottoms but the Selector (bottom[last]) | ||
for (int t = 0; t < top.size(); t++) { | ||
const Dtype* bottom_data = bottom[t]->cpu_data(); | ||
Dtype* top_data = top[t]->mutable_cpu_data(); | ||
int dim = bottom[t]->count() / bottom[t]->shape(0); | ||
for (int n = 0; n < new_tops_num; n++) { | ||
int data_offset_top = top[t]->offset(n); | ||
int data_offset_bottom = bottom[t]->offset(indices_to_forward_[n]); | ||
caffe_copy(dim, bottom_data + data_offset_bottom, | ||
top_data + data_offset_top); | ||
} | ||
} | ||
} | ||
|
||
template <typename Dtype> | ||
void FilterLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, | ||
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { | ||
if (propagate_down[bottom.size() - 1]) { | ||
LOG(FATAL) << this->type() | ||
<< "Layer cannot backpropagate to filter index inputs"; | ||
} | ||
for (int i = 0; i < top.size(); i++) { | ||
// bottom[last] is the selector and never needs backpropagation | ||
// so we can iterate over top vector because top.size() == bottom.size() -1 | ||
if (propagate_down[i]) { | ||
const int dim = top[i]->count() / top[i]->shape(0); | ||
int next_to_backward_offset = 0; | ||
int batch_offset = 0; | ||
int data_offset_bottom = 0; | ||
int data_offset_top = 0; | ||
for (int n = 0; n < bottom[i]->shape(0); n++) { | ||
data_offset_bottom = bottom[i]->offset(n); | ||
if (next_to_backward_offset >= indices_to_forward_.size()) { | ||
// we already visited all items that were been forwarded, so | ||
// just set to zero remaining ones | ||
caffe_set(dim, Dtype(0), | ||
bottom[i]->mutable_cpu_diff() + data_offset_bottom); | ||
} else { | ||
batch_offset = indices_to_forward_[next_to_backward_offset]; | ||
if (n != batch_offset) { // this data was not been forwarded | ||
caffe_set(dim, Dtype(0), | ||
bottom[i]->mutable_cpu_diff() + data_offset_bottom); | ||
} else { // this data was been forwarded | ||
data_offset_top = top[i]->offset(next_to_backward_offset); | ||
next_to_backward_offset++; // point to next forwarded item index | ||
caffe_copy(dim, top[i]->mutable_cpu_diff() + data_offset_top, | ||
bottom[i]->mutable_cpu_diff() + data_offset_bottom); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
#ifdef CPU_ONLY | ||
STUB_GPU(FilterLayer); | ||
#endif | ||
|
||
INSTANTIATE_CLASS(FilterLayer); | ||
REGISTER_LAYER_CLASS(Filter); | ||
|
||
} // namespace caffe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#include <vector> | ||
|
||
#include "caffe/layer.hpp" | ||
#include "caffe/util/math_functions.hpp" | ||
#include "caffe/vision_layers.hpp" | ||
|
||
namespace caffe { | ||
|
||
template <typename Dtype> | ||
void FilterLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom, | ||
const vector<Blob<Dtype>*>& top) { | ||
int new_tops_num = indices_to_forward_.size(); | ||
// forward all filtered items for all bottoms but the Selector (bottom[last]) | ||
for (int t = 0; t < top.size(); t++) { | ||
const Dtype* bottom_data = bottom[t]->gpu_data(); | ||
Dtype* top_data = top[t]->mutable_gpu_data(); | ||
int dim = bottom[t]->count() / bottom[t]->shape(0); | ||
for (int n = 0; n < new_tops_num; n++) { | ||
int data_offset_top = top[t]->offset(n); | ||
int data_offset_bottom = bottom[t]->offset(indices_to_forward_[n]); | ||
caffe_copy(dim, bottom_data + data_offset_bottom, | ||
top_data + data_offset_top); | ||
} | ||
} | ||
} | ||
|
||
template <typename Dtype> | ||
void FilterLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top, | ||
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { | ||
if (propagate_down[bottom.size() - 1]) { | ||
LOG(FATAL) << this->type() | ||
<< "Layer cannot backpropagate to filter index inputs"; | ||
} | ||
for (int i = 0; i < top.size(); i++) { | ||
// bottom[last] is the selector and never needs backpropagation | ||
// so we can iterate over top vector because top.size() == bottom.size() -1 | ||
if (propagate_down[i]) { | ||
const int dim = top[i]->count() / top[i]->shape(0); | ||
int next_to_backward_offset = 0; | ||
int batch_offset = 0; | ||
int data_offset_bottom = 0; | ||
int data_offset_top = 0; | ||
for (int n = 0; n < bottom[i]->shape(0); n++) { | ||
if (next_to_backward_offset >= indices_to_forward_.size()) { | ||
// we already visited all items that were been forwarded, so | ||
// just set to zero remaining ones | ||
data_offset_bottom = top[i]->offset(n); | ||
caffe_set(dim, Dtype(0), | ||
bottom[i]->mutable_gpu_diff() + data_offset_bottom); | ||
} else { | ||
batch_offset = indices_to_forward_[next_to_backward_offset]; | ||
data_offset_bottom = top[i]->offset(n); | ||
if (n != batch_offset) { // this data was not been forwarded | ||
caffe_set(dim, Dtype(0), | ||
bottom[i]->mutable_gpu_diff() + data_offset_bottom); | ||
} else { // this data was been forwarded | ||
data_offset_top = top[i]->offset(next_to_backward_offset); | ||
next_to_backward_offset++; // point to next forwarded item index | ||
caffe_copy(dim, top[i]->mutable_gpu_diff() + data_offset_top, | ||
bottom[i]->mutable_gpu_diff() + data_offset_bottom); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
INSTANTIATE_LAYER_GPU_FUNCS(FilterLayer); | ||
|
||
} // namespace caffe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#include <cstring> | ||
#include <limits> | ||
#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 FilterLayerTest : public MultiDeviceTest<TypeParam> { | ||
typedef typename TypeParam::Dtype Dtype; | ||
|
||
protected: | ||
FilterLayerTest() | ||
: blob_bottom_data_(new Blob<Dtype>(4, 3, 6, 4)), | ||
blob_bottom_labels_(new Blob<Dtype>(4, 1, 1, 1)), | ||
blob_bottom_selector_(new Blob<Dtype>(4, 1, 1, 1)), | ||
blob_top_data_(new Blob<Dtype>()), | ||
blob_top_labels_(new Blob<Dtype>()) {} | ||
virtual void SetUp() { | ||
// fill the values | ||
Caffe::set_random_seed(1890); | ||
FillerParameter filler_param; | ||
GaussianFiller<Dtype> filler(filler_param); | ||
// fill the selector blob | ||
Dtype* bottom_data_selector_ = blob_bottom_selector_->mutable_cpu_data(); | ||
bottom_data_selector_[0] = 0; | ||
bottom_data_selector_[1] = 1; | ||
bottom_data_selector_[2] = 1; | ||
bottom_data_selector_[3] = 0; | ||
// fill the other bottom blobs | ||
filler.Fill(blob_bottom_data_); | ||
for (int i = 0; i < blob_bottom_labels_->count(); ++i) { | ||
blob_bottom_labels_->mutable_cpu_data()[i] = caffe_rng_rand() % 5; | ||
} | ||
blob_bottom_vec_.push_back(blob_bottom_data_); | ||
blob_bottom_vec_.push_back(blob_bottom_labels_); | ||
blob_bottom_vec_.push_back(blob_bottom_selector_); | ||
blob_top_vec_.push_back(blob_top_data_); | ||
blob_top_vec_.push_back(blob_top_labels_); | ||
} | ||
virtual ~FilterLayerTest() { | ||
delete blob_bottom_data_; | ||
delete blob_bottom_labels_; | ||
delete blob_bottom_selector_; | ||
delete blob_top_data_; | ||
delete blob_top_labels_; | ||
} | ||
Blob<Dtype>* const blob_bottom_data_; | ||
Blob<Dtype>* const blob_bottom_labels_; | ||
Blob<Dtype>* const blob_bottom_selector_; | ||
// blobs for the top of FilterLayer | ||
Blob<Dtype>* const blob_top_data_; | ||
Blob<Dtype>* const blob_top_labels_; | ||
vector<Blob<Dtype>*> blob_bottom_vec_; | ||
vector<Blob<Dtype>*> blob_top_vec_; | ||
}; | ||
|
||
TYPED_TEST_CASE(FilterLayerTest, TestDtypesAndDevices); | ||
|
||
TYPED_TEST(FilterLayerTest, TestReshape) { | ||
typedef typename TypeParam::Dtype Dtype; | ||
LayerParameter layer_param; | ||
FilterLayer<Dtype> layer(layer_param); | ||
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); | ||
layer.Reshape(this->blob_bottom_vec_, this->blob_top_vec_); | ||
// In the test first and last items should have been filtered | ||
// so we just expect 2 remaining items | ||
EXPECT_EQ(this->blob_top_data_->shape(0), 2); | ||
EXPECT_EQ(this->blob_top_labels_->shape(0), 2); | ||
EXPECT_GT(this->blob_bottom_data_->shape(0), | ||
this->blob_top_data_->shape(0)); | ||
EXPECT_GT(this->blob_bottom_labels_->shape(0), | ||
this->blob_top_labels_->shape(0)); | ||
for (int i = 1; i < this->blob_bottom_labels_->num_axes(); i++) { | ||
EXPECT_EQ(this->blob_bottom_labels_->shape(i), | ||
this->blob_top_labels_->shape(i)); | ||
} | ||
} | ||
|
||
TYPED_TEST(FilterLayerTest, TestForward) { | ||
typedef typename TypeParam::Dtype Dtype; | ||
LayerParameter layer_param; | ||
FilterLayer<Dtype> layer(layer_param); | ||
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); | ||
layer.Reshape(this->blob_bottom_vec_, this->blob_top_vec_); | ||
layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); | ||
EXPECT_EQ(this->blob_top_labels_->data_at(0, 0, 0, 0), | ||
this->blob_bottom_labels_->data_at(1, 0, 0, 0)); | ||
EXPECT_EQ(this->blob_top_labels_->data_at(1, 0, 0, 0), | ||
this->blob_bottom_labels_->data_at(2, 0, 0, 0)); | ||
|
||
int dim = this->blob_top_data_->count() / | ||
this->blob_top_data_->shape(0); | ||
const Dtype* top_data = this->blob_top_data_->cpu_data(); | ||
const Dtype* bottom_data = this->blob_bottom_data_->cpu_data(); | ||
// selector is 0 1 1 0, so we need to compare bottom(1,c,h,w) | ||
// with top(0,c,h,w) and bottom(2,c,h,w) with top(1,c,h,w) | ||
bottom_data += dim; // bottom(1,c,h,w) | ||
for (size_t n = 0; n < dim; n++) | ||
EXPECT_EQ(top_data[n], bottom_data[n]); | ||
|
||
bottom_data += dim; // bottom(2,c,h,w) | ||
top_data += dim; // top(1,c,h,w) | ||
for (size_t n = 0; n < dim; n++) | ||
EXPECT_EQ(top_data[n], bottom_data[n]); | ||
} | ||
|
||
TYPED_TEST(FilterLayerTest, TestGradient) { | ||
typedef typename TypeParam::Dtype Dtype; | ||
LayerParameter layer_param; | ||
FilterLayer<Dtype> layer(layer_param); | ||
GradientChecker<Dtype> checker(1e-2, 1e-3); | ||
// check only input 0 (data) because labels and selector | ||
// don't need backpropagation | ||
checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, | ||
this->blob_top_vec_, 0); | ||
} | ||
|
||
} // namespace caffe |