Class: Chainer::Functions::Connection::Deconvolution2DFunction

Inherits:
Chainer::FunctionNode show all
Defined in:
lib/chainer/functions/connection/deconvolution_2d.rb

Instance Attribute Summary collapse

Attributes inherited from Chainer::FunctionNode

#inputs, #outputs, #rank

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Chainer::FunctionNode

#apply, #backward_accumulate, #forward_cpu, #get_retained_inputs, #get_retained_outputs, #label, #output_data, #retain_inputs, #retain_outputs, #unchain

Constructor Details

#initialize(stride: 1, pad: 0, outsize: nil) ⇒ Deconvolution2DFunction

Returns a new instance of Deconvolution2DFunction.



75
76
77
78
79
80
81
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 75

def initialize(stride: 1, pad: 0, outsize: nil)
  @cover_all = nil

  @sy, @sx = stride.is_a?(::Array) ? stride : [stride, stride]
  @ph, @pw = pad.is_a?(::Array) ? pad : [pad, pad]
  @outh, @outw = outsize.nil? ? [nil, nil] : outsize
end

Instance Attribute Details

#cover_allObject (readonly)

Returns the value of attribute cover_all.



5
6
7
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 5

def cover_all
  @cover_all
end

#phObject (readonly)

Returns the value of attribute ph.



5
6
7
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 5

def ph
  @ph
end

#pwObject (readonly)

Returns the value of attribute pw.



5
6
7
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 5

def pw
  @pw
end

#sxObject (readonly)

Returns the value of attribute sx.



5
6
7
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 5

def sx
  @sx
end

#syObject (readonly)

Returns the value of attribute sy.



5
6
7
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 5

def sy
  @sy
end

Class Method Details

.deconvolution_2d(x, w, b: nil, stride: 1, pad: 0, outsize: nil) ⇒ Chainer::Variable

Two dimensional deconvolution function.

This is an implementation of two-dimensional deconvolution. In most of deep learning frameworks and papers, this function is called transposed convolution. But because of historical reasons (e.g. paper by Ziller Deconvolutional Networks) and backward compatibility, this function is called deconvolution in Chainer.

It takes three variables: input image x, the filter weight W, and the bias vector b.

  • $n$ is the batch size.

  • $c_I$ and $c_O$ are the number of the input and output channels, respectively.

  • $h_I$ and $w_I$ are the height and width of the input image, respectively.

  • $h_K$ and $w_K$ are the height and width of the filters, respectively.

  • $h_P$ and $w_P$ are the height and width of the spatial padding size, respectively.

Let $(s_Y, s_X)$ be the stride of filter application. Then, the output size $(h_O, w_O)$ is estimated by the following equations:

$ h_O &= s_Y (h_I - 1) + h_K - 2h_P, w_O &= s_X (w_I - 1) + w_K - 2w_P. $

Example > n = 10 > c_i, c_o = 1, 3 > h_i, w_i = 5, 10 > h_k, w_k = 10, 10 > h_p, w_p = 5, 5 > x = Numo::DFloat.new(n, c_i, h_i, w_i).rand > x.shape

> [10, 1, 5, 10]

> w = Numo::DFloat.new(c_i, c_o, h_k, w_k).rand > w.shape

> [1, 3, 10, 10]

> b = Numo::DFloat.new(c_o).rand > b.shape

> [3]

> s_y, s_x = 5, 5 > y = Chainer::Functions::Connection::Deconvolution2DFunction.deconvolution_2d(x, w, b: b, stride: [s_y, s_x], pad: [h_p, w_p]) > y.shape

> [10, 3, 20, 45]

> h_o = s_y * (h_i - 1) + h_k - 2 * h_p > w_o = s_x * (w_i - 1) + w_k - 2 * w_p > y.shape == [n, c_o, h_o, w_o]

> true

Parameters:

  • x (Chainer::Variable or Numo::NArray)

    Input variable of shape $(n, c_I, h_I, w_I)$.

  • w (Chainer::Variable or Numo::NArray)

    Weight variable of shape $(c_I, c_O, h_K, w_K)$.

  • b (Chainer::Variable or Numo::NArray) (defaults to: nil)

    Bias variable of length $c_O$ (optional).

  • stride (integer or Array<integer>) (defaults to: 1)

    Stride of filter applications. stride=s and stride=[s, s] are equivalent.

  • pad (integer or Array<integer>) (defaults to: 0)

    Spatial padding width for input arrays. pad=p and pad=[p, p] are equivalent.

  • outsize (integer or Arrat<integer>) (defaults to: nil)

    Expected output size of deconvolutional operation. It should be pair of height and width $(h_O, w_O)$. Default value is nil and the outsize is estimated by input size, stride and pad.

Returns:



65
66
67
68
69
70
71
72
73
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 65

def self.deconvolution_2d(x, w, b: nil, stride: 1, pad: 0, outsize: nil)
  func = Deconvolution2DFunction.new(stride: stride, pad: pad, outsize: outsize)
  if b.nil?
    args = x, w
  else
    args = x, w, b
  end
  func.apply(args).first
end

Instance Method Details

#backward(indexes, grad_outputs) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 122

def backward(indexes, grad_outputs)
  x, w = get_retained_inputs
  gy = grad_outputs.first

  ret = []

  if indexes.include?(0)
    set_cover_all(x, w) if @cover_all.nil?
    gw = Chainer::Functions::Connection::Convolution2DFunction.convolution_2d(gy, w, stride: [@sy, @sx], pad: [@ph, @pw], cover_all: @cover_all)
    ret << gw
  end

  if indexes.include?(1)
    set_cover_all(x, w) if @cover_all.nil?
    gw = Chainer::Functions::Connection::Convolution2DGradW.new(self).apply([gy, x]).first
    ret << gw
  end

  if indexes.include?(2)
    gb = Chainer::Functions::Math::Sum.sum(gy, axis: [0, 2, 3])
    ret << gb
  end

  ret
end

#forward(inputs) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/chainer/functions/connection/deconvolution_2d.rb', line 83

def forward(inputs)
  retain_inputs([0, 1])
  x, w = inputs[0...2]
  b = inputs.size == 3 ? inputs[2] : nil

  unless inputs.all? { |i| i.is_a?(Numo::NArray) }
    if b.nil?
      raise TypeError, "Numo::NArray must not be used together w: #{w.class}, x: #{x.class}"
    else
      raise TypeError, "Numo::NArray must not be used together w: #{w.class}, x: #{x.class}, b: #{b.class}"
    end
  end

  kh, kw = w.shape[2..-1]
  _, _, x_h, x_w = x.shape

  gcol = Chainer::Utils::Math.tensordot(w, x, [0, 1]).cast_to(x.class)
  # - k, m, n: shape of out_channel
  # - b: number of inputs
  # - h, w: height and width of kernels
  # k, m, n, b, h, w -> b, k, m, n, h, w
  gcol = gcol.transpose(3, 0, 1, 2, 4, 5)

  if @outh.nil?
    @outh = Chainer::Utils::Conv.get_deconv_outsize(x_h, kh, @sy, @ph)
    raise TypeError, 'Height in the output should be positive.' if @outh <= 0
  end
  if @outw.nil?
    @outw = Chainer::Utils::Conv.get_deconv_outsize(x_w, kw, @sx, @pw)
    raise TypeError, 'Width in the output should be positive.' if @outw <= 0
  end

  y = Chainer::Utils::Conv.col2im(gcol, @sy, @sx, @ph, @pw, @outh, @outw)
  if !b.nil?
    y += b.reshape(1, b.size, 1, 1)
  end
  [y]
end