Module audiocraft.utils.utils

Functions

def collate(tensors: List[torch.Tensor], dim: int = 0) ‑> Tuple[torch.Tensor, torch.Tensor]
Expand source code
def collate(tensors: tp.List[torch.Tensor], dim: int = 0) -> tp.Tuple[torch.Tensor, torch.Tensor]:
    """Get a list of tensors and collate them to a single tensor. according to the following logic:
    - `dim` specifies the time dimension which will be stacked and padded.
    - The output will contain 1 new dimension (dimension index 0) which will be the size of
    of the original list.

    Args:
        tensors (tp.List[torch.Tensor]): List of tensors to collate.
        dim (int): Dimension which will be stacked and padded.
    Returns:
        tp.Tuple[torch.Tensor, torch.Tensor]:
            torch.Tensor: Stacked and padded tensor. The output will contain 1 new dimension
                (dimension index 0) which will be the size of the original list.
            torch.Tensor: Tensor containing length of original tensor sizes (without padding).
    """
    tensors = [x.transpose(0, dim) for x in tensors]
    lens = torch.LongTensor([len(x) for x in tensors])
    padded_tensors = pad_sequence(tensors)
    padded_tensors = padded_tensors.transpose(0, 1)
    padded_tensors = padded_tensors.transpose(1, dim + 1)
    return padded_tensors, lens

Get a list of tensors and collate them to a single tensor. according to the following logic: - dim specifies the time dimension which will be stacked and padded. - The output will contain 1 new dimension (dimension index 0) which will be the size of of the original list.

Args

tensors : tp.List[torch.Tensor]
List of tensors to collate.
dim : int
Dimension which will be stacked and padded.

Returns

tp.Tuple[torch.Tensor, torch.Tensor]:
torch.Tensor
Stacked and padded tensor. The output will contain 1 new dimension (dimension index 0) which will be the size of the original list. torch.Tensor: Tensor containing length of original tensor sizes (without padding).
def construct_frame_chords(min_timestamp: int,
chord_changes: List[Tuple[float, str]],
mapping_dict: Dict,
prev_chord: str,
frame_rate: float,
segment_duration: float) ‑> List[str]
Expand source code
def construct_frame_chords(
                    min_timestamp: int,
                    chord_changes: tp.List[tp.Tuple[float, str]],
                    mapping_dict: tp.Dict,
                    prev_chord: str,
                    frame_rate: float,
                    segment_duration: float,
                    ) -> tp.List[str]:
    """ Translate symbolic chords [(start_time, tuples),...] into a frame-level int sequence"""

    frames = [
        frame / frame_rate
        for frame in range(
            min_timestamp, int(min_timestamp + segment_duration * frame_rate)
        )
    ]

    frame_chords = []
    current_chord = prev_chord

    for frame in frames:
        while chord_changes and frame >= chord_changes[0][0]:
            current_chord = chord_changes.pop(0)[1]
        current_chord = 'N' if current_chord in {None, ''} else current_chord
        frame_chords.append(mapping_dict[current_chord])

    return frame_chords

Translate symbolic chords [(start_time, tuples),…] into a frame-level int sequence

def copy_state(state: Any,
device: torch.device | str = 'cpu',
dtype: torch.dtype | None = None) ‑> Any
Expand source code
def copy_state(state: tp.Any, device: tp.Union[torch.device, str] = 'cpu',
               dtype: tp.Optional[torch.dtype] = None) -> tp.Any:
    if isinstance(state, torch.Tensor):
        if dtype is None or not state.is_floating_point():
            dtype = state.dtype
        return state.detach().to(device=device, dtype=dtype, copy=True)
    elif isinstance(state, dict):
        return {k: copy_state(v, device, dtype) for k, v in state.items()}
    elif isinstance(state, list):
        return [copy_state(v, device, dtype) for v in state]
