提交 1029066e authored 作者: Frédéric Bastien's avatar Frédéric Bastien 提交者: GitHub

Merge pull request #5957 from notoraptor/op-params-tensor-corr3d

Wrap Op params for theano.tensor.nnet.corr3d.BaseCorr3dMM and subclasses.
...@@ -7,6 +7,8 @@ from six import integer_types ...@@ -7,6 +7,8 @@ from six import integer_types
import theano import theano
from theano import Apply from theano import Apply
from theano import gof from theano import gof
from theano.gof import ParamsType, EnumList
from theano.scalar import int64
from theano.tensor import as_tensor_variable, TensorType from theano.tensor import as_tensor_variable, TensorType
from theano.tensor.nnet.abstract_conv import get_conv_output_shape from theano.tensor.nnet.abstract_conv import get_conv_output_shape
from theano.tensor import blas_headers from theano.tensor import blas_headers
...@@ -19,6 +21,16 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -19,6 +21,16 @@ class BaseCorr3dMM(gof.OpenMPOp):
""" """
Base class for `Corr3dMM`, `Corr3dMM_gradWeights` and Base class for `Corr3dMM`, `Corr3dMM_gradWeights` and
`Corr3dMM_gradInputs`. Cannot be used directly. `Corr3dMM_gradInputs`. Cannot be used directly.
Every sub-class must define internal attribute ``_direction`` out of __init__().
``_direction`` must take one of following values:
- "forward" to correlate bottom with weights and store results in top.
- "backprop weights" to do a valid convolution of bottom with top
(swapping the first two dimensions) and store results in weights.
- "backprop inputs" to do a full convolution of top with weights
(swapping the first two dimensions) and store results in bottom.
Parameters Parameters
---------- ----------
border_mode : {'valid', 'full', 'half'} border_mode : {'valid', 'full', 'half'}
...@@ -32,6 +44,15 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -32,6 +44,15 @@ class BaseCorr3dMM(gof.OpenMPOp):
check_broadcast = False check_broadcast = False
__props__ = ('border_mode', 'subsample', 'filter_dilation') __props__ = ('border_mode', 'subsample', 'filter_dilation')
_direction = None
params_type = ParamsType(direction=EnumList(('DIRECTION_FORWARD', 'forward'), # 0
('DIRECTION_BACKPROP_WEIGHTS', 'backprop weights'), # 1
('DIRECTION_BACKPROP_INPUTS', 'backprop inputs')), # 2
dH=int64, dW=int64, dD=int64,
dilH=int64, dilW=int64, dilD=int64,
padH=int64, padW=int64, padD=int64)
def __init__(self, border_mode="valid", subsample=(1, 1, 1), def __init__(self, border_mode="valid", subsample=(1, 1, 1),
filter_dilation=(1, 1, 1), openmp=None): filter_dilation=(1, 1, 1), openmp=None):
super(BaseCorr3dMM, self).__init__(openmp=openmp) super(BaseCorr3dMM, self).__init__(openmp=openmp)
...@@ -73,11 +94,37 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -73,11 +94,37 @@ class BaseCorr3dMM(gof.OpenMPOp):
else: else:
self.blas_type = '' self.blas_type = ''
if self._direction not in ["forward", "backprop weights", "backprop inputs"]:
raise ValueError("_direction must be one of 'forward', "
"'backprop weights', 'backprop inputs'")
@property @property
def pad(self): def pad(self):
if self.border_mode != 'valid': if self.border_mode == "half":
return (-1, -1, -1)
elif self.border_mode == "full":
return (-2, -2, -2)
elif isinstance(self.border_mode, tuple):
return self.border_mode return self.border_mode
return (0, 0, 0) else:
assert self.border_mode == "valid"
return (0, 0, 0)
# Direction should be converted to real enum value,
# as it is compared to integer later in c_code_helper().
direction = property(lambda self: self.params_type.enum_from_alias(self._direction))
dH = property(lambda self: self.subsample[0])
dW = property(lambda self: self.subsample[1])
dD = property(lambda self: self.subsample[2])
dilH = property(lambda self: self.filter_dilation[0])
dilW = property(lambda self: self.filter_dilation[1])
dilD = property(lambda self: self.filter_dilation[2])
padH = property(lambda self: self.pad[0])
padW = property(lambda self: self.pad[1])
padD = property(lambda self: self.pad[2])
def __str__(self): def __str__(self):
return '%s{%s, %s, %s}' % ( return '%s{%s, %s, %s}' % (
...@@ -123,7 +170,7 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -123,7 +170,7 @@ class BaseCorr3dMM(gof.OpenMPOp):
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 (5, self.openmp, blas_header_version()) return (7, self.openmp, blas_header_version())
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
...@@ -173,7 +220,7 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -173,7 +220,7 @@ class BaseCorr3dMM(gof.OpenMPOp):
final_code += code final_code += code
return final_code % sub return final_code % sub
def c_code_helper(self, bottom, weights, top, direction, sub, def c_code_helper(self, bottom, weights, top, sub,
height=None, width=None, depth=None): height=None, width=None, depth=None):
""" """
This generates the C code for Corr3dMM (direction="forward"), This generates the C code for Corr3dMM (direction="forward"),
...@@ -188,12 +235,6 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -188,12 +235,6 @@ class BaseCorr3dMM(gof.OpenMPOp):
or the gradient of the filters in backprop wrt. weights or the gradient of the filters in backprop wrt. weights
:param top: Variable name of the output images / feature maps in the :param top: Variable name of the output images / feature maps in the
forward pass, or the gradient of the outputs in the backprop passes forward pass, or the gradient of the outputs in the backprop passes
:param direction: "forward" to correlate bottom with weights and store
results in top,
"backprop weights" to do a valid convolution of bottom with top
(swapping the first two dimensions) and store results in weights,
and "backprop inputs" to do a full convolution of top with weights
(swapping the first two dimensions) and store results in bottom.
: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
...@@ -215,72 +256,65 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -215,72 +256,65 @@ class BaseCorr3dMM(gof.OpenMPOp):
If self.border_mode == 'half', a variable giving the depth of the If self.border_mode == 'half', a variable giving the depth of the
filters for direction="backprop weights". Ignored otherwise. filters for direction="backprop weights". Ignored otherwise.
""" """
dH, dW, dD = self.subsample
dilH, dilW, dilD = self.filter_dilation
if self.border_mode == "half":
padH = padW = padD = -1
elif self.border_mode == "full":
padH = padW = padD = -2
elif isinstance(self.border_mode, tuple):
padH, padW, padD = self.border_mode
else:
assert self.border_mode == "valid"
padH = padW = padD = 0
if direction == "forward":
direction = 0
out = top
elif direction == "backprop weights":
direction = 1
out = weights
elif direction == "backprop inputs":
direction = 2
out = bottom
else:
raise ValueError("direction must be one of 'forward', "
"'backprop weights', 'backprop inputs'")
# When subsampling, we cannot unambiguously infer the height and width # When subsampling, we cannot unambiguously infer the height and width
# of bottom and weights from top, so we require them to be given. # of bottom and weights from top, so we require them to be given.
# Similarly, when border_mode="half", we cannot infer the weight size. # Similarly, when border_mode="half", we cannot infer the weight size.
if height: if height:
height = '(*(npy_int64 *)(PyArray_DATA(%s)))' % height height = '(*(npy_int64 *)(PyArray_DATA(%s)))' % height
else: else:
if ((direction != 0) and (dH != 1)) or ((direction == 1) and (padH == -1)): if ((self.direction != 0) and (self.dH != 1)) or ((self.direction == 1) and (self.padH == -1)):
raise ValueError("height must be given for backprop with vertical sampling or border_mode='half'") raise ValueError("height must be given for backprop with vertical sampling or border_mode='half'")
height = '-1' height = '-1'
if width: if width:
width = '(*(npy_int64 *)(PyArray_DATA(%s)))' % width width = '(*(npy_int64 *)(PyArray_DATA(%s)))' % width
else: else:
if ((direction != 0) and (dW != 1)) or ((direction == 1) and (padW == -1)): if ((self.direction != 0) and (self.dW != 1)) or ((self.direction == 1) and (self.padW == -1)):
raise ValueError("width must be given for backprop with horizontal sampling or border_mode='half'") raise ValueError("width must be given for backprop with horizontal sampling or border_mode='half'")
width = '-1' width = '-1'
if depth: if depth:
depth = '(*(npy_int64 *)(PyArray_DATA(%s)))' % depth depth = '(*(npy_int64 *)(PyArray_DATA(%s)))' % depth
else: else:
if ((direction != 0) and (dD != 1)) or ((direction == 1) and (padD == -1)): if ((self.direction != 0) and (self.dD != 1)) or ((self.direction == 1) and (self.padD == -1)):
raise ValueError("depth must be given for backprop with depth sampling or border_mode='half'") raise ValueError("depth must be given for backprop with depth sampling or border_mode='half'")
depth = '-1' depth = '-1'
sub = sub.copy()
sub.update(locals())
return """ return """
// Mandatory args // Mandatory args
int direction = %(direction)s; // forward, bprop weights, bprop inputs int direction = %(params)s->direction; // forward, bprop weights, bprop inputs
// Optional args // Optional args
int dH = %(dH)s; int dH = %(params)s->dH;
int dW = %(dW)s; int dW = %(params)s->dW;
int dD = %(dD)s; int dD = %(params)s->dD;
int dilH = %(dilH)s; int dilH = %(params)s->dilH;
int dilW = %(dilW)s; int dilW = %(params)s->dilW;
int dilD = %(dilD)s; int dilD = %(params)s->dilD;
int padH = %(padH)s; int padH = %(params)s->padH;
int padW = %(padW)s; int padW = %(params)s->padW;
int padD = %(padD)s; int padD = %(params)s->padD;
PyArrayObject * bottom = %(bottom)s; PyArrayObject * bottom = %(bottom)s;
PyArrayObject * weights = %(weights)s; PyArrayObject * weights = %(weights)s;
PyArrayObject * top = %(top)s; PyArrayObject * top = %(top)s;
PyArrayObject * out2 = NULL; PyArrayObject * out2 = NULL;
PyArrayObject **out = NULL;
switch(%(params)s->direction) {
case DIRECTION_FORWARD:
out = &%(top)s;
break;
case DIRECTION_BACKPROP_WEIGHTS:
out = &%(weights)s;
break;
case DIRECTION_BACKPROP_INPUTS:
out = &%(bottom)s;
break;
default:
PyErr_SetString(PyExc_ValueError, "CPU Corr3dMM: Invalid direction.");
{%(fail)s}
break;
}
// Obtain or infer kernel width, height and depth // Obtain or infer kernel width, height and depth
// (we need to know it early to be able to handle auto-padding) // (we need to know it early to be able to handle auto-padding)
...@@ -449,16 +483,16 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -449,16 +483,16 @@ class BaseCorr3dMM(gof.OpenMPOp):
// Prepare output array // Prepare output array
int typenum; int typenum;
if ( !(%(out)s if ( !(*out
&& PyArray_NDIM(%(out)s)==4 && PyArray_NDIM(*out)==4
&& PyArray_IS_C_CONTIGUOUS(%(out)s) && PyArray_IS_C_CONTIGUOUS(*out)
&& PyArray_DIMS(%(out)s)[0]==out_dim[0] && PyArray_DIMS(*out)[0]==out_dim[0]
&& PyArray_DIMS(%(out)s)[1]==out_dim[1] && PyArray_DIMS(*out)[1]==out_dim[1]
&& PyArray_DIMS(%(out)s)[2]==out_dim[2] && PyArray_DIMS(*out)[2]==out_dim[2]
&& PyArray_DIMS(%(out)s)[3]==out_dim[3] && PyArray_DIMS(*out)[3]==out_dim[3]
&& PyArray_DIMS(%(out)s)[4]==out_dim[4])) && PyArray_DIMS(*out)[4]==out_dim[4]))
{ {
Py_XDECREF(%(out)s); Py_XDECREF(*out);
if (direction != 1) { if (direction != 1) {
typenum = PyArray_TYPE(weights); typenum = PyArray_TYPE(weights);
} }
...@@ -466,11 +500,11 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -466,11 +500,11 @@ class BaseCorr3dMM(gof.OpenMPOp):
typenum = PyArray_TYPE(bottom); typenum = PyArray_TYPE(bottom);
} }
//Change to PyArray_ZEROS which is faster than PyArray_EMPTY. //Change to PyArray_ZEROS which is faster than PyArray_EMPTY.
%(out)s = (PyArrayObject*)PyArray_ZEROS(5, *out = (PyArrayObject*)PyArray_ZEROS(5,
out_dim, out_dim,
typenum, typenum,
0); 0);
if (NULL == %(out)s) if (NULL == *out)
{ {
PyErr_Format(PyExc_RuntimeError, PyErr_Format(PyExc_RuntimeError,
"BaseCorr3dMM: Failed to allocate output of %%lld x %%lld x %%lld x %%lld x %%lld", "BaseCorr3dMM: Failed to allocate output of %%lld x %%lld x %%lld x %%lld x %%lld",
...@@ -486,9 +520,11 @@ class BaseCorr3dMM(gof.OpenMPOp): ...@@ -486,9 +520,11 @@ class BaseCorr3dMM(gof.OpenMPOp):
if (out2==NULL){ if (out2==NULL){
%(fail)s %(fail)s
} }
assert (out2 == %(out)s); assert (out2 == *out);
""" % sub """ % dict(bottom=bottom, weights=weights, top=top,
height=height, width=width, depth=depth,
fail=sub['fail'], params=sub['params'])
class Corr3dMM(BaseCorr3dMM): class Corr3dMM(BaseCorr3dMM):
...@@ -519,6 +555,8 @@ class Corr3dMM(BaseCorr3dMM): ...@@ -519,6 +555,8 @@ class Corr3dMM(BaseCorr3dMM):
""" """
_direction = "forward"
def make_node(self, img, kern): def make_node(self, img, kern):
img = as_tensor_variable(img) img = as_tensor_variable(img)
kern = as_tensor_variable(kern) kern = as_tensor_variable(kern)
...@@ -547,8 +585,7 @@ class Corr3dMM(BaseCorr3dMM): ...@@ -547,8 +585,7 @@ class Corr3dMM(BaseCorr3dMM):
def c_code(self, node, nodename, inp, out_, sub): def c_code(self, node, nodename, inp, out_, sub):
bottom, weights = inp bottom, weights = inp
top, = out_ top, = out_
direction = "forward" return super(Corr3dMM, self).c_code_helper(bottom, weights, top, sub)
return super(Corr3dMM, self).c_code_helper(bottom, weights, top, direction, sub)
def grad(self, inp, grads): def grad(self, inp, grads):
bottom, weights = inp bottom, weights = inp
...@@ -576,6 +613,8 @@ class Corr3dMM_gradWeights(BaseCorr3dMM): ...@@ -576,6 +613,8 @@ class Corr3dMM_gradWeights(BaseCorr3dMM):
""" """
_direction = "backprop weights"
def make_node(self, img, topgrad, shape=None): def make_node(self, img, topgrad, shape=None):
img = as_tensor_variable(img) img = as_tensor_variable(img)
topgrad = as_tensor_variable(topgrad) topgrad = as_tensor_variable(topgrad)
...@@ -643,9 +682,8 @@ class Corr3dMM_gradWeights(BaseCorr3dMM): ...@@ -643,9 +682,8 @@ class Corr3dMM_gradWeights(BaseCorr3dMM):
bottom, top = inp[:2] bottom, top = inp[:2]
height, width, depth = inp[2:] or (None, None, None) height, width, depth = inp[2:] or (None, None, None)
weights, = out_ weights, = out_
direction = "backprop weights"
return super(Corr3dMM_gradWeights, return super(Corr3dMM_gradWeights,
self).c_code_helper(bottom, weights, top, direction, self).c_code_helper(bottom, weights, top,
sub, height, width, depth) sub, height, width, depth)
def grad(self, inp, grads): def grad(self, inp, grads):
...@@ -681,6 +719,8 @@ class Corr3dMM_gradInputs(BaseCorr3dMM): ...@@ -681,6 +719,8 @@ class Corr3dMM_gradInputs(BaseCorr3dMM):
""" """
_direction = "backprop inputs"
def make_node(self, kern, topgrad, shape=None): def make_node(self, kern, topgrad, shape=None):
kern = as_tensor_variable(kern) kern = as_tensor_variable(kern)
topgrad = as_tensor_variable(topgrad) topgrad = as_tensor_variable(topgrad)
...@@ -758,9 +798,8 @@ class Corr3dMM_gradInputs(BaseCorr3dMM): ...@@ -758,9 +798,8 @@ class Corr3dMM_gradInputs(BaseCorr3dMM):
weights, top = inp[:2] weights, top = inp[:2]
height, width, depth = inp[2:] or (None, None, None) height, width, depth = inp[2:] or (None, None, None)
bottom, = out_ bottom, = out_
direction = "backprop inputs"
return super(Corr3dMM_gradInputs, return super(Corr3dMM_gradInputs,
self).c_code_helper(bottom, weights, top, direction, sub, self).c_code_helper(bottom, weights, top, sub,
height, width, depth) height, width, depth)
def grad(self, inp, grads): def grad(self, inp, grads):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论