Skip to content

Optimise masking for streaming (code from Samsung AI Center Cambridge) - #2426

Merged
mravanelli merged 33 commits into
speechbrain:developfrom
TParcollet:optimise_augmentation
Feb 24, 2024
Merged

mravanelli merged 33 commits into
speechbrain:developfrom
TParcollet:optimise_augmentation

Conversation

@TParcollet

@TParcollet TParcollet commented Feb 21, 2024

Copy link
Copy Markdown
Collaborator

What does this PR do?

This PR does two things:

  1. divides by 10x 50x the time taken to generate masks for streaming training. The optimization is done by turning the loop over timesteps into a loop over chunk.s
  2. Create unit tests for all the masking functions of the transformer. We had none, which is ... no gud.

I WOULD ADVISE A REVIEWER TO RETRAIN THE MODEL for a few epochs EVEN THOUGH I ADDED TESTS

@TParcollet TParcollet added the ready to review Waiting on reviewer to provide feedback label Feb 21, 2024
@TParcollet
TParcollet requested a review from asumagic February 21, 2024 18:04
@mravanelli

Copy link
Copy Markdown
Collaborator

@asumagic, could you please proceed with the review? It would be great to include it in the upcoming release

@lucadellalib

Copy link
Copy Markdown
Collaborator

We can make it faster by removing the for loops completely:

def make_transformer_src_mask(
    src: torch.Tensor,
    causal: bool = False,
    dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
) -> Optional[torch.Tensor]:
    if causal:
        assert dynchunktrain_config is None
        return get_lookahead_mask(src)

    if dynchunktrain_config is None:
        return

    # The following is not really the sole source used to implement this,
    # but it helps introduce the concept.
    # ref: Unified Streaming and Non-streaming Two-pass End-to-end Model for Speech Recognition
    # https://arxiv.org/pdf/2012.05481.pdf
    timesteps = src.size(1)

    # Mask the future at the right of each chunk
    chunk_size = dynchunktrain_config.chunk_size
    num_chunks = timesteps // chunk_size
    timestep_idx = torch.arange(timesteps, device=src.device)
    mask_idx = torch.arange(
        chunk_size, chunk_size * (num_chunks + 2), chunk_size, device=src.device
    ).repeat_interleave(chunk_size)[:timesteps]
    src_mask = timestep_idx[None] >= mask_idx[:, None]

    # Mask the past at the left of each chunk (accounting for left context)
    # only relevant if using left context
    if not dynchunktrain_config.is_infinite_left_context():
        num_left_chunks = dynchunktrain_config.left_context_size
        mask_idx -= chunk_size * (num_left_chunks + 1)
        src_mask += timestep_idx[None] < mask_idx[:, None]

    return src_mask

Comment thread tests/unittests/test_transformer_src_tgt_masks.py
Comment thread recipes/VoxPopuli/ASR/transducer/hparams/conformer_transducer.yaml Outdated
@mravanelli

Copy link
Copy Markdown
Collaborator

I ran recipe tests (including full-inference ones) and everything works fine. Thank you @TParcollet, @Adel-Moumen, and @lucadellalib .

@mravanelli
mravanelli self-requested a review February 24, 2024 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to review Waiting on reviewer to provide feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants