提交 a4646f2c authored 作者: James Bergstra's avatar James Bergstra

revised handling of casting of constant and shared scalars.

The basic idea is to apply autocasting to constants but not to shared scalars. Joint work with Fred.
上级 1ea851e5
...@@ -445,7 +445,7 @@ class Test_pfunc(unittest.TestCase): ...@@ -445,7 +445,7 @@ class Test_pfunc(unittest.TestCase):
def test_givens_replaces_shared_variable(self): def test_givens_replaces_shared_variable(self):
a = shared(1.,'a') a = shared(1.,'a')
a.default_update = a+3. a.default_update = a+3.
b = tensor.scalar('b') b = tensor.dscalar('b')
c = a + 10 c = a + 10
f = pfunc([b],c, givens = {a:b}) f = pfunc([b],c, givens = {a:b})
......
...@@ -16,9 +16,9 @@ class Test_SharedVariable(unittest.TestCase): ...@@ -16,9 +16,9 @@ class Test_SharedVariable(unittest.TestCase):
assert shared(7, dtype='float64').type == Scalar('float64') assert shared(7, dtype='float64').type == Scalar('float64')
else: else:
assert shared(7).type == theano.tensor.lscalar assert shared(7).type == theano.tensor.lscalar, shared(7).type
assert shared(7.0).type == theano.tensor.scalar().type assert shared(7.0).type == theano.tensor.dscalar
assert shared(7, dtype='float64').type == theano.tensor.dscalar assert shared(numpy.float32(7)).type == theano.tensor.fscalar
# test tensor constructor # test tensor constructor
b = shared(numpy.zeros((5,5), dtype='int32')) b = shared(numpy.zeros((5,5), dtype='int32'))
...@@ -169,6 +169,13 @@ class Test_SharedVariable(unittest.TestCase): ...@@ -169,6 +169,13 @@ class Test_SharedVariable(unittest.TestCase):
def test_scalar_floatX(self): def test_scalar_floatX(self):
#
# the test should assure that floatX is not used in the shared constructor for scalars
# Shared values can change, and since we don't know the range they might take, we
# should keep the same bit width / precision as the original value used to create the
# shared variable.
#
def f(var, val): var.value = val def f(var, val): var.value = val
b = shared(numpy.int64(7)) b = shared(numpy.int64(7))
...@@ -184,11 +191,11 @@ class Test_SharedVariable(unittest.TestCase): ...@@ -184,11 +191,11 @@ class Test_SharedVariable(unittest.TestCase):
f(b,8) f(b,8)
b = shared(numpy.float(7.234)) b = shared(numpy.float(7.234))
assert b.dtype == theano.config.floatX assert b.type == theano.tensor.dscalar
f(b,8) f(b,8)
b = shared(7.234) b = shared(7.234)
assert b.dtype == theano.config.floatX assert b.type == theano.tensor.dscalar
f(b,8) f(b,8)
c = shared(numpy.zeros((5,5), dtype='float32')) c = shared(numpy.zeros((5,5), dtype='float32'))
......
...@@ -6,7 +6,7 @@ __docformat__ = "restructuredtext en" ...@@ -6,7 +6,7 @@ __docformat__ = "restructuredtext en"
import numpy import numpy
def _asarray(a, dtype=None, order=None): def _asarray(a, dtype, order=None):
"""Convert the input to a Numpy array. """Convert the input to a Numpy array.
This function is almost identical to ``numpy.asarray``, but it should be This function is almost identical to ``numpy.asarray``, but it should be
......
...@@ -154,11 +154,18 @@ as_tensor = as_tensor_variable ...@@ -154,11 +154,18 @@ as_tensor = as_tensor_variable
class NumpyAutocaster(object): class NumpyAutocaster(object):
""" This class is used to cast python ints and floats to numpy arrays. """ This class is used to cast python ints and floats to numpy arrays.
The behaviour for numpy scalars is a bit tricky... but tends to work in practice.
If the dtype of a numpy scalar is in the self.dtypes list, then this 'cast' is a no-op.
When config.floatX is float32 (at the time of calling), then this function downcasts float
and numpy.float arguments to numpy.float32, if float32 is in the self.dtypes list.
Python ints are always 64bit and floats are always double precision. Python ints are always 64bit and floats are always double precision.
This class uses the algorithm in __call__ to use a narrower dtype when no precision would This class uses the algorithm in __call__ to use a narrower dtype when no precision would
be lost, and to even lose precision when this is demanded (e.g. to automatically cast all be lost, and to even lose precision when this is demanded by the list of dtypes (e.g. to
floats to single-precision). automatically cast all floats to single-precision if self.dtypes does not include full
precision floats).
""" """
def __init__(self, dtypes): def __init__(self, dtypes):
...@@ -166,15 +173,17 @@ class NumpyAutocaster(object): ...@@ -166,15 +173,17 @@ class NumpyAutocaster(object):
def __call__(self, x): def __call__(self, x):
# change the default casting behaviour for python floats to always cast to float32 # change the default casting behaviour for python floats to always cast to float32
dtype = None dtype = None
if isinstance(x, numpy.float64) and 'float64' in self.dtypes:
dtype = 'float64' try: # pass through numpy scalars, since they are already typed on purpose typically.
elif isinstance(x, numpy.float32) and 'float32' in self.dtypes: if str(x.dtype) in self.dtypes:
dtype = 'float32' return theano._asarray(x, dtype=x.dtype) #leave dtype alone
elif isinstance(x, float) and config.floatX in self.dtypes and config.floatX == 'float32': except AttributeError:
dtype = config.floatX pass
if dtype: # unsafe downcast of float64 variables when config.floatX == 'float32'
return theano._asarray(x, dtype=dtype) # recall: float is numpy.float
if isinstance(x, float) and config.floatX in self.dtypes and config.floatX == 'float32':
return theano._asarray(x, dtype='float32')
for dtype in self.dtypes: for dtype in self.dtypes:
x_ = theano._asarray(x, dtype=dtype) x_ = theano._asarray(x, dtype=dtype)
...@@ -1857,15 +1866,21 @@ class Alloc(gof.Op): ...@@ -1857,15 +1866,21 @@ class Alloc(gof.Op):
def grad(self, inputs, (gout,)): def grad(self, inputs, (gout,)):
return [None for i in inputs] return [None for i in inputs]
def __call__(self, *inputs, **kwargs): def __call__(self, val, *shapes):
""" """
Don't generate alloc that do nothing. If the alloc would be useless, this function returns val.
If you always want an Alloc node, call make_node. If you always want an Alloc node, call make_node.
""" """
ret = super(Alloc,self).__call__(*inputs,**kwargs) ret = super(Alloc,self).__call__(val, *shapes)
if inputs[0].type == ret.type: try:
return inputs[0] #It makes optimization difficult when useless allocs are thrown into the graph at every
else: return ret #stage of optimization. This little logic tries to help at least in some cases.
if val.type == ret.type:
return val
except AttributeError:
pass
return ret
alloc = Alloc() alloc = Alloc()
pprint.assign(alloc, printing.FunctionPrinter('alloc')) pprint.assign(alloc, printing.FunctionPrinter('alloc'))
......
import traceback import traceback
import numpy import numpy
import theano.tensor.basic import theano.tensor.basic
from basic import TensorType, _tensor_py_operators from basic import TensorType, _tensor_py_operators, autocast_int, autocast_float
from theano.compile import shared_constructor, SharedVariable from theano.compile import shared_constructor, SharedVariable
from theano import config from theano import config
...@@ -35,29 +35,22 @@ class ScalarSharedVariable(SharedVariable, _tensor_py_operators): ...@@ -35,29 +35,22 @@ class ScalarSharedVariable(SharedVariable, _tensor_py_operators):
pass pass
@shared_constructor @shared_constructor
def scalar_constructor(value, name=None, strict=False, dtype=None): def scalar_constructor(value, name=None, strict=False):
"""SharedVariable constructor for scalar values. Default: int64 or float64. """SharedVariable constructor for scalar values. Default: int64 or float64.
:note: We implement this using 0-d tensors for now. :note: We implement this using 0-d tensors for now.
""" """
if not isinstance (value, (numpy.number, float, int)): if not isinstance (value, (numpy.number, float, int, complex)):
raise TypeError() raise TypeError()
if dtype is None: try:
if isinstance(value, numpy.float64): dtype=value.dtype
dtype = 'float64' except:
elif isinstance(value, numpy.float32): dtype=numpy.asarray(value).dtype
dtype = 'float32'
elif isinstance(value, float) and not strict:
dtype = config.floatX
elif isinstance(value, float):
dtype = 'float64'
elif isinstance(value, int):
dtype = 'int64'
else:
dtype = type(value).__name__
tensor_type = TensorType(dtype=dtype, broadcastable=[]) dtype=str(dtype)
value = theano._asarray(value, dtype=dtype)
tensor_type = TensorType(dtype=str(value.dtype), broadcastable=[])
try: try:
# Do not pass the dtype to asarray because we want this to fail if # Do not pass the dtype to asarray because we want this to fail if
...@@ -69,4 +62,3 @@ def scalar_constructor(value, name=None, strict=False, dtype=None): ...@@ -69,4 +62,3 @@ def scalar_constructor(value, name=None, strict=False, dtype=None):
except: except:
traceback.print_exc() traceback.print_exc()
raise raise
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论