提交 e8fe4ad0 authored 作者: jiakai's avatar jiakai

Maybe I did something wrong, or the padding in GpuCorrMM is wrong

上级 95d9c1a5
import copy import copy
import os import os
import logging
_logger = logging.getLogger(__name__)
import theano import theano
from theano import Apply from theano import Apply
...@@ -504,39 +506,61 @@ gpu_ger_inplace = GpuGer(inplace=True) ...@@ -504,39 +506,61 @@ gpu_ger_inplace = GpuGer(inplace=True)
class BaseGpuCorrMM(GpuOp): class BaseGpuCorrMM(GpuOp):
"""Base class for `GpuCorrMM`, `GpuCorrMM_gradWeights` and """Base class for `GpuCorrMM`, `GpuCorrMM_gradWeights` and
`GpuCorrMM_gradInputs`. Cannot be used directly.""" `GpuCorrMM_gradInputs`. Cannot be used directly.
def __init__(self, border_mode="valid", :param border_mode: one of 'valid', 'full', 'half'; additionally, the
subsample=(1, 1), padding size could be directly specified by an integer or a pair of
pad=(0, 0)): integers
:param subsample: perform subsampling of the output (default: (1, 1))
:param pad: *deprecated*, now you should always use border_mode
"""
def __init__(self, border_mode="valid", subsample=(1, 1), pad=(0, 0)):
if pad != (0, 0):
_logger.warning(
'do not use pad for BaseGpuCorrMM; please set padding in'
'border_mode, see the docstring for more details')
if border_mode != "valid": if border_mode != "valid":
raise ValueError("border_mode must be 'valid'") raise ValueError("border_mode must be 'valid'")
border_mode = pad
if isinstance(border_mode, int):
border_mode = (border_mode, border_mode)
if isinstance(border_mode, tuple):
pad_h, pad_w = map(int, border_mode)
border_mode = (pad_h, pad_w)
if not ((isinstance(border_mode, tuple) and min(border_mode) >= 0) or
border_mode in ('valid', 'full', 'half')):
raise ValueError(
'invalid border_mode {}, which must be either '
'"valid", "full", "half", an integer or a pair of'
' integers'.format(border_mode))
self.border_mode = border_mode self.border_mode = border_mode
if len(subsample) != 2: if len(subsample) != 2:
raise ValueError("subsample must have two elements") raise ValueError("subsample must have two elements")
self.subsample = subsample self.subsample = subsample
if (pad not in ("half", "full")) and (len(pad) != 2):
raise ValueError("pad must be 'half', 'full', or have two elements") @property
self.pad = pad def pad(self):
if self.border_mode != 'valid':
return self.border_mode
return (0, 0)
def __eq__(self, other): def __eq__(self, other):
return type(self) == type(other) \ return type(self) == type(other) \
and self.border_mode == other.border_mode \ and self.border_mode == other.border_mode \
and self.subsample == other.subsample \ and self.subsample == other.subsample
and self.pad == other.pad
def __hash__(self): def __hash__(self):
return hash(type(self)) \ return hash(type(self)) \
^ hash(self.border_mode) \ ^ hash(self.border_mode) \
^ hash(self.subsample) \ ^ hash(self.subsample)
^ hash(self.pad)
def __str__(self): def __str__(self):
return '%s{%s, %s, pad=%r}' % ( return '%s{%s, %s}' % (
self.__class__.__name__, self.__class__.__name__,
self.border_mode, self.border_mode,
str(self.subsample), str(self.subsample))
self.pad)
def flops(self, inp, outp): def flops(self, inp, outp):
""" Useful with the hack in profilemode to print the MFlops""" """ Useful with the hack in profilemode to print the MFlops"""
...@@ -558,7 +582,7 @@ class BaseGpuCorrMM(GpuOp): ...@@ -558,7 +582,7 @@ class BaseGpuCorrMM(GpuOp):
def c_code_cache_version(self): def c_code_cache_version(self):
# raise this whenever modifying any of the support_code_files # raise this whenever modifying any of the support_code_files
return (0, 23) return (0, 24)
def c_support_code_apply(self, node, nodename): def c_support_code_apply(self, node, nodename):
# REMEMBER TO RAISE c_code_cache_version when changing any of # REMEMBER TO RAISE c_code_cache_version when changing any of
...@@ -591,27 +615,28 @@ class BaseGpuCorrMM(GpuOp): ...@@ -591,27 +615,28 @@ class BaseGpuCorrMM(GpuOp):
:param sub: Dictionary of substitutions useable to help generating the :param sub: Dictionary of substitutions useable to help generating the
C code. C code.
:param height: If self.subsample[0] != 1, a variable giving the height :param height: If self.subsample[0] != 1, a variable giving the height
of the filters for direction="backprop weights" or the height of the of the filters for direction="backprop weights" or the height of
input images for direction="backprop inputs". the input images for direction="backprop inputs".
If self.pad == 'half', a variable giving the height of the filters
for direction="backprop weights". If self.border_mode == 'half', a variable giving the height of the
Ignored otherwise. filters for direction="backprop weights". Ignored otherwise.
:param width: If self.subsample[1] != 1, a variable giving the width :param width: If self.subsample[1] != 1, a variable giving the width
of the filters for direction="backprop weights" or the width of the of the filters for direction="backprop weights" or the width of the
input images for direction="backprop inputs". input images for direction="backprop inputs".
If self.pad == 'half', a variable giving the width of the filters
for direction="backprop weights". If self.border_mode == 'half', a variable giving the width of the
Ignored otherwise. filters for direction="backprop weights". Ignored otherwise.
""" """
if self.border_mode != "valid":
raise ValueError("mode must be 'valid'")
dH, dW = self.subsample dH, dW = self.subsample
if self.pad == "half": if self.border_mode == "half":
padH = padW = -1 padH = padW = -1
elif self.pad == "full": elif self.border_mode == "full":
padH = padW = -2 padH = padW = -2
elif isinstance(self.border_mode, tuple):
padH, padW = self.border_mode
else: else:
padH, padW = self.pad assert self.border_mode == "valid"
padH = padW = 0
if direction == "forward": if direction == "forward":
direction = 0 direction = 0
out = top out = top
...@@ -841,9 +866,9 @@ class GpuCorrMM(BaseGpuCorrMM): ...@@ -841,9 +866,9 @@ class GpuCorrMM(BaseGpuCorrMM):
bottom, weights = inp bottom, weights = inp
top, = grads top, = grads
top = gpu_contiguous(top) top = gpu_contiguous(top)
d_bottom = GpuCorrMM_gradInputs(self.border_mode, self.subsample, self.pad)( d_bottom = GpuCorrMM_gradInputs(self.border_mode, self.subsample)(
weights, top, bottom.shape[-2:]) weights, top, bottom.shape[-2:])
d_weights = GpuCorrMM_gradWeights(self.border_mode, self.subsample, self.pad)( d_weights = GpuCorrMM_gradWeights(self.border_mode, self.subsample)(
bottom, top, weights.shape[-2:]) bottom, top, weights.shape[-2:])
return d_bottom, d_weights return d_bottom, d_weights
......
...@@ -122,9 +122,7 @@ class GpuDnnConvDesc(GpuOp): ...@@ -122,9 +122,7 @@ class GpuDnnConvDesc(GpuOp):
"""This Op builds a convolution descriptor for use in the other """This Op builds a convolution descriptor for use in the other
convolution operations. convolution operations.
:param border_mode: 'valid' or 'full' see the doc of :func:`dnn_conv` for a description of the parameters
:param subsample: The subsample, tuple like (dx, dy)
:param conv_mode: 'conv' or 'cross'
""" """
__props__ = ('border_mode', 'subsample', 'conv_mode') __props__ = ('border_mode', 'subsample', 'conv_mode')
......
...@@ -586,7 +586,7 @@ def test_gemm_valid(): ...@@ -586,7 +586,7 @@ def test_gemm_valid():
extra_shapes += get_shapes2(scales_kern=(2, 2), kern_stride=(2, 2)) extra_shapes += get_shapes2(scales_kern=(2, 2), kern_stride=(2, 2))
for t in _test_valid(cuda.blas.BaseGpuCorrMM, for t in _test_valid(cuda.blas.BaseGpuCorrMM,
mode=theano_mode.including("conv_gemm"), mode=theano_mode.excluding("cudnn"),
extra_shapes=extra_shapes): extra_shapes=extra_shapes):
yield t yield t
...@@ -695,7 +695,7 @@ def test_full(): ...@@ -695,7 +695,7 @@ def test_full():
def test_gemm_full(): def test_gemm_full():
for t in _test_full(cuda.blas.BaseGpuCorrMM, for t in _test_full(cuda.blas.BaseGpuCorrMM,
mode=theano_mode.including("conv_gemm")): mode=theano_mode.excluding("cudnn")):
yield t yield t
...@@ -747,7 +747,7 @@ def test_subsample(): ...@@ -747,7 +747,7 @@ def test_subsample():
def test_gemm_subsample(): def test_gemm_subsample():
for t in _test_subsample(cuda.blas.BaseGpuCorrMM, for t in _test_subsample(cuda.blas.BaseGpuCorrMM,
theano_mode.including("conv_gemm")): theano_mode.excluding("cudnn")):
yield t yield t
...@@ -837,7 +837,8 @@ class TestConvWithPadding(object): ...@@ -837,7 +837,8 @@ class TestConvWithPadding(object):
note that in order to make the yield work, we can not subclass from note that in order to make the yield work, we can not subclass from
unittest.TestCase unittest.TestCase
""" """
conv_ops = [] conv_ops = [lambda i, k, border_mode:
theano.sandbox.cuda.blas.GpuCorrMM(border_mode=border_mode)(i, k)]
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
...@@ -855,7 +856,7 @@ class TestConvWithPadding(object): ...@@ -855,7 +856,7 @@ class TestConvWithPadding(object):
assert_raises(ValueError, i, img, kern, assert_raises(ValueError, i, img, kern,
border_mode='not border') border_mode='not border')
def _run_onecase(self, img_shape, kern_shape, padding): def _run_onecase(self, img_shape, kern_shape, padding, op):
npy_img = numpy.random.rand(*img_shape).astype('float32') npy_img = numpy.random.rand(*img_shape).astype('float32')
npy_kern = numpy.random.rand(*kern_shape).astype('float32') npy_kern = numpy.random.rand(*kern_shape).astype('float32')
img = theano._asarray(npy_img, dtype='float32') img = theano._asarray(npy_img, dtype='float32')
...@@ -863,10 +864,9 @@ class TestConvWithPadding(object): ...@@ -863,10 +864,9 @@ class TestConvWithPadding(object):
border_mode = padding border_mode = padding
cpuval = py_conv(npy_img, npy_kern, border_mode, (1, 1)) cpuval = py_conv(npy_img, npy_kern, border_mode, (1, 1))
X = tensor.ftensor4() X = tensor.ftensor4()
for op in self.conv_ops:
Y = op(X, kern, border_mode=border_mode) Y = op(X, kern, border_mode=border_mode)
func = theano.function([X], Y) func = theano.function([X], Y, mode=theano_mode)
gpuval = func(img) gpuval = numpy.asarray(func(img))
assert_allclose(cpuval, gpuval, rtol=1e-5, atol=1e-5) assert_allclose(cpuval, gpuval, rtol=1e-5, atol=1e-5)
def test_numeric_value(self): def test_numeric_value(self):
...@@ -877,7 +877,8 @@ class TestConvWithPadding(object): ...@@ -877,7 +877,8 @@ class TestConvWithPadding(object):
((5, 10, 9, 6), (12, 10, 9, 4), 'valid') ((5, 10, 9, 6), (12, 10, 9, 4), 'valid')
] ]
for img_shape, kern_shape, padding in params: for img_shape, kern_shape, padding in params:
yield (self._run_onecase, img_shape, kern_shape, padding) for op in self.conv_ops:
yield self._run_onecase, img_shape, kern_shape, padding, op
def gemm_directly(bs, ch, nf, rImg1, rImg2, rFlt1, rFlt2, subsx, subsy, def gemm_directly(bs, ch, nf, rImg1, rImg2, rFlt1, rFlt2, subsx, subsy,
...@@ -938,8 +939,7 @@ def test_gemm_directly(): ...@@ -938,8 +939,7 @@ def test_gemm_directly():
def gemm_op(mode, subsample): def gemm_op(mode, subsample):
pad = 'full' if mode == 'full' else (0, 0) return theano.sandbox.cuda.blas.GpuCorrMM(mode, subsample)
return theano.sandbox.cuda.blas.GpuCorrMM('valid', subsample, pad)
def dnn_op(mode, subsample): def dnn_op(mode, subsample):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论