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

Add deconvolution output padding option. #643

Merged
merged 1 commit into from
May 27, 2020
Merged
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
2 changes: 2 additions & 0 deletions build-tools/code_generator/api_levels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,5 @@
RandGamma_ffiIi: 290
14:
Interpolate_iIiBBBB: 291
15:
Deconvolution_iiIiIiIiBiI: 292
13 changes: 13 additions & 0 deletions build-tools/code_generator/functions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,14 @@ Neural Network Layer:
The weights are specified in the same manner as :meth:`~nnabla.functions.convolution` , as if it was an ordinary convolution function.
The forward operation of :meth:`~nnabla.functions.deconvolution` will then be operationally equivalent to the backward pass of :meth:`~nnabla.functions.convolution` .
Therefore, the number of input channels (can be seen as output channels of forward convolution) is specified in the first dimension, and the number of the output channels divided by the number of groups is specified in the second dimension.

For `stride > 1`, a parameter-wise identical deconvolution on the output
of a convolution may not produce the same output shape as the input to
the convolution if, due to striding, the convolution did not fully cover
the input spatial dimension. The `output_padding` parameter can then be
used to appropriately increase the calculated output shape. Note that
this is used to find the output shape for the deconvolution operation,
but not to add zero-padding to the output.
inputs:
x:
doc: :math:`(B + 1 + N)`-D array (:math:`M_1 \times ... \times M_B \times
Expand Down Expand Up @@ -437,6 +445,10 @@ Neural Network Layer:
NHWC order.
type: bool
default: 'False'
output_padding:
doc: Additional size added to the output shape.
type: Shape
default: (0,) * (len(x.shape) - (base_axis+1))
outputs:
y:
doc: |2
Expand All @@ -453,6 +465,7 @@ Neural Network Layer:
function_ids:
iiIiIiIi: 3
iiIiIiIiB: 279
iiIiIiIiBiI: 292
c_runtime: support
DepthwiseDeconvolution:
snake_name: depthwise_deconvolution
Expand Down
14 changes: 7 additions & 7 deletions examples/cpp/mnist_collection/dcgan_training.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,22 @@ CgVariablePtr generator(CgVariablePtr z, int max_h, bool test,
assert(max_h / 4 > 0);

// # (Z, 1, 1) --> (256, 4, 4)
pf::ConvolutionOpts opts1 = pf::ConvolutionOpts().with_bias(false);
pf::DeconvolutionOpts opts1 = pf::DeconvolutionOpts().with_bias(false);
auto h1 = pf::deconvolution(z, 1, max_h, {4, 4}, params["deconv1"], opts1);
auto b1 = pf::batch_normalization(h1, !test, params["deconv1"]);
auto e1 = f::elu(b1, 1.0);

// # (256, 4, 4) --> (128, 8, 8)
pf::ConvolutionOpts opts2 =
pf::ConvolutionOpts().with_bias(false).pad({1, 1}).stride({2, 2});
pf::DeconvolutionOpts opts2 =
pf::DeconvolutionOpts().with_bias(false).pad({1, 1}).stride({2, 2});
auto h2 =
pf::deconvolution(e1, 1, max_h / 2, {4, 4}, params["deconv2"], opts2);
auto b2 = pf::batch_normalization(h2, !test, params["deconv2"]);
auto e2 = f::elu(b2, 1.0);

// # (128, 8, 8) --> (64, 16, 16)
pf::ConvolutionOpts opts3 =
pf::ConvolutionOpts().with_bias(false).pad({1, 1}).stride({2, 2});
pf::DeconvolutionOpts opts3 =
pf::DeconvolutionOpts().with_bias(false).pad({1, 1}).stride({2, 2});
auto h3 =
pf::deconvolution(e2, 1, max_h / 4, {4, 4}, params["deconv3"], opts3);
auto b3 = pf::batch_normalization(h3, !test, params["deconv3"]);
Expand All @@ -76,8 +76,8 @@ CgVariablePtr generator(CgVariablePtr z, int max_h, bool test,
// # Convolution with kernel=4, pad=3 and stride=2 transforms a 28 x 28 map
// # to a 16 x 16 map. Deconvolution with those parameters behaves like an
// # inverse operation, i.e. maps 16 x 16 to 28 x 28.
pf::ConvolutionOpts opts4 =
pf::ConvolutionOpts().with_bias(false).pad({3, 3}).stride({2, 2});
pf::DeconvolutionOpts opts4 =
pf::DeconvolutionOpts().with_bias(false).pad({3, 3}).stride({2, 2});
auto h4 =
pf::deconvolution(e3, 1, max_h / 4, {4, 4}, params["deconv4"], opts4);
auto b4 = pf::batch_normalization(h4, !test, params["deconv4"]);
Expand Down
27 changes: 15 additions & 12 deletions include/nbla/function/deconvolution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ namespace nbla {
using std::string;
using std::vector;

NBLA_REGISTER_FUNCTION_HEADER(Deconvolution, int, // base_axis
const vector<int> &, // pad
const vector<int> &, // stride
const vector<int> &, // dilation
int, // group
bool); // channel_last
NBLA_REGISTER_FUNCTION_HEADER(Deconvolution, int, // base_axis
const vector<int> &, // pad
const vector<int> &, // stride
const vector<int> &, // dilation
int, // group
bool, // channel_last
const vector<int> &); // output_padding

/** N-D Deconvolution with bias operates backward convolution (derivative of
output wrt input) plus channel-wise learned bias. The weights must be given with
Expand All @@ -59,7 +60,7 @@ Networks for Semantic Segmentation. https://arxiv.org/abs/1605.06211
template <typename T>
class Deconvolution
: public BaseFunction<int, const vector<int> &, const vector<int> &,
const vector<int> &, int, bool> {
const vector<int> &, int, bool, const vector<int> &> {
protected:
// Note that member variables below are actually copied from Convolution. But
// the meanings of input and output are back and forth. We consider the output
Expand All @@ -70,6 +71,7 @@ class Deconvolution
vector<int> dilation_;
int group_;
bool channel_last_;
vector<int> output_padding_;
vector<int> kernel_;
int channels_i_, channels_o_, channels_g_;
vector<int> spatial_shape_i_;
Expand All @@ -92,15 +94,16 @@ class Deconvolution
public:
Deconvolution(const Context &ctx, int base_axis, const vector<int> &pad,
const vector<int> &stride, const vector<int> &dilation,
int group, bool channel_last)
: BaseFunction(ctx, base_axis, pad, stride, dilation, group,
channel_last),
int group, bool channel_last, const vector<int> &output_padding)
: BaseFunction(ctx, base_axis, pad, stride, dilation, group, channel_last,
output_padding),
base_axis_(base_axis), pad_(pad), stride_(stride), dilation_(dilation),
group_(group), channel_last_(channel_last) {}
group_(group), channel_last_(channel_last),
output_padding_(output_padding) {}

virtual shared_ptr<Function> copy() const {
return create_Deconvolution(ctx_, base_axis_, pad_, stride_, dilation_,
group_, channel_last_);
group_, channel_last_, output_padding_);
}

virtual vector<dtypes> in_types() {
Expand Down
38 changes: 31 additions & 7 deletions include/nbla/functions.hpp.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,35 @@ public:
ConvolutionOpts &stride(const vector<int> &val);
ConvolutionOpts &dilation(const vector<int> &val);
ConvolutionOpts &channel_last(bool val);
int group();
const vector<int> &pad() const;
const vector<int> &stride() const;
const vector<int> &dilation() const;
bool channel_last() const;
int group() { return group_; };
const vector<int> &pad() const { return pad_; };
const vector<int> &stride() const { return stride_; };
const vector<int> &dilation() const {return dilation_; };
bool channel_last() const { return channel_last_; };
};

class NBLA_API DeconvolutionOpts {
private:
int group_;
vector<int> pad_;
vector<int> stride_;
vector<int> dilation_;
bool channel_last_;
vector<int> output_padding_;
public:
DeconvolutionOpts();
DeconvolutionOpts &group(int val);
DeconvolutionOpts &pad(const vector<int> &val);
DeconvolutionOpts &stride(const vector<int> &val);
DeconvolutionOpts &dilation(const vector<int> &val);
DeconvolutionOpts &channel_last(bool val);
DeconvolutionOpts &output_padding(const vector<int> &val);
int group() { return group_; };
const vector<int> &pad() const { return pad_; };
const vector<int> &stride() const { return stride_; };
const vector<int> &dilation() const {return dilation_; };
bool channel_last() const { return channel_last_; };
const vector<int> &output_padding() const { return output_padding_; };
};

class NBLA_API BatchNormalizationOpts {
Expand Down Expand Up @@ -113,14 +137,14 @@ public:
};

NBLA_API vector<CgVariablePtr> convolution(Context &ctx, CgVariablePtr x, CgVariablePtr weight, CgVariablePtr bias, int base_axis, int group, ConvolutionOpts &conv_opts);
NBLA_API vector<CgVariablePtr> deconvolution(Context &ctx, CgVariablePtr x, CgVariablePtr weight, CgVariablePtr bias, int base_axis, int group, ConvolutionOpts &conv_opts);
NBLA_API vector<CgVariablePtr> deconvolution(Context &ctx, CgVariablePtr x, CgVariablePtr weight, CgVariablePtr bias, int base_axis, int group, DeconvolutionOpts &);
NBLA_API vector<CgVariablePtr> batch_normalization(Context &ctx, CgVariablePtr x, CgVariablePtr beta, CgVariablePtr gamma, CgVariablePtr mean, CgVariablePtr variance, bool batch_stat, BatchNormalizationOpts batch_opts);
NBLA_API vector<CgVariablePtr> max_pooling(Context &ctx, CgVariablePtr x, const vector<int> & kernel, const vector<int> & stride, PoolingOpts pooling_opts = PoolingOpts());
NBLA_API vector<CgVariablePtr> average_pooling(Context &ctx, CgVariablePtr x, const vector<int> & kernel, const vector<int> & stride, PoolingOpts pooling_opts = PoolingOpts());
NBLA_API vector<CgVariablePtr> sum_pooling(Context &ctx, CgVariablePtr x, const vector<int> & kernel, const vector<int> & stride, PoolingOpts pooling_opts = PoolingOpts());

NBLA_API CgVariablePtr convolution(CgVariablePtr x, CgVariablePtr weight, CgVariablePtr bias, int base_axis, int group, ConvolutionOpts &conv_opts);
NBLA_API CgVariablePtr deconvolution(CgVariablePtr x, CgVariablePtr weight, CgVariablePtr bias, int base_axis, int group, ConvolutionOpts &conv_opts);
NBLA_API CgVariablePtr deconvolution(CgVariablePtr x, CgVariablePtr weight, CgVariablePtr bias, int base_axis, int group, DeconvolutionOpts &);
NBLA_API CgVariablePtr batch_normalization(CgVariablePtr x, CgVariablePtr beta, CgVariablePtr gamma, CgVariablePtr mean, CgVariablePtr variance, bool batch_stat, BatchNormalizationOpts batch_opts);
NBLA_API CgVariablePtr max_pooling(CgVariablePtr x, const vector<int> & kernel, const vector<int> & stride, PoolingOpts pooling_opts = PoolingOpts());
NBLA_API CgVariablePtr average_pooling(CgVariablePtr x, const vector<int> & kernel, const vector<int> & stride, PoolingOpts pooling_opts = PoolingOpts());
Expand Down
57 changes: 47 additions & 10 deletions include/nbla/parametric_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,57 @@ class NBLA_API ConvolutionOpts {
ConvolutionOpts &dilation(const vector<int> &val);
ConvolutionOpts &channel_last(bool val);

int group();
const vector<int> &pad() const;
const vector<int> &stride() const;
const vector<int> &dilation() const;
bool channel_last() const;
int group() { return base_.group(); };
const vector<int> &pad() const { return base_.pad(); };
const vector<int> &stride() const { return base_.stride(); };
const vector<int> &dilation() const { return base_.dilation(); };
bool channel_last() const { return base_.channel_last(); };

ConvolutionOpts &with_bias(bool with_bias);
ConvolutionOpts &fix_parameters(bool val);
ConvolutionOpts &w_init(Initializer *w_init);
ConvolutionOpts &b_init(Initializer *b_init);
bool with_bias();
bool fix_parameters();
Initializer *w_init();
Initializer *b_init();

bool with_bias() { return with_bias_; };
bool fix_parameters() { return fix_parameters_; };
Initializer *w_init() { return w_init_; };
Initializer *b_init() { return b_init_; };
};

class NBLA_API DeconvolutionOpts {
private:
nbla::functions::DeconvolutionOpts base_;
bool with_bias_;
bool fix_parameters_;
Initializer *w_init_;
Initializer *b_init_;

public:
DeconvolutionOpts();

DeconvolutionOpts &group(int val);
DeconvolutionOpts &pad(const vector<int> &val);
DeconvolutionOpts &stride(const vector<int> &val);
DeconvolutionOpts &dilation(const vector<int> &val);
DeconvolutionOpts &channel_last(bool val);
DeconvolutionOpts &output_padding(const vector<int> &val);

int group() { return base_.group(); };
const vector<int> &pad() const { return base_.pad(); };
const vector<int> &stride() const { return base_.stride(); };
const vector<int> &dilation() const { return base_.dilation(); };
bool channel_last() const { return base_.channel_last(); };
const vector<int> &output_padding() const { return base_.output_padding(); };

DeconvolutionOpts &with_bias(bool with_bias);
DeconvolutionOpts &fix_parameters(bool val);
DeconvolutionOpts &w_init(Initializer *w_init);
DeconvolutionOpts &b_init(Initializer *b_init);

bool with_bias() { return with_bias_; };
bool fix_parameters() { return fix_parameters_; };
Initializer *w_init() { return w_init_; };
Initializer *b_init() { return b_init_; };
};

class NBLA_API BatchNormalizationOpts {
Expand Down Expand Up @@ -184,7 +221,7 @@ deconvolution(Context &ctx, CgVariablePtr x, int base_axis, int n_map_out,
NBLA_API CgVariablePtr
deconvolution(CgVariablePtr x, int base_axis, int n_map_out,
const vector<int> &kernel, ParameterDirectory parameters,
ConvolutionOpts conv_opts = ConvolutionOpts());
DeconvolutionOpts conv_opts = DeconvolutionOpts());
}
}

Expand Down
9 changes: 6 additions & 3 deletions python/src/nnabla/backward_function/deconvolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def backward_impl(self, inputs, outputs, prop_down, accum):
dilation = self.forward_func.info.args["dilation"]
group = self.forward_func.info.args["group"]
channel_last = self.forward_func.info.args["channel_last"]
output_padding = self.forward_func.info.args["output_padding"]

# Inputs
x0 = inputs[0].data
Expand Down Expand Up @@ -97,9 +98,11 @@ def backward_impl(self, inputs, outputs, prop_down, accum):
## w.r.t. dy
if (not with_bias and prop_down[2]) or (with_bias and prop_down[3]):
accum_dy = accum[3] if with_bias else accum[2]
g_dy_ = F.deconvolution(g_dx0, w0, None, base_axis, pad, stride, dilation, group, channel_last) \
+ F.deconvolution(x0, g_dw0, None, base_axis,
pad, stride, dilation, group, channel_last)
params = {'base_axis': base_axis, 'pad': pad, 'stride': stride,
'dilation': dilation, 'output_padding': output_padding,
'group': group, 'channel_last': channel_last}
g_dy_ = (F.deconvolution(g_dx0, w0, None, **params) +
F.deconvolution(x0, g_dw0, None, **params))
if with_bias:
if not channel_last:
g_db0 = F.reshape(
Expand Down
8 changes: 4 additions & 4 deletions python/src/nnabla/parametric_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1245,9 +1245,9 @@ def depthwise_convolution(inp, kernel, pad=None, stride=None, dilation=None,
('b', 'Bias vector', '(outmaps,)', True),
])
def deconvolution(inp, outmaps, kernel, pad=None, stride=None, dilation=None,
group=1, channel_last=False, w_init=None, b_init=None,
base_axis=1, fix_parameters=False, rng=None, with_bias=True,
apply_w=None, apply_b=None):
group=1, channel_last=False, output_padding=None,
w_init=None, b_init=None, base_axis=1, fix_parameters=False,
rng=None, with_bias=True, apply_w=None, apply_b=None):
"""
Deconvolution layer.

Expand Down Expand Up @@ -1294,7 +1294,7 @@ def deconvolution(inp, outmaps, kernel, pad=None, stride=None, dilation=None,
if apply_b is not None:
b = apply_b(b)
return F.deconvolution(inp, w, b, base_axis, pad, stride, dilation, group,
channel_last)
channel_last, output_padding)


@parametric_function_api("depthwise_deconv", [
Expand Down
15 changes: 10 additions & 5 deletions python/test/function/refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,15 @@ def get_conv_out_size_recursive(d, ndim):
return y


def deconvolution_1d(x, w, b, pad, stride, dilation, group, dtype=np.float32):
def deconvolution_1d(x, w, b, pad, stride, dilation, group, dtype=np.float32,
output_padding=(0,)):
y = x
K, Ho = y.shape
K, Cg, M = w.shape
C = Cg * group

H = get_deconv_out_size(Ho, M, pad[0], stride[0], dilation[0])
H = (get_deconv_out_size(Ho, M, pad[0], stride[0], dilation[0])
+ output_padding[0])
x_pad = np.zeros((C, H + pad[0] * 2), dtype=dtype)
for k in range(K):
g = int(k // (K // group))
Expand All @@ -148,14 +150,17 @@ def deconvolution_1d(x, w, b, pad, stride, dilation, group, dtype=np.float32):
return x


def deconvolution_2d(x, w, b, pad, stride, dilation, group, dtype=np.float32):
def deconvolution_2d(x, w, b, pad, stride, dilation, group, dtype=np.float32,
output_padding=(0, 0)):
y = x
K, Ho, Wo = y.shape
K, Cg, M, N = w.shape
C = Cg * group

H = get_deconv_out_size(Ho, M, pad[0], stride[0], dilation[0])
W = get_deconv_out_size(Wo, N, pad[1], stride[1], dilation[1])
H = (get_deconv_out_size(Ho, M, pad[0], stride[0], dilation[0])
+ output_padding[0])
W = (get_deconv_out_size(Wo, N, pad[1], stride[1], dilation[1])
+ output_padding[1])
x_pad = np.zeros((C, H + pad[0] * 2, W + pad[1] * 2), dtype=dtype)
for k in range(K):
g = int(k // (K // group))
Expand Down
Loading