提交 1272852f authored 作者: lamblin's avatar lamblin

Merge pull request #1448 from nouiz/adv_sub1_grad

Add theano.sparse_grad interface.
...@@ -48,3 +48,5 @@ There are also some top-level imports that you might find more convenient: ...@@ -48,3 +48,5 @@ There are also some top-level imports that you might find more convenient:
Works like :func:`tensor.dot` for both sparse and dense matrix products Works like :func:`tensor.dot` for both sparse and dense matrix products
.. autofunction:: theano.clone .. autofunction:: theano.clone
.. autofunction:: theano.sparse_grad
...@@ -168,6 +168,20 @@ def get_scalar_constant_value(v): ...@@ -168,6 +168,20 @@ def get_scalar_constant_value(v):
return tensor.get_scalar_constant_value(v) return tensor.get_scalar_constant_value(v)
def sparse_grad(var):
"""This function return a new variable whose gradient will be
stored in a sparse format instead of dense.
Currently only variable created by AdvancedSubtensor1 is supported.
i.e. a_tensor_var[an_int_vector].
.. versionadded:: 0.6rc4
"""
assert isinstance(var.owner.op, tensor.AdvancedSubtensor1)
ret = var.owner.op.__class__(sparse_grad=True)(*var.owner.inputs)
return ret
import theano.tests import theano.tests
if hasattr(theano.tests, "TheanoNoseTester"): if hasattr(theano.tests, "TheanoNoseTester"):
test = theano.tests.TheanoNoseTester().test test = theano.tests.TheanoNoseTester().test
......
...@@ -1266,6 +1266,12 @@ class numeric_grad(object): ...@@ -1266,6 +1266,12 @@ class numeric_grad(object):
""" """
abs_err = abs(a - b) abs_err = abs(a - b)
rel_err = abs_err / numpy.maximum(abs(a) + abs(b), 1e-8) rel_err = abs_err / numpy.maximum(abs(a) + abs(b), 1e-8)
# The numpy.asarray are needed as if a or b is a sparse matrix
# this would result in a numpy.matrix and not a numpy.ndarray
# and the behave differently causing problem later.
# In particular a_npy_matrix.flatten().shape == (1, n_element)
abs_err = numpy.asarray(abs_err)
rel_err = numpy.asarray(rel_err)
return (abs_err, rel_err) return (abs_err, rel_err)
def abs_rel_errors(self, g_pt): def abs_rel_errors(self, g_pt):
......
...@@ -417,6 +417,75 @@ class SparseInferShapeTester(utt.InferShapeTester): ...@@ -417,6 +417,75 @@ class SparseInferShapeTester(utt.InferShapeTester):
) )
class TestConstructSparseFromList(unittest.TestCase):
def test_adv_sub1_sparse_grad(self):
v = theano.tensor.ivector()
# Assert we don't create a sparse grad by default
m = theano.tensor.matrix()
sub = m[v]
g = theano.grad(sub.sum(), m)
assert isinstance(g.owner.op, tensor.AdvancedIncSubtensor1)
# Test that we create a sparse grad when asked
# OLD INTERFACE
m = theano.tensor.matrix()
sub = m[v]
m.type.sparse_grad = True
g = theano.grad(sub.sum(), m)
assert isinstance(g.owner.op, ConstructSparseFromList)
# Test that we create a sparse grad when asked
# OLD INTERFACE CONSEQUENCE
m = theano.tensor.matrix()
sub = m[v]
sub.type.sparse_grad = True
g = theano.grad(sub.sum(), m)
assert isinstance(g.owner.op, ConstructSparseFromList)
# Test that we create a sparse grad when asked
# USER INTERFACE
m = theano.tensor.matrix()
v = theano.tensor.ivector()
sub = theano.sparse_grad(m[v])
g = theano.grad(sub.sum(), m)
assert isinstance(g.owner.op, ConstructSparseFromList)
# Test that we create a sparse grad when asked
# Op INTERFACE
m = theano.tensor.matrix()
v = theano.tensor.ivector()
sub = theano.tensor.AdvancedSubtensor1(sparse_grad=True)(m, v)
g = theano.grad(sub.sum(), m)
assert isinstance(g.owner.op, ConstructSparseFromList)
# Test the sparse grad
valm = numpy.random.rand(5, 4).astype(config.floatX)
valv = numpy.random.random_integers(0, 4, 10)
m = theano.tensor.matrix()
shared_v = theano.shared(valv)
def fn(m):
return theano.sparse_grad(m[shared_v])
verify_grad_sparse(fn, [valm])
def test_err(self):
for ndim in [1, 3]:
t = theano.tensor.TensorType(dtype=config.floatX,
broadcastable=(False,) * ndim)()
v = theano.tensor.ivector()
sub = t[v]
# Assert we don't create a sparse grad by default
g = theano.grad(sub.sum(), t)
assert isinstance(g.owner.op, tensor.AdvancedIncSubtensor1)
# Test that we raise an error, as we can't create a sparse
# grad from tensors that don't have 2 dimensions.
sub = theano.sparse_grad(sub)
self.assertRaises(TypeError, theano.grad, sub.sum(), t)
class T_AddMul(unittest.TestCase): class T_AddMul(unittest.TestCase):
def testAddSS(self): def testAddSS(self):
self._testSS(add) self._testSS(add)
......
...@@ -706,6 +706,11 @@ class TensorType(Type): ...@@ -706,6 +706,11 @@ class TensorType(Type):
self.name = name self.name = name
self.numpy_dtype = numpy.dtype(self.dtype) self.numpy_dtype = numpy.dtype(self.dtype)
self.sparse_grad = sparse_grad self.sparse_grad = sparse_grad
if sparse_grad:
warnings.warn(
"DEPRECATION WARNING: You use an old interface to"
" AdvancedSubtensor1 sparse_grad. Now use"
" theano.sparse_grad(a_tensor[an_int_vector]).")
def filter(self, data, strict=False, allow_downcast=None): def filter(self, data, strict=False, allow_downcast=None):
"""Convert `data` to something which can be associated to a """Convert `data` to something which can be associated to a
...@@ -7153,10 +7158,17 @@ def inverse_permutation(perm): ...@@ -7153,10 +7158,17 @@ def inverse_permutation(perm):
class AdvancedSubtensor1(Op): class AdvancedSubtensor1(Op):
"""Implement x[ilist] where ilist is a vector of integers.""" """Implement x[ilist] where ilist is a vector of integers."""
def __init__(self, sparse_grad=False):
self.sparse_grad = sparse_grad
def __hash__(self): def __hash__(self):
return hash(type(self)) return hash(type(self))
def __eq__(self, other): def __eq__(self, other):
# Don't check the sparse_grad attribute as
# This don't change the output of this op
# So we want the merge optimier to merge two op
# that differ from there sparse_grad attribute.
return type(self) == type(other) return type(self) == type(other)
def __str__(self): def __str__(self):
...@@ -7212,8 +7224,18 @@ class AdvancedSubtensor1(Op): ...@@ -7212,8 +7224,18 @@ class AdvancedSubtensor1(Op):
x, ilist = inputs x, ilist = inputs
gz, = grads gz, = grads
assert len(inputs) == 2 assert len(inputs) == 2
sparse = False
if x.type.sparse_grad: if getattr(x.type, 'sparse_grad', False):
sparse = True
warnings.warn(
"DEPRECATION WARNING: AdvancedSubtensor1, you are using"
" an old interface to the sparse grad. You should use"
" theano.sparse_grad(a_tensor[an_int_vector]). ")
if sparse or self.sparse_grad:
if x.type.ndim != 2:
raise TypeError(
"AdvancedSubtensor1: you can't take the sparse grad"
" from a tensor with ndim != 2. ndim is " + str(x.type.ndim))
if sparse_module_ref is None: if sparse_module_ref is None:
import theano.sparse as sparse_module_ref import theano.sparse as sparse_module_ref
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论