Automatic Speech Recognition
Transformers
TensorBoard
Safetensors
msp
Generated from Trainer
custom_code
Instructions to use MahmoodAnaam/MSP-Fusion with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use MahmoodAnaam/MSP-Fusion with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("automatic-speech-recognition", model="MahmoodAnaam/MSP-Fusion", trust_remote_code=True)# Load model directly from transformers import AutoModelForCTC model = AutoModelForCTC.from_pretrained("MahmoodAnaam/MSP-Fusion", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| from collections import OrderedDict | |
| from copy import deepcopy | |
| from typing import Dict, List, Optional, Tuple | |
| import numpy as np | |
| import torch | |
| from torch import nn | |
| from transformers import PreTrainedModel | |
| from transformers.modeling_outputs import BaseModelOutput | |
| from transformers.models.wav2vec2.modeling_wav2vec2 import ( | |
| Wav2Vec2Encoder, | |
| Wav2Vec2EncoderLayer, | |
| is_deepspeed_zero3_enabled, | |
| ) | |
| from .configuration_avhubert import AVHubertConfig | |
| from .resnet import ResEncoder | |
| def find_runs(x): | |
| """Find runs of consecutive items in an array.""" | |
| # ensure array | |
| x = np.asanyarray(x) | |
| if x.ndim != 1: | |
| raise ValueError("only 1D array supported") | |
| n = x.shape[0] | |
| # handle empty array | |
| if n == 0: | |
| return np.array([]), np.array([]), np.array([]) | |
| else: | |
| # find run starts | |
| loc_run_start = np.empty(n, dtype=bool) | |
| loc_run_start[0] = True | |
| np.not_equal(x[:-1], x[1:], out=loc_run_start[1:]) | |
| run_starts = np.nonzero(loc_run_start)[0] | |
| # find run values | |
| run_values = x[loc_run_start] | |
| # find run lengths | |
| run_lengths = np.diff(np.append(run_starts, n)) | |
| return run_values, run_starts, run_lengths | |
| def compute_mask_indices( | |
| shape: Tuple[int, int], | |
| padding_mask: Optional[torch.Tensor], | |
| mask_prob: float, | |
| mask_length: int, | |
| mask_type: str = "static", | |
| mask_other: float = 0.0, | |
| min_masks: int = 0, | |
| no_overlap: bool = False, | |
| min_space: int = 0, | |
| ) -> np.ndarray: | |
| """ | |
| Computes random mask spans for a given shape | |
| Args: | |
| shape: the the shape for which to compute masks. | |
| should be of size 2 where first element is batch size and 2nd is timesteps | |
| padding_mask: optional padding mask of the same size as shape, which will prevent masking padded elements | |
| mask_prob: probability for each token to be chosen as start of the span to be masked. this will be multiplied by | |
| number of timesteps divided by length of mask span to mask approximately this percentage of all elements. | |
| however due to overlaps, the actual number will be smaller (unless no_overlap is True) | |
| mask_type: how to compute mask lengths | |
| static = fixed size | |
| uniform = sample from uniform distribution [mask_other, mask_length*2] | |
| normal = sample from normal distribution with mean mask_length and stdev mask_other. mask is min 1 element | |
| poisson = sample from possion distribution with lambda = mask length | |
| min_masks: minimum number of masked spans | |
| no_overlap: if false, will switch to an alternative recursive algorithm that prevents spans from overlapping | |
| min_space: only used if no_overlap is True, this is how many elements to keep unmasked between spans | |
| """ | |
| bsz, all_sz = shape | |
| mask = np.full((bsz, all_sz), False) | |
| all_num_mask = int( | |
| # add a random number for probabilistic rounding | |
| mask_prob * all_sz / float(mask_length) + np.random.rand() | |
| ) | |
| all_num_mask = max(min_masks, all_num_mask) | |
| mask_idcs = [] | |
| for i in range(bsz): | |
| if padding_mask is not None: | |
| sz = all_sz - padding_mask[i].long().sum().item() | |
| num_mask = int( | |
| # add a random number for probabilistic rounding | |
| mask_prob * sz / float(mask_length) + np.random.rand() | |
| ) | |
| num_mask = max(min_masks, num_mask) | |
| else: | |
| sz = all_sz | |
| num_mask = all_num_mask | |
| if mask_type == "static": | |
| lengths = np.full(num_mask, mask_length) | |
| elif mask_type == "uniform": | |
| lengths = np.random.randint(mask_other, mask_length * 2 + 1, size=num_mask) | |
| elif mask_type == "normal": | |
| lengths = np.random.normal(mask_length, mask_other, size=num_mask) | |
| lengths = [max(1, int(round(x))) for x in lengths] | |
| elif mask_type == "poisson": | |
| lengths = np.random.poisson(mask_length, size=num_mask) | |
| lengths = [int(round(x)) for x in lengths] | |
| else: | |
| raise Exception("unknown mask selection " + mask_type) | |
| if sum(lengths) == 0: | |
| lengths[0] = min(mask_length, sz - 1) | |
| if no_overlap: | |
| mask_idc = [] | |
| def arrange(s, e, length, keep_length): | |
| span_start = np.random.randint(s, e - length) | |
| mask_idc.extend(span_start + i for i in range(length)) | |
| new_parts = [] | |
| if span_start - s - min_space >= keep_length: | |
| new_parts.append((s, span_start - min_space + 1)) | |
| if e - span_start - keep_length - min_space > keep_length: | |
| new_parts.append((span_start + length + min_space, e)) | |
| return new_parts | |
| parts = [(0, sz)] | |
| min_length = min(lengths) | |
| for length in sorted(lengths, reverse=True): | |
| lens = np.fromiter( | |
| (e - s if e - s >= length + min_space else 0 for s, e in parts), | |
| np.int, | |
| ) | |
| l_sum = np.sum(lens) | |
| if l_sum == 0: | |
| break | |
| probs = lens / np.sum(lens) | |
| c = np.random.choice(len(parts), p=probs) | |
| s, e = parts.pop(c) | |
| parts.extend(arrange(s, e, length, min_length)) | |
| mask_idc = np.asarray(mask_idc) | |
| else: | |
| min_len = min(lengths) | |
| if sz - min_len <= num_mask: | |
| min_len = sz - num_mask - 1 | |
| mask_idc = np.random.choice(sz - min_len, num_mask, replace=False) | |
| mask_idc = np.asarray( | |
| [ | |
| mask_idc[j] + offset | |
| for j in range(len(mask_idc)) | |
| for offset in range(lengths[j]) | |
| ] | |
| ) | |
| mask_idcs.append(np.unique(mask_idc[mask_idc < sz])) | |
| min_len = min([len(m) for m in mask_idcs]) | |
| batch_indexes, starts, ends = [], [], [] | |
| for i, mask_idc in enumerate(mask_idcs): | |
| if len(mask_idc) > min_len: | |
| mask_idc = np.random.choice(mask_idc, min_len, replace=False) | |
| mask[i, mask_idc] = True | |
| vals, run_starts, run_lengths = find_runs(mask[i]) | |
| start_indices, lengths = run_starts[vals], run_lengths[vals] | |
| starts.append(start_indices) | |
| ends.append(start_indices + lengths) | |
| batch_indexes.append(np.zeros([len(start_indices)]) + i) | |
| return ( | |
| mask, | |
| np.concatenate(starts).astype(np.int64), | |
| np.concatenate(ends).astype(np.int64), | |
| np.concatenate(batch_indexes).astype(np.int64), | |
| ) | |
| class GradMultiply(torch.autograd.Function): | |
| def forward(ctx, x, scale): | |
| ctx.scale = scale | |
| res = x.new(x) | |
| return res | |
| def backward(ctx, grad): | |
| return grad * ctx.scale, None | |
| def LayerNorm(normalized_shape, eps=1e-5, elementwise_affine=True, export=False): | |
| return torch.nn.LayerNorm(normalized_shape, eps, elementwise_affine) | |
| class SubModel(nn.Module): | |
| def __init__(self, resnet=None, input_dim=None, cfg=None): | |
| super().__init__() | |
| self.resnet = resnet | |
| self.proj = nn.Linear(input_dim, cfg.encoder_embed_dim) | |
| def forward(self, x): | |
| if self.resnet is not None: | |
| x = self.resnet(x) | |
| x = self.proj(x.transpose(1, 2)) | |
| x = x.transpose(1, 2) | |
| return x | |
| class AVHubertModel(PreTrainedModel): | |
| config_class = AVHubertConfig | |
| base_model_prefix = "avhubert" | |
| all_tied_weights_keys = OrderedDict() | |
| # main_input_name = "input_values" | |
| # supports_gradient_checkpointing = True | |
| # _supports_flash_attn_2 = True | |
| # _supports_sdpa = True | |
| def __init__( | |
| self, | |
| cfg: AVHubertConfig, | |
| ) -> None: | |
| super().__init__(cfg) | |
| # logger.info(f"HubertModel Config: {cfg}") | |
| feature_ds_rate = 1 | |
| self.feat2tar_ratio = cfg.label_rate * feature_ds_rate / cfg.sample_rate | |
| sub_cfg = deepcopy(cfg) | |
| sub_cfg.encoder_layers = sub_cfg.sub_encoder_layers | |
| resnet = ResEncoder(relu_type=cfg.resnet_relu_type, weights=cfg.resnet_weights) | |
| self.feature_extractor_audio = SubModel( | |
| resnet=None, input_dim=cfg.audio_feat_dim, cfg=sub_cfg | |
| ) | |
| self.feature_extractor_video = SubModel( | |
| resnet=resnet, input_dim=resnet.backend_out, cfg=sub_cfg | |
| ) | |
| self.modality_dropout, self.audio_dropout = ( | |
| cfg.modality_dropout, | |
| cfg.audio_dropout, | |
| ) | |
| self.modality_fuse = cfg.modality_fuse | |
| self.encoder_embed_dim = cfg.encoder_embed_dim | |
| if self.modality_fuse == "concat": | |
| self.embed = cfg.encoder_embed_dim * 2 | |
| elif self.modality_fuse == "add": | |
| self.embed = cfg.encoder_embed_dim | |
| self.post_extract_proj = ( | |
| nn.Linear(self.embed, cfg.encoder_embed_dim) | |
| if self.embed != cfg.encoder_embed_dim | |
| else None | |
| ) | |
| self.mask_prob_image, self.mask_prob_audio = ( | |
| cfg.mask_prob_image, | |
| cfg.mask_prob_audio, | |
| ) | |
| self.mask_selection = cfg.mask_selection | |
| self.mask_other = cfg.mask_other | |
| self.mask_length_image, self.mask_length_audio = ( | |
| cfg.mask_length_image, | |
| cfg.mask_length_audio, | |
| ) | |
| self.no_mask_overlap = cfg.no_mask_overlap | |
| self.mask_min_space = cfg.mask_min_space | |
| self.mask_channel_prob = cfg.mask_channel_prob | |
| self.mask_channel_selection = cfg.mask_channel_selection | |
| self.mask_channel_other = cfg.mask_channel_other | |
| self.mask_channel_length = cfg.mask_channel_length | |
| self.no_mask_channel_overlap = cfg.no_mask_channel_overlap | |
| self.mask_channel_min_space = cfg.mask_channel_min_space | |
| self.dropout_input = nn.Dropout(cfg.dropout_input) | |
| self.dropout_features = nn.Dropout(cfg.dropout_features) | |
| self.feature_grad_mult = cfg.feature_grad_mult | |
| self.logit_temp = cfg.logit_temp | |
| self.skip_masked = cfg.skip_masked | |
| self.skip_nomask = cfg.skip_nomask | |
| self.sim_type = cfg.sim_type | |
| self.selection_type = cfg.selection_type | |
| self.masking_type = cfg.masking_type | |
| self.modality = cfg.modality | |
| final_dim = cfg.final_dim if cfg.final_dim > 0 else cfg.encoder_embed_dim | |
| self.mask_emb = nn.Parameter( | |
| torch.FloatTensor(cfg.audio_feat_dim).uniform_() | |
| if self.masking_type == "input" | |
| else torch.FloatTensor(cfg.encoder_embed_dim).uniform_() | |
| ) | |
| self.encoder = AVHubertEncoder(cfg) | |
| self.layer_norm = LayerNorm(self.embed) | |
| self.target_glu = None | |
| if cfg.target_glu: | |
| self.target_glu = nn.Sequential( | |
| nn.Linear(final_dim, final_dim * 2), nn.GLU() | |
| ) | |
| self.untie_final_proj = cfg.untie_final_proj | |
| # if self.untie_final_proj: | |
| # self.final_proj = nn.Linear( | |
| # cfg.encoder_embed_dim, final_dim * cfg.num_dictionaries | |
| # ) | |
| # else: | |
| # self.final_proj = nn.Linear(cfg.encoder_embed_dim, final_dim) | |
| self.num_classes = [cfg.num_classes] | |
| self.label_embs_concat = nn.Parameter( | |
| torch.FloatTensor(sum(self.num_classes), final_dim) | |
| ) | |
| nn.init.uniform_(self.label_embs_concat) | |
| def upgrade_state_dict_named(self, state_dict, name): | |
| """Upgrade a (possibly old) state dict for new versions of fairseq.""" | |
| super().upgrade_state_dict_named(state_dict, name) | |
| return state_dict | |
| def apply_input_mask(self, x, padding_mask, target_list): | |
| B, C, T = x.shape[:3] | |
| is_audio = True if len(x.shape) == 3 else False | |
| if is_audio: | |
| mask_prob, mask_length = self.mask_prob_audio, self.mask_length_audio | |
| else: | |
| mask_prob, mask_length = self.mask_prob_image, self.mask_length_image | |
| if mask_prob > 0: | |
| mask_indices, starts, ends, batch_indexes = compute_mask_indices( | |
| (B, T), | |
| padding_mask, | |
| mask_prob, | |
| mask_length, | |
| self.mask_selection, | |
| self.mask_other, | |
| min_masks=2, | |
| no_overlap=self.no_mask_overlap, | |
| min_space=self.mask_min_space, | |
| ) | |
| mask_indices = torch.from_numpy(mask_indices).to(x.device) | |
| x = x.transpose(1, 2).contiguous() # [B, T, C, H, W] | |
| if B == 1: | |
| x[mask_indices] = 0 | |
| elif is_audio: | |
| x[mask_indices] = self.mask_emb | |
| elif self.selection_type == "same_other_seq": | |
| perm = (torch.arange(B) + torch.randint(low=1, high=B, size=(1,))) % B | |
| x_perm = x[perm] | |
| x[mask_indices] = x_perm[mask_indices] | |
| elif self.selection_type == "same_seq": | |
| batch_indexes_, other_indexes = [], [] | |
| for batch_index, start, end in zip(batch_indexes, starts, ends): | |
| length = end - start | |
| other_start = np.setdiff1d( | |
| np.arange(T), np.arange(max(0, start - length), end) | |
| ) | |
| if len(other_start) > 0: | |
| other_start = np.random.choice(other_start, size=1) | |
| else: | |
| other_start = 0 | |
| other_end = other_start + length | |
| other_indexes.append( | |
| np.arange(other_start, other_end).clip(max=T - 1) | |
| ) | |
| batch_indexes_.append( | |
| np.zeros([length], dtype=np.int64) + batch_index | |
| ) | |
| batch_indexes, other_indexes = ( | |
| np.concatenate(batch_indexes_), | |
| np.concatenate(other_indexes), | |
| ) | |
| x[mask_indices] = x[batch_indexes, other_indexes] | |
| x = x.transpose(1, 2).contiguous() | |
| else: | |
| mask_indices = None | |
| # if self.mask_channel_prob > 0: | |
| # logger.info(f"No mask channel prob for input masking") | |
| return x, mask_indices | |
| def apply_feature_mask(self, x, padding_mask, target_list): | |
| B, T, C = x.shape | |
| assert ( | |
| self.mask_prob_audio == self.mask_prob_image | |
| and self.mask_length_audio == self.mask_length_image | |
| ), "masking prob/length for image/audio be same for feature masking" | |
| mask_prob, mask_length = self.mask_prob_audio, self.mask_length_image | |
| if mask_prob > 0: | |
| mask_indices, _, _, _ = compute_mask_indices( | |
| (B, T), | |
| padding_mask, | |
| mask_prob, | |
| mask_length, | |
| self.mask_selection, | |
| self.mask_other, | |
| min_masks=2, | |
| no_overlap=self.no_mask_overlap, | |
| min_space=self.mask_min_space, | |
| ) | |
| mask_indices = torch.from_numpy(mask_indices).to(x.device) | |
| x[mask_indices] = self.mask_emb | |
| else: | |
| mask_indices = None | |
| if self.mask_channel_prob > 0: | |
| mask_channel_indices, _, _, _ = compute_mask_indices( | |
| (B, C), | |
| None, | |
| self.mask_channel_prob, | |
| self.mask_channel_length, | |
| self.mask_channel_selection, | |
| self.mask_channel_other, | |
| no_overlap=self.no_mask_channel_overlap, | |
| min_space=self.mask_channel_min_space, | |
| ) | |
| mask_channel_indices = ( | |
| torch.from_numpy(mask_channel_indices) | |
| .to(x.device) | |
| .unsqueeze(1) | |
| .expand(-1, T, -1) | |
| ) | |
| x[mask_channel_indices] = 0 | |
| return x, mask_indices | |
| def forward_features(self, source: torch.Tensor, modality: str) -> torch.Tensor: | |
| extractor = eval(f"self.feature_extractor_{modality}") | |
| if self.feature_grad_mult > 0: | |
| features = extractor(source) | |
| if self.feature_grad_mult != 1.0: | |
| features = GradMultiply.apply(features, self.feature_grad_mult) | |
| else: | |
| with torch.no_grad(): | |
| features = extractor(source) | |
| return features | |
| def forward_targets( | |
| self, | |
| features: torch.Tensor, | |
| mask_indices: torch.Tensor, | |
| target_list: List[torch.Tensor], | |
| ) -> Tuple[torch.Tensor, torch.Tensor]: | |
| # Trim features to ensure labels exist and then get aligned labels | |
| feat_tsz = features.size(2) | |
| targ_tsz = min([t.size(1) for t in target_list]) | |
| if self.feat2tar_ratio * feat_tsz > targ_tsz: | |
| feat_tsz = int(targ_tsz / self.feat2tar_ratio) | |
| features = features[..., :feat_tsz] | |
| if mask_indices is not None: | |
| mask_indices = mask_indices[..., :feat_tsz] | |
| target_inds = torch.arange(feat_tsz).float() * self.feat2tar_ratio | |
| target_list = [t[:, target_inds.long()] for t in target_list] | |
| return features, mask_indices, target_list | |
| def forward_padding_mask( | |
| self, | |
| features: torch.Tensor, | |
| padding_mask: torch.Tensor, | |
| ) -> torch.Tensor: | |
| extra = padding_mask.size(1) % features.size(1) | |
| if extra > 0: | |
| padding_mask = padding_mask[:, :-extra] | |
| padding_mask = padding_mask.view(padding_mask.size(0), features.size(1), -1) | |
| padding_mask = padding_mask.all(-1) | |
| return padding_mask | |
| def compute_logits(self, feats, emb_mat): | |
| # feats: [B, T, F], emb_mat: [V, F] | |
| if self.sim_type == "dot": | |
| logits = torch.matmul(feats, emb_mat.transpose(0, 1)) | |
| elif self.sim_type == "cosine": | |
| batch_size, timesteps, emb_dim = feats.size() | |
| feats_ = feats.view(-1, emb_dim) | |
| nom = (feats_.unsqueeze(dim=1) * emb_mat.unsqueeze(dim=0)).sum( | |
| dim=-1 | |
| ) # [B*T, V] | |
| denom = (feats_**2).sum(dim=-1).sqrt().unsqueeze(dim=1) * (emb_mat**2).sum( | |
| dim=-1 | |
| ).sqrt().unsqueeze(dim=0) # [B*T, V] | |
| logits = (nom / denom.clamp(min=1e-6)).view(batch_size, timesteps, -1) | |
| else: | |
| raise NotImplementedError | |
| logits = logits / self.logit_temp | |
| return logits | |
| def forward_gen( | |
| self, | |
| source: torch.Tensor, | |
| target_list: Optional[List[torch.Tensor]] = None, | |
| padding_mask: Optional[torch.Tensor] = None, | |
| mask: bool = True, | |
| features_only: bool = False, | |
| output_layer: Optional[int] = None, | |
| video: Optional[torch.Tensor] = None, | |
| ) -> Dict[str, torch.Tensor]: | |
| """output layer is 1-based""" | |
| src_audio, src_video = source["audio"], source["video"] | |
| if mask and self.masking_type == "input": | |
| src_video, mask_indices_video = self.apply_input_mask( | |
| src_video, padding_mask, target_list | |
| ) | |
| src_audio, mask_indices_audio = self.apply_input_mask( | |
| src_audio, padding_mask, target_list | |
| ) | |
| mask_indices = torch.logical_or(mask_indices_audio, mask_indices_video) | |
| else: | |
| src_audio, src_video, mask_indices = src_audio, src_video, None | |
| features_audio = self.forward_features( | |
| src_audio, modality="audio" | |
| ) # features: [B, F, T] | |
| features_video = self.forward_features(src_video, modality="video") | |
| if self.modality == "audio": | |
| features_video = 0 * features_video | |
| elif self.modality == "video": | |
| features_audio = 0 * features_audio | |
| else: | |
| if self.training: | |
| modality_drop_prob, audio_drop_prob = ( | |
| np.random.random(), | |
| np.random.random(), | |
| ) | |
| if modality_drop_prob < self.modality_dropout: | |
| if audio_drop_prob < self.audio_dropout: | |
| features_audio = 0 * features_audio | |
| else: | |
| features_video = 0 * features_video | |
| if self.modality_fuse == "concat": | |
| features = torch.cat([features_audio, features_video], dim=1) | |
| elif self.modality_fuse == "add": | |
| features = features_audio + features_video | |
| if target_list is not None: | |
| features, mask_indices, target_list = self.forward_targets( | |
| features, mask_indices, target_list | |
| ) | |
| features_pen = features.float().pow(2).mean() | |
| features = features.transpose(1, 2) | |
| features = self.layer_norm(features) | |
| if padding_mask is not None: | |
| padding_mask = self.forward_padding_mask(features, padding_mask) | |
| if self.post_extract_proj is not None: | |
| features = self.post_extract_proj(features) | |
| features = self.dropout_input(features) | |
| if self.masking_type == "feature" and mask: | |
| x, mask_indices = self.apply_feature_mask( | |
| features, padding_mask, target_list | |
| ) | |
| else: | |
| x = features | |
| # feature: (B, T, D), float | |
| # target: (B, T), long | |
| # x: (B, T, D), float | |
| # padding_mask: (B, T), bool | |
| # mask_indices: (B, T), bool | |
| x = self.encoder(x, attention_mask=padding_mask)[0] | |
| # x = self.encoder( | |
| # x, | |
| # # attention_mask=padding_mask, | |
| # # layer=None if output_layer is None else output_layer - 1 | |
| # )[0] | |
| if features_only: | |
| return {"x": x, "padding_mask": padding_mask, "features": features} | |
| label_embs_list = self.label_embs_concat.split(self.num_classes, 0) | |
| proj_x = self.final_proj(x) | |
| if self.untie_final_proj: | |
| proj_x_list = proj_x.chunk(len(self.num_classes), dim=-1) | |
| else: | |
| proj_x_list = [proj_x for _ in self.num_classes] | |
| logit_list = [ | |
| self.compute_logits(proj, emb).view(-1, num_class) | |
| for proj, emb, num_class in zip( | |
| proj_x_list, label_embs_list, self.num_classes | |
| ) | |
| ] # [[B*T, V]] | |
| mask, unmask = ( | |
| torch.logical_and(mask_indices, ~padding_mask).view(-1), | |
| torch.logical_and(~mask_indices, ~padding_mask).view(-1), | |
| ) # [B*T] | |
| logit_m_list, logit_u_list = ( | |
| [logit[mask] for logit in logit_list], | |
| [logit[unmask] for logit in logit_list], | |
| ) | |
| target_m_list, target_u_list = ( | |
| [target.view(-1)[mask].long() for target in target_list], | |
| [target.view(-1)[unmask].long() for target in target_list], | |
| ) | |
| result = { | |
| "logit_m_list": logit_m_list, | |
| "logit_u_list": logit_u_list, | |
| "target_m_list": target_m_list, | |
| "target_u_list": target_u_list, | |
| "padding_mask": padding_mask, | |
| "features_pen": features_pen, | |
| } | |
| return result | |
| def forward( | |
| self, | |
| input_features: torch.Tensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| video: torch.Tensor = None, | |
| **kwargs, | |
| ) -> Tuple[torch.Tensor, torch.Tensor]: | |
| res = self.forward_gen( | |
| {"audio": input_features, "video": video}, | |
| padding_mask=attention_mask, | |
| mask=False, | |
| features_only=True, | |
| output_layer=None, | |
| ) | |
| feature = res["x"] | |
| return BaseModelOutput( | |
| last_hidden_state=feature, hidden_states=None, attentions=None | |
| ) | |
| def extract_features( | |
| self, | |
| source: torch.Tensor, | |
| padding_mask: Optional[torch.Tensor] = None, | |
| mask: bool = False, | |
| ret_conv: bool = False, | |
| output_layer: Optional[int] = None, | |
| ) -> Tuple[torch.Tensor, torch.Tensor]: | |
| res = self.forward_gen( | |
| source, | |
| padding_mask=padding_mask, | |
| mask=mask, | |
| features_only=True, | |
| output_layer=output_layer, | |
| ) | |
| feature = res["features"] if ret_conv else res["x"] | |
| return feature, res["padding_mask"] | |
| def extract_finetune( | |
| self, source, padding_mask=None, mask=False, ret_conv=False, output_layer=None | |
| ): | |
| src_audio, src_video = source["audio"], source["video"] | |
| if mask and self.masking_type == "input": | |
| src_video, mask_indices_video = self.apply_input_mask( | |
| src_video, padding_mask, target_list=None | |
| ) | |
| src_audio, mask_indices_audio = self.apply_input_mask( | |
| src_audio, padding_mask, target_list=None | |
| ) | |
| mask_indices = torch.logical_or( | |
| mask_indices_audio, mask_indices_video | |
| ) # mask_indices not used in fine-tuning | |
| else: | |
| src_audio, src_video, mask_indices = src_audio, src_video, None # noqa: F841 | |
| if src_audio is not None and src_video is None: | |
| features_audio = self.forward_features( | |
| src_audio, modality="audio" | |
| ) # features: [B, F, T] | |
| features_video = features_audio.new_zeros( | |
| features_audio.size(0), self.encoder_embed_dim, features_audio.size(-1) | |
| ) | |
| elif src_audio is None and src_video is not None: | |
| features_video = self.forward_features(src_video, modality="video") | |
| features_audio = features_video.new_zeros( | |
| features_video.size(0), self.encoder_embed_dim, features_video.size(-1) | |
| ) | |
| elif src_audio is not None and src_video is not None: | |
| features_video = self.forward_features(src_video, modality="video") | |
| features_audio = self.forward_features( | |
| src_audio, modality="audio" | |
| ) # features: [B, F, T] | |
| if self.modality_fuse == "concat": | |
| features = torch.cat([features_audio, features_video], dim=1) | |
| elif self.modality_fuse == "add": | |
| features = features_audio + features_video | |
| features.float().pow(2).mean() | |
| features = features.transpose(1, 2) | |
| features = self.layer_norm(features) | |
| unmasked_features = features.clone() | |
| if padding_mask is not None: | |
| padding_mask = self.forward_padding_mask(features, padding_mask) | |
| if self.post_extract_proj is not None: | |
| features = self.post_extract_proj(features) | |
| features = self.dropout_input(features) | |
| unmasked_features = self.dropout_features(unmasked_features) | |
| x = features | |
| # feature: (B, T, D), float | |
| # target: (B, T), long | |
| # x: (B, T, D), float | |
| # padding_mask: (B, T), bool | |
| # mask_indices: (B, T), bool | |
| x = self.encoder( | |
| x, | |
| # padding_mask=padding_mask, | |
| # layer=None if output_layer is None else output_layer - 1 | |
| )[0] | |
| return x, padding_mask | |
| def get_extra_losses(self, net_output): | |
| extra_losses = [] | |
| names = [] | |
| if "features_pen" in net_output: | |
| extra_losses.append(net_output["features_pen"]) | |
| names.append("features_pen") | |
| return extra_losses, names | |
| def remove_pretraining_modules(self): | |
| self.target_glu = None | |
| self.final_proj = None | |
| def get_logits(self, net_output, is_masked=True): | |
| raise NotImplementedError | |
| def get_targets(self, net_output, is_masked=True): | |
| raise NotImplementedError | |
| def compute_nce(self, x, pos, negs): | |
| neg_is_pos = (pos == negs).all(-1) | |
| pos = pos.unsqueeze(0) | |
| targets = torch.cat([pos, negs], dim=0) | |
| logits = torch.cosine_similarity(x.float(), targets.float(), dim=-1).type_as(x) | |
| logits /= self.logit_temp | |
| if neg_is_pos.any(): | |
| logits[1:][neg_is_pos] = float("-inf") | |
| logits = logits.transpose(0, 1) # (num_x, num_cls+1) | |
| return logits | |
| class AVHubertEncoder(Wav2Vec2Encoder): | |
| def __init__(self, config): | |
| super().__init__(config) | |
| self.layers = nn.ModuleList( | |
| [AVHubertEncoderLayer(config) for _ in range(config.num_hidden_layers)] | |
| ) | |
| def forward( | |
| self, | |
| hidden_states: torch.tensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| output_attentions: bool = False, | |
| output_hidden_states: bool = False, | |
| return_dict: bool = True, | |
| ): | |
| all_hidden_states = () if output_hidden_states else None | |
| all_self_attentions = () if output_attentions else None | |
| if attention_mask is not None: | |
| # make sure padded tokens output 0 | |
| expand_attention_mask = attention_mask.unsqueeze(-1).repeat( | |
| 1, 1, hidden_states.shape[2] | |
| ) | |
| hidden_states[~expand_attention_mask] = 0 | |
| if self._use_flash_attention_2: | |
| # 2d mask is passed through the layers | |
| attention_mask = ( | |
| attention_mask | |
| if (attention_mask is not None and 0 in attention_mask) | |
| else None | |
| ) | |
| else: | |
| # extend attention_mask | |
| attention_mask = 1.0 - attention_mask[:, None, None, :].to( | |
| dtype=hidden_states.dtype | |
| ) | |
| attention_mask = attention_mask * torch.finfo(hidden_states.dtype).min | |
| attention_mask = attention_mask.expand( | |
| attention_mask.shape[0], | |
| 1, | |
| attention_mask.shape[-1], | |
| attention_mask.shape[-1], | |
| ) | |
| position_embeddings = self.pos_conv_embed(hidden_states) | |
| hidden_states = hidden_states + position_embeddings | |
| # hidden_states = self.layer_norm(hidden_states) | |
| hidden_states = self.dropout(hidden_states) | |
| deepspeed_zero3_is_enabled = is_deepspeed_zero3_enabled() | |
| for layer in self.layers: | |
| if output_hidden_states: | |
| all_hidden_states = all_hidden_states + (hidden_states,) | |
| # add LayerDrop (see https://arxiv.org/abs/1909.11556 for description) | |
| dropout_probability = torch.rand([]) | |
| skip_the_layer = ( | |
| True | |
| if self.training and (dropout_probability < self.config.layerdrop) | |
| else False | |
| ) | |
| if not skip_the_layer or deepspeed_zero3_is_enabled: | |
| # under deepspeed zero3 all gpus must run in sync | |
| if self.gradient_checkpointing and self.training: | |
| layer_outputs = self._gradient_checkpointing_func( | |
| layer.__call__, | |
| hidden_states, | |
| attention_mask, | |
| output_attentions, | |
| ) | |
| else: | |
| layer_outputs = layer( | |
| hidden_states, | |
| attention_mask=attention_mask, | |
| output_attentions=output_attentions, | |
| ) | |
| hidden_states = layer_outputs[0] | |
| if skip_the_layer: | |
| layer_outputs = (None, None) | |
| if output_attentions: | |
| all_self_attentions = all_self_attentions + (layer_outputs[1],) | |
| hidden_states = self.layer_norm(hidden_states) | |
| if output_hidden_states: | |
| all_hidden_states = all_hidden_states + (hidden_states,) | |
| if not return_dict: | |
| return tuple( | |
| v | |
| for v in [hidden_states, all_hidden_states, all_self_attentions] | |
| if v is not None | |
| ) | |
| return BaseModelOutput( | |
| last_hidden_state=hidden_states, | |
| hidden_states=all_hidden_states, | |
| attentions=all_self_attentions, | |
| ) | |
| class AVHubertEncoderLayer(Wav2Vec2EncoderLayer): | |
| def forward(self, hidden_states, attention_mask=None, output_attentions=False): | |
| attn_residual = hidden_states | |
| hidden_states = self.layer_norm(hidden_states) | |
| hidden_states, attn_weights, _ = self.attention( | |
| hidden_states, | |
| attention_mask=attention_mask, | |
| output_attentions=output_attentions, | |
| ) | |
| hidden_states = self.dropout(hidden_states) | |
| hidden_states = attn_residual + hidden_states | |
| # hidden_states = self.layer_norm(hidden_states) | |
| residual = hidden_states | |
| hidden_states = self.final_layer_norm(hidden_states) | |
| hidden_states = residual + self.feed_forward(hidden_states) | |
| # hidden_states = self.final_layer_norm(hidden_states) | |
| outputs = (hidden_states,) | |
| if output_attentions: | |
| outputs += (attn_weights,) | |
| return outputs | |