提交 9db32cee authored 作者: Gijs van Tulder's avatar Gijs van Tulder

Replace Conv3D references in tests with Corr3dMM.

上级 146ef971
...@@ -832,7 +832,6 @@ def test_conv3d_fwd(): ...@@ -832,7 +832,6 @@ def test_conv3d_fwd():
inputs = theano.shared(inputs_val) inputs = theano.shared(inputs_val)
filters = theano.shared(filters_val) filters = theano.shared(filters_val)
bias = theano.shared(numpy.zeros(filters_shape[0]).astype('float32'))
# Compile a theano function for the cuDNN implementation # Compile a theano function for the cuDNN implementation
conv = dnn.dnn_conv3d(img=inputs, kerns=filters, conv = dnn.dnn_conv3d(img=inputs, kerns=filters,
...@@ -847,33 +846,11 @@ def test_conv3d_fwd(): ...@@ -847,33 +846,11 @@ def test_conv3d_fwd():
else: else:
flipped_filters = filters flipped_filters = filters
# If border mode is anything but 'valid', the reference implementation
# should operate on padded inputs
if border_mode == 'valid':
padded_inputs = inputs
else:
if border_mode == 'full':
pad_per_dim = [filters_shape[i] - 1 for i in range(2, 5)]
elif border_mode == 'half':
pad_per_dim = [filters_shape[i] // 2 for i in range(2, 5)]
else:
if isinstance(border_mode, int):
pad_per_dim = [border_mode] * 3
else:
pad_per_dim = border_mode
pad_before_after = ([(0, 0), (0, 0)] +
[(p, p) for p in pad_per_dim])
padded_inputs_val = numpy.pad(inputs_val, pad_before_after,
'constant')
padded_inputs = theano.shared(padded_inputs_val)
# Compile a theano function for the reference implementation # Compile a theano function for the reference implementation
conv_ref = theano.tensor.nnet.conv3D( conv_ref = theano.tensor.nnet.corr3d.Corr3dMM(border_mode=border_mode,
V=padded_inputs.dimshuffle(0, 2, 3, 4, 1), subsample=subsample
W=flipped_filters.dimshuffle(0, 2, 3, 4, 1), )(inputs, flipped_filters)
b=bias, d=subsample) f_ref = theano.function([], conv_ref, mode="FAST_RUN")
f_ref = theano.function([], conv_ref.dimshuffle(0, 4, 1, 2, 3), mode="FAST_RUN")
# Compare the results of the two implementations # Compare the results of the two implementations
res_ref = f_ref() res_ref = f_ref()
...@@ -899,7 +876,6 @@ def test_conv3d_bwd(): ...@@ -899,7 +876,6 @@ def test_conv3d_bwd():
inputs = theano.shared(inputs_val) inputs = theano.shared(inputs_val)
filters = theano.shared(filters_val) filters = theano.shared(filters_val)
bias = theano.shared(numpy.zeros(filters_shape[0]).astype('float32'))
# Compile a theano function for the cuDNN implementation # Compile a theano function for the cuDNN implementation
conv = dnn.dnn_conv3d(img=inputs, kerns=filters, conv = dnn.dnn_conv3d(img=inputs, kerns=filters,
...@@ -917,47 +893,13 @@ def test_conv3d_bwd(): ...@@ -917,47 +893,13 @@ def test_conv3d_bwd():
else: else:
flipped_filters = filters flipped_filters = filters
# If border mode is anything but 'valid', the reference implementation
# should operate on padded inputs
if border_mode == 'valid':
padded_inputs = inputs
else:
if border_mode == 'full':
pad_per_dim = [filters_shape[i] - 1 for i in range(2, 5)]
elif border_mode == 'half':
pad_per_dim = [filters_shape[i] // 2 for i in range(2, 5)]
else:
if isinstance(border_mode, int):
pad_per_dim = [border_mode] * 3
else:
pad_per_dim = border_mode
pad_before_after = ([(0, 0), (0, 0)] +
[(p, p) for p in pad_per_dim])
padded_inputs_val = numpy.pad(inputs_val, pad_before_after,
'constant')
padded_inputs = theano.shared(padded_inputs_val)
# Compile a theano function for the reference implementation # Compile a theano function for the reference implementation
conv_ref = theano.tensor.nnet.conv3D( conv_ref = theano.tensor.nnet.corr3d.Corr3dMM(border_mode=border_mode,
V=padded_inputs.dimshuffle(0, 2, 3, 4, 1), subsample=subsample
W=flipped_filters.dimshuffle(0, 2, 3, 4, 1), )(inputs, flipped_filters)
b=bias, d=subsample) (grad_i_ref,
(grad_padded_i_ref,
grad_w_ref) = theano.tensor.grad(conv_ref.sum(), grad_w_ref) = theano.tensor.grad(conv_ref.sum(),
[padded_inputs, filters]) [inputs, filters])
# Recover grad_i_ref from grad_padded_i_ref
if border_mode == 'valid':
grad_i_ref = grad_padded_i_ref
else:
shp = grad_padded_i_ref.shape
grad_i_ref = grad_padded_i_ref[
:, :,
pad_per_dim[0]:shp[2] - pad_per_dim[0],
pad_per_dim[1]:shp[3] - pad_per_dim[1],
pad_per_dim[2]:shp[4] - pad_per_dim[2]]
f_ref = theano.function([], [grad_i_ref, grad_w_ref], mode="FAST_RUN") f_ref = theano.function([], [grad_i_ref, grad_w_ref], mode="FAST_RUN")
# Compare the results of the two implementations # Compare the results of the two implementations
......
...@@ -1551,7 +1551,6 @@ def test_conv3d_fwd(): ...@@ -1551,7 +1551,6 @@ def test_conv3d_fwd():
inputs = shared(inputs_val) inputs = shared(inputs_val)
filters = shared(filters_val) filters = shared(filters_val)
bias = shared(numpy.zeros(filters_shape[0]).astype('float32'))
# Compile a theano function for the cuDNN implementation # Compile a theano function for the cuDNN implementation
conv = dnn.dnn_conv3d(img=inputs, kerns=filters, conv = dnn.dnn_conv3d(img=inputs, kerns=filters,
...@@ -1566,33 +1565,11 @@ def test_conv3d_fwd(): ...@@ -1566,33 +1565,11 @@ def test_conv3d_fwd():
else: else:
flipped_filters = filters flipped_filters = filters
# If border mode is anything but 'valid', the reference implementation
# should operate on padded inputs
if border_mode == 'valid':
padded_inputs = inputs
else:
if border_mode == 'full':
pad_per_dim = [filters_shape[i] - 1 for i in range(2, 5)]
elif border_mode == 'half':
pad_per_dim = [filters_shape[i] // 2 for i in range(2, 5)]
else:
if isinstance(border_mode, int):
pad_per_dim = [border_mode] * 3
else:
pad_per_dim = border_mode
pad_before_after = ([(0, 0), (0, 0)] +
[(p, p) for p in pad_per_dim])
padded_inputs_val = numpy.pad(inputs_val, pad_before_after,
'constant')
padded_inputs = shared(padded_inputs_val)
# Compile a theano function for the reference implementation # Compile a theano function for the reference implementation
conv_ref = theano.tensor.nnet.conv3D( conv_ref = theano.tensor.nnet.corr3d.Corr3dMM(border_mode=border_mode,
V=padded_inputs.dimshuffle(0, 2, 3, 4, 1), subsample=subsample
W=flipped_filters.dimshuffle(0, 2, 3, 4, 1), )(inputs, flipped_filters)
b=bias, d=subsample) f_ref = theano.function([], conv_ref, mode="FAST_RUN")
f_ref = theano.function([], conv_ref.dimshuffle(0, 4, 1, 2, 3), mode="FAST_RUN")
# Compare the results of the two implementations # Compare the results of the two implementations
res_ref = f_ref() res_ref = f_ref()
...@@ -1618,7 +1595,6 @@ def test_conv3d_bwd(): ...@@ -1618,7 +1595,6 @@ def test_conv3d_bwd():
inputs = shared(inputs_val) inputs = shared(inputs_val)
filters = shared(filters_val) filters = shared(filters_val)
bias = shared(numpy.zeros(filters_shape[0]).astype('float32'))
# Compile a theano function for the cuDNN implementation # Compile a theano function for the cuDNN implementation
conv = dnn.dnn_conv3d(img=inputs, kerns=filters, conv = dnn.dnn_conv3d(img=inputs, kerns=filters,
...@@ -1636,46 +1612,13 @@ def test_conv3d_bwd(): ...@@ -1636,46 +1612,13 @@ def test_conv3d_bwd():
else: else:
flipped_filters = filters flipped_filters = filters
# If border mode is anything but 'valid', the reference implementation
# should operate on padded inputs
if border_mode == 'valid':
padded_inputs = inputs
else:
if border_mode == 'full':
pad_per_dim = [filters_shape[i] - 1 for i in range(2, 5)]
elif border_mode == 'half':
pad_per_dim = [filters_shape[i] // 2 for i in range(2, 5)]
else:
if isinstance(border_mode, int):
pad_per_dim = [border_mode] * 3
else:
pad_per_dim = border_mode
pad_before_after = ([(0, 0), (0, 0)] +
[(p, p) for p in pad_per_dim])
padded_inputs_val = numpy.pad(inputs_val, pad_before_after,
'constant')
padded_inputs = shared(padded_inputs_val)
# Compile a theano function for the reference implementation # Compile a theano function for the reference implementation
conv_ref = theano.tensor.nnet.conv3D( conv_ref = theano.tensor.nnet.corr3d.Corr3dMM(border_mode=border_mode,
V=padded_inputs.dimshuffle(0, 2, 3, 4, 1), subsample=subsample
W=flipped_filters.dimshuffle(0, 2, 3, 4, 1), )(inputs, flipped_filters)
b=bias, d=subsample) (grad_i_ref,
(grad_padded_i_ref,
grad_w_ref) = theano.tensor.grad(conv_ref.sum(), grad_w_ref) = theano.tensor.grad(conv_ref.sum(),
[padded_inputs, filters]) [inputs, filters])
# Recover grad_i_ref from grad_padded_i_ref
if border_mode == 'valid':
grad_i_ref = grad_padded_i_ref
else:
shp = grad_padded_i_ref.shape
grad_i_ref = grad_padded_i_ref[
:, :,
pad_per_dim[0]:shp[2] - pad_per_dim[0],
pad_per_dim[1]:shp[3] - pad_per_dim[1],
pad_per_dim[2]:shp[4] - pad_per_dim[2]]
f_ref = theano.function([], [grad_i_ref, grad_w_ref], mode="FAST_RUN") f_ref = theano.function([], [grad_i_ref, grad_w_ref], mode="FAST_RUN")
......
from __future__ import absolute_import, print_function, division from __future__ import absolute_import, print_function, division
import unittest import unittest
import numpy import numpy
from six.moves import xrange
try:
from scipy import ndimage
except ImportError:
ndimage = None
import theano import theano
from theano.tests import unittest_tools as utt from theano.tests import unittest_tools as utt
# Skip tests if cuda_ndarray is not available. # Skip tests if cuda_ndarray is not available.
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
from theano.tensor.nnet.corr3d import Corr3dMM, Corr3dMM_gradWeights, Corr3dMM_gradInputs
from theano.sandbox.cuda import float32_shared_constructor as shared from theano.sandbox.cuda import float32_shared_constructor as shared
from theano.sandbox.cuda.blas import ( from theano.sandbox.cuda.blas import (
GpuCorr3dMM, GpuCorr3dMM_gradWeights, GpuCorr3dMM_gradInputs) GpuCorr3dMM, GpuCorr3dMM_gradWeights, GpuCorr3dMM_gradInputs)
...@@ -29,72 +25,6 @@ else: ...@@ -29,72 +25,6 @@ else:
# python reference implementation of a 3D convolution # python reference implementation of a 3D convolution
# see also: theano.tensor.nnet.tests.test_conv3d2d # see also: theano.tensor.nnet.tests.test_conv3d2d
# expects: (batch, 0, channels, 1, 2) # expects: (batch, 0, channels, 1, 2)
def pyconv3d(signals, filters, border_mode='valid', dilation=(1, 1, 1)):
Ns, Ts, C, Hs, Ws = signals.shape
Nf, Tf, C, Hf, Wf = filters.shape
Tdil, Hdil, Wdil = dilation
Tfdil = (Tf - 1) * Tdil + 1
Hfdil = (Hf - 1) * Hdil + 1
Wfdil = (Wf - 1) * Wdil + 1
# if border_mode is not 'valid', the signals need zero-padding
if border_mode == 'full':
Tpad = Tfdil - 1
Hpad = Hfdil - 1
Wpad = Wfdil - 1
elif border_mode == 'half':
Tpad = Tfdil // 2
Hpad = Hfdil // 2
Wpad = Wfdil // 2
elif isinstance(border_mode, tuple):
Tpad, Hpad, Wpad = map(int, border_mode)
else:
Tpad = 0
Hpad = 0
Wpad = 0
if Tpad > 0 or Hpad > 0 or Wpad > 0:
# zero-pad signals
signals_padded = numpy.zeros((Ns, Ts + 2 * Tpad, C,
Hs + 2 * Hpad, Ws + 2 * Wpad), 'float32')
signals_padded[:, Tpad:(Ts + Tpad), :, Hpad:(Hs + Hpad),
Wpad:(Ws + Wpad)] = signals
Ns, Ts, C, Hs, Ws = signals_padded.shape
signals = signals_padded
Tfdil2 = Tfdil // 2
Hfdil2 = Hfdil // 2
Wfdil2 = Wfdil // 2
dilated_filters = numpy.zeros((Nf, Tfdil, C, Hfdil, Wfdil), dtype=filters.dtype)
dilated_filters[:, ::Tdil, :, ::Hdil, ::Wdil] = filters
# perform valid convolution on the padded signals
rval = numpy.zeros((Ns, Ts - Tfdil + 1, Nf, Hs - Hfdil + 1, Ws - Wfdil + 1))
for ns in xrange(Ns):
for nf in xrange(Nf):
for c in xrange(C):
s_i = signals[ns, :, c, :, :]
f_i = dilated_filters[nf, :, c, :, :]
r_i = rval[ns, :, nf, :, :]
# scipy.signal.convolve performs valid convolution,
# but is quite slow. scipy.ndimage.convolve is faster
# only supports 'same' convolution.
# origin must be -1 for even filters, 0 for odd filters
o_i = ndimage.convolve(s_i, f_i, mode='constant', cval=1,
origin=(f_i.shape[0] % 2 - 1,
f_i.shape[1] % 2 - 1,
f_i.shape[2] % 2 - 1))
# crop to get the result of 'valid' convolution
o_i = o_i[Tfdil2:(r_i.shape[0] + Tfdil2),
Hfdil2:(r_i.shape[1] + Hfdil2),
Wfdil2:(r_i.shape[2] + Wfdil2)]
# the result should be equal to 'valid' convolution
# utt.assert_allclose(o_i, signal.convolve(s_i, f_i, mode='valid'))
r_i += o_i
return rval
class TestCorr3DMM(unittest.TestCase): class TestCorr3DMM(unittest.TestCase):
def run_conv_valid(self, inputs_shape, filters_shape, def run_conv_valid(self, inputs_shape, filters_shape,
...@@ -107,26 +37,14 @@ class TestCorr3DMM(unittest.TestCase): ...@@ -107,26 +37,14 @@ class TestCorr3DMM(unittest.TestCase):
inputs = shared(inputs_val) inputs = shared(inputs_val)
filters = shared(filters_val) filters = shared(filters_val)
bias = shared(numpy.zeros(filters_shape[0]).astype('float32'))
if filter_dilation == (1, 1, 1) and border_mode in ('valid', (0, 0, 0)): conv_ref = Corr3dMM(border_mode=border_mode,
conv_ref = theano.tensor.nnet.conv3D(V=inputs, W=filters, filter_dilation=filter_dilation,
b=bias, d=subsample) subsample=subsample)(
f_ref = theano.function([], conv_ref) inputs.dimshuffle(0, 4, 1, 2, 3),
res_ref = f_ref() filters.dimshuffle(0, 4, 1, 2, 3))
elif subsample == (1, 1, 1): conv_ref = conv_ref.dimshuffle(0, 2, 3, 4, 1)
if ndimage is None: f_ref = theano.function([], conv_ref, mode='FAST_RUN')
raise SkipTest('This test needs SciPy.')
# input = b012c
# pyconv3d wants = b0c12 = (0, 1, 4, 2, 3)
# pyconv3d outputs = b0c12 = (0, 1, 3, 4, 2)
res_ref = pyconv3d(signals=inputs_val.transpose(0, 1, 4, 2, 3),
filters=filters_val.transpose(0, 1, 4, 2, 3)[:, ::-1, :, ::-1, ::-1],
dilation=filter_dilation,
border_mode=border_mode).transpose(0, 1, 3, 4, 2)
else:
raise SkipTest('No reference implementation that combines '
'border_mode and subsampling.')
conv = GpuCorr3dMM(border_mode=border_mode, conv = GpuCorr3dMM(border_mode=border_mode,
filter_dilation=filter_dilation, filter_dilation=filter_dilation,
...@@ -134,9 +52,9 @@ class TestCorr3DMM(unittest.TestCase): ...@@ -134,9 +52,9 @@ class TestCorr3DMM(unittest.TestCase):
inputs.dimshuffle(0, 4, 1, 2, 3), inputs.dimshuffle(0, 4, 1, 2, 3),
filters.dimshuffle(0, 4, 1, 2, 3)) filters.dimshuffle(0, 4, 1, 2, 3))
conv = conv.dimshuffle(0, 2, 3, 4, 1) conv = conv.dimshuffle(0, 2, 3, 4, 1)
f = theano.function([], conv, mode=mode_with_gpu) f = theano.function([], conv, mode=mode_with_gpu)
res_ref = f_ref()
res = f() res = f()
utt.assert_allclose(res_ref, res) utt.assert_allclose(res_ref, res)
...@@ -220,19 +138,20 @@ class TestCorr3DMM(unittest.TestCase): ...@@ -220,19 +138,20 @@ class TestCorr3DMM(unittest.TestCase):
inputs = shared(inputs_val) inputs = shared(inputs_val)
dCdH = shared(dCdH_val) dCdH = shared(dCdH_val)
conv = theano.tensor.nnet.convGrad3D(V=inputs, dCdH=dCdH,
WShape=filters_shape,
d=subsample)
img = gpu_contiguous(inputs.dimshuffle(0, 4, 1, 2, 3)) img = gpu_contiguous(inputs.dimshuffle(0, 4, 1, 2, 3))
topgrad = gpu_contiguous(dCdH.dimshuffle(0, 4, 1, 2, 3)) topgrad = gpu_contiguous(dCdH.dimshuffle(0, 4, 1, 2, 3))
if (subsample == (1, 1, 1)): if (subsample == (1, 1, 1)):
conv_gemm = GpuCorr3dMM_gradWeights(subsample=subsample)(img, conv_ref = Corr3dMM_gradWeights(subsample=subsample)(
topgrad) img, topgrad)
conv_gemm = GpuCorr3dMM_gradWeights(subsample=subsample)(
img, topgrad)
else: else:
conv_ref = GpuCorr3dMM_gradWeights(subsample=subsample)(
img, topgrad, shape=filters_shape[1:4])
conv_gemm = GpuCorr3dMM_gradWeights(subsample=subsample)( conv_gemm = GpuCorr3dMM_gradWeights(subsample=subsample)(
img, topgrad, shape=filters_shape[1:4]) img, topgrad, shape=filters_shape[1:4])
conv_gemm = conv_gemm.dimshuffle(0, 2, 3, 4, 1)
f_ref = theano.function([], conv) f_ref = theano.function([], conv_ref)
f = theano.function([], conv_gemm, mode=mode_with_gpu) f = theano.function([], conv_gemm, mode=mode_with_gpu)
res_ref = f_ref() res_ref = f_ref()
...@@ -265,31 +184,31 @@ class TestCorr3DMM(unittest.TestCase): ...@@ -265,31 +184,31 @@ class TestCorr3DMM(unittest.TestCase):
inputs = shared(inputs_val) inputs = shared(inputs_val)
filters = shared(filters_val) filters = shared(filters_val)
bias = shared(numpy.zeros(filters_shape[4]).astype('float32'))
conv = theano.tensor.nnet.convTransp3D(W=filters,
b=bias,
d=subsample,
H=inputs)
f_ref = theano.function([], conv)
res_ref = f_ref()
# Get bottom shape using convTransp3D bottom_height = (inputs_shape[1] - 1) * subsample[0] + filters_shape[1]
bottom_shape = res_ref.shape bottom_width = (inputs_shape[2] - 1) * subsample[1] + filters_shape[2]
bottom_val = numpy.random.random(bottom_shape).astype('float32') bottom_depth = (inputs_shape[3] - 1) * subsample[2] + filters_shape[3]
bottom = shared(bottom_val) bottom_shape = theano.shared(numpy.array([bottom_height, bottom_width, bottom_depth]))
weight = gpu_contiguous(filters.dimshuffle(0, 4, 1, 2, 3)) weight = gpu_contiguous(filters.dimshuffle(0, 4, 1, 2, 3))
top = gpu_contiguous(inputs.dimshuffle(0, 4, 1, 2, 3)) top = gpu_contiguous(inputs.dimshuffle(0, 4, 1, 2, 3))
if (subsample == (1, 1, 1)): if (subsample == (1, 1, 1)):
conv_ref = Corr3dMM_gradInputs(subsample=subsample)(
kern=weight, topgrad=top)
conv_gemm = GpuCorr3dMM_gradInputs(subsample=subsample)( conv_gemm = GpuCorr3dMM_gradInputs(subsample=subsample)(
kern=weight, topgrad=top) kern=weight, topgrad=top)
else: else:
conv_ref = Corr3dMM_gradInputs(subsample=subsample)(
kern=weight, topgrad=top,
shape=bottom_shape)
conv_gemm = GpuCorr3dMM_gradInputs(subsample=subsample)( conv_gemm = GpuCorr3dMM_gradInputs(subsample=subsample)(
kern=weight, topgrad=top, kern=weight, topgrad=top,
shape=bottom.shape[1:4]) shape=bottom_shape)
conv_gemm = conv_gemm.dimshuffle(0, 2, 3, 4, 1)
f_ref = theano.function([], conv_ref)
f = theano.function([], conv_gemm, mode=mode_with_gpu) f = theano.function([], conv_gemm, mode=mode_with_gpu)
res_ref = f_ref()
res = f() res = f()
utt.assert_allclose(res_ref, res) utt.assert_allclose(res_ref, res)
......
...@@ -14,7 +14,6 @@ from theano.compat import izip ...@@ -14,7 +14,6 @@ from theano.compat import izip
from theano.tests import unittest_tools as utt from theano.tests import unittest_tools as utt
from theano import gradient from theano import gradient
from theano.tensor.nnet.Conv3D import conv3D
from theano import config from theano import config
from theano.gof.null_type import NullType from theano.gof.null_type import NullType
...@@ -187,16 +186,19 @@ class test_grad(unittest.TestCase): ...@@ -187,16 +186,19 @@ class test_grad(unittest.TestCase):
def test_undefined_grad_grad(self): def test_undefined_grad_grad(self):
# tests that undefined grads are caught in the grad method # tests that undefined grads are caught in the grad method
V = theano.tensor.TensorType(dtype=config.floatX, class DummyOp(gof.Op):
broadcastable=(False, False, False, False, False))() __props__ = ()
W = theano.tensor.TensorType(dtype=config.floatX,
broadcastable=(False, False, False, False, False))()
b = theano.tensor.vector()
d = theano.tensor.ivector()
Z = conv3D(V, W, b, d) def make_node(self, x):
return gof.Apply(self, [x], [x.type()])
self.assertRaises(TypeError, theano.gradient.grad, Z.sum(), d) def grad(self, inputs, output_grads):
return [theano.gradient.grad_undefined(self, 0, inputs[0])]
a = theano.tensor.scalar()
b = DummyOp()(a)
self.assertRaises(TypeError, theano.gradient.grad, b, a)
def test_grad_name(self): def test_grad_name(self):
A = theano.tensor.matrix('A') A = theano.tensor.matrix('A')
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论