#include
#include
#include
#include "google/protobuf/text_format.h"
#include "gtest/gtest.h"
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/layers/absval_layer.hpp"
#include "caffe/layers/bnll_layer.hpp"
#include "caffe/layers/dropout_layer.hpp"
#include "caffe/layers/elu_layer.hpp"
#include "caffe/layers/exp_layer.hpp"
#include "caffe/layers/inner_product_layer.hpp"
#include "caffe/layers/log_layer.hpp"
#include "caffe/layers/power_layer.hpp"
#include "caffe/layers/prelu_layer.hpp"
#include "caffe/layers/relu_layer.hpp"
#include "caffe/layers/sigmoid_layer.hpp"
#include "caffe/layers/tanh_layer.hpp"
#include "caffe/layers/threshold_layer.hpp"
#ifdef USE_CUDNN
#include "caffe/layers/cudnn_relu_layer.hpp"
#include "caffe/layers/cudnn_sigmoid_layer.hpp"
#include "caffe/layers/cudnn_tanh_layer.hpp"
#endif
#include "caffe/test/test_caffe_main.hpp"
#include "caffe/test/test_gradient_check_util.hpp"
namespace caffe {
typedef ::testing::Types float_only;
#define TestDtypesAndDevices float_only
#define SET_LAYER(name) \
layer_param.set_type(#name);\
shared_ptr new_layer=\
LayerRegistry::CreateLayer(layer_param);\
shared_ptr< name ## Layer > layer= \
boost::static_pointer_cast< name ## Layer > (new_layer);\
if(0) layer=shared_ptr(new name ## Layer(layer_param));\
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
template
class NeuronLayerTest : public MultiDeviceTest {
typedef typename TypeParam::Dtype Dtype;
protected:
NeuronLayerTest()
: blob_bottom_(new Blob(2, 3, 4, 5)),
blob_top_(new Blob()) {
Caffe::set_random_seed(1701);
// 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 ~NeuronLayerTest() { delete blob_bottom_; delete blob_top_; }
Blob* const blob_bottom_;
Blob* const blob_top_;
vector blob_bottom_vec_;
vector blob_top_vec_;
void TestPReLU(PReLULayer *layer) {
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Now, check values
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
const Dtype* slope_data = layer->blobs()[0]->cpu_data();
int hw = this->blob_bottom_->height() * this->blob_bottom_->width();
int channels = this->blob_bottom_->channels();
bool channel_shared = layer->layer_param().prelu_param().channel_shared();
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
int c = channel_shared ? 0 : (i / hw) % channels;
EXPECT_EQ(top_data[i],
std::max(bottom_data[i], (Dtype)(0))
+ slope_data[c] * std::min(bottom_data[i], (Dtype)(0)));
}
}
};
TYPED_TEST_CASE(NeuronLayerTest, TestDtypesAndDevices);
TYPED_TEST(NeuronLayerTest, TestAbsVal) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
SET_LAYER(AbsVal);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
const int count = this->blob_bottom_->count();
for (int i = 0; i < count; ++i) {
EXPECT_EQ(top_data[i], fabs(bottom_data[i]));
}
}
TYPED_TEST(NeuronLayerTest, TestReLU) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
SET_LAYER(ReLU);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Now, check values
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
EXPECT_GE(top_data[i], 0.);
EXPECT_TRUE(top_data[i] == 0 || top_data[i] == bottom_data[i]);
}
}
#if 1
TYPED_TEST(NeuronLayerTest, TestReLUWithNegativeSlope) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
CHECK(google::protobuf::TextFormat::ParseFromString(
"relu_param { negative_slope: 0.01 }", &layer_param));
SET_LAYER(ReLU);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Now, check values
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
if (top_data[i] >= 0) {
EXPECT_FLOAT_EQ(top_data[i], bottom_data[i]);
} else {
EXPECT_FLOAT_EQ(top_data[i], bottom_data[i] * 0.01);
}
}
}
TYPED_TEST(NeuronLayerTest, TestSigmoid) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
SET_LAYER(Sigmoid);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Now, check values
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
EXPECT_FLOAT_EQ(top_data[i], 1. / (1 + exp(-bottom_data[i])));
// check that we squashed the value between 0 and 1
EXPECT_GE(top_data[i], 0.);
EXPECT_LE(top_data[i], 1.);
}
}
TYPED_TEST(NeuronLayerTest, TestTanH) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
int number=10;
this->blob_bottom_->Reshape(1,2,number,2);
for(int i=0;iblob_bottom_->mutable_cpu_data()[i]=i*10;
SET_LAYER(TanH);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Test exact values
for (int i = 0; i < this->blob_bottom_->num(); ++i) {
for (int j = 0; j < this->blob_bottom_->channels(); ++j) {
for (int k = 0; k < this->blob_bottom_->height(); ++k) {
for (int l = 0; l < this->blob_bottom_->width(); ++l) {
EXPECT_GE(this->blob_top_->data_at(i, j, k, l) + 1e-4,
(exp(2*this->blob_bottom_->data_at(i, j, k, l)) - 1) /
(exp(2*this->blob_bottom_->data_at(i, j, k, l)) + 1));
EXPECT_LE(this->blob_top_->data_at(i, j, k, l) - 1e-4,
(exp(2*this->blob_bottom_->data_at(i, j, k, l)) - 1) /
(exp(2*this->blob_bottom_->data_at(i, j, k, l)) + 1));
}
}
}
}
}
TYPED_TEST(NeuronLayerTest, TestBNLL) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
SET_LAYER(BNLL);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// Now, check values
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
for (int i = 0; i < this->blob_bottom_->count(); ++i) {
Dtype target=log(1+exp(bottom_data[i]));
EXPECT_NEAR(top_data[i], target,1e-4);
}
}
#endif
#if 0 /* Not try PReLU now */
TYPED_TEST(NeuronLayerTest, TestPReLUParam) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
PReLULayer layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
const Dtype* slopes = layer.blobs()[0]->cpu_data();
int count = layer.blobs()[0]->count();
for (int i = 0; i < count; ++i, ++slopes) {
EXPECT_EQ(*slopes, 0.25);
}
}
TYPED_TEST(NeuronLayerTest, TestPReLUForward) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
PReLULayer layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
FillerParameter filler_param;
GaussianFiller filler(filler_param);
filler.Fill(layer.blobs()[0].get());
this->TestPReLU(&layer);
}
TYPED_TEST(NeuronLayerTest, TestPReLUForwardChannelShared) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
layer_param.mutable_prelu_param()->set_channel_shared(true);
PReLULayer layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
this->TestPReLU(&layer);
}
TYPED_TEST(NeuronLayerTest, TestPReLUConsistencyReLU) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter prelu_layer_param;
LayerParameter relu_layer_param;
relu_layer_param.mutable_relu_param()->set_negative_slope(0.25);
PReLULayer prelu(prelu_layer_param);
ReLULayer relu(relu_layer_param);
// Set up blobs
vector blob_bottom_vec_2;
vector blob_top_vec_2;
shared_ptr blob_bottom_2(new Blob());
shared_ptr blob_top_2(new Blob());
blob_bottom_vec_2.push_back(blob_bottom_2.get());
blob_top_vec_2.push_back(blob_top_2.get());
blob_bottom_2->CopyFrom(*this->blob_bottom_, false, true);
// SetUp layers
prelu.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
relu.SetUp(blob_bottom_vec_2, blob_top_vec_2);
// Check forward
prelu.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
relu.Forward(this->blob_bottom_vec_, blob_top_vec_2);
for (int s = 0; s < blob_top_2->count(); ++s) {
EXPECT_EQ(this->blob_top_->cpu_data()[s], blob_top_2->cpu_data()[s]);
}
// Check backward
}
TYPED_TEST(NeuronLayerTest, TestPReLUInPlace) {
typedef typename TypeParam::Dtype Dtype;
// Set layer parameters
LayerParameter ip_layer_param;
LayerParameter prelu_layer_param;
InnerProductParameter *ip_param =
ip_layer_param.mutable_inner_product_param();
ip_param->mutable_weight_filler()->set_type("gaussian");
ip_param->set_num_output(3);
InnerProductLayer ip(ip_layer_param);
PReLULayer prelu(prelu_layer_param);
InnerProductLayer ip2(ip_layer_param);
PReLULayer prelu2(prelu_layer_param);
// Set up blobs
vector blob_bottom_vec_2;
vector blob_middle_vec_2;
vector blob_top_vec_2;
shared_ptr blob_bottom_2(new Blob());
shared_ptr blob_middle_2(new Blob());
shared_ptr blob_top_2(new Blob());
blob_bottom_vec_2.push_back(blob_bottom_2.get());
blob_middle_vec_2.push_back(blob_middle_2.get());
blob_top_vec_2.push_back(blob_top_2.get());
blob_bottom_2->CopyFrom(*this->blob_bottom_, false, true);
// SetUp layers
ip.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
prelu.SetUp(this->blob_top_vec_, this->blob_top_vec_);
ip2.SetUp(blob_bottom_vec_2, blob_middle_vec_2);
prelu2.SetUp(blob_middle_vec_2, blob_top_vec_2);
caffe_copy(ip2.blobs()[0]->count(), ip.blobs()[0]->cpu_data(),
ip2.blobs()[0]->mutable_cpu_data());
// Forward in-place
ip.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
prelu.Forward(this->blob_top_vec_, this->blob_top_vec_);
// Forward non-in-place
ip2.Forward(blob_bottom_vec_2, blob_middle_vec_2);
prelu2.Forward(blob_middle_vec_2, blob_top_vec_2);
// Check numbers
for (int s = 0; s < blob_top_2->count(); ++s) {
EXPECT_EQ(this->blob_top_->cpu_data()[s], blob_top_2->cpu_data()[s]);
}
// Fill top diff with random numbers
shared_ptr tmp_blob(new Blob());
tmp_blob->ReshapeLike(*blob_top_2.get());
FillerParameter filler_param;
GaussianFiller filler(filler_param);
filler.Fill(tmp_blob.get());
caffe_copy(blob_top_2->count(), tmp_blob->cpu_data(),
this->blob_top_->mutable_cpu_diff());
caffe_copy(blob_top_2->count(), tmp_blob->cpu_data(),
blob_top_2->mutable_cpu_diff());
// Backward in-place
vector propagate_down;
propagate_down.push_back(true);
prelu.Backward(this->blob_top_vec_, propagate_down, this->blob_top_vec_);
ip.Backward(this->blob_top_vec_, propagate_down, this->blob_bottom_vec_);
// Backward non-in-place
prelu2.Backward(blob_top_vec_2, propagate_down, blob_middle_vec_2);
ip2.Backward(blob_middle_vec_2, propagate_down, blob_bottom_vec_2);
// Check numbers
for (int s = 0; s < blob_bottom_2->count(); ++s) {
EXPECT_EQ(this->blob_bottom_->cpu_diff()[s], blob_bottom_2->cpu_diff()[s]);
}
for (int s = 0; s < ip.blobs()[0]->count(); ++s) {
EXPECT_EQ(ip.blobs()[0]->cpu_diff()[s], ip2.blobs()[0]->cpu_diff()[s]);
}
for (int s = 0; s < ip.blobs()[1]->count(); ++s) {
EXPECT_EQ(ip.blobs()[1]->cpu_diff()[s], ip2.blobs()[1]->cpu_diff()[s]);
}
for (int s = 0; s < prelu.blobs()[0]->count(); ++s) {
EXPECT_EQ(prelu.blobs()[0]->cpu_diff()[s],
prelu2.blobs()[0]->cpu_diff()[s]);
}
}
#endif
} // namespace caffe