提交 0b18cf56 authored 作者: nouiz's avatar nouiz

Merge pull request #398 from dwf/tests_for_tile

Tests for tile (and some fixes besides)
...@@ -153,6 +153,12 @@ Bug fixes (the result changed): ...@@ -153,6 +153,12 @@ Bug fixes (the result changed):
* When indexing into a subtensor of negative stride (for instance, x[a:b:-1][c]), * When indexing into a subtensor of negative stride (for instance, x[a:b:-1][c]),
an optimization replacing it with a direct indexing (x[d]) used an incorrect formula, an optimization replacing it with a direct indexing (x[d]) used an incorrect formula,
leading to incorrect results. (Pascal, reported by Razvan) leading to incorrect results. (Pascal, reported by Razvan)
* The tile() function is now stricter in what it accepts to allow for better
error-checking/avoiding nonsensical situations. The gradient has been
disabled for the time being as it only implemented (incorrectly) one special
case. The `reps` argument must be a constant (not a tensor variable), and
must have the same length as the number of dimensions in the `x` argument;
this is now checked. (David)
Crashes fixed: Crashes fixed:
...@@ -238,6 +244,7 @@ Others: ...@@ -238,6 +244,7 @@ Others:
* Fix opt warning when the opt ShapeOpt is disabled(enabled by default) (Frederic) * Fix opt warning when the opt ShapeOpt is disabled(enabled by default) (Frederic)
* More internal verification on what each op.infer_shape return. (Frederic, James) * More internal verification on what each op.infer_shape return. (Frederic, James)
* Argmax dtype to int64 (Olivier) * Argmax dtype to int64 (Olivier)
* Improved docstring and basic tests for the Tile Op (David).
Reviewers (alphabetical order): Reviewers (alphabetical order):
* David, Frederic, Ian, James, Olivier, Razvan * David, Frederic, Ian, James, Olivier, Razvan
...@@ -32,7 +32,7 @@ import logging ...@@ -32,7 +32,7 @@ import logging
_logger=logging.getLogger("theano.tensor.basic") _logger=logging.getLogger("theano.tensor.basic")
#This is needed as we will hide it later #This is needed as we will hide it later
python_complex=complex python_complex = complex
python_any = any python_any = any
python_all = all python_all = all
...@@ -4685,25 +4685,25 @@ def flatten(x, outdim=1): ...@@ -4685,25 +4685,25 @@ def flatten(x, outdim=1):
return Flatten(outdim)(x) return Flatten(outdim)(x)
class TileGrad(Op): # class TileGrad(Op):
""" # """
Calculates the gradient of the Tile Op. # Calculates the gradient of the Tile Op.
""" # """
#this is so weird, I can't think of how to make this a general thing. # #this is so weird, I can't think of how to make this a general thing.
def make_node(self, x, reps, g_out): # def make_node(self, x, reps, g_out):
return gof.Apply(self, [x, reps, g_out], [x.type()]) # return gof.Apply(self, [x, reps, g_out], [x.type()])
#
def perform(self, node, inp, out): # def perform(self, node, inp, out):
x, reps, g_out = inp # x, reps, g_out = inp
gx, = out # gx, = out
xsh = x.shape # xsh = x.shape
if len(reps) == 2 and reps[1] == 1 and len(x.shape) == 1: # if len(reps) == 2 and reps[1] == 1 and len(x.shape) == 1:
gx[0] = numpy.sum(g_out, axis=0) # gx[0] = numpy.sum(g_out, axis=0)
else: # else:
raise NotImplementedError('x.shape, reps combination not ' # raise NotImplementedError('x.shape, reps combination not '
'supported', (x.shape, reps)) # 'supported', (x.shape, reps))
#
tilegrad = TileGrad() # tilegrad = TileGrad()
class Tile(Op): class Tile(Op):
...@@ -4742,7 +4742,8 @@ class Tile(Op): ...@@ -4742,7 +4742,8 @@ class Tile(Op):
def grad(self, inp, grads): def grad(self, inp, grads):
x, reps = inp x, reps = inp
g_out, = grads g_out, = grads
return [tilegrad(x, reps, g_out), None] # return [tilegrad(x, reps, g_out), None]
raise NotImplementedError()
def tile(x, reps, ndim=None): def tile(x, reps, ndim=None):
...@@ -4750,10 +4751,21 @@ def tile(x, reps, ndim=None): ...@@ -4750,10 +4751,21 @@ def tile(x, reps, ndim=None):
Tile input array `x` according to `reps`. See the docstring of `numpy.tile` Tile input array `x` according to `reps`. See the docstring of `numpy.tile`
for details. for details.
Currently, `reps` must be a constant.
TODO: expand this. TODO: expand this.
""" """
if len(reps) != x.ndim:
raise ValueError("len(reps) != x.ndim not currently supported")
if not hasattr(tile, 'op'): if not hasattr(tile, 'op'):
tile.op = {} tile.op = {}
try:
assert python_all([int(i) == i for i in iter(reps)])
except (TypeError, AssertionError):
raise ValueError("reps argument to tile must be a constant (e.g. "
"tuple, list of integers)")
if ndim is None: if ndim is None:
ndim = len(reps) ndim = len(reps)
......
...@@ -33,7 +33,8 @@ from theano.tensor import (_shared, wvector, bvector, autocast_float_as, ...@@ -33,7 +33,8 @@ from theano.tensor import (_shared, wvector, bvector, autocast_float_as,
var, value, Join, shape, MaxAndArgmax, lscalar, zvector, exp, var, value, Join, shape, MaxAndArgmax, lscalar, zvector, exp,
get_constant_value, ivector, reshape, scalar_from_tensor, scal, get_constant_value, ivector, reshape, scalar_from_tensor, scal,
iscalars, arange, dscalars, fvector, imatrix, numeric_grad, iscalars, arange, dscalars, fvector, imatrix, numeric_grad,
opt, ComplexError, TensorDot, lvector, true_div, max, min, Split, roll) opt, ComplexError, TensorDot, lvector, true_div, max, min, Split, roll,
tile)
from theano.tests import unittest_tools as utt from theano.tests import unittest_tools as utt
...@@ -3984,11 +3985,43 @@ def test_flatten_outdim_invalid(): ...@@ -3984,11 +3985,43 @@ def test_flatten_outdim_invalid():
except ValueError: except ValueError:
pass pass
# TODO: write test case for Tile Op def test_tile():
# See Ticket #619 # Test the one-dimensional case.
#def test_tile(): rng = numpy.random.RandomState(utt.fetch_seed())
# print >> sys.stderr, "WARNING: No testcase for Tile" x = vector()
# pass f = function([x], tile(x, (2,)))
x_ = rng.randn(5)
assert numpy.all(f(x_) == numpy.tile(x_, (2,)))
# Test the two-dimensional case.
x = matrix()
f = function([x], tile(x, (2, 3)))
x_ = rng.randn(2, 4)
assert numpy.all(f(x_) == numpy.tile(x_, (2, 3)))
# Test the three-dimensional case.
x = tensor3()
f = function([x], tile(x, (2, 3, 4)))
x_ = rng.randn(2, 4, 3)
assert numpy.all(f(x_) == numpy.tile(x_, (2, 3, 4)))
# XXX: It turns out that almost no cases of the tile gradient actually work.
# This is a test that should pass if the proper implementation is filled in.
def test_tile_grad_3d():
# N.B.: we should also use verify_grad in this test.
raise SkipTest() # Remove me when this is implemented.
rng = numpy.random.RandomState(utt.fetch_seed())
w = rng.randn(3, 4, 2)
w_tiled = numpy.tile(w, (2, 3, 4))
x = tensor.tensor3()
c = (as_tensor_variable(w_tiled) * tile(x, (2, 3, 4))).sum()
f = function([x], grad(c, x))
x_ = rng.randn(3, 4, 2)
# The gradient should be w, multiplied by its tiling dimensions (since
# the gradients are additive through the tiling operation)
assert numpy.all(f(x_) == 2 * 3 * 4 * w)
class TestARange(unittest.TestCase): class TestARange(unittest.TestCase):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论