提交 123d9007 authored 作者: Frédéric Bastien's avatar Frédéric Bastien 提交者: GitHub

Merge pull request #4756 from Thrandis/ccw2

Theano variable arguments for pooling 2
......@@ -187,7 +187,7 @@
<Compile Include="theano\tensor\sharedvar.py" />
<Compile Include="theano\tensor\shared_randomstreams.py" />
<Compile Include="theano\tensor\signal\conv.py" />
<Compile Include="theano\tensor\signal\downsample.py" />
<Compile Include="theano\tensor\signal\pool.py" />
<Compile Include="theano\tensor\signal\__init__.py" />
<Compile Include="theano\tensor\tensor_grad.py" />
<Compile Include="theano\tensor\xlogx.py" />
......
......@@ -9,9 +9,6 @@
:synopsis: ops for performing various forms of downsampling
.. moduleauthor:: LISA
.. seealso:: :func:`theano.tensor.nnet.neighbours.images2neibs`
.. function:: fft(*todo)
[James has some code for this, but hasn't gotten it into the source tree yet.]
.. note::
This module is deprecated. Use the functions in :func:`theano.tensor.nnet.signal.pool`
......@@ -20,5 +20,5 @@ forms of signal processing.
:maxdepth: 1
conv
downsample
pool
downsample
......@@ -1859,13 +1859,10 @@ def local_gpua_pool_dnn_alternative(op, ctx_name, inputs, outputs):
raise_no_cudnn()
if not op.ignore_border:
return
img, = inputs
img, ws, stride, pad = inputs
img = as_gpuarray_variable(img, ctx_name)
ds = op.ds
stride = op.st
pad = op.padding
mode = op.mode
return dnn_pool(gpu_contiguous(img), ds, stride=stride, pad=pad, mode=mode)
return dnn_pool(gpu_contiguous(img), ws, stride=stride, pad=pad, mode=mode)
@register_opt('cudnn', 'fast_compile')
......@@ -1876,20 +1873,17 @@ def local_gpua_pool_dnn_grad_stride(op, ctx_name, inputs, outputs):
raise_no_cudnn()
if not op.ignore_border:
return
inp, out, out_grad = inputs
inp, out, out_grad, ws, stride, pad = inputs
inp = as_gpuarray_variable(inp, ctx_name)
out = as_gpuarray_variable(out, ctx_name)
out_grad = as_gpuarray_variable(out_grad, ctx_name)
ds = op.ds
st = op.st
pad = op.padding
mode = op.mode
return GpuDnnPoolGrad(mode=mode)(gpu_contiguous(inp),
gpu_contiguous(out),
gpu_contiguous(out_grad),
ds,
st,
ws,
stride,
pad)
......@@ -1901,12 +1895,9 @@ def local_gpua_avg_pool_dnn_grad_stride(op, ctx_name, inputs, outputs):
raise_no_cudnn()
if not op.ignore_border:
return
inp, out_grad = inputs
inp, out_grad, ws, stride, pad = inputs
inp = as_gpuarray_variable(inp, ctx_name)
out_grad = as_gpuarray_variable(out_grad, ctx_name)
ds = op.ds
st = op.st
pad = op.padding
mode = op.mode
cg = gpu_contiguous(out_grad)
......@@ -1914,7 +1905,7 @@ def local_gpua_avg_pool_dnn_grad_stride(op, ctx_name, inputs, outputs):
# We reuse cg because cuDNN does not use the value of the `out`
# argument but still checks its shape for average pooling. This
# has been observed in v2 and v3 as far as I know.
return GpuDnnPoolGrad(mode=mode)(gpu_contiguous(inp), cg, cg, ds, st, pad)
return GpuDnnPoolGrad(mode=mode)(gpu_contiguous(inp), cg, cg, ws, stride, pad)
@register_opt('cudnn', 'fast_compile')
......
......@@ -2962,14 +2962,11 @@ if True:
if isinstance(node.op, Pool):
if not node.op.ignore_border:
return
img, = node.inputs
ds = node.op.ds
stride = node.op.st
pad = node.op.padding
img, ws, stride, pad = node.inputs
mode = node.op.mode
if (img.owner and isinstance(img.owner.op, HostFromGpu)):
ret = dnn_pool(gpu_contiguous(img.owner.inputs[0]),
ds, stride=stride, pad=pad, mode=mode)
ws, stride=stride, pad=pad, mode=mode)
return [host_from_gpu(ret)]
@register_opt('cudnn')
......@@ -2996,10 +2993,7 @@ if True:
if isinstance(node.op, MaxPoolGrad):
if not node.op.ignore_border:
return
inp, out, inp_grad = node.inputs
ds = node.op.ds
st = node.op.st
pad = node.op.padding
inp, out, inp_grad, ws, stride, pad = node.inputs
mode = node.op.mode
if ((inp.owner and isinstance(inp.owner.op, HostFromGpu)) or
......@@ -3010,7 +3004,7 @@ if True:
ret = GpuDnnPoolGrad(mode=mode)(gpu_contiguous(inp),
gpu_contiguous(out),
gpu_contiguous(inp_grad),
ds, st, pad)
ws, stride, pad)
return [host_from_gpu(ret)]
@register_opt('cudnn')
......@@ -3021,10 +3015,7 @@ if True:
if isinstance(node.op, AveragePoolGrad):
if not node.op.ignore_border:
return
inp, inp_grad = node.inputs
ds = node.op.ds
st = node.op.st
pad = node.op.padding
inp, inp_grad, ws, stride, pad = node.inputs
mode = node.op.mode
if ((inp.owner and isinstance(inp.owner.op, HostFromGpu)) or
......@@ -3034,7 +3025,7 @@ if True:
ret = GpuDnnPoolGrad(mode=mode)(gpu_contiguous(inp),
contiguous_inp_grad,
contiguous_inp_grad,
ds, st, pad)
ws, stride, pad)
return [host_from_gpu(ret)]
@register_opt('cudnn')
......
......@@ -1891,37 +1891,61 @@ def local_convtransp3d_gemm(node):
gpu_optimizer.register("convtransp3d_gemm", local_convtransp3d_gemm)
def _check_constant_args_pool(ws, stride, pad, node):
"""Check if the args of pool are constants. Warns if not."""
try:
ws_w = tensor.get_scalar_constant_value(ws[0])
ws_h = tensor.get_scalar_constant_value(ws[1])
stride_w = tensor.get_scalar_constant_value(stride[0])
stride_h = tensor.get_scalar_constant_value(stride[1])
pad_w = tensor.get_scalar_constant_value(pad[0])
pad_h = tensor.get_scalar_constant_value(pad[1])
except tensor.NotScalarConstantError:
msg = ("Pool with tensor variable for the window size, stride or "
"padding is only supported in the new GPU backend, so this op "
"will run on CPU. (op %s)" % node)
if config.assert_no_cpu_op == "warn":
_logger.warning(msg)
elif config.assert_no_cpu_op == "raise":
raise AssertionError(msg)
return None
ws = (ws_w, ws_h)
stride = (stride_w, stride_h)
pad = (pad_w, pad_h)
return ws, stride, pad
@register_opt()
@local_optimizer([pool.Pool])
def local_gpu_downsample_factor_max(node):
if (isinstance(node.op, pool.Pool) and
node.op.ds == node.op.st):
assert node.op.__props__ == ('ds', 'ignore_border', 'st', 'padding',
'mode')
if node.op.padding != (0, 0) or node.op.mode != 'max':
if isinstance(node.op, pool.Pool):
assert node.op.__props__ == ('ignore_border', 'mode')
x, ws, stride, pad = node.inputs
ret = _check_constant_args_pool(ws, stride, pad, node)
if ret is None:
return
ws, stride, pad = ret
if (pad) != (0, 0) or node.op.mode != 'max' or stride != ws:
return
x, = node.inputs
if (x.owner and isinstance(x.owner.op, HostFromGpu)):
gpu_ds = GpuDownsampleFactorMax(node.op.ds, node.op.ignore_border)
gpu_ds = GpuDownsampleFactorMax(ws, node.op.ignore_border)
return [host_from_gpu(gpu_ds(x.owner.inputs[0]))]
@register_opt()
@local_optimizer([pool.MaxPoolGrad])
def local_gpu_downsample_factor_max_grad(node):
if (isinstance(node.op, pool.MaxPoolGrad) and node.op.ds == node.op.st):
assert node.op.__props__ == ('ds', 'ignore_border', 'st', 'padding',
'mode')
if (node.op.padding != (0, 0) or
node.op.mode != 'max' or
node.op.st != node.op.ds):
if isinstance(node.op, pool.MaxPoolGrad):
assert node.op.__props__ == ('ignore_border', 'mode')
x, z, gz, ws, stride, pad = node.inputs
ret = _check_constant_args_pool(ws, stride, pad, node)
if ret is None:
return
ws, stride, pad = ret
if pad != (0, 0) or node.op.mode != 'max' or stride != ws:
return
x, z, gz = node.inputs
if (x.owner and isinstance(x.owner.op, HostFromGpu)):
gpu_ds_grad = GpuDownsampleFactorMaxGrad(node.op.ds,
node.op.ignore_border)
gpu_ds_grad = GpuDownsampleFactorMaxGrad(ws, node.op.ignore_border)
return [host_from_gpu(gpu_ds_grad(x.owner.inputs[0],
as_cuda_ndarray_variable(z),
as_cuda_ndarray_variable(gz)))]
......@@ -1931,16 +1955,16 @@ def local_gpu_downsample_factor_max_grad(node):
@local_optimizer([pool.DownsampleFactorMaxGradGrad])
def local_gpu_downsample_factor_max_grad_grad(node):
if isinstance(node.op, pool.DownsampleFactorMaxGradGrad):
assert node.op.__props__ == ('ds', 'ignore_border', 'st',
'padding', 'mode')
if (node.op.padding != (0, 0) or
node.op.mode != 'max' or
node.op.st != node.op.ds):
assert node.op.__props__ == ('ignore_border', 'mode')
x, z, gx, ws, stride, pad = node.inputs
ret = _check_constant_args_pool(ws, stride, pad, node)
if ret is None:
return
ws, stride, pad = ret
if pad != (0, 0) or node.op.mode != 'max' or stride != ws:
return
x, z, gx = node.inputs
if (x.owner and isinstance(x.owner.op, HostFromGpu)):
op = GpuDownsampleFactorMaxGradGrad(node.op.ds,
node.op.ignore_border)
op = GpuDownsampleFactorMaxGradGrad(ws, node.op.ignore_border)
return [host_from_gpu(op(x.owner.inputs[0],
as_cuda_ndarray_variable(z),
as_cuda_ndarray_variable(gx)))]
......
......@@ -369,12 +369,12 @@ def test_downsample():
continue
for ignore_border in (True, False):
# print 'test_downsample', shp, ds, ignore_border
ds_op = Pool(ds, ignore_border=ignore_border)
ds_op = Pool(ignore_border=ignore_border)
a = tcn.shared_constructor(my_rand(*shp), 'a')
f = pfunc([], ds_op(tensor.as_tensor_variable(a)),
f = pfunc([], ds_op(tensor.as_tensor_variable(a), ds),
mode=mode_with_gpu.excluding('cudnn'))
f2 = pfunc([], ds_op(tensor.as_tensor_variable(a)),
f2 = pfunc([], ds_op(tensor.as_tensor_variable(a), ds),
mode=mode_without_gpu)
assert any([isinstance(node.op,
tcn.blas.GpuDownsampleFactorMax)
......@@ -393,12 +393,12 @@ def test_downsample():
g = pfunc(
[],
tensor.grad(ds_op(tensor.as_tensor_variable(a)).sum(),
tensor.grad(ds_op(tensor.as_tensor_variable(a), ds).sum(),
a),
mode=mode_with_gpu.excluding('cudnn'))
g2 = pfunc(
[],
tensor.grad(ds_op(tensor.as_tensor_variable(a)).sum(),
tensor.grad(ds_op(tensor.as_tensor_variable(a), ds).sum(),
a),
mode=mode_without_gpu)
assert any([isinstance(node.op,
......@@ -409,7 +409,7 @@ def test_downsample():
assert numpy.allclose(g(), g2()), shp
ggf = gradient.Lop(tensor.grad((ds_op(
tensor.as_tensor_variable(a))**2).sum(), a), a, a)
tensor.as_tensor_variable(a), ds)**2).sum(), a), a, a)
ref_mode = copy.copy(mode_without_gpu)
ref_mode.check_py_code = False
......
......@@ -381,9 +381,10 @@ def build_conv_nnet2_classif(use_gpu, isize, ksize, n_batch,
(n_kern, logical_hid_shape[0] // 2, logical_hid_shape[1] // 2),
shape_kern1[2:], n_kern1, n_batch, 1, 1, verbose=verbose, version=version)
ds_op = pool.Pool((2, 2), ignore_border=False)
ds_op = pool.Pool(ignore_border=False)
if downsample_ops:
hid = tensor.tanh(ds_op(conv_op(x, w0) + b0.dimshuffle((0, 'x', 'x'))))
hid = tensor.tanh(ds_op(conv_op(x, w0) + b0.dimshuffle((0, 'x', 'x')),
(2, 2)))
else:
hid = tensor.tanh(
(conv_op(x, w0) + b0.dimshuffle(
......
from __future__ import absolute_import, print_function, division
from . import pool
import warnings
warnings.warn(
"downsample module has been moved to the theano.tensor.signal.pool module.")
max_pool_2d_same_size = pool.max_pool_2d_same_size
max_pool_2d = pool.pool_2d
DownsampleFactorMax = pool.Pool
PoolGrad = pool.PoolGrad
MaxPoolGrad = pool.MaxPoolGrad
AveragePoolGrad = pool.AveragePoolGrad
# This is for compatibility with pickled things. It should go away at
# some point.
class DownsampleFactorMaxGrad(object):
def __new__(self, ds, ignore_border, st=None, padding=(0, 0), mode='max'):
if mode == 'max':
return MaxPoolGrad(ds=ds, ignore_border=ignore_border, st=st,
padding=padding)
else:
return AveragePoolGrad(ds=ds, ignore_border=ignore_border, st=st,
padding=padding, mode=mode)
DownsampleFactorMaxGradGrad = pool.DownsampleFactorMaxGradGrad
......@@ -9,11 +9,11 @@ from __future__ import absolute_import, print_function, division
import warnings
import numpy
from six import integer_types
from six.moves import xrange
import six.moves.builtins as builtins
import theano
from theano import gof, OpenMPOp, tensor, Variable, Apply
from theano.gradient import DisconnectedType
def max_pool_2d_same_size(input, patch_size):
......@@ -27,13 +27,13 @@ def max_pool_2d_same_size(input, patch_size):
----------
input : 4-D theano tensor of input images
Input images. Max pooling will be done over the 2 last dimensions.
patch_size : tuple of length 2
patch_size : tuple of length 2 or theano vector of ints of size 2.
Size of the patch (patch height, patch width).
(2,2) will retain only one non-zero value per patch of 4 values.
"""
output = Pool(patch_size, True)(input)
outs = MaxPoolGrad(patch_size, True)(input, output, output)
output = Pool(True)(input, patch_size)
outs = MaxPoolGrad(True)(input, output, output, patch_size)
return outs
......@@ -49,17 +49,17 @@ def pool_2d(input, ds, ignore_border=None, st=None, padding=(0, 0),
----------
input : N-D theano tensor of input images
Input images. Max pooling will be done over the 2 last dimensions.
ds : tuple of length 2
ds : tuple of length 2 or theano vector of ints of size 2.
Factor by which to downscale (vertical ds, horizontal ds).
(2,2) will halve the image in each dimension.
ignore_border : bool (default None, will print a warning and set to False)
When True, (5,5) input with ds=(2,2) will generate a (2,2) output.
(3,3) otherwise.
st : tuple of two ints
st : tuple of two ints or theano vector of ints of size 2.
Stride size, which is the number of shifts over rows/cols to get the
next pool region. If st is None, it is considered equal to ds
(no overlap on pooling regions).
padding : tuple of two ints
padding : tuple of two ints or theano vector of ints of size 2.
(pad_h, pad_w), pad zeros to extend beyond four borders of the
images, pad_h is the size of the top and bottom margins, and
pad_w is the size of the left and right margins.
......@@ -85,9 +85,8 @@ def pool_2d(input, ds, ignore_border=None, st=None, padding=(0, 0),
stacklevel=2)
ignore_border = False
if input.ndim == 4:
op = Pool(ds, ignore_border, st=st, padding=padding,
mode=mode)
output = op(input)
op = Pool(ignore_border, mode=mode)
output = op(input, ds, st, padding)
return output
# extract image dimensions
......@@ -104,9 +103,8 @@ def pool_2d(input, ds, ignore_border=None, st=None, padding=(0, 0),
input_4D = tensor.reshape(input, new_shape, ndim=4)
# downsample mini-batch of images
op = Pool(ds, ignore_border, st=st, padding=padding,
mode=mode)
output = op(input_4D)
op = Pool(ignore_border, mode=mode)
output = op(input_4D, ds, st, padding)
# restore to original shape
outshp = tensor.join(0, input.shape[:-2], output.shape[-2:])
......@@ -143,7 +141,7 @@ class Pool(OpenMPOp):
"""
__props__ = ('ds', 'ignore_border', 'st', 'padding', 'mode')
__props__ = ('ignore_border', 'mode')
@staticmethod
def out_shape(imgshape, ds, ignore_border=False, st=None, padding=(0, 0)):
......@@ -188,9 +186,9 @@ class Pool(OpenMPOp):
r = tensor.extract_constant(r)
c = tensor.extract_constant(c)
if padding[0]:
r += padding[0] * 2
r = r + padding[0] * 2
if padding[1]:
c += padding[1] * 2
c = c + padding[1] * 2
if ignore_border:
if ds[0] == st[0]:
......@@ -234,50 +232,84 @@ class Pool(OpenMPOp):
rval = list(imgshape[:-2]) + [nr, nc]
return rval
def __init__(self, ds, ignore_border=False, st=None, padding=(0, 0),
mode='max', openmp=None):
def __init__(self, ignore_border=False, mode='max', openmp=None):
super(Pool, self).__init__(openmp=openmp)
self.ds = tuple(ds)
if not all([isinstance(d, integer_types) for d in ds]):
raise ValueError(
"Pool downsample parameters must be ints."
" Got %s" % str(ds))
if st is None:
st = ds
assert isinstance(st, (tuple, list))
self.st = tuple(st)
self.ignore_border = ignore_border
self.padding = tuple(padding)
if self.padding != (0, 0) and not ignore_border:
raise NotImplementedError(
'padding works only with ignore_border=True')
if self.padding[0] >= self.ds[0] or self.padding[1] >= self.ds[1]:
raise NotImplementedError(
'padding_h and padding_w must be smaller than strides')
if mode not in ['max', 'average_inc_pad', 'average_exc_pad', 'sum']:
raise ValueError(
"Pool mode parameter only support 'max', 'sum',"
" 'average_inc_pad' and 'average_exc_pad'. Got %s" % mode)
self.mode = mode
def make_node(self, x):
def prepare_node(self, node, storage_map, compute_map):
if len(node.inputs) == 1:
# Old interface
self.mode = node.op.mode
ws = theano.tensor.constant(node.op.ds)
st = theano.tensor.constant(node.op.st)
pad = theano.tensor.constant(node.op.padding)
node.inputs.append(ws)
node.inputs.append(st)
node.inputs.append(pad)
if isinstance(ws, theano.Constant):
storage_map[ws] = [ws.data]
compute_map[ws] = [True]
else:
storage_map[ws] = [None]
compute_map[ws] = [False]
if isinstance(st, theano.Constant):
storage_map[st] = [st.data]
compute_map[st] = [True]
else:
storage_map[st] = [None]
compute_map[st] = [False]
if isinstance(pad, theano.Constant):
storage_map[pad] = [pad.data]
compute_map[pad] = [True]
else:
storage_map[pad] = [None]
compute_map[pad] = [False]
def make_node(self, x, ws, stride=None, pad=(0, 0)):
# TODO: consider restricting the dtype?
x = tensor.as_tensor_variable(x)
if stride is None:
stride = ws
if isinstance(pad, (tuple, list)):
if tuple(pad) != (0, 0) and not self.ignore_border:
raise NotImplementedError(
'padding works only with ignore_border=True')
if isinstance(ws, (tuple, list)):
if pad[0] >= ws[0] or pad[1] >= ws[1]:
raise NotImplementedError(
'padding_h and padding_w must be smaller than strides')
ws = tensor.as_tensor_variable(ws)
stride = tensor.as_tensor_variable(stride)
pad = tensor.as_tensor_variable(pad)
assert ws.ndim == 1
assert stride.ndim == 1
assert pad.ndim == 1
if x.type.ndim != 4:
raise TypeError()
if not ws.dtype.startswith('int'):
raise TypeError('Pool downsample parameters must be ints.')
if not stride.dtype.startswith('int'):
raise TypeError('Stride parameters must be ints.')
if not pad.dtype.startswith('int'):
raise TypeError('Padding parameters must be ints.')
# If the input shape are broadcastable we can have 0 in the output shape
broad = x.broadcastable[:2] + (False, False)
out = tensor.TensorType(x.dtype, broad)
return gof.Apply(self, [x], [out()])
return gof.Apply(self, [x, ws, stride, pad], [out()])
def perform(self, node, inp, out):
x, = inp
x, ws, stride, pad = inp
z, = out
assert ws.shape == stride.shape == pad.shape == (2,)
if len(x.shape) != 4:
raise NotImplementedError(
'Pool requires 4D input for now')
z_shape = self.out_shape(x.shape, self.ds, self.ignore_border, self.st,
self.padding)
z_shape = self.out_shape(x.shape, ws, self.ignore_border, stride, pad)
if not self.ignore_border:
assert z_shape[2] > 0
assert z_shape[3] > 0
......@@ -288,16 +320,16 @@ class Pool(OpenMPOp):
pr = zz.shape[-2]
# number of pooling output cols
pc = zz.shape[-1]
ds0, ds1 = self.ds
st0, st1 = self.st
pad_h = self.padding[0]
pad_w = self.padding[1]
ws0, ws1 = ws
st0, st1 = stride
pad_h = pad[0]
pad_w = pad[1]
img_rows = x.shape[-2] + 2 * pad_h
img_cols = x.shape[-1] + 2 * pad_w
inc_pad = self.mode == 'average_inc_pad'
# pad the image
if self.padding != (0, 0):
if (pad_h, pad_w) != (0, 0):
y = numpy.zeros(
(x.shape[0], x.shape[1], img_rows, img_cols),
dtype=x.dtype)
......@@ -314,40 +346,41 @@ class Pool(OpenMPOp):
for k in xrange(x.shape[1]):
for r in xrange(pr):
row_st = r * st0
row_end = builtins.min(row_st + ds0, img_rows)
row_end = builtins.min(row_st + ws0, img_rows)
if not inc_pad:
row_st = builtins.max(row_st, self.padding[0])
row_st = builtins.max(row_st, pad_h)
row_end = builtins.min(row_end, x.shape[-2] + pad_h)
for c in xrange(pc):
col_st = c * st1
col_end = builtins.min(col_st + ds1, img_cols)
col_end = builtins.min(col_st + ws1, img_cols)
if not inc_pad:
col_st = builtins.max(col_st, self.padding[1])
col_st = builtins.max(col_st, pad_w)
col_end = builtins.min(col_end,
x.shape[-1] + pad_w)
zz[n, k, r, c] = func(y[
n, k, row_st:row_end, col_st:col_end])
def infer_shape(self, node, in_shapes):
shp = self.out_shape(in_shapes[0], self.ds,
self.ignore_border, self.st, self.padding)
ws, stride, pad = [node.inputs[1], node.inputs[2], node.inputs[3]]
shp = self.out_shape(in_shapes[0], ws, self.ignore_border, stride,
pad)
return [shp]
def grad(self, inp, grads):
x, = inp
x, ws, stride, pad = inp
gz, = grads
disc = [DisconnectedType()() for i in inp[1:]]
if self.mode == 'max':
maxout = self(x)
return [MaxPoolGrad(self.ds,
ignore_border=self.ignore_border,
st=self.st, padding=self.padding)(
x, maxout, gz)]
maxout = self(x, ws, stride, pad)
return [MaxPoolGrad(ignore_border=self.ignore_border)(
x, maxout, gz, ws=ws, stride=stride, pad=pad)] + disc
else:
return [AveragePoolGrad(self.ds,
ignore_border=self.ignore_border,
st=self.st, padding=self.padding,
return [AveragePoolGrad(ignore_border=self.ignore_border,
mode=self.mode)(
x, gz)]
x, gz, ws=ws, stride=stride, pad=pad)] + disc
def connection_pattern(self, node):
return [[1], [0], [0], [0]]
def c_headers(self):
headers = ['<algorithm>']
......@@ -357,21 +390,41 @@ class Pool(OpenMPOp):
def c_code(self, node, name, inp, out, sub):
if self.mode not in ('max', 'sum', 'average_exc_pad', 'average_inc_pad'):
raise theano.gof.utils.MethodNotDefined()
x, = inp
x, ws, stride, pad = inp
z, = out
fail = sub['fail']
ignore_border = int(self.ignore_border)
ds0, ds1 = self.ds
st0, st1 = self.st
pd0, pd1 = self.padding
if self.openmp:
omp_parallel = '#pragma omp parallel for private(r_st, r_end, c_st, c_end, collector) schedule(static)'
else:
omp_parallel = ''
ccode = """
int ws0, ws1, st0, st1, pd0, pd1;
int typenum = PyArray_ObjectType((PyObject*)%(x)s, 0);
int z_r, z_c; // shape of the output
int r, c; // shape of the padded_input
if(PyArray_DIM(%(ws)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "ws must be a vector of size 2");
%(fail)s;
}
if(PyArray_DIM(%(stride)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "stride must be a vector of size 2");
%(fail)s;
}
if(PyArray_DIM(%(pad)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "pad must be a vector of size 2");
%(fail)s;
}
// Getting ws, stride and pad
ws0 = *((npy_intp*)PyArray_GETPTR1(%(ws)s, 0));
ws1 = *((npy_intp*)PyArray_GETPTR1(%(ws)s, 1));
st0 = *((npy_intp*)PyArray_GETPTR1(%(stride)s, 0));
st1 = *((npy_intp*)PyArray_GETPTR1(%(stride)s, 1));
pd0 = *((npy_intp*)PyArray_GETPTR1(%(pad)s, 0));
pd1 = *((npy_intp*)PyArray_GETPTR1(%(pad)s, 1));
if(PyArray_NDIM(%(x)s)!=4)
{
PyErr_SetString(PyExc_ValueError, "x must be a 4d ndarray");
......@@ -379,9 +432,9 @@ class Pool(OpenMPOp):
}
r = PyArray_DIMS(%(x)s)[2];
c = PyArray_DIMS(%(x)s)[3];
r += %(pd0)s * 2;
c += %(pd1)s * 2;
if (%(pd0)s != 0 && %(pd1)s != 0 && !%(ignore_border)s)
r += pd0 * 2;
c += pd1 * 2;
if (pd0 != 0 && pd1 != 0 && !%(ignore_border)s)
{
PyErr_SetString(PyExc_ValueError,
"padding must be (0,0) when ignore border is False");
......@@ -390,42 +443,42 @@ class Pool(OpenMPOp):
if (%(ignore_border)s)
{
// '/' in C is different from '/' in python
if (r - %(ds0)s < 0)
if (r - ws0 < 0)
{
z_r = 0;
}
else
{
z_r = (r - %(ds0)s) / %(st0)s + 1;
z_r = (r - ws0) / st0 + 1;
}
if (c - %(ds1)s < 0)
if (c - ws1 < 0)
{
z_c = 0;
}
else
{
z_c = (c - %(ds1)s) / %(st1)s + 1;
z_c = (c - ws1) / st1 + 1;
}
}
else
{
// decide how many rows the output has
if (%(st0)s >= %(ds0)s)
if (st0 >= ws0)
{
z_r = (r - 1) / %(st0)s + 1;
z_r = (r - 1) / st0 + 1;
}
else
{
z_r = std::max(0, (r - 1 - %(ds0)s + %(st0)s) / %(st0)s) + 1;
z_r = std::max(0, (r - 1 - ws0 + st0) / st0) + 1;
}
// decide how many columns the output has
if (%(st1)s >= %(ds1)s)
if (st1 >= ws1)
{
z_c = (c - 1) / %(st1)s + 1;
z_c = (c - 1) / st1 + 1;
}
else
{
z_c = std::max(0, (c - 1 - %(ds1)s + %(st0)s) / %(st1)s) + 1;
z_c = std::max(0, (c - 1 - ws1 + st0) / st1) + 1;
}
assert(z_r > 0);
assert(z_c > 0);
......@@ -458,30 +511,30 @@ class Pool(OpenMPOp):
int b = t %% PyArray_DIMS(%(x)s)[0];
int k = t / PyArray_DIMS(%(x)s)[0];
for(int i=0; i < z_r; i++){
r_st = i * %(st0)s;
r_end = r_st + %(ds0)s;
r_st = i * st0;
r_end = r_st + ws0;
// skip the padding
r_st = r_st < %(pd0)s ? %(pd0)s : r_st;
r_end = r_end > (r - %(pd0)s) ? r - %(pd0)s : r_end;
r_st = r_st < pd0 ? pd0 : r_st;
r_end = r_end > (r - pd0) ? r - pd0 : r_end;
// from padded_img space to img space
r_st -= %(pd0)s;
r_end -= %(pd0)s;
r_st -= pd0;
r_end -= pd0;
// handle the case where no padding, ignore border is True
if (%(ignore_border)s)
{
r_end = r_end > r ? r : r_end;
}
for(int j=0; j<z_c; j++){
c_st = j * %(st1)s;
c_end = c_st + %(ds1)s;
c_st = j * st1;
c_end = c_st + ws1;
// skip the padding
c_st = c_st < %(pd1)s ? %(pd1)s : c_st;
c_end = c_end > (c - %(pd1)s) ? c - %(pd1)s : c_end;
c_st = c_st < pd1 ? pd1 : c_st;
c_end = c_end > (c - pd1) ? c - pd1 : c_end;
dtype_%(z)s * z = (
(dtype_%(z)s*)(PyArray_GETPTR4(%(z)s, b, k, i, j)));
// change coordinates from padding_img space into img space
c_st -= %(pd1)s;
c_end -= %(pd1)s;
c_st -= pd1;
c_end -= pd1;
// handle the case where no padding, ignore border is True
if (%(ignore_border)s)
{
......@@ -523,7 +576,7 @@ class Pool(OpenMPOp):
"""
elif self.mode == 'average_inc_pad' and self.ignore_border:
ccode += """
z[0] = collector / (%(ds0)s * %(ds1)s);
z[0] = collector / (ws0 * ws1);
"""
else:
ccode += """
......@@ -538,11 +591,11 @@ class Pool(OpenMPOp):
return ccode % locals()
def c_code_cache_version(self):
return (0, 6, 8, 4, self.openmp)
return (0, 6, 8, 6, self.openmp)
class PoolGrad(OpenMPOp):
__props__ = ('ds', 'ignore_border', 'st', 'padding', 'mode')
__props__ = ('ignore_border', 'mode')
@staticmethod
def out_shape(imgshape, ds, ignore_border=False, st=None, padding=(0, 0)):
......@@ -624,13 +677,8 @@ class PoolGrad(OpenMPOp):
rval = list(imgshape[:-2]) + [nr, nc]
return rval
def __init__(self, ds, ignore_border, st=None, padding=(0, 0), mode='max', openmp=None):
self.ds = tuple(ds)
def __init__(self, ignore_border, mode='max', openmp=None):
self.ignore_border = ignore_border
if st is None:
st = ds
self.st = tuple(st)
self.padding = tuple(padding)
if mode not in ['max', 'sum', 'average_inc_pad', 'average_exc_pad']:
raise ValueError(
"Pool mode parameter only support 'max', 'sum',"
......@@ -638,43 +686,86 @@ class PoolGrad(OpenMPOp):
self.mode = mode
super(PoolGrad, self).__init__(openmp=openmp)
def prepare_node(self, node, storage_map, compute_map):
if len(node.inputs) < 5: # 5 for AveragePoolGrad, 6 for MaxPoolGrad
# Old interface
self.mode = node.op.mode
ws = theano.tensor.constant(node.op.ds)
st = theano.tensor.constant(node.op.st)
pad = theano.tensor.constant(node.op.padding)
node.inputs.append(ws)
node.inputs.append(st)
node.inputs.append(pad)
if isinstance(ws, theano.Constant):
storage_map[ws] = [ws.data]
compute_map[ws] = [True]
else:
storage_map[ws] = [None]
compute_map[ws] = [False]
if isinstance(st, theano.Constant):
storage_map[st] = [st.data]
compute_map[st] = [True]
else:
storage_map[st] = [None]
compute_map[st] = [False]
if isinstance(pad, theano.Constant):
storage_map[pad] = [pad.data]
compute_map[pad] = [True]
else:
storage_map[pad] = [None]
compute_map[pad] = [False]
def infer_shape(self, node, in_shapes):
return [in_shapes[0]]
class MaxPoolGrad(PoolGrad):
def __init__(self, ds, ignore_border, st=None, padding=(0, 0), openmp=None):
PoolGrad.__init__(self, ds, ignore_border, st, padding, 'max', openmp)
def __init__(self, ignore_border, openmp=None):
PoolGrad.__init__(self, ignore_border, mode='max', openmp=openmp)
def make_node(self, x, maxout, gz):
def make_node(self, x, maxout, gz, ws, stride=None, pad=(0, 0)):
# make_node should only be called by the grad function of
# Pool, so these asserts should not fail.
x = tensor.as_tensor_variable(x)
maxout = tensor.as_tensor_variable(maxout)
gz = tensor.as_tensor_variable(gz)
if stride is None:
stride = ws
ws = tensor.as_tensor_variable(ws)
stride = tensor.as_tensor_variable(stride)
pad = tensor.as_tensor_variable(pad)
assert isinstance(x, Variable) and x.ndim == 4
assert isinstance(maxout, Variable) and maxout.ndim == 4
assert isinstance(gz, Variable) and gz.ndim == 4
return Apply(self, [x, maxout, gz], [x.type()])
assert isinstance(ws, Variable) and ws.ndim == 1
assert isinstance(stride, Variable) and stride.ndim == 1
assert isinstance(pad, Variable) and pad.ndim == 1
if not ws.dtype.startswith('int'):
raise TypeError('Pool downsample parameters must be ints.')
if not stride.dtype.startswith('int'):
raise TypeError('Stride parameters must be ints.')
if not pad.dtype.startswith('int'):
raise TypeError('Padding parameters must be ints.')
return Apply(self, [x, maxout, gz, ws, stride, pad], [x.type()])
def perform(self, node, inp, out):
assert self.mode == 'max'
x, maxout, gz = inp
x, maxout, gz, ws, stride, pad = inp
gx_stg, = out
assert ws.shape == stride.shape == pad.shape == (2,)
# number of pooling output rows
pr = maxout.shape[-2]
# number of pooling output cols
pc = maxout.shape[-1]
ds0, ds1 = self.ds
st0, st1 = self.st
pad_h = self.padding[0]
pad_w = self.padding[1]
ws0, ws1 = ws
st0, st1 = stride
pad_h = pad[0]
pad_w = pad[1]
img_rows = x.shape[-2] + 2 * pad_h
img_cols = x.shape[-1] + 2 * pad_w
# pad the image
if self.padding != (0, 0):
if (pad_h, pad_w) != (0, 0):
y = numpy.zeros(
(x.shape[0], x.shape[1], img_rows, img_cols),
dtype=x.dtype)
......@@ -685,11 +776,11 @@ class MaxPoolGrad(PoolGrad):
for n in xrange(x.shape[0]):
for k in xrange(x.shape[1]):
for r in xrange(pr):
row_st = builtins.max(r * st0, self.padding[0])
row_end = builtins.min(row_st + ds0, img_rows)
row_st = builtins.max(r * st0, pad_h)
row_end = builtins.min(row_st + ws0, img_rows)
for c in xrange(pc):
col_st = builtins.max(c * st1, self.padding[1])
col_end = builtins.min(col_st + ds1, img_cols)
col_st = builtins.max(c * st1, pad_w)
col_end = builtins.min(col_st + ws1, img_cols)
for row_ind in xrange(row_st, row_end):
for col_ind in xrange(col_st, col_end):
if (maxout[n, k, r, c] == y[n, k, row_ind, col_ind]):
......@@ -699,23 +790,23 @@ class MaxPoolGrad(PoolGrad):
gx_stg[0] = gx
def grad(self, inp, grads):
x, maxout, gz = inp
x, maxout, gz, ws, stride, pad = inp
ggx, = grads
return [theano.tensor.zeros_like(x),
theano.tensor.zeros_like(maxout),
DownsampleFactorMaxGradGrad(
self.ds, ignore_border=self.ignore_border,
st=self.st, padding=self.padding)(x, maxout, ggx)]
return ([theano.tensor.zeros_like(x),
theano.tensor.zeros_like(maxout),
DownsampleFactorMaxGradGrad(ignore_border=self.ignore_border)(
x, maxout, ggx, ws, stride, pad)] +
[DisconnectedType()() for i in inp[3:]])
def connection_pattern(self, node):
return [[1], [1], [1], [0], [0], [0]]
def c_code(self, node, name, inp, out, sub):
assert self.mode == 'max'
x, z, gz = inp
x, z, gz, ws, stride, pad = inp
gx, = out
fail = sub['fail']
ignore_border = int(self.ignore_border)
ds0, ds1 = self.ds
st0, st1 = self.st
pd0, pd1 = self.padding
if self.openmp:
omp_parallel = '#pragma omp parallel for private(r_st, r_end, c_st, c_end, maximum) schedule(static)'
else:
......@@ -725,6 +816,9 @@ class MaxPoolGrad(PoolGrad):
int x_typenum = PyArray_ObjectType((PyObject*)%(x)s, 0);
int z_typenum = PyArray_ObjectType((PyObject*)%(z)s, 0);
int gz_typenum = PyArray_ObjectType((PyObject*)%(gz)s, 0);
int ws0, ws1, st0, st1, pd0, pd1;
int z_r, z_c;
int r, c; // shape of the padded_input
if ((x_typenum != z_typenum) || (x_typenum != gz_typenum))
{
PyErr_SetString(PyExc_ValueError, "input types must all match");
......@@ -745,14 +839,34 @@ class MaxPoolGrad(PoolGrad):
PyErr_SetString(PyExc_ValueError, "gz must be a 4d ndarray");
%(fail)s;
}
int z_r, z_c;
if(PyArray_DIM(%(ws)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "ws must be a vector of size 2");
%(fail)s;
}
if(PyArray_DIM(%(stride)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "stride must be a vector of size 2");
%(fail)s;
}
if(PyArray_DIM(%(pad)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "pad must be a vector of size 2");
%(fail)s;
}
// Getting ws, stride and pad
ws0 = *((npy_intp*)PyArray_GETPTR1(%(ws)s, 0));
ws1 = *((npy_intp*)PyArray_GETPTR1(%(ws)s, 1));
st0 = *((npy_intp*)PyArray_GETPTR1(%(stride)s, 0));
st1 = *((npy_intp*)PyArray_GETPTR1(%(stride)s, 1));
pd0 = *((npy_intp*)PyArray_GETPTR1(%(pad)s, 0));
pd1 = *((npy_intp*)PyArray_GETPTR1(%(pad)s, 1));
z_r = PyArray_DIMS(%(z)s)[2];
z_c = PyArray_DIMS(%(z)s)[3];
int r, c; // shape of the padded_input
r = PyArray_DIMS(%(x)s)[2];
c = PyArray_DIMS(%(x)s)[3];
r += %(pd0)s * 2;
c += %(pd1)s * 2;
r += pd0 * 2;
c += pd1 * 2;
// allocating memory for gx
if ((!%(gx)s)
|| !PyArray_ISCONTIGUOUS(%(gx)s)
......@@ -778,23 +892,23 @@ class MaxPoolGrad(PoolGrad):
int b = t %% PyArray_DIMS(%(x)s)[0];
int k = t / PyArray_DIMS(%(x)s)[0];
for(int i=0; i < z_r; i++){
r_st = i * %(st0)s;
r_end = r_st + %(ds0)s;
r_st = i * st0;
r_end = r_st + ws0;
// skip the padding
r_st = r_st < %(pd0)s ? %(pd0)s : r_st;
r_end = r_end > (r - %(pd0)s) ? r - %(pd0)s : r_end;
r_st = r_st < pd0 ? pd0 : r_st;
r_end = r_end > (r - pd0) ? r - pd0 : r_end;
// from padded_img space to img space
r_st -= %(pd0)s;
r_end -= %(pd0)s;
r_st -= pd0;
r_end -= pd0;
for(int j=0; j<z_c; j++){
c_st = j * %(st1)s;
c_end = c_st + %(ds1)s;
c_st = j * st1;
c_end = c_st + ws1;
// skip the padding
c_st = c_st < %(pd1)s ? %(pd1)s : c_st;
c_end = c_end > (c - %(pd1)s) ? c - %(pd1)s : c_end;
c_st = c_st < pd1 ? pd1 : c_st;
c_end = c_end > (c - pd1) ? c - pd1 : c_end;
// change coordinates from padding_img space into img space
c_st -= %(pd1)s;
c_end -= %(pd1)s;
c_st -= pd1;
c_end -= pd1;
// the maximum value
maximum = ((dtype_%(z)s*)(PyArray_GETPTR4(%(z)s,b,k,i,j)))[0];
// the gradient corresponding to this maximum value in z
......@@ -820,36 +934,48 @@ class MaxPoolGrad(PoolGrad):
""" % locals()
def c_code_cache_version(self):
return (0, 7, self.openmp)
return (0, 9, self.openmp)
class AveragePoolGrad(PoolGrad):
def __init__(self, ds, ignore_border, st=None, padding=(0, 0),
mode='average_inc_pad'):
def __init__(self, ignore_border, mode='average_inc_pad'):
assert mode in ['sum', 'average_inc_pad', 'average_exc_pad']
PoolGrad.__init__(self, ds, ignore_border, st, padding, mode)
PoolGrad.__init__(self, ignore_border, mode)
# There is an extra dummy parameter to match the parameter count
# of MaxPoolGrad. They have to keep the same interface because of
# the DownsampleFactorMaxGrad trick to keep old scripts working
# (see downsample.py for details on this).
def make_node(self, x, gz, dummy=None):
def make_node(self, x, gz, ws, stride=None, pad=(0, 0), dummy=None):
# make_node should only be called by the grad function of
# Pool, so these asserts should not fail.
x = tensor.as_tensor_variable(x)
gz = tensor.as_tensor_variable(gz)
if stride is None:
stride = ws
ws = tensor.as_tensor_variable(ws)
stride = tensor.as_tensor_variable(stride)
pad = tensor.as_tensor_variable(pad)
assert isinstance(x, Variable) and x.ndim == 4
assert isinstance(gz, Variable) and gz.ndim == 4
return Apply(self, [x, gz], [x.type()])
assert isinstance(ws, Variable) and ws.ndim == 1
assert isinstance(stride, Variable) and stride.ndim == 1
assert isinstance(pad, Variable) and pad.ndim == 1
if not ws.dtype.startswith('int'):
raise TypeError('Pool downsample parameters must be ints.')
if not stride.dtype.startswith('int'):
raise TypeError('Stride parameters must be ints.')
if not pad.dtype.startswith('int'):
raise TypeError('Padding parameters must be ints.')
return Apply(self, [x, gz, ws, stride, pad], [x.type()])
def perform(self, node, inp, out):
if self.mode == 'average_exc_pad' and self.padding != (0, 0):
raise NotImplementedError()
x, gz = inp
x, gz, ws, stride, pad = inp
gx_stg, = out
z_shape = self.out_shape(x.shape, self.ds, self.ignore_border, self.st,
self.padding)
assert ws.shape == stride.shape == pad.shape == (2,)
if self.mode == 'average_exc_pad' and pad[0] != 0 and pad[1] != 0:
raise NotImplementedError()
z_shape = self.out_shape(x.shape, ws, self.ignore_border, stride, pad)
if (gx_stg[0] is None) or (gx_stg[0].shape != z_shape):
gx_stg[0] = numpy.empty(z_shape, dtype=x.dtype)
zz = gx_stg[0]
......@@ -857,17 +983,17 @@ class AveragePoolGrad(PoolGrad):
pr = zz.shape[-2]
# number of pooling output cols
pc = zz.shape[-1]
ds0, ds1 = self.ds
st0, st1 = self.st
pad_h = self.padding[0]
pad_w = self.padding[1]
ws0, ws1 = ws
st0, st1 = stride
pad_h = pad[0]
pad_w = pad[1]
img_rows = x.shape[-2] + 2 * pad_h
img_cols = x.shape[-1] + 2 * pad_w
inc_pad = self.mode == 'average_inc_pad'
sum_mode = self.mode == 'sum'
# pad the image
if self.padding != (0, 0):
if (pad_h, pad_w) != (0, 0):
y = numpy.zeros(
(x.shape[0], x.shape[1], img_rows, img_cols),
dtype=x.dtype)
......@@ -881,15 +1007,14 @@ class AveragePoolGrad(PoolGrad):
if sum_mode or inc_pad:
row_st = r * st0
else:
row_st = builtins.max(r * st0, self.padding[0])
row_end = builtins.min(row_st + ds0, img_rows)
row_st = builtins.max(r * st0, pad_h)
row_end = builtins.min(row_st + ws0, img_rows)
for c in xrange(pc):
if sum_mode or inc_pad:
col_st = c * st1
else:
col_st = builtins.max(c * st1,
self.padding[1])
col_end = builtins.min(col_st + ds1, img_cols)
col_st = builtins.max(c * st1, pad_w)
col_end = builtins.min(col_st + ws1, img_cols)
if sum_mode:
val = gz[n, k, r, c]
else:
......@@ -901,39 +1026,26 @@ class AveragePoolGrad(PoolGrad):
gx_stg[0] = gx
def grad(self, inp, grads):
x, gz = inp
x, gz, ws, stride, pad = inp
ggx, = grads
return [theano.tensor.zeros_like(x),
Pool(self.ds, ignore_border=self.ignore_border,
st=self.st, padding=self.padding, mode=self.mode)(ggx)]
return ([theano.tensor.zeros_like(x),
Pool(ignore_border=self.ignore_border, mode=self.mode)(ggx,
ws, stride, pad)] + [DisconnectedType()() for i in inp[2:]])
def connection_pattern(self, node):
return [[1], [1], [0], [0], [0]]
class DownsampleFactorMaxGradGrad(OpenMPOp):
__props__ = ('ds', 'ignore_border', 'st', 'padding', 'mode')
__props__ = ('ignore_border', 'mode')
def __init__(self, ds, ignore_border, st=None, padding=(0, 0), mode='max', openmp=None):
self.ds = tuple(ds)
if not all([isinstance(d, integer_types) for d in ds]):
raise ValueError(
"Pool downsample parameters must be ints."
" Got %s" % str(ds))
if st is None:
st = ds
assert isinstance(st, (tuple, list))
self.st = tuple(st)
def __init__(self, ignore_border, mode='max', openmp=None):
self.ignore_border = ignore_border
self.padding = tuple(padding)
if self.padding != (0, 0) and not ignore_border:
raise NotImplementedError(
'padding works only with ignore_border=True')
if self.padding[0] >= self.ds[0] or self.padding[1] >= self.ds[1]:
raise NotImplementedError(
'padding_h and padding_w must be smaller than strides')
self.mode = mode
super(DownsampleFactorMaxGradGrad, self).__init__(openmp=openmp)
assert self.mode == 'max'
def make_node(self, x, maxout, gz):
def make_node(self, x, maxout, gz, ws, stride=None, pad=(0, 0)):
# make_node should only be called by the grad function of
# MaxPoolGrad, so these asserts should not fail.
x = tensor.as_tensor_variable(x)
......@@ -942,12 +1054,34 @@ class DownsampleFactorMaxGradGrad(OpenMPOp):
assert x.ndim == 4
assert maxout.ndim == 4
assert gz.ndim == 4
return Apply(self, [x, maxout, gz], [x.type()])
if stride is None:
stride = ws
if isinstance(pad, (tuple, list)):
if tuple(pad) != (0, 0) and not self.ignore_border:
raise NotImplementedError(
'padding works only with ignore_border=True')
if isinstance(ws, (tuple, list)):
if pad[0] >= ws[0] or pad[1] >= ws[1]:
raise NotImplementedError(
'padding_h and padding_w must be smaller than strides')
ws = tensor.as_tensor_variable(ws)
stride = tensor.as_tensor_variable(stride)
pad = tensor.as_tensor_variable(pad)
assert ws.ndim == 1
assert stride.ndim == 1
assert pad.ndim == 1
if not ws.dtype.startswith('int'):
raise TypeError('Pool downsample parameters must be ints.')
if not stride.dtype.startswith('int'):
raise TypeError('Stride parameters must be ints.')
if not pad.dtype.startswith('int'):
raise TypeError('Padding parameters must be ints.')
return Apply(self, [x, maxout, gz, ws, stride, pad], [x.type()])
def perform(self, node, inp, out):
x, maxout, ggx = inp
x, maxout, ggx, ws, stride, pad = inp
z, = out
assert ws.shape == stride.shape == pad.shape == (2,)
if len(x.shape) != 4:
raise NotImplementedError(
'DownsampleFactorMaxGradGrad requires 4D input for now')
......@@ -958,14 +1092,14 @@ class DownsampleFactorMaxGradGrad(OpenMPOp):
pr = ggz.shape[-2]
# number of pooling output cols
pc = ggz.shape[-1]
ds0, ds1 = self.ds
st0, st1 = self.st
pd0, pd1 = self.padding
ws0, ws1 = ws
st0, st1 = stride
pd0, pd1 = pad
img_rows = x.shape[-2] + 2 * pd0
img_cols = x.shape[-1] + 2 * pd1
# pad the image and its gradients
if self.padding != (0, 0):
if pd0 != 0 and pd1 != 0:
y_padded = numpy.zeros(
(x.shape[0], x.shape[1], img_rows, img_cols),
dtype=x.dtype) + x.min() - 1
......@@ -982,10 +1116,10 @@ class DownsampleFactorMaxGradGrad(OpenMPOp):
for k in xrange(x.shape[1]):
for r in xrange(pr):
row_st = r * st0
row_end = builtins.min(row_st + ds0, img_rows)
row_end = builtins.min(row_st + ws0, img_rows)
for c in xrange(pc):
col_st = c * st1
col_end = builtins.min(col_st + ds1, img_cols)
col_end = builtins.min(col_st + ws1, img_cols)
for row_ind in xrange(row_st, row_end):
for col_ind in xrange(col_st, col_end):
if (maxout[n, k, r, c] == y_padded[n, k, row_ind, col_ind]):
......@@ -995,38 +1129,63 @@ class DownsampleFactorMaxGradGrad(OpenMPOp):
return [in_shapes[1]]
def grad(self, inp, grads):
x, maxout, ggx = inp
x, maxout, ggx, ws, stride, pad = inp
gz, = grads
return [theano.tensor.zeros_like(x),
theano.tensor.zeros_like(maxout),
MaxPoolGrad(
self.ds, ignore_border=self.ignore_border,
st=self.st, padding=self.padding)(x, maxout, gz)]
MaxPoolGrad(ignore_border=self.ignore_border)(x, maxout, gz,
ws, stride, pad),
DisconnectedType()(),
DisconnectedType()(),
DisconnectedType()()]
def connection_pattern(self, node):
return [[1], [1], [1], [0], [0], [0]]
def c_code(self, node, name, inp, out, sub):
if self.mode != 'max':
raise theano.gof.utils.MethodNotDefined()
x, maxout, ggx = inp
x, maxout, ggx, ws, stride, pad = inp
z, = out # the grad of grad
fail = sub['fail']
ignore_border = int(self.ignore_border)
ds0, ds1 = self.ds
st0, st1 = self.st
pd0, pd1 = self.padding
if self.openmp:
omp_parallel = '#pragma omp parallel for private(r_st, r_end, c_st, c_end, maximum) schedule(static)'
else:
omp_parallel = ''
return """
int ws0, ws1, st0, st1, pd0, pd1;
int z_typenum = PyArray_ObjectType((PyObject*)%(maxout)s, 0);
int z_r, z_c;
int r, c; // shape of the padded_input
if(PyArray_DIM(%(ws)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "ws must be a vector of size 2");
%(fail)s;
}
if(PyArray_DIM(%(stride)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "stride must be a vector of size 2");
%(fail)s;
}
if(PyArray_DIM(%(pad)s, 0)!=2)
{
PyErr_SetString(PyExc_ValueError, "pad must be a vector of size 2");
%(fail)s;
}
// Getting ws, stride and pad
ws0 = *((npy_intp*)PyArray_GETPTR1(%(ws)s, 0));
ws1 = *((npy_intp*)PyArray_GETPTR1(%(ws)s, 1));
st0 = *((npy_intp*)PyArray_GETPTR1(%(stride)s, 0));
st1 = *((npy_intp*)PyArray_GETPTR1(%(stride)s, 1));
pd0 = *((npy_intp*)PyArray_GETPTR1(%(pad)s, 0));
pd1 = *((npy_intp*)PyArray_GETPTR1(%(pad)s, 1));
z_r = PyArray_DIMS(%(maxout)s)[2];
z_c = PyArray_DIMS(%(maxout)s)[3];
int r, c; // shape of the padded_input
r = PyArray_DIMS(%(x)s)[2];
c = PyArray_DIMS(%(x)s)[3];
r += %(pd0)s * 2;
c += %(pd1)s * 2;
r += pd0 * 2;
c += pd1 * 2;
// allocating memory for output
if ((!%(z)s)
|| !PyArray_ISCONTIGUOUS(%(z)s)
......@@ -1050,23 +1209,23 @@ class DownsampleFactorMaxGradGrad(OpenMPOp):
int b = t %% PyArray_DIMS(%(x)s)[0];
int k = t / PyArray_DIMS(%(x)s)[0];
for(int i=0; i < z_r; i++){
r_st = i * %(st0)s;
r_end = r_st + %(ds0)s;
r_st = i * st0;
r_end = r_st + ws0;
// skip the padding
r_st = r_st < %(pd0)s ? %(pd0)s : r_st;
r_end = r_end > (r - %(pd0)s) ? r - %(pd0)s : r_end;
r_st = r_st < pd0 ? pd0 : r_st;
r_end = r_end > (r - pd0) ? r - pd0 : r_end;
// from padded_img space to img space
r_st -= %(pd0)s;
r_end -= %(pd0)s;
r_st -= pd0;
r_end -= pd0;
for(int j=0; j<z_c; j++){
c_st = j * %(st1)s;
c_end = c_st + %(ds1)s;
c_st = j * st1;
c_end = c_st + ws1;
// skip the padding
c_st = c_st < %(pd1)s ? %(pd1)s : c_st;
c_end = c_end > (c - %(pd1)s) ? c - %(pd1)s : c_end;
c_st = c_st < pd1 ? pd1 : c_st;
c_end = c_end > (c - pd1) ? c - pd1 : c_end;
// from padding_img space into img space
c_st -= %(pd1)s;
c_end -= %(pd1)s;
c_st -= pd1;
c_end -= pd1;
// the maximum value
maximum = ((dtype_%(maxout)s*)(PyArray_GETPTR4(%(maxout)s,b,k,i,j)))[0];
// z at this position
......@@ -1090,4 +1249,4 @@ class DownsampleFactorMaxGradGrad(OpenMPOp):
""" % locals()
def c_code_cache_version(self):
return (0, 1, self.openmp)
return (0, 3, self.openmp)
from __future__ import absolute_import, print_function, division
from nose.plugins.skip import SkipTest
from itertools import product
import os
import unittest
from six import reraise
from six.moves import cPickle
import six.moves.builtins as builtins
import sys
import numpy
......@@ -14,8 +19,6 @@ from theano.tensor.signal.pool import (Pool, pool_2d,
max_pool_2d_same_size,
DownsampleFactorMaxGradGrad)
from theano.tensor.signal.downsample import DownsampleFactorMaxGrad
from theano import function
......@@ -197,9 +200,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
utt.assert_allclose(output_val, numpy_output_val)
# Pool op
maxpool_op = Pool(maxpoolshp,
ignore_border=ignore_border,
mode=mode)(images)
maxpool_op = Pool(ignore_border=ignore_border,
mode=mode)(images, maxpoolshp)
output_shape = Pool.out_shape(imval.shape, maxpoolshp,
ignore_border=ignore_border)
......@@ -245,9 +247,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
"outshape is %s, calculated shape is %s"
% (outputshp, numpy_output_val.shape))
maxpool_op = \
Pool(maxpoolshp,
ignore_border=ignore_border,
st=stride, mode=mode)(images)
Pool(ignore_border=ignore_border, mode=mode)(
images, maxpoolshp, stride)
f = function([images], maxpool_op)
output_val = f(imval)
utt.assert_allclose(output_val, numpy_output_val)
......@@ -286,9 +287,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
"outshape is %s, calculated shape is %s"
% (outputshp, numpy_output_val.shape))
maxpool_op = \
Pool(maxpoolshp,
ignore_border=ignore_border,
st=stride, mode=mode)(images)
Pool(ignore_border=ignore_border, mode=mode)(
images, maxpoolshp, stride)
f = function([images], maxpool_op)
output_val = f(imval)
utt.assert_allclose(output_val, numpy_output_val)
......@@ -315,10 +315,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
numpy_output_val = self.numpy_max_pool_2d_stride_padding(
imval, maxpoolsize, ignore_border,
stridesize, paddingsize, mode)
maxpool_op = Pool(
maxpoolsize,
ignore_border=ignore_border,
st=stridesize, padding=paddingsize, mode=mode)(images)
maxpool_op = Pool(ignore_border=ignore_border, mode=mode)(
images, maxpoolsize, stridesize, paddingsize)
f = function([images], maxpool_op)
output_val = f(imval)
utt.assert_allclose(output_val, numpy_output_val)
......@@ -340,12 +338,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
paddingsize = paddingsizes[i]
def mp(input):
return Pool(
maxpoolsize, ignore_border=True,
st=stridesize,
padding=paddingsize,
mode=mode,
)(input)
return Pool(ignore_border=True, mode=mode)(
input, maxpoolsize, stridesize, paddingsize)
utt.verify_grad(mp, [imval], rng=rng)
def test_DownsampleFactorMax_grad(self):
......@@ -361,9 +355,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
'average_inc_pad',
'average_exc_pad']):
def mp(input):
return Pool(maxpoolshp,
ignore_border=ignore_border,
mode=mode)(input)
return Pool(ignore_border=ignore_border, mode=mode)(
input, maxpoolshp)
utt.verify_grad(mp, [imval], rng=rng)
def test_DownsampleFactorMax_grad_st(self):
......@@ -381,9 +374,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
'average_exc_pad'],
stridesizes):
def mp(input):
return Pool(maxpoolshp,
ignore_border=ignore_border,
st=stride, mode=mode)(input)
return Pool(ignore_border=ignore_border, mode=mode)(
input, maxpoolshp, stride)
utt.verify_grad(mp, [imval], rng=rng)
def test_DownsampleFactorMax_grad_st_extra(self):
......@@ -404,10 +396,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
maxpoolshp = maxpoolshps[indx]
for ignore_border in [True, False]:
def mp(input):
return Pool(maxpoolshp,
ignore_border=ignore_border,
st=stride,
mode=mode)(input)
return Pool(ignore_border=ignore_border, mode=mode)(
input, maxpoolshp, stride)
utt.verify_grad(mp, [imval], rng=rng)
def test_DownsampleFactorMaxGrad_grad(self):
......@@ -426,11 +416,9 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape) * 10.0
def mp(input, grad):
out = Pool(
maxpoolshp, ignore_border=ignore_border)(input)
grad_op = MaxPoolGrad(
maxpoolshp, ignore_border=ignore_border)
return grad_op(input, out, grad)
out = Pool(ignore_border=ignore_border)(input, maxpoolshp)
grad_op = MaxPoolGrad(ignore_border=ignore_border)
return grad_op(input, out, grad, maxpoolshp)
utt.verify_grad(mp, [imval, grad_val], rng=rng)
......@@ -451,9 +439,9 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape) * 10.0
def mp(input, grad):
grad_op = AveragePoolGrad(
avgpoolshp, ignore_border=ignore_border, mode=mode)
return grad_op(input, grad)
grad_op = AveragePoolGrad(ignore_border=ignore_border,
mode=mode)
return grad_op(input, grad, avgpoolshp)
utt.verify_grad(mp, [imval, grad_val], rng=rng)
......@@ -474,13 +462,10 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape)
def mp(input, grad):
out = Pool(
maxpoolshp, ignore_border=ignore_border,
st=stride)(input)
grad_op = MaxPoolGrad(
maxpoolshp, ignore_border=ignore_border,
st=stride)
return grad_op(input, out, grad)
out = Pool(ignore_border=ignore_border)(
input, maxpoolshp, stride)
grad_op = MaxPoolGrad(ignore_border=ignore_border)
return grad_op(input, out, grad, maxpoolshp, stride)
utt.verify_grad(mp, [imval, grad_val], rng=rng)
......@@ -503,9 +488,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
def mp(input, grad):
grad_op = AveragePoolGrad(
avgpoolshp, ignore_border=ignore_border,
st=stride, mode=mode)
return grad_op(input, grad)
ignore_border=ignore_border, mode=mode)
return grad_op(input, grad, avgpoolshp, stride)
utt.verify_grad(mp, [imval, grad_val], rng=rng)
......@@ -531,13 +515,10 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape)
def mp(input, grad):
out = Pool(
maxpoolshp, ignore_border=ignore_border,
st=stride)(input)
grad_op = MaxPoolGrad(
maxpoolshp, ignore_border=ignore_border,
st=stride)
return grad_op(input, out, grad)
out = Pool(ignore_border=ignore_border)(input, maxpoolshp,
stride)
grad_op = MaxPoolGrad(ignore_border=ignore_border)
return grad_op(input, out, grad, maxpoolshp, stride)
# skip the grad verification when the output is empty
if numpy.prod(grad_shape) == 0:
......@@ -567,10 +548,9 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape)
def mp(input, grad):
grad_op = AveragePoolGrad(
avgpoolshp, ignore_border=ignore_border,
st=stride, mode=mode)
return grad_op(input, grad)
grad_op = AveragePoolGrad(ignore_border=ignore_border,
mode=mode)
return grad_op(input, grad, avgpoolshp, stride)
# skip the grad verification when the output is empty
if numpy.prod(grad_shape) == 0:
......@@ -598,14 +578,11 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape) * 10.0
def mp(input, grad):
out = Pool(
maxpoolsize, ignore_border=True,
st=stridesize,
padding=paddingsize,
)(input)
grad_op = MaxPoolGrad(maxpoolsize, ignore_border=True,
st=stridesize, padding=paddingsize)
return grad_op(input, out, grad)
out = Pool(ignore_border=True)(input, maxpoolsize, stridesize,
paddingsize)
grad_op = MaxPoolGrad(ignore_border=True)
return grad_op(input, out, grad, maxpoolsize, stridesize,
paddingsize)
utt.verify_grad(mp, [imval, grad_val], rng=rng)
def test_AveragePoolPaddingStride_grad_grad(self):
......@@ -630,10 +607,8 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
grad_val = rng.rand(*grad_shape) * 10.0
def mp(input, grad):
grad_op = AveragePoolGrad(avgpoolsize, ignore_border=True,
st=stridesize, padding=paddingsize,
mode=mode)
return grad_op(input, grad)
grad_op = AveragePoolGrad(ignore_border=True, mode=mode)
return grad_op(input, grad, avgpoolsize, stridesize, paddingsize)
utt.verify_grad(mp, [imval, grad_val], rng=rng)
def test_DownsampleFactorMax_hessian(self):
......@@ -669,16 +644,12 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
paddingsize = paddingsizes[i]
def mp(input1, input2):
pooled_out = Pool(
maxpoolsize, ignore_border=True,
st=stridesize,
padding=paddingsize,
)(input1)
out = DownsampleFactorMaxGradGrad(
ds=maxpoolsize,
ignore_border=True,
st=stridesize,
padding=paddingsize)(input1, pooled_out, input2)
op1 = Pool(ignore_border=True)
pooled_out = op1(input1, maxpoolsize, stride=stridesize,
pad=paddingsize)
op2 = DownsampleFactorMaxGradGrad(ignore_border=True)
out = op2(input1, pooled_out, input2, ws=maxpoolsize,
stride=stridesize, pad=paddingsize)
return out
utt.verify_grad(mp, [imval1, imval2], rng=rng)
......@@ -813,19 +784,18 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
continue
# checking shapes generated by Pool
self._compile_and_check([image],
[Pool(maxpoolshp,
ignore_border=ignore_border,
padding=padding)(image)],
[Pool(ignore_border=ignore_border)
(image, maxpoolshp, pad=padding)],
[image_val], Pool)
# checking shapes generated by MaxPoolGrad
maxout_val = rng.rand(*out_shapes[k][i][j])
gz_val = rng.rand(*out_shapes[k][i][j])
self._compile_and_check([image, maxout, gz],
[MaxPoolGrad(maxpoolshp,
ignore_border=ignore_border,
padding=padding)
(image, maxout, gz)],
[MaxPoolGrad(
ignore_border=ignore_border)
(image, maxout, gz, maxpoolshp,
pad=padding)],
[image_val, maxout_val, gz_val],
MaxPoolGrad,
warn=False)
......@@ -835,33 +805,75 @@ class TestDownsampleFactorMax(utt.InferShapeTester):
image_val = rng.rand(4, 6, 1, 1)
self._compile_and_check(
[image],
[Pool((2, 2),
ignore_border=True,
padding=(0, 0))(image)],
[Pool(ignore_border=True)(image, (2, 2), pad=(0, 0))],
[image_val], Pool)
def test_DownsampleFactorMaxGrad(self):
im = theano.tensor.tensor4()
maxout = theano.tensor.tensor4()
grad = theano.tensor.tensor4()
def test_pooling_with_tensor_vars(self):
x = tensor.ftensor4()
window_size = tensor.ivector()
stride = tensor.ivector()
padding = tensor.ivector()
data = numpy.random.normal(0, 1, (1, 1, 5, 5)).astype('float32')
# checking variable params vs fixed params
for ignore_border in [True, False]:
for mode in ['max', 'sum', 'average_inc_pad', 'average_exc_pad']:
y = pool_2d(x, window_size, ignore_border, stride, padding,
mode)
dx = theano.gradient.grad(y.sum(), x)
var_fct = theano.function([x, window_size, stride, padding],
[y, dx])
for ws in (4, 2, 5):
for st in (2, 3):
for pad in (0, 1):
if (pad > st or st > ws or
(pad != 0 and not ignore_border) or
(mode == 'average_exc_pad' and pad != 0)):
continue
y = pool_2d(x, (ws, ws), ignore_border, (st, st),
(pad, pad), mode)
dx = theano.gradient.grad(y.sum(), x)
fix_fct = theano.function([x], [y, dx])
var_y, var_dx = var_fct(data, (ws, ws), (st, st),
(pad, pad))
fix_y, fix_dx = fix_fct(data)
utt.assert_allclose(var_y, fix_y)
utt.assert_allclose(var_dx, fix_dx)
def test_old_pool_interface(self):
if sys.version_info[0] != 3:
# Only tested with python 3 because of pickling issues.
raise SkipTest('Skip old pool interface with python 2.x')
# 1. Load the old version
testfile_dir = os.path.dirname(os.path.realpath(__file__))
fname = 'old_pool_interface.pkl'
with open(os.path.join(testfile_dir, fname), 'rb') as fp:
try:
old_fct = cPickle.load(fp, encoding='latin1')
except ImportError:
# Windows sometimes fail with nonsensical errors like:
# ImportError: No module named type
# ImportError: No module named copy_reg
# when "type" and "copy_reg" are builtin modules.
if sys.platform == 'win32':
exc_type, exc_value, exc_trace = sys.exc_info()
reraise(SkipTest, exc_value, exc_trace)
raise
# 2. Create the new version
x = theano.tensor.ftensor4()
y = pool_2d(x, (2, 2), mode='max', ignore_border=True)
z = pool_2d(x, (2, 2), mode='average_exc_pad', ignore_border=True)
dy_dx = theano.gradient.grad(y.sum(), x)
dz_dx = theano.gradient.grad(z.sum(), x)
new_fct = theano.function([x], [y, z, dy_dx, dz_dx])
# 3. Assert that the answer is the same
rng = numpy.random.RandomState(utt.fetch_seed())
image_val = rng.rand(4, 6, 7, 9).astype(numpy.float32)
old_out = old_fct(image_val)
new_out = new_fct(image_val)
for o, n in zip(old_out, new_out):
utt.assert_allclose(o, n)
for mode in ['max', 'sum', 'average_inc_pad', 'average_exc_pad']:
f = theano.function([im, maxout, grad],
DownsampleFactorMaxGrad(ds=(3, 3),
ignore_border=False,
mode=mode)(im, maxout, grad),
on_unused_input='ignore')
if mode == 'max':
assert any(isinstance(n.op, MaxPoolGrad)
for n in f.maker.fgraph.toposort())
assert not any(isinstance(n.op, AveragePoolGrad)
for n in f.maker.fgraph.toposort())
else:
assert not any(isinstance(n.op, MaxPoolGrad)
for n in f.maker.fgraph.toposort())
assert any(isinstance(n.op, AveragePoolGrad)
for n in f.maker.fgraph.toposort())
if __name__ == '__main__':
unittest.main()
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论