/
average_pooling_nd.py
138 lines (108 loc) · 5.06 KB
/
average_pooling_nd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import numpy
import functools
import operator
import six
import chainer
from chainer import cuda
from chainer.functions.pooling import average_pooling_nd_kernel
from chainer.functions.pooling import pooling_nd
from chainer import utils
from chainer.utils import conv_nd
class AveragePoolingND(pooling_nd._PoolingND):
"""Average pooling over a set of N-dimensional planes."""
def __init__(self, ndim, ksize, stride=None, pad=0, cover_all=False):
utils.experimental('chainer.functions.pooling.AveragePoolingND')
# TODO(takagi) Support cover_all mode.
if cover_all is True:
raise ValueError('`cover_all` mode is not supported yet.')
super(AveragePoolingND, self).__init__(
ndim, ksize, stride=stride, pad=pad, cover_all=cover_all)
def forward_cpu(self, x):
self.retain_inputs(())
self._in_shape = x[0].shape
self._in_dtype = x[0].dtype
col = conv_nd.im2col_nd_cpu(
x[0], self.ksize, self.stride, self.pad, cover_all=self.cover_all)
# mean along (_, _, k_1, k_2, ..., k_N, _, ..., _)
y_axis = tuple(six.moves.range(2, 2 + len(self.ksize)))
y = col.mean(axis=y_axis)
return y,
def forward_gpu(self, x):
if chainer.should_use_cudnn('>=auto') and self.ndim >= 2:
# With cuDNN v3 or greater, use cuDNN implementation for inputs
# with spatial dimensions of two or more.
return super(AveragePoolingND, self).forward_gpu(x)
self.retain_inputs(())
self._in_shape = x[0].shape
self._in_dtype = x[0].dtype
n, c = x[0].shape[:2]
dims = x[0].shape[2:]
ys = tuple(conv_nd.get_conv_outsize(d, k, s, p,
cover_all=self.cover_all)
for (d, k, s, p) in six.moves.zip(
dims, self.ksize, self.stride, self.pad))
# (n, c, y_1, y_2, ..., y_N)
y_shape = (n, c) + ys
y = cuda.cupy.empty(y_shape, dtype=x[0].dtype)
coeff = 1. / functools.reduce(operator.mul, self.ksize)
in_params, out_params, operation, name = \
average_pooling_nd_kernel.AveragePoolingNDKernelForward.generate(
self.ndim)
cuda.elementwise(in_params, out_params, operation, name)(
x[0].reduced_view(),
*(dims + ys + self.ksize + self.stride + self.pad + (coeff, y)))
return y,
def backward_cpu(self, x, gy):
dims = self._in_shape[2:]
outs = gy[0].shape[2:]
colon = slice(None, None, None)
gy_index = (colon, colon) + (None,) * len(dims)
gcol_reps = (1, 1) + self.ksize + (1,) * len(outs)
gcol = numpy.tile(gy[0][gy_index], gcol_reps)
gx = conv_nd.col2im_nd_cpu(gcol, self.stride, self.pad, dims)
gx /= functools.reduce(operator.mul, self.ksize)
return gx,
def backward_gpu(self, x, gy):
if self._used_cudnn:
return super(AveragePoolingND, self).backward_gpu(x, gy)
n, c = self._in_shape[:2]
dims = self._in_shape[2:]
ys = gy[0].shape[2:]
gx = cuda.cupy.empty(self._in_shape, self._in_dtype)
coeff = 1. / functools.reduce(operator.mul, self.ksize)
in_params, out_params, operation, name = \
average_pooling_nd_kernel.AveragePoolingNDKernelBackward.generate(
self.ndim)
cuda.elementwise(in_params, out_params, operation, name)(
gy[0].reduced_view(),
*(dims + ys + self.ksize + self.stride + self.pad + (coeff, gx)))
return gx,
def create_pool_desc(self):
return cuda.cudnn.create_pooling_descriptor(
self.ksize, self.stride, self.pad,
cuda.cudnn.cudnn.CUDNN_POOLING_AVERAGE_COUNT_INCLUDE_PADDING)
def average_pooling_nd(x, ksize, stride=None, pad=0):
"""N-dimensionally spatial average pooling function.
This function provides a N-dimensionally generalized version of
:func:`~functions.average_pooling_2d`. This acts similarly to
:class:`~functions.ConvolutionND`, but it computes the average of input
spatial patch for each channel without any parameter instead of computing
the inner products.
Args:
x(~chainer.Variable): Input variable.
ksize (int or tuple of ints): Size of pooling window. ``ksize=k`` and
``ksize=(k, k, ..., k)`` are equivalent.
stride (int or tuple of ints or None): Stride of pooling applications.
``stride=s`` and ``stride=(s, s, ..., s)`` are equivalent. If
``None`` is specified, then it uses same stride as the pooling
window size.
pad (int or tuple of ints): Spatial padding width for the input array.
``pad=p`` and ``pad=(p, p, ..., p)`` are equivalent.
Returns:
~chainer.Variable: Output variable.
.. note::
This function currently does not support ``cover_all`` mode as
:func:`max_pooling_nd`. Average pooling runs in non-cover-all mode.
"""
ndim = len(x.shape[2:])
return AveragePoolingND(ndim, ksize, stride=stride, pad=pad)(x)