diff --git a/paddle/gserver/layers/ConvTransBaseLayer.cpp b/paddle/gserver/layers/ConvTransBaseLayer.cpp index 68fb48a38b5187ea81055351c9cf804b01e0f2b4..1b58b7fed43d498f2e69229fb20cd6eef1ef3d34 100644 --- a/paddle/gserver/layers/ConvTransBaseLayer.cpp +++ b/paddle/gserver/layers/ConvTransBaseLayer.cpp @@ -23,6 +23,17 @@ bool ConvTransBaseLayer::init(const LayerMap& layerMap, Layer::init(layerMap, parameterMap); /* Initialize the convolutional layer parameter */ + /* Everything is the same as ConvBaseLayer.cpp except that the meaning of + * num_filters and channel is switched. + * + * In the config, num_filters refer to the number of feature maps in the + * output of convTransLayer, and channel refer to the number of feature maps + * in the input of convTransLayer. + * + * However, within the convTrans class, the channel is related to the output + * and num_filters is related to the input, so that it is consistent with the + * settings in convLayer. + * */ channel_ = config_.num_filters(); sharedBiases_ = config_.shared_biases(); for (auto& inputConfig : config_.inputs()) { diff --git a/paddle/gserver/layers/ConvTransBaseLayer.h b/paddle/gserver/layers/ConvTransBaseLayer.h index 467a2605697593233035091a5f851a4f7bbab652..d7acc184cc9acd88b18fcb7aa4bf2a93f24909ad 100644 --- a/paddle/gserver/layers/ConvTransBaseLayer.h +++ b/paddle/gserver/layers/ConvTransBaseLayer.h @@ -96,6 +96,11 @@ public: * - outputSize = 5; */ + /* + * In order to be consistent with the convLayer, here the outputSize is + * actually the size of the input image of convTransLayer, and the image size + * is actually the size of the output image of convTransLayer + */ int imageSize(int outputSize, int filterSize, int padding, int stride) { int imageSize; if (!caffeMode_) { diff --git a/paddle/gserver/layers/ExpandConvTransLayer.cpp b/paddle/gserver/layers/ExpandConvTransLayer.cpp index 56cc042653b60316b4b8ce740d4904ca2e10c8ae..67c045821d173c0cffb0c29e8e6d02bd343fcd71 100644 --- a/paddle/gserver/layers/ExpandConvTransLayer.cpp +++ b/paddle/gserver/layers/ExpandConvTransLayer.cpp @@ -17,6 +17,11 @@ limitations under the License. */ #include "paddle/utils/Stat.h" #include "ExpandConvTransLayer.h" +/* The implementation of the convTransLayer is basically a swap of forward and + * backward of the original convLayer. + * The variable naming follows the convention of the convLayer. + * */ + namespace paddle { REGISTER_LAYER(exconvt, ExpandConvTransLayer); diff --git a/paddle/gserver/layers/ExpandConvTransLayer.h b/paddle/gserver/layers/ExpandConvTransLayer.h index f19aa81f3d3c23d2e24f1c49710cb124dd714a54..a6591fe1aa3863ee455a78c8dcccc81afff418db 100644 --- a/paddle/gserver/layers/ExpandConvTransLayer.h +++ b/paddle/gserver/layers/ExpandConvTransLayer.h @@ -26,7 +26,7 @@ namespace paddle { * This layer expands input and use matrix multiplication to * calculate convolution operation. * - * The config file api is img_conv_layer. + * The config file api is img_convTrans_layer. */ class ExpandConvTransLayer : public ConvTransBaseLayer { protected: diff --git a/paddle/gserver/tests/test_ConvTrans.cpp b/paddle/gserver/tests/test_ConvTrans.cpp index e7cbe2614facae631a3184258370a80c11f76eed..787113d242391d7ec652b6e85d9f744afa051da5 100644 --- a/paddle/gserver/tests/test_ConvTrans.cpp +++ b/paddle/gserver/tests/test_ConvTrans.cpp @@ -33,7 +33,9 @@ P_DECLARE_double(checkgrad_eps); P_DECLARE_bool(thread_local_rand_use_global_seed); P_DECLARE_bool(prev_batch_state); +// Test that the convTrans forward is the same as conv backward TEST(Layer, convTransLayerFwd) { + // Setting up conv-trans layer TestConfig configt; configt.biasSize = 3; configt.layerConfig.set_type("exconvt"); @@ -68,7 +70,7 @@ TEST(Layer, convTransLayerFwd) { LayerMap layerMap; vector datas; initDataLayer(configt, &dataLayers, &datas, &layerMap, "convTrans", - 100, false, useGpu); + 100, false, false); // test layer initialize std::vector parameters; LayerPtr convtLayer; @@ -76,6 +78,7 @@ TEST(Layer, convTransLayerFwd) { convtLayer->getBiasParameter()->zeroMem(); convtLayer->forward(PASS_GC); + // Setting up conv-layer config TestConfig config; config.biasSize = 16; config.layerConfig.set_type("exconv"); @@ -109,16 +112,18 @@ TEST(Layer, convTransLayerFwd) { LayerMap layerMap2; vector datas2; initDataLayer(config, &dataLayers2, &datas2, &layerMap2, "conv", - 100, false, useGpu); + 100, false, false); // test layer initialize std::vector parameters2; LayerPtr convLayer; initTestLayer(config, &layerMap2, ¶meters2, &convLayer); + // Sync convLayer and convtLayer parameter convLayer->getBiasParameter()->zeroMem(); convLayer->getParameters()[0]->getBuf(PARAMETER_VALUE)->copyFrom( *(convtLayer->getParameters()[0]->getBuf(PARAMETER_VALUE))); + // Set convLayer outputGrad as convTransLayer input value convLayer->forward(PASS_GC); convLayer->getOutput().grad->copyFrom(*(dataLayers[0]->getOutputValue())); @@ -126,10 +131,117 @@ TEST(Layer, convTransLayerFwd) { auto callback = [&](Parameter* para) { ++callbackFlags[para->getID()]; }; convLayer->backward(callback); + // Check that the convLayer backward is the same as convTransLayer forward checkMatrixEqual(convtLayer->getOutputValue(), dataLayers2[0]->getOutputGrad()); } + +// Do one forward pass of convTrans layer and check to see if its output +// matches the given result +void doOneConvtTest(size_t imgSize, size_t output_x, size_t stride, + size_t padding, size_t filter_size, MatrixPtr& result) { + TestConfig configt; + configt.biasSize = 1; + configt.layerConfig.set_type("exconvt"); + configt.layerConfig.set_num_filters(1); + configt.layerConfig.set_partial_sum(1); + configt.layerConfig.set_shared_biases(true); + + configt.inputDefs.push_back({INPUT_DATA, "layer_0", output_x * output_x, + filter_size * filter_size}); + LayerInputConfig* input = configt.layerConfig.add_inputs(); + ConvConfig* conv = input->mutable_conv_conf(); + conv->set_filter_size(filter_size); + conv->set_filter_size_y(filter_size); + conv->set_channels(1); + conv->set_padding(padding); + conv->set_padding_y(padding); + conv->set_stride(stride); + conv->set_stride_y(stride); + conv->set_groups(1); + conv->set_filter_channels(1); + conv->set_img_size(imgSize); + conv->set_output_x(output_x); + + configt.layerConfig.set_size(conv->img_size() * conv->img_size() * + configt.layerConfig.num_filters()); + configt.layerConfig.set_name("convTrans"); + + std::vector dataLayers; + LayerMap layerMap; + vector datas; + initDataLayer(configt, &dataLayers, &datas, &layerMap, "convTrans", + 1, false, false); + dataLayers[0]->getOutputValue()->zeroMem(); + dataLayers[0]->getOutputValue()->add(1.0); + + // test layer initialize + std::vector parameters; + LayerPtr convtLayer; + initTestLayer(configt, &layerMap, ¶meters, &convtLayer); + convtLayer->getBiasParameter()->zeroMem(); + convtLayer->getParameters()[0]->zeroMem(); + convtLayer->getParameters()[0]->getBuf(PARAMETER_VALUE)->add(1.0); + convtLayer->forward(PASS_GC); + + checkMatrixEqual(convtLayer->getOutputValue(), result); +} + +TEST(Layer, convTransLayerFwd2) { + size_t imgSize, output_x, stride, padding, filter_size; + MatrixPtr result; + + imgSize = 5; + output_x = 1; + stride = 1; + padding = 0; + filter_size = 5; + result = Matrix::create(1, imgSize * imgSize, false, false); + result->zeroMem(); + result->add(1.0); + doOneConvtTest(imgSize, output_x, stride, padding, filter_size, result); + + imgSize = 5; + output_x = 2; + stride = 1; + padding = 0; + filter_size = 4; + float resultData[] = {1, 2, 2, 2, 1, + 2, 4, 4, 4, 2, + 2, 4, 4, 4, 2, + 2, 4, 4, 4, 2, + 1, 2, 2, 2, 1}; + result = Matrix::create(resultData, 1, imgSize * imgSize, false, false); + doOneConvtTest(imgSize, output_x, stride, padding, filter_size, result); + + imgSize = 5; + output_x = 2; + stride = 2; + padding = 1; + filter_size = 5; + float resultData2[] = {1, 2, 2, 2, 1, + 2, 4, 4, 4, 2, + 2, 4, 4, 4, 2, + 2, 4, 4, 4, 2, + 1, 2, 2, 2, 1}; + result = Matrix::create(resultData2, 1, imgSize * imgSize, false, false); + doOneConvtTest(imgSize, output_x, stride, padding, filter_size, result); + + imgSize = 5; + output_x = 2; + stride = 2; + padding = 0; + filter_size = 3; + float resultData3[] = {1, 1, 2, 1, 1, + 1, 1, 2, 1, 1, + 2, 2, 4, 2, 2, + 1, 1, 2, 1, 1, + 1, 1, 2, 1, 1}; + result = Matrix::create(resultData3, 1, imgSize * imgSize, false, false); + doOneConvtTest(imgSize, output_x, stride, padding, filter_size, result); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); initMain(argc, argv); diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 2d28b34999cb05005c8dc34c6d1e252023de3f79..95c5f774c6cea931dfb7d0e8ce35937f3fa25926 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -1107,7 +1107,7 @@ def parse_conv(conv, input_layer_name, conv_conf): conv_conf.caffe_mode) -def parse_convt(conv, input_layer_name, conv_conf): +def parse_convt(conv, input_layer_name, conv_conf, num_filters): conv_conf.filter_size = conv.filter_size conv_conf.filter_size_y = conv.filter_size_y conv_conf.channels = conv.channels @@ -1116,7 +1116,7 @@ def parse_convt(conv, input_layer_name, conv_conf): conv_conf.stride = conv.stride conv_conf.stride_y = conv.stride_y conv_conf.groups = conv.groups - conv_conf.filter_channels = conv.channels / conv.groups + conv_conf.filter_channels = num_filters / conv.groups conv_conf.caffe_mode = conv.caffe_mode outputSize = g_layer_map[input_layer_name].size / conv.channels @@ -1126,14 +1126,14 @@ def parse_convt(conv, input_layer_name, conv_conf): config_assert((conv_conf.output_x ** 2) == outputSize, ("Input layer %s: Incorrect input image size %d for input " + "image pixels %d") - % (input_layer_name, conv_conf.img_size, img_pixels)) + % (input_layer_name, conv_conf.output_x, outputSize)) if conv.caffe_mode: conv_conf.img_size = \ (conv_conf.output_x - 1) * conv.stride \ + conv.filter_size - 2 * conv.padding else: conv_conf.img_size = \ - (conv_conf.output_x - 1) * conv.stride \ + (conv_conf.output_x - 2) * conv.stride \ + conv.filter_size - 2 * conv.padding + 1 @@ -1655,7 +1655,7 @@ class ConvTransLayerBase(LayerBase): num_filters=None, shared_biases=False, **xargs): - super(ConvLayerBase, self).__init__( + super(ConvTransLayerBase, self).__init__( name, self.layer_type, 0, inputs=inputs, **xargs) if num_filters is not None: @@ -1686,7 +1686,7 @@ class ConvTransLayerBase(LayerBase): parse_convt( self.inputs[input_index].conv, input_layer.name, - self.config.inputs[input_index].conv_conf) + self.config.inputs[input_index].conv_conf, num_filters) conv_conf = self.config.inputs[input_index].conv_conf psize = self.calc_parameter_size(conv_conf) print("output size for %s is %d " % (name, conv_conf.output_x)) @@ -1700,7 +1700,7 @@ class ConvTransLayerBase(LayerBase): self.create_bias_parameter(bias, psize, [psize, 1]) def calc_parameter_size(self, conv_conf): - return conv_conf.channels() * conv_conf.filter_channels \ + return conv_conf.channels * conv_conf.filter_channels \ * (conv_conf.filter_size * conv_conf.filter_size_y) @config_layer('exconvt') diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 853df8b83709d54acabd109b565e4ca920de5d1a..172d45a761ecc7be65865e2e1d686e74e28074d0 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -36,7 +36,7 @@ __all__ = ["full_matrix_projection", "AggregateLevel", "ExpandLevel", "pooling_layer", "lstmemory", "last_seq", "first_seq", "cos_sim", "hsigmoid", "conv_projection", "regression_cost", 'classification_cost', "LayerOutput", - 'img_conv_layer', 'img_pool_layer', 'batch_norm_layer', + 'img_conv_layer', 'img_convTrans_layer', 'img_pool_layer', 'batch_norm_layer', 'img_cmrnorm_layer', 'addto_layer', 'concat_layer', 'lstm_step_layer', 'recurrent_group', 'memory', 'StaticInput', 'expand_layer', 'scaling_layer',