提交 78e1dec1 authored 作者: James Bergstra's avatar James Bergstra

merge

...@@ -143,7 +143,7 @@ class SoftmaxWithBias(gof.Op): ...@@ -143,7 +143,7 @@ class SoftmaxWithBias(gof.Op):
@staticmethod @staticmethod
def c_code_cache_version(): def c_code_cache_version():
return (3,) return (4,)
@staticmethod @staticmethod
def c_code_template(): def c_code_template():
# this implementation was lifted from # this implementation was lifted from
...@@ -180,7 +180,8 @@ class SoftmaxWithBias(gof.Op): ...@@ -180,7 +180,8 @@ class SoftmaxWithBias(gof.Op):
} }
if ((%(x)s->dimensions[1] != %(b)s->dimensions[0])) if ((%(x)s->dimensions[1] != %(b)s->dimensions[0]))
{ {
PyErr_SetString(PyExc_ValueError, "dimension mismatch in arguments"); PyErr_Format(PyExc_ValueError, "number of columns in x (%%i) does not match length of b (%%i)",
%(x)s->dimensions[1], %(b)s->dimensions[0]);
%(fail)s; %(fail)s;
} }
...@@ -594,7 +595,8 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op): ...@@ -594,7 +595,8 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op):
} }
if (%(x)s->dimensions[0] != %(y_idx)s->dimensions[0]) if (%(x)s->dimensions[0] != %(y_idx)s->dimensions[0])
{ {
PyErr_SetString(PyExc_ValueError, "dimension mismatch in arguments"); PyErr_Format(PyExc_ValueError, "number of rows in x (%%i) does not match length of y (%%i)",
%(x)s->dimensions[0], %(y_idx)s->dimensions[0]);
%(fail)s; %(fail)s;
} }
...@@ -644,7 +646,7 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op): ...@@ -644,7 +646,7 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op):
def c_code_cache_version(self): def c_code_cache_version(self):
return (3,) + SoftmaxWithBias.c_code_cache_version() return (4,) + SoftmaxWithBias.c_code_cache_version()
def c_code(self, node, name, (x, b, y_idx), (nll, sm, am), sub): def c_code(self, node, name, (x, b, y_idx), (nll, sm, am), sub):
y_idx_type = node.inputs[2].type.dtype_specs()[1] y_idx_type = node.inputs[2].type.dtype_specs()[1]
am_type = y_idx_type am_type = y_idx_type
......
...@@ -507,6 +507,8 @@ class Canonizer(gof.LocalOptimizer): ...@@ -507,6 +507,8 @@ class Canonizer(gof.LocalOptimizer):
(a / b) * (b / c) * (c / d) -> a / d (a / b) * (b / c) * (c / d) -> a / d
(2.0 * x) / (4.0 * y) -> (0.5 * x) / y (2.0 * x) / (4.0 * y) -> (0.5 * x) / y
2 * x / 2 -> x 2 * x / 2 -> x
x * y * z -> Elemwise(T.mul){x,y,z} #only one pass over the memory.
!-> Elemwise(T.mul){x,Elemwise(T.mul){y,z}}
""" """
def __init__(self, main, inverse, reciprocal, calculate, use_reciprocal = True): def __init__(self, main, inverse, reciprocal, calculate, use_reciprocal = True):
...@@ -538,6 +540,7 @@ class Canonizer(gof.LocalOptimizer): ...@@ -538,6 +540,7 @@ class Canonizer(gof.LocalOptimizer):
a / (b / c) -> ([a, c], [b]) a / (b / c) -> ([a, c], [b])
log(x) -> ([log(x)], []) log(x) -> ([log(x)], [])
x**y -> ([x**y], []) x**y -> ([x**y], [])
x * y * z -> ([x, y, z], [])
""" """
...@@ -547,6 +550,14 @@ class Canonizer(gof.LocalOptimizer): ...@@ -547,6 +550,14 @@ class Canonizer(gof.LocalOptimizer):
# the dtype of the 'input' argument. The leaf-Variables of the graph covered by the # the dtype of the 'input' argument. The leaf-Variables of the graph covered by the
# recursion may be of any Variable type. # recursion may be of any Variable type.
if len(input.clients) > 1:
# this logic is too conservative, but doing it is better than not doing it.
#
# we don't want to canonize a subgraph that we will need to compute anyway for the other clients.
# This check is too conservative because if the other clients are also in the subgraph we are canonizing,
# then we should [probably?] recurse anyway.
return [input], []
if input.owner is None or input.owner.op not in [self.main, self.inverse, self.reciprocal]: if input.owner is None or input.owner.op not in [self.main, self.inverse, self.reciprocal]:
if input.owner and isinstance(input.owner.op, T.DimShuffle): if input.owner and isinstance(input.owner.op, T.DimShuffle):
# If input is a DimShuffle of some input which does something like this: # If input is a DimShuffle of some input which does something like this:
...@@ -778,6 +789,18 @@ class Canonizer(gof.LocalOptimizer): ...@@ -778,6 +789,18 @@ class Canonizer(gof.LocalOptimizer):
out = node.outputs[0] out = node.outputs[0]
assert len(node.outputs) == 1 assert len(node.outputs) == 1
# check if any of the clients of this node would be part of this canonized graph...
# if so, we do nothing and wait for them to be transformed.
def _bypass_dimshuffle(n):
if isinstance(n.op, DimShuffle) and len(n.outputs[0].clients) <= 1:
return _bypass_dimshuffle(n.outputs[0].clients.__iter__().next()[0])
else:
return n
for c,c_idx in out.clients:
if c=='output': continue
if _bypass_dimshuffle(c).op in [self.main, self.inverse, self.reciprocal]:
return False
# Here we make the canonical version of the graph around this node # Here we make the canonical version of the graph around this node
# See the documentation of get_num_denum and simplify # See the documentation of get_num_denum and simplify
orig_num, orig_denum = self.get_num_denum(node.outputs[0]) orig_num, orig_denum = self.get_num_denum(node.outputs[0])
......
...@@ -237,6 +237,177 @@ class test_canonize(unittest.TestCase): ...@@ -237,6 +237,177 @@ class test_canonize(unittest.TestCase):
out = f(*val_inputs) out = f(*val_inputs)
assert(len(f.maker.env.toposort())==expected_out_nb_elemwise) assert(len(f.maker.env.toposort())==expected_out_nb_elemwise)
assert(out_dtype==out.dtype) assert(out_dtype==out.dtype)
def test_multiple_case(self):
""" test those case take from the comment in Canonizer
x / x -> 1
(x * y) / x -> y
x / y / x -> 1 / y
x / y / z -> x / (y * z)
x / (y / z) -> (x * z) / y
(a / b) * (b / c) * (c / d) -> a / d
(2.0 * x) / (4.0 * y) -> (0.5 * x) / y
2 * x / 2 -> x
with and without DimShuffle
TODO: with DimShuffle
"""
import theano.tensor, theano.compile
shp=(3,3)
fx, fy, fz, fw = fmatrices('xyzw')
dx, dy, dz, dw = dmatrices('xyzw')
fxv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
fyv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
fzv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
fwv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dxv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dyv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dzv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dwv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
#we need the optimisation enabled, debug do this.
mode=compile.mode.predefined_modes['DEBUG_MODE']
#test x / x -> 1
for (g, sym_inputs, val_inputs, out_dtype) in [(fx/fx,[fx],[fxv],'float32'),
(dx/dx,[dx],[dxv],'float64')]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert (out==numpy.ones(shp, dtype=out_dtype)).all()
topo=f.maker.env.toposort()
assert len(topo)==1
assert isinstance(topo[0].op,(T.Elemwise,))
assert isinstance(topo[0].op.scalar_op,theano.scalar.basic.Second)
assert len(topo[0].inputs)==2
assert(out_dtype==out.dtype)
#test (x * y) / x -> y
for (g, sym_inputs, val_inputs, out_dtype) in [
((dx*dy)/dx,[dx,dy],[dxv,dyv],'float64'),
((fx*fy)/fx,[fx,fy],[fxv,fyv],'float32')
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert (out==val_inputs[1]).all()
topo=f.maker.env.toposort()
assert len(topo)==0
assert(out_dtype==out.dtype)
#test x / y / x -> 1 / y
for (g, sym_inputs, val_inputs, out_dtype) in [
((dx/dy)/dx,[dx,dy],[dxv,dyv],'float64'),
((fx/fy)/fx,[fx,fy],[fxv,fyv],'float32')
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert numpy.allclose(out,(1/val_inputs[1]))
topo=f.maker.env.toposort()
assert len(topo)==1
assert isinstance(topo[0].op,(T.Elemwise,))
assert isinstance(topo[0].op.scalar_op,theano.scalar.basic.Inv)
assert len(topo[0].inputs)==1
assert(out_dtype==out.dtype)
#test (a / b) * (b / c) * (c / d) -> a / d
for (g, sym_inputs, val_inputs, out_dtype) in [
((dx / dy) * (dy / dz) * (dz / dw),[dx,dy,dz,dw],[dxv,dyv,dzv,dwv],'float64'),
((fx / fy) * (fy / fz) * (fz / fw),[fx,fy,fz,fw],[fxv,fyv,fzv,fwv],'float32')
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert numpy.allclose(out,(val_inputs[0]/val_inputs[3]))
topo=f.maker.env.toposort()
assert len(topo)==1
assert isinstance(topo[0].op,(T.Elemwise,))
assert isinstance(topo[0].op.scalar_op,theano.scalar.basic.TrueDiv)
assert len(topo[0].inputs)==2
assert(out_dtype==out.dtype)
#test (2.0 * x) / (4.0 * y) -> (0.5 * x) / y
for (g, sym_inputs, val_inputs, out_dtype) in [
(((2.0*dx)/(4.0*dy)),[dx,dy],[dxv,dyv],'float64'),
(((2.0*fx)/(4.0*fy)),[fx,fy],[fxv,fyv],'float32'),
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert numpy.allclose(out,(0.5*val_inputs[0]/val_inputs[1]))
topo=f.maker.env.toposort()
assert len(topo)==2
assert isinstance(topo[0].op,(T.Elemwise,))
assert isinstance(topo[0].op.scalar_op,theano.scalar.basic.Mul)
assert len(topo[0].inputs)==2
assert isinstance(topo[1].op,(T.Elemwise,))
assert isinstance(topo[1].op.scalar_op,theano.scalar.basic.TrueDiv)
assert len(topo[1].inputs)==2
assert(out_dtype==out.dtype)
#test 2 * x / 2 -> x
for (g, sym_inputs, val_inputs, out_dtype) in [
((2*dx)/2,[dx],[dxv],'float64'),
((2*fx)/2,[fx],[fxv],'float32'),
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert (out==val_inputs[0]).all()
topo=f.maker.env.toposort()
assert len(topo)==0
assert(out_dtype==out.dtype)
def test_multiple_case_that_fail(self):
import theano.tensor, theano.compile
shp=(4,4)
fx, fy, fz = fmatrices('xyz')
dx, dy, dz = dmatrices('xyz')
fxv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
fyv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
fzv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dxv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dyv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
dzv = numpy.asarray(numpy.random.rand(*shp),dtype='float32')
fvv = numpy.asarray(numpy.random.rand(shp[0]),dtype='float32').reshape(1,shp[0])
mode=compile.mode.predefined_modes['DEBUG_MODE']
#test fail!
#test x / y / z -> x / (y * z)
for (g, sym_inputs, val_inputs, out_dtype) in [
((dx/dy)/dz,[dx,dy,dz],[dxv,dyv,dzv],'float64'),
((fx/fy)/fz,[fx,fy,fz],[fxv,fyv,fzv],'float32')
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert numpy.allclose(out,val_inputs[0]/val_inputs[1]/val_inputs[2])
topo=f.maker.env.toposort()
print topo
assert len(topo)==2
assert isinstance(topo[0].op,(T.Elemwise,))
assert isinstance(topo[0].op.scalar_op,theano.scalar.basic.Inv)
assert len(topo[0].inputs)==1
assert(out_dtype==out.dtype)
#test x / (y / z) -> (x * z) / y
for (g, sym_inputs, val_inputs, out_dtype) in [
(dx/(dy/dz),[dx,dy,dz],[dxv,dyv,dzv],'float64'),
(fx/(fy/fz),[fx,fy,fz],[fxv,fyv,fzv],'float32')
]:
f = compile.function(list(sym_inputs), g,
mode=mode)
out = f(*val_inputs)
assert numpy.allclose(out,val_inputs[0]/(val_inputs[1]/val_inputs[2]))
topo=f.maker.env.toposort()
print topo
assert len(topo)==2
assert isinstance(topo[0].op,(T.Elemwise,))
assert isinstance(topo[0].op.scalar_op,theano.scalar.basic.Inv)
assert len(topo[0].inputs)==1
assert(out_dtype==out.dtype)
def test_mixeddiv(): def test_mixeddiv():
"""Test that int division is preserved""" """Test that int division is preserved"""
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论