NeuraCraft commited on
Commit
7c89374
·
verified ·
1 Parent(s): d4694ba

Upload Main Files

Browse files
configuration_mini_whisper.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # configuration_mini_whisper.py
2
+ from transformers import PretrainedConfig
3
+
4
+ NON_SPEECH_TOKENS = [
5
+ 1, 2, 7, 8, 9, 10, 14, 25,
6
+ 26, 27, 28, 29, 31, 58, 59, 60, 61, 62,
7
+ 63, 90, 91, 92, 93, 357, 366, 438, 532, 685,
8
+ 705, 796, 930, 1058, 1220, 1267, 1279, 1303, 1343, 1377,
9
+ 1391, 1635, 1782, 1875, 2162, 2361, 2488, 3467, 4008, 4211,
10
+ 4600, 4808, 5299, 5855, 6329, 7203, 9609, 9959, 10563, 10786,
11
+ 11420, 11709, 11907, 13163, 13697, 13700, 14808, 15306, 16410, 16791,
12
+ 17992, 19203, 19510, 20724, 22305, 22935, 27007, 30109, 30420, 33409,
13
+ 34949, 40283, 40493, 40549, 47282, 49146, 50257, 50359, 50360, 50361
14
+ ]
15
+
16
+ NON_SPEECH_TOKENS_MULTI = [
17
+ 1, 2, 7, 8, 9, 10, 14, 25,
18
+ 26, 27, 28, 29, 31, 58, 59, 60, 61, 62,
19
+ 63, 90, 91, 92, 93, 359, 503, 522, 542, 873,
20
+ 893, 902, 918, 922, 931, 1350, 1853, 1982, 2460, 2627,
21
+ 3246, 3253, 3268, 3536, 3846, 3961, 4183, 4667, 6585, 6647,
22
+ 7273, 9061, 9383, 10428, 10929, 11938, 12033, 12331, 12562, 13793,
23
+ 14157, 14635, 15265, 15618, 16553, 16604, 18362, 18956, 20075, 21675,
24
+ 22520, 26130, 26161, 26435, 28279, 29464, 31650, 32302, 32470, 36865,
25
+ 42863, 47425, 49870, 50254, 50258, 50360, 50361, 50362
26
+ ]
27
+
28
+ class MiniWhisperConfig(PretrainedConfig):
29
+ model_type = "mini_whisper"
30
+ keys_to_ignore_at_inference = ["past_key_values"]
31
+ attribute_map = {
32
+ "num_key_value_heads": "encoder_attention_heads",
33
+ "num_attention_heads": "encoder_attention_heads",
34
+ "hidden_size": "d_model",
35
+ "num_hidden_layers": "encoder_layers",
36
+ }
37
+
38
+ vocab_size: int = 51865
39
+ num_mel_bins: int = 80
40
+ encoder_layers: int = 4
41
+ encoder_attention_heads: int = 6
42
+ decoder_layers: int = 4
43
+ decoder_attention_heads: int = 6
44
+ decoder_ffn_dim: int = 1536
45
+ encoder_ffn_dim: int = 1536
46
+ encoder_layerdrop: float | int = 0.0
47
+ decoder_layerdrop: float | int = 0.0
48
+ decoder_start_token_id: int = 50257
49
+ use_cache: bool = True
50
+ is_encoder_decoder: bool = True
51
+ activation_function: str = "gelu"
52
+ d_model: int = 384
53
+ dropout: float | int = 0.0
54
+ attention_dropout: float | int = 0.0
55
+ activation_dropout: float | int = 0.0
56
+ init_std: float = 0.02
57
+ scale_embedding: bool = False
58
+ max_source_positions: int = 1500
59
+ max_target_positions: int = 448
60
+ pad_token_id: int | None = 50256
61
+ bos_token_id: int | None = 50256
62
+ eos_token_id: int | list[int] | None = 50256
63
+ suppress_tokens: list | None = None
64
+ begin_suppress_tokens: list[int] | tuple[int, ...] | None = (220, 50256)
65
+ use_weighted_layer_sum: bool = False
66
+ classifier_proj_size: int = 256
67
+ apply_spec_augment: bool = False
68
+ mask_time_prob: float | int = 0.05
69
+ mask_time_length: int = 10
70
+ mask_time_min_masks: int = 2
71
+ mask_feature_prob: float | int = 0.0
72
+ mask_feature_length: int = 10
73
+ mask_feature_min_masks: int = 0
74
+ median_filter_width: int = 7
75
+ tie_word_embeddings: bool = True
76
+
77
+ def __init__(
78
+ self,
79
+ vocab_size=51865,
80
+ num_mel_bins=80,
81
+ d_model=384,
82
+ encoder_layers=4,
83
+ decoder_layers=4,
84
+ encoder_attention_heads=6,
85
+ decoder_attention_heads=6,
86
+ encoder_ffn_dim=1536,
87
+ decoder_ffn_dim=1536,
88
+ encoder_layerdrop=0.0,
89
+ decoder_layerdrop=0.0,
90
+ decoder_start_token_id=50257,
91
+ use_cache=True,
92
+ is_encoder_decoder=True,
93
+ activation_function="gelu",
94
+ dropout=0.0,
95
+ attention_dropout=0.0,
96
+ activation_dropout=0.0,
97
+ init_std=0.02,
98
+ scale_embedding=False,
99
+ max_source_positions=1500,
100
+ max_target_positions=448,
101
+ pad_token_id=50256,
102
+ bos_token_id=50256,
103
+ eos_token_id=50256,
104
+ suppress_tokens=None,
105
+ begin_suppress_tokens=(220, 50256),
106
+ use_weighted_layer_sum=False,
107
+ classifier_proj_size=256,
108
+ apply_spec_augment=False,
109
+ mask_time_prob=0.05,
110
+ mask_time_length=10,
111
+ mask_time_min_masks=2,
112
+ mask_feature_prob=0.0,
113
+ mask_feature_length=10,
114
+ mask_feature_min_masks=0,
115
+ median_filter_width=7,
116
+ tie_word_embeddings=True,
117
+ **kwargs
118
+ ):
119
+ self.vocab_size = vocab_size
120
+ self.num_mel_bins = num_mel_bins
121
+ self.d_model = d_model
122
+ self.encoder_layers = encoder_layers
123
+ self.decoder_layers = decoder_layers
124
+ self.encoder_attention_heads = encoder_attention_heads
125
+ self.decoder_attention_heads = decoder_attention_heads
126
+ self.encoder_ffn_dim = encoder_ffn_dim
127
+ self.decoder_ffn_dim = decoder_ffn_dim
128
+ self.encoder_layerdrop = encoder_layerdrop
129
+ self.decoder_layerdrop = decoder_layerdrop
130
+ self.decoder_start_token_id = decoder_start_token_id
131
+ self.use_cache = use_cache
132
+ self.is_encoder_decoder = is_encoder_decoder
133
+ self.activation_function = activation_function
134
+ self.dropout = dropout
135
+ self.attention_dropout = attention_dropout
136
+ self.activation_dropout = activation_dropout
137
+ self.init_std = init_std
138
+ self.scale_embedding = scale_embedding
139
+ self.max_source_positions = max_source_positions
140
+ self.max_target_positions = max_target_positions
141
+ self.pad_token_id = pad_token_id
142
+ self.bos_token_id = bos_token_id
143
+ self.eos_token_id = eos_token_id
144
+ self.suppress_tokens = suppress_tokens if suppress_tokens is not None else NON_SPEECH_TOKENS
145
+ self.begin_suppress_tokens = list(begin_suppress_tokens)
146
+ self.use_weighted_layer_sum = use_weighted_layer_sum
147
+ self.classifier_proj_size = classifier_proj_size
148
+ self.apply_spec_augment = apply_spec_augment
149
+ self.mask_time_prob = mask_time_prob
150
+ self.mask_time_length = mask_time_length
151
+ self.mask_time_min_masks = mask_time_min_masks
152
+ self.mask_feature_prob = mask_feature_prob
153
+ self.mask_feature_length = mask_feature_length
154
+ self.mask_feature_min_masks = mask_feature_min_masks
155
+ self.median_filter_width = median_filter_width
156
+ self.tie_word_embeddings = tie_word_embeddings
157
+
158
+ super().__init__(
159
+ pad_token_id=pad_token_id,
160
+ bos_token_id=bos_token_id,
161
+ eos_token_id=eos_token_id,
162
+ decoder_start_token_id=decoder_start_token_id,
163
+ tie_word_embeddings=tie_word_embeddings,
164
+ **kwargs,
165
+ )
166
+
167
+ __all__ = ["MiniWhisperConfig"]
feature_extraction_mini_whisper.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # feature_extraction_mini_whisper.py
2
+ import numpy as np
3
+ import torch
4
+
5
+
6
+ class MiniWhisperFeatureExtractor:
7
+ """Feature extractor matching Whisper exactly."""
8
+
9
+ model_input_names = ["input_features"]
10
+
11
+ def __init__(
12
+ self,
13
+ feature_size=80,
14
+ sampling_rate=16000,
15
+ hop_length=160,
16
+ chunk_length=30,
17
+ n_fft=400,
18
+ padding_value=0.0,
19
+ dither=0.0,
20
+ return_attention_mask=False,
21
+ **kwargs,
22
+ ):
23
+ self.feature_size = feature_size
24
+ self.sampling_rate = sampling_rate
25
+ self.hop_length = hop_length
26
+ self.chunk_length = chunk_length
27
+ self.n_fft = n_fft
28
+ self.n_samples = chunk_length * sampling_rate
29
+ self.nb_max_frames = self.n_samples // hop_length
30
+ self.padding_value = padding_value
31
+ self.dither = dither
32
+ self.return_attention_mask = return_attention_mask
33
+
34
+ self.mel_filters = self._mel_filter_bank()
35
+
36
+ def _mel_filter_bank(self):
37
+ """Create mel filter bank matching Whisper's parameters."""
38
+ try:
39
+ from torchaudio.functional import melscale_fbanks
40
+ n_freq_bins = 1 + self.n_fft // 2
41
+ mel_filters = melscale_fbanks(
42
+ n_freqs=n_freq_bins,
43
+ f_min=0.0,
44
+ f_max=8000.0,
45
+ n_mels=self.feature_size,
46
+ sample_rate=self.sampling_rate,
47
+ norm="slaney",
48
+ mel_scale="slaney",
49
+ )
50
+ return mel_filters.numpy()
51
+ except ImportError:
52
+ return self._numpy_mel_filter_bank()
53
+
54
+ def _numpy_mel_filter_bank(self):
55
+ """NumPy fallback for mel filter bank."""
56
+ def hz_to_mel(hz):
57
+ return 2595 * np.log10(1 + hz / 700)
58
+
59
+ def mel_to_hz(mel):
60
+ return 700 * (10 ** (mel / 2595) - 1)
61
+
62
+ n_freq_bins = 1 + self.n_fft // 2
63
+ fmin, fmax = 0.0, 8000.0
64
+
65
+ mel_min = hz_to_mel(fmin)
66
+ mel_max = hz_to_mel(fmax)
67
+ mel_points = np.linspace(mel_min, mel_max, self.feature_size + 2)
68
+ hz_points = mel_to_hz(mel_points)
69
+
70
+ bin_points = np.floor((self.n_fft + 1) * hz_points / self.sampling_rate).astype(int)
71
+
72
+ filters = np.zeros((self.feature_size, n_freq_bins))
73
+ for i in range(self.feature_size):
74
+ left, center, right = bin_points[i], bin_points[i + 1], bin_points[i + 2]
75
+ for j in range(left, center):
76
+ filters[i, j] = (j - left) / (center - left)
77
+ for j in range(center, right):
78
+ filters[i, j] = (right - j) / (right - center)
79
+
80
+ filters = filters / (filters.sum(axis=1, keepdims=True) + 1e-10)
81
+ return filters
82
+
83
+ def _np_extract_fbank_features(self, waveform_batch, device="cpu"):
84
+ """NumPy implementation matching Whisper exactly."""
85
+ log_spec_batch = []
86
+ for waveform in waveform_batch:
87
+ # Apply dither
88
+ if self.dither != 0.0:
89
+ waveform = waveform + self.dither * np.random.randn(*waveform.shape).astype(np.float32)
90
+
91
+ # Compute STFT
92
+ n_frames = 1 + (len(waveform) - self.n_fft) // self.hop_length
93
+ spec = np.zeros((self.n_fft // 2 + 1, n_frames), dtype=np.float32)
94
+
95
+ window = np.hanning(self.n_fft).astype(np.float32)
96
+ for i in range(n_frames):
97
+ start = i * self.hop_length
98
+ frame = waveform[start:start + self.n_fft]
99
+ if len(frame) < self.n_fft:
100
+ frame = np.pad(frame, (0, self.n_fft - len(frame)))
101
+ spec[:, i] = np.abs(np.fft.rfft(frame * window)) ** 2
102
+
103
+ # Apply mel filters: mel_filters is [n_mels, n_freqs]
104
+ mel_spec = self.mel_filters @ spec
105
+
106
+ # Log10 with floor
107
+ log_spec = np.log10(np.maximum(mel_spec, 1e-10))
108
+
109
+ # Whisper uses: np.maximum(log_spec, log_spec.max() - 8.0)
110
+ log_spec = np.maximum(log_spec, log_spec.max() - 8.0)
111
+
112
+ # Normalize: (log_spec + 4.0) / 4.0
113
+ log_spec = (log_spec + 4.0) / 4.0
114
+
115
+ # Remove last frame to match Whisper
116
+ log_spec = log_spec[:, :-1]
117
+
118
+ log_spec_batch.append(log_spec)
119
+
120
+ return np.array(log_spec_batch)
121
+
122
+ def _torch_extract_fbank_features(self, waveform, device="cpu"):
123
+ """PyTorch implementation matching Whisper exactly."""
124
+ waveform = torch.from_numpy(waveform).to(device, torch.float32)
125
+ window = torch.hann_window(self.n_fft, device=device)
126
+
127
+ if self.dither != 0.0:
128
+ waveform += self.dither * torch.randn(waveform.shape, dtype=waveform.dtype, device=waveform.device)
129
+
130
+ stft = torch.stft(waveform, self.n_fft, self.hop_length, window=window, return_complex=True)
131
+ magnitudes = stft[..., :-1].abs() ** 2 # Remove last frame
132
+
133
+ # mel_filters shape: [n_mels, n_freqs] from mel_filter_bank
134
+ mel_filters = torch.from_numpy(self.mel_filters).to(device, torch.float32)
135
+ # Transpose and matmul: mel_filters.T @ magnitudes
136
+ mel_spec = mel_filters.T @ magnitudes
137
+
138
+ log_spec = torch.clamp(mel_spec, min=1e-10).log10()
139
+
140
+ if waveform.dim() == 2:
141
+ max_val = log_spec.max(dim=2, keepdim=True)[0].max(dim=1, keepdim=True)[0]
142
+ log_spec = torch.maximum(log_spec, max_val - 8.0)
143
+ else:
144
+ log_spec = torch.maximum(log_spec, log_spec.max() - 8.0)
145
+
146
+ log_spec = (log_spec + 4.0) / 4.0
147
+
148
+ if device != "cpu":
149
+ log_spec = log_spec.detach().cpu()
150
+
151
+ return log_spec.numpy()
152
+
153
+ @staticmethod
154
+ def zero_mean_unit_var_norm(input_values, attention_mask, padding_value=0.0):
155
+ if attention_mask is not None:
156
+ attention_mask = np.array(attention_mask, np.int32)
157
+ normed = []
158
+ for vector, length in zip(input_values, attention_mask.sum(-1)):
159
+ normed_slice = (vector - vector[:length].mean()) / (np.sqrt(vector[:length].var() + 1e-7))
160
+ if length < normed_slice.shape[0]:
161
+ normed_slice[length:] = padding_value
162
+ normed.append(normed_slice)
163
+ else:
164
+ normed = [(x - x.mean()) / (np.sqrt(x.var() + 1e-7)) for x in input_values]
165
+ return normed
166
+
167
+ def __call__(
168
+ self,
169
+ raw_speech,
170
+ sampling_rate=None,
171
+ truncation=True,
172
+ return_tensors="pt",
173
+ return_attention_mask=None,
174
+ do_normalize=False,
175
+ **kwargs,
176
+ ):
177
+ if sampling_rate is not None and sampling_rate != self.sampling_rate:
178
+ raise ValueError(f"Expected {self.sampling_rate}, got {sampling_rate}")
179
+
180
+ is_batched = isinstance(raw_speech, list) or (isinstance(raw_speech, np.ndarray) and raw_speech.ndim > 1)
181
+
182
+ if not is_batched:
183
+ raw_speech = [raw_speech]
184
+
185
+ # Convert to numpy float32
186
+ processed = []
187
+ for audio in raw_speech:
188
+ if isinstance(audio, np.ndarray) and audio.dtype == np.float64:
189
+ audio = audio.astype(np.float32)
190
+ elif not isinstance(audio, np.ndarray):
191
+ audio = np.array(audio, dtype=np.float32)
192
+
193
+ # Pad or truncate
194
+ if len(audio) > self.n_samples:
195
+ if truncation:
196
+ audio = audio[:self.n_samples]
197
+ elif len(audio) < self.n_samples:
198
+ audio = np.pad(audio, (0, self.n_samples - len(audio)), constant_values=self.padding_value)
199
+
200
+ processed.append(audio)
201
+
202
+ waveform_batch = np.stack(processed, axis=0)
203
+
204
+ # Use torch if available
205
+ if torch.cuda.is_available():
206
+ input_features = self._torch_extract_fbank_features(waveform_batch, device="cuda")
207
+ # Result is [batch, n_mels, n_frames]
208
+ else:
209
+ input_features = self._np_extract_fbank_features(waveform_batch)
210
+
211
+ result = {"input_features": input_features}
212
+
213
+ if return_attention_mask is None:
214
+ return_attention_mask = self.return_attention_mask
215
+
216
+ if return_attention_mask:
217
+ result["attention_mask"] = np.ones(input_features.shape[0], dtype=np.int64)
218
+
219
+ if return_tensors == "pt":
220
+ result["input_features"] = torch.from_numpy(input_features).float()
221
+ if "attention_mask" in result:
222
+ result["attention_mask"] = torch.from_numpy(result["attention_mask"]).long()
223
+
224
+ return result
225
+
226
+ def to_dict(self):
227
+ return {
228
+ "feature_size": self.feature_size,
229
+ "sampling_rate": self.sampling_rate,
230
+ "hop_length": self.hop_length,
231
+ "n_fft": self.n_fft,
232
+ "chunk_length": self.chunk_length,
233
+ "padding_value": self.padding_value,
234
+ "dither": self.dither,
235
+ }
236
+
237
+ def save_pretrained(self, save_directory):
238
+ import json, os
239
+ os.makedirs(save_directory, exist_ok=True)
240
+ with open(os.path.join(save_directory, "preprocessor_config.json"), "w") as f:
241
+ json.dump(self.to_dict(), f, indent=2)
242
+
243
+ @classmethod
244
+ def from_pretrained(cls, save_directory):
245
+ import json, os
246
+ path = os.path.join(save_directory, "preprocessor_config.json")
247
+ if os.path.exists(path):
248
+ with open(path) as f:
249
+ return cls(**json.load(f))
250
+ return cls()
251
+
252
+
253
+ __all__ = ["MiniWhisperFeatureExtractor"]
mini_whisper_model.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mini_whisper_model.py - Backwards compatibility alias
2
+ from configuration_mini_whisper import MiniWhisperConfig
3
+ from modeling_mini_whisper import (
4
+ MiniWhisperForConditionalGeneration,
5
+ MiniWhisperModel,
6
+ MiniWhisperEncoder,
7
+ MiniWhisperDecoder,
8
+ )
9
+
10
+ __all__ = [
11
+ "MiniWhisperConfig",
12
+ "MiniWhisperForConditionalGeneration",
13
+ "MiniWhisperModel",
14
+ "MiniWhisperEncoder",
15
+ "MiniWhisperDecoder",
16
+ ]
modeling_mini_whisper.py ADDED
@@ -0,0 +1,738 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modeling_mini_whisper.py
2
+ import math
3
+ from collections.abc import Callable
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ from torch.nn import functional as F
8
+ from transformers import PreTrainedModel, GenerationMixin
9
+ from transformers.modeling_outputs import (
10
+ BaseModelOutput,
11
+ BaseModelOutputWithPastAndCrossAttentions,
12
+ CausalLMOutputWithCrossAttentions,
13
+ Seq2SeqLMOutput,
14
+ Seq2SeqModelOutput,
15
+ )
16
+ from transformers.activations import ACT2FN
17
+ from transformers.utils import logging
18
+ from configuration_mini_whisper import MiniWhisperConfig
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ _HIDDEN_STATES_START_POSITION = 1
23
+
24
+ def sinusoids(length: int, channels: int, max_timescale: float = 10000) -> torch.Tensor:
25
+ """Returns sinusoids for positional embedding"""
26
+ if channels % 2 != 0:
27
+ raise ValueError(f"Number of channels has to be divisible by 2, got {channels} channels.")
28
+ log_timescale_increment = math.log(max_timescale) / (channels // 2 - 1)
29
+ inv_timescales = torch.exp(-log_timescale_increment * torch.arange(channels // 2))
30
+ scaled_time = torch.arange(length).view(-1, 1) * inv_timescales.view(1, -1)
31
+ return torch.cat([scaled_time.sin(), scaled_time.cos()], dim=1)
32
+
33
+
34
+ def shift_tokens_right(input_ids: torch.Tensor, pad_token_id: int, decoder_start_token_id: int):
35
+ """Shift input ids one token to the right."""
36
+ shifted_input_ids = input_ids.new_zeros(input_ids.shape)
37
+ shifted_input_ids[:, 1:] = input_ids[:, :-1].clone()
38
+ shifted_input_ids[:, 0] = decoder_start_token_id
39
+
40
+ if pad_token_id is None:
41
+ raise ValueError("pad_token_id has to be defined.")
42
+ shifted_input_ids.masked_fill_(shifted_input_ids == -100, pad_token_id)
43
+
44
+ return shifted_input_ids
45
+
46
+ def _compute_mask_indices(
47
+ shape: tuple[int, int],
48
+ mask_prob: float,
49
+ mask_length: int,
50
+ attention_mask: torch.LongTensor | None = None,
51
+ min_masks: int = 0,
52
+ ):
53
+ """Computes random mask spans for SpecAugment"""
54
+ import numpy as np
55
+
56
+ batch_size, sequence_length = shape
57
+
58
+ if mask_length < 1:
59
+ raise ValueError(f"mask_length has to be bigger than 0.")
60
+ if mask_length > sequence_length:
61
+ raise ValueError(f"mask_length has to be smaller than sequence_length.")
62
+
63
+ epsilon = np.random.rand(1).item()
64
+
65
+ def compute_num_masked_span(input_length):
66
+ num_masked_span = int(mask_prob * input_length / mask_length + epsilon)
67
+ num_masked_span = max(num_masked_span, min_masks)
68
+ if num_masked_span * mask_length > sequence_length:
69
+ num_masked_span = sequence_length // mask_length
70
+ if input_length - (mask_length - 1) < num_masked_span:
71
+ num_masked_span = max(input_length - (mask_length - 1), 0)
72
+ return num_masked_span
73
+
74
+ input_lengths = (
75
+ attention_mask.detach().sum(-1).tolist()
76
+ if attention_mask is not None
77
+ else [sequence_length for _ in range(batch_size)]
78
+ )
79
+
80
+ spec_aug_mask = np.zeros((batch_size, sequence_length), dtype=bool)
81
+ spec_aug_mask_idxs = []
82
+ max_num_masked_span = compute_num_masked_span(sequence_length)
83
+
84
+ if max_num_masked_span == 0:
85
+ return spec_aug_mask
86
+
87
+ for input_length in input_lengths:
88
+ num_masked_span = compute_num_masked_span(input_length)
89
+ spec_aug_mask_idx = np.random.choice(
90
+ np.arange(input_length - (mask_length - 1)), num_masked_span, replace=False
91
+ )
92
+
93
+ if len(spec_aug_mask_idx) == 0:
94
+ dummy_mask_idx = sequence_length - 1
95
+ else:
96
+ dummy_mask_idx = spec_aug_mask_idx[0]
97
+
98
+ spec_aug_mask_idx = np.concatenate(
99
+ [spec_aug_mask_idx, np.ones(max_num_masked_span - num_masked_span, dtype=np.int32) * dummy_mask_idx]
100
+ )
101
+ spec_aug_mask_idxs.append(spec_aug_mask_idx)
102
+
103
+ spec_aug_mask_idxs = np.array(spec_aug_mask_idxs)
104
+ spec_aug_mask_idxs = np.broadcast_to(
105
+ spec_aug_mask_idxs[:, :, None], (batch_size, max_num_masked_span, mask_length)
106
+ )
107
+ spec_aug_mask_idxs = spec_aug_mask_idxs.reshape(batch_size, max_num_masked_span * mask_length)
108
+
109
+ offsets = np.arange(mask_length)[None, None, :]
110
+ offsets = np.broadcast_to(offsets, (batch_size, max_num_masked_span, mask_length)).reshape(
111
+ batch_size, max_num_masked_span * mask_length
112
+ )
113
+ spec_aug_mask_idxs = spec_aug_mask_idxs + offsets
114
+
115
+ if spec_aug_mask_idxs.max() > sequence_length - 1:
116
+ spec_aug_mask_idxs[spec_aug_mask_idxs > sequence_length - 1] = sequence_length - 1
117
+
118
+ np.put_along_axis(spec_aug_mask, spec_aug_mask_idxs, 1, -1)
119
+
120
+ return spec_aug_mask
121
+
122
+ class WhisperPositionalEmbedding(nn.Module):
123
+ def __init__(self, num_positions: int, embedding_dim: int, padding_idx: int | None = None):
124
+ super().__init__()
125
+ self.num_positions = num_positions
126
+ self.embedding_dim = embedding_dim
127
+ self.weight = nn.Parameter(torch.zeros(num_positions, embedding_dim))
128
+ self._init_weights()
129
+
130
+ def _init_weights(self):
131
+ with torch.no_grad():
132
+ self.weight.copy_(sinusoids(self.num_positions, self.embedding_dim))
133
+
134
+ def forward(self, input_ids, past_key_values_length=0, position_ids=None):
135
+ if position_ids is None:
136
+ return self.weight[past_key_values_length : past_key_values_length + input_ids.shape[1]]
137
+ else:
138
+ return self.weight[position_ids]
139
+
140
+ class MiniWhisperAttention(nn.Module):
141
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
142
+
143
+ def __init__(
144
+ self,
145
+ embed_dim: int,
146
+ num_heads: int,
147
+ dropout: float = 0.0,
148
+ is_decoder: bool = False,
149
+ bias: bool = True,
150
+ is_causal: bool = False,
151
+ layer_idx: int | None = None,
152
+ config: MiniWhisperConfig | None = None,
153
+ ):
154
+ super().__init__()
155
+ self.embed_dim = embed_dim
156
+ self.num_heads = num_heads
157
+ self.dropout = dropout
158
+ self.head_dim = embed_dim // num_heads
159
+ self.config = config
160
+
161
+ if (self.head_dim * num_heads) != self.embed_dim:
162
+ raise ValueError(f"embed_dim must be divisible by num_heads")
163
+ self.scaling = self.head_dim ** -0.5
164
+ self.is_decoder = is_decoder
165
+ self.is_causal = is_causal
166
+ self.layer_idx = layer_idx
167
+
168
+ self.k_proj = nn.Linear(embed_dim, embed_dim, bias=False)
169
+ self.v_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
170
+ self.q_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
171
+ self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
172
+
173
+ def forward(
174
+ self,
175
+ hidden_states: torch.Tensor,
176
+ key_value_states: torch.Tensor | None = None,
177
+ past_key_value: tuple[torch.Tensor, torch.Tensor] | None = None,
178
+ attention_mask: torch.Tensor | None = None,
179
+ output_attentions: bool = False,
180
+ ):
181
+ """Input shape: Batch x Time x Channel"""
182
+ is_cross_attention = key_value_states is not None
183
+ input_shape = hidden_states.shape[:-1]
184
+ hidden_shape = (*input_shape, -1, self.head_dim)
185
+
186
+ query_states = (self.q_proj(hidden_states) * self.scaling).view(hidden_shape).transpose(1, 2).contiguous()
187
+
188
+ if past_key_value is not None and not is_cross_attention:
189
+ key_states = past_key_value[0]
190
+ value_states = past_key_value[1]
191
+ else:
192
+ current_states = key_value_states if key_value_states is not None else hidden_states
193
+ kv_shape = (input_shape[0], -1, self.num_heads, self.head_dim)
194
+ key_states = self.k_proj(current_states).view(kv_shape).transpose(1, 2).contiguous()
195
+ value_states = self.v_proj(current_states).view(kv_shape).transpose(1, 2).contiguous()
196
+
197
+ if past_key_value is not None and is_cross_attention:
198
+ key_states = torch.cat([past_key_value[0], key_states], dim=2)
199
+ value_states = torch.cat([past_key_value[1], value_states], dim=2)
200
+
201
+ attn_weights = torch.matmul(query_states, key_states.transpose(-2, -1)) * 1.0
202
+ if attention_mask is not None:
203
+ attn_weights = attn_weights + attention_mask
204
+
205
+ attn_weights = F.softmax(attn_weights, dim=-1)
206
+ attn_weights = F.dropout(attn_weights, p=self.dropout, training=self.training)
207
+
208
+ attn_output = torch.matmul(attn_weights, value_states)
209
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
210
+ attn_output = self.out_proj(attn_output)
211
+
212
+ return attn_output, None
213
+
214
+ class MiniWhisperEncoderLayer(nn.Module):
215
+ def __init__(self, config: MiniWhisperConfig):
216
+ super().__init__()
217
+ self.embed_dim = config.d_model
218
+
219
+ self.self_attn = MiniWhisperAttention(
220
+ embed_dim=self.embed_dim,
221
+ num_heads=config.encoder_attention_heads,
222
+ dropout=config.attention_dropout,
223
+ is_decoder=False,
224
+ config=config,
225
+ )
226
+ self.self_attn_layer_norm = nn.LayerNorm(self.embed_dim)
227
+ self.dropout = config.dropout
228
+ self.activation_fn = ACT2FN[config.activation_function]
229
+ self.activation_dropout = config.activation_dropout
230
+ self.fc1 = nn.Linear(self.embed_dim, config.encoder_ffn_dim)
231
+ self.fc2 = nn.Linear(config.encoder_ffn_dim, self.embed_dim)
232
+ self.final_layer_norm = nn.LayerNorm(self.embed_dim)
233
+
234
+ def forward(
235
+ self,
236
+ hidden_states: torch.Tensor,
237
+ attention_mask: torch.Tensor,
238
+ ):
239
+ residual = hidden_states
240
+ hidden_states = self.self_attn_layer_norm(hidden_states)
241
+ hidden_states, _ = self.self_attn(
242
+ hidden_states=hidden_states,
243
+ attention_mask=attention_mask,
244
+ )
245
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
246
+ hidden_states = residual + hidden_states
247
+
248
+ residual = hidden_states
249
+ hidden_states = self.final_layer_norm(hidden_states)
250
+ hidden_states = self.activation_fn(self.fc1(hidden_states))
251
+ hidden_states = F.dropout(hidden_states, p=self.activation_dropout, training=self.training)
252
+ hidden_states = self.fc2(hidden_states)
253
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
254
+ hidden_states = residual + hidden_states
255
+
256
+ if hidden_states.dtype == torch.float16:
257
+ clamp_value = torch.finfo(hidden_states.dtype).max - 1000
258
+ hidden_states = torch.clamp(hidden_states, min=-clamp_value, max=clamp_value)
259
+
260
+ return hidden_states
261
+
262
+ class MiniWhisperDecoderLayer(nn.Module):
263
+ def __init__(self, config: MiniWhisperConfig, layer_idx: int | None = None):
264
+ super().__init__()
265
+ self.embed_dim = config.d_model
266
+
267
+ self.self_attn = MiniWhisperAttention(
268
+ embed_dim=self.embed_dim,
269
+ num_heads=config.decoder_attention_heads,
270
+ dropout=config.attention_dropout,
271
+ is_decoder=True,
272
+ is_causal=True,
273
+ layer_idx=layer_idx,
274
+ config=config,
275
+ )
276
+ self.dropout = config.dropout
277
+ self.activation_fn = ACT2FN[config.activation_function]
278
+ self.activation_dropout = config.activation_dropout
279
+
280
+ self.self_attn_layer_norm = nn.LayerNorm(self.embed_dim)
281
+ self.encoder_attn = MiniWhisperAttention(
282
+ self.embed_dim,
283
+ config.decoder_attention_heads,
284
+ dropout=config.attention_dropout,
285
+ is_decoder=True,
286
+ layer_idx=layer_idx,
287
+ config=config,
288
+ )
289
+ self.encoder_attn_layer_norm = nn.LayerNorm(self.embed_dim)
290
+ self.fc1 = nn.Linear(self.embed_dim, config.decoder_ffn_dim)
291
+ self.fc2 = nn.Linear(config.decoder_ffn_dim, self.embed_dim)
292
+ self.final_layer_norm = nn.LayerNorm(self.embed_dim)
293
+
294
+ def forward(
295
+ self,
296
+ hidden_states: torch.Tensor,
297
+ attention_mask: torch.Tensor | None = None,
298
+ encoder_hidden_states: torch.Tensor | None = None,
299
+ encoder_attention_mask: torch.Tensor | None = None,
300
+ past_key_value: tuple[tuple[torch.Tensor, torch.Tensor]] | None = None,
301
+ use_cache: bool | None = True,
302
+ ):
303
+ residual = hidden_states
304
+ hidden_states = self.self_attn_layer_norm(hidden_states)
305
+
306
+ self_attn_past = past_key_value[0] if past_key_value is not None else None
307
+ hidden_states, _ = self.self_attn(
308
+ hidden_states,
309
+ past_key_value=self_attn_past,
310
+ attention_mask=attention_mask,
311
+ )
312
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
313
+ hidden_states = residual + hidden_states
314
+
315
+ if encoder_hidden_states is not None:
316
+ residual = hidden_states
317
+ hidden_states = self.encoder_attn_layer_norm(hidden_states)
318
+ cross_attn_past = past_key_value[1] if past_key_value is not None else None
319
+ hidden_states, _ = self.encoder_attn(
320
+ hidden_states,
321
+ key_value_states=encoder_hidden_states,
322
+ attention_mask=encoder_attention_mask,
323
+ past_key_value=cross_attn_past,
324
+ )
325
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
326
+ hidden_states = residual + hidden_states
327
+
328
+ residual = hidden_states
329
+ hidden_states = self.final_layer_norm(hidden_states)
330
+ hidden_states = self.activation_fn(self.fc1(hidden_states))
331
+ hidden_states = F.dropout(hidden_states, p=self.activation_dropout, training=self.training)
332
+ hidden_states = self.fc2(hidden_states)
333
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
334
+ hidden_states = residual + hidden_states
335
+
336
+ return hidden_states, None
337
+
338
+ class MiniWhisperEncoder(PreTrainedModel):
339
+ config_class = MiniWhisperConfig
340
+ main_input_name = "input_features"
341
+
342
+ def __init__(self, config: MiniWhisperConfig):
343
+ super().__init__(config)
344
+ self.dropout = config.dropout
345
+ self.layerdrop = config.encoder_layerdrop
346
+
347
+ embed_dim = config.d_model
348
+ self.num_mel_bins = config.num_mel_bins
349
+ self.padding_idx = config.pad_token_id
350
+ self.max_source_positions = config.max_source_positions
351
+ self.embed_scale = math.sqrt(embed_dim) if config.scale_embedding else 1.0
352
+
353
+ self.conv1 = nn.Conv1d(self.num_mel_bins, embed_dim, kernel_size=3, padding=1)
354
+ self.conv2 = nn.Conv1d(embed_dim, embed_dim, kernel_size=3, stride=2, padding=1)
355
+
356
+ self.embed_positions = nn.Embedding(self.max_source_positions, embed_dim)
357
+ self.embed_positions.requires_grad_(False)
358
+
359
+ self.layers = nn.ModuleList([MiniWhisperEncoderLayer(config) for _ in range(config.encoder_layers)])
360
+ self.layer_norm = nn.LayerNorm(config.d_model)
361
+
362
+ self.gradient_checkpointing = False
363
+ self.post_init()
364
+
365
+ def _init_weights(self, module):
366
+ if isinstance(module, MiniWhisperEncoder):
367
+ with torch.no_grad():
368
+ module.embed_positions.weight.copy_(sinusoids(*module.embed_positions.weight.shape))
369
+
370
+ def forward(
371
+ self,
372
+ input_features,
373
+ attention_mask=None,
374
+ **kwargs,
375
+ ):
376
+ x = F.gelu(self.conv1(input_features))
377
+ x = F.gelu(self.conv2(x))
378
+
379
+ x = x.permute(0, 2, 1)
380
+
381
+ seq_len = x.shape[1]
382
+ positions = torch.arange(seq_len, device=x.device)
383
+ hidden_states = x + self.embed_positions(positions)
384
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
385
+
386
+ for idx, encoder_layer in enumerate(self.layers):
387
+ if self.training:
388
+ dropout_probability = torch.rand([])
389
+ if dropout_probability < self.layerdrop:
390
+ continue
391
+
392
+ hidden_states = encoder_layer(hidden_states, None)
393
+
394
+ hidden_states = self.layer_norm(hidden_states)
395
+
396
+ return BaseModelOutput(last_hidden_state=hidden_states)
397
+
398
+ def _get_feat_extract_output_lengths(self, input_lengths: torch.LongTensor):
399
+ input_lengths = (input_lengths - 1) // 2 + 1
400
+ return input_lengths
401
+
402
+ class MiniWhisperDecoder(PreTrainedModel):
403
+ config_class = MiniWhisperConfig
404
+ main_input_name = "input_ids"
405
+
406
+ def __init__(self, config: MiniWhisperConfig):
407
+ super().__init__(config)
408
+ self.dropout = config.dropout
409
+ self.layerdrop = config.decoder_layerdrop
410
+ self.padding_idx = config.pad_token_id
411
+ self.max_target_positions = config.max_target_positions
412
+ self.max_source_positions = config.max_source_positions
413
+ self.embed_scale = math.sqrt(config.d_model) if config.scale_embedding else 1.0
414
+
415
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.d_model, self.padding_idx)
416
+ self.embed_positions = WhisperPositionalEmbedding(self.max_target_positions, config.d_model)
417
+
418
+ self.layers = nn.ModuleList(
419
+ [MiniWhisperDecoderLayer(config, layer_idx) for layer_idx in range(config.decoder_layers)]
420
+ )
421
+
422
+ self.layer_norm = nn.LayerNorm(config.d_model)
423
+
424
+ self.gradient_checkpointing = False
425
+ self.post_init()
426
+
427
+ def forward(
428
+ self,
429
+ input_ids=None,
430
+ attention_mask=None,
431
+ encoder_hidden_states=None,
432
+ encoder_attention_mask=None,
433
+ past_key_values=None,
434
+ inputs_embeds=None,
435
+ position_ids=None,
436
+ use_cache=None,
437
+ **kwargs,
438
+ ):
439
+ if input_ids is not None and inputs_embeds is not None:
440
+ raise ValueError("You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time")
441
+ elif input_ids is not None:
442
+ inputs_embeds = self.embed_tokens(input_ids)
443
+ elif inputs_embeds is None:
444
+ raise ValueError("You have to specify either decoder_input_ids or decoder_inputs_embeds")
445
+
446
+ past_key_values_length = 0
447
+ if past_key_values is not None and len(past_key_values) > 0:
448
+ if past_key_values[0] is not None and past_key_values[0][0] is not None:
449
+ past_key_values_length = past_key_values[0][0].shape[2]
450
+
451
+ if position_ids is None:
452
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_key_values_length
453
+ position_ids = position_ids.unsqueeze(0).repeat(inputs_embeds.shape[0], 1)
454
+
455
+ if input_ids is not None:
456
+ positions = self.embed_positions(input_ids, past_key_values_length=past_key_values_length, position_ids=position_ids)
457
+ else:
458
+ positions = self.embed_positions(inputs_embeds, past_key_values_length=past_key_values_length, position_ids=position_ids)
459
+
460
+ hidden_states = inputs_embeds + positions
461
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
462
+
463
+ seq_len = inputs_embeds.shape[1]
464
+ causal_mask = torch.triu(
465
+ torch.ones(seq_len, seq_len, device=hidden_states.device) * float('-inf'),
466
+ diagonal=1
467
+ ).unsqueeze(0).unsqueeze(0)
468
+
469
+ if attention_mask is not None:
470
+ padding_mask = (1 - attention_mask) * float('-inf')
471
+ padding_mask = padding_mask.unsqueeze(1).unsqueeze(2)
472
+ attention_mask = causal_mask + padding_mask
473
+ else:
474
+ attention_mask = causal_mask
475
+
476
+ for idx, decoder_layer in enumerate(self.layers):
477
+ if self.training:
478
+ dropout_probability = torch.rand([])
479
+ if dropout_probability < self.layerdrop:
480
+ continue
481
+
482
+ layer_past = past_key_values[idx] if past_key_values is not None and idx < len(past_key_values) else None
483
+ hidden_states, _ = decoder_layer(
484
+ hidden_states,
485
+ attention_mask=attention_mask,
486
+ encoder_hidden_states=encoder_hidden_states,
487
+ encoder_attention_mask=encoder_attention_mask,
488
+ past_key_value=layer_past,
489
+ use_cache=use_cache,
490
+ )
491
+
492
+ hidden_states = self.layer_norm(hidden_states)
493
+
494
+ return BaseModelOutputWithPastAndCrossAttentions(
495
+ last_hidden_state=hidden_states,
496
+ past_key_values=None,
497
+ )
498
+
499
+ class MiniWhisperModel(PreTrainedModel):
500
+ config_class = MiniWhisperConfig
501
+
502
+ def __init__(self, config: MiniWhisperConfig):
503
+ super().__init__(config)
504
+
505
+ self.encoder = MiniWhisperEncoder(config)
506
+ self.decoder = MiniWhisperDecoder(config)
507
+ self.post_init()
508
+
509
+ def get_input_embeddings(self):
510
+ return self.decoder.embed_tokens
511
+
512
+ def set_input_embeddings(self, value):
513
+ self.decoder.embed_tokens = value
514
+
515
+ def _mask_input_features(
516
+ self,
517
+ input_features: torch.FloatTensor,
518
+ attention_mask: torch.LongTensor | None = None,
519
+ ):
520
+ """Apply SpecAugment"""
521
+ import numpy as np
522
+
523
+ if not getattr(self.config, "apply_spec_augment", True):
524
+ return input_features
525
+
526
+ batch_size, hidden_size, sequence_length = input_features.size()
527
+
528
+ if self.config.mask_time_prob > 0 and self.training:
529
+ mask_time_indices = _compute_mask_indices(
530
+ (batch_size, sequence_length),
531
+ mask_prob=self.config.mask_time_prob,
532
+ mask_length=self.config.mask_time_length,
533
+ attention_mask=attention_mask,
534
+ min_masks=self.config.mask_time_min_masks,
535
+ )
536
+ mask_time_indices = torch.tensor(mask_time_indices, device=input_features.device, dtype=torch.bool)
537
+ mask_time_indices = mask_time_indices[:, None].expand(-1, hidden_size, -1)
538
+ input_features[mask_time_indices] = 0
539
+
540
+ if self.config.mask_feature_prob > 0 and self.training:
541
+ mask_feature_indices = _compute_mask_indices(
542
+ (batch_size, hidden_size),
543
+ mask_prob=self.config.mask_feature_prob,
544
+ mask_length=self.config.mask_feature_length,
545
+ min_masks=self.config.mask_feature_min_masks,
546
+ )
547
+ mask_feature_indices = torch.tensor(mask_feature_indices, device=input_features.device, dtype=torch.bool)
548
+ input_features[mask_feature_indices] = 0
549
+
550
+ return input_features
551
+
552
+ def forward(
553
+ self,
554
+ input_features: torch.FloatTensor | None = None,
555
+ attention_mask: torch.LongTensor | None = None,
556
+ decoder_input_ids: torch.LongTensor | None = None,
557
+ decoder_attention_mask: torch.LongTensor | None = None,
558
+ encoder_outputs: tuple[torch.FloatTensor] | None = None,
559
+ past_key_values: tuple[tuple[torch.FloatTensor]] | None = None,
560
+ decoder_inputs_embeds: tuple[torch.FloatTensor] | None = None,
561
+ decoder_position_ids: tuple[torch.LongTensor] | None = None,
562
+ use_cache: bool | None = None,
563
+ **kwargs,
564
+ ):
565
+ if encoder_outputs is None:
566
+ input_features = self._mask_input_features(input_features, attention_mask=attention_mask)
567
+
568
+ encoder_outputs = self.encoder(input_features, **kwargs)
569
+ elif not isinstance(encoder_outputs, BaseModelOutput):
570
+ encoder_outputs = BaseModelOutput(
571
+ last_hidden_state=encoder_outputs[0],
572
+ hidden_states=encoder_outputs[1] if len(encoder_outputs) > 1 else None,
573
+ attentions=encoder_outputs[2] if len(encoder_outputs) > 2 else None,
574
+ )
575
+
576
+ decoder_outputs = self.decoder(
577
+ input_ids=decoder_input_ids,
578
+ attention_mask=decoder_attention_mask,
579
+ encoder_hidden_states=encoder_outputs.last_hidden_state,
580
+ past_key_values=past_key_values,
581
+ inputs_embeds=decoder_inputs_embeds,
582
+ position_ids=decoder_position_ids,
583
+ use_cache=use_cache,
584
+ **kwargs,
585
+ )
586
+
587
+ return Seq2SeqModelOutput(
588
+ last_hidden_state=decoder_outputs.last_hidden_state,
589
+ past_key_values=decoder_outputs.past_key_values,
590
+ decoder_hidden_states=decoder_outputs.hidden_states,
591
+ decoder_attentions=decoder_outputs.attentions,
592
+ cross_attentions=decoder_outputs.cross_attentions,
593
+ encoder_last_hidden_state=encoder_outputs.last_hidden_state,
594
+ encoder_hidden_states=encoder_outputs.hidden_states,
595
+ encoder_attentions=encoder_outputs.attentions,
596
+ )
597
+
598
+ class MiniWhisperForConditionalGeneration(PreTrainedModel, GenerationMixin):
599
+ config_class = MiniWhisperConfig
600
+ base_model_prefix = "model"
601
+ _tied_weights_keys = ["proj_out.weight"]
602
+
603
+ def __init__(self, config: MiniWhisperConfig):
604
+ super().__init__(config)
605
+ self.model = MiniWhisperModel(config)
606
+ self.proj_out = nn.Linear(config.d_model, config.vocab_size, bias=False)
607
+ self.max_target_positions = config.max_target_positions
608
+
609
+ self.post_init()
610
+
611
+ def get_output_embeddings(self):
612
+ return self.proj_out
613
+
614
+ def set_output_embeddings(self, new_embeddings):
615
+ self.proj_out = new_embeddings
616
+
617
+ def get_input_embeddings(self):
618
+ return self.model.get_input_embeddings()
619
+
620
+ def forward(
621
+ self,
622
+ input_features: torch.FloatTensor | None = None,
623
+ attention_mask: torch.LongTensor | None = None,
624
+ decoder_input_ids: torch.LongTensor | None = None,
625
+ decoder_attention_mask: torch.LongTensor | None = None,
626
+ encoder_outputs: tuple[torch.FloatTensor] | None = None,
627
+ past_key_values: tuple[tuple[torch.FloatTensor]] | None = None,
628
+ decoder_inputs_embeds: tuple[torch.FloatTensor] | None = None,
629
+ decoder_position_ids: tuple[torch.LongTensor] | None = None,
630
+ labels: torch.LongTensor | None = None,
631
+ use_cache: bool | None = None,
632
+ **kwargs,
633
+ ):
634
+ if labels is not None:
635
+ if labels.shape[1] > self.max_target_positions:
636
+ raise ValueError(f"Labels' sequence length {labels.shape[1]} cannot exceed max_target_positions")
637
+ if decoder_input_ids is None and decoder_inputs_embeds is None:
638
+ decoder_input_ids = shift_tokens_right(
639
+ labels, self.config.pad_token_id, self.config.decoder_start_token_id
640
+ )
641
+
642
+ outputs = self.model(
643
+ input_features,
644
+ attention_mask=attention_mask,
645
+ decoder_input_ids=decoder_input_ids,
646
+ encoder_outputs=encoder_outputs,
647
+ decoder_attention_mask=decoder_attention_mask,
648
+ past_key_values=past_key_values,
649
+ decoder_inputs_embeds=decoder_inputs_embeds,
650
+ decoder_position_ids=decoder_position_ids,
651
+ use_cache=use_cache,
652
+ **kwargs,
653
+ )
654
+
655
+ lm_logits = self.proj_out(outputs.last_hidden_state)
656
+
657
+ loss = None
658
+ if labels is not None:
659
+ loss_fct = nn.CrossEntropyLoss()
660
+ labels = labels.to(lm_logits.device)
661
+ loss = loss_fct(lm_logits.view(-1, self.config.vocab_size), labels.view(-1))
662
+
663
+ return Seq2SeqLMOutput(
664
+ loss=loss,
665
+ logits=lm_logits,
666
+ past_key_values=outputs.past_key_values,
667
+ decoder_hidden_states=outputs.decoder_hidden_states,
668
+ decoder_attentions=outputs.decoder_attentions,
669
+ cross_attentions=outputs.cross_attentions,
670
+ encoder_last_hidden_state=outputs.encoder_last_hidden_state,
671
+ encoder_hidden_states=outputs.encoder_hidden_states,
672
+ encoder_attentions=outputs.encoder_attentions,
673
+ )
674
+
675
+ def generate(
676
+ self,
677
+ input_features,
678
+ generation_config=None,
679
+ max_new_tokens=50,
680
+ **kwargs,
681
+ ):
682
+ self.eval()
683
+ batch_size = input_features.shape[0]
684
+ device = input_features.device
685
+
686
+ decoder_input_ids = torch.full(
687
+ (batch_size, 1), self.config.decoder_start_token_id, dtype=torch.long, device=device
688
+ )
689
+
690
+ encoder_outputs = self.model.encoder(input_features)
691
+
692
+ for _ in range(max_new_tokens):
693
+ outputs = self.forward(
694
+ decoder_input_ids=decoder_input_ids,
695
+ encoder_outputs=encoder_outputs,
696
+ use_cache=True,
697
+ )
698
+
699
+ next_token_logits = outputs.logits[:, -1, :]
700
+ next_token = torch.argmax(next_token_logits, dim=-1, keepdim=True)
701
+ decoder_input_ids = torch.cat([decoder_input_ids, next_token], dim=1)
702
+
703
+ if (next_token == self.config.eos_token_id).all():
704
+ break
705
+
706
+ return decoder_input_ids
707
+
708
+ def prepare_inputs_for_generation(
709
+ self,
710
+ decoder_input_ids,
711
+ past_key_values=None,
712
+ encoder_outputs=None,
713
+ attention_mask=None,
714
+ use_cache=None,
715
+ **kwargs,
716
+ ):
717
+ return {
718
+ "decoder_input_ids": decoder_input_ids,
719
+ "past_key_values": past_key_values,
720
+ "encoder_outputs": encoder_outputs,
721
+ "attention_mask": attention_mask,
722
+ "use_cache": use_cache,
723
+ }
724
+
725
+ def get_encoder(self):
726
+ return self.model.get_encoder()
727
+
728
+ __all__ = [
729
+ "MiniWhisperConfig",
730
+ "MiniWhisperModel",
731
+ "MiniWhisperForConditionalGeneration",
732
+ "MiniWhisperEncoder",
733
+ "MiniWhisperDecoder",
734
+ "MiniWhisperEncoderLayer",
735
+ "MiniWhisperDecoderLayer",
736
+ "MiniWhisperAttention",
737
+ "WhisperPositionalEmbedding",
738
+ ]
processing_mini_whisper.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # processing_mini_whisper.py
2
+ from typing import Union, List
3
+ from transformers import ProcessorMixin
4
+ from .feature_extraction_mini_whisper import MiniWhisperFeatureExtractor
5
+ from .tokenization_mini_whisper import MiniWhisperTokenizer
6
+
7
+ class MiniWhisperProcessor(ProcessorMixin):
8
+ """Combines feature extractor and tokenizer."""
9
+
10
+ attributes = ["feature_extractor", "tokenizer"]
11
+ feature_extractor_class = "MiniWhisperFeatureExtractor"
12
+ tokenizer_class = "MiniWhisperTokenizer"
13
+
14
+ def __init__(self, feature_extractor, tokenizer):
15
+ super().__init__(feature_extractor, tokenizer)
16
+
17
+ def __call__(
18
+ self,
19
+ audio=None,
20
+ text=None,
21
+ sampling_rate=None,
22
+ return_tensors="pt",
23
+ **kwargs,
24
+ ):
25
+ if audio is not None and text is not None:
26
+ raise ValueError("Cannot specify both audio and text")
27
+
28
+ if audio is not None:
29
+ inputs = self.feature_extractor(
30
+ audio,
31
+ sampling_rate=sampling_rate,
32
+ return_tensors=return_tensors,
33
+ **kwargs,
34
+ )
35
+ return inputs
36
+
37
+ if text is not None:
38
+ encodings = self.tokenizer(
39
+ text,
40
+ return_tensors=return_tensors,
41
+ **kwargs,
42
+ )
43
+ return encodings
44
+
45
+ def get_decoder_prompt_ids(self, task=None, language=None, no_timestamps=True):
46
+ return self.tokenizer.get_decoder_prompt_ids(task=task, language=language, no_timestamps=no_timestamps)
47
+
48
+ def batch_decode(self, sequences, **kwargs):
49
+ return self.tokenizer.batch_decode(sequences, **kwargs)
50
+
51
+ def decode(self, token_ids, **kwargs):
52
+ return self.tokenizer.decode(token_ids, **kwargs)
53
+
54
+ def save_pretrained(self, save_directory):
55
+ self.feature_extractor.save_pretrained(save_directory)
56
+ self.tokenizer.save_pretrained(save_directory)
57
+
58
+ import json
59
+ import os
60
+ config = {
61
+ "feature_extractor_type": "MiniWhisperFeatureExtractor",
62
+ "tokenizer_type": "MiniWhisperTokenizer",
63
+ }
64
+ with open(os.path.join(save_directory, "preprocessor_config.json"), "w") as f:
65
+ json.dump(config, f, indent=2)
66
+
67
+ @classmethod
68
+ def from_pretrained(cls, pretrained_model_name_or_path, **kwargs):
69
+ feature_extractor = MiniWhisperFeatureExtractor.from_pretrained(pretrained_model_name_or_path)
70
+ tokenizer = MiniWhisperTokenizer.from_pretrained(pretrained_model_name_or_path, **kwargs)
71
+ return cls(feature_extractor=feature_extractor, tokenizer=tokenizer)
72
+
73
+ __all__ = ["MiniWhisperProcessor"]
tokenization_mini_whisper.py ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # tokenization_mini_whisper.py
2
+ """Tokenization classes for MiniWhisper"""
3
+
4
+ import json
5
+ import os
6
+ import re
7
+ from functools import lru_cache
8
+ from typing import List, Optional, Tuple, Union
9
+
10
+ import numpy as np
11
+ from transformers import PreTrainedTokenizer
12
+ from transformers.tokenization_utils_base import AddedToken
13
+ from transformers.utils import logging
14
+
15
+ logger = logging.get_logger(__name__)
16
+
17
+ VOCAB_FILES_NAMES = {
18
+ "vocab_file": "vocab.json",
19
+ "tokenizer_file": "tokenizer.json",
20
+ "merges_file": "merges.txt",
21
+ "normalizer_file": "normalizer.json",
22
+ }
23
+
24
+ LANGUAGES = {
25
+ "en": "english",
26
+ "zh": "chinese",
27
+ "de": "german",
28
+ "es": "spanish",
29
+ "ru": "russian",
30
+ "ko": "korean",
31
+ "fr": "french",
32
+ "ja": "japanese",
33
+ "pt": "portuguese",
34
+ "tr": "turkish",
35
+ "pl": "polish",
36
+ "ca": "catalan",
37
+ "nl": "dutch",
38
+ "ar": "arabic",
39
+ "sv": "swedish",
40
+ "it": "italian",
41
+ "id": "indonesian",
42
+ "hi": "hindi",
43
+ "fi": "finnish",
44
+ "vi": "vietnamese",
45
+ "he": "hebrew",
46
+ "uk": "ukrainian",
47
+ "el": "greek",
48
+ "ms": "malay",
49
+ "cs": "czech",
50
+ "ro": "romanian",
51
+ "da": "danish",
52
+ "hu": "hungarian",
53
+ "ta": "tamil",
54
+ "no": "norwegian",
55
+ "th": "thai",
56
+ "ur": "urdu",
57
+ "hr": "croatian",
58
+ "bg": "bulgarian",
59
+ "lt": "lithuanian",
60
+ "la": "latin",
61
+ "mi": "maori",
62
+ "ml": "malayalam",
63
+ "cy": "welsh",
64
+ "sk": "slovak",
65
+ "te": "telugu",
66
+ "fa": "persian",
67
+ "lv": "latvian",
68
+ "bn": "bengali",
69
+ "sr": "serbian",
70
+ "az": "azerbaijani",
71
+ "sl": "slovenian",
72
+ "kn": "kannada",
73
+ "et": "estonian",
74
+ "mk": "macedonian",
75
+ "br": "breton",
76
+ "eu": "basque",
77
+ "is": "icelandic",
78
+ "hy": "armenian",
79
+ "ne": "nepali",
80
+ "mn": "mongolian",
81
+ "bs": "bosnian",
82
+ "kk": "kazakh",
83
+ "sq": "albanian",
84
+ "sw": "swahili",
85
+ "gl": "galician",
86
+ "mr": "marathi",
87
+ "pa": "punjabi",
88
+ "si": "sinhala",
89
+ "km": "khmer",
90
+ "sn": "shona",
91
+ "yo": "yoruba",
92
+ "so": "somali",
93
+ "af": "afrikaans",
94
+ "oc": "occitan",
95
+ "ka": "georgian",
96
+ "be": "belarusian",
97
+ "tg": "tajik",
98
+ "sd": "sindhi",
99
+ "gu": "gujarati",
100
+ "am": "amharic",
101
+ "yi": "yiddish",
102
+ "lo": "lao",
103
+ "uz": "uzbek",
104
+ "fo": "faroese",
105
+ "ht": "haitian creole",
106
+ "ps": "pashto",
107
+ "tk": "turkmen",
108
+ "nn": "nynorsk",
109
+ "mt": "maltese",
110
+ "sa": "sanskrit",
111
+ "lb": "luxembourgish",
112
+ "my": "myanmar",
113
+ "bo": "tibetan",
114
+ "tl": "tagalog",
115
+ "mg": "malagasy",
116
+ "as": "assamese",
117
+ "tt": "tatar",
118
+ "haw": "hawaiian",
119
+ "ln": "lingala",
120
+ "ha": "hausa",
121
+ "ba": "bashkir",
122
+ "jw": "javanese",
123
+ "su": "sundanese",
124
+ "yue": "cantonese",
125
+ }
126
+
127
+ TO_LANGUAGE_CODE = {
128
+ **{language: code for code, language in LANGUAGES.items()},
129
+ "burmese": "my",
130
+ "valencian": "ca",
131
+ "flemish": "nl",
132
+ "haitian": "ht",
133
+ "letzeburgesch": "lb",
134
+ "pushto": "ps",
135
+ "panjabi": "pa",
136
+ "moldavian": "ro",
137
+ "moldovan": "ro",
138
+ "sinhalese": "si",
139
+ "castilian": "es",
140
+ "mandarin": "zh",
141
+ }
142
+
143
+ TASK_IDS = ["translate", "transcribe"]
144
+
145
+ class MiniWhisperTokenizer(PreTrainedTokenizer):
146
+ """
147
+ Construct a MiniWhisper tokenizer.
148
+
149
+ For actual use, load pretrained:
150
+ tokenizer = WhisperTokenizer.from_pretrained("openai/whisper-tiny")
151
+
152
+ Or provide path to trained vocab/merges files:
153
+ tokenizer = MiniWhisperTokenizer(vocab_file="vocab.json", merges_file="merges.txt")
154
+ """
155
+
156
+ vocab_files_names = VOCAB_FILES_NAMES
157
+ model_input_names = ["input_ids", "attention_mask"]
158
+
159
+ def __init__(
160
+ self,
161
+ vocab_file=None,
162
+ merges_file=None,
163
+ normalizer_file=None,
164
+ tokenizer_file=None,
165
+ unk_token="<|endoftext|>",
166
+ bos_token="<|endoftext|>",
167
+ eos_token="<|endoftext|>",
168
+ pad_token="<|endoftext|>",
169
+ add_prefix_space=False,
170
+ language=None,
171
+ task=None,
172
+ predict_timestamps=False,
173
+ **kwargs,
174
+ ):
175
+ bos_token = AddedToken(bos_token, lstrip=False, rstrip=False, normalized=False, special=True) if isinstance(bos_token, str) else bos_token
176
+ eos_token = AddedToken(eos_token, lstrip=False, rstrip=False, normalized=False, special=True) if isinstance(eos_token, str) else eos_token
177
+ unk_token = AddedToken(unk_token, lstrip=False, rstrip=False, normalized=False, special=True) if isinstance(unk_token, str) else unk_token
178
+ pad_token = AddedToken(pad_token, lstrip=False, rstrip=False, normalized=False, special=True) if isinstance(pad_token, str) else pad_token
179
+
180
+ self._vocab = {}
181
+ self._merges = []
182
+
183
+ if vocab_file is not None and os.path.isfile(vocab_file):
184
+ with open(vocab_file, encoding="utf-8") as f:
185
+ self._vocab = json.load(f)
186
+
187
+ if merges_file is not None and os.path.isfile(merges_file):
188
+ with open(merges_file, encoding="utf-8") as f:
189
+ lines = f.read().splitlines()
190
+ if lines and lines[0] == "#version: 0.2":
191
+ self._merges = lines[1:]
192
+ else:
193
+ self._merges = lines
194
+
195
+ self.english_spelling_normalizer = None
196
+ if normalizer_file is not None and os.path.isfile(normalizer_file):
197
+ with open(normalizer_file, encoding="utf-8") as f:
198
+ self.english_spelling_normalizer = json.load(f)
199
+
200
+ self.add_prefix_space = add_prefix_space
201
+ self.language = language
202
+ self.task = task
203
+ self.predict_timestamps = predict_timestamps
204
+ self.timestamp_pat = re.compile(r"<\|(\d+\.\d+)\|>")
205
+
206
+ super().__init__(
207
+ unk_token=unk_token,
208
+ bos_token=bos_token,
209
+ eos_token=eos_token,
210
+ pad_token=pad_token,
211
+ add_prefix_space=add_prefix_space,
212
+ **kwargs,
213
+ )
214
+
215
+ self.set_prefix_tokens()
216
+
217
+ @property
218
+ def vocab_size(self) -> int:
219
+ if self._vocab:
220
+ return len(self._vocab)
221
+ return 51865
222
+
223
+ def get_vocab(self):
224
+ if self._vocab:
225
+ return dict(self._vocab, **self.added_tokens_encoder)
226
+ return {self.unk_token: 0, self.bos_token: 1, self.eos_token: 2, self.pad_token: 3}
227
+
228
+ def _tokenize(self, text, **kwargs):
229
+ """Tokenize a string using BPE"""
230
+ if not self._vocab or not self._merges:
231
+ return list(text)
232
+
233
+ tokens = []
234
+ i = 0
235
+ text = text.lower()
236
+
237
+ while i < len(text):
238
+ if i < len(text) - 1:
239
+ bigram = text[i:i+2]
240
+ if bigram in self._merges:
241
+ tokens.append(bigram)
242
+ i += 2
243
+ continue
244
+ tokens.append(text[i])
245
+ i += 1
246
+
247
+ return tokens
248
+
249
+ def _convert_token_to_id(self, token):
250
+ if self._vocab:
251
+ return self._vocab.get(token, self._vocab.get(self.unk_token))
252
+ index = len(self._vocab) if self._vocab else 0
253
+ if token == self.unk_token:
254
+ return index
255
+ if token == self.bos_token:
256
+ return index + 1
257
+ if token == self.eos_token:
258
+ return index + 2
259
+ if token == self.pad_token:
260
+ return index + 3
261
+ return index
262
+
263
+ def _convert_id_to_token(self, index):
264
+ if hasattr(self, 'ids_to_tokens') and self.ids_to_tokens:
265
+ return self.ids_to_tokens.get(index, self.unk_token)
266
+ return super()._convert_id_to_token(index)
267
+
268
+ def convert_tokens_to_string(self, tokens):
269
+ return "".join(tokens)
270
+
271
+ @property
272
+ def prefix_tokens(self) -> List[int]:
273
+ bos_token_id = self.convert_tokens_to_ids("<|startoftranscript|>")
274
+ transcribe_token_id = self.convert_tokens_to_ids("<|transcribe|>")
275
+ translate_token_id = self.convert_tokens_to_ids("<|translate|>")
276
+ notimestamps_token_id = self.convert_tokens_to_ids("<|notimestamps|>")
277
+ langs = tuple(LANGUAGES.keys())
278
+
279
+ if self.language is not None:
280
+ self.language = self.language.lower()
281
+ if self.language in TO_LANGUAGE_CODE:
282
+ language_id = TO_LANGUAGE_CODE[self.language]
283
+ elif self.language in TO_LANGUAGE_CODE.values():
284
+ language_id = self.language
285
+ else:
286
+ language_id = "en"
287
+
288
+ bos_sequence = [bos_token_id]
289
+
290
+ if self.language is not None:
291
+ bos_sequence.append(bos_token_id + 1 + langs.index(language_id))
292
+
293
+ if self.task is not None:
294
+ if self.task == "transcribe":
295
+ bos_sequence.append(transcribe_token_id)
296
+ elif self.task == "translate":
297
+ bos_sequence.append(translate_token_id)
298
+
299
+ if not self.predict_timestamps:
300
+ bos_sequence.append(notimestamps_token_id)
301
+
302
+ return bos_sequence
303
+
304
+ def set_prefix_tokens(self, language=None, task=None, predict_timestamps=None):
305
+ if language is not None:
306
+ self.language = language
307
+ if task is not None:
308
+ self.task = task
309
+ if predict_timestamps is not None:
310
+ self.predict_timestamps = predict_timestamps
311
+
312
+ def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None) -> List[int]:
313
+ prefix = self.prefix_tokens
314
+ eos = [self.eos_token_id]
315
+
316
+ if token_ids_1 is None:
317
+ return prefix + token_ids_0 + eos
318
+ return prefix + token_ids_0 + token_ids_1 + eos
319
+
320
+ def get_special_tokens_mask(
321
+ self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None,
322
+ already_has_special_tokens: bool = False
323
+ ) -> List[int]:
324
+ if already_has_special_tokens:
325
+ return super().get_special_tokens_mask(
326
+ token_ids_0=token_ids_0, token_ids_1=token_ids_1,
327
+ already_has_special_tokens=True
328
+ )
329
+
330
+ prefix_ones = [1] * len(self.prefix_tokens)
331
+ suffix_ones = [1]
332
+
333
+ if token_ids_1 is None:
334
+ return prefix_ones + ([0] * len(token_ids_0)) + suffix_ones
335
+ return prefix_ones + ([0] * len(token_ids_0)) + ([0] * len(token_ids_1)) + suffix_ones
336
+
337
+ def get_decoder_prompt_ids(self, task=None, language=None, no_timestamps=True):
338
+ self.set_prefix_tokens(task=task, language=language, predict_timestamps=not no_timestamps)
339
+ forced_tokens = self.prefix_tokens[1:]
340
+ forced_decoder_ids = [(rank + 1, token_id) for rank, token_id in enumerate(forced_tokens)]
341
+ return forced_decoder_ids
342
+
343
+ def _decode(
344
+ self, token_ids, skip_special_tokens=False, clean_up_tokenization_spaces=True,
345
+ normalize=False, basic_normalize=False, remove_diacritics=False, **kwargs
346
+ ) -> str:
347
+ filtered_ids = token_ids
348
+ if skip_special_tokens:
349
+ filtered_ids = [t for t in token_ids if t not in self.all_special_ids]
350
+
351
+ tokens = self.convert_ids_to_tokens(filtered_ids)
352
+ text = self.convert_tokens_to_string(tokens)
353
+
354
+ if clean_up_tokenization_spaces:
355
+ text = re.sub(r"\s+", " ", text).strip()
356
+
357
+ text = re.sub(self.timestamp_pat, "", text)
358
+
359
+ return text
360
+
361
+ def decode(
362
+ self, token_ids, skip_special_tokens=False, clean_up_tokenization_spaces=True,
363
+ output_offsets=False, time_precision=0.02, decode_with_timestamps=False, **kwargs
364
+ ) -> str:
365
+ return self._decode(token_ids, skip_special_tokens, clean_up_tokenization_spaces, **kwargs)
366
+
367
+ def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:
368
+ if not os.path.isdir(save_directory):
369
+ logger.error(f"Vocabulary path ({save_directory}) should be a directory")
370
+ return
371
+
372
+ vocab_file = os.path.join(
373
+ save_directory,
374
+ (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]
375
+ )
376
+ merges_file = os.path.join(
377
+ save_directory,
378
+ (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["merges_file"]
379
+ )
380
+
381
+ if self._vocab:
382
+ with open(vocab_file, "w", encoding="utf-8") as f:
383
+ json.dump(self._vocab, f, indent=2, ensure_ascii=False)
384
+
385
+ if self._merges:
386
+ with open(merges_file, "w", encoding="utf-8") as f:
387
+ f.write("#version: 0.2\n")
388
+ f.writelines(merge + "\n" for merge in self._merges)
389
+
390
+ return (vocab_file, merges_file)
391
+
392
+ @lru_cache
393
+ def timestamp_ids(self, time_precision=0.02):
394
+ return self.convert_tokens_to_ids([("<|%.2f|>" % (i * time_precision)) for i in range(1500 + 1)])
395
+
396
+ __all__ = ["MiniWhisperTokenizer"]