Instructions to use Pierfrancesco/adt-str with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Pierfrancesco/adt-str with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Pierfrancesco/adt-str", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download model.py from Pierfrancesco/adt-str: direct link, hf CLI and curl.
- Browser
- Download file 18.9 kB
-
https://huggingface.co/Pierfrancesco/adt-str/resolve/main/model.py
- Command line
-
hf download hf://Pierfrancesco/adt-str/model.py
-
curl -L -o model.py https://huggingface.co/Pierfrancesco/adt-str/resolve/main/model.py
18.9 kB
| import math | |
| from typing import Optional | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from config import ADTModelConfig | |
| from transformers import PreTrainedModel | |
| import torchaudio.transforms as T | |
| from utils.utils import create_mask_plain | |
| class TokenEmbedding(nn.Module): | |
| def __init__(self, vocab_size, emb_size): | |
| """ | |
| Args: | |
| vocab_size: Vocabulary size | |
| emb_size: Dimension of embedding vectors | |
| """ | |
| super(TokenEmbedding, self).__init__() | |
| self.embedding = nn.Embedding(vocab_size, emb_size) | |
| self.emb_size = emb_size | |
| def forward(self, token_vector: torch.Tensor): | |
| """ | |
| Args: | |
| token_vector: One-hot or multi-hot tensor (batch, seq_len, vocab_size) | |
| Returns: | |
| Embedded vectors (batch, seq_len, emb_size) | |
| """ | |
| # Get embedding weights (vocab_size, emb_size) | |
| embedding_weights = self.embedding.weight # Get weights | |
| # Multiply one-hot or multi-hot vector with embedding weights | |
| embeddings = torch.matmul(token_vector, embedding_weights) * math.sqrt( | |
| self.emb_size | |
| ) | |
| return embeddings | |
| class TokenEmbedding_plain(nn.Module): | |
| def __init__(self, vocab_size, emb_size): | |
| super(TokenEmbedding_plain, self).__init__() | |
| self.embedding = nn.Embedding(vocab_size, emb_size) | |
| self.emb_size = emb_size | |
| def forward(self, tokens: torch.Tensor): | |
| return self.embedding(tokens.long()) * math.sqrt(self.emb_size) | |
| class PositionalEncoding(nn.Module): | |
| def __init__(self, emb_size: int, maxlen: int = 2048): | |
| super(PositionalEncoding, self).__init__() | |
| den = torch.exp(-torch.arange(0, emb_size, 2) * math.log(10000) / emb_size) | |
| pos = torch.arange(0, maxlen).reshape(maxlen, 1) | |
| pos_embedding = torch.zeros((maxlen, emb_size)) | |
| pos_embedding[:, 0::2] = torch.sin(pos * den) | |
| pos_embedding[:, 1::2] = torch.cos(pos * den) | |
| pos_embedding = pos_embedding.unsqueeze(0) | |
| self.register_buffer("pos_embedding", pos_embedding) | |
| def forward(self, token_embedding: torch.Tensor): | |
| return token_embedding + self.pos_embedding[:, : token_embedding.size(1), :] | |
| class ComputeMelSpectrogram(torch.nn.Module): | |
| def __init__(self, sample_rate, win_length, time_res, n_mels): | |
| super(ComputeMelSpectrogram, self).__init__() | |
| with torch.device("cpu"): | |
| self.compute_spec = T.MelSpectrogram( | |
| sample_rate=sample_rate, | |
| n_fft=win_length, | |
| hop_length=int(time_res * sample_rate), | |
| n_mels=n_mels, | |
| f_min=20.0, | |
| power=2, | |
| ) | |
| self.window_pad_idxs = int((win_length / 2) // int(time_res * sample_rate) + 1) | |
| def forward(self, wave): | |
| wave_device = wave.device | |
| self.compute_spec = self.compute_spec.to(wave_device) | |
| # Esegui in float32: torchaudio MelSpectrogram + bf16 autocast causano CUBLAS_STATUS_INVALID_VALUE | |
| with torch.amp.autocast( | |
| device_type="cuda" if wave_device.type == "cuda" else "cpu", enabled=False | |
| ): | |
| wave_f32 = wave.float() | |
| mel_spec = self.compute_spec(wave_f32) | |
| logmel_spec = torch.log(mel_spec + 1e-10) | |
| logmel_spec = torch.clamp(logmel_spec, -23, 12) | |
| logmel_spec = (logmel_spec + 23) / (12 + 23) | |
| return logmel_spec.permute(0, 2, 1)[ | |
| :, self.window_pad_idxs : -(self.window_pad_idxs + 1), : | |
| ] # (batch, len, emb) | |
| class Encoder(nn.Module): | |
| def __init__( | |
| self, | |
| enc_layers, | |
| d_query, | |
| nhead, | |
| ffn_hid_dim, | |
| dropout, | |
| ): | |
| super(Encoder, self).__init__() | |
| self.num_features = d_query * nhead | |
| self.dense_layer = nn.Linear(self.num_features, self.num_features, bias=False) | |
| self.positional_encoding = PositionalEncoding(self.num_features) | |
| self.layer_norm = nn.LayerNorm( | |
| normalized_shape=self.num_features, elementwise_affine=True | |
| ) | |
| self.dropout_layer = nn.Dropout(p=dropout) | |
| layer = nn.TransformerEncoderLayer( | |
| d_model=self.num_features, | |
| nhead=nhead, | |
| dim_feedforward=ffn_hid_dim, | |
| dropout=dropout, | |
| activation="gelu", | |
| batch_first=True, | |
| norm_first=False, | |
| ) | |
| self.encoder = nn.TransformerEncoder(layer, num_layers=enc_layers) | |
| def forward(self, src_emb): | |
| src_emb = self.dense_layer(src_emb) | |
| src_emb = self.positional_encoding(src_emb) | |
| src_emb = self.dropout_layer(src_emb) | |
| encoder_output = self.encoder(src_emb) | |
| encoder_output = self.dropout_layer(self.layer_norm(encoder_output)) | |
| return encoder_output | |
| class Decoder(nn.Module): | |
| def __init__( | |
| self, | |
| dec_layers, | |
| d_query, | |
| nhead, | |
| ffn_hid_dim, | |
| tgt_vocab_size, | |
| dropout, | |
| plain=False, | |
| ): | |
| super(Decoder, self).__init__() | |
| self.num_features = d_query * nhead | |
| if plain: | |
| self.tgt_tok_emb = TokenEmbedding_plain(tgt_vocab_size, self.num_features) | |
| if not plain: | |
| self.tgt_tok_emb = TokenEmbedding(tgt_vocab_size, self.num_features) | |
| self.positional_encoding = PositionalEncoding(self.num_features) | |
| self.dropout_layer = nn.Dropout(p=dropout) | |
| self.generator = nn.Linear(self.num_features, tgt_vocab_size) | |
| layer = nn.TransformerDecoderLayer( | |
| d_model=self.num_features, | |
| nhead=nhead, | |
| dim_feedforward=ffn_hid_dim, | |
| dropout=dropout, | |
| activation="gelu", | |
| batch_first=True, | |
| norm_first=False, | |
| ) | |
| self.decoder = nn.TransformerDecoder(layer, num_layers=dec_layers) | |
| def forward(self, tgt, encoder_output, tgt_mask, tgt_padding_mask): | |
| tgt_emb = self.positional_encoding(self.tgt_tok_emb(tgt)) | |
| tgt_emb = self.dropout_layer(tgt_emb) | |
| # bf16-safe and PyTorch same-type: use additive float masks (0 / -1e4) instead of bool (-inf) | |
| if tgt_mask is not None: | |
| tgt_mask = torch.zeros_like( | |
| tgt_mask, dtype=tgt_emb.dtype, device=tgt_mask.device | |
| ).masked_fill_(tgt_mask, -1e4) | |
| if tgt_padding_mask is not None and tgt_padding_mask.dtype == torch.bool: | |
| tgt_padding_mask = torch.zeros_like( | |
| tgt_padding_mask, dtype=tgt_emb.dtype, device=tgt_padding_mask.device | |
| ).masked_fill_(tgt_padding_mask, -1e4) | |
| decoder_outputs = self.decoder( | |
| tgt_emb, | |
| encoder_output, | |
| tgt_mask=tgt_mask, | |
| tgt_key_padding_mask=tgt_padding_mask, | |
| memory_mask=None, | |
| memory_key_padding_mask=None, | |
| ) | |
| return self.generator(decoder_outputs) | |
| class ADTModel(PreTrainedModel): | |
| config_class = ADTModelConfig | |
| _supports_assign_param_buffer = False | |
| _tied_weights_keys = [] | |
| all_tied_weights_keys = {} | |
| def __init__( | |
| self, | |
| config: ADTModelConfig, | |
| ) -> None: | |
| super(ADTModel, self).__init__(config) | |
| self.config = config | |
| self.encoder = Encoder( | |
| enc_layers=config.enc_layers, | |
| d_query=config.d_query, | |
| nhead=config.nhead, | |
| ffn_hid_dim=int(config.d_query * config.nhead * 4), | |
| dropout=config.dropout, | |
| ) | |
| self.decoder = Decoder( | |
| dec_layers=config.dec_layers, | |
| d_query=config.d_query, | |
| nhead=config.nhead, | |
| ffn_hid_dim=int(config.d_query * config.nhead * 4), | |
| dropout=config.dropout, | |
| tgt_vocab_size=config.tgt_vocab_size, | |
| plain=config.plain, | |
| ) | |
| self.compute_spectrogram = ComputeMelSpectrogram( | |
| sample_rate=config.sample_rate, | |
| win_length=config.win_length, | |
| time_res=config.time_res, | |
| n_mels=config.n_mels, | |
| ) | |
| self.project_to_mel = nn.Linear( | |
| config.n_mels, int(config.d_query * config.nhead) | |
| ) | |
| def _loss_fn( | |
| self, logits: torch.FloatTensor, tgt: torch.LongTensor | |
| ) -> torch.FloatTensor: | |
| # Use fp32 for stable loss and ignore PAD token (id=1) | |
| logits = logits.float() | |
| logits = torch.nan_to_num(logits, nan=0.0, posinf=1e4, neginf=-1e4) | |
| return F.cross_entropy( | |
| logits.reshape(-1, logits.shape[-1]), | |
| tgt.long().reshape(-1), | |
| ignore_index=1, | |
| ) | |
| def forward( | |
| self, | |
| src: torch.FloatTensor, | |
| tgt: torch.LongTensor, | |
| tgt_mask: Optional[torch.BoolTensor], | |
| tgt_padding_mask: Optional[torch.BoolTensor], | |
| labels: torch.LongTensor, | |
| ) -> torch.FloatTensor: | |
| src_emb = self.compute_spectrogram(src) | |
| src_emb = self.project_to_mel(src_emb) | |
| encoder_out = self.encoder(src_emb) | |
| # Build causal mask inside model so DataParallel does not split (T,T) across devices | |
| if tgt_mask is None: | |
| tgt_seq_len = tgt.size(1) | |
| tgt_mask, _ = create_mask_plain(tgt_seq_len, None, tgt.device) | |
| logits = self.decoder(tgt, encoder_out, tgt_mask, tgt_padding_mask) | |
| loss = self._loss_fn(logits, labels) | |
| return loss | |
| def sample( | |
| self, | |
| src: torch.FloatTensor, | |
| src_mask: torch.BoolTensor, | |
| tgt_mask: torch.BoolTensor, | |
| max_length: int = 1000, | |
| start_token: int = 2, | |
| end_token: int = 3, | |
| ) -> torch.LongTensor: | |
| """ | |
| Basic autoregressive greedy sampling for ADT model. | |
| Args: | |
| src: Input audio tensor (batch_size, audio_length) | |
| src_mask: Source mask (not used in current implementation) | |
| tgt_mask: Target mask (not used in current implementation) | |
| max_length: Maximum sequence length to generate | |
| start_token: Token ID to start generation (default: 0) | |
| end_token: Token ID to end generation (default: 2, assuming EOS) | |
| Returns: | |
| Generated token sequence (batch_size, seq_length) | |
| """ | |
| if not self.config.plain: | |
| raise NotImplementedError("Non-plain mode is not implemented") | |
| self.eval() | |
| device = src.device | |
| batch_size = src.shape[0] | |
| # Process input through encoder | |
| src_emb = self.compute_spectrogram(src) | |
| src_emb = self.project_to_mel(src_emb) | |
| memory = self.encoder(src_emb) | |
| # Initialize with start token | |
| generated = torch.full( | |
| (batch_size, 1), start_token, dtype=torch.long, device=device | |
| ) | |
| finished = torch.zeros(batch_size, dtype=torch.bool, device=device) | |
| # Greedy decoding loop (per-row finished mask so batch items can end at different steps) | |
| for step in range(max_length - 1): | |
| # Create causal mask for current sequence length (no padding in decoding) | |
| seq_len = generated.shape[1] | |
| tgt_mask, tgt_padding_mask = create_mask_plain(seq_len, None, device) | |
| # Get decoder output | |
| logits = self.decoder( | |
| generated, | |
| memory, | |
| tgt_mask, | |
| tgt_padding_mask=None, | |
| ) | |
| # Get next token (greedy); freeze finished rows at EOS so they do not keep consuming argmax noise | |
| next_token_logits = logits[:, -1, :] # Last position | |
| next_token = torch.argmax(next_token_logits, dim=-1) # Greedy selection | |
| next_token = torch.where( | |
| finished, torch.full_like(next_token, end_token), next_token | |
| ) | |
| generated = torch.cat([generated, next_token.unsqueeze(1)], dim=1) | |
| finished = finished | (next_token == end_token) | |
| if torch.all(finished): | |
| break | |
| return generated | |
| def beam_search( | |
| self, | |
| src: torch.FloatTensor, | |
| src_mask: torch.BoolTensor, | |
| tgt_mask: torch.BoolTensor, | |
| beam_size: int = 5, | |
| max_length: int = 1000, | |
| start_token: int = 2, | |
| end_token: int = 3, | |
| length_penalty: float = 1.0, | |
| ) -> torch.LongTensor: | |
| """ | |
| Beam search sampling for ADT model. | |
| Args: | |
| src: Input audio tensor (batch_size, audio_length) | |
| src_mask: Source mask (not used in current implementation) | |
| tgt_mask: Target mask (not used in current implementation) | |
| beam_size: Number of beams to keep at each step | |
| max_length: Maximum sequence length to generate | |
| start_token: Token ID to start generation (default: 0) | |
| end_token: Token ID to end generation (default: 2, assuming EOS) | |
| length_penalty: Length penalty factor for scoring (default: 1.0) | |
| Returns: | |
| Best generated token sequence (batch_size, seq_length) | |
| """ | |
| if not self.config.plain: | |
| raise NotImplementedError("Non-plain mode is not implemented") | |
| self.eval() | |
| device = src.device | |
| batch_size = src.shape[0] | |
| # Process input through encoder | |
| src_emb = self.compute_spectrogram(src) | |
| src_emb = self.project_to_mel(src_emb) | |
| memory = self.encoder(src_emb) | |
| # Initialize beams for each batch | |
| # Each beam contains: [batch_idx, sequence, log_prob, finished] | |
| all_beams = [] | |
| for batch_idx in range(batch_size): | |
| # Initialize beams for this batch item | |
| initial_sequence = torch.full( | |
| (1, 1), start_token, dtype=torch.long, device=device | |
| ) | |
| beams = [{"sequence": initial_sequence, "log_prob": 0.0, "finished": False}] | |
| all_beams.append(beams) | |
| # Beam search loop | |
| for step in range(max_length - 1): | |
| new_all_beams = [] | |
| for batch_idx in range(batch_size): | |
| beams = all_beams[batch_idx] | |
| new_beams = [] | |
| # Skip if all beams are finished | |
| if all(beam["finished"] for beam in beams): | |
| new_all_beams.append(beams) | |
| continue | |
| # Collect all active sequences for this batch | |
| active_beams = [beam for beam in beams if not beam["finished"]] | |
| if not active_beams: | |
| new_all_beams.append(beams) | |
| continue | |
| # Stack sequences for parallel processing | |
| sequences = torch.cat( | |
| [beam["sequence"] for beam in active_beams], dim=0 | |
| ) | |
| batch_memory = memory[batch_idx : batch_idx + 1].expand( | |
| len(active_beams), -1, -1 | |
| ) | |
| # Create causal mask | |
| seq_len = sequences.shape[1] | |
| tgt_mask = torch.triu( | |
| torch.ones(seq_len, seq_len, device=device), diagonal=1 | |
| ).bool() | |
| # Get decoder output | |
| logits = self.decoder( | |
| sequences, | |
| batch_memory, | |
| tgt_mask, | |
| None, | |
| ) | |
| # Get next token probabilities | |
| next_token_logits = logits[:, -1, :] # Last position | |
| log_probs = torch.log_softmax(next_token_logits, dim=-1) | |
| # Generate candidates for each active beam | |
| candidates = [] | |
| for beam_idx, beam in enumerate(active_beams): | |
| beam_log_probs = log_probs[beam_idx] | |
| top_k_log_probs, top_k_tokens = torch.topk( | |
| beam_log_probs, beam_size | |
| ) | |
| for k in range(beam_size): | |
| token = top_k_tokens[k].item() | |
| token_log_prob = top_k_log_probs[k].item() | |
| new_log_prob = beam["log_prob"] + token_log_prob | |
| # Create new sequence | |
| new_sequence = torch.cat( | |
| [beam["sequence"], torch.tensor([[token]], device=device)], | |
| dim=1, | |
| ) | |
| # Check if finished | |
| finished = token == end_token | |
| candidates.append( | |
| { | |
| "sequence": new_sequence, | |
| "log_prob": new_log_prob, | |
| "finished": finished, | |
| } | |
| ) | |
| # Add finished beams (they don't expand further) | |
| finished_beams = [beam for beam in beams if beam["finished"]] | |
| candidates.extend(finished_beams) | |
| # Select top beam_size candidates | |
| candidates.sort( | |
| key=lambda x: self._score_sequence(x, length_penalty), reverse=True | |
| ) | |
| new_beams = candidates[:beam_size] | |
| new_all_beams.append(new_beams) | |
| all_beams = new_all_beams | |
| # Check if all beams in all batches are finished | |
| all_finished = all( | |
| all(beam["finished"] for beam in beams) for beams in all_beams | |
| ) | |
| if all_finished: | |
| break | |
| # Select best sequence for each batch | |
| results = [] | |
| for batch_idx in range(batch_size): | |
| beams = all_beams[batch_idx] | |
| # Sort by score and take the best | |
| beams.sort( | |
| key=lambda x: self._score_sequence(x, length_penalty), reverse=True | |
| ) | |
| best_sequence = beams[0]["sequence"] | |
| results.append(best_sequence) | |
| # Stack results and pad to same length | |
| max_len = max(seq.shape[1] for seq in results) | |
| padded_results = [] | |
| for seq in results: | |
| if seq.shape[1] < max_len: | |
| padding = torch.full( | |
| (1, max_len - seq.shape[1]), | |
| end_token, | |
| dtype=torch.long, | |
| device=device, | |
| ) | |
| padded_seq = torch.cat([seq, padding], dim=1) | |
| else: | |
| padded_seq = seq | |
| padded_results.append(padded_seq) | |
| return torch.cat(padded_results, dim=0) | |
| def _score_sequence(self, beam: dict, length_penalty: float) -> float: | |
| seq_len = beam["sequence"].shape[1] | |
| if length_penalty == 0.0: | |
| return beam["log_prob"] | |
| else: | |
| # Length normalization: log_prob / (length^length_penalty) | |
| return beam["log_prob"] / (seq_len**length_penalty) | |