Exemplo n.º 1
0
def theano_dtw(batch, compiled_dtw, pack_batch=True):
    """
    Calls the provided compiled Theano DTW function using the appropriate mechanims (batching inputs when supported else
    submitted the sequence pairs one-at-a-time).

    :param batch: The sequence pairs whose DTW distances are to be calculated.
    :param compiled_dtw: The compiled DTW function to use.
    :param pack_batch: Whether the batch should be packed or not (i.e. the input dimensionality supported by
                       compiled_dtw). If pack_batch=True then compiled_dtw must accept 3D (batched) inputs else
                       compiled_dtw must accept 2D (non-batched) inputs.
    :return: The DTW distances between all the sequence pairs in batch.
    """

    assert compiled_dtw is not None
    assert isinstance(pack_batch, bool)
    dtype = utility.get_standard_dtype()

    def dtw(validated_batch):
        if pack_batch:
            # A validated batch input to this method is still in "list of pairs" format. To use the batched DTW function
            # we must pack those pairs into a single 3D tensor. The tensor size is determined by the maximum sequence
            # lengths on each side of the pairs. All shorter sequences are zero padded to fill the tensor. An integer
            # array is constructed to inform the DTW function how long the sequences are on each side.
            max_lengths = [
                max(lengths)
                for lengths in zip(*((len(x1), len(x2))
                                     for x1, x2 in validated_batch))
            ]
            batch_size = len(validated_batch)
            input_size = validated_batch[0][0].shape[1]
            packed_x1 = numpy.zeros((max_lengths[0], batch_size, input_size),
                                    dtype=dtype)
            packed_x2 = numpy.zeros((max_lengths[1], batch_size, input_size),
                                    dtype=dtype)
            x1_lengths = numpy.empty(batch_size, dtype='int32')
            x2_lengths = numpy.empty(batch_size, dtype='int32')

            for index, (x1, x2) in enumerate(validated_batch):
                packed_x1[:len(x1), index, :] = x1
                packed_x2[:len(x2), index, :] = x2
                x1_lengths[index] = len(x1)
                x2_lengths[index] = len(x2)

            outputs = compiled_dtw(packed_x1, packed_x2, x1_lengths,
                                   x2_lengths)
            _check_outputs(outputs)
            return [float(output) for output in outputs[0]]

        # If we're not packing the batch then we need to submit them one at a time.
        results = []
        for x1, x2 in validated_batch:
            outputs = compiled_dtw(x1, x2, len(x1), len(x2))
            _check_outputs(outputs)
            results.append(float(outputs[0]))
        return results

    return _common_dtw(batch, dtw)
Exemplo n.º 2
0
def theano_symbolic_dtw(x1, x2, x1_lengths, x2_lengths, distance_function=cosine, normalize=True, debug_level=None,
                        eps=None):
    """
    A symbolic implementation of DTW that supports batches of sequence pairs.

    Returns a scalar if ndim == 2 and a vector of size x1.shape[1] if ndim == 3

    This is slow! About 90 times slower than the Cython implementation using the parameters below.

    :param x1: A tensor containing the first side of the sequence pairs to be aligned.
    :param x2: A tensor containing the second side of the sequence pairs to be aligned.
    :param x1_lengths: An integer vector identifying the lengths of the sequences in x1
    :param x2_lengths: An integer vector identifying the lengths of the sequences in x2
    :param distance_function: The symbolic distance function to use (e.g. a reference to a function in
                              distance).
    :param normalize: Whether the DTW distances should be sequence length normalized.
    :param debug_level: The debug level to use (see above for explanation).
    :param eps: The minimum value to use inside the distance function. Set to the machine epsilon if None.
    :return: The DTW distances for every sequence pair in the batch.
    """

    if eps is None:
        eps = numpy.dtype(theano.config.floatX).type(numpy.finfo(float).eps)

    assert 0 <= x1_lengths.ndim == x2_lengths.ndim <= 1
    assert isinstance(normalize, bool)

    ndim = x1.ndim
    assert 2 <= ndim == x2.ndim <= 3

    # Ensure x2 is the shorter input to minimize the number of scan iterations
    x1_shorter_than_x2 = tt.le(x1.shape[0], x2.shape[0])
    x1, x2 = _swap(x1_shorter_than_x2, x1, x2, 'x1', 'x2', debug_level)
    x1_lengths, x2_lengths = _swap(x1_shorter_than_x2, x1_lengths, x2_lengths, 'x1_lengths', 'x2_lengths', debug_level)

    # Compute distances between x1 sequences and paired x2 sequences
    d = distance_function(x1, x2, eps)

    # Iterate over the temporal slices of x2. See dtw_outer_step for an explanation of the other inputs to this scan
    # operation
    x1_indexes = tt.arange(x1.shape[0], dtype=DTYPE_INT64)
    results, _ = theano.scan(_create_dtw_outer_step(distance_function, debug_level), sequences=[x1_indexes, d],
                             outputs_info=[
                                 tt.zeros_like(x2[:, :, 0] if x2.ndim == 3 else x2[:, 0], dtype=theano.config.floatX)],
                             non_sequences=[x1_lengths, x2_lengths])
    result = results[x1_lengths - 1, x2_lengths - 1, tt.arange(x1.shape[1])] if x2.ndim == 3 else results[
        x1_lengths - 1, x2_lengths - 1]
    result = _debug(result, 'theano_symbolic_dtw.result', debug_level)
    assert result.ndim == x1_lengths.ndim

    # Length normalize the distances if requested to do so
    if normalize:
        result = _debug(result / tt.cast(x1_lengths + x2_lengths, dtype=utility.get_standard_dtype()),
                        'theano_symbolic_dtw.norm_result', debug_level)

    return result
