提交 48a64a38 authored 作者: lamblin's avatar lamblin

Merge pull request #888 from nouiz/view_map

View map
......@@ -21,5 +21,11 @@
TODO: Give examples for how to use these things! They are pretty complicated.
.. autofunction:: theano.tensor.nnet.conv.conv2d
- Conv implemented
- :func:`signal.conv2d <theano.tensor.signal.conv.conv2d>`.
- :func:`nnet.conv2d <theano.tensor.nnet.conv.conv2d>`.
- :func:`conv3D <theano.tensor.nnet.Conv3D.conv3D>`.
.. autofunction:: theano.tensor.signal.conv.conv2d
.. autofunction:: theano.tensor.nnet.conv.conv2d
.. autofunction:: theano.tensor.nnet.Conv3D.conv3D
......@@ -319,6 +319,56 @@ Exercises 8
- Our current element-wise fusion generates computation with only 1 output.
SciPy
-----
We can wrap SciPy function in Theano. But Scipy is an optional dependency.
Here is some code that allow to make the op Optional:
.. code-block:: python
try:
import scipy.linalg
imported_scipy = True
except ImportError:
# some ops (e.g. Cholesky, Solve, A_Xinv_b) won't work
imported_scipy = False
class SomeOp(Op):
...
def make_node(self, x):
assert imported_scipy, (
"Scipy not available. Scipy is needed for the SomeOp op.")
from nose.plugins.skip import SkipTest
class test_Solve(utt.InferShapeTester):
...
def test_infer_shape(self):
if not imported_scipy:
raise SkipTest("Scipy needed for the Cholesky op.")
Random number in tests
----------------------
Making test errors more reproducable is a good practice. To make your
tests more reproducable, you need a way to get the same random
number. You can do this by seeding NumPy's randon number
generator. There is the Theano flag unittest.rseed that specify the
seed that should be used to init random number generators. There is 2
ways to do this it numpy, here is one:
.. code-block:: python
# You can set NumPy's internal random number generator state with
numpy.random.seed(utt.fetch_seed())
# All following call to numpy.random.*() function will get affected.
# Or you can create a new RandomState separate from the others
rng = numpy.random.RandomState(utt.fetch_seed())
# You can call all numpy's random number generator function's on rng
rng.rand(5, 5)
GPU Op
------
......
......@@ -780,46 +780,40 @@ def _check_viewmap(node, storage_map):
outstorage = storage_map[onode][0]
instorage_id = [id(storage_map[i][0]) for i in node.inputs]
# TODO: investigate ways in which other Types may be aliased
# TODO: consider adding a function to Type to detect aliasing
danger_flag = id(outstorage) in instorage_id or\
(type(outstorage)==numpy.ndarray and
outstorage.flags['OWNDATA']==False)
# first find out which input it aliases
view_map = getattr(node.op, 'view_map', {})
destroy_map = getattr(node.op, 'destroy_map', {})
if danger_flag:
# first find out which input it aliases
view_map = getattr(node.op, 'view_map', {})
destroy_map = getattr(node.op, 'destroy_map', {})
# In theory, theano's view_map only allows for 1 output to
# alias 1 input. Checking for multiple aliases just in
# case...
# In theory, theano's view_map only allows for 1 output to
# alias 1 input. Checking for multiple aliases just in
# case...
for ii, inode in enumerate(node.inputs):
for ii, inode in enumerate(node.inputs):
if _may_share_memory(outstorage, storage_map[inode][0]):
if _may_share_memory(outstorage, storage_map[inode][0]):
nodeid = id(inode)
bad_alias[nodeid] = ii
nodeid = id(inode)
bad_alias[nodeid] = ii
# check that the aliasing was declared in [view|destroy]_map
if ([ii] == view_map.get(oi, None) or
[ii] == destroy_map.get(oi, None)):
# check that the aliasing was declared in [view|destroy]_map
if ([ii]==view_map.get(oi,None) or\
[ii]==destroy_map.get(oi,None)):
good_alias[nodeid] = bad_alias.pop(nodeid)
good_alias[nodeid] = bad_alias.pop(nodeid)
#TODO: make sure this is correct
# According to OB, duplicate inputs are rejected on build graph time
# if they cause problems. So if they are here it should be ok.
for key, val in good_alias.iteritems():
bad_alias.pop(key, None)
if bad_alias:
raise BadViewMap(node, oi, outstorage, bad_alias.values())
#TODO: make sure this is correct
# According to OB, duplicate inputs are rejected on build graph time
# if they cause problems. So if they are here it should be ok.
for key, val in good_alias.iteritems():
bad_alias.pop(key, None)
if bad_alias:
raise BadViewMap(node, oi, outstorage, bad_alias.values())
#if its not aliased to input, check output->output aliasing
if not good_alias and _is_used_in_graph(onode):
for other_oi, other_onode in enumerate(node.outputs):
if other_oi == oi: continue
if other_oi == oi:
continue
other_storage = storage_map[other_onode][0]
# check to see if we share memory with this other output
......@@ -1547,7 +1541,8 @@ class _VariableEquivalenceTracker(object):
#List of default version of make thunk.
#This is needed to know if the user overrided it.
#The GpuOp will be added here when theano.sandbox.cuda is imported.
default_make_thunk = [theano.gof.Op.make_thunk.im_func]
default_make_thunk = [theano.gof.Op.make_thunk.im_func,
theano.gof.OpenMPOp.make_thunk.im_func]
class _Linker(gof.link.LocalLinker):
......
......@@ -905,20 +905,26 @@ nd_collapse_[i]=0;
//std::cerr << "C_CODE %(opname)s checking input %(iname)s\\n";
if (%(nd)s != %(iname)s->nd)
{
PyErr_Format(PyExc_TypeError, "need %(nd)s dims, not %%i", %(iname)s->nd);
PyErr_Format(PyExc_TypeError,
"need %(nd)s dims, not %%i", %(iname)s->nd);
%(fail)s;
}
for (int i = 0; i< %(nd)s; ++i)
{
dims[i] = (dims[i] == 1) ? CudaNdarray_HOST_DIMS(%(iname)s)[i] : dims[i];
if ((!(broadcasts_%(iname)s[i] && CudaNdarray_HOST_DIMS(%(iname)s)[i] == 1))&& (dims[i] != CudaNdarray_HOST_DIMS(%(iname)s)[i]))
if ((!(broadcasts_%(iname)s[i] &&
CudaNdarray_HOST_DIMS(%(iname)s)[i] == 1)) &&
(dims[i] != CudaNdarray_HOST_DIMS(%(iname)s)[i]))
{
//std::cerr << "C_CODE %(opname)s checking input %(iname)s failed\\n";
PyErr_Format(PyExc_ValueError, "GpuElemwise. Input dimension mis-match. One of your inputs has shape[%%i] == %%i, but the output's size on that axis is %%i.",
i,
CudaNdarray_HOST_DIMS(%(iname)s)[i],
dims[i]
);
PyErr_Format(PyExc_ValueError,
"GpuElemwise. Input dimension mis-match. Input"
" %(id)d (index start at 0 has shape[%%i] == %%i"
", but the output's size on that axis is %%i.",
i,
CudaNdarray_HOST_DIMS(%(iname)s)[i],
dims[i]
);
%(fail)s;
}
}
......
......@@ -62,7 +62,7 @@ optdb.register('gpu_after_fusion',
def register_opt(*tags, **kwargs):
def f(local_opt):
name = (kwargs and kwargs.pop('name')) or local_opt.__name__
gpu_optimizer.register(name, local_opt, 'fast_run', 'inplace', *tags)
gpu_optimizer.register(name, local_opt, 'fast_run', *tags)
return local_opt
return f
......
......@@ -380,6 +380,7 @@ def test_reshape():
#print n_bb
assert numpy.all(aa == n_bb)
assert aa.shape == n_bb.shape
# Test the not contiguous case
shape_1_2x = (shape_1[0] * 2,) + shape_1[1:]
......@@ -396,6 +397,7 @@ def test_reshape():
#print n_bb
assert numpy.all(aa == n_bb)
assert aa.shape == n_bb.shape
def bad_subtest(shape_1, shape_2, rng):
a = theano._asarray(rng.randn(*shape_1), dtype='float32')
......
......@@ -679,10 +679,6 @@ class CSM(gof.Op):
a regular grad.
"""
# should view the other inputs too, but viewing multiple inputs is not
view_map = {0: [0]}
#currently supported by the destroyhandler
kmap = None
"""Indexing to speficied what part of the data parameter
should be use to construct the sparse matrix."""
......@@ -701,6 +697,11 @@ class CSM(gof.Op):
self.kmap = kmap
if not isinstance(self.kmap, numpy.ndarray):
# should view the other inputs too, but viewing multiple
# inputs is not currently supported by the destroyhandler
self.view_map = {0: [0]}
self._hashval = (hash(type(self)) ^ hash(self.format) ^
_kmap_hash(self.kmap))
......@@ -711,6 +712,11 @@ class CSM(gof.Op):
def __hash__(self):
return self._hashval
def __str__(self):
if self.kmap is not None:
return "%s{%s}" %(self.__class__.__name__, str(self.kmap))
return self.__class__.__name__
def make_node(self, data, indices, indptr, shape):
data = tensor.as_tensor_variable(data)
......@@ -802,8 +808,10 @@ class CSMGrad(gof.op.Op):
def __init__(self, kmap=None):
self.kmap = kmap
if self.kmap is None:
self.view_map = {0: [1]}
#This class always allocate a new output.
#I keep this here to help GD understand what this kmap think is.
#if self.kmap is None:
# self.view_map = {0: [1]}
def __eq__(self, other):
return type(self) == type(other) and _kmap_eq(self.kmap, other.kmap)
......@@ -1224,6 +1232,7 @@ class Transpose(gof.op.Op):
matrix.
:note: The grad is regular, i.e. not structured.
"""
view_map = {0: [0]}
format_map = {'csr': 'csc',
'csc': 'csr'}
......@@ -1355,6 +1364,8 @@ class RowScaleCSC(gof.op.Op):
# :note: The grad implemented is structured.
view_map = {0: [0]}
def __eq__(self, other):
return type(self) == type(other)
......
......@@ -2070,6 +2070,7 @@ def _hv_switch(op, expected_function):
def expected_f(self, a, format=None, dtype=None):
return expected_function(a, format, dtype)
XStackTester.__name__ = op.__name__ + "Tester"
return XStackTester
HStackTester = _hv_switch(HStack, sp.hstack)
......@@ -2385,6 +2386,7 @@ def elemwise_checker(op, expected_f, gap=None, test_dtypes=None,
verify_grad_sparse(self.op,
data,
structured=True)
Tester.__name__ = op.__name__ + "Tester"
return Tester
......
......@@ -817,10 +817,9 @@ class Elemwise(Op):
min_informative_str(output) + '\n'
errormsg += 'original exception was: ' + '\n'.join(
traceback.format_exception_only(*sys.exc_info()[0:2]))
raise Exception(errormsg)
else:
e.args = e.args + (errormsg, )
raise
e.args = e.args + (errormsg, )
raise
if nout == 1:
variables = [variables]
......
......@@ -549,6 +549,20 @@ class Conv3D(theano.Op):
global conv3D
conv3D = Conv3D()
"""
3D "convolution" of multiple filters on a minibatch
(does not flip the kernel, moves kernel with a user specified stride)
:param V: Visible unit, input.
dimensions: (batch, row, column, time, in channel)
:param W: Weights, filter.
dimensions: (out channel, row, column, time ,in channel)
:param b: bias, shape == (W.shape[0],)
:param d: strides when moving the filter over the input(dx, dy, dt)
:note: The order of dimensions do not correspond with the one in `conv2d`.
This is for optimization.
"""
def computeH(V,W,b,d):
assert len(W.shape) == 5
......
......@@ -690,8 +690,8 @@ class ConvOp(OpenMPOp):
fulloutshp = tuple(ConvOp.getOutputShape(imshp_logical[
1:], kshp_logical, (1, 1), self.out_mode))
if z[0] is None or z[0].shape != (bsize,) + (nkern,) + fulloutshp:
z[0] = numpy.zeros((bsize,) + (nkern,) + fulloutshp,
if z[0] is None or z[0].shape != (bsize, nkern,) + fulloutshp:
z[0] = numpy.zeros((bsize, nkern,) + fulloutshp,
dtype=img2d.dtype)
zz = z[0]
......
......@@ -1397,12 +1397,8 @@ _good_broadcast_unary_gammaln = dict(
normal=(rand_ranged(-1 + 1e-2, 10, (2, 3)),),
empty=(numpy.asarray([]),),)
_grad_broadcast_unary_gammaln = dict(
normal=(rand_ranged(1e-8, 10, (2, 3)),),)
if theano.config.floatX == 'float32':
gamma_eps = 3e-4
else:
gamma_eps = 2e-10
# smaler range as our grad method don't estimate it good enough.
normal=(rand_ranged(1e-8, 8, (2, 3)),),)
GammaTester = makeBroadcastTester(
op=tensor.gamma,
......@@ -1410,7 +1406,6 @@ GammaTester = makeBroadcastTester(
good=_good_broadcast_unary_gammaln,
grad=_grad_broadcast_unary_gammaln,
mode=mode_no_scipy,
eps=gamma_eps,
skip=skip_scipy)
GammaInplaceTester = makeBroadcastTester(
op=inplace.gamma_inplace,
......@@ -1418,7 +1413,6 @@ GammaInplaceTester = makeBroadcastTester(
good=_good_broadcast_unary_gammaln,
grad=_grad_broadcast_unary_gammaln,
mode=mode_no_scipy,
eps=gamma_eps,
inplace=True,
skip=skip_scipy)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论