Example #1
0
def test_pack_padded_long_sequence_forward_backward(total_length, padding_value,
                                                    batch_first, shapes, seed, ctx, func_name):
    if not func_name.endswith("Cuda"):
        pytest.skip(
            "PackPaddedSequence tests except for Cuda for very long sequence skips.")

    from nbla_test_utils import function_tester
    rng = np.random.RandomState(seed)

    sequences = [rng.randn(*shape).astype(np.float32) for shape in shapes]
    padded_sequence = pad_sequence(sequences, batch_first)
    lengths = np.array([seq.shape[0] for seq in sequences])
    inputs = [padded_sequence, lengths]
    func_args0 = [batch_first]
    func_args1 = [batch_first, padding_value, total_length]
    insert_identity = [True, False]

    # Forward
    function_tester(rng, F.pack_padded_sequence, ref_pack_padded_sequence, inputs,
                    ctx=ctx, func_name=func_name, func_args=func_args0,
                    backward=[False, False],
                    atol_f=1e-3, atol_b=1e-2, insert_identity=insert_identity)

    # Backward
    import nnabla as nn
    padded_sequence0 = nn.Variable.from_numpy_array(
        inputs[0]).apply(need_grad=True)
    lengths = nn.Variable.from_numpy_array(inputs[1])
    with nn.context_scope(ctx), nn.auto_forward():
        # Pack backward
        padded_sequence0.g = rng.randn(*padded_sequence0.shape)
        packed_sequence0, batch_sizes = F.pack_padded_sequence(
            padded_sequence0, lengths, *func_args0)
        g = rng.randn(*packed_sequence0.shape)
        packed_sequence0.g = g
        packed_sequence0.parent.backward([padded_sequence0, lengths], [packed_sequence0, batch_sizes],
                                         [False, False])
        # Unpack
        packed_sequence1 = nn.Variable.from_numpy_array(g)
        padded_sequence1, lengths = F.pad_packed_sequence(
            packed_sequence1, batch_sizes, *func_args1)
        # Compare w/o accum
        np.testing.assert_allclose(padded_sequence0.g.flatten(),
                                   padded_sequence1.d.flatten(
                                   )[:np.prod(padded_sequence0.shape)],
                                   atol=1e-4,
                                   err_msg="{} test (w/o accum) with long sequence failed.".format(func_name))
        # Compare w/ accum
        packed_sequence0.parent.backward([padded_sequence0, lengths], [packed_sequence0, batch_sizes],
                                         [True, False])
        np.testing.assert_allclose(padded_sequence0.g.flatten() / 2,
                                   padded_sequence1.d.flatten(
                                   )[:np.prod(padded_sequence0.shape)],
                                   atol=1e-4,
                                   err_msg="{} test (w/ accum) with long sequence failed.".format(func_name))
Example #2
0
def pad_packed_sequence(sequence,
                        batch_first=False,
                        padding_value=0.0,
                        total_length=None):
    """Pad packed sequence.

    This method unpacks the packed sequqnce and pad it, the inverse operation of :func:`pack_padded_sequence`.

    :math:`T_i` is the length of the :math:`i`-th Variable in the sequences.
    :math:`B` is the batch size equal to the length of the sequences. 
    :math:`T` is the max of :math:`T_i` for all :math:`i`. 
    :math:`*` is the remaining dimensions including none.

    .. note::
      This function **must** be used the dynamic computation mode.

    Example:

    .. code-block:: python

      import numpy as np
      import nnabla as nn
      import nnabla.functions as F
      import nnabla.utils.rnn as rnn_utils

      nn.set_auto_forward(True)

      l2v = lambda ldata: nn.Variable.from_numpy_array(np.asarray(ldata))
      a = l2v([3, 3])
      b = l2v([2, 2, 2])
      c = l2v([2, 2, 2])
      d = l2v([1, 1, 1, 1])
      e = l2v([3, 3])
      sequences = [a, b, c, d, e]

      packed_sequence = rnn_utils.pack_sequence(sequences, enforce_sorted=False)
      print(packed_sequence.data.d)
      print(packed_sequence.batch_sizes.d)

      padded_sequence, lengths = rnn_utils.pad_packed_sequence(packed_sequence)
      print(padded_sequence.d)
      print(lengths.d)

    Args: 
      sequence (:obj:`PackedSequence`): PackedSequence.
      batch_first (bool): If False, output is of (:math:`T`, :math:`B`, :math:`*`) shape,
                          otherwise (:math:`B`, :math:`T`, :math:`*`).
      padding_value (float): Padding value.
      total_length (int): If not None, the outputs are padded up to the `total_length`.
                          If the `total_length` is less than the max length in the `sequences`,
                          the error is thrown.
                          This is normally used in the distributed training to align with 
                          the longest sequence in a distributed system.

    Returns:
      :obj:`nnabla.Variable` of (:math:`T`, :math:`B`, :math:`*`) or (:math:`B`, :math:`T`, :math:`*`) shape
    """
    packed_sequence = sequence.data
    batch_sizes = sequence.batch_sizes
    sorted_indices = sequence.sorted_indices
    unsorted_indices = sequence.unsorted_indices

    T = batch_sizes.shape[0]
    if total_length is not None:
        if total_length < T:
            raise ValueError(
                "`total length ({})` must be greater than or equal to the maximum length ({})."
                .format(total_length, T))

    padded_sequence, lengths = F.pad_packed_sequence(packed_sequence,
                                                     batch_sizes, batch_first,
                                                     padding_value,
                                                     total_length)
    if unsorted_indices is not None:
        axis = 0 if batch_first else 1
        padded_sequence = F.gather(padded_sequence, unsorted_indices, axis)
        lengths = lengths[unsorted_indices]
    return padded_sequence, lengths