提交 6a231fea authored 作者: lamblin's avatar lamblin

Merge pull request #1369 from nouiz/neibs

Image2Neibs.perform
""" """
TODO: implement Images2Neibs.{perform,infer_shape}() methods TODO: implement Images2Neibs.infer_shape() methods
""" """
from theano import Op, Apply from theano import Op, Apply
...@@ -7,16 +7,23 @@ import theano.tensor as T ...@@ -7,16 +7,23 @@ import theano.tensor as T
from theano.gradient import grad_not_implemented from theano.gradient import grad_not_implemented
from theano.gradient import grad_undefined from theano.gradient import grad_undefined
import numpy
class Images2Neibs(Op): class Images2Neibs(Op):
def __init__(self, mode='valid'): def __init__(self, mode='valid'):
""" """
Modes: :type mode: str
valid : Reshapes the input as a a 2D tensor where each row is a pooling example. :param mode: Possible values:
Requires an input that is a multiple of the pooling factor (in each direction) 'valid': Requires an input that is a multiple of the
ignore_borders : Same as valid, but will ignore the borders if the shape(s) of the input pooling factor (in each direction)
'ignore_borders': Same as valid, but will ignore the borders
if the shape(s) of the input
is not a multiple of the pooling factor(s) is not a multiple of the pooling factor(s)
wrap_centered : ?? TODO comment 'wrap_centered' : ?? TODO comment
:return:
Reshapes the input as a 2D tensor where each row is an
pooling example
""" """
if mode not in ['valid', 'wrap_centered', 'ignore_borders']: if mode not in ['valid', 'wrap_centered', 'ignore_borders']:
raise NotImplementedError("Only the mode valid, ignore_borders" raise NotImplementedError("Only the mode valid, ignore_borders"
...@@ -63,7 +70,8 @@ class Images2Neibs(Op): ...@@ -63,7 +70,8 @@ class Images2Neibs(Op):
= flattened version of ten4[i,j,l:l+r,k:k+c] = flattened version of ten4[i,j,l:l+r,k:k+c]
idx += 1 idx += 1
(note: the op isn't necessarily implemented internally with these (note: the op isn't necessarily implemented internally with these
for loops, they're just the easiest way to describe the output pattern) for loops, they're just the easiest way to describe the output
pattern)
""" """
ten4 = T.as_tensor_variable(ten4) ten4 = T.as_tensor_variable(ten4)
neib_shape = T.as_tensor_variable(neib_shape) neib_shape = T.as_tensor_variable(neib_shape)
...@@ -100,6 +108,103 @@ class Images2Neibs(Op): ...@@ -100,6 +108,103 @@ class Images2Neibs(Op):
def c_code_cache_version(self): def c_code_cache_version(self):
return (5,) return (5,)
def perform(self, node, inp, out_):
ten4, neib_shape, neib_step = inp
z, = out_
def CEIL_INTDIV(a, b):
if a % b:
return (a // b) + 1
else:
return a // b
grid_c = -1 # number of patch in height
grid_d = -1 # number of patch in width
assert ten4.ndim == 4
assert neib_shape.ndim == 1
assert neib_shape.shape[0] == 2
assert neib_step.ndim == 1
assert neib_step.shape[0] == 2
c, d = neib_shape
step_x, step_y = neib_step
mode = self.mode
if mode == "wrap_centered":
if (c % 2 != 1) or (d % 2 != 1):
raise TypeError(
"Images2Neibs:"
" in mode wrap_centered need patch with odd shapes")
if (ten4.shape[2] < c) or (ten4.shape[3] < d):
raise TypeError(
"Images2Neibs: in wrap_centered mode, don't support"
" image shapes smaller then the patch shapes:"
" neib_shape=(%d,%d), ten4[2:]=[%d,%d]" %
(c, d, ten4.shape[2], ten4.shape[3]))
grid_c = CEIL_INTDIV(ten4.shape[2], step_x)
grid_d = CEIL_INTDIV(ten4.shape[3], step_y)
elif mode == "valid":
if (ten4.shape[2] < c) or (((ten4.shape[2] - c) % step_x) != 0):
raise TypeError(
"neib_shape[0]=%d, neib_step[0]=%d and"
" ten4.shape[2]=%d not consistent" %
(c, step_x, ten4.shape[2]))
if (ten4.shape[3] < d) or (((ten4.shape[3] - d) % step_y) != 0):
raise TypeError(
"neib_shape[1]=%d, neib_step[1]=%d and"
" ten4.shape[3]=%d not consistent" %
(d, step_y, ten4.shape[3]))
# number of patch in height
grid_c = 1 + ((ten4.shape[2] - c) // step_x)
# number of patch in width
grid_d = 1 + ((ten4.shape[3] - d) // step_y)
elif mode == "ignore_borders":
# number of patch in height
grid_c = 1 + ((ten4.shape[2] - c) // step_x)
# number of patch in width
grid_d = 1 + ((ten4.shape[3] - d) // step_y)
else:
raise TypeError("Images2Neibs: unknow mode '%s'" % mode)
z_dim0 = grid_c * grid_d * ten4.shape[1] * ten4.shape[0]
z_dim1 = c * d
z[0] = numpy.empty((z_dim0, z_dim1), dtype=node.outputs[0].dtype)
nb_batch = ten4.shape[0]
nb_stack = ten4.shape[1]
height = ten4.shape[2]
width = ten4.shape[3]
wrap_centered_idx_shift_x = c // 2
wrap_centered_idx_shift_y = d // 2
for n in range(nb_batch):
for s in range(nb_stack):
# loop over the number of patch in height
for a in range(grid_c):
# loop over the number of patch in width
for b in range(grid_d):
z_row = b + grid_d * (a + grid_c * (s + nb_stack * n))
for i in range(c):
ten4_2 = i + a * step_x
if mode == "wrap_centered":
ten4_2 -= wrap_centered_idx_shift_x
if ten4_2 < 0:
ten4_2 += height
elif ten4_2 >= height:
ten4_2 -= height
for j in range(d):
ten4_3 = j + b * step_y
if mode == "wrap_centered":
ten4_3 -= wrap_centered_idx_shift_y
if ten4_3 < 0:
ten4_3 += width
elif ten4_3 >= width:
ten4_3 -= width
z_col = j + d * i
z[0][z_row, z_col] = ten4[n, s, ten4_2, ten4_3]
def c_code(self, node, name, inp, out, sub): def c_code(self, node, name, inp, out, sub):
ten4, neib_shape, neib_step = inp ten4, neib_shape, neib_step = inp
z, = out z, = out
...@@ -151,38 +256,59 @@ class Images2Neibs(Op): ...@@ -151,38 +256,59 @@ class Images2Neibs(Op):
if ( "%(mode)s" == "wrap_centered") { if ( "%(mode)s" == "wrap_centered") {
if (c%%2!=1 || d%%2!=1){ if (c%%2!=1 || d%%2!=1){
PyErr_Format(PyExc_TypeError, "Images2Neibs: in mode wrap_centered need patch with odd shapes"); PyErr_Format(PyExc_TypeError,
"Images2Neibs: in mode wrap_centered"
" need patch with odd shapes");
%(fail)s; %(fail)s;
} }
if ( (PyArray_DIMS(%(ten4)s))[2] < c || (PyArray_DIMS(%(ten4)s))[3] < d) if ( (PyArray_DIMS(%(ten4)s))[2] < c ||
(PyArray_DIMS(%(ten4)s))[3] < d)
{ {
PyErr_Format(PyExc_TypeError, "Images2Neibs: in wrap_centered mode, don't support image shapes smaller then the patch shapes: neib_shape=(%%ld,%%ld), ten4[2:]=[%%ld,%%ld]", PyErr_Format(PyExc_TypeError,
(long int)c, (long int)d, (long int)(PyArray_DIMS(%(ten4)s)[2]), (long int)(PyArray_DIMS(%(ten4)s)[3])); "Images2Neibs: in wrap_centered mode, don't support image"
" shapes smaller then the patch shapes:"
" neib_shape=(%%ld,%%ld), ten4[2:]=[%%ld,%%ld]",
(long int)c, (long int)d,
(long int)(PyArray_DIMS(%(ten4)s)[2]),
(long int)(PyArray_DIMS(%(ten4)s)[3]));
%(fail)s; %(fail)s;
} }
grid_c = CEIL_INTDIV(((PyArray_DIMS(%(ten4)s))[2]),step_x); grid_c = CEIL_INTDIV(((PyArray_DIMS(%(ten4)s))[2]),step_x);
grid_d = CEIL_INTDIV(((PyArray_DIMS(%(ten4)s))[3]),step_y); grid_d = CEIL_INTDIV(((PyArray_DIMS(%(ten4)s))[3]),step_y);
}else if ( "%(mode)s" == "valid") { }else if ( "%(mode)s" == "valid") {
if ( ((PyArray_DIMS(%(ten4)s))[2] < c) ||( (((PyArray_DIMS(%(ten4)s))[2]-c) %% step_x)!=0)) if ( ((PyArray_DIMS(%(ten4)s))[2] < c) ||
( (((PyArray_DIMS(%(ten4)s))[2]-c) %% step_x)!=0))
{ {
PyErr_Format(PyExc_TypeError, "neib_shape[0]=%%ld, neib_step[0]=%%ld and ten4.shape[2]=%%ld not consistent", PyErr_Format(PyExc_TypeError,
(long int)c, (long int)step_x, (long int)(PyArray_DIMS(%(ten4)s)[2])); "neib_shape[0]=%%ld, neib_step[0]=%%ld and"
" ten4.shape[2]=%%ld not consistent",
(long int)c, (long int)step_x,
(long int)(PyArray_DIMS(%(ten4)s)[2]));
%(fail)s; %(fail)s;
} }
if ( ((PyArray_DIMS(%(ten4)s))[3] < d) ||( (((PyArray_DIMS(%(ten4)s))[3]-d) %% step_y)!=0)) if ( ((PyArray_DIMS(%(ten4)s))[3] < d) ||
( (((PyArray_DIMS(%(ten4)s))[3]-d) %% step_y)!=0))
{ {
PyErr_Format(PyExc_TypeError, "neib_shape[1]=%%ld, neib_step[1]=%%ld and ten4.shape[3]=%%ld not consistent", PyErr_Format(PyExc_TypeError,
(long int)d, (long int)step_y, (long int)(PyArray_DIMS(%(ten4)s)[3])); "neib_shape[1]=%%ld, neib_step[1]=%%ld and"
" ten4.shape[3]=%%ld not consistent",
(long int)d, (long int)step_y,
(long int)(PyArray_DIMS(%(ten4)s)[3]));
%(fail)s; %(fail)s;
} }
grid_c = 1+(((PyArray_DIMS(%(ten4)s))[2]-c)/step_x); //number of patch in height //number of patch in height
grid_d = 1+(((PyArray_DIMS(%(ten4)s))[3]-d)/step_y); //number of patch in width grid_c = 1+(((PyArray_DIMS(%(ten4)s))[2]-c)/step_x);
//number of patch in width
grid_d = 1+(((PyArray_DIMS(%(ten4)s))[3]-d)/step_y);
}else if ( "%(mode)s" == "ignore_borders") { }else if ( "%(mode)s" == "ignore_borders") {
grid_c = 1+(((PyArray_DIMS(%(ten4)s))[2]-c)/step_x); //number of patch in height //number of patch in height
grid_d = 1+(((PyArray_DIMS(%(ten4)s))[3]-d)/step_y); //number of patch in width grid_c = 1+(((PyArray_DIMS(%(ten4)s))[2]-c)/step_x);
//number of patch in width
grid_d = 1+(((PyArray_DIMS(%(ten4)s))[3]-d)/step_y);
}else{ }else{
PyErr_Format(PyExc_TypeError, "Images2Neibs: unknow mode '%(mode)s'"); PyErr_Format(PyExc_TypeError,
"Images2Neibs: unknow mode '%(mode)s'");
%(fail)s; %(fail)s;
} }
...@@ -261,7 +387,8 @@ class Images2Neibs(Op): ...@@ -261,7 +387,8 @@ class Images2Neibs(Op):
dtype_%(z)s* curr_z = (dtype_%(z)s*) PyArray_GETPTR2(%(z)s, z_row, z_col); dtype_%(z)s* curr_z = (dtype_%(z)s*) PyArray_GETPTR2(%(z)s, z_row, z_col);
*curr_z = *( (dtype_%(ten4)s*) PyArray_GETPTR4(%(ten4)s, n, s, ten4_2, ten4_3)); *curr_z = *( (dtype_%(ten4)s*) PyArray_GETPTR4(%(ten4)s, n, s, ten4_2, ten4_3));
//printf("\\n(%%i,%%i,%%i,%%i) --> (%%i,%%i)",n,s, ten4_2, ten4_3, z_row, z_col); //printf("\\n(%%i,%%i,%%i,%%i) --> (%%i,%%i)",
// n, s, ten4_2, ten4_3, z_row, z_col);
//printf("%%f ", *curr_z); //printf("%%f ", *curr_z);
} }
} }
......
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
import numpy import numpy
import unittest
import theano import theano
from theano import shared, function from theano import shared, function
...@@ -9,14 +10,7 @@ from neighbours import images2neibs, neibs2images, Images2Neibs ...@@ -9,14 +10,7 @@ from neighbours import images2neibs, neibs2images, Images2Neibs
from theano.tests import unittest_tools from theano.tests import unittest_tools
if theano.config.mode == 'FAST_COMPILE': mode_without_gpu = theano.compile.mode.get_default_mode().excluding('gpu')
mode_without_gpu = theano.compile.mode.get_mode(
'FAST_RUN').excluding('gpu')
else:
mode_without_gpu = theano.compile.mode.get_default_mode().excluding('gpu')
if not theano.config.cxx:
raise SkipTest("G++ not available, so we need to skip this test.")
class T_Images2Neibs(unittest_tools.InferShapeTester): class T_Images2Neibs(unittest_tools.InferShapeTester):
...@@ -25,10 +19,10 @@ class T_Images2Neibs(unittest_tools.InferShapeTester): ...@@ -25,10 +19,10 @@ class T_Images2Neibs(unittest_tools.InferShapeTester):
dtypes = ['int64', 'float32', 'float64'] dtypes = ['int64', 'float32', 'float64']
def test_neibs(self): def test_neibs(self):
for shape, pshape in [((100, 40, 18, 18), (2, 2)), for shape, pshape in [((10, 7, 18, 18), (2, 2)),
((100, 40, 6, 18), (3, 2)), ((10, 7, 6, 18), (3, 2)),
((10, 40, 66, 66), (33, 33)), ((5, 7, 66, 66), (33, 33)),
((10, 40, 68, 66), (34, 33)) ((5, 7, 68, 66), (34, 33))
]: ]:
for border in ['valid', 'ignore_borders']: for border in ['valid', 'ignore_borders']:
for dtype in self.dtypes: for dtype in self.dtypes:
...@@ -69,7 +63,8 @@ class T_Images2Neibs(unittest_tools.InferShapeTester): ...@@ -69,7 +63,8 @@ class T_Images2Neibs(unittest_tools.InferShapeTester):
#print images.get_value(borrow=True) #print images.get_value(borrow=True)
neibs = f() neibs = f()
#print neibs #print neibs
assert numpy.allclose(neibs,[[ 0, 1, 4, 5], assert numpy.allclose(neibs,
[[ 0, 1, 4, 5],
[ 2, 3, 6, 7], [ 2, 3, 6, 7],
[ 8, 9, 12, 13], [ 8, 9, 12, 13],
[10, 11, 14, 15], [10, 11, 14, 15],
...@@ -274,7 +269,8 @@ class T_Images2Neibs(unittest_tools.InferShapeTester): ...@@ -274,7 +269,8 @@ class T_Images2Neibs(unittest_tools.InferShapeTester):
images = shared(numpy.arange(numpy.prod(shape)).reshape(shape)) images = shared(numpy.arange(numpy.prod(shape)).reshape(shape))
neib_shape = T.as_tensor_variable((3, 3)) neib_shape = T.as_tensor_variable((3, 3))
f = function([], images2neibs(images, neib_shape, mode="wrap_centered"), f = function([],
images2neibs(images, neib_shape, mode="wrap_centered"),
mode=self.mode) mode=self.mode)
f() f()
...@@ -289,7 +285,6 @@ class T_Images2Neibs(unittest_tools.InferShapeTester): ...@@ -289,7 +285,6 @@ class T_Images2Neibs(unittest_tools.InferShapeTester):
self.assertRaises(TypeError, unittest_tools.verify_grad, self.assertRaises(TypeError, unittest_tools.verify_grad,
fn, [images_val], mode=self.mode) fn, [images_val], mode=self.mode)
def test_grad_valid(self): def test_grad_valid(self):
shape = (2, 3, 4, 4) shape = (2, 3, 4, 4)
images_val = numpy.random.rand(*shape).astype('float32') images_val = numpy.random.rand(*shape).astype('float32')
...@@ -310,7 +305,6 @@ class T_Images2Neibs(unittest_tools.InferShapeTester): ...@@ -310,7 +305,6 @@ class T_Images2Neibs(unittest_tools.InferShapeTester):
def test_grad_ignore_border(self): def test_grad_ignore_border(self):
shape = (2, 3, 5, 5) shape = (2, 3, 5, 5)
images = T.dtensor4()
images_val = numpy.random.rand(*shape).astype('float32') images_val = numpy.random.rand(*shape).astype('float32')
def fn(images): def fn(images):
...@@ -321,13 +315,12 @@ class T_Images2Neibs(unittest_tools.InferShapeTester): ...@@ -321,13 +315,12 @@ class T_Images2Neibs(unittest_tools.InferShapeTester):
eps=0.1) eps=0.1)
def test_neibs2images_grad(self): def test_neibs2images_grad(self):
# say we had images of size (2, 3, 20, 20) # say we had images of size (2, 3, 10, 10)
# then we extracted 2x2 neighbors on this, we get (2 * 3 * 10 * 10, 4) # then we extracted 2x2 neighbors on this, we get (2 * 3 * 5 * 5, 4)
neibs = T.dmatrix() neibs_val = numpy.random.rand(150, 4)
neibs_val = numpy.random.rand(600, 4)
def fn(neibs): def fn(neibs):
return neibs2images(neibs, (2, 2), (2, 3, 20, 20)) return neibs2images(neibs, (2, 2), (2, 3, 10, 10))
unittest_tools.verify_grad(fn, [neibs_val], mode=self.mode, unittest_tools.verify_grad(fn, [neibs_val], mode=self.mode,
eps=0.1) eps=0.1)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论