提交 697d1e78 authored 作者: Olivier Delalleau's avatar Olivier Delalleau

Merge pull request #287 from nouiz/shape_opt_disabled

Fixes for shape opt
...@@ -162,8 +162,9 @@ Others: ...@@ -162,8 +162,9 @@ Others:
* Don't request to load the GPU module by default in scan module. (Razvan) * Don't request to load the GPU module by default in scan module. (Razvan)
* Fixed some import problems. * Fixed some import problems.
* Filtering update. (James) * Filtering update. (James)
* The buidbot now raises optimization errors instead of just printing a warning. (Frederic)
* On Windows, the default compiledir changed to be local to the computer/user and not transferred with roaming profile. (Sebastian Urban) * On Windows, the default compiledir changed to be local to the computer/user and not transferred with roaming profile. (Sebastian Urban)
* New theano flag "on_shape_error". Default to "warn" (same as previous behavior): it print a warning when an error occur when infering the shape of some apply node. The other accepted value is "raise" to raise an error when this happen.
* The buidbot now raises optimization/shape errors instead of just printing a warning. (Frederic)
Reviewers (alphabetical order): Reviewers (alphabetical order):
* David, Frederic, Ian, James, Olivier, Razvan * David, Frederic, Ian, James, Olivier, Razvan
...@@ -162,8 +162,8 @@ Others: ...@@ -162,8 +162,8 @@ Others:
* Don't request to load the GPU module by default in scan module. (Razvan) * Don't request to load the GPU module by default in scan module. (Razvan)
* Fixed some import problems. * Fixed some import problems.
* Filtering update. (James) * Filtering update. (James)
* The buidbot now raises optimization errors instead of just printing a warning. (Frederic)
* On Windows, the default compiledir changed to be local to the computer/user and not transferred with roaming profile. (Sebastian Urban) * On Windows, the default compiledir changed to be local to the computer/user and not transferred with roaming profile. (Sebastian Urban)
* The buidbot now raises optimization/shape errors instead of just printing a warning. (Frederic)
Reviewers (alphabetical order): Reviewers (alphabetical order):
* David, Frederic, Ian, James, Olivier, Razvan * David, Frederic, Ian, James, Olivier, Razvan
...@@ -243,6 +243,16 @@ import theano and print the config variable, as in: ...@@ -243,6 +243,16 @@ import theano and print the config variable, as in:
the user and skip this optimization ('warn'), or raise the exception the user and skip this optimization ('warn'), or raise the exception
('raise'). ('raise').
.. attribute:: on_shape_error
String value: 'warn' or 'raise'
Default: 'warn'
When an exception is raised when infering the shape of some apply
node, either warn the user and use a default value ('warn'), or
raise the exception ('raise').
.. attribute:: config.warn.ignore_bug_before .. attribute:: config.warn.ignore_bug_before
String value: 'None', 'all', '0.3', '0.4', '0.4.1', '0.5' String value: 'None', 'all', '0.3', '0.4', '0.4.1', '0.5'
......
...@@ -1333,7 +1333,8 @@ class _Linker(gof.link.LocalLinker): ...@@ -1333,7 +1333,8 @@ class _Linker(gof.link.LocalLinker):
if (storage_map[r][0] is None): if (storage_map[r][0] is None):
raise Exception('Missing input', r) raise Exception('Missing input', r)
if not r.type.is_valid_value(storage_map[r][0]): if not r.type.is_valid_value(storage_map[r][0]):
raise InvalidValueError(r, storage_map[r][0]) raise InvalidValueError(r, storage_map[r][0],
hint="Graph Input '%s' is missing" % str(r))
r_vals[r] = storage_map[r][0] r_vals[r] = storage_map[r][0]
storage_map[r][0] = None storage_map[r][0] = None
r_vals_initialized.append(r) r_vals_initialized.append(r)
......
...@@ -25,9 +25,10 @@ echo "Number of elements in the compiledir:" ...@@ -25,9 +25,10 @@ echo "Number of elements in the compiledir:"
ls ${COMPILEDIR}|wc -l ls ${COMPILEDIR}|wc -l
# We don't want warnings in the buildbod for errors already fixed. # We don't want warnings in the buildbod for errors already fixed.
FLAGS=${THEANO_FLAGS},warn.argmax_pushdown_bug=False,warn.gpusum_01_011_0111_bug=False,warn.sum_sum_bug=False,warn.sum_div_dimshuffle_bug=False,$FLAGS FLAGS=${THEANO_FLAGS},warn.argmax_pushdown_bug=False,warn.gpusum_01_011_0111_bug=False,warn.sum_sum_bug=False,warn.sum_div_dimshuffle_bug=False,$FLAGS
# We want to see correctly optimization errors, so make make them raise an # We want to see correctly optimization/shape errors, so make make them raise an
# error. # error.
FLAGS=on_opt_error=raise,$FLAGS FLAGS=on_opt_error=raise,$FLAGS
FLAGS=on_shape_error=raise,$FLAGS
# Ignore user device and floatX config, because: # Ignore user device and floatX config, because:
# 1. Tests are intended to be run with device=cpu. # 1. Tests are intended to be run with device=cpu.
# 2. We explicitly add 'floatX=float32' in one run of the test suite below, # 2. We explicitly add 'floatX=float32' in one run of the test suite below,
......
...@@ -166,7 +166,13 @@ class Scan(PureOp): ...@@ -166,7 +166,13 @@ class Scan(PureOp):
'could happen if the inner graph of scan results in ' 'could happen if the inner graph of scan results in '
'an upcast or downcast. Please make sure that you use' 'an upcast or downcast. Please make sure that you use'
'dtypes consistently') 'dtypes consistently')
# TODO make the assert exact
# TODO assert the type(dtype, nbdim of self.inputs and inputs correspond)
#assert len(inputs) >= len(self.inputs)
# if self.info['as_while']:
# assert len(inputs) == len(self.inputs) + 2 + self.info["n_nit_sot"]
# else:
# assert len(inputs) == len(self.inputs) + 1 + self.info["n_nit_sot"]
# Flags that indicate which inputs are vectors # Flags that indicate which inputs are vectors
self.vector_seqs = [seq.ndim == 1 for seq in self.vector_seqs = [seq.ndim == 1 for seq in
...@@ -903,7 +909,12 @@ class Scan(PureOp): ...@@ -903,7 +909,12 @@ class Scan(PureOp):
# Here, we build a list inner_ins_shape, such that inner_ins_shape[i] # Here, we build a list inner_ins_shape, such that inner_ins_shape[i]
# is the shape of self.inputs[i] # is the shape of self.inputs[i]
for inp, inp_shp in zip(node.inputs, input_shapes):
assert inp_shp is None or len(inp_shp) == inp.ndim
# sequences # sequences
# We skip iputs_shapes[0] as it is the total or current number
# of iteration
seqs_shape = [x[1:] for x in input_shapes[1:1 + self.n_seqs]] seqs_shape = [x[1:] for x in input_shapes[1:1 + self.n_seqs]]
# mit_mot, mit_sot, sit_sot # mit_mot, mit_sot, sit_sot
......
...@@ -374,7 +374,13 @@ class ScanSaveMem(gof.Optimizer): ...@@ -374,7 +374,13 @@ class ScanSaveMem(gof.Optimizer):
else: else:
return tensor.as_tensor_variable(x) return tensor.as_tensor_variable(x)
shape_of = node.env.shape_feature.shape_of if hasattr(env, 'shape_feature'):
shape_of = node.env.shape_feature.shape_of
else:
# Each call site of shape_of is in a try..except
# That use a default version when the variable is not
# in the dictionary
shape_of = {}
# 1. Initialization of variables # 1. Initialization of variables
# Note 1) We do not actually care about outputs representing shared # Note 1) We do not actually care about outputs representing shared
# variables (those have no intermediate values) so it is safer to # variables (those have no intermediate values) so it is safer to
...@@ -472,12 +478,12 @@ class ScanSaveMem(gof.Optimizer): ...@@ -472,12 +478,12 @@ class ScanSaveMem(gof.Optimizer):
if i > op.n_mit_mot: if i > op.n_mit_mot:
try: try:
length = shape_of[out][0] length = shape_of[out][0]
except Exception: except KeyError:
length = node.inputs[0] + init_l[i] length = node.inputs[0] + init_l[i]
else: else:
try: try:
length = shape_of[out][0] length = shape_of[out][0]
except Exception: except KeyError:
length = out.shape[0] length = out.shape[0]
cf_slice = tensor.basic.get_canonical_form_slice( cf_slice = tensor.basic.get_canonical_form_slice(
this_slice[0], length) this_slice[0], length)
...@@ -575,7 +581,7 @@ class ScanSaveMem(gof.Optimizer): ...@@ -575,7 +581,7 @@ class ScanSaveMem(gof.Optimizer):
else: else:
try: try:
length = shape_of[out][0] length = shape_of[out][0]
except Exception: except KeyError:
length = out.shape[0] length = out.shape[0]
cf_slice = tensor.basic.get_canonical_form_slice( cf_slice = tensor.basic.get_canonical_form_slice(
this_slice[0], length) this_slice[0], length)
......
...@@ -372,6 +372,10 @@ def infer_shape(outs, inputs, input_shapes): ...@@ -372,6 +372,10 @@ def infer_shape(outs, inputs, input_shapes):
# inside. We don't use the full ShapeFeature interface, but we # inside. We don't use the full ShapeFeature interface, but we
# let it initialize itself with an empty env, otherwise we will # let it initialize itself with an empty env, otherwise we will
# need to do it manually # need to do it manually
for inp, inp_shp in zip(inputs, input_shapes):
if inp_shp is not None and len(inp_shp) != inp.ndim:
assert len(inp_shp) == inp.ndim
shape_feature = tensor.opt.ShapeFeature() shape_feature = tensor.opt.ShapeFeature()
shape_feature.on_attach(theano.gof.Env([], [])) shape_feature.on_attach(theano.gof.Env([], []))
......
...@@ -423,9 +423,12 @@ class GemmRelated(Op): ...@@ -423,9 +423,12 @@ class GemmRelated(Op):
#setup_z_Nz_Sz = None #setup_z_Nz_Sz = None
check_xyz_rank2 = """ check_xyz_rank2 = """
if (%(_x)s->nd != 2) {PyErr_SetString(PyExc_NotImplementedError, "rank(x) != 2"); %(fail)s;} if (%(_x)s->nd != 2) {
if (%(_y)s->nd != 2) {PyErr_SetString(PyExc_NotImplementedError, "rank(y) != 2"); %(fail)s;} PyErr_Format(PyExc_NotImplementedError, "rank(x) != 2. rank(x) is %%d.", %(_x)s->nd); %(fail)s;}
if (%(_zout)s->nd != 2) {PyErr_SetString(PyExc_NotImplementedError, "rank(z) != 2"); %(fail)s;} if (%(_y)s->nd != 2) {
PyErr_Format(PyExc_NotImplementedError, "rank(y) != 2. rank(y) is %%d.", %(_y)s->nd); %(fail)s;}
if (%(_zout)s && %(_zout)s->nd != 2) {
PyErr_Format(PyExc_NotImplementedError, "rank(z) != 2. rank(z) is %%d.", %(_zout)s->nd); %(fail)s;}
""" """
check_xyz_double_or_float = """ check_xyz_double_or_float = """
if ((%(_x)s->descr->type_num != PyArray_DOUBLE) if ((%(_x)s->descr->type_num != PyArray_DOUBLE)
...@@ -442,7 +445,7 @@ class GemmRelated(Op): ...@@ -442,7 +445,7 @@ class GemmRelated(Op):
if ((%(_x)s->descr->type_num != %(_y)s->descr->type_num) if ((%(_x)s->descr->type_num != %(_y)s->descr->type_num)
||(%(_x)s->descr->type_num != %(_zout)s->descr->type_num)) ||(%(_x)s->descr->type_num != %(_zout)s->descr->type_num))
{ PyErr_SetString(PyExc_NotImplementedError, "type(z), type(y), type(z) are not all the same"); %(fail)s; } { PyErr_SetString(PyExc_NotImplementedError, "type(x), type(y), type(z) are not all the same"); %(fail)s; }
""" """
#it is not necessary that a or b have the same type as x,y,z #it is not necessary that a or b have the same type as x,y,z
...@@ -626,8 +629,8 @@ class GemmRelated(Op): ...@@ -626,8 +629,8 @@ class GemmRelated(Op):
return reduce(str.__add__, ( return reduce(str.__add__, (
self.declare_NS, self.declare_NS,
self.setup_z_Nz_Sz,
self.check_xyz_rank2, self.check_xyz_rank2,
self.setup_z_Nz_Sz,
self.check_xyz_double_or_float, self.check_xyz_double_or_float,
self.check_ab_double_or_float, self.check_ab_double_or_float,
self.check_dims, self.check_dims,
...@@ -644,7 +647,7 @@ class GemmRelated(Op): ...@@ -644,7 +647,7 @@ class GemmRelated(Op):
self.end_switch_typenum), '') self.end_switch_typenum), '')
def build_gemm_version(self): def build_gemm_version(self):
return (9,) return (10,)
class Gemm(GemmRelated): class Gemm(GemmRelated):
"""In-place version of matrix-matrix multiplication (with accumulation): """In-place version of matrix-matrix multiplication (with accumulation):
......
...@@ -34,6 +34,12 @@ from theano.gof import toolbox, DestroyHandler ...@@ -34,6 +34,12 @@ from theano.gof import toolbox, DestroyHandler
from basic import get_constant_value, ShapeError from basic import get_constant_value, ShapeError
theano.configparser.AddConfigVar('on_shape_error',
"warn: print a warning and use the default"
" value. raise: raise an error",
theano.configparser.EnumStr("warn", "raise"),
in_c_key=False)
# Utilities # Utilities
...@@ -101,13 +107,16 @@ def broadcast_like(value, template, env, dtype=None): ...@@ -101,13 +107,16 @@ def broadcast_like(value, template, env, dtype=None):
value = T.as_tensor_variable(value) value = T.as_tensor_variable(value)
if value.type == template.type: if value.type == template.type:
return value return value
shape_of = env.shape_feature.shape_of if template not in env.variables:
if template not in shape_of:
raise NotImplementedError('broadcast_like currently requires the ' raise NotImplementedError('broadcast_like currently requires the '
'template Variable to be in the env already') 'template Variable to be in the env already')
if hasattr(env, 'shape_feature'):
new_shape = env.shape_feature.shape_of[template]
else:
new_shape = template.shape
if dtype is None: if dtype is None:
dtype = template.dtype dtype = template.dtype
rval = T.alloc(T.cast(value, dtype), *shape_of[template]) rval = T.alloc(T.cast(value, dtype), *new_shape)
# the template may have 1s in its shape without being broadcastable # the template may have 1s in its shape without being broadcastable
if rval.broadcastable != template.broadcastable: if rval.broadcastable != template.broadcastable:
rval = T.unbroadcast(rval, *[i for i in xrange(rval.ndim) rval = T.unbroadcast(rval, *[i for i in xrange(rval.ndim)
...@@ -776,6 +785,11 @@ class ShapeFeature(object): ...@@ -776,6 +785,11 @@ class ShapeFeature(object):
if s is None: if s is None:
self.shape_of[r] = s self.shape_of[r] = s
else: else:
if r.ndim != len(s):
raise ShapeError("Something infered a shape with %d dimensions"
" for a variable with %d dimensions." % (
len(s), r.ndim))
shape_vars = [self.unpack(s_i) for s_i in s] shape_vars = [self.unpack(s_i) for s_i in s]
self.shape_of[r] = tuple(shape_vars) self.shape_of[r] = tuple(shape_vars)
for sv in shape_vars: for sv in shape_vars:
...@@ -893,6 +907,8 @@ class ShapeFeature(object): ...@@ -893,6 +907,8 @@ class ShapeFeature(object):
o_shapes = shape_infer(node, o_shapes = shape_infer(node,
[self.shape_of[r] for r in node.inputs]) [self.shape_of[r] for r in node.inputs])
except ShapeError: except ShapeError:
if config.on_shape_error == "raise":
raise
o_shapes = self.default_infer_shape(node, [self.shape_of[r] for o_shapes = self.default_infer_shape(node, [self.shape_of[r] for
r in node.inputs]) r in node.inputs])
except NotImplementedError, e: except NotImplementedError, e:
...@@ -903,11 +919,15 @@ class ShapeFeature(object): ...@@ -903,11 +919,15 @@ class ShapeFeature(object):
'supported, and one should now use tensor.ShapeError ' 'supported, and one should now use tensor.ShapeError '
'instead. The original exception message is: %s' % e) 'instead. The original exception message is: %s' % e)
except Exception, e: except Exception, e:
_logger.error(('Failed to infer_shape from Op %s.\nInput shapes:' msg = ('Failed to infer_shape from Op %s.\nInput shapes:'
'%s\nException encountered during infer_shape: ' '%s\nException encountered during infer_shape: '
'%s\nException message: %s\nTraceback: %s') % '%s\nException message: %s\nTraceback: %s') % (
(node.op, [self.shape_of[r] for r in node.inputs], node.op, [self.shape_of[r] for r in node.inputs],
type(e), str(e), traceback.format_exc())) type(e), str(e), traceback.format_exc())
if config.on_shape_error == "raise":
raise Exception(msg)
else:
_logger.error(msg)
o_shapes = self.default_infer_shape( o_shapes = self.default_infer_shape(
node, [self.shape_of[r] for r in node.inputs]) node, [self.shape_of[r] for r in node.inputs])
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论