def dict_from_config(cfg: omegaconf.dictconfig.DictConfig) ‑> dict
Expand source code
def dict_from_config(cfg: omegaconf.DictConfig) -> dict:
    """Convenience function to map an omegaconf configuration to a dictionary.

    Args:
        cfg (omegaconf.DictConfig): Original configuration to map to dict.
    Returns:
        dict: Config as dictionary object.
    """
    dct = omegaconf.OmegaConf.to_container(cfg, resolve=True)
    assert isinstance(dct, dict)
    return dct

Convenience function to map an omegaconf configuration to a dictionary.

Args

cfg : omegaconf.DictConfig
Original configuration to map to dict.

Returns

dict
Config as dictionary object.
def get_dataset_from_loader(dataloader)
Expand source code
def get_dataset_from_loader(dataloader):
    dataset = dataloader.dataset
    if isinstance(dataset, torch.utils.data.Subset):
        return dataset.dataset
    else:
        return dataset
def get_loader(dataset,
num_samples: int | None,
batch_size: int,
num_workers: int,
seed: int,
**kwargs) ‑> torch.utils.data.dataloader.DataLoader
Expand source code
def get_loader(dataset, num_samples: tp.Optional[int], batch_size: int,
               num_workers: int, seed: int, **kwargs) -> torch.utils.data.DataLoader:
    """Convenience function to load dataset into a dataloader with optional subset sampling.

    Args:
        dataset: Dataset to load.
        num_samples (Optional[int]): Number of samples to limit subset size.
        batch_size (int): Batch size.
        num_workers (int): Number of workers for data loading.
        seed (int): Random seed.
    """
    if num_samples is not None:
        dataset = random_subset(dataset, num_samples, seed)

    dataloader = flashy.distrib.loader(
        dataset,
        batch_size=batch_size,
        num_workers=num_workers,
        **kwargs
    )
    return dataloader

Convenience function to load dataset into a dataloader with optional subset sampling.

Args

dataset
Dataset to load.
num_samples : Optional[int]
Number of samples to limit subset size.
batch_size : int
Batch size.
num_workers : int
Number of workers for data loading.
seed : int
Random seed.
def get_pool_executor(num_workers: int, mp_context=None)
Expand source code
def get_pool_executor(num_workers: int, mp_context=None):
    return ProcessPoolExecutor(num_workers, mp_context) if num_workers > 1 else DummyPoolExecutor(1)
def hash_trick(word: str, vocab_size: int) ‑> int
Expand source code
def hash_trick(word: str, vocab_size: int) -> int:
    """Hash trick to pair each word with an index

    Args:
        word (str): word we wish to convert to an index
        vocab_size (int): size of the vocabulary
    Returns:
        int: index of the word in the embedding LUT
    """
    hash = int(hashlib.sha256(word.encode("utf-8")).hexdigest(), 16)
    return hash % vocab_size

Hash trick to pair each word with an index

Args

word : str
word we wish to convert to an index
vocab_size : int
size of the vocabulary

Returns

int
index of the word in the embedding LUT
def is_jsonable(x: Any)
Expand source code
def is_jsonable(x: tp.Any):
    """Check if an object can be serialized into a json:"""
    try:
        json.dumps(x)
        return True
    except (TypeError, OverflowError):
        return False

Check if an object can be serialized into a json:

def length_to_mask(lengths: torch.Tensor, max_len: int | None = None) ‑> torch.Tensor
Expand source code
def length_to_mask(lengths: torch.Tensor, max_len: tp.Optional[int] = None) -> torch.Tensor:
    """Utility function to convert a tensor of sequence lengths to a mask (useful when working on padded sequences).
    For example: [3, 5] => [[1, 1, 1, 0, 0], [1, 1, 1, 1, 1]]

    Args:
        lengths (torch.Tensor): tensor with lengths
        max_len (int): can set the max length manually. Defaults to None.
    Returns:
        torch.Tensor: mask with 0s where there is pad tokens else 1s
    """
    assert len(lengths.shape) == 1, "Length shape should be 1 dimensional."
    final_length = lengths.max().item() if not max_len else max_len
    final_length = max(final_length, 1)  # if all seqs are of len zero we don't want a zero-size tensor
    return torch.arange(final_length, device=lengths.device)[None, :] < lengths[:, None]