Exemplo n.º 3
0
def theano_dtw(batch, compiled_dtw, pack_batch=True):
    """
    Calls the provided compiled Theano DTW function using the appropriate mechanims (batching inputs when supported else
    submitted the sequence pairs one-at-a-time).

    :param batch: The sequence pairs whose DTW distances are to be calculated.
    :param compiled_dtw: The compiled DTW function to use.
    :param pack_batch: Whether the batch should be packed or not (i.e. the input dimensionality supported by
                       compiled_dtw). If pack_batch=True then compiled_dtw must accept 3D (batched) inputs else
                       compiled_dtw must accept 2D (non-batched) inputs.
    :return: The DTW distances between all the sequence pairs in batch.
    """

    assert compiled_dtw is not None
    assert isinstance(pack_batch, bool)
    dtype = utility.get_standard_dtype()

    def dtw(validated_batch):
        if pack_batch:
            # A validated batch input to this method is still in "list of pairs" format. To use the batched DTW function
            # we must pack those pairs into a single 3D tensor. The tensor size is determined by the maximum sequence
            # lengths on each side of the pairs. All shorter sequences are zero padded to fill the tensor. An integer
            # array is constructed to inform the DTW function how long the sequences are on each side.
            max_lengths = [max(lengths) for lengths in zip(*((len(x1), len(x2)) for x1, x2 in validated_batch))]
            batch_size = len(validated_batch)
            input_size = validated_batch[0][0].shape[1]
            packed_x1 = numpy.zeros((max_lengths[0], batch_size, input_size), dtype=dtype)
            packed_x2 = numpy.zeros((max_lengths[1], batch_size, input_size), dtype=dtype)
            x1_lengths = numpy.empty(batch_size, dtype='int32')
            x2_lengths = numpy.empty(batch_size, dtype='int32')

            for index, (x1, x2) in enumerate(validated_batch):
                packed_x1[:len(x1), index, :] = x1
                packed_x2[:len(x2), index, :] = x2
                x1_lengths[index] = len(x1)
                x2_lengths[index] = len(x2)

            outputs = compiled_dtw(packed_x1, packed_x2, x1_lengths, x2_lengths)
            _check_outputs(outputs)
            return [float(output) for output in outputs[0]]

        # If we're not packing the batch then we need to submit them one at a time.
        results = []
        for x1, x2 in validated_batch:
            outputs = compiled_dtw(x1, x2, len(x1), len(x2))
            _check_outputs(outputs)
            results.append(float(outputs[0]))
        return results

    return _common_dtw(batch, dtw)
Exemplo n.º 4
0
def _var(name,
         test_value_shape,
         debug_name_prefix,
         debug_level,
         dtype=None,
         test_value_getter=lambda shape: numpy.random.randn(*shape)):
    """
    Creates a new symbolic variable with the given name and generates a synthetic test value of the requested shape. The
    resulting Theano variable is wrapped in a Debug operation.

    :param name: The name of the variable to be created.
    :param test_value_shape: The shape of the test value.
    :param debug_name_prefix: Used in naming the Debug operation.
    :param debug_level: The debug level to use (see above for explanation).
    :param dtype: The type of the variable being created. Defaults to whatever is returned by
                  utility.get_standard_dtype.
    :param test_value_getter: A method for generating test values. Defaults to zero mean unit variance Gaussian values.
    :return: The newly created Theano variable.
    """

    if dtype is None:
        dtype = utility.get_standard_dtype()

    if len(test_value_shape) == 0:
        x = tt.scalar(name, dtype=dtype)
    elif len(test_value_shape) == 1:
        x = tt.vector(name, dtype=dtype)
    elif len(test_value_shape) == 2:
        x = tt.matrix(name, dtype=dtype)
    elif len(test_value_shape) == 3:
        x = tt.tensor3(name, dtype=dtype)
    else:
        raise Exception('Unsupported number of dimensions: ' +
                        str(len(test_value_shape)))

    if debug_level > 0:
        x.tag.test_value = test_value_getter(test_value_shape)

        if len(test_value_shape) == 0:
            x.tag.test_value = numpy.dtype(dtype).type(x.tag.test_value)
        else:
            x.tag.test_value = x.tag.test_value.astype(dtype)

    return x, _debug(x, '%s.%s' % (debug_name_prefix, name), debug_level)
Exemplo n.º 5
0
def _var(name, test_value_shape, debug_name_prefix, debug_level, dtype=None,
         test_value_getter=lambda shape: numpy.random.randn(*shape)):
    """
    Creates a new symbolic variable with the given name and generates a synthetic test value of the requested shape. The
    resulting Theano variable is wrapped in a Debug operation.

    :param name: The name of the variable to be created.
    :param test_value_shape: The shape of the test value.
    :param debug_name_prefix: Used in naming the Debug operation.
    :param debug_level: The debug level to use (see above for explanation).
    :param dtype: The type of the variable being created. Defaults to whatever is returned by
                  utility.get_standard_dtype.
    :param test_value_getter: A method for generating test values. Defaults to zero mean unit variance Gaussian values.
    :return: The newly created Theano variable.
    """

    if dtype is None:
        dtype = utility.get_standard_dtype()

    if len(test_value_shape) == 0:
        x = tt.scalar(name, dtype=dtype)
    elif len(test_value_shape) == 1:
        x = tt.vector(name, dtype=dtype)
    elif len(test_value_shape) == 2:
        x = tt.matrix(name, dtype=dtype)
    elif len(test_value_shape) == 3:
        x = tt.tensor3(name, dtype=dtype)
    else:
        raise Exception('Unsupported number of dimensions: ' + str(len(test_value_shape)))

    if debug_level > 0:
        x.tag.test_value = test_value_getter(test_value_shape)

        if len(test_value_shape) == 0:
            x.tag.test_value = numpy.dtype(dtype).type(x.tag.test_value)
        else:
            x.tag.test_value = x.tag.test_value.astype(dtype)

    return x, _debug(x, '%s.%s' % (debug_name_prefix, name), debug_level)
Exemplo n.º 6
0
def theano_symbolic_dtw(x1,
                        x2,
                        x1_lengths,
                        x2_lengths,
                        distance_function=cosine,
                        normalize=True,
                        debug_level=None,
                        eps=None):
    """
    A symbolic implementation of DTW that supports batches of sequence pairs.

    Returns a scalar if ndim == 2 and a vector of size x1.shape[1] if ndim == 3

    This is slow! About 90 times slower than the Cython implementation using the parameters below.

    :param x1: A tensor containing the first side of the sequence pairs to be aligned.
    :param x2: A tensor containing the second side of the sequence pairs to be aligned.
    :param x1_lengths: An integer vector identifying the lengths of the sequences in x1
    :param x2_lengths: An integer vector identifying the lengths of the sequences in x2
    :param distance_function: The symbolic distance function to use (e.g. a reference to a function in
                              distance).
    :param normalize: Whether the DTW distances should be sequence length normalized.
    :param debug_level: The debug level to use (see above for explanation).
    :param eps: The minimum value to use inside the distance function. Set to the machine epsilon if None.
    :return: The DTW distances for every sequence pair in the batch.
    """

    if eps is None:
        eps = numpy.dtype(theano.config.floatX).type(numpy.finfo(float).eps)

    assert 0 <= x1_lengths.ndim == x2_lengths.ndim <= 1
    assert isinstance(normalize, bool)

    ndim = x1.ndim
    assert 2 <= ndim == x2.ndim <= 3

    # Ensure x2 is the shorter input to minimize the number of scan iterations
    x1_shorter_than_x2 = tt.le(x1.shape[0], x2.shape[0])
    x1, x2 = _swap(x1_shorter_than_x2, x1, x2, 'x1', 'x2', debug_level)
    x1_lengths, x2_lengths = _swap(x1_shorter_than_x2, x1_lengths, x2_lengths,
                                   'x1_lengths', 'x2_lengths', debug_level)

    # Compute distances between x1 sequences and paired x2 sequences
    d = distance_function(x1, x2, eps)

    # Iterate over the temporal slices of x2. See dtw_outer_step for an explanation of the other inputs to this scan
    # operation
    x1_indexes = tt.arange(x1.shape[0], dtype=DTYPE_INT64)
    results, _ = theano.scan(
        _create_dtw_outer_step(distance_function, debug_level),
        sequences=[x1_indexes, d],
        outputs_info=[
            tt.zeros_like(x2[:, :, 0] if x2.ndim == 3 else x2[:, 0],
                          dtype=theano.config.floatX)
        ],
        non_sequences=[x1_lengths, x2_lengths])
    result = results[x1_lengths - 1, x2_lengths - 1,
                     tt.arange(x1.shape[1])] if x2.ndim == 3 else results[
                         x1_lengths - 1, x2_lengths - 1]
    result = _debug(result, 'theano_symbolic_dtw.result', debug_level)
    assert result.ndim == x1_lengths.ndim

    # Length normalize the distances if requested to do so
    if normalize:
        result = _debug(
            result / tt.cast(x1_lengths + x2_lengths,
                             dtype=utility.get_standard_dtype()),
            'theano_symbolic_dtw.norm_result', debug_level)

    return result