提交 ef088251 authored 作者: Nicolas Bouchard's avatar Nicolas Bouchard

Merge files extra_ops/BinCountOp.py and extra_ops/DiffOp.py to extra_ops.py.

Split tests and code for these ops. Move the tests to tests/test_extra_ops.py. Create extra_ops/__init__.py
上级 1f244aa4
# TODO implement grad for higher dimension
import theano
import numpy as np
from theano import tensor as T
from theano.tests import unittest_tools as utt
class DiffOp(theano.Op):
"""Calculate the n-th order discrete difference along given axis.
The first order difference is given by out[n] = a[n+1] - a[n]
along the given axis, higher order differences are calculated by
using diff recursively. Wraping of numpy.diff for vector.
Parameter:
x -- Input vector.
Keywords arguments:
n -- The number of times values are differenced, default is 1.
"""
def __init__(self, n=1):
self.n = n
# self.axis = axis
def __eq__(self, other):
return (type(self) == type(other) and
self.n == other.n)
# self.axis == other.axis
def __hash__(self):
return hash(type(self)) ^ hash(self.n) # ^ hash(self.axis)
def make_node(self, x):
x = T.as_tensor_variable(x)
return theano.Apply(self, [x], [x.type()])
def perform(self, node, inputs, output_storage):
x = inputs[0]
z = output_storage[0]
z[0] = np.diff(x, self.n) # axis
def grad(self, inputs, outputs_gradients):
z = outputs_gradients[0]
def _grad_helper(z):
pre = T.concatenate([[0.], z]) # Prepend 0
app = T.concatenate([z, [0.]]) # Append 0
return pre - app
for k in range(self.n): # Apply grad recursively
z = _grad_helper(z)
return [z]
def infer_shape(self, node, ins_shapes):
i0_shapes = ins_shapes[0]
out_shape = list(i0_shapes)
out_shape[0] = out_shape[0] - self.n # Axis
return [out_shape]
def __str__(self):
return self.__class__.__name__
def diff(x, n=1): # Axis
return DiffOp(n=n)(x)
class TestDiffOp(utt.InferShapeTester):
nb = 10 # Number of time iterating for n
def setUp(self):
super(TestDiffOp, self).setUp()
self.op_class = DiffOp
self.op = DiffOp()
def test_diffOp(self):
x = T.dvector('x')
a = np.random.random(500)
f = theano.function([x], diff(x))
assert np.allclose(np.diff(a), f(a))
# Test n
for k in range(TestDiffOp.nb):
g = theano.function([x], diff(x, n=k))
assert np.allclose(np.diff(a, n=k), g(a))
def test_infer_shape(self):
x = T.dvector('x')
self._compile_and_check([x],
[self.op(x)],
[np.random.random(500)],
self.op_class)
for k in range(TestDiffOp.nb):
self._compile_and_check([x],
[DiffOp(n=k)(x)],
[np.random.random(500)],
self.op_class)
def test_grad(self):
x = T.vector('x')
a = np.random.random(500)
gf = theano.function([x], T.grad(T.sum(diff(x)), x))
utt.verify_grad(self.op, [a])
# Test n
for k in range(TestDiffOp.nb):
dg = theano.function([x], T.grad(T.sum(diff(x, n=k)), x))
utt.verify_grad(DiffOp(n=k), [a])
from extra_ops import (DiffOp, diff,
BinCountOp, bincount)
import theano import theano
import numpy as np import numpy as np
from theano import tensor as T from theano import tensor as T
from theano.tests import unittest_tools as utt
class DiffOp(theano.Op):
"""Calculate the n-th order discrete difference along given axis.
The first order difference is given by out[n] = a[n+1] - a[n]
along the given axis, higher order differences are calculated by
using diff recursively. Wraping of numpy.diff.
Parameter:
x -- Input vector.
Keywords arguments:
n -- The number of times values are differenced, default is 1.
"""
def __init__(self, n=1, axis=-1):
self.n = n
self.axis = axis
def __eq__(self, other):
return (type(self) == type(other) and
self.n == other.n and
self.axis == other.axis)
def __hash__(self):
return hash(type(self)) ^ hash(self.n) ^ hash(self.axis)
def make_node(self, x):
x = T.as_tensor_variable(x)
return theano.Apply(self, [x], [x.type()])
def perform(self, node, inputs, output_storage):
x = inputs[0]
z = output_storage[0]
z[0] = np.diff(x, n=self.n, axis=self.axis)
def grad(self, inputs, outputs_gradients):
inputs = inputs[0]
if inputs.ndim != 1:
raise TypeError("Grad is not implemented for inputs with"
"number of dimension other than 1.")
z = outputs_gradients[0]
def _grad_helper(z):
pre = T.concatenate([[0.], z])
app = T.concatenate([z, [0.]])
return pre - app
for k in range(self.n):
z = _grad_helper(z)
return [z]
def infer_shape(self, node, ins_shapes):
i0_shapes = ins_shapes[0]
out_shape = list(i0_shapes)
out_shape[self.axis] = out_shape[self.axis] - self.n
return [out_shape]
def __str__(self):
return self.__class__.__name__
def diff(x, n=1, axis=-1):
"""Calculate the n-th order discrete difference along given axis.
The first order difference is given by out[n] = a[n+1] - a[n]
along the given axis, higher order differences are calculated by
using diff recursively. Wraping of numpy.diff.
Parameter:
x -- Input vector.
Keywords arguments:
n -- The number of times values are differenced, default is 1.
"""
return DiffOp(n=n, axis=axis)(x)
class BinCountOp(theano.Op): class BinCountOp(theano.Op):
...@@ -24,50 +104,59 @@ class BinCountOp(theano.Op): ...@@ -24,50 +104,59 @@ class BinCountOp(theano.Op):
minlength -- A minimum number of bins for the output array. minlength -- A minimum number of bins for the output array.
""" """
compatible_type = ('int8', 'int16', 'int32', 'int64', compatible_type = ('int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64') 'uint8', 'uint16', 'uint32', 'uint64')
"""Tuple of all compatible dtype for the parameter of this op."""
def __init__(self, weights=None, minlength=None): def __init__(self, minlength=None):
self.weights = weights
self.minlength = minlength self.minlength = minlength
def __eq__(self, other): def __eq__(self, other):
return (type(self) == type(other) and return (type(self) == type(other) and
self.weights == other.weights and
self.minlength == other.minlength) self.minlength == other.minlength)
def __hash__(self): def __hash__(self):
h = 0 return hash(type(self)) ^ hash(self.minlength)
if self.weights != None:
for k in range(len(self.weights)):
h = h ^ hash(self.weights[k])
return hash(type(self)) ^ h ^ hash(self.minlength)
def make_node(self, x): def make_node(self, x, weights):
x = T.as_tensor_variable(x) x = T.as_tensor_variable(x)
if x.dtype not in BinCountOp.compatible_type: if x.dtype not in BinCountOp.compatible_type:
raise TypeError("Inputs must be integers.") raise TypeError("Inputs dtype must be an integer.")
if x.ndim != 1: if x.ndim != 1:
raise TypeError("Inputs must be of dimension 1.") raise TypeError("Inputs must be of dimension 1.")
return theano.Apply(self, [x], [x.type()])
if weights is None:
weights = theano.gof.Constant(theano.gof.Generic(), None)
out_type = x.type()
else:
weights = T.as_tensor_variable(weights)
out_type = weights.type()
if weights.ndim != 1:
raise TypeError("Weights cannot have a number of"
"dimension different of 1.")
return theano.Apply(self, [x, weights], [out_type])
def perform(self, node, inputs, output_storage): def perform(self, node, inputs, output_storage):
x = inputs[0] x = inputs[0]
if x.dtype not in BinCountOp.compatible_type: weights = inputs[1]
raise TypeError("Inputs must be integers.")
if x.ndim != 1:
raise TypeError("Input must be of dimension 1.")
z = output_storage[0] z = output_storage[0]
z[0] = np.bincount(x, self.weights, self.minlength)
if weights is not None and weights.shape != x.shape:
raise TypeError("All inputs must have the same shape.")
z[0] = np.bincount(x, weights=weights, minlength=self.minlength)
def grad(self, inputs, outputs_gradients): def grad(self, inputs, outputs_gradients):
return [None for i in inputs] # Non differentiable return [None for i in inputs]
def infer_shape(self, node, ins_shapes): def infer_shape(self, node, ins_shapes):
inputs = node.inputs[0] x = node.inputs[0]
m = T.max(inputs) + 1 m = T.max(x) + 1
if self.minlength != None: if self.minlength != None:
m = T.max(T.stack(m, self.minlength)) m = T.maximum(m, self.minlength)
return [[m]] return [[m]]
def __str__(self): def __str__(self):
...@@ -75,43 +164,23 @@ class BinCountOp(theano.Op): ...@@ -75,43 +164,23 @@ class BinCountOp(theano.Op):
def bincount(x, weights=None, minlength=None): def bincount(x, weights=None, minlength=None):
return BinCountOp(weights=weights, minlength=minlength)(x) """Count number of occurrences of each value in array of non-negative ints.
The number of bins (of size 1) is one larger than the largest
class TestBinCountOp(utt.InferShapeTester): value in x. If minlength is specified, there will be at least
def setUp(self): this number of bins in the output array (though it will be longer
super(TestBinCountOp, self).setUp() if necessary, depending on the contents of x). Each bin gives the
self.op_class = BinCountOp number of occurrences of its index value in x. If weights is
self.op = BinCountOp() specified the input array is weighted by it, i.e. if a value n
is found at position i, out[n] += weight[i] instead of out[n] += 1.
def test_bincountOp(self): Wraping of numpy.bincount
x = T.lvector('x')
a = np.random.random_integers(50, size=(25)) Parameter:
w = np.random.random((25,)) x -- 1 dimension, nonnegative ints
f1 = theano.function([x], bincount(x)) Keywords arguments:
f2 = theano.function([x], bincount(x, weights=w)) weights -- Weights, array of the same shape as x.
f3 = theano.function([x], bincount(x, minlength=23)) minlength -- A minimum number of bins for the output array.
assert (np.bincount(a) == f1(a)).all """
assert (np.bincount(a, weights=w) == f2(a)).all return BinCountOp(minlength=minlength)(x, weights)
assert (np.bincount(a, minlength=23) == f3(a)).all
def test_infer_shape(self):
x = T.lvector('x')
self._compile_and_check([x],
[self.op(x)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
w = np.random.random((25,))
self._compile_and_check([x],
[bincount(x, weights=w)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
self._compile_and_check([x],
[bincount(x, minlength=60)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
import theano
import numpy as np
from theano import tensor as T
from theano.tests import unittest_tools as utt
from theano.extra_ops import *
class TestBinCountOp(utt.InferShapeTester):
def setUp(self):
super(TestBinCountOp, self).setUp()
self.op_class = BinCountOp
self.op = BinCountOp()
def test_bincountOp(self):
x = T.lvector('x')
w = T.dvector('w')
a = np.random.random_integers(50, size=(25))
weights = np.random.random((25,))
f1 = theano.function([x], bincount(x))
f2 = theano.function([x, w], bincount(x, weights=w))
f3 = theano.function([x], bincount(x, minlength=23))
f4 = theano.function([x], bincount(x, minlength=5))
assert (np.bincount(a) == f1(a)).all()
assert np.allclose(np.bincount(a, weights=weights), f2(a, weights))
assert (np.bincount(a, minlength=23) == f3(a)).all()
assert (np.bincount(a, minlength=5) == f3(a)).all()
def test_infer_shape(self):
x = T.lvector('x')
self._compile_and_check([x],
[bincount(x)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
weights = np.random.random((25,))
self._compile_and_check([x],
[bincount(x, weights=weights)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
self._compile_and_check([x],
[bincount(x, minlength=60)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
self._compile_and_check([x],
[bincount(x, minlength=5)],
[np.random.random_integers(50, size=(25,))],
self.op_class)
class TestDiffOp(utt.InferShapeTester):
nb = 10 # Number of time iterating for n
def setUp(self):
super(TestDiffOp, self).setUp()
self.op_class = DiffOp
self.op = DiffOp()
def test_diffOp(self):
x = T.dmatrix('x')
a = np.random.random((30, 50))
f = theano.function([x], diff(x))
assert np.allclose(np.diff(a), f(a))
for axis in range(len(a.shape)):
for k in range(TestDiffOp.nb):
g = theano.function([x], diff(x, n=k, axis=axis))
assert np.allclose(np.diff(a, n=k, axis=axis), g(a))
def test_infer_shape(self):
x = T.dmatrix('x')
a = np.random.random((30, 50))
self._compile_and_check([x],
[self.op(x)],
[a],
self.op_class)
for axis in range(len(a.shape)):
for k in range(TestDiffOp.nb):
self._compile_and_check([x],
[diff(x, n=k, axis=axis)],
[a],
self.op_class)
def test_grad(self):
x = T.vector('x')
a = np.random.random(500)
gf = theano.function([x], T.grad(T.sum(diff(x)), x))
utt.verify_grad(self.op, [a])
for k in range(TestDiffOp.nb):
dg = theano.function([x], T.grad(T.sum(diff(x, n=k)), x))
utt.verify_grad(DiffOp(n=k), [a])
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论