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