Utility function to convert a tensor of sequence lengths to a mask (useful when working on padded sequences). For example: [3, 5] => [[1, 1, 1, 0, 0], [1, 1, 1, 1, 1]]

Args

lengths : torch.Tensor
tensor with lengths
max_len : int
can set the max length manually. Defaults to None.

Returns

torch.Tensor
mask with 0s where there is pad tokens else 1s
def load_clap_state_dict(clap_model, path: str | pathlib.Path)
Expand source code
def load_clap_state_dict(clap_model, path: tp.Union[str, Path]):
    """Wrapper around state dict loading of CLAP model
    addressing compatibility issues between CLAP and AudioCraft
    HuggingFace transformer version.
    See: https://github.com/LAION-AI/CLAP/issues/118
    """
    from clap_module.factory import load_state_dict  # type: ignore
    pkg = load_state_dict(path)
    pkg.pop('text_branch.embeddings.position_ids', None)
    clap_model.model.load_state_dict(pkg)

Wrapper around state dict loading of CLAP model addressing compatibility issues between CLAP and AudioCraft HuggingFace transformer version. See: https://github.com/LAION-AI/CLAP/issues/118

def model_hash(model: torch.nn.modules.module.Module) ‑> str
Expand source code
def model_hash(model: torch.nn.Module) -> str:
    """Return a model hash. This should allow us to track regressions in model init
    from the logs of past experiments.
    """
    hasher = hashlib.sha1()
    for p in model.parameters():
        hasher.update(p.data.cpu().numpy().tobytes())
    return hasher.hexdigest()

Return a model hash. This should allow us to track regressions in model init from the logs of past experiments.

def multinomial(input: torch.Tensor, num_samples: int, replacement=False, *, generator=None)
Expand source code
def multinomial(input: torch.Tensor, num_samples: int, replacement=False, *, generator=None):
    """torch.multinomial with arbitrary number of dimensions, and number of candidates on the last dimension.

    Args:
        input (torch.Tensor): The input tensor containing probabilities.
        num_samples (int): Number of samples to draw.
        replacement (bool): Whether to draw with replacement or not.
    Keywords args:
        generator (torch.Generator): A pseudorandom number generator for sampling.
    Returns:
        torch.Tensor: Last dimension contains num_samples indices
            sampled from the multinomial probability distribution
            located in the last dimension of tensor input.
    """
    input_ = input.reshape(-1, input.shape[-1])
    output_ = torch.multinomial(input_, num_samples=num_samples, replacement=replacement, generator=generator)
    output = output_.reshape(*list(input.shape[:-1]), -1)
    return output

torch.multinomial with arbitrary number of dimensions, and number of candidates on the last dimension.

Args

input : torch.Tensor
The input tensor containing probabilities.
num_samples : int
Number of samples to draw.
replacement : bool
Whether to draw with replacement or not.

Keywords args: generator (torch.Generator): A pseudorandom number generator for sampling.

Returns

torch.Tensor
Last dimension contains num_samples indices sampled from the multinomial probability distribution located in the last dimension of tensor input.
def random_subset(dataset, max_samples: int, seed: int = 42) ‑> torch.utils.data.dataset.Subset
Expand source code
def random_subset(dataset, max_samples: int, seed: int = 42) -> torch.utils.data.Subset:
    if max_samples >= len(dataset):
        return dataset

    generator = torch.Generator().manual_seed(seed)
    perm = torch.randperm(len(dataset), generator=generator)
    return torch.utils.data.Subset(dataset, perm[:max_samples].tolist())
def sample_top_k(probs: torch.Tensor, k: int) ‑> torch.Tensor
Expand source code
def sample_top_k(probs: torch.Tensor, k: int) -> torch.Tensor:
    """Sample next token from top K values along the last dimension of the input probs tensor.

    Args:
        probs (torch.Tensor): Input probabilities with token candidates on the last dimension.
        k (int): The k in “top-k”.
    Returns:
        torch.Tensor: Sampled tokens.
    """
    top_k_value, _ = torch.topk(probs, k, dim=-1)
    min_value_top_k = top_k_value[..., [-1]]
    probs *= (probs >= min_value_top_k).float()
    probs.div_(probs.sum(dim=-1, keepdim=True))
    next_token = multinomial(probs, num_samples=1)
    return next_token

Sample next token from top K values along the last dimension of the input probs tensor.

Args

probs : torch.Tensor
Input probabilities with token candidates on the last dimension.
k : int
The k in “top-k”.

Returns

torch.Tensor
Sampled tokens.
def sample_top_p(probs: torch.Tensor, p: float) ‑> torch.Tensor
Expand source code
def sample_top_p(probs: torch.Tensor, p: float) -> torch.Tensor:
    """Sample next token from top P probabilities along the last dimension of the input probs tensor.

    Args:
        probs (torch.Tensor): Input probabilities with token candidates on the last dimension.
        p (int): The p in “top-p”.
    Returns:
        torch.Tensor: Sampled tokens.
    """
    probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True)
    probs_sum = torch.cumsum(probs_sort, dim=-1)
    mask = probs_sum - probs_sort > p
    probs_sort *= (~mask).float()
    probs_sort.div_(probs_sort.sum(dim=-1, keepdim=True))
    next_token = multinomial(probs_sort, num_samples=1)
    next_token = torch.gather(probs_idx, -1, next_token)
    return next_token

Sample next token from top P probabilities along the last dimension of the input probs tensor.

Args

probs : torch.Tensor
Input probabilities with token candidates on the last dimension.
p : int
The p in “top-p”.

Returns

torch.Tensor
Sampled tokens.
def swap_state(model, state, **kwargs)
Expand source code
@contextmanager
def swap_state(model, state, **kwargs):
    old_state = copy_state(model.state_dict())
    model.load_state_dict(state, **kwargs)
    try:
        yield
    finally:
        model.load_state_dict(old_state)
def warn_once(logger, msg)
Expand source code
@lru_cache(None)
def warn_once(logger, msg):
    """Warn about a given message only once."""
    logger.warning(msg)

Warn about a given message only once.

def with_rank_rng(base_seed: int = 1234)
Expand source code
def with_rank_rng(base_seed: int = 1234):
    """Decorator for a function so that the function will use a Random Number Generator
    whose state depend on the GPU rank. The original RNG state is restored upon returning.

    Args:
        base_seed (int): Random seed.
    """
    def _decorator(fun: tp.Callable):
        @wraps(fun)
        def _decorated(*args, **kwargs):
            state = torch.get_rng_state()
            seed = base_seed ^ flashy.distrib.rank()
            torch.manual_seed(seed)
            logger.debug('Rank dependent seed set to %d', seed)
            try:
                return fun(*args, **kwargs)
            finally:
                torch.set_rng_state(state)
                logger.debug('RNG state restored.')
        return _decorated
    return _decorator

Decorator for a function so that the function will use a Random Number Generator whose state depend on the GPU rank. The original RNG state is restored upon returning.

Args

base_seed : int
Random seed.

Classes

class DummyPoolExecutor (workers, mp_context=None)
Expand source code
class DummyPoolExecutor:
    """Dummy pool executor to use when we actually have only 1 worker.
    (e.g. instead of ProcessPoolExecutor).
    """
    class DummyResult:
        def __init__(self, func, *args, **kwargs):
            self.func = func
            self.args = args
            self.kwargs = kwargs

        def result(self):
            return self.func(*self.args, **self.kwargs)

    def __init__(self, workers, mp_context=None):
        pass

    def submit(self, func, *args, **kwargs):
        return DummyPoolExecutor.DummyResult(func, *args, **kwargs)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        return

Dummy pool executor to use when we actually have only 1 worker. (e.g. instead of ProcessPoolExecutor).

Class variables

var DummyResult

Methods

def submit(self, func, *args, **kwargs)
Expand source code
def submit(self, func, *args, **kwargs):
    return DummyPoolExecutor.DummyResult(func, *args, **kwargs)