提交 0d197386 authored 作者: notoraptor's avatar notoraptor

Update. Many of @abergeron comments have been taken account.

Tests have been rewritten, and they run so that a CPU computation and a GPU computation are always performed (separately) on the same input. This allow to run the tests with Theano profiling flags and then compare the execution time of MaxAndArgmax (CPU) and GpuMaxAndArgmax (GPU). Some code has also been modified in theano/tensor/basic.py, related to MaxAndArgmax, to make the API more uniform and to put most of axis checking in maxandargmax wrapper instead of in make_node functions of (Gpu)MaxAndArgmax.
上级 37115ad1
...@@ -28,7 +28,7 @@ from .type import (GpuArrayType, GpuArrayVariable, GpuArrayConstant, ...@@ -28,7 +28,7 @@ from .type import (GpuArrayType, GpuArrayVariable, GpuArrayConstant,
GpuArraySharedVariable, gpuarray_shared_constructor, GpuArraySharedVariable, gpuarray_shared_constructor,
reg_context, get_context, ContextNotDefined, _get_props) reg_context, get_context, ContextNotDefined, _get_props)
from .basic_ops import as_gpuarray_variable from .basic_ops import as_gpuarray_variable
from . import fft, dnn, opt, nerv, extra_ops, multinomial from . import fft, dnn, opt, nerv, extra_ops, multinomial, reduction
def transfer(x, target): def transfer(x, target):
try: try:
......
...@@ -65,6 +65,7 @@ from .subtensor import (GpuIncSubtensor, GpuSubtensor, ...@@ -65,6 +65,7 @@ from .subtensor import (GpuIncSubtensor, GpuSubtensor,
GpuAdvancedIncSubtensor1, GpuAdvancedIncSubtensor1,
GpuAdvancedIncSubtensor1_dev20) GpuAdvancedIncSubtensor1_dev20)
from .opt_util import alpha_merge, output_merge, pad_dims, unpad_dims from .opt_util import alpha_merge, output_merge, pad_dims, unpad_dims
from .reduction import gpu_maxandargmax
_logger = logging.getLogger("theano.gpuarray.opt") _logger = logging.getLogger("theano.gpuarray.opt")
......
from unittest import TestCase from unittest import TestCase
from theano.gpuarray import GpuArrayType
from theano.tests import unittest_tools as utt from theano.tests import unittest_tools as utt
import numpy as np import numpy as np
import theano import theano
import theano.tensor as T import theano.tensor as T
from .config import mode_with_gpu, mode_without_gpu
from .test_basic_ops import rand_gpuarray
test_shape = (1000, 100, 10, 5, 2)
def randomTensor(*shapes): def numpy_random_array(*shapes):
dimlist = shapes dimlist = shapes
size = 1 size = 1
for dimsize in dimlist: for dimsize in dimlist:
size *= dimsize size *= dimsize
return np.random.normal(size=size).astype(np.float32).reshape(dimlist) return np.random.normal(size=size).astype(theano.config.floatX).reshape(dimlist)
def numpyMaxAndArgmax(X, axis=None): def numpy_maxandargmax(X, axis=None):
if axis is None: if axis is None:
axis = range(X.ndim) axis = range(X.ndim)
elif not isinstance(axis, (tuple, list)): elif not isinstance(axis, (tuple, list)):
...@@ -33,107 +40,93 @@ def numpyMaxAndArgmax(X, axis=None): ...@@ -33,107 +40,93 @@ def numpyMaxAndArgmax(X, axis=None):
reshaped_x = transposed_x.reshape(new_shape) reshaped_x = transposed_x.reshape(new_shape)
return (ref_max, np.argmax(reshaped_x, axis=-1)) return (ref_max, np.argmax(reshaped_x, axis=-1))
# We run all tests with 5-D tensors of 10 000 000 elements.
# NB: In each test, any first call of theano function should be ignored
# with Theano config flag profiling.ignore_first_call=True.
def check_if_gpu_maxandargmax_in_graph(theano_function):
assert len([node for node in theano_function.maker.fgraph.apply_nodes
if isinstance(node.op, theano.gpuarray.reduction.GpuMaxAndArgmax)]) > 0
def check_if_gpu_maxandargmax_not_in_graph(theano_function):
assert len([node for node in theano_function.maker.fgraph.apply_nodes
if isinstance(node.op, theano.gpuarray.reduction.GpuMaxAndArgmax)]) == 0
def run_gpu_tensor5(test_matrix=None, axis=None):
M = GpuArrayType(dtype=theano.config.floatX, broadcastable=(False,) * 5)()
f = theano.function([M], [T.max(M, axis=axis), T.argmax(M, axis=axis)], name='GPU-function', mode=mode_with_gpu)
check_if_gpu_maxandargmax_in_graph(f)
if test_matrix is None:
test_matrix = rand_gpuarray(*test_shape)
f(test_matrix)
theano_max, theano_argmax = f(test_matrix)
ref_max, ref_argmax = numpy_maxandargmax(np.asarray(test_matrix), axis=axis)
utt.assert_allclose(ref_max, theano_max)
utt.assert_allclose(ref_argmax, theano_argmax)
def run_cpu_tensor5(test_matrix=None, axis=None):
M = T.tensor5()
f = theano.function([M], [T.max(M, axis=axis), T.argmax(M, axis=axis)], name='cpu-function', mode=mode_without_gpu)
check_if_gpu_maxandargmax_not_in_graph(f)
if test_matrix is None:
test_matrix = numpy_random_array(*test_shape)
f(test_matrix)
theano_max, theano_argmax = f(test_matrix)
ref_max, ref_argmax = numpy_maxandargmax(test_matrix, axis=axis)
utt.assert_allclose(ref_max, theano_max)
utt.assert_allclose(ref_argmax, theano_argmax)
def run_tensor5(axis=None):
test_cpu_matrix = numpy_random_array(*test_shape)
test_gpu_matrix = rand_gpuarray(*test_shape)
run_cpu_tensor5(test_cpu_matrix, axis)
run_gpu_tensor5(test_gpu_matrix, axis)
def test_none():
run_tensor5(None)
def test_all_axes():
run_tensor5((0, 1, 2, 3, 4))
def test_all_axes_unsorted():
run_tensor5((4, 1, 3, 0, 2))
def test_axis_1():
run_tensor5(0)
def test_axis_2():
run_tensor5(1)
def test_axis_3():
run_tensor5(2)
def test_axis_4():
run_tensor5(3)
def test_axis_5():
run_tensor5(4)
def test_2_axes():
run_tensor5((0, 3))
def test_3_axes():
run_tensor5((0, 3, 4))
class TestGpuMaxAndArgmax(TestCase): def test_4_axes():
# We run all tests with 5-D tensors of 10 000 000 elements. run_tensor5((0, 1, 2, 4))
# NB: In each test, any first call of theano function should be ignored
# with Theano config flag profiling.ignore_first_call=True.
# To just check if GpuMaxAndArgmax is called:
# $ theano-cache purge && THEANO_FLAGS=floatX=float32,device=cuda,profile=True,profiling.ignore_first_call=True \
# nosetests --verbose theano/gpuarray/tests/test_GpuMaxAndArgmax.py:TestGpuMaxAndArgmax.test_none
def _basic_test_tensor5(self, axis=None):
M = T.tensor5()
max_M = T.max(M, axis=axis)
argmax_M = T.argmax(M, axis=axis)
f = theano.function([M], [max_M, argmax_M])
test_matrix = randomTensor(1000, 100, 10, 5, 2)
f(test_matrix)
theano_max, theano_argmax = f(test_matrix)
ref_max, ref_argmax = numpyMaxAndArgmax(test_matrix, axis=axis)
utt.assert_allclose(ref_max, theano_max)
utt.assert_allclose(ref_argmax, theano_argmax)
def _basic_test_assert_equals(self, axis1, axis2):
M1 = T.tensor5()
M2 = T.tensor5()
f1 = theano.function([M1], [T.max(M1, axis=axis1), T.argmax(M1, axis=axis1)])
f2 = theano.function([M2], [T.max(M2, axis=axis2), T.argmax(M2, axis=axis2)])
test_matrix = randomTensor(1000, 100, 10, 5, 2)
f1(test_matrix)
f2(test_matrix)
theano1 = f1(test_matrix)
theano2 = f2(test_matrix)
ref1 = numpyMaxAndArgmax(test_matrix, axis1)
ref2 = numpyMaxAndArgmax(test_matrix, axis2)
utt.assert_allclose(ref1, ref2)
utt.assert_allclose(theano1, theano2)
utt.assert_allclose(ref1, theano1)
def test_none(self):
self._basic_test_tensor5(None)
def test_all_axes(self):
self._basic_test_tensor5((0, 1, 2, 3, 4))
def test_1_axe(self):
self._basic_test_tensor5(3)
def test_2_axes(self):
self._basic_test_tensor5((0, 3))
def test_3_axes(self):
self._basic_test_tensor5((0, 3, 4))
def test_4_axes(self):
self._basic_test_tensor5((0, 1, 2, 4))
def test_simple(self):
self._basic_test_tensor5(None)
self._basic_test_tensor5((0, 1, 2, 3, 4))
self._basic_test_tensor5((4, 1, 3, 2))
def test_assert_equals(self):
self._basic_test_assert_equals(None, (0, 1, 2, 3, 4))
self._basic_test_assert_equals(0, (0, 0))
self._basic_test_assert_equals((4, 1, 3, 2), (1, 2, 3, 4))
self._basic_test_assert_equals((4, 3, 2, 1, 0), None)
self._basic_test_assert_equals((1, 3, 4), (1, 4, 4, 1, 3, 1, 3, 4, 3, 1, 1, 3, 1, 4, 1, 4))
def test_simple_1_axis(self):
self._basic_test_tensor5(0)
self._basic_test_tensor5(1)
self._basic_test_tensor5(2)
self._basic_test_tensor5(3)
self._basic_test_tensor5(4)
def test_simple_2_axis(self):
self._basic_test_tensor5((0, 0))
self._basic_test_tensor5((0, 1))
self._basic_test_tensor5((0, 2))
self._basic_test_tensor5((0, 3))
self._basic_test_tensor5((0, 4))
self._basic_test_tensor5((1, 0))
self._basic_test_tensor5((1, 1))
self._basic_test_tensor5((1, 2))
self._basic_test_tensor5((1, 3))
self._basic_test_tensor5((1, 4))
self._basic_test_tensor5((2, 0))
self._basic_test_tensor5((2, 1))
self._basic_test_tensor5((2, 2))
self._basic_test_tensor5((2, 3))
self._basic_test_tensor5((2, 4))
self._basic_test_tensor5((3, 0))
self._basic_test_tensor5((3, 1))
self._basic_test_tensor5((3, 2))
self._basic_test_tensor5((3, 3))
self._basic_test_tensor5((3, 4))
self._basic_test_tensor5((4, 0))
self._basic_test_tensor5((4, 1))
self._basic_test_tensor5((4, 2))
self._basic_test_tensor5((4, 3))
self._basic_test_tensor5((4, 4))
...@@ -1186,55 +1186,28 @@ class MaxAndArgmax(Op): ...@@ -1186,55 +1186,28 @@ class MaxAndArgmax(Op):
def make_node(self, x, axis=None): def make_node(self, x, axis=None):
x = _as_tensor_variable(x) x = _as_tensor_variable(x)
if isinstance(axis, (integer_types, numpy.integer)): if axis is None:
axis = [int(axis)] axis = range(x.type.ndim)
elif isinstance(axis, numpy.ndarray) and axis.ndim == 0: elif not isinstance(axis, list):
axis = [int(axis)] raise TypeError("Axis must be a list. Got %s" % axis)
elif isinstance(axis, (tuple, list, numpy.ndarray)):
axis = [int(a) for a in axis]
if axis == list(range(x.type.ndim)):
axis = None
elif isinstance(axis, Variable):
if NoneConst.equals(axis):
axis = None
elif not isinstance(axis, TensorConstant):
raise TypeError(
"MaxAndArgmax needs a constant axis. Got %s" % axis)
else:
assert (axis.dtype.startswith("int") or
axis.dtype.startswith("uint"))
if isinstance(axis.data, (integer_types, numpy.integer)) or \
(isinstance(axis.data, numpy.ndarray) and
axis.data.ndim == 0):
axis = [int(axis.data)]
elif isinstance(axis.data, (list, numpy.ndarray)):
axis = [int(i) for i in axis.data]
# Make axis entries non-negative, and sort them # Make axis entries non-negative, and sort them
if isinstance(axis, list): for idx in xrange(len(axis)):
for idx in xrange(len(axis)): if axis[idx] < 0:
if axis[idx] < 0: axis[idx] += x.type.ndim
axis[idx] += x.type.ndim axis.sort()
axis.sort()
# Verify that axes are valid # Verify that axes are valid
all_axes = [] all_axes = []
if isinstance(axis, list): for ax in axis:
for ax in axis: if ax < 0 or ax >= x.type.ndim:
if ax < 0 or ax >= x.type.ndim: raise ValueError(
raise ValueError( 'Invalid axis: %s (the number of dimensions of the '
'Invalid axis: %s (the number of dimensions of the ' 'input is: %s)' % (ax, x.type.ndim))
'input is: %s)' % (ax, x.type.ndim)) if ax not in all_axes:
if ax not in all_axes: all_axes.append(ax)
all_axes.append(ax) axis = _as_tensor_variable(all_axes)
else: assert axis.ndim == 1
all_axes = list(range(x.ndim))
if axis is None or axis == list(range(x.type.ndim)):
axis = NoneConst.clone()
else:
axis = _as_tensor_variable(all_axes)
assert axis.ndim == 1
inputs = [x, axis] inputs = [x, axis]
# We keep the original broadcastable flags for dimensions on which # We keep the original broadcastable flags for dimensions on which
...@@ -1272,7 +1245,7 @@ class MaxAndArgmax(Op): ...@@ -1272,7 +1245,7 @@ class MaxAndArgmax(Op):
x, axis = inp x, axis = inp
max, argmax = out max, argmax = out
fail = sub["fail"] fail = sub["fail"]
if NoneConst.equals(node.inputs[1]): if NoneConst.equals(node.inputs[1]) or len(node.inputs[1].data) == node.inputs[0].ndim:
axis_code = "axis = NPY_MAXDIMS;" axis_code = "axis = NPY_MAXDIMS;"
else: else:
assert node.inputs[1].ndim == 1 assert node.inputs[1].ndim == 1
...@@ -1637,6 +1610,26 @@ def max_and_argmax(a, axis=None, keepdims=False): ...@@ -1637,6 +1610,26 @@ def max_and_argmax(a, axis=None, keepdims=False):
will broadcast correctly against the original tensor. will broadcast correctly against the original tensor.
""" """
# Check axis and convert it to a Python list of integers.
if axis is None:
axis = range(a.type.ndim)
elif (isinstance(axis, (integer_types, numpy.integer)) or
(isinstance(axis, numpy.ndarray) and axis.ndim == 0)):
axis = [int(axis)]
elif isinstance(axis, (tuple, list, numpy.ndarray)):
axis = [int(i) for i in axis]
elif isinstance(axis, Variable):
if NoneConst.equals(axis):
axis = range(a.type.ndim)
elif not isinstance(axis, TensorConstant):
raise TypeError("max and argmax computation needs a constant axis. Got %s" % axis)
else:
assert (axis.dtype.startswith("int") or axis.dtype.startswith("uint"))
if (isinstance(axis.data, (integer_types, numpy.integer)) or
(isinstance(axis.data, numpy.ndarray) and axis.data.ndim == 0)):
axis = [int(axis.data)]
elif isinstance(axis.data, (list, numpy.ndarray)):
axis = [int(i) for i in axis.data]
out, argout = _max_and_argmax(a, axis) out, argout = _max_and_argmax(a, axis)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论