SidSaxena commited on
Commit
cbd57b6
·
verified ·
1 Parent(s): 9a546be

Deploy hf-space @ c63b38a: batch export + ZeroGPU dynamic duration

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +17 -1
  2. LICENSE +25 -0
  3. README.md +3 -1
  4. app.py +458 -125
  5. export_utils.py +206 -0
  6. src/SongFormer/infer.sh +3 -1
  7. src/SongFormer/infer/infer.py +149 -6
  8. src/third_party/MuQ/.gitattributes +0 -2
  9. src/third_party/MuQ/.gitignore +0 -46
  10. src/third_party/MuQ/.gitmodules +0 -3
  11. src/third_party/MuQ/LICENSE +0 -21
  12. src/third_party/MuQ/LICENSE_weights +0 -399
  13. src/third_party/MuQ/README.md +0 -129
  14. src/third_party/MuQ/images/muq-logo.jpeg +0 -0
  15. src/third_party/MuQ/images/radar.jpg +0 -0
  16. src/third_party/MuQ/images/tab-marble.jpg +0 -3
  17. src/third_party/MuQ/images/tab-mulan.png +0 -0
  18. src/third_party/MuQ/images/tagging.jpg +0 -0
  19. src/third_party/MuQ/requirements.txt +0 -11
  20. src/third_party/MuQ/setup.py +0 -34
  21. src/third_party/MuQ/src/muq/__init__.py +0 -2
  22. src/third_party/MuQ/src/muq/muq/__init__.py +0 -1
  23. src/third_party/MuQ/src/muq/muq/models/__init__.py +0 -0
  24. src/third_party/MuQ/src/muq/muq/models/muq_model.py +0 -366
  25. src/third_party/MuQ/src/muq/muq/modules/__init__.py +0 -2
  26. src/third_party/MuQ/src/muq/muq/modules/conv.py +0 -77
  27. src/third_party/MuQ/src/muq/muq/modules/features.py +0 -37
  28. src/third_party/MuQ/src/muq/muq/modules/flash_conformer.py +0 -2114
  29. src/third_party/MuQ/src/muq/muq/modules/random_quantizer.py +0 -68
  30. src/third_party/MuQ/src/muq/muq/modules/rvq.py +0 -314
  31. src/third_party/MuQ/src/muq/muq/muq.py +0 -90
  32. src/third_party/MuQ/src/muq/muq_mulan/__init__.py +0 -1
  33. src/third_party/MuQ/src/muq/muq_mulan/models/__init__.py +0 -0
  34. src/third_party/MuQ/src/muq/muq_mulan/models/audio.py +0 -294
  35. src/third_party/MuQ/src/muq/muq_mulan/models/mulan.py +0 -148
  36. src/third_party/MuQ/src/muq/muq_mulan/models/text.py +0 -241
  37. src/third_party/MuQ/src/muq/muq_mulan/modules/__init__.py +0 -0
  38. src/third_party/MuQ/src/muq/muq_mulan/modules/contrastive.py +0 -238
  39. src/third_party/MuQ/src/muq/muq_mulan/modules/distributed.py +0 -83
  40. src/third_party/MuQ/src/muq/muq_mulan/modules/extend_distributed.py +0 -604
  41. src/third_party/MuQ/src/muq/muq_mulan/modules/transformer.py +0 -185
  42. src/third_party/MuQ/src/muq/muq_mulan/modules/utils.py +0 -45
  43. src/third_party/MuQ/src/muq/muq_mulan/muq_mulan.py +0 -271
  44. src/third_party/MuQ/src/recipes/contrastive_learning/README.md +0 -107
  45. src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/2gpu.yaml +0 -17
  46. src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/32gpu4node.yaml +0 -19
  47. src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/32gpu4node_fp16.yaml +0 -19
  48. src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/32gpu4node_zero2.yaml +0 -25
  49. src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/8gpu_fp16.yaml +0 -17
  50. src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/8gpu_fp16_zero2.yaml +0 -22
.gitignore CHANGED
@@ -8,7 +8,6 @@ ENV/
8
  env/
9
  .venv/
10
  .ENV/
11
-
12
  # Python IDEs
13
  .idea/
14
  .vscode/
@@ -29,6 +28,17 @@ tensorboard_logs/
29
  .DS_Store
30
  Thumbs.db
31
 
 
 
 
 
 
 
 
 
 
 
 
32
  # Compiled extension modules
33
  *.so
34
  *.dylib
@@ -40,3 +50,9 @@ cython_debug/
40
  # Other custom ignore rules
41
  *.bak
42
  *.swp
 
 
 
 
 
 
 
8
  env/
9
  .venv/
10
  .ENV/
 
11
  # Python IDEs
12
  .idea/
13
  .vscode/
 
28
  .DS_Store
29
  Thumbs.db
30
 
31
+ # PyCharm files
32
+ *.iml
33
+ .idea/
34
+
35
+ # Coverage and testing tools
36
+ .coverage
37
+ nosetests.xml
38
+ coverage.xml
39
+ *.cover
40
+ *.log
41
+
42
  # Compiled extension modules
43
  *.so
44
  *.dylib
 
50
  # Other custom ignore rules
51
  *.bak
52
  *.swp
53
+
54
+ .ruff_cache/
55
+
56
+ # Model checkpoints (downloaded at runtime)
57
+ ckpts/
58
+ .gradio/
LICENSE ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Creative Commons Attribution 4.0 International Public License
2
+
3
+ By exercising the Licensed Rights (defined below), You accept and agree
4
+ to be bound by the terms and conditions of this Creative Commons
5
+ Attribution 4.0 International Public License ("Public License").
6
+ To the extent this Public License may be interpreted as a contract,
7
+ You are granted the Licensed Rights in consideration of Your acceptance
8
+ of these terms and conditions, and the Licensor grants You such rights
9
+ in consideration of benefits the Licensor receives from making
10
+ the Licensed Material available under these terms and conditions.
11
+
12
+ You are free to:
13
+ - Share — copy and redistribute the material in any medium or format
14
+ - Adapt — remix, transform, and build upon the material for any purpose, even commercially.
15
+
16
+ Under the following terms:
17
+ - Attribution — You must give appropriate credit, provide a link to the license,
18
+ and indicate if changes were made. You may do so in any reasonable manner,
19
+ but not in any way that suggests the licensor endorses you or your use.
20
+
21
+ No additional restrictions — You may not apply legal terms or
22
+ technological measures that legally restrict others from doing
23
+ anything the license permits.
24
+
25
+ Full license text: https://creativecommons.org/licenses/by/4.0/legalcode
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: SongFormer
3
- emoji: "\U0001F3B5"
4
  colorFrom: blue
5
  colorTo: indigo
6
  sdk: gradio
@@ -40,6 +40,8 @@ Chunbo Hao<sup>&ast;</sup>, Ruibin Yuan<sup>&ast;</sup>, Jixun Yao, Qixin Deng,
40
 
41
  SongFormer is a music structure analysis framework that leverages multi-resolution self-supervised representations and heterogeneous supervision, accompanied by the large-scale multilingual dataset SongFormDB and the high-quality benchmark SongFormBench to foster fair and reproducible research.
42
 
 
 
43
  ![](https://github.com/ASLP-lab/SongFormer/blob/main/figs/songformer.png?raw=true)
44
 
45
  ## Citation
 
1
  ---
2
  title: SongFormer
3
+ emoji: "🎵"
4
  colorFrom: blue
5
  colorTo: indigo
6
  sdk: gradio
 
40
 
41
  SongFormer is a music structure analysis framework that leverages multi-resolution self-supervised representations and heterogeneous supervision, accompanied by the large-scale multilingual dataset SongFormDB and the high-quality benchmark SongFormBench to foster fair and reproducible research.
42
 
43
+ **This Space offers two modes:** analyze a *Single File* with downloadable results (JSON / MSA / CSV / plot / ZIP), or use the *Batch* tab to process multiple files with live per-file status, a combined ZIP (downloadable mid-run), and a per-file inspector with audio playback. Runs on ZeroGPU — each analyzed file consumes daily GPU quota.
44
+
45
  ![](https://github.com/ASLP-lab/SongFormer/blob/main/figs/songformer.png?raw=true)
46
 
47
  ## Citation
app.py CHANGED
@@ -1,30 +1,9 @@
1
  import os
2
  import sys
3
 
4
- current_file = os.path.abspath(__file__)
5
- current_dir = os.path.dirname(current_file)
6
-
7
- songformer_path = os.path.join(current_dir, "src", "SongFormer")
8
- if os.path.exists(songformer_path):
9
- os.chdir(songformer_path)
10
- else:
11
- print(f"The target working directory does not exist: {songformer_path}")
12
-
13
- working_dir = os.getcwd()
14
-
15
- third_party_path = os.path.join(current_dir, "src", "third_party")
16
- if os.path.exists(third_party_path):
17
- sys.path.insert(0, third_party_path)
18
- sys.path.insert(0, working_dir)
19
-
20
- musicfm_paths = [
21
- os.path.join(current_dir, "src"),
22
- os.path.join(current_dir, "third_party"),
23
- os.path.join(current_dir, "src", "SongFormer"),
24
- ]
25
- for path in musicfm_paths:
26
- if os.path.exists(path):
27
- sys.path.insert(0, path)
28
 
29
  # monkey patch to fix issues in msaf
30
  import scipy
@@ -39,7 +18,8 @@ import json
39
  import math
40
  import importlib
41
  import matplotlib
42
- matplotlib.use('Agg')
 
43
  import matplotlib.pyplot as plt
44
  import matplotlib.ticker as ticker
45
  from pathlib import Path
@@ -51,6 +31,11 @@ from musicfm.model.musicfm_25hz import MusicFM25Hz
51
  from postprocessing.functional import postprocess_functional_structure
52
  from dataset.label2id import DATASET_ID_ALLOWED_LABEL_IDS, DATASET_LABEL_TO_DATASET_ID
53
  from utils.fetch_pretrained import download_all
 
 
 
 
 
54
  import spaces
55
 
56
  # Constants
@@ -69,6 +54,23 @@ msa_model = None
69
  device = None
70
 
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  def load_checkpoint(checkpoint_path, device=None):
73
  """Load checkpoint from path"""
74
  if device is None:
@@ -90,7 +92,7 @@ def initialize_models(model_name: str, checkpoint: str, config_path: str):
90
  global muq_model, musicfm_model, msa_model, device
91
 
92
  # Set device
93
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
94
 
95
  # Load MuQ
96
  muq_model = MuQ.from_pretrained("OpenMuQ/MuQ-large-msd-iter")
@@ -123,17 +125,26 @@ def initialize_models(model_name: str, checkpoint: str, config_path: str):
123
  return hp
124
 
125
 
126
- @spaces.GPU(duration=300)
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  def process_audio(audio_path, win_size=420, hop_size=420, num_classes=128):
128
  """Process audio file and return structure analysis results"""
129
  global muq_model, musicfm_model, msa_model, device
130
 
131
  if muq_model is None:
132
- hp = initialize_models(
133
- model_name="SongFormer",
134
- checkpoint="SongFormer.safetensors",
135
- config_path="SongFormer.yaml",
136
- )
137
  else:
138
  hp = OmegaConf.load(os.path.join("configs", "SongFormer.yaml"))
139
 
@@ -180,14 +191,14 @@ def process_audio(audio_path, win_size=420, hop_size=420, num_classes=128):
180
  muq_output = muq_model(audio_seg.unsqueeze(0), output_hidden_states=True)
181
  muq_embd_420s = muq_output["hidden_states"][10]
182
  del muq_output
183
- torch.cuda.empty_cache()
184
 
185
  _, musicfm_hidden_states = musicfm_model.get_predictions(
186
  audio_seg.unsqueeze(0)
187
  )
188
  musicfm_embd_420s = musicfm_hidden_states[10]
189
  del musicfm_hidden_states
190
- torch.cuda.empty_cache()
191
 
192
  # Process 30-second segments
193
  wraped_muq_embd_30s = []
@@ -211,14 +222,14 @@ def process_audio(audio_path, win_size=420, hop_size=420, num_classes=128):
211
  output_hidden_states=True,
212
  )["hidden_states"][10]
213
  )
214
- torch.cuda.empty_cache()
215
 
216
  wraped_musicfm_embd_30s.append(
217
  musicfm_model.get_predictions(
218
  audio[start_idx_30s:end_idx_30s].unsqueeze(0)
219
  )[1][10]
220
  )
221
- torch.cuda.empty_cache()
222
 
223
  if wraped_muq_embd_30s:
224
  wraped_muq_embd_30s = torch.concatenate(wraped_muq_embd_30s, dim=1)
@@ -325,6 +336,7 @@ def create_visualization(
325
  logits, msa_output, label_num=8, frame_rates=AFTER_DOWNSAMPLING_FRAME_RATES
326
  ):
327
  """Create visualization plot"""
 
328
  try:
329
  from dataset.label2id import ID_TO_LABEL
330
  except:
@@ -420,48 +432,241 @@ def rule_post_processing(msa_list):
420
  return result
421
 
422
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  def process_and_analyze(audio_file):
424
  """Main processing function"""
425
 
426
- def format_time(t: float) -> str:
427
- minutes = int(t // 60)
428
- seconds = t % 60
429
- return f"{minutes:02d}:{seconds:06.3f}"
430
-
431
  if audio_file is None:
432
- return None, "", "", None
433
 
434
  try:
435
- # Process audio
436
- logits, msa_output = process_audio(audio_file)
437
- # Apply rule-based post-processing
438
- msa_output = rule_post_processing(msa_output)
439
- # Format outputs
440
- segments = format_as_segments(msa_output)
441
- msa_format = format_as_msa(msa_output)
442
- json_format = format_as_json(segments)
443
 
444
  # Create table data
445
- table_data = [
446
- [
447
- f"{float(seg['start']):.2f} ({format_time(float(seg['start']))})",
448
- f"{float(seg['end']):.2f} ({format_time(float(seg['end']))})",
449
- seg["label"],
450
- ]
451
- for seg in segments
452
- ]
453
 
454
- # Create visualization
455
- fig = create_visualization(logits, msa_output)
456
-
457
- return table_data, json_format, msa_format, fig
 
 
 
 
 
 
 
 
 
 
 
 
458
 
459
  except Exception as e:
460
  import traceback
461
 
462
  error_msg = f"Error: {str(e)}\n{traceback.format_exc()}"
463
- print(error_msg)
464
- return None, "", error_msg, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
 
466
 
467
  # Create Gradio interface
@@ -503,78 +708,173 @@ with gr.Blocks(
503
  # Links
504
  gr.HTML("""
505
  <div class="links-container">
506
- <a href="https://img.shields.io/badge/Python-3.10-brightgreen"><img src="https://img.shields.io/badge/Python-3.10-brightgreen" alt="Python 3.10"></a>
507
- <a href="https://img.shields.io/badge/License-CC%20BY%204.0-lightblue"><img src="https://img.shields.io/badge/License-CC%20BY%204.0-lightblue" alt="License CC BY 4.0"></a>
508
- <a href="https://arxiv.org/abs/2510.02797"><img src="https://img.shields.io/badge/arXiv-2510.02797-blue" alt="arXiv Paper"></a>
509
- <a href="https://github.com/ASLP-lab/SongFormer"><img src="https://img.shields.io/badge/GitHub-SongFormer-black" alt="GitHub"></a>
510
- <a href="https://huggingface.co/spaces/SidSaxena/SongFormer"><img src="https://img.shields.io/badge/HuggingFace-space-yellow" alt="HuggingFace Space"></a>
511
- <a href="https://huggingface.co/ASLP-lab/SongFormer"><img src="https://img.shields.io/badge/HuggingFace-model-blue" alt="HuggingFace Model"></a>
512
- <a href="https://huggingface.co/datasets/ASLP-lab/SongFormDB"><img src="https://img.shields.io/badge/HF%20Dataset-SongFormDB-green" alt="Dataset SongFormDB"></a>
513
- <a href="https://huggingface.co/datasets/ASLP-lab/SongFormBench"><img src="https://img.shields.io/badge/HF%20Dataset-SongFormBench-orange" alt="Dataset SongFormBench"></a>
514
- <a href="https://discord.gg/p5uBryC4Zs"><img src="https://img.shields.io/badge/Discord-join%20us-purple?logo=discord&logoColor=white" alt="Discord"></a>
515
- <a href="http://www.npu-aslp.org/"><img src="https://img.shields.io/badge/%F0%9F%8F%AB-ASLP-grey?labelColor=lightgrey" alt="ASLP lab"></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
516
  </div>
517
  """)
518
 
 
 
 
 
 
 
 
 
519
 
520
- # Main input area
521
- with gr.Row():
522
- with gr.Column(scale=3):
523
- audio_input = gr.Audio(
524
- label="Upload Audio File", type="filepath", elem_id="audio-input"
525
- )
 
 
 
 
 
 
526
 
527
- with gr.Column(scale=1):
528
- gr.Markdown("### Examples")
529
- gr.Examples(
530
- examples=[
531
- ["examples/BC_5cd6a6.mp3"],
532
- ["examples/BC_282ece.mp3"],
533
- ["examples/BHX_0158_letitrock.wav"],
534
- ["examples/BHX_0374_drunkonyou.wav"],
535
- ],
536
- inputs=[audio_input],
537
- label="Click to load example",
538
- )
539
 
540
- # Analyze button
541
- with gr.Row():
542
- analyze_btn = gr.Button(
543
- "Analyze Music Structure", variant="primary", scale=1
544
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
 
546
- # Results display area
547
- with gr.Row():
548
- with gr.Column(scale=13):
549
- segments_table = gr.Dataframe(
550
- headers=["Start / s (m:s.ms)", "End / s (m:s.ms)", "Label"],
551
- label="Detected Music Segments",
552
- interactive=False,
553
- elem_id="result-table",
 
 
 
 
 
 
 
 
 
554
  )
555
- with gr.Column(scale=8):
556
  with gr.Row():
557
- with gr.Accordion("JSON Output", open=False):
558
- json_output = gr.Textbox(
559
- label="JSON Format",
560
- lines=15,
561
- max_lines=20,
562
- interactive=False,
563
- show_copy_button=True,
564
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  with gr.Row():
566
- with gr.Accordion("MSA Text Output", open=False):
567
- msa_output = gr.Textbox(
568
- label="MSA Format",
569
- lines=15,
570
- max_lines=20,
 
 
 
 
 
 
 
 
571
  interactive=False,
572
- show_copy_button=True,
573
  )
574
-
575
- # Visualization plot
576
- with gr.Row():
577
- plot_output = gr.Plot(label="Activation Curves Visualization")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
 
579
  gr.HTML("""
580
  <div style="display: flex; justify-content: center; align-items: center;">
@@ -586,7 +886,39 @@ with gr.Blocks(
586
  analyze_btn.click(
587
  fn=process_and_analyze,
588
  inputs=[audio_input],
589
- outputs=[segments_table, json_output, msa_output, plot_output],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  )
591
 
592
  if __name__ == "__main__":
@@ -601,5 +933,6 @@ if __name__ == "__main__":
601
  )
602
  print("Models loaded successfully!")
603
 
604
- # Launch interface
 
605
  demo.launch()
 
1
  import os
2
  import sys
3
 
4
+ os.chdir(os.path.join("src", "SongFormer"))
5
+ sys.path.append(os.path.join("..", "third_party"))
6
+ sys.path.append(".")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  # monkey patch to fix issues in msaf
9
  import scipy
 
18
  import math
19
  import importlib
20
  import matplotlib
21
+
22
+ matplotlib.use("Agg") # non-interactive backend: safe for rendering plots off the main thread
23
  import matplotlib.pyplot as plt
24
  import matplotlib.ticker as ticker
25
  from pathlib import Path
 
31
  from postprocessing.functional import postprocess_functional_structure
32
  from dataset.label2id import DATASET_ID_ALLOWED_LABEL_IDS, DATASET_LABEL_TO_DATASET_ID
33
  from utils.fetch_pretrained import download_all
34
+
35
+ import export_utils
36
+
37
+ # ZeroGPU (Hugging Face Spaces). Preinstalled on the Space; this branch
38
+ # is Space-only and never runs locally.
39
  import spaces
40
 
41
  # Constants
 
54
  device = None
55
 
56
 
57
+ def get_device():
58
+ """Select the best available device: MPS (Apple Silicon), CUDA, or CPU."""
59
+ if torch.cuda.is_available():
60
+ return torch.device("cuda")
61
+ if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
62
+ return torch.device("mps")
63
+ return torch.device("cpu")
64
+
65
+
66
+ def clear_device_cache(device):
67
+ """Clear GPU memory cache for the given device type."""
68
+ if device.type == "cuda":
69
+ torch.cuda.empty_cache()
70
+ elif device.type == "mps":
71
+ torch.mps.empty_cache()
72
+
73
+
74
  def load_checkpoint(checkpoint_path, device=None):
75
  """Load checkpoint from path"""
76
  if device is None:
 
92
  global muq_model, musicfm_model, msa_model, device
93
 
94
  # Set device
95
+ device = get_device()
96
 
97
  # Load MuQ
98
  muq_model = MuQ.from_pretrained("OpenMuQ/MuQ-large-msd-iter")
 
125
  return hp
126
 
127
 
128
+ def _gpu_duration(audio_path, win_size=420, hop_size=420, num_classes=128):
129
+ """Estimate GPU seconds for one file (ZeroGPU dynamic duration).
130
+
131
+ Conservative: 30s base + 0.2s per audio second, clamped to [60, 300].
132
+ Tune the constants from observed Space timings.
133
+ """
134
+ try:
135
+ audio_secs = librosa.get_duration(path=audio_path)
136
+ except Exception:
137
+ return 120
138
+ return int(min(300, max(60, 30 + 0.2 * audio_secs)))
139
+
140
+
141
+ @spaces.GPU(duration=_gpu_duration)
142
  def process_audio(audio_path, win_size=420, hop_size=420, num_classes=128):
143
  """Process audio file and return structure analysis results"""
144
  global muq_model, musicfm_model, msa_model, device
145
 
146
  if muq_model is None:
147
+ hp = initialize_models()
 
 
 
 
148
  else:
149
  hp = OmegaConf.load(os.path.join("configs", "SongFormer.yaml"))
150
 
 
191
  muq_output = muq_model(audio_seg.unsqueeze(0), output_hidden_states=True)
192
  muq_embd_420s = muq_output["hidden_states"][10]
193
  del muq_output
194
+ clear_device_cache(device)
195
 
196
  _, musicfm_hidden_states = musicfm_model.get_predictions(
197
  audio_seg.unsqueeze(0)
198
  )
199
  musicfm_embd_420s = musicfm_hidden_states[10]
200
  del musicfm_hidden_states
201
+ clear_device_cache(device)
202
 
203
  # Process 30-second segments
204
  wraped_muq_embd_30s = []
 
222
  output_hidden_states=True,
223
  )["hidden_states"][10]
224
  )
225
+ clear_device_cache(device)
226
 
227
  wraped_musicfm_embd_30s.append(
228
  musicfm_model.get_predictions(
229
  audio[start_idx_30s:end_idx_30s].unsqueeze(0)
230
  )[1][10]
231
  )
232
+ clear_device_cache(device)
233
 
234
  if wraped_muq_embd_30s:
235
  wraped_muq_embd_30s = torch.concatenate(wraped_muq_embd_30s, dim=1)
 
336
  logits, msa_output, label_num=8, frame_rates=AFTER_DOWNSAMPLING_FRAME_RATES
337
  ):
338
  """Create visualization plot"""
339
+ # Assume ID_TO_LABEL mapping exists
340
  try:
341
  from dataset.label2id import ID_TO_LABEL
342
  except:
 
432
  return result
433
 
434
 
435
+ def analyze_one(audio_file, out_dir, stem=None):
436
+ """Run the full per-file analysis pipeline and write export files.
437
+
438
+ Shared by the single-file and batch handlers so the two paths cannot
439
+ drift. Returns (segments, json_str, msa_str, fig, export_paths). The
440
+ caller owns the returned figure (single-file displays it via gr.Plot;
441
+ batch saves+closes it); on a write failure the figure is closed here
442
+ before re-raising so it never leaks.
443
+ """
444
+ logits, msa_output = process_audio(audio_file)
445
+ # Apply rule-based post-processing, if not needed, use in cli infer
446
+ msa_output = rule_post_processing(msa_output)
447
+ segments = format_as_segments(msa_output)
448
+ msa_str = format_as_msa(msa_output)
449
+ json_str = format_as_json(segments)
450
+ fig = create_visualization(logits, msa_output)
451
+ try:
452
+ export_paths = export_utils.write_exports(
453
+ audio_file, segments, json_str, msa_str, fig, out_dir, stem=stem
454
+ )
455
+ except Exception:
456
+ plt.close(fig)
457
+ raise
458
+ return segments, json_str, msa_str, fig, export_paths
459
+
460
+
461
  def process_and_analyze(audio_file):
462
  """Main processing function"""
463
 
 
 
 
 
 
464
  if audio_file is None:
465
+ return None, "", "", None, None, None, None, None, None
466
 
467
  try:
468
+ # Shared pipeline; exports land in a fresh per-run temp directory
469
+ # (stale runs are swept automatically by the bootstrap).
470
+ out_dir = export_utils.new_run_dir()
471
+ segments, json_format, msa_format, fig, export_paths = analyze_one(
472
+ audio_file, out_dir
473
+ )
 
 
474
 
475
  # Create table data
476
+ table_data = export_utils.segments_to_table(segments)
 
 
 
 
 
 
 
477
 
478
+ zip_path = os.path.join(
479
+ out_dir, export_utils.stem_of(audio_file) + "_songformer.zip"
480
+ )
481
+ export_utils.make_zip(list(export_paths.values()), zip_path)
482
+
483
+ return (
484
+ table_data,
485
+ json_format,
486
+ msa_format,
487
+ fig,
488
+ export_paths["json"],
489
+ export_paths["msa"],
490
+ export_paths["csv"],
491
+ export_paths["png"],
492
+ zip_path,
493
+ )
494
 
495
  except Exception as e:
496
  import traceback
497
 
498
  error_msg = f"Error: {str(e)}\n{traceback.format_exc()}"
499
+ print(error_msg) # 在命令行输出完整错误
500
+ return None, "", error_msg, None, None, None, None, None, None
501
+
502
+
503
+ def process_batch(files):
504
+ """Analyze multiple files sequentially, yielding live status.
505
+
506
+ The status table itself is the progress display: every file is listed
507
+ as queued upfront, flips to processing, then to done/failed. Dropdown
508
+ choices update as files finish so completed results can be inspected
509
+ while the rest of the batch is still running.
510
+
511
+ Outputs (per yield): status rows, ZIP download update, file-selector
512
+ update, per-file results dict (for the detail viewer).
513
+ """
514
+ if not files:
515
+ yield (
516
+ [["(no files uploaded)", "", "", ""]],
517
+ gr.update(value=None),
518
+ gr.update(choices=[], value=None),
519
+ {},
520
+ )
521
+ return
522
+
523
+ run_dir = export_utils.new_run_dir()
524
+ bundle = os.path.join(run_dir, "bundle")
525
+ os.makedirs(bundle, exist_ok=True)
526
+
527
+ # De-duplicate stems upfront so same-named uploads don't overwrite each
528
+ # other and the queued list shows the final names.
529
+ used_stems = set()
530
+ queue = []
531
+ for audio_file in files:
532
+ base = export_utils.stem_of(audio_file)
533
+ stem = base
534
+ n = 2
535
+ while stem in used_stems:
536
+ stem = f"{base}_{n}"
537
+ n += 1
538
+ used_stems.add(stem)
539
+ queue.append((audio_file, stem))
540
+
541
+ status_rows = [[stem, "⏳ queued", "", ""] for _, stem in queue]
542
+ results = {}
543
+ zipped_count = 0 # how many files the on-disk ZIP actually contains
544
+ zip_path = os.path.join(run_dir, "songformer_batch.zip")
545
+
546
+ def _rebuild_bundle_zip():
547
+ """Rewrite manifests and atomically swap in an updated ZIP.
548
+
549
+ Called after each completed file so the download button always
550
+ serves "everything so far". os.replace is atomic, so a click can
551
+ never observe a half-written archive. The (stem, segments) pairs
552
+ are derived from `results` — the single source of truth.
553
+ """
554
+ named = [(s, r["segments"]) for s, r in results.items()]
555
+ with open(
556
+ os.path.join(bundle, "summary.csv"), "w", encoding="utf-8", newline=""
557
+ ) as f:
558
+ f.write(export_utils.segments_to_combined_csv(named))
559
+ with open(
560
+ os.path.join(bundle, "combined.json"), "w", encoding="utf-8"
561
+ ) as f:
562
+ f.write(export_utils.combined_json(named))
563
+ part = zip_path + ".part"
564
+ export_utils.zip_dir(bundle, part)
565
+ os.replace(part, zip_path)
566
+
567
+ # List every file as queued; clear any previous run's results
568
+ yield (
569
+ status_rows,
570
+ gr.update(value=None, interactive=False, label="⬇️ Download all (ZIP)"),
571
+ gr.update(choices=[], value=None),
572
+ {},
573
+ )
574
+
575
+ for idx, (audio_file, stem) in enumerate(queue):
576
+ status_rows[idx] = [stem, "🔄 processing…", "", ""]
577
+ yield status_rows, gr.update(), gr.update(), results
578
+ try:
579
+ file_dir = os.path.join(bundle, stem)
580
+ os.makedirs(file_dir, exist_ok=True)
581
+ segments, json_str, msa_str, fig, paths = analyze_one(
582
+ audio_file, file_dir, stem=stem
583
+ )
584
+ plt.close(fig)
585
+ duration = (
586
+ export_utils.format_time(float(segments[-1]["end"]))
587
+ if segments
588
+ else ""
589
+ )
590
+ status_rows[idx] = [stem, "✅", len(segments), duration]
591
+ results[stem] = {
592
+ "segments": segments,
593
+ "json": json_str,
594
+ "msa": msa_str,
595
+ "png": paths["png"],
596
+ "audio": audio_file,
597
+ }
598
+ except Exception as e:
599
+ import traceback
600
+
601
+ print(f"Batch error for {stem}:\n{traceback.format_exc()}")
602
+ status_rows[idx] = [stem, "❌ " + str(e)[:80], 0, ""]
603
+ # ZeroGPU quota exhausted: every remaining file would fail the
604
+ # same way, so skip them. (Message heuristic — ZeroGPU does not
605
+ # document a stable exception class.)
606
+ if "quota" in str(e).lower():
607
+ for j in range(idx + 1, len(queue)):
608
+ status_rows[j] = [queue[j][1], "⏭️ skipped (GPU quota)", "", ""]
609
+ yield (
610
+ status_rows,
611
+ gr.update(),
612
+ gr.update(choices=list(results.keys())),
613
+ results,
614
+ )
615
+ break
616
+ else:
617
+ # A ZIP rebuild failure must NOT mark the analyzed file as
618
+ # failed: its exports exist and the next successful rebuild
619
+ # will include it (pairs derive from `results`).
620
+ try:
621
+ # Keep the ZIP downloadable mid-run with everything so far
622
+ _rebuild_bundle_zip()
623
+ zipped_count = len(results)
624
+ except Exception:
625
+ import traceback
626
+
627
+ print(f"ZIP rebuild error after {stem}:\n{traceback.format_exc()}")
628
+ if zipped_count:
629
+ zip_update = gr.update(
630
+ value=zip_path,
631
+ interactive=True,
632
+ label=f"⬇️ Download all (ZIP) — {zipped_count}/{len(queue)} files",
633
+ )
634
+ else:
635
+ zip_update = gr.update()
636
+ # Completed files become inspectable while the batch continues
637
+ yield status_rows, zip_update, gr.update(choices=list(results.keys())), results
638
+
639
+ # Manifests + ZIP were rebuilt incrementally per file; just normalize
640
+ # the button label now that the batch is complete. The button is only
641
+ # active if at least one rebuild actually produced a ZIP on disk.
642
+ yield (
643
+ status_rows,
644
+ gr.update(
645
+ value=zip_path if zipped_count else None,
646
+ interactive=bool(zipped_count),
647
+ label="⬇️ Download all (ZIP)",
648
+ ),
649
+ gr.update(choices=list(results.keys())),
650
+ results,
651
+ )
652
+
653
+
654
+ def on_select_file(stem, results):
655
+ """Render a previously-computed file's result in the batch detail viewer."""
656
+ # A selection can race an in-flight batch iteration under rare scheduler
657
+ # timings (choices reach the browser just before the state lands); the
658
+ # guard degrades to an empty view, recoverable by re-selecting.
659
+ results = results or {}
660
+ if not stem or stem not in results:
661
+ return None, "", "", None, None
662
+ r = results[stem]
663
+ return (
664
+ export_utils.segments_to_table(r["segments"]),
665
+ r["json"],
666
+ r["msa"],
667
+ r["png"],
668
+ r.get("audio"),
669
+ )
670
 
671
 
672
  # Create Gradio interface
 
708
  # Links
709
  gr.HTML("""
710
  <div class="links-container">
711
+ <img src="https://img.shields.io/badge/Python-3.10-brightgreen" alt="Python">
712
+ <img src="https://img.shields.io/badge/License-CC%20BY%204.0-lightblue" alt="License">
713
+ <a href="https://arxiv.org/abs/2510.02797">
714
+ <img src="https://img.shields.io/badge/arXiv-2510.02797-blue" alt="arXiv">
715
+ </a>
716
+ <a href="https://github.com/ASLP-lab/SongFormer">
717
+ <img src="https://img.shields.io/badge/GitHub-SongFormer-black" alt="GitHub">
718
+ </a>
719
+ <a href="https://huggingface.co/spaces/SidSaxena/SongFormer">
720
+ <img src="https://img.shields.io/badge/HuggingFace-space-yellow" alt="HuggingFace Space">
721
+ </a>
722
+ <a href="https://huggingface.co/ASLP-lab/SongFormer">
723
+ <img src="https://img.shields.io/badge/HuggingFace-model-blue" alt="HuggingFace Model">
724
+ </a>
725
+ <a href="https://huggingface.co/datasets/ASLP-lab/SongFormDB">
726
+ <img src="https://img.shields.io/badge/HF%20Dataset-SongFormDB-green" alt="Dataset SongFormDB">
727
+ </a>
728
+ <a href="https://huggingface.co/datasets/ASLP-lab/SongFormBench">
729
+ <img src="https://img.shields.io/badge/HF%20Dataset-SongFormBench-orange" alt="Dataset SongFormBench">
730
+ </a>
731
+ <a href="https://discord.gg/p5uBryC4Zs">
732
+ <img src="https://img.shields.io/badge/Discord-join%20us-purple?logo=discord&logoColor=white" alt="Discord">
733
+ </a>
734
+ <a href="http://www.npu-aslp.org/">
735
+ <img src="https://img.shields.io/badge/🏫-ASLP-grey?labelColor=lightgrey" alt="ASLP">
736
+ </a>
737
  </div>
738
  """)
739
 
740
+ with gr.Tabs():
741
+ with gr.Tab("Single File"):
742
+ # Main input area
743
+ with gr.Row():
744
+ with gr.Column(scale=3):
745
+ audio_input = gr.Audio(
746
+ label="Upload Audio File", type="filepath", elem_id="audio-input"
747
+ )
748
 
749
+ with gr.Column(scale=1):
750
+ gr.Markdown("### 📌 Examples")
751
+ gr.Examples(
752
+ examples=[
753
+ ["examples/BC_5cd6a6.mp3"],
754
+ ["examples/BC_282ece.mp3"],
755
+ ["examples/BHX_0158_letitrock.wav"],
756
+ ["examples/BHX_0374_drunkonyou.wav"],
757
+ ],
758
+ inputs=[audio_input],
759
+ label="Click to load example",
760
+ )
761
 
762
+ # Analyze button
763
+ with gr.Row():
764
+ analyze_btn = gr.Button(
765
+ "🚀 Analyze Music Structure", variant="primary", scale=1
766
+ )
 
 
 
 
 
 
 
767
 
768
+ # Results display area
769
+ with gr.Row():
770
+ with gr.Column(scale=13):
771
+ segments_table = gr.Dataframe(
772
+ headers=["Start / s (m:s.ms)", "End / s (m:s.ms)", "Label"],
773
+ label="Detected Music Segments",
774
+ interactive=False,
775
+ elem_id="result-table",
776
+ )
777
+ with gr.Column(scale=8):
778
+ with gr.Row():
779
+ with gr.Accordion("📄 JSON Output", open=False):
780
+ json_output = gr.Textbox(
781
+ label="JSON Format",
782
+ lines=15,
783
+ max_lines=20,
784
+ interactive=False,
785
+ show_copy_button=True,
786
+ )
787
+ with gr.Row():
788
+ with gr.Accordion("📋 MSA Text Output", open=False):
789
+ msa_output = gr.Textbox(
790
+ label="MSA Format",
791
+ lines=15,
792
+ max_lines=20,
793
+ interactive=False,
794
+ show_copy_button=True,
795
+ )
796
+
797
+ # Visualization plot
798
+ with gr.Row():
799
+ plot_output = gr.Plot(label="Activation Curves Visualization")
800
 
801
+ # Export / download buttons (populated after analysis)
802
+ with gr.Row():
803
+ download_json_btn = gr.DownloadButton("⬇️ JSON")
804
+ download_msa_btn = gr.DownloadButton("⬇️ MSA (.txt)")
805
+ download_csv_btn = gr.DownloadButton("⬇️ CSV")
806
+ download_png_btn = gr.DownloadButton("⬇️ Plot (.png)")
807
+ download_zip_btn = gr.DownloadButton(
808
+ "⬇️ Download all (ZIP)", variant="primary"
809
+ )
810
+
811
+ with gr.Tab("Batch"):
812
+ gr.Markdown(
813
+ "Upload multiple audio files, analyze them sequentially, "
814
+ "and download all results as a single ZIP.\n\n"
815
+ "*This Space runs on ZeroGPU: each file consumes your daily "
816
+ "GPU quota (2–40 min depending on account tier). The ZIP "
817
+ "below always contains everything analyzed so far.*"
818
  )
 
819
  with gr.Row():
820
+ with gr.Column(scale=3):
821
+ batch_files = gr.File(
822
+ label="Upload Audio Files",
823
+ file_count="multiple",
824
+ type="filepath",
 
 
825
  )
826
+ with gr.Column(scale=1):
827
+ batch_analyze_btn = gr.Button(
828
+ "🚀 Analyze Batch", variant="primary"
829
+ )
830
+ batch_zip_btn = gr.DownloadButton(
831
+ "⬇️ Download all (ZIP)", variant="primary", interactive=False
832
+ )
833
+ with gr.Row():
834
+ batch_status = gr.Dataframe(
835
+ headers=["File", "Status", "Segments", "Duration"],
836
+ label="Batch Status",
837
+ interactive=False,
838
+ )
839
+ batch_results_state = gr.State({})
840
+ gr.Markdown("### Inspect a file")
841
  with gr.Row():
842
+ with gr.Column(scale=1):
843
+ batch_file_selector = gr.Dropdown(
844
+ label="Processed File", choices=[], interactive=True
845
+ )
846
+ with gr.Column(scale=2):
847
+ batch_detail_audio = gr.Audio(
848
+ label="Listen", type="filepath", interactive=False
849
+ )
850
+ with gr.Row():
851
+ with gr.Column(scale=13):
852
+ batch_detail_table = gr.Dataframe(
853
+ headers=["Start / s (m:s.ms)", "End / s (m:s.ms)", "Label"],
854
+ label="Detected Music Segments",
855
  interactive=False,
 
856
  )
857
+ with gr.Column(scale=8):
858
+ with gr.Row():
859
+ with gr.Accordion("📄 JSON Output", open=False):
860
+ batch_detail_json = gr.Textbox(
861
+ label="JSON Format",
862
+ lines=15,
863
+ max_lines=20,
864
+ interactive=False,
865
+ show_copy_button=True,
866
+ )
867
+ with gr.Row():
868
+ with gr.Accordion("📋 MSA Text Output", open=False):
869
+ batch_detail_msa = gr.Textbox(
870
+ label="MSA Format",
871
+ lines=15,
872
+ max_lines=20,
873
+ interactive=False,
874
+ show_copy_button=True,
875
+ )
876
+ with gr.Row():
877
+ batch_detail_plot = gr.Image(label="Activation Curves Visualization")
878
 
879
  gr.HTML("""
880
  <div style="display: flex; justify-content: center; align-items: center;">
 
886
  analyze_btn.click(
887
  fn=process_and_analyze,
888
  inputs=[audio_input],
889
+ outputs=[
890
+ segments_table,
891
+ json_output,
892
+ msa_output,
893
+ plot_output,
894
+ download_json_btn,
895
+ download_msa_btn,
896
+ download_csv_btn,
897
+ download_png_btn,
898
+ download_zip_btn,
899
+ ],
900
+ )
901
+ batch_analyze_btn.click(
902
+ fn=process_batch,
903
+ inputs=[batch_files],
904
+ outputs=[
905
+ batch_status,
906
+ batch_zip_btn,
907
+ batch_file_selector,
908
+ batch_results_state,
909
+ ],
910
+ show_progress="minimal",
911
+ )
912
+ batch_file_selector.change(
913
+ fn=on_select_file,
914
+ inputs=[batch_file_selector, batch_results_state],
915
+ outputs=[
916
+ batch_detail_table,
917
+ batch_detail_json,
918
+ batch_detail_msa,
919
+ batch_detail_plot,
920
+ batch_detail_audio,
921
+ ],
922
  )
923
 
924
  if __name__ == "__main__":
 
933
  )
934
  print("Models loaded successfully!")
935
 
936
+ # Launch interface (Spaces injects its own server settings; an explicit
937
+ # port would break the platform health check)
938
  demo.launch()
export_utils.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Serialize SongFormer analysis results to downloadable files.
2
+
3
+ Pure, UI-agnostic helpers used by app.py. No model or Gradio imports, so
4
+ these can be unit-tested without loading any checkpoint.
5
+ """
6
+
7
+ import csv
8
+ import io
9
+ import json
10
+ import os
11
+ import shutil
12
+ import tempfile
13
+ import time
14
+ import zipfile
15
+
16
+ # Per-run export directories older than this (seconds) are swept at the start
17
+ # of each analysis. Recent runs are kept so their download files stay servable.
18
+ DEFAULT_EXPORT_TTL_SECONDS = 3600
19
+
20
+
21
+ def format_time(t: float) -> str:
22
+ """Render seconds as mm:ss.mmm (e.g. 61.5 -> '01:01.500')."""
23
+ minutes = int(t // 60)
24
+ seconds = t % 60
25
+ return f"{minutes:02d}:{seconds:06.3f}"
26
+
27
+
28
+ def stem_of(audio_path: str) -> str:
29
+ """Return the audio filename without directory or extension."""
30
+ return os.path.splitext(os.path.basename(audio_path))[0]
31
+
32
+
33
+ def segments_to_table(segments) -> list:
34
+ """Build display table rows: [start "(mm:ss.mmm)", end "(mm:ss.mmm)", label]."""
35
+ rows = []
36
+ for seg in segments:
37
+ start = float(seg["start"])
38
+ end = float(seg["end"])
39
+ rows.append(
40
+ [
41
+ f"{start:.2f} ({format_time(start)})",
42
+ f"{end:.2f} ({format_time(end)})",
43
+ seg["label"],
44
+ ]
45
+ )
46
+ return rows
47
+
48
+
49
+ def segments_to_csv(segments) -> str:
50
+ """Build CSV text from segment dicts.
51
+
52
+ Each segment is {"start": str|float, "end": str|float, "label": str}.
53
+ Columns: start_sec, start_mmss, end_sec, end_mmss, label.
54
+ """
55
+ buf = io.StringIO()
56
+ writer = csv.writer(buf, lineterminator="\n")
57
+ writer.writerow(["start_sec", "start_mmss", "end_sec", "end_mmss", "label"])
58
+ for seg in segments:
59
+ start = float(seg["start"])
60
+ end = float(seg["end"])
61
+ writer.writerow(
62
+ [
63
+ f"{start:.2f}",
64
+ format_time(start),
65
+ f"{end:.2f}",
66
+ format_time(end),
67
+ seg["label"],
68
+ ]
69
+ )
70
+ return buf.getvalue()
71
+
72
+
73
+ def segments_to_combined_csv(named) -> str:
74
+ """Build a combined CSV across files.
75
+
76
+ `named` is a list of (filename, segments). Columns:
77
+ filename, start_sec, start_mmss, end_sec, end_mmss, label.
78
+ """
79
+ buf = io.StringIO()
80
+ writer = csv.writer(buf, lineterminator="\n")
81
+ writer.writerow(
82
+ ["filename", "start_sec", "start_mmss", "end_sec", "end_mmss", "label"]
83
+ )
84
+ for filename, segments in named:
85
+ for seg in segments:
86
+ start = float(seg["start"])
87
+ end = float(seg["end"])
88
+ writer.writerow(
89
+ [
90
+ filename,
91
+ f"{start:.2f}",
92
+ format_time(start),
93
+ f"{end:.2f}",
94
+ format_time(end),
95
+ seg["label"],
96
+ ]
97
+ )
98
+ return buf.getvalue()
99
+
100
+
101
+ def combined_json(named) -> str:
102
+ """Build a combined JSON mapping {filename: segments} across files."""
103
+ return json.dumps(
104
+ {filename: segments for filename, segments in named},
105
+ indent=2,
106
+ ensure_ascii=False,
107
+ )
108
+
109
+
110
+ def write_exports(audio_path, segments, json_str, msa_str, fig, out_dir, stem=None) -> dict:
111
+ """Write json/msa/csv/png into out_dir; return {format: path}.
112
+
113
+ Reuses the already-built json_str/msa_str from app.py rather than
114
+ re-serializing. Saves the matplotlib figure as PNG. `stem` overrides the
115
+ filename stem (used by batch to keep de-duplicated folder and file names
116
+ consistent); defaults to the audio filename's stem.
117
+ """
118
+ if stem is None:
119
+ stem = stem_of(audio_path)
120
+ paths = {
121
+ "json": os.path.join(out_dir, f"{stem}.json"),
122
+ "msa": os.path.join(out_dir, f"{stem}.msa.txt"),
123
+ "csv": os.path.join(out_dir, f"{stem}.csv"),
124
+ "png": os.path.join(out_dir, f"{stem}.png"),
125
+ }
126
+ with open(paths["json"], "w", encoding="utf-8") as f:
127
+ f.write(json_str)
128
+ with open(paths["msa"], "w", encoding="utf-8") as f:
129
+ f.write(msa_str)
130
+ with open(paths["csv"], "w", encoding="utf-8", newline="") as f:
131
+ f.write(segments_to_csv(segments))
132
+ fig.savefig(paths["png"], dpi=150, bbox_inches="tight")
133
+ return paths
134
+
135
+
136
+ def make_zip(paths, zip_path) -> str:
137
+ """Bundle the given files into zip_path using their basenames.
138
+
139
+ Returns zip_path.
140
+ """
141
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
142
+ for p in paths:
143
+ zf.write(p, arcname=os.path.basename(p))
144
+ return zip_path
145
+
146
+
147
+ # File types that are already compressed: deflating them again wastes CPU
148
+ # (which matters because the batch ZIP is rebuilt incrementally per file).
149
+ _STORED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".zip", ".mp3", ".flac", ".ogg"}
150
+
151
+
152
+ def zip_dir(src_dir, zip_path) -> str:
153
+ """Zip the contents of src_dir into zip_path.
154
+
155
+ Arcnames are relative to src_dir, preserving subfolders. Files that are
156
+ already compressed (see _STORED_EXTENSIONS) are stored uncompressed.
157
+ Returns zip_path.
158
+ """
159
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
160
+ for root, _dirs, files in os.walk(src_dir):
161
+ for name in files:
162
+ full = os.path.join(root, name)
163
+ arcname = os.path.relpath(full, src_dir)
164
+ compress = (
165
+ zipfile.ZIP_STORED
166
+ if os.path.splitext(name)[1].lower() in _STORED_EXTENSIONS
167
+ else zipfile.ZIP_DEFLATED
168
+ )
169
+ zf.write(full, arcname=arcname, compress_type=compress)
170
+ return zip_path
171
+
172
+
173
+ def cleanup_old_exports(parent_dir, max_age_seconds, now=None) -> list:
174
+ """Remove run subdirectories of parent_dir older than max_age_seconds.
175
+
176
+ Only directories are swept (stray files are left alone). A missing
177
+ parent_dir is a no-op. Recent runs are preserved so their download files
178
+ remain servable. Returns the list of removed directory paths.
179
+ """
180
+ if now is None:
181
+ now = time.time()
182
+ removed = []
183
+ if not os.path.isdir(parent_dir):
184
+ return removed
185
+ cutoff = now - max_age_seconds
186
+ for name in sorted(os.listdir(parent_dir)):
187
+ path = os.path.join(parent_dir, name)
188
+ if not os.path.isdir(path):
189
+ continue
190
+ if os.path.getmtime(path) < cutoff:
191
+ shutil.rmtree(path, ignore_errors=True)
192
+ removed.append(path)
193
+ return removed
194
+
195
+
196
+ def new_run_dir(parent_dir=None, ttl_seconds=DEFAULT_EXPORT_TTL_SECONDS) -> str:
197
+ """Create a fresh run directory for export files, sweeping stale runs.
198
+
199
+ Shared bootstrap for the single-file and batch handlers. parent_dir
200
+ defaults to <system tempdir>/songformer_exports.
201
+ """
202
+ if parent_dir is None:
203
+ parent_dir = os.path.join(tempfile.gettempdir(), "songformer_exports")
204
+ os.makedirs(parent_dir, exist_ok=True)
205
+ cleanup_old_exports(parent_dir, ttl_seconds)
206
+ return tempfile.mkdtemp(prefix="run_", dir=parent_dir)
src/SongFormer/infer.sh CHANGED
@@ -16,6 +16,8 @@ python infer/infer.py \
16
  --checkpoint SongFormer.safetensors \
17
  --config_path SongFormer.yaml \
18
  -gn 1 \
19
- -tn 1
 
 
20
  # --debug
21
  # --no_rule_post_processing
 
16
  --checkpoint SongFormer.safetensors \
17
  --config_path SongFormer.yaml \
18
  -gn 1 \
19
+ -tn 1 \
20
+ --save_plots \
21
+ --device mps
22
  # --debug
23
  # --no_rule_post_processing
src/SongFormer/infer/infer.py CHANGED
@@ -22,9 +22,33 @@ from muq import MuQ
22
  from musicfm.model.musicfm_25hz import MusicFM25Hz
23
  from omegaconf import OmegaConf
24
  from tqdm import tqdm
 
 
 
 
25
 
26
  mp.set_start_method("spawn", force=True)
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  MUSICFM_HOME_PATH = os.path.join("ckpts", "MusicFM")
29
 
30
  BEFORE_DOWNSAMPLING_FRAME_RATES = 25
@@ -36,7 +60,7 @@ DATASET_IDS = [5]
36
  TIME_DUR = 420
37
  INPUT_SAMPLING_RATE = 24000
38
 
39
- from dataset.label2id import DATASET_ID_ALLOWED_LABEL_IDS, DATASET_LABEL_TO_DATASET_ID
40
  from postprocessing.functional import postprocess_functional_structure
41
 
42
 
@@ -60,6 +84,112 @@ def get_processing_ids(input_path, processed_ids_set):
60
  return ret
61
 
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  def load_checkpoint(checkpoint_path, device=None):
64
  """Load checkpoint from path"""
65
  if device is None:
@@ -115,7 +245,7 @@ def rule_post_processing(msa_list):
115
 
116
  def inference(rank, queue_input: mp.Queue, queue_output: mp.Queue, args):
117
  """Run inference on the input audio"""
118
- device = f"cuda:{rank}"
119
 
120
  # MuQ model loading (this will automatically fetch the checkpoint from huggingface)
121
  muq = MuQ.from_pretrained("OpenMuQ/MuQ-large-msd-iter")
@@ -199,7 +329,7 @@ def inference(rank, queue_input: mp.Queue, queue_output: mp.Queue, args):
199
  muq_output = muq(audio_seg.unsqueeze(0), output_hidden_states=True)
200
  muq_embd_420s = muq_output["hidden_states"][10]
201
  del muq_output
202
- torch.cuda.empty_cache()
203
 
204
  # MusicFM embedding
205
  _, musicfm_hidden_states = musicfm.get_predictions(
@@ -207,7 +337,7 @@ def inference(rank, queue_input: mp.Queue, queue_output: mp.Queue, args):
207
  )
208
  musicfm_embd_420s = musicfm_hidden_states[10]
209
  del musicfm_hidden_states
210
- torch.cuda.empty_cache()
211
 
212
  wraped_muq_embd_30s = []
213
  wraped_musicfm_embd_30s = []
@@ -229,13 +359,13 @@ def inference(rank, queue_input: mp.Queue, queue_output: mp.Queue, args):
229
  output_hidden_states=True,
230
  )["hidden_states"][10]
231
  )
232
- torch.cuda.empty_cache()
233
  wraped_musicfm_embd_30s.append(
234
  musicfm.get_predictions(
235
  audio[start_idx_30s:end_idx_30s].unsqueeze(0)
236
  )[1][10]
237
  )
238
- torch.cuda.empty_cache()
239
 
240
  wraped_muq_embd_30s = torch.concatenate(wraped_muq_embd_30s, dim=1)
241
  wraped_musicfm_embd_30s = torch.concatenate(
@@ -326,6 +456,11 @@ def inference(rank, queue_input: mp.Queue, queue_output: mp.Queue, args):
326
  ensure_ascii=False,
327
  )
328
 
 
 
 
 
 
329
  queue_output.put(None)
330
 
331
  except Exception as e:
@@ -368,6 +503,8 @@ def main(args):
368
  checkpoint=args.checkpoint,
369
  config_path=args.config_path,
370
  no_rule_post_processing=args.no_rule_post_processing,
 
 
371
  )
372
 
373
  processes = []
@@ -433,6 +570,12 @@ if __name__ == "__main__":
433
  help="Disable rule-based post-processing",
434
  )
435
  parser.add_argument("--debug", action="store_true", help="Enable debug mode")
 
 
 
 
 
 
436
 
437
  args = parser.parse_args()
438
 
 
22
  from musicfm.model.musicfm_25hz import MusicFM25Hz
23
  from omegaconf import OmegaConf
24
  from tqdm import tqdm
25
+ import matplotlib
26
+ matplotlib.use("Agg")
27
+ import matplotlib.pyplot as plt
28
+ import matplotlib.ticker as ticker
29
 
30
  mp.set_start_method("spawn", force=True)
31
 
32
+
33
+ def get_device(device_override=None, rank=0):
34
+ """Select the best available device: MPS (Apple Silicon), CUDA, or CPU."""
35
+ if device_override:
36
+ return torch.device(device_override)
37
+ if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
38
+ return torch.device("mps")
39
+ if torch.cuda.is_available():
40
+ return torch.device(f"cuda:{rank}")
41
+ return torch.device("cpu")
42
+
43
+
44
+ def clear_device_cache(device):
45
+ """Clear GPU memory cache for the given device type."""
46
+ if device.type == "cuda":
47
+ torch.cuda.empty_cache()
48
+ elif device.type == "mps":
49
+ torch.mps.empty_cache()
50
+
51
+
52
  MUSICFM_HOME_PATH = os.path.join("ckpts", "MusicFM")
53
 
54
  BEFORE_DOWNSAMPLING_FRAME_RATES = 25
 
60
  TIME_DUR = 420
61
  INPUT_SAMPLING_RATE = 24000
62
 
63
+ from dataset.label2id import DATASET_ID_ALLOWED_LABEL_IDS, DATASET_LABEL_TO_DATASET_ID, ID_TO_LABEL
64
  from postprocessing.functional import postprocess_functional_structure
65
 
66
 
 
84
  return ret
85
 
86
 
87
+ def save_visualizations(logits, msa_infer_output, data_id, output_dir, label_num=8):
88
+ """Save activation visualizations (line plot PDF + heatmap PNG) and raw logits (.npy)."""
89
+ try:
90
+ function_vals = logits["function_logits"].squeeze().cpu().numpy() # [T, 128]
91
+ boundary_vals = logits["boundary_logits"].squeeze().cpu().numpy() # [T]
92
+
93
+ # --- Raw logits ---
94
+ logits_dir = os.path.join(output_dir, "logits")
95
+ os.makedirs(logits_dir, exist_ok=True)
96
+ np.save(os.path.join(logits_dir, f"{data_id}_function.npy"), function_vals)
97
+ np.save(os.path.join(logits_dir, f"{data_id}_boundary.npy"), boundary_vals)
98
+
99
+ # --- Plots ---
100
+ plots_dir = os.path.join(output_dir, "plots")
101
+ os.makedirs(plots_dir, exist_ok=True)
102
+
103
+ T = function_vals.shape[0]
104
+ time_axis = np.arange(T) / AFTER_DOWNSAMPLING_FRAME_RATES
105
+ top_classes = np.argsort(function_vals.mean(axis=0))[-label_num:]
106
+
107
+ # A. Line plot (PDF)
108
+ fig, ax = plt.subplots(2, 1, figsize=(15, 8), sharex=True)
109
+
110
+ ax[0].plot(time_axis, boundary_vals, label="Boundary logit", color="orange")
111
+ ax[0].set_title("Boundary logits")
112
+ ax[0].set_ylabel("Logit")
113
+ ax[0].legend()
114
+ ax[0].grid(True)
115
+
116
+ for cls in top_classes:
117
+ ax[1].plot(
118
+ time_axis,
119
+ function_vals[:, cls],
120
+ label=f"{ID_TO_LABEL.get(cls, f'Class_{cls}')}",
121
+ )
122
+ ax[1].set_title(f"Top {label_num} Function logits by mean activation")
123
+ ax[1].set_xlabel("Time (seconds)")
124
+ ax[1].set_ylabel("Logit")
125
+ ax[1].xaxis.set_major_locator(ticker.MultipleLocator(20))
126
+ ax[1].xaxis.set_minor_locator(ticker.MultipleLocator(5))
127
+ ax[1].xaxis.set_major_formatter(ticker.FormatStrFormatter("%.1f"))
128
+ ax[1].legend()
129
+ ax[1].grid(True)
130
+
131
+ for t_sec, label in msa_infer_output:
132
+ for a in ax:
133
+ a.axvline(x=t_sec, color="red", linestyle="--", linewidth=0.8)
134
+ if label != "end":
135
+ ax[1].text(
136
+ t_sec + 0.3,
137
+ ax[1].get_ylim()[1] * 0.85,
138
+ label,
139
+ rotation=90,
140
+ fontsize=8,
141
+ color="red",
142
+ )
143
+
144
+ plt.suptitle(f"{data_id} — MSA Logits Overview", fontsize=16)
145
+ plt.tight_layout()
146
+ plt.savefig(os.path.join(plots_dir, f"{data_id}.pdf"), bbox_inches="tight")
147
+ plt.close(fig)
148
+
149
+ # B. Heatmap (PNG)
150
+ fig, ax = plt.subplots(figsize=(15, 6))
151
+ class_labels = [ID_TO_LABEL.get(cls, f"Class_{cls}") for cls in top_classes]
152
+ heatmap_data = function_vals[:, top_classes] # [T, label_num]
153
+
154
+ im = ax.imshow(
155
+ heatmap_data.T,
156
+ aspect="auto",
157
+ origin="lower",
158
+ cmap="viridis",
159
+ extent=[time_axis[0], time_axis[-1], -0.5, label_num - 0.5],
160
+ )
161
+ ax.set_yticks(range(label_num))
162
+ ax.set_yticklabels(class_labels)
163
+ ax.set_xlabel("Time (seconds)")
164
+ ax.set_ylabel("Class")
165
+ ax.set_title(f"{data_id} — Function Logits Heatmap (Top {label_num})")
166
+
167
+ for t_sec, label in msa_infer_output:
168
+ ax.axvline(x=t_sec, color="white", linestyle="--", linewidth=0.8, alpha=0.7)
169
+ if label != "end":
170
+ ax.text(
171
+ t_sec + 0.3,
172
+ label_num - 0.7,
173
+ label,
174
+ rotation=90,
175
+ fontsize=7,
176
+ color="white",
177
+ alpha=0.9,
178
+ )
179
+
180
+ plt.colorbar(im, ax=ax, label="Logit value")
181
+ plt.tight_layout()
182
+ plt.savefig(
183
+ os.path.join(plots_dir, f"{data_id}_heatmap.png"),
184
+ dpi=150,
185
+ bbox_inches="tight",
186
+ )
187
+ plt.close(fig)
188
+
189
+ except Exception as e:
190
+ logger.error(f"Visualization failed for {data_id}: {e}")
191
+
192
+
193
  def load_checkpoint(checkpoint_path, device=None):
194
  """Load checkpoint from path"""
195
  if device is None:
 
245
 
246
  def inference(rank, queue_input: mp.Queue, queue_output: mp.Queue, args):
247
  """Run inference on the input audio"""
248
+ device = get_device(getattr(args, 'device', None), rank)
249
 
250
  # MuQ model loading (this will automatically fetch the checkpoint from huggingface)
251
  muq = MuQ.from_pretrained("OpenMuQ/MuQ-large-msd-iter")
 
329
  muq_output = muq(audio_seg.unsqueeze(0), output_hidden_states=True)
330
  muq_embd_420s = muq_output["hidden_states"][10]
331
  del muq_output
332
+ clear_device_cache(device)
333
 
334
  # MusicFM embedding
335
  _, musicfm_hidden_states = musicfm.get_predictions(
 
337
  )
338
  musicfm_embd_420s = musicfm_hidden_states[10]
339
  del musicfm_hidden_states
340
+ clear_device_cache(device)
341
 
342
  wraped_muq_embd_30s = []
343
  wraped_musicfm_embd_30s = []
 
359
  output_hidden_states=True,
360
  )["hidden_states"][10]
361
  )
362
+ clear_device_cache(device)
363
  wraped_musicfm_embd_30s.append(
364
  musicfm.get_predictions(
365
  audio[start_idx_30s:end_idx_30s].unsqueeze(0)
366
  )[1][10]
367
  )
368
+ clear_device_cache(device)
369
 
370
  wraped_muq_embd_30s = torch.concatenate(wraped_muq_embd_30s, dim=1)
371
  wraped_musicfm_embd_30s = torch.concatenate(
 
456
  ensure_ascii=False,
457
  )
458
 
459
+ if args.save_plots:
460
+ save_visualizations(
461
+ logits, msa_infer_output, Path(item).stem, args.output_dir
462
+ )
463
+
464
  queue_output.put(None)
465
 
466
  except Exception as e:
 
503
  checkpoint=args.checkpoint,
504
  config_path=args.config_path,
505
  no_rule_post_processing=args.no_rule_post_processing,
506
+ device=getattr(args, 'device', None),
507
+ save_plots=args.save_plots,
508
  )
509
 
510
  processes = []
 
570
  help="Disable rule-based post-processing",
571
  )
572
  parser.add_argument("--debug", action="store_true", help="Enable debug mode")
573
+ parser.add_argument(
574
+ "--save_plots",
575
+ action="store_true",
576
+ help="Save activation visualizations (line plots + heatmaps) and raw logits (.npy)",
577
+ )
578
+ parser.add_argument("--device", type=str, default=None, help="Force device (mps, cuda, cpu). Auto-detected if not set.")
579
 
580
  args = parser.parse_args()
581
 
src/third_party/MuQ/.gitattributes DELETED
@@ -1,2 +0,0 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
 
 
 
src/third_party/MuQ/.gitignore DELETED
@@ -1,46 +0,0 @@
1
- # Byte-compiled / optimized / DLL files
2
- __pycache__/
3
- *.py[cod]
4
- *$py.class
5
- *.egg*/
6
- *pyc
7
-
8
- # Distribution / packaging
9
- .Python
10
- env/
11
- build/
12
- dist/
13
- *.log
14
-
15
- # pyenv
16
- .python-version
17
-
18
- # dotenv
19
- .env
20
-
21
- # virtualenv
22
- .venv/
23
- venv/
24
- ENV/
25
-
26
- # VSCode settings
27
- .vscode
28
-
29
- # IDEA files
30
- .idea
31
-
32
- # OSX dir files
33
- .DS_Store
34
-
35
- # Sublime Text settings
36
- *.sublime-workspace
37
- *.sublime-project
38
-
39
- # custom
40
- open/
41
- src/recipes/pretrain/dataset/music4all/*.json
42
- src/recipes/contrastive_learning/datasets/mtg-jamendo/*.json
43
- runs/
44
- output/
45
- logs
46
- outputs/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/.gitmodules DELETED
@@ -1,3 +0,0 @@
1
- [submodule "src/recipes/pretrain/fairseq"]
2
- path = src/recipes/pretrain/fairseq
3
- url = https://github.com/facebookresearch/fairseq
 
 
 
 
src/third_party/MuQ/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) Tencent.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/LICENSE_weights DELETED
@@ -1,399 +0,0 @@
1
- Attribution-NonCommercial 4.0 International
2
-
3
- =======================================================================
4
-
5
- Creative Commons Corporation ("Creative Commons") is not a law firm and
6
- does not provide legal services or legal advice. Distribution of
7
- Creative Commons public licenses does not create a lawyer-client or
8
- other relationship. Creative Commons makes its licenses and related
9
- information available on an "as-is" basis. Creative Commons gives no
10
- warranties regarding its licenses, any material licensed under their
11
- terms and conditions, or any related information. Creative Commons
12
- disclaims all liability for damages resulting from their use to the
13
- fullest extent possible.
14
-
15
- Using Creative Commons Public Licenses
16
-
17
- Creative Commons public licenses provide a standard set of terms and
18
- conditions that creators and other rights holders may use to share
19
- original works of authorship and other material subject to copyright
20
- and certain other rights specified in the public license below. The
21
- following considerations are for informational purposes only, are not
22
- exhaustive, and do not form part of our licenses.
23
-
24
- Considerations for licensors: Our public licenses are
25
- intended for use by those authorized to give the public
26
- permission to use material in ways otherwise restricted by
27
- copyright and certain other rights. Our licenses are
28
- irrevocable. Licensors should read and understand the terms
29
- and conditions of the license they choose before applying it.
30
- Licensors should also secure all rights necessary before
31
- applying our licenses so that the public can reuse the
32
- material as expected. Licensors should clearly mark any
33
- material not subject to the license. This includes other CC-
34
- licensed material, or material used under an exception or
35
- limitation to copyright. More considerations for licensors:
36
- wiki.creativecommons.org/Considerations_for_licensors
37
-
38
- Considerations for the public: By using one of our public
39
- licenses, a licensor grants the public permission to use the
40
- licensed material under specified terms and conditions. If
41
- the licensor's permission is not necessary for any reason--for
42
- example, because of any applicable exception or limitation to
43
- copyright--then that use is not regulated by the license. Our
44
- licenses grant only permissions under copyright and certain
45
- other rights that a licensor has authority to grant. Use of
46
- the licensed material may still be restricted for other
47
- reasons, including because others have copyright or other
48
- rights in the material. A licensor may make special requests,
49
- such as asking that all changes be marked or described.
50
- Although not required by our licenses, you are encouraged to
51
- respect those requests where reasonable. More_considerations
52
- for the public:
53
- wiki.creativecommons.org/Considerations_for_licensees
54
-
55
- =======================================================================
56
-
57
- Creative Commons Attribution-NonCommercial 4.0 International Public
58
- License
59
-
60
- By exercising the Licensed Rights (defined below), You accept and agree
61
- to be bound by the terms and conditions of this Creative Commons
62
- Attribution-NonCommercial 4.0 International Public License ("Public
63
- License"). To the extent this Public License may be interpreted as a
64
- contract, You are granted the Licensed Rights in consideration of Your
65
- acceptance of these terms and conditions, and the Licensor grants You
66
- such rights in consideration of benefits the Licensor receives from
67
- making the Licensed Material available under these terms and
68
- conditions.
69
-
70
- Section 1 -- Definitions.
71
-
72
- a. Adapted Material means material subject to Copyright and Similar
73
- Rights that is derived from or based upon the Licensed Material
74
- and in which the Licensed Material is translated, altered,
75
- arranged, transformed, or otherwise modified in a manner requiring
76
- permission under the Copyright and Similar Rights held by the
77
- Licensor. For purposes of this Public License, where the Licensed
78
- Material is a musical work, performance, or sound recording,
79
- Adapted Material is always produced where the Licensed Material is
80
- synched in timed relation with a moving image.
81
-
82
- b. Adapter's License means the license You apply to Your Copyright
83
- and Similar Rights in Your contributions to Adapted Material in
84
- accordance with the terms and conditions of this Public License.
85
-
86
- c. Copyright and Similar Rights means copyright and/or similar rights
87
- closely related to copyright including, without limitation,
88
- performance, broadcast, sound recording, and Sui Generis Database
89
- Rights, without regard to how the rights are labeled or
90
- categorized. For purposes of this Public License, the rights
91
- specified in Section 2(b)(1)-(2) are not Copyright and Similar
92
- Rights.
93
- d. Effective Technological Measures means those measures that, in the
94
- absence of proper authority, may not be circumvented under laws
95
- fulfilling obligations under Article 11 of the WIPO Copyright
96
- Treaty adopted on December 20, 1996, and/or similar international
97
- agreements.
98
-
99
- e. Exceptions and Limitations means fair use, fair dealing, and/or
100
- any other exception or limitation to Copyright and Similar Rights
101
- that applies to Your use of the Licensed Material.
102
-
103
- f. Licensed Material means the artistic or literary work, database,
104
- or other material to which the Licensor applied this Public
105
- License.
106
-
107
- g. Licensed Rights means the rights granted to You subject to the
108
- terms and conditions of this Public License, which are limited to
109
- all Copyright and Similar Rights that apply to Your use of the
110
- Licensed Material and that the Licensor has authority to license.
111
-
112
- h. Licensor means the individual(s) or entity(ies) granting rights
113
- under this Public License.
114
-
115
- i. NonCommercial means not primarily intended for or directed towards
116
- commercial advantage or monetary compensation. For purposes of
117
- this Public License, the exchange of the Licensed Material for
118
- other material subject to Copyright and Similar Rights by digital
119
- file-sharing or similar means is NonCommercial provided there is
120
- no payment of monetary compensation in connection with the
121
- exchange.
122
-
123
- j. Share means to provide material to the public by any means or
124
- process that requires permission under the Licensed Rights, such
125
- as reproduction, public display, public performance, distribution,
126
- dissemination, communication, or importation, and to make material
127
- available to the public including in ways that members of the
128
- public may access the material from a place and at a time
129
- individually chosen by them.
130
-
131
- k. Sui Generis Database Rights means rights other than copyright
132
- resulting from Directive 96/9/EC of the European Parliament and of
133
- the Council of 11 March 1996 on the legal protection of databases,
134
- as amended and/or succeeded, as well as other essentially
135
- equivalent rights anywhere in the world.
136
-
137
- l. You means the individual or entity exercising the Licensed Rights
138
- under this Public License. Your has a corresponding meaning.
139
-
140
- Section 2 -- Scope.
141
-
142
- a. License grant.
143
-
144
- 1. Subject to the terms and conditions of this Public License,
145
- the Licensor hereby grants You a worldwide, royalty-free,
146
- non-sublicensable, non-exclusive, irrevocable license to
147
- exercise the Licensed Rights in the Licensed Material to:
148
-
149
- a. reproduce and Share the Licensed Material, in whole or
150
- in part, for NonCommercial purposes only; and
151
-
152
- b. produce, reproduce, and Share Adapted Material for
153
- NonCommercial purposes only.
154
-
155
- 2. Exceptions and Limitations. For the avoidance of doubt, where
156
- Exceptions and Limitations apply to Your use, this Public
157
- License does not apply, and You do not need to comply with
158
- its terms and conditions.
159
-
160
- 3. Term. The term of this Public License is specified in Section
161
- 6(a).
162
-
163
- 4. Media and formats; technical modifications allowed. The
164
- Licensor authorizes You to exercise the Licensed Rights in
165
- all media and formats whether now known or hereafter created,
166
- and to make technical modifications necessary to do so. The
167
- Licensor waives and/or agrees not to assert any right or
168
- authority to forbid You from making technical modifications
169
- necessary to exercise the Licensed Rights, including
170
- technical modifications necessary to circumvent Effective
171
- Technological Measures. For purposes of this Public License,
172
- simply making modifications authorized by this Section 2(a)
173
- (4) never produces Adapted Material.
174
-
175
- 5. Downstream recipients.
176
-
177
- a. Offer from the Licensor -- Licensed Material. Every
178
- recipient of the Licensed Material automatically
179
- receives an offer from the Licensor to exercise the
180
- Licensed Rights under the terms and conditions of this
181
- Public License.
182
-
183
- b. No downstream restrictions. You may not offer or impose
184
- any additional or different terms or conditions on, or
185
- apply any Effective Technological Measures to, the
186
- Licensed Material if doing so restricts exercise of the
187
- Licensed Rights by any recipient of the Licensed
188
- Material.
189
-
190
- 6. No endorsement. Nothing in this Public License constitutes or
191
- may be construed as permission to assert or imply that You
192
- are, or that Your use of the Licensed Material is, connected
193
- with, or sponsored, endorsed, or granted official status by,
194
- the Licensor or others designated to receive attribution as
195
- provided in Section 3(a)(1)(A)(i).
196
-
197
- b. Other rights.
198
-
199
- 1. Moral rights, such as the right of integrity, are not
200
- licensed under this Public License, nor are publicity,
201
- privacy, and/or other similar personality rights; however, to
202
- the extent possible, the Licensor waives and/or agrees not to
203
- assert any such rights held by the Licensor to the limited
204
- extent necessary to allow You to exercise the Licensed
205
- Rights, but not otherwise.
206
-
207
- 2. Patent and trademark rights are not licensed under this
208
- Public License.
209
-
210
- 3. To the extent possible, the Licensor waives any right to
211
- collect royalties from You for the exercise of the Licensed
212
- Rights, whether directly or through a collecting society
213
- under any voluntary or waivable statutory or compulsory
214
- licensing scheme. In all other cases the Licensor expressly
215
- reserves any right to collect such royalties, including when
216
- the Licensed Material is used other than for NonCommercial
217
- purposes.
218
-
219
- Section 3 -- License Conditions.
220
-
221
- Your exercise of the Licensed Rights is expressly made subject to the
222
- following conditions.
223
-
224
- a. Attribution.
225
-
226
- 1. If You Share the Licensed Material (including in modified
227
- form), You must:
228
-
229
- a. retain the following if it is supplied by the Licensor
230
- with the Licensed Material:
231
-
232
- i. identification of the creator(s) of the Licensed
233
- Material and any others designated to receive
234
- attribution, in any reasonable manner requested by
235
- the Licensor (including by pseudonym if
236
- designated);
237
-
238
- ii. a copyright notice;
239
-
240
- iii. a notice that refers to this Public License;
241
-
242
- iv. a notice that refers to the disclaimer of
243
- warranties;
244
-
245
- v. a URI or hyperlink to the Licensed Material to the
246
- extent reasonably practicable;
247
-
248
- b. indicate if You modified the Licensed Material and
249
- retain an indication of any previous modifications; and
250
-
251
- c. indicate the Licensed Material is licensed under this
252
- Public License, and include the text of, or the URI or
253
- hyperlink to, this Public License.
254
-
255
- 2. You may satisfy the conditions in Section 3(a)(1) in any
256
- reasonable manner based on the medium, means, and context in
257
- which You Share the Licensed Material. For example, it may be
258
- reasonable to satisfy the conditions by providing a URI or
259
- hyperlink to a resource that includes the required
260
- information.
261
-
262
- 3. If requested by the Licensor, You must remove any of the
263
- information required by Section 3(a)(1)(A) to the extent
264
- reasonably practicable.
265
-
266
- 4. If You Share Adapted Material You produce, the Adapter's
267
- License You apply must not prevent recipients of the Adapted
268
- Material from complying with this Public License.
269
-
270
- Section 4 -- Sui Generis Database Rights.
271
-
272
- Where the Licensed Rights include Sui Generis Database Rights that
273
- apply to Your use of the Licensed Material:
274
-
275
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right
276
- to extract, reuse, reproduce, and Share all or a substantial
277
- portion of the contents of the database for NonCommercial purposes
278
- only;
279
-
280
- b. if You include all or a substantial portion of the database
281
- contents in a database in which You have Sui Generis Database
282
- Rights, then the database in which You have Sui Generis Database
283
- Rights (but not its individual contents) is Adapted Material; and
284
-
285
- c. You must comply with the conditions in Section 3(a) if You Share
286
- all or a substantial portion of the contents of the database.
287
-
288
- For the avoidance of doubt, this Section 4 supplements and does not
289
- replace Your obligations under this Public License where the Licensed
290
- Rights include other Copyright and Similar Rights.
291
-
292
- Section 5 -- Disclaimer of Warranties and Limitation of Liability.
293
-
294
- a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
295
- EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
296
- AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
297
- ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
298
- IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
299
- WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
300
- PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
301
- ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
302
- KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
303
- ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
304
-
305
- b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
306
- TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
307
- NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
308
- INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
309
- COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
310
- USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
311
- ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
312
- DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
313
- IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
314
-
315
- c. The disclaimer of warranties and limitation of liability provided
316
- above shall be interpreted in a manner that, to the extent
317
- possible, most closely approximates an absolute disclaimer and
318
- waiver of all liability.
319
-
320
- Section 6 -- Term and Termination.
321
-
322
- a. This Public License applies for the term of the Copyright and
323
- Similar Rights licensed here. However, if You fail to comply with
324
- this Public License, then Your rights under this Public License
325
- terminate automatically.
326
-
327
- b. Where Your right to use the Licensed Material has terminated under
328
- Section 6(a), it reinstates:
329
-
330
- 1. automatically as of the date the violation is cured, provided
331
- it is cured within 30 days of Your discovery of the
332
- violation; or
333
-
334
- 2. upon express reinstatement by the Licensor.
335
-
336
- For the avoidance of doubt, this Section 6(b) does not affect any
337
- right the Licensor may have to seek remedies for Your violations
338
- of this Public License.
339
-
340
- c. For the avoidance of doubt, the Licensor may also offer the
341
- Licensed Material under separate terms or conditions or stop
342
- distributing the Licensed Material at any time; however, doing so
343
- will not terminate this Public License.
344
-
345
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
346
- License.
347
-
348
- Section 7 -- Other Terms and Conditions.
349
-
350
- a. The Licensor shall not be bound by any additional or different
351
- terms or conditions communicated by You unless expressly agreed.
352
-
353
- b. Any arrangements, understandings, or agreements regarding the
354
- Licensed Material not stated herein are separate from and
355
- independent of the terms and conditions of this Public License.
356
-
357
- Section 8 -- Interpretation.
358
-
359
- a. For the avoidance of doubt, this Public License does not, and
360
- shall not be interpreted to, reduce, limit, restrict, or impose
361
- conditions on any use of the Licensed Material that could lawfully
362
- be made without permission under this Public License.
363
-
364
- b. To the extent possible, if any provision of this Public License is
365
- deemed unenforceable, it shall be automatically reformed to the
366
- minimum extent necessary to make it enforceable. If the provision
367
- cannot be reformed, it shall be severed from this Public License
368
- without affecting the enforceability of the remaining terms and
369
- conditions.
370
-
371
- c. No term or condition of this Public License will be waived and no
372
- failure to comply consented to unless expressly agreed to by the
373
- Licensor.
374
-
375
- d. Nothing in this Public License constitutes or may be interpreted
376
- as a limitation upon, or waiver of, any privileges and immunities
377
- that apply to the Licensor or You, including from the legal
378
- processes of any jurisdiction or authority.
379
-
380
- =======================================================================
381
-
382
- Creative Commons is not a party to its public
383
- licenses. Notwithstanding, Creative Commons may elect to apply one of
384
- its public licenses to material it publishes and in those instances
385
- will be considered the “Licensor.” The text of the Creative Commons
386
- public licenses is dedicated to the public domain under the CC0 Public
387
- Domain Dedication. Except for the limited purpose of indicating that
388
- material is shared under a Creative Commons public license or as
389
- otherwise permitted by the Creative Commons policies published at
390
- creativecommons.org/policies, Creative Commons does not authorize the
391
- use of the trademark "Creative Commons" or any other trademark or logo
392
- of Creative Commons without its prior written consent including,
393
- without limitation, in connection with any unauthorized modifications
394
- to any of its public licenses or any other arrangements,
395
- understandings, or agreements concerning use of licensed material. For
396
- the avoidance of doubt, this paragraph does not form part of the
397
- public licenses.
398
-
399
- Creative Commons may be contacted at creativecommons.org.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/README.md DELETED
@@ -1,129 +0,0 @@
1
- # <img src="images/muq-logo.jpeg" alt="" height="24px"> MuQ & MuQ-MuLan
2
-
3
- <div>
4
- <a href='#'><img alt="Static Badge" src="https://img.shields.io/badge/Python-3.8%2B-blue?logo=python&logoColor=white"></a>
5
- <a href='https://arxiv.org/abs/2501.01108'><img alt="Static Badge" src="https://img.shields.io/badge/arXiv-2501.01108-%23b31b1b?logo=arxiv&link=https%3A%2F%2Farxiv.org%2F"></a>
6
- <a href='https://huggingface.co/OpenMuQ'><img alt="Static Badge" src="https://img.shields.io/badge/huggingface-OpenMuQ-%23FFD21E?logo=huggingface&link=https%3A%2F%2Fhuggingface.co%2FOpenMuQ"></a>
7
- <a href='https://pytorch.org/'><img alt="Static Badge" src="https://img.shields.io/badge/framework-PyTorch-%23EE4C2C?logo=pytorch"></a>
8
- <a href='https://pypi.org/project/muq'><img alt="Static Badge" src="https://img.shields.io/badge/pip%20install-muq-green?logo=PyPI&logoColor=white&link=https%3A%2F%2Fpypi.org%2Fproject%2Fmuq"></a>
9
- </div>
10
-
11
- This is the official repository for the paper *"**MuQ**: Self-Supervised **Mu**sic Representation Learning
12
- with Mel Residual Vector **Q**uantization"*.
13
-
14
- In this repo, the following models are released:
15
-
16
- - **MuQ**: A large music foundation model pre-trained via Self-Supervised Learning (SSL), achieving SOTA in various MIR tasks.
17
- - **MuQ-MuLan**: A music-text joint embedding model trained via contrastive learning, supporting both English and Chinese texts.
18
-
19
- ## Overview
20
-
21
- We develop the **MuQ** for music SSL. MuQ applys our proposed Mel-RVQ as quantitative targets and achieves SOTA performance on many music understanding (or MIR) tasks.
22
-
23
- We also construct the **MuQ-MuLan**, a CLIP-like model trained by contrastive learning, which jointly represents music and text into embeddings.
24
-
25
- For more details, please refer to our [paper](https://arxiv.org/abs/2501.01108).
26
-
27
- <div>
28
- <img src="images/radar.jpg" width="45%" alt="Evaluation on MARBLE Benchmark">
29
- <img src="images/tagging.jpg" width="45%" alt="Evaluation on Zero-shot Music Tagging">
30
- </div>
31
-
32
- ## Usage
33
-
34
- To begin with, please use pip to install the official `muq` lib, and ensure that your `python>=3.8`:
35
- ```bash
36
- pip3 install muq
37
- ```
38
-
39
-
40
- To extract music audio features using **MuQ**, you can refer to the following code:
41
- ```python
42
- import torch, librosa
43
- from muq import MuQ
44
-
45
- device = 'cuda'
46
- wav, sr = librosa.load("path/to/music_audio.wav", sr = 24000)
47
- wavs = torch.tensor(wav).unsqueeze(0).to(device)
48
-
49
- # This will automatically fetch the checkpoint from huggingface
50
- muq = MuQ.from_pretrained("OpenMuQ/MuQ-large-msd-iter")
51
- muq = muq.to(device).eval()
52
-
53
- with torch.no_grad():
54
- output = muq(wavs, output_hidden_states=True)
55
-
56
- print('Total number of layers: ', len(output.hidden_states))
57
- print('Feature shape: ', output.last_hidden_state.shape)
58
-
59
- ```
60
-
61
- Using **MuQ-MuLan** to extract the music and text embeddings and calculate the similarity:
62
- ```python
63
- import torch, librosa
64
- from muq import MuQMuLan
65
-
66
- # This will automatically fetch checkpoints from huggingface
67
- device = 'cuda'
68
- mulan = MuQMuLan.from_pretrained("OpenMuQ/MuQ-MuLan-large")
69
- mulan = mulan.to(device).eval()
70
-
71
- # Extract music embeddings
72
- wav, sr = librosa.load("path/to/music_audio.wav", sr = 24000)
73
- wavs = torch.tensor(wav).unsqueeze(0).to(device)
74
- with torch.no_grad():
75
- audio_embeds = mulan(wavs = wavs)
76
-
77
- # Extract text embeddings (texts can be in English or Chinese)
78
- texts = ["classical genres, hopeful mood, piano.", "一首适合海边风景的小提琴曲,节奏欢快"]
79
- with torch.no_grad():
80
- text_embeds = mulan(texts = texts)
81
-
82
- # Calculate dot product similarity
83
- sim = mulan.calc_similarity(audio_embeds, text_embeds)
84
- print(sim)
85
- ```
86
-
87
- > Note that both MuQ and MuQ-MuLan strictly require **24 kHz** audio as input.
88
- > We recommend using **fp32** during MuQ inference to avoid potential NaN issues.
89
-
90
-
91
- ## Performance
92
-
93
- <img src="images/tab-marble.jpg" width="100%" style="max-width: 800px" alt="Table MARBLE Benchmark">
94
- <img src="images/tab-mulan.png" width="50%" style="max-width: 400px; margin: 0 25%" alt="Table Mulan Results">
95
-
96
- ## Model Checkpoints
97
-
98
- | Model Name | Parameters | Data | HuggingFace🤗 |
99
- | ----------- | --- | --- | ----------- |
100
- | MuQ | ~300M | MSD dataset | [OpenMuQ/MuQ-large-msd-iter](https://huggingface.co/OpenMuQ/MuQ-large-msd-iter) |
101
- | MuQ-MuLan | ~700M | music-text pairs | [OpenMuQ/MuQ-MuLan-large](https://huggingface.co/OpenMuQ/MuQ-MuLan-large) |
102
-
103
- **Note**: Please note that the open-sourced MuQ was trained on the Million Song Dataset. Due to differences in dataset size, the open-sourced model may not achieve the same level of performance as reported in the paper. The training recipes can be found [here](./src/recipes).
104
-
105
- ## License
106
-
107
- The code in this repository is released under the MIT license as found in the [LICENSE](LICENSE) file.
108
-
109
- The model weights (MuQ-large-msd-iter, MuQ-MuLan-large) in this repository are released under the CC-BY-NC 4.0 license, as detailed in the [LICENSE_weights](LICENSE_weights) file.
110
-
111
- ## Citation
112
-
113
- ```
114
- @article{zhu2025muq,
115
- title={MuQ: Self-Supervised Music Representation Learning with Mel Residual Vector Quantization},
116
- author={Haina Zhu and Yizhi Zhou and Hangting Chen and Jianwei Yu and Ziyang Ma and Rongzhi Gu and Yi Luo and Wei Tan and Xie Chen},
117
- journal={arXiv preprint arXiv:2501.01108},
118
- year={2025}
119
- }
120
- ```
121
-
122
- ## Acknowledgement
123
-
124
- We borrow many codes from the following repositories:
125
- - [lucidrains/musiclm-pytorch](https://github.com/lucidrains/musiclm-pytorch)
126
- - [minzwon/musicfm](https://github.com/minzwon/musicfm)
127
-
128
-
129
- Also, we are especially grateful to the awesome [MARBLE-Benchmark](https://github.com/a43992899/MARBLE-Benchmark).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/images/muq-logo.jpeg DELETED
Binary file (39.3 kB)
 
src/third_party/MuQ/images/radar.jpg DELETED
Binary file (43.1 kB)
 
src/third_party/MuQ/images/tab-marble.jpg DELETED

Git LFS Details

  • SHA256: d7287c7741b06062fb5cb57b10149c9138cbb56ad3eabef7e3b957ea32db1639
  • Pointer size: 131 Bytes
  • Size of remote file: 264 kB
src/third_party/MuQ/images/tab-mulan.png DELETED
Binary file (83.6 kB)
 
src/third_party/MuQ/images/tagging.jpg DELETED
Binary file (44.4 kB)
 
src/third_party/MuQ/requirements.txt DELETED
@@ -1,11 +0,0 @@
1
- einops
2
- librosa
3
- nnAudio
4
- numpy
5
- soundfile
6
- torch
7
- torchaudio
8
- tqdm
9
- transformers
10
- easydict
11
- x_clip
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/setup.py DELETED
@@ -1,34 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- setup(
4
- name='muq', # Name of the package
5
- version='0.1.0', # Version of the package
6
- packages=find_packages(where='src'), # Automatically discover packages under the 'src' directory
7
- package_dir={'': 'src'}, # Specify the root directory for packages as 'src'
8
- include_package_data=True, # Include additional files, such as static files
9
- install_requires=[ # List of dependencies
10
- "einops",
11
- "librosa",
12
- "nnAudio",
13
- "numpy",
14
- "soundfile",
15
- "torch",
16
- "torchaudio",
17
- "tqdm",
18
- "transformers",
19
- "easydict",
20
- "x_clip",
21
- ],
22
- author='Haina Zhu', # Author name
23
- author_email='juhayna@qq.com', # Author email address
24
- description='MuQ: A deep learning model for music and text', # Short description of the package
25
- long_description=open('README.md', encoding='utf-8').read(), # Long description from the README file
26
- long_description_content_type='text/markdown', # Format of the long description (Markdown)
27
- url='https://github.com/tencent-ailab/MuQ', # Project URL
28
- classifiers=[
29
- 'Programming Language :: Python :: 3', # Python 3 support
30
- 'License :: OSI Approved :: MIT License', # License type
31
- 'Operating System :: OS Independent', # Supports all operating systems
32
- ],
33
- python_requires='>=3.8', # Supported Python version
34
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/__init__.py DELETED
@@ -1,2 +0,0 @@
1
- from .muq import MuQ, MuQConfig
2
- from .muq_mulan import MuQMuLan, MuQMuLanConfig
 
 
 
src/third_party/MuQ/src/muq/muq/__init__.py DELETED
@@ -1 +0,0 @@
1
- from .muq import MuQConfig, MuQ
 
 
src/third_party/MuQ/src/muq/muq/models/__init__.py DELETED
File without changes
src/third_party/MuQ/src/muq/muq/models/muq_model.py DELETED
@@ -1,366 +0,0 @@
1
- import json
2
- import random
3
- import torch
4
- from torch import nn
5
- from einops import rearrange
6
- import os
7
- from easydict import EasyDict
8
-
9
- from ..modules.random_quantizer import RandomProjectionQuantizer
10
- from ..modules.features import MelSTFT
11
- from ..modules.conv import Conv2dSubsampling
12
-
13
- class MuQModel(nn.Module):
14
-
15
- def __init__(
16
- self,
17
- num_codebooks=1,
18
- codebook_dim=16,
19
- codebook_size=4096,
20
- features=["melspec_2048"],
21
- hop_length=240,
22
- n_mels=128,
23
- conv_dim=512,
24
- encoder_dim=1024,
25
- encoder_depth=12,
26
- mask_hop=0.4,
27
- mask_prob=0.6,
28
- is_flash=False,
29
- stat=dict(),
30
- w2v2_config=dict(),
31
- use_rvq_target=False,
32
- use_vq_target=False,
33
- use_encodec_target=False,
34
- rvq_ckpt_path=None,
35
- recon_loss_ratio=None,
36
- label_rate=25,
37
- rvq_n_codebooks=8,
38
- rvq_multi_layer_num=1,
39
- ):
40
- super().__init__()
41
-
42
- # global variables
43
- self.hop_length = hop_length
44
- self.mask_hop = mask_hop
45
- self.mask_prob = mask_prob
46
- self.num_codebooks = num_codebooks
47
- self.codebook_size = codebook_size
48
- self.features = features
49
- self.recon_loss_ratio = recon_loss_ratio
50
- self.n_fold = int(100//label_rate)
51
- self.label_rate = label_rate
52
-
53
- # load feature mean / std stats
54
- self.stat = stat
55
-
56
- # feature extractor
57
- self.preprocessor_melspec_2048 = MelSTFT(
58
- n_fft=2048, hop_length=hop_length, is_db=True
59
- )
60
-
61
- # random quantizer
62
- self.use_rvq_target = use_rvq_target
63
- self.use_vq_target = use_vq_target
64
- self.use_encodec_target = use_encodec_target
65
-
66
- seed = 142
67
- if self.use_rvq_like_target:
68
- if use_rvq_target:
69
- from ..modules.rvq import ResidualVectorQuantize
70
-
71
- inp_dim = 128*self.n_fold
72
- self.rvq = ResidualVectorQuantize(
73
- input_dim = inp_dim,
74
- n_codebooks = rvq_n_codebooks,
75
- codebook_size = 1024,
76
- codebook_dim = 16,
77
- quantizer_dropout = 0.0,
78
- use_multi_layer_num = rvq_multi_layer_num,
79
- )
80
- elif use_vq_target:
81
- from ..modules.rvq import VectorQuantize
82
-
83
- self.rvq = VectorQuantize(
84
- input_dim = 128*self.n_fold,
85
- codebook_size = 1024,
86
- codebook_dim = 8,
87
- stale_tolerance = 1000,
88
- mfcc_clustering = False
89
- )
90
- elif use_encodec_target:
91
- from encodec import EncodecModel
92
- self.rvq = EncodecModel.encodec_model_24khz()
93
- self.rvq.set_target_bandwidth(6.0)
94
- for param in self.rvq.parameters():
95
- param.requires_grad = False
96
-
97
- if rvq_ckpt_path is not None and os.path.exists(rvq_ckpt_path):
98
- state_dict = torch.load(rvq_ckpt_path, map_location="cpu")
99
- self.rvq.load_state_dict(state_dict)
100
- else:
101
- pass
102
- # print(f'Checkpoint for rvq `{rvq_ckpt_path}` not found. Using random initialization.')
103
- else:
104
- for feature in self.features:
105
- for i in range(num_codebooks):
106
- setattr(
107
- self,
108
- f"quantizer_{feature}", # _{i}
109
- RandomProjectionQuantizer(
110
- n_mels * self.n_fold, codebook_dim, codebook_size, seed=seed + i
111
- ),
112
- )
113
-
114
- # two residual convolution layers + one projection layer
115
- strides_factory = {
116
- 4: [2, 2],
117
- 2: [2, 1]
118
- }
119
- self.conv = Conv2dSubsampling(
120
- 1, conv_dim, encoder_dim, strides=strides_factory.get(self.n_fold), n_bands=n_mels
121
- )
122
-
123
- # Conformer
124
- if is_flash:
125
- from modules.flash_conformer import (
126
- Wav2Vec2ConformerEncoder,
127
- Wav2Vec2ConformerConfig,
128
- )
129
- else:
130
- from transformers.models.wav2vec2_conformer.modeling_wav2vec2_conformer import (
131
- Wav2Vec2ConformerEncoder,
132
- Wav2Vec2ConformerConfig,
133
- )
134
- config = EasyDict(w2v2_config)
135
- config.num_hidden_layers = encoder_depth
136
- config.hidden_size = encoder_dim
137
-
138
- self.conformer = Wav2Vec2ConformerEncoder(config)
139
-
140
- self.linear = nn.Linear(encoder_dim, codebook_size) # projection layer
141
-
142
- # reconstruct melspec
143
- if self.recon_loss_ratio is not None and self.recon_loss_ratio > 0:
144
- self.recon_proj = nn.Linear(encoder_dim, n_mels * self.n_fold)
145
- self.recon_loss = nn.MSELoss()
146
-
147
- # loss function
148
- self.loss = nn.CrossEntropyLoss()
149
-
150
- # cls token (used for sequence classification)
151
- random.seed(seed)
152
- self.cls_token = nn.Parameter(torch.randn(encoder_dim))
153
-
154
-
155
- @property
156
- def use_rvq_like_target(self):
157
- return self.use_rvq_target or self.use_vq_target or self.use_encodec_target
158
-
159
- def masking(self, x, attention_mask=None):
160
- """random masking of 400ms with given probability"""
161
- mx = x.clone()
162
- b, t = mx.shape
163
- len_masking_raw = int(24000 * self.mask_hop)
164
- len_masking_token = int(24000 / self.hop_length / 2 / 2 * self.mask_hop)
165
-
166
- # get random mask indices
167
- start_indices = torch.rand(b, t // len_masking_raw) < self.mask_prob
168
- time_domain_masked_indices = torch.nonzero(
169
- start_indices.repeat_interleave(len_masking_raw, dim=1)
170
- )
171
- token_domain_masked_indices = torch.nonzero(
172
- start_indices.repeat_interleave(len_masking_token, dim=1)
173
- )
174
-
175
- # mask with random values
176
- masking_noise = (
177
- torch.randn(time_domain_masked_indices.shape[0], dtype=x.dtype) * 0.1
178
- ) # 0 mean 0.1 std
179
- mx[tuple(time_domain_masked_indices.t())] = masking_noise.to(x.device)
180
-
181
- return mx, token_domain_masked_indices
182
-
183
-
184
- @torch.no_grad()
185
- def preprocessing(self, x, features):
186
- """extract classic audio features"""
187
- # check precision
188
- if x.dtype == torch.float16 or x.dtype == torch.bfloat16:
189
- precision = 16
190
- else:
191
- precision = 32
192
-
193
- out = {}
194
- for key in features:
195
- layer = getattr(self, "preprocessor_%s" % key)
196
- layer.to(x.device)
197
- dtype = x.dtype
198
- out[key] = layer(x.float())[..., :-1]
199
- if precision == 16:
200
- out[key] = out[key].half()
201
- if out[key].dtype != dtype:
202
- out[key].to(dtype=dtype)
203
- return out
204
-
205
- def encoder(self, x, *, attention_mask=None, is_features_only=False):
206
- """2-layer conv + w2v-conformer"""
207
- x = self.conv(x)
208
- mask_indices = None
209
- if attention_mask is None:
210
- out = self.conformer(x, output_hidden_states=True)
211
- else:
212
- attention_mask = attention_mask.bool()
213
- skip_n = int(attention_mask.size(-1) / x.size(1))
214
- attention_mask = attention_mask[:, ::skip_n]
215
- attention_mask = attention_mask[:, :x.size(1)]
216
- out = self.conformer(x, attention_mask=attention_mask, output_hidden_states=True)
217
- hidden_emb = out["hidden_states"]
218
- last_emb = out["last_hidden_state"]
219
- logits = self.linear(last_emb)
220
- interval = self.codebook_size
221
- logits = {
222
- key: logits[:, :, i * interval : (i + 1) * interval]
223
- for i, key in enumerate(self.features)
224
- }
225
- return logits, hidden_emb, mask_indices
226
-
227
- @torch.no_grad()
228
- def normalize(self, x):
229
- """normalize the input audio to have zero mean unit variance"""
230
- for key in x.keys():
231
- x[key] = (x[key] - self.stat["%s_mean" % key]) / self.stat["%s_std" % key]
232
- return x
233
-
234
- @torch.no_grad()
235
- def rearrange(self, x):
236
- """rearrange the batch to flatten every 4 steps"""
237
- for key in x.keys():
238
- if key == "chromagram":
239
- x[key] = rearrange(x[key], "b f t -> b t f")
240
- else:
241
- x[key] = rearrange(x[key], "b f (t s) -> b t (s f)", s=self.n_fold)
242
- return x
243
-
244
- def get_rvq_codes(self, inp, raw_wav):
245
- if self.use_rvq_target:
246
- quantized_prompt_embeds, codes, _, commitment_loss, codebook_loss, rvq_usage = self.rvq(inp)
247
- return codes
248
- if self.use_vq_target:
249
- quantized_prompt_embeds, commitment_loss, codebook_loss, codes, _ = self.rvq(inp)
250
- return codes.unsqueeze(1)
251
- if self.use_encodec_target:
252
- encoded_frames = self.rvq.encode(raw_wav.unsqueeze(1)) #list, B,[ 8,T ]
253
- codes = torch.cat([encoded[0].detach() for encoded in encoded_frames], dim=-1)
254
- if self.label_rate == 25:
255
- codes = codes[:, :, ::3]
256
- return codes
257
-
258
- @torch.no_grad()
259
- def tokenize(self, x, raw_wav):
260
- out = {}
261
- for key in x.keys():
262
- if self.use_rvq_like_target:
263
- self.rvq.eval()
264
- inp = x[key].permute((0, 2, 1))
265
- codes = self.get_rvq_codes(inp, raw_wav)
266
- out[key] = torch.cat([codes[:, idx, ...] for idx in range(int(self.codebook_size//1024))], dim=-1)
267
- else:
268
- layer = getattr(self, "quantizer_%s" % key)
269
- out[key] = layer(x[key])
270
- return out
271
-
272
- def get_targets(self, x, label=None):
273
- if self.use_encodec_target:
274
- raw_x = x.clone()
275
- else:
276
- raw_x = None
277
- x = self.preprocessing(x, features=self.features)
278
- x = self.normalize(x)
279
- x = self.rearrange(x)
280
- melspec = x['melspec_2048']
281
- if label is None:
282
- # Use labels from Mel-RVQ
283
- target_tokens = self.tokenize(x, raw_x)
284
- else:
285
- # Use labels pre-extracted for iteration training
286
- target_tokens = {'melspec_2048': rearrange(label, "b n s -> b (n s)").long()}
287
- return target_tokens, melspec
288
-
289
- def get_predictions(self, x, *, mask=None, attention_mask=None, return_new_mask=False, is_features_only=False):
290
- # preprocessing
291
- x = self.preprocessing(x, features=["melspec_2048"])
292
- x = self.normalize(x)
293
-
294
- # encoding
295
- logits, hidden_emb, new_mask = self.encoder(x["melspec_2048"], attention_mask=attention_mask, is_features_only=is_features_only)
296
-
297
- if return_new_mask:
298
- return logits, hidden_emb, mask if new_mask is None else new_mask
299
- else:
300
- return logits, hidden_emb
301
-
302
- def get_latent(self, x, layer_ix=12):
303
- _, hidden_states = self.get_predictions(x)
304
- emb = hidden_states[layer_ix]
305
- return emb
306
-
307
- def compute_nce(self, x, pos, negs):
308
- neg_is_pos = (pos == negs).all(-1)
309
- pos = pos.unsqueeze(0)
310
- targets = torch.cat([pos, negs], dim=0)
311
-
312
- logits = torch.cosine_similarity(x.float(), targets.float(), dim=-1).type_as(x)
313
- logits /= 0.1
314
- if neg_is_pos.any():
315
- logits[1:][neg_is_pos] = float("-inf")
316
- logits = logits.transpose(0, 1)
317
- return logits
318
-
319
- def get_loss(self, logits, target_tokens, masked_indices):
320
- losses = {}
321
- accuracies = {}
322
- for key in logits.keys():
323
- if not self.use_rvq_like_target:
324
- masked_logits = logits[key][tuple(masked_indices.t())]
325
- masked_tokens = target_tokens[key][tuple(masked_indices.t())]
326
- else:
327
- Batch, SeqLen, N_Codebook_x_CodebookSize = logits[key].shape
328
- Batch, N_Codebook_x_SeqLen = target_tokens[key].shape
329
- N_Codebook = int(N_Codebook_x_SeqLen // SeqLen)
330
- target_tokens[key] = rearrange(target_tokens[key], "b (n s) -> b s n", n=N_Codebook) # Batch, SeqLen=750, N_Codebook=4
331
- masked_logits = logits[key][tuple(masked_indices.t())]
332
- masked_tokens = target_tokens[key][tuple(masked_indices.t())]
333
- masked_logits = rearrange(masked_logits, "b (n c) -> (b n) c", n=N_Codebook)
334
- masked_tokens = rearrange(masked_tokens, "b n -> (b n)", n=N_Codebook)
335
-
336
- losses[key] = self.loss(masked_logits, masked_tokens)
337
- accuracies[key] = (
338
- torch.sum(masked_logits.argmax(-1) == masked_tokens)
339
- / masked_tokens.numel()
340
- )
341
- return losses, accuracies
342
-
343
- def get_recon_loss(self, last_hidden_emb, melspec, masked_indices):
344
- pred_melspec = self.recon_proj(last_hidden_emb[tuple(masked_indices.t())])
345
- target_melspec = melspec[tuple(masked_indices.t())]
346
- recon_loss = self.recon_loss(pred_melspec, target_melspec)
347
- return recon_loss
348
-
349
- def forward(self, x, attention_mask=None, label=None):
350
- dtype = x.dtype
351
- # get target feature tokens
352
- target_tokens, melspec = self.get_targets(x, label=label)
353
-
354
- # masking
355
- x, masked_indices = self.masking(x, attention_mask=attention_mask)
356
-
357
- # forward
358
- logits, hidden_emb, masked_indices = self.get_predictions(x, mask=masked_indices, attention_mask=attention_mask, return_new_mask=True)
359
-
360
- # get loss
361
- losses, accuracies = self.get_loss(logits, target_tokens, masked_indices)
362
-
363
- if self.recon_loss_ratio:
364
- losses["recon_loss"] = self.get_recon_loss(hidden_emb[-1], melspec, masked_indices) * self.recon_loss_ratio
365
-
366
- return logits, hidden_emb, losses, accuracies
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq/modules/__init__.py DELETED
@@ -1,2 +0,0 @@
1
-
2
-
 
 
 
src/third_party/MuQ/src/muq/muq/modules/conv.py DELETED
@@ -1,77 +0,0 @@
1
- from torch import nn
2
- from einops import rearrange
3
-
4
-
5
- class Res2dModule(nn.Module):
6
- def __init__(self, idim, odim, stride=(2, 2)):
7
- super(Res2dModule, self).__init__()
8
- self.conv1 = nn.Conv2d(idim, odim, 3, padding=1, stride=stride)
9
- self.bn1 = nn.BatchNorm2d(odim)
10
- self.conv2 = nn.Conv2d(odim, odim, 3, padding=1)
11
- self.bn2 = nn.BatchNorm2d(odim)
12
- self.relu = nn.ReLU()
13
-
14
- # residual
15
- self.diff = False
16
- if (idim != odim) or (stride[0] > 1):
17
- self.conv3 = nn.Conv2d(idim, odim, 3, padding=1, stride=stride)
18
- self.bn3 = nn.BatchNorm2d(odim)
19
- self.diff = True
20
-
21
- def forward(self, x):
22
- out = self.bn2(self.conv2(self.relu(self.bn1(self.conv1(x)))))
23
- if self.diff:
24
- x = self.bn3(self.conv3(x))
25
- out = x + out
26
- out = self.relu(out)
27
- return out
28
-
29
-
30
- class Conv2dSubsampling(nn.Module):
31
- """Convolutional 2D subsampling (to 1/4 length).
32
-
33
- Args:
34
- idim (int): Input dimension.
35
- hdim (int): Hidden dimension.
36
- odim (int): Output dimension.
37
- strides (list): Sizes of strides.
38
- n_bands (int): Number of frequency bands.
39
- """
40
-
41
- def __init__(self, idim, hdim, odim, strides=[2, 2], n_bands=64):
42
- """Construct an Conv2dSubsampling object."""
43
- super(Conv2dSubsampling, self).__init__()
44
-
45
- self.conv = nn.Sequential(
46
- Res2dModule(idim, hdim, (2, strides[0])),
47
- Res2dModule(hdim, hdim, (2, strides[1])),
48
- )
49
- self.linear = nn.Linear(hdim * n_bands // 2 // 2, odim)
50
-
51
- def forward(self, x):
52
- """Subsample x.
53
-
54
- Args:
55
- x (torch.Tensor): Input tensor (#batch, idim, time).
56
-
57
- Returns:
58
- torch.Tensor: Subsampled tensor (#batch, time', odim),
59
- where time' = time // 4.
60
- """
61
-
62
- if x.dim() == 3:
63
- x = x.unsqueeze(1) # (b, c, f, t)
64
- x = self.conv(x)
65
- x = rearrange(x, "b c f t -> b t (c f)")
66
- x = self.linear(x)
67
- return x
68
-
69
- if __name__ == '__main__':
70
- import torch
71
- conv_dim, encoder_dim = 512, 1024
72
- conv = Conv2dSubsampling(
73
- 1, conv_dim, encoder_dim, strides=[2, 1], n_bands=128
74
- )
75
- inp = torch.randn((1, 128, 3000))
76
- out = conv(inp)
77
- print(out.shape)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq/modules/features.py DELETED
@@ -1,37 +0,0 @@
1
- import torchaudio
2
- from torch import nn
3
- import torch
4
-
5
-
6
- class MelSTFT:
7
- def __init__(
8
- self,
9
- sample_rate=24000,
10
- n_fft=2048,
11
- hop_length=240,
12
- n_mels=128,
13
- is_db=False,
14
- ):
15
- super(MelSTFT, self).__init__()
16
-
17
- # spectrogram
18
- self.mel_stft = torchaudio.transforms.MelSpectrogram(
19
- sample_rate=sample_rate, n_fft=n_fft, hop_length=hop_length, n_mels=n_mels
20
- )
21
-
22
- # amplitude to decibel
23
- self.is_db = is_db
24
- if is_db:
25
- self.amplitude_to_db = torchaudio.transforms.AmplitudeToDB()
26
-
27
- def __call__(self, waveform):
28
- if self.is_db:
29
- return self.amplitude_to_db(self.mel_stft(waveform))
30
- else:
31
- return self.mel_stft(waveform)
32
-
33
- def to(self, device):
34
- self.mel_stft = self.mel_stft.to(device)
35
- if self.is_db:
36
- self.amplitude_to_db = self.amplitude_to_db.to(device)
37
- return self
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq/modules/flash_conformer.py DELETED
@@ -1,2114 +0,0 @@
1
- # coding=utf-8
2
- # Copyright 2022 The Fairseq Authors and the HuggingFace Inc. team. All rights reserved.
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
- """ PyTorch Wav2Vec2-Conformer model."""
16
-
17
- import math
18
- from dataclasses import dataclass
19
- from typing import Optional, Tuple, Union
20
-
21
- import numpy as np
22
- import torch
23
- import torch.utils.checkpoint
24
- from torch import nn
25
- from torch.nn import CrossEntropyLoss
26
- from torch.nn import functional as F
27
-
28
- from transformers.activations import ACT2FN
29
- from transformers.deepspeed import is_deepspeed_zero3_enabled
30
- from transformers.modeling_outputs import (
31
- BaseModelOutput,
32
- CausalLMOutput,
33
- SequenceClassifierOutput,
34
- TokenClassifierOutput,
35
- Wav2Vec2BaseModelOutput,
36
- XVectorOutput,
37
- )
38
- from transformers.modeling_utils import PreTrainedModel
39
- from transformers.utils import (
40
- ModelOutput,
41
- add_code_sample_docstrings,
42
- add_start_docstrings,
43
- add_start_docstrings_to_model_forward,
44
- logging,
45
- replace_return_docstrings,
46
- )
47
- from transformers.models.wav2vec2_conformer.configuration_wav2vec2_conformer import Wav2Vec2ConformerConfig
48
-
49
-
50
- logger = logging.get_logger(__name__)
51
-
52
-
53
- _HIDDEN_STATES_START_POSITION = 2
54
-
55
- # General docstring
56
- _CONFIG_FOR_DOC = "Wav2Vec2ConformerConfig"
57
-
58
- # Base docstring
59
- _CHECKPOINT_FOR_DOC = "facebook/wav2vec2-conformer-rope-large-960h-ft"
60
- _EXPECTED_OUTPUT_SHAPE = [1, 292, 1024]
61
-
62
- # CTC docstring
63
- _CTC_EXPECTED_OUTPUT = "'MISTER QUILTER IS THE APOSTLE OF THE MIDDLE CLASSES AND WE ARE GLAD TO WELCOME HIS GOSPEL'"
64
- _CTC_EXPECTED_LOSS = 64.21
65
-
66
-
67
- WAV2VEC2_CONFORMER_PRETRAINED_MODEL_ARCHIVE_LIST = [
68
- "facebook/wav2vec2-conformer-rel-pos-large",
69
- # See all Wav2Vec2Conformer models at https://huggingface.co/models?filter=wav2vec2-conformer
70
- ]
71
-
72
-
73
- @dataclass
74
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForPreTrainingOutput with Wav2Vec2->Wav2Vec2Conformer
75
- class Wav2Vec2ConformerForPreTrainingOutput(ModelOutput):
76
- """
77
- Output type of [`Wav2Vec2ConformerForPreTraining`], with potential hidden states and attentions.
78
-
79
- Args:
80
- loss (*optional*, returned when `sample_negative_indices` are passed, `torch.FloatTensor` of shape `(1,)`):
81
- Total loss as the sum of the contrastive loss (L_m) and the diversity loss (L_d) as stated in the [official
82
- paper](https://arxiv.org/pdf/2006.11477.pdf) . (classification) loss.
83
- projected_states (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.proj_codevector_dim)`):
84
- Hidden-states of the model projected to *config.proj_codevector_dim* that can be used to predict the masked
85
- projected quantized states.
86
- projected_quantized_states (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.proj_codevector_dim)`):
87
- Quantized extracted feature vectors projected to *config.proj_codevector_dim* representing the positive
88
- target vectors for contrastive loss.
89
- hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
90
- Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer) of
91
- shape `(batch_size, sequence_length, hidden_size)`.
92
-
93
- Hidden-states of the model at the output of each layer plus the initial embedding outputs.
94
- attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
95
- Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
96
- sequence_length)`.
97
-
98
- Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
99
- heads.
100
- contrastive_loss (*optional*, returned when `sample_negative_indices` are passed, `torch.FloatTensor` of shape `(1,)`):
101
- The contrastive loss (L_m) as stated in the [official paper](https://arxiv.org/pdf/2006.11477.pdf) .
102
- diversity_loss (*optional*, returned when `sample_negative_indices` are passed, `torch.FloatTensor` of shape `(1,)`):
103
- The diversity loss (L_d) as stated in the [official paper](https://arxiv.org/pdf/2006.11477.pdf) .
104
- """
105
-
106
- loss: Optional[torch.FloatTensor] = None
107
- projected_states: torch.FloatTensor = None
108
- projected_quantized_states: torch.FloatTensor = None
109
- codevector_perplexity: torch.FloatTensor = None
110
- hidden_states: Optional[Tuple[torch.FloatTensor]] = None
111
- attentions: Optional[Tuple[torch.FloatTensor]] = None
112
- contrastive_loss: Optional[torch.FloatTensor] = None
113
- diversity_loss: Optional[torch.FloatTensor] = None
114
-
115
-
116
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2._compute_mask_indices
117
- def _compute_mask_indices(
118
- shape: Tuple[int, int],
119
- mask_prob: float,
120
- mask_length: int,
121
- attention_mask: Optional[torch.LongTensor] = None,
122
- min_masks: int = 0,
123
- ) -> np.ndarray:
124
- """
125
- Computes random mask spans for a given shape. Used to implement [SpecAugment: A Simple Data Augmentation Method for
126
- ASR](https://arxiv.org/abs/1904.08779). Note that this method is not optimized to run on TPU and should be run on
127
- CPU as part of the preprocessing during training.
128
-
129
- Args:
130
- shape: The shape for which to compute masks. This should be of a tuple of size 2 where
131
- the first element is the batch size and the second element is the length of the axis to span.
132
- mask_prob: The percentage of the whole axis (between 0 and 1) which will be masked. The number of
133
- independently generated mask spans of length `mask_length` is computed by
134
- `mask_prob*shape[1]/mask_length`. Note that due to overlaps, `mask_prob` is an upper bound and the
135
- actual percentage will be smaller.
136
- mask_length: size of the mask
137
- min_masks: minimum number of masked spans
138
- attention_mask: A (right-padded) attention mask which independently shortens the feature axis of
139
- each batch dimension.
140
- """
141
- batch_size, sequence_length = shape
142
-
143
- if mask_length < 1:
144
- raise ValueError("`mask_length` has to be bigger than 0.")
145
-
146
- if mask_length > sequence_length:
147
- raise ValueError(
148
- f"`mask_length` has to be smaller than `sequence_length`, but got `mask_length`: {mask_length}"
149
- f" and `sequence_length`: {sequence_length}`"
150
- )
151
-
152
- # epsilon is used for probabilistic rounding
153
- epsilon = np.random.rand(1).item()
154
-
155
- def compute_num_masked_span(input_length):
156
- """Given input length, compute how many spans should be masked"""
157
- num_masked_span = int(mask_prob * input_length / mask_length + epsilon)
158
- num_masked_span = max(num_masked_span, min_masks)
159
-
160
- # make sure num masked span <= sequence_length
161
- if num_masked_span * mask_length > sequence_length:
162
- num_masked_span = sequence_length // mask_length
163
-
164
- # make sure num_masked span is also <= input_length - (mask_length - 1)
165
- if input_length - (mask_length - 1) < num_masked_span:
166
- num_masked_span = max(input_length - (mask_length - 1), 0)
167
-
168
- return num_masked_span
169
-
170
- # compute number of masked spans in batch
171
- input_lengths = (
172
- attention_mask.sum(-1).detach().tolist()
173
- if attention_mask is not None
174
- else [sequence_length for _ in range(batch_size)]
175
- )
176
-
177
- # SpecAugment mask to fill
178
- spec_aug_mask = np.zeros((batch_size, sequence_length), dtype=bool)
179
- spec_aug_mask_idxs = []
180
-
181
- max_num_masked_span = compute_num_masked_span(sequence_length)
182
-
183
- if max_num_masked_span == 0:
184
- return spec_aug_mask
185
-
186
- for input_length in input_lengths:
187
- # compute num of masked spans for this input
188
- num_masked_span = compute_num_masked_span(input_length)
189
-
190
- # get random indices to mask
191
- spec_aug_mask_idx = np.random.choice(
192
- np.arange(input_length - (mask_length - 1)), num_masked_span, replace=False
193
- )
194
-
195
- # pick first sampled index that will serve as a dummy index to pad vector
196
- # to ensure same dimension for all batches due to probabilistic rounding
197
- # Picking first sample just pads those vectors twice.
198
- if len(spec_aug_mask_idx) == 0:
199
- # this case can only happen if `input_length` is strictly smaller then
200
- # `sequence_length` in which case the last token has to be a padding
201
- # token which we can use as a dummy mask id
202
- dummy_mask_idx = sequence_length - 1
203
- else:
204
- dummy_mask_idx = spec_aug_mask_idx[0]
205
-
206
- spec_aug_mask_idx = np.concatenate(
207
- [spec_aug_mask_idx, np.ones(max_num_masked_span - num_masked_span, dtype=np.int32) * dummy_mask_idx]
208
- )
209
- spec_aug_mask_idxs.append(spec_aug_mask_idx)
210
-
211
- spec_aug_mask_idxs = np.array(spec_aug_mask_idxs)
212
-
213
- # expand masked indices to masked spans
214
- spec_aug_mask_idxs = np.broadcast_to(
215
- spec_aug_mask_idxs[:, :, None], (batch_size, max_num_masked_span, mask_length)
216
- )
217
- spec_aug_mask_idxs = spec_aug_mask_idxs.reshape(batch_size, max_num_masked_span * mask_length)
218
-
219
- # add offset to the starting indexes so that indexes now create a span
220
- offsets = np.arange(mask_length)[None, None, :]
221
- offsets = np.broadcast_to(offsets, (batch_size, max_num_masked_span, mask_length)).reshape(
222
- batch_size, max_num_masked_span * mask_length
223
- )
224
- spec_aug_mask_idxs = spec_aug_mask_idxs + offsets
225
-
226
- # ensure that we cannot have indices larger than sequence_length
227
- if spec_aug_mask_idxs.max() > sequence_length - 1:
228
- spec_aug_mask_idxs[spec_aug_mask_idxs > sequence_length - 1] = sequence_length - 1
229
-
230
- # scatter indices to mask
231
- np.put_along_axis(spec_aug_mask, spec_aug_mask_idxs, 1, -1)
232
-
233
- return spec_aug_mask
234
-
235
-
236
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2._sample_negative_indices
237
- def _sample_negative_indices(
238
- features_shape: Tuple, num_negatives: int, mask_time_indices: Optional[np.ndarray] = None
239
- ):
240
- """
241
- Sample `num_negatives` vectors from feature vectors.
242
- """
243
- batch_size, sequence_length = features_shape
244
-
245
- # generate indices of the positive vectors themselves, repeat them `num_negatives` times
246
- sequence_length_range = np.arange(sequence_length)
247
-
248
- # get `num_negatives` random vector indices from the same utterance
249
- sampled_negative_indices = np.zeros(shape=(batch_size, sequence_length, num_negatives), dtype=np.int32)
250
-
251
- mask_time_indices = (
252
- mask_time_indices.astype(bool) if mask_time_indices is not None else np.ones(features_shape, dtype=bool)
253
- )
254
-
255
- for batch_idx in range(batch_size):
256
- high = mask_time_indices[batch_idx].sum() - 1
257
- mapped_masked_indices = sequence_length_range[mask_time_indices[batch_idx]]
258
-
259
- feature_indices = np.broadcast_to(np.arange(high + 1)[:, None], (high + 1, num_negatives))
260
- sampled_indices = np.random.randint(0, high, size=(high + 1, num_negatives))
261
- # avoid sampling the same positive vector, but keep the distribution uniform
262
- sampled_indices[sampled_indices >= feature_indices] += 1
263
-
264
- # remap to actual indices
265
- sampled_negative_indices[batch_idx][mask_time_indices[batch_idx]] = mapped_masked_indices[sampled_indices]
266
-
267
- # correct for batch size
268
- sampled_negative_indices[batch_idx] += batch_idx * sequence_length
269
-
270
- return sampled_negative_indices
271
-
272
-
273
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2NoLayerNormConvLayer with Wav2Vec2->Wav2Vec2Conformer
274
- class Wav2Vec2ConformerNoLayerNormConvLayer(nn.Module):
275
- def __init__(self, config, layer_id=0):
276
- super().__init__()
277
- self.in_conv_dim = config.conv_dim[layer_id - 1] if layer_id > 0 else 1
278
- self.out_conv_dim = config.conv_dim[layer_id]
279
-
280
- self.conv = nn.Conv1d(
281
- self.in_conv_dim,
282
- self.out_conv_dim,
283
- kernel_size=config.conv_kernel[layer_id],
284
- stride=config.conv_stride[layer_id],
285
- bias=config.conv_bias,
286
- )
287
- self.activation = ACT2FN[config.feat_extract_activation]
288
-
289
- def forward(self, hidden_states):
290
- hidden_states = self.conv(hidden_states)
291
- hidden_states = self.activation(hidden_states)
292
- return hidden_states
293
-
294
-
295
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2LayerNormConvLayer with Wav2Vec2->Wav2Vec2Conformer
296
- class Wav2Vec2ConformerLayerNormConvLayer(nn.Module):
297
- def __init__(self, config, layer_id=0):
298
- super().__init__()
299
- self.in_conv_dim = config.conv_dim[layer_id - 1] if layer_id > 0 else 1
300
- self.out_conv_dim = config.conv_dim[layer_id]
301
-
302
- self.conv = nn.Conv1d(
303
- self.in_conv_dim,
304
- self.out_conv_dim,
305
- kernel_size=config.conv_kernel[layer_id],
306
- stride=config.conv_stride[layer_id],
307
- bias=config.conv_bias,
308
- )
309
- self.layer_norm = nn.LayerNorm(self.out_conv_dim, elementwise_affine=True)
310
- self.activation = ACT2FN[config.feat_extract_activation]
311
-
312
- def forward(self, hidden_states):
313
- hidden_states = self.conv(hidden_states)
314
-
315
- hidden_states = hidden_states.transpose(-2, -1)
316
- hidden_states = self.layer_norm(hidden_states)
317
- hidden_states = hidden_states.transpose(-2, -1)
318
-
319
- hidden_states = self.activation(hidden_states)
320
- return hidden_states
321
-
322
-
323
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2GroupNormConvLayer with Wav2Vec2->Wav2Vec2Conformer
324
- class Wav2Vec2ConformerGroupNormConvLayer(nn.Module):
325
- def __init__(self, config, layer_id=0):
326
- super().__init__()
327
- self.in_conv_dim = config.conv_dim[layer_id - 1] if layer_id > 0 else 1
328
- self.out_conv_dim = config.conv_dim[layer_id]
329
-
330
- self.conv = nn.Conv1d(
331
- self.in_conv_dim,
332
- self.out_conv_dim,
333
- kernel_size=config.conv_kernel[layer_id],
334
- stride=config.conv_stride[layer_id],
335
- bias=config.conv_bias,
336
- )
337
- self.activation = ACT2FN[config.feat_extract_activation]
338
-
339
- self.layer_norm = nn.GroupNorm(num_groups=self.out_conv_dim, num_channels=self.out_conv_dim, affine=True)
340
-
341
- def forward(self, hidden_states):
342
- hidden_states = self.conv(hidden_states)
343
- hidden_states = self.layer_norm(hidden_states)
344
- hidden_states = self.activation(hidden_states)
345
- return hidden_states
346
-
347
-
348
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2PositionalConvEmbedding with Wav2Vec2->Wav2Vec2Conformer
349
- class Wav2Vec2ConformerPositionalConvEmbedding(nn.Module):
350
- def __init__(self, config):
351
- super().__init__()
352
- self.conv = nn.Conv1d(
353
- config.hidden_size,
354
- config.hidden_size,
355
- kernel_size=config.num_conv_pos_embeddings,
356
- padding=config.num_conv_pos_embeddings // 2,
357
- groups=config.num_conv_pos_embedding_groups,
358
- )
359
-
360
- if is_deepspeed_zero3_enabled():
361
- import deepspeed
362
-
363
- with deepspeed.zero.GatheredParameters(self.conv.weight, modifier_rank=0):
364
- self.conv = nn.utils.weight_norm(self.conv, name="weight", dim=2)
365
- deepspeed.zero.register_external_parameter(self, self.conv.weight_v)
366
- deepspeed.zero.register_external_parameter(self, self.conv.weight_g)
367
- else:
368
- self.conv = nn.utils.weight_norm(self.conv, name="weight", dim=2)
369
-
370
- self.padding = Wav2Vec2ConformerSamePadLayer(config.num_conv_pos_embeddings)
371
- self.activation = ACT2FN[config.feat_extract_activation]
372
-
373
- def forward(self, hidden_states):
374
- hidden_states = hidden_states.transpose(1, 2)
375
-
376
- hidden_states = self.conv(hidden_states)
377
- hidden_states = self.padding(hidden_states)
378
- hidden_states = self.activation(hidden_states)
379
-
380
- hidden_states = hidden_states.transpose(1, 2)
381
- return hidden_states
382
-
383
-
384
- class Wav2Vec2ConformerRotaryPositionalEmbedding(nn.Module):
385
- """Rotary positional embedding
386
- Reference : https://blog.eleuther.ai/rotary-embeddings/ Paper: https://arxiv.org/pdf/2104.09864.pdf
387
- """
388
-
389
- def __init__(self, config):
390
- super().__init__()
391
- dim = config.hidden_size // config.num_attention_heads
392
- base = config.rotary_embedding_base
393
-
394
- inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
395
- self.register_buffer("inv_freq", inv_freq)
396
- self.cached_sequence_length = None
397
- self.cached_rotary_positional_embedding = None
398
-
399
- def forward(self, hidden_states):
400
- sequence_length = hidden_states.shape[1]
401
-
402
- if sequence_length == self.cached_sequence_length and self.cached_rotary_positional_embedding is not None:
403
- return self.cached_rotary_positional_embedding
404
-
405
- self.cached_sequence_length = sequence_length
406
- time_stamps = torch.arange(sequence_length).type_as(self.inv_freq)
407
- freqs = torch.einsum("i,j->ij", time_stamps, self.inv_freq)
408
- embeddings = torch.cat((freqs, freqs), dim=-1)
409
-
410
- cos_embeddings = embeddings.cos()[:, None, None, :]
411
- sin_embeddings = embeddings.sin()[:, None, None, :]
412
- self.cached_rotary_positional_embedding = torch.stack([cos_embeddings, sin_embeddings])
413
- return self.cached_rotary_positional_embedding
414
-
415
-
416
- class Wav2Vec2ConformerRelPositionalEmbedding(nn.Module):
417
- """Relative positional encoding module."""
418
-
419
- def __init__(self, config):
420
- super().__init__()
421
- self.max_len = config.max_source_positions
422
- self.d_model = config.hidden_size
423
- self.pe = None
424
- self.extend_pe(torch.tensor(0.0).expand(1, self.max_len))
425
-
426
- def extend_pe(self, x):
427
- # Reset the positional encodings
428
- if self.pe is not None:
429
- # self.pe contains both positive and negative parts
430
- # the length of self.pe is 2 * input_len - 1
431
- if self.pe.size(1) >= x.size(1) * 2 - 1:
432
- if self.pe.dtype != x.dtype or self.pe.device != x.device:
433
- self.pe = self.pe.to(dtype=x.dtype, device=x.device)
434
- return
435
- # Suppose `i` is the position of query vector and `j` is the
436
- # position of key vector. We use positive relative positions when keys
437
- # are to the left (i>j) and negative relative positions otherwise (i<j).
438
- pe_positive = torch.zeros(x.size(1), self.d_model)
439
- pe_negative = torch.zeros(x.size(1), self.d_model)
440
- position = torch.arange(0, x.size(1), dtype=torch.float32).unsqueeze(1)
441
- div_term = torch.exp(
442
- torch.arange(0, self.d_model, 2, dtype=torch.float32) * -(math.log(10000.0) / self.d_model)
443
- )
444
- pe_positive[:, 0::2] = torch.sin(position * div_term)
445
- pe_positive[:, 1::2] = torch.cos(position * div_term)
446
- pe_negative[:, 0::2] = torch.sin(-1 * position * div_term)
447
- pe_negative[:, 1::2] = torch.cos(-1 * position * div_term)
448
-
449
- # Reverse the order of positive indices and concat both positive and
450
- # negative indices. This is used to support the shifting trick
451
- # as in https://arxiv.org/abs/1901.02860
452
- pe_positive = torch.flip(pe_positive, [0]).unsqueeze(0)
453
- pe_negative = pe_negative[1:].unsqueeze(0)
454
- pe = torch.cat([pe_positive, pe_negative], dim=1)
455
- self.pe = pe.to(device=x.device, dtype=x.dtype)
456
-
457
- def forward(self, hidden_states: torch.Tensor):
458
- self.extend_pe(hidden_states)
459
- start_idx = self.pe.size(1) // 2 - hidden_states.size(1) + 1
460
- end_idx = self.pe.size(1) // 2 + hidden_states.size(1)
461
- relative_position_embeddings = self.pe[:, start_idx:end_idx]
462
-
463
- return relative_position_embeddings
464
-
465
-
466
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2SamePadLayer with Wav2Vec2->Wav2Vec2Conformer
467
- class Wav2Vec2ConformerSamePadLayer(nn.Module):
468
- def __init__(self, num_conv_pos_embeddings):
469
- super().__init__()
470
- self.num_pad_remove = 1 if num_conv_pos_embeddings % 2 == 0 else 0
471
-
472
- def forward(self, hidden_states):
473
- if self.num_pad_remove > 0:
474
- hidden_states = hidden_states[:, :, : -self.num_pad_remove]
475
- return hidden_states
476
-
477
-
478
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2FeatureEncoder with Wav2Vec2->Wav2Vec2Conformer
479
- class Wav2Vec2ConformerFeatureEncoder(nn.Module):
480
- """Construct the features from raw audio waveform"""
481
-
482
- def __init__(self, config):
483
- super().__init__()
484
-
485
- if config.feat_extract_norm == "group":
486
- conv_layers = [Wav2Vec2ConformerGroupNormConvLayer(config, layer_id=0)] + [
487
- Wav2Vec2ConformerNoLayerNormConvLayer(config, layer_id=i + 1)
488
- for i in range(config.num_feat_extract_layers - 1)
489
- ]
490
- elif config.feat_extract_norm == "layer":
491
- conv_layers = [
492
- Wav2Vec2ConformerLayerNormConvLayer(config, layer_id=i) for i in range(config.num_feat_extract_layers)
493
- ]
494
- else:
495
- raise ValueError(
496
- f"`config.feat_extract_norm` is {config.feat_extract_norm}, but has to be one of ['group', 'layer']"
497
- )
498
- self.conv_layers = nn.ModuleList(conv_layers)
499
- self.gradient_checkpointing = False
500
- self._requires_grad = True
501
-
502
- def _freeze_parameters(self):
503
- for param in self.parameters():
504
- param.requires_grad = False
505
- self._requires_grad = False
506
-
507
- def forward(self, input_values):
508
- hidden_states = input_values[:, None]
509
-
510
- # make sure hidden_states require grad for gradient_checkpointing
511
- if self._requires_grad and self.training:
512
- hidden_states.requires_grad = True
513
-
514
- for conv_layer in self.conv_layers:
515
- if self._requires_grad and self.gradient_checkpointing and self.training:
516
-
517
- def create_custom_forward(module):
518
- def custom_forward(*inputs):
519
- return module(*inputs)
520
-
521
- return custom_forward
522
-
523
- hidden_states = torch.utils.checkpoint.checkpoint(
524
- create_custom_forward(conv_layer),
525
- hidden_states,
526
- )
527
- else:
528
- hidden_states = conv_layer(hidden_states)
529
-
530
- return hidden_states
531
-
532
-
533
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2FeatureProjection with Wav2Vec2->Wav2Vec2Conformer
534
- class Wav2Vec2ConformerFeatureProjection(nn.Module):
535
- def __init__(self, config):
536
- super().__init__()
537
- self.layer_norm = nn.LayerNorm(config.conv_dim[-1], eps=config.layer_norm_eps)
538
- self.projection = nn.Linear(config.conv_dim[-1], config.hidden_size)
539
- self.dropout = nn.Dropout(config.feat_proj_dropout)
540
-
541
- def forward(self, hidden_states):
542
- # non-projected hidden states are needed for quantization
543
- norm_hidden_states = self.layer_norm(hidden_states)
544
- hidden_states = self.projection(norm_hidden_states)
545
- hidden_states = self.dropout(hidden_states)
546
- return hidden_states, norm_hidden_states
547
-
548
-
549
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2FeedForward with Wav2Vec2->Wav2Vec2Conformer
550
- class Wav2Vec2ConformerFeedForward(nn.Module):
551
- def __init__(self, config):
552
- super().__init__()
553
- self.intermediate_dropout = nn.Dropout(config.activation_dropout)
554
-
555
- self.intermediate_dense = nn.Linear(config.hidden_size, config.intermediate_size)
556
- if isinstance(config.hidden_act, str):
557
- self.intermediate_act_fn = ACT2FN[config.hidden_act]
558
- else:
559
- self.intermediate_act_fn = config.hidden_act
560
-
561
- self.output_dense = nn.Linear(config.intermediate_size, config.hidden_size)
562
- self.output_dropout = nn.Dropout(config.hidden_dropout)
563
-
564
- def forward(self, hidden_states):
565
- hidden_states = self.intermediate_dense(hidden_states)
566
- hidden_states = self.intermediate_act_fn(hidden_states)
567
- hidden_states = self.intermediate_dropout(hidden_states)
568
-
569
- hidden_states = self.output_dense(hidden_states)
570
- hidden_states = self.output_dropout(hidden_states)
571
- return hidden_states
572
-
573
-
574
- class Wav2Vec2ConformerConvolutionModule(nn.Module):
575
- """Convolution block used in the conformer block"""
576
-
577
- def __init__(self, config):
578
- super().__init__()
579
- if (config.conv_depthwise_kernel_size - 1) % 2 == 1:
580
- raise ValueError("`config.conv_depthwise_kernel_size` should be a odd number for 'SAME' padding")
581
- self.layer_norm = nn.LayerNorm(config.hidden_size)
582
- self.pointwise_conv1 = torch.nn.Conv1d(
583
- config.hidden_size,
584
- 2 * config.hidden_size,
585
- kernel_size=1,
586
- stride=1,
587
- padding=0,
588
- bias=False,
589
- )
590
- self.glu = torch.nn.GLU(dim=1)
591
- self.depthwise_conv = torch.nn.Conv1d(
592
- config.hidden_size,
593
- config.hidden_size,
594
- config.conv_depthwise_kernel_size,
595
- stride=1,
596
- padding=(config.conv_depthwise_kernel_size - 1) // 2,
597
- groups=config.hidden_size,
598
- bias=False,
599
- )
600
- self.batch_norm = torch.nn.BatchNorm1d(config.hidden_size)
601
- self.activation = ACT2FN[config.hidden_act]
602
- self.pointwise_conv2 = torch.nn.Conv1d(
603
- config.hidden_size,
604
- config.hidden_size,
605
- kernel_size=1,
606
- stride=1,
607
- padding=0,
608
- bias=False,
609
- )
610
- self.dropout = torch.nn.Dropout(config.conformer_conv_dropout)
611
-
612
- def forward(self, hidden_states):
613
- hidden_states = self.layer_norm(hidden_states)
614
- # exchange the temporal dimension and the feature dimension
615
- hidden_states = hidden_states.transpose(1, 2)
616
-
617
- # GLU mechanism
618
- # => (batch, 2*channel, dim)
619
- hidden_states = self.pointwise_conv1(hidden_states)
620
- # => (batch, channel, dim)
621
- hidden_states = self.glu(hidden_states)
622
-
623
- # 1D Depthwise Conv
624
- hidden_states = self.depthwise_conv(hidden_states)
625
- hidden_states = self.batch_norm(hidden_states)
626
- hidden_states = self.activation(hidden_states)
627
-
628
- hidden_states = self.pointwise_conv2(hidden_states)
629
- hidden_states = self.dropout(hidden_states)
630
- hidden_states = hidden_states.transpose(1, 2)
631
- return hidden_states
632
-
633
-
634
- class Wav2Vec2ConformerSelfAttention(nn.Module):
635
- """Construct an Wav2Vec2ConformerSelfAttention object.
636
- Can be enhanced with rotary or relative position embeddings.
637
- """
638
-
639
- def __init__(self, config):
640
- super().__init__()
641
-
642
- self.head_size = config.hidden_size // config.num_attention_heads
643
- self.num_heads = config.num_attention_heads
644
- self.position_embeddings_type = config.position_embeddings_type
645
-
646
- self.linear_q = nn.Linear(config.hidden_size, config.hidden_size)
647
- self.linear_k = nn.Linear(config.hidden_size, config.hidden_size)
648
- self.linear_v = nn.Linear(config.hidden_size, config.hidden_size)
649
- self.linear_out = nn.Linear(config.hidden_size, config.hidden_size)
650
-
651
- self.dropout = nn.Dropout(p=config.attention_dropout)
652
- self.dropout_p = config.attention_dropout
653
-
654
- self.is_causal = config.is_causal
655
-
656
- if self.position_embeddings_type == "relative":
657
- # linear transformation for positional encoding
658
- self.linear_pos = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
659
- # these two learnable bias are used in matrix c and matrix d
660
- # as described in https://arxiv.org/abs/1901.02860 Section 3.3
661
- self.pos_bias_u = nn.Parameter(torch.zeros(self.num_heads, self.head_size))
662
- self.pos_bias_v = nn.Parameter(torch.zeros(self.num_heads, self.head_size))
663
-
664
- def forward(
665
- self,
666
- hidden_states: torch.Tensor,
667
- attention_mask: Optional[torch.Tensor] = None,
668
- relative_position_embeddings: Optional[torch.Tensor] = None,
669
- output_attentions: bool = False,
670
- ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
671
- # self-attention mechanism
672
- batch_size, sequence_length, hidden_size = hidden_states.size()
673
-
674
- # make sure query/key states can be != value states
675
- query_key_states = hidden_states
676
- value_states = hidden_states
677
-
678
- if self.position_embeddings_type == "rotary":
679
- if relative_position_embeddings is None:
680
- raise ValueError(
681
- "`relative_position_embeddings` has to be defined when `self.position_embeddings_type == 'rotary'"
682
- )
683
- query_key_states = self._apply_rotary_embedding(query_key_states, relative_position_embeddings)
684
-
685
- # project query_key_states and value_states
686
- query = self.linear_q(query_key_states).view(batch_size, -1, self.num_heads, self.head_size)
687
- key = self.linear_k(query_key_states).view(batch_size, -1, self.num_heads, self.head_size)
688
- value = self.linear_v(value_states).view(batch_size, -1, self.num_heads, self.head_size)
689
-
690
- # => (batch, head, time1, d_k)
691
- query = query.transpose(1, 2)
692
- key = key.transpose(1, 2)
693
- value = value.transpose(1, 2)
694
-
695
- with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False):
696
- hidden_states = F.scaled_dot_product_attention(query, key, value, attn_mask=attention_mask, dropout_p=self.dropout_p, is_causal=self.is_causal)
697
- probs = None
698
-
699
- # # apply attention_mask if necessary
700
- # if attention_mask is not None:
701
- # scores = scores + attention_mask
702
-
703
- # # => (batch, head, time1, time2)
704
- # probs = torch.softmax(scores, dim=-1)
705
- # probs = self.dropout(probs)
706
-
707
- # # => (batch, head, time1, d_k)
708
- # hidden_states = torch.matmul(probs, value)
709
-
710
- # => (batch, time1, hidden_size)
711
- hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, self.num_heads * self.head_size)
712
- hidden_states = self.linear_out(hidden_states)
713
-
714
- return hidden_states, probs
715
-
716
- def _apply_rotary_embedding(self, hidden_states, relative_position_embeddings):
717
- batch_size, sequence_length, hidden_size = hidden_states.size()
718
- hidden_states = hidden_states.view(batch_size, sequence_length, self.num_heads, self.head_size)
719
-
720
- cos = relative_position_embeddings[0, :sequence_length, ...]
721
- sin = relative_position_embeddings[1, :sequence_length, ...]
722
-
723
- # rotate hidden_states with rotary embeddings
724
- hidden_states = hidden_states.transpose(0, 1)
725
- rotated_states_begin = hidden_states[..., : self.head_size // 2]
726
- rotated_states_end = hidden_states[..., self.head_size // 2 :]
727
- rotated_states = torch.cat((-rotated_states_end, rotated_states_begin), dim=rotated_states_begin.ndim - 1)
728
- hidden_states = (hidden_states * cos) + (rotated_states * sin)
729
- hidden_states = hidden_states.transpose(0, 1)
730
-
731
- hidden_states = hidden_states.view(batch_size, sequence_length, self.num_heads * self.head_size)
732
-
733
- return hidden_states
734
-
735
- def _apply_relative_embeddings(self, query, key, relative_position_embeddings):
736
- # 1. project positional embeddings
737
- # => (batch, head, 2*time1-1, d_k)
738
- proj_relative_position_embeddings = self.linear_pos(relative_position_embeddings)
739
- proj_relative_position_embeddings = proj_relative_position_embeddings.view(
740
- relative_position_embeddings.size(0), -1, self.num_heads, self.head_size
741
- )
742
- proj_relative_position_embeddings = proj_relative_position_embeddings.transpose(1, 2)
743
- proj_relative_position_embeddings = proj_relative_position_embeddings.transpose(2, 3)
744
-
745
- # 2. Add bias to query
746
- # => (batch, head, time1, d_k)
747
- query = query.transpose(1, 2)
748
- q_with_bias_u = (query + self.pos_bias_u).transpose(1, 2)
749
- q_with_bias_v = (query + self.pos_bias_v).transpose(1, 2)
750
-
751
- # 3. attention score: first compute matrix a and matrix c
752
- # as described in https://arxiv.org/abs/1901.02860 Section 3.3
753
- # => (batch, head, time1, time2)
754
- scores_ac = torch.matmul(q_with_bias_u, key.transpose(-2, -1))
755
-
756
- # 4. then compute matrix b and matrix d
757
- # => (batch, head, time1, 2*time1-1)
758
- scores_bd = torch.matmul(q_with_bias_v, proj_relative_position_embeddings)
759
-
760
- # 5. shift matrix b and matrix d
761
- zero_pad = torch.zeros((*scores_bd.size()[:3], 1), device=scores_bd.device, dtype=scores_bd.dtype)
762
- scores_bd_padded = torch.cat([zero_pad, scores_bd], dim=-1)
763
- scores_bd_padded_shape = scores_bd.size()[:2] + (scores_bd.shape[3] + 1, scores_bd.shape[2])
764
- scores_bd_padded = scores_bd_padded.view(*scores_bd_padded_shape)
765
- scores_bd = scores_bd_padded[:, :, 1:].view_as(scores_bd)
766
- scores_bd = scores_bd[:, :, :, : scores_bd.size(-1) // 2 + 1]
767
-
768
- # 6. sum matrices
769
- # => (batch, head, time1, time2)
770
- scores = (scores_ac + scores_bd) / math.sqrt(self.head_size)
771
-
772
- return scores
773
-
774
-
775
- class Wav2Vec2ConformerEncoderLayer(nn.Module):
776
- """Conformer block based on https://arxiv.org/abs/2005.08100."""
777
-
778
- def __init__(self, config):
779
- super().__init__()
780
- embed_dim = config.hidden_size
781
- dropout = config.attention_dropout
782
-
783
- # Feed-forward 1
784
- self.ffn1_layer_norm = nn.LayerNorm(embed_dim)
785
- self.ffn1 = Wav2Vec2ConformerFeedForward(config)
786
-
787
- # Self-Attention
788
- self.self_attn_layer_norm = nn.LayerNorm(embed_dim)
789
- self.self_attn_dropout = torch.nn.Dropout(dropout)
790
- self.self_attn = Wav2Vec2ConformerSelfAttention(config)
791
-
792
- # Conformer Convolution
793
- self.conv_module = Wav2Vec2ConformerConvolutionModule(config)
794
-
795
- # Feed-forward 2
796
- self.ffn2_layer_norm = nn.LayerNorm(embed_dim)
797
- self.ffn2 = Wav2Vec2ConformerFeedForward(config)
798
- self.final_layer_norm = nn.LayerNorm(embed_dim)
799
-
800
- def forward(
801
- self,
802
- hidden_states,
803
- attention_mask: Optional[torch.Tensor] = None,
804
- relative_position_embeddings: Optional[torch.Tensor] = None,
805
- output_attentions: bool = False,
806
- ):
807
- hidden_states = hidden_states
808
-
809
- # 1. Feed-Forward 1 layer
810
- residual = hidden_states
811
- hidden_states = self.ffn1_layer_norm(hidden_states)
812
- hidden_states = self.ffn1(hidden_states)
813
- hidden_states = hidden_states * 0.5 + residual
814
- residual = hidden_states
815
-
816
- # 2. Self-Attention layer
817
- hidden_states = self.self_attn_layer_norm(hidden_states)
818
- hidden_states, attn_weigts = self.self_attn(
819
- hidden_states=hidden_states,
820
- attention_mask=attention_mask,
821
- relative_position_embeddings=relative_position_embeddings,
822
- output_attentions=output_attentions,
823
- )
824
- hidden_states = self.self_attn_dropout(hidden_states)
825
- hidden_states = hidden_states + residual
826
-
827
- # 3. Convolutional Layer
828
- residual = hidden_states
829
- hidden_states = self.conv_module(hidden_states)
830
- hidden_states = residual + hidden_states
831
-
832
- # 4. Feed-Forward 2 Layer
833
- residual = hidden_states
834
- hidden_states = self.ffn2_layer_norm(hidden_states)
835
- hidden_states = self.ffn2(hidden_states)
836
- hidden_states = hidden_states * 0.5 + residual
837
- hidden_states = self.final_layer_norm(hidden_states)
838
-
839
- return hidden_states, attn_weigts
840
-
841
-
842
- class Wav2Vec2ConformerEncoder(nn.Module):
843
- def __init__(self, config, is_causal=False):
844
- super().__init__()
845
- config.is_causal = is_causal
846
- self.config = config
847
-
848
- if config.position_embeddings_type == "relative":
849
- self.embed_positions = Wav2Vec2ConformerRelPositionalEmbedding(config)
850
- elif config.position_embeddings_type == "rotary":
851
- self.embed_positions = Wav2Vec2ConformerRotaryPositionalEmbedding(config)
852
- else:
853
- self.embed_positions = None
854
-
855
- self.pos_conv_embed = Wav2Vec2ConformerPositionalConvEmbedding(config)
856
- self.layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
857
- self.dropout = nn.Dropout(config.hidden_dropout)
858
- self.layers = nn.ModuleList([Wav2Vec2ConformerEncoderLayer(config) for _ in range(config.num_hidden_layers)])
859
- self.gradient_checkpointing = False
860
-
861
- def forward(
862
- self,
863
- hidden_states,
864
- attention_mask=None,
865
- output_attentions=False,
866
- output_hidden_states=False,
867
- return_dict=True,
868
- ):
869
- all_hidden_states = () if output_hidden_states else None
870
- all_self_attentions = () if output_attentions else None
871
-
872
- if attention_mask is not None:
873
- # make sure padded tokens output 0
874
- hidden_states[~attention_mask] = 0.0
875
-
876
- # extend attention_mask
877
- attention_mask = 1.0 - attention_mask[:, None, None, :].to(dtype=hidden_states.dtype)
878
- attention_mask = attention_mask * torch.finfo(hidden_states.dtype).min
879
- attention_mask = attention_mask.expand(
880
- attention_mask.shape[0], 1, attention_mask.shape[-1], attention_mask.shape[-1]
881
- )
882
-
883
- hidden_states = self.dropout(hidden_states)
884
-
885
- if self.embed_positions is not None:
886
- relative_position_embeddings = self.embed_positions(hidden_states)
887
- else:
888
- relative_position_embeddings = None
889
-
890
- deepspeed_zero3_is_enabled = is_deepspeed_zero3_enabled()
891
-
892
- for i, layer in enumerate(self.layers):
893
- if output_hidden_states:
894
- all_hidden_states = all_hidden_states + (hidden_states,)
895
-
896
- # add LayerDrop (see https://arxiv.org/abs/1909.11556 for description)
897
- dropout_probability = np.random.uniform(0, 1)
898
-
899
- skip_the_layer = True if self.training and (dropout_probability < self.config.layerdrop) else False
900
- if not skip_the_layer or deepspeed_zero3_is_enabled:
901
- # under deepspeed zero3 all gpus must run in sync
902
- if self.gradient_checkpointing and self.training:
903
- # create gradient checkpointing function
904
- def create_custom_forward(module):
905
- def custom_forward(*inputs):
906
- return module(*inputs, output_attentions)
907
-
908
- return custom_forward
909
-
910
- layer_outputs = torch.utils.checkpoint.checkpoint(
911
- create_custom_forward(layer),
912
- hidden_states,
913
- attention_mask,
914
- relative_position_embeddings,
915
- )
916
- else:
917
- layer_outputs = layer(
918
- hidden_states,
919
- attention_mask=attention_mask,
920
- relative_position_embeddings=relative_position_embeddings,
921
- output_attentions=output_attentions,
922
- )
923
- hidden_states = layer_outputs[0]
924
-
925
- if skip_the_layer:
926
- layer_outputs = (None, None)
927
-
928
- if output_attentions:
929
- all_self_attentions = all_self_attentions + (layer_outputs[1],)
930
-
931
- hidden_states = self.layer_norm(hidden_states)
932
- if output_hidden_states:
933
- all_hidden_states = all_hidden_states + (hidden_states,)
934
-
935
- if not return_dict:
936
- return tuple(v for v in [hidden_states, all_hidden_states, all_self_attentions] if v is not None)
937
- return BaseModelOutput(
938
- last_hidden_state=hidden_states,
939
- hidden_states=all_hidden_states,
940
- attentions=all_self_attentions,
941
- )
942
-
943
-
944
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2GumbelVectorQuantizer with Wav2Vec2->Wav2Vec2Conformer
945
- class Wav2Vec2ConformerGumbelVectorQuantizer(nn.Module):
946
- """
947
- Vector quantization using gumbel softmax. See `[CATEGORICAL REPARAMETERIZATION WITH
948
- GUMBEL-SOFTMAX](https://arxiv.org/pdf/1611.01144.pdf) for more information.
949
- """
950
-
951
- def __init__(self, config):
952
- super().__init__()
953
- self.num_groups = config.num_codevector_groups
954
- self.num_vars = config.num_codevectors_per_group
955
-
956
- if config.codevector_dim % self.num_groups != 0:
957
- raise ValueError(
958
- f"`config.codevector_dim {config.codevector_dim} must be divisible "
959
- f"by `config.num_codevector_groups` {self.num_groups} for concatenation"
960
- )
961
-
962
- # storage for codebook variables (codewords)
963
- self.codevectors = nn.Parameter(
964
- torch.FloatTensor(1, self.num_groups * self.num_vars, config.codevector_dim // self.num_groups)
965
- )
966
- self.weight_proj = nn.Linear(config.conv_dim[-1], self.num_groups * self.num_vars)
967
-
968
- # can be decayed for training
969
- self.temperature = 2
970
-
971
- @staticmethod
972
- def _compute_perplexity(probs, mask=None):
973
- if mask is not None:
974
- mask_extended = mask.flatten()[:, None, None].expand(probs.shape)
975
- probs = torch.where(mask_extended, probs, torch.zeros_like(probs))
976
- marginal_probs = probs.sum(dim=0) / mask.sum()
977
- else:
978
- marginal_probs = probs.mean(dim=0)
979
-
980
- perplexity = torch.exp(-torch.sum(marginal_probs * torch.log(marginal_probs + 1e-7), dim=-1)).sum()
981
- return perplexity
982
-
983
- def forward(self, hidden_states, mask_time_indices=None):
984
- batch_size, sequence_length, hidden_size = hidden_states.shape
985
-
986
- # project to codevector dim
987
- hidden_states = self.weight_proj(hidden_states)
988
- hidden_states = hidden_states.view(batch_size * sequence_length * self.num_groups, -1)
989
-
990
- if self.training:
991
- # sample code vector probs via gumbel in differentiateable way
992
- codevector_probs = nn.functional.gumbel_softmax(
993
- hidden_states.float(), tau=self.temperature, hard=True
994
- ).type_as(hidden_states)
995
-
996
- # compute perplexity
997
- codevector_soft_dist = torch.softmax(
998
- hidden_states.view(batch_size * sequence_length, self.num_groups, -1).float(), dim=-1
999
- )
1000
- perplexity = self._compute_perplexity(codevector_soft_dist, mask_time_indices)
1001
- else:
1002
- # take argmax in non-differentiable way
1003
- # comptute hard codevector distribution (one hot)
1004
- codevector_idx = hidden_states.argmax(dim=-1)
1005
- codevector_probs = hidden_states.new_zeros(hidden_states.shape).scatter_(
1006
- -1, codevector_idx.view(-1, 1), 1.0
1007
- )
1008
- codevector_probs = codevector_probs.view(batch_size * sequence_length, self.num_groups, -1)
1009
-
1010
- perplexity = self._compute_perplexity(codevector_probs, mask_time_indices)
1011
-
1012
- codevector_probs = codevector_probs.view(batch_size * sequence_length, -1)
1013
- # use probs to retrieve codevectors
1014
- codevectors_per_group = codevector_probs.unsqueeze(-1) * self.codevectors
1015
- codevectors = codevectors_per_group.view(batch_size * sequence_length, self.num_groups, self.num_vars, -1)
1016
- codevectors = codevectors.sum(-2).view(batch_size, sequence_length, -1)
1017
-
1018
- return codevectors, perplexity
1019
-
1020
-
1021
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2Adapter with Wav2Vec2->Wav2Vec2Conformer
1022
- class Wav2Vec2ConformerAdapter(nn.Module):
1023
- def __init__(self, config):
1024
- super().__init__()
1025
-
1026
- # feature dim might need to be down-projected
1027
- if config.output_hidden_size != config.hidden_size:
1028
- self.proj = nn.Linear(config.hidden_size, config.output_hidden_size)
1029
- self.proj_layer_norm = nn.LayerNorm(config.output_hidden_size)
1030
- else:
1031
- self.proj = self.proj_layer_norm = None
1032
-
1033
- self.layers = nn.ModuleList(Wav2Vec2ConformerAdapterLayer(config) for _ in range(config.num_adapter_layers))
1034
- self.layerdrop = config.layerdrop
1035
-
1036
- def forward(self, hidden_states):
1037
- # down project hidden_states if necessary
1038
- if self.proj is not None and self.proj_layer_norm is not None:
1039
- hidden_states = self.proj(hidden_states)
1040
- hidden_states = self.proj_layer_norm(hidden_states)
1041
-
1042
- hidden_states = hidden_states.transpose(1, 2)
1043
-
1044
- for layer in self.layers:
1045
- layerdrop_prob = np.random.random()
1046
- if not self.training or (layerdrop_prob > self.layerdrop):
1047
- hidden_states = layer(hidden_states)
1048
-
1049
- hidden_states = hidden_states.transpose(1, 2)
1050
- return hidden_states
1051
-
1052
-
1053
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2AdapterLayer with Wav2Vec2->Wav2Vec2Conformer
1054
- class Wav2Vec2ConformerAdapterLayer(nn.Module):
1055
- def __init__(self, config):
1056
- super().__init__()
1057
- self.conv = nn.Conv1d(
1058
- config.output_hidden_size,
1059
- 2 * config.output_hidden_size,
1060
- config.adapter_kernel_size,
1061
- stride=config.adapter_stride,
1062
- padding=1,
1063
- )
1064
-
1065
- def forward(self, hidden_states):
1066
- hidden_states = self.conv(hidden_states)
1067
- hidden_states = nn.functional.glu(hidden_states, dim=1)
1068
-
1069
- return hidden_states
1070
-
1071
-
1072
- class Wav2Vec2ConformerPreTrainedModel(PreTrainedModel):
1073
- """
1074
- An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained
1075
- models.
1076
- """
1077
-
1078
- config_class = Wav2Vec2ConformerConfig
1079
- base_model_prefix = "wav2vec2_conformer"
1080
- main_input_name = "input_values"
1081
- _keys_to_ignore_on_load_missing = [r"position_ids"]
1082
- supports_gradient_checkpointing = True
1083
-
1084
- def _init_weights(self, module):
1085
- """Initialize the weights"""
1086
- # Wav2Vec2ForPreTraining last 2 linear layers need standard Linear init.
1087
- if isinstance(module, Wav2Vec2ConformerForPreTraining):
1088
- module.project_hid.reset_parameters()
1089
- module.project_q.reset_parameters()
1090
- module.project_hid._is_hf_initialized = True
1091
- module.project_q._is_hf_initialized = True
1092
- # gumbel softmax requires special init
1093
- elif isinstance(module, Wav2Vec2ConformerGumbelVectorQuantizer):
1094
- module.weight_proj.weight.data.normal_(mean=0.0, std=1)
1095
- module.weight_proj.bias.data.zero_()
1096
- nn.init.uniform_(module.codevectors)
1097
- elif isinstance(module, Wav2Vec2ConformerSelfAttention):
1098
- if hasattr(module, "pos_bias_u"):
1099
- nn.init.xavier_uniform_(module.pos_bias_u)
1100
- if hasattr(module, "pos_bias_v"):
1101
- nn.init.xavier_uniform_(module.pos_bias_v)
1102
- elif isinstance(module, Wav2Vec2ConformerPositionalConvEmbedding):
1103
- nn.init.normal_(
1104
- module.conv.weight,
1105
- mean=0,
1106
- std=2 * math.sqrt(1 / (module.conv.kernel_size[0] * module.conv.in_channels)),
1107
- )
1108
- nn.init.constant_(module.conv.bias, 0)
1109
- elif isinstance(module, Wav2Vec2ConformerFeatureProjection):
1110
- k = math.sqrt(1 / module.projection.in_features)
1111
- nn.init.uniform_(module.projection.weight, a=-k, b=k)
1112
- nn.init.uniform_(module.projection.bias, a=-k, b=k)
1113
- elif isinstance(module, nn.Linear):
1114
- module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
1115
-
1116
- if module.bias is not None:
1117
- module.bias.data.zero_()
1118
- elif isinstance(module, (nn.LayerNorm, nn.GroupNorm)):
1119
- module.bias.data.zero_()
1120
- module.weight.data.fill_(1.0)
1121
- elif isinstance(module, nn.Conv1d):
1122
- nn.init.kaiming_normal_(module.weight)
1123
-
1124
- if module.bias is not None:
1125
- k = math.sqrt(module.groups / (module.in_channels * module.kernel_size[0]))
1126
- nn.init.uniform_(module.bias, a=-k, b=k)
1127
-
1128
- def _get_feat_extract_output_lengths(
1129
- self, input_lengths: Union[torch.LongTensor, int], add_adapter: Optional[bool] = None
1130
- ):
1131
- """
1132
- Computes the output length of the convolutional layers
1133
- """
1134
-
1135
- add_adapter = self.config.add_adapter if add_adapter is None else add_adapter
1136
-
1137
- def _conv_out_length(input_length, kernel_size, stride):
1138
- # 1D convolutional layer output length formula taken
1139
- # from https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html
1140
- return torch.div(input_length - kernel_size, stride, rounding_mode="floor") + 1
1141
-
1142
- for kernel_size, stride in zip(self.config.conv_kernel, self.config.conv_stride):
1143
- input_lengths = _conv_out_length(input_lengths, kernel_size, stride)
1144
-
1145
- if add_adapter:
1146
- for _ in range(self.config.num_adapter_layers):
1147
- input_lengths = _conv_out_length(input_lengths, 1, self.config.adapter_stride)
1148
-
1149
- return input_lengths
1150
-
1151
- def _get_feature_vector_attention_mask(
1152
- self, feature_vector_length: int, attention_mask: torch.LongTensor, add_adapter=None
1153
- ):
1154
- # Effectively attention_mask.sum(-1), but not inplace to be able to run
1155
- # on inference mode.
1156
- non_padded_lengths = attention_mask.cumsum(dim=-1)[:, -1]
1157
-
1158
- output_lengths = self._get_feat_extract_output_lengths(non_padded_lengths, add_adapter=add_adapter)
1159
- output_lengths = output_lengths.to(torch.long)
1160
-
1161
- batch_size = attention_mask.shape[0]
1162
-
1163
- attention_mask = torch.zeros(
1164
- (batch_size, feature_vector_length), dtype=attention_mask.dtype, device=attention_mask.device
1165
- )
1166
- # these two operations makes sure that all values before the output lengths idxs are attended to
1167
- attention_mask[(torch.arange(attention_mask.shape[0], device=attention_mask.device), output_lengths - 1)] = 1
1168
- attention_mask = attention_mask.flip([-1]).cumsum(-1).flip([-1]).bool()
1169
- return attention_mask
1170
-
1171
- def _set_gradient_checkpointing(self, module, value=False):
1172
- if isinstance(module, (Wav2Vec2ConformerEncoder, Wav2Vec2ConformerFeatureEncoder)):
1173
- module.gradient_checkpointing = value
1174
-
1175
-
1176
- WAV2VEC2_CONFORMER_START_DOCSTRING = r"""
1177
- Wav2Vec2Conformer was proposed in [wav2vec 2.0: A Framework for Self-Supervised Learning of Speech
1178
- Representations](https://arxiv.org/abs/2006.11477) by Alexei Baevski, Henry Zhou, Abdelrahman Mohamed, Michael
1179
- Auli.
1180
-
1181
- This model inherits from [`PreTrainedModel`]. Check the superclass documentation for the generic methods the
1182
- library implements for all its model (such as downloading or saving etc.).
1183
-
1184
- This model is a PyTorch [nn.Module](https://pytorch.org/docs/stable/nn.html#nn.Module) sub-class. Use it as a
1185
- regular PyTorch Module and refer to the PyTorch documentation for all matter related to general usage and behavior.
1186
-
1187
- Parameters:
1188
- config ([`Wav2Vec2ConformerConfig`]): Model configuration class with all the parameters of the model.
1189
- Initializing with a config file does not load the weights associated with the model, only the
1190
- configuration. Check out the [`~PreTrainedModel.from_pretrained`] method to load the model weights.
1191
- """
1192
-
1193
-
1194
- WAV2VEC2_CONFORMER_INPUTS_DOCSTRING = r"""
1195
- Args:
1196
- input_values (`torch.FloatTensor` of shape `(batch_size, sequence_length)`):
1197
- Float values of input raw speech waveform. Values can be obtained by loading a `.flac` or `.wav` audio file
1198
- into an array of type `List[float]` or a `numpy.ndarray`, *e.g.* via the soundfile library (`pip install
1199
- soundfile`). To prepare the array into `input_values`, the [`AutoProcessor`] should be used for padding and
1200
- conversion into a tensor of type `torch.FloatTensor`. See [`Wav2Vec2Processor.__call__`] for details.
1201
- attention_mask (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1202
- Mask to avoid performing convolution and attention on padding token indices. Mask values selected in `[0,
1203
- 1]`:
1204
-
1205
- - 1 for tokens that are **not masked**,
1206
- - 0 for tokens that are **masked**.
1207
-
1208
- [What are attention masks?](../glossary#attention-mask)
1209
-
1210
- <Tip warning={true}>
1211
-
1212
- `attention_mask` should only be passed if the corresponding processor has `config.return_attention_mask ==
1213
- True`. For all models whose processor has `config.return_attention_mask == False`, such as
1214
- [wav2vec2-conformer-rel-pos-large](https://huggingface.co/facebook/wav2vec2-conformer-rel-pos-large),
1215
- `attention_mask` should **not** be passed to avoid degraded performance when doing batched inference. For
1216
- such models `input_values` should simply be padded with 0 and passed without `attention_mask`. Be aware
1217
- that these models also yield slightly different results depending on whether `input_values` is padded or
1218
- not.
1219
-
1220
- </Tip>
1221
-
1222
- output_attentions (`bool`, *optional*):
1223
- Whether or not to return the attentions tensors of all attention layers. See `attentions` under returned
1224
- tensors for more detail.
1225
- output_hidden_states (`bool`, *optional*):
1226
- Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors for
1227
- more detail.
1228
- return_dict (`bool`, *optional*):
1229
- Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
1230
- """
1231
-
1232
-
1233
- @add_start_docstrings(
1234
- "The bare Wav2Vec2Conformer Model transformer outputting raw hidden-states without any specific head on top.",
1235
- WAV2VEC2_CONFORMER_START_DOCSTRING,
1236
- )
1237
- class Wav2Vec2ConformerModel(Wav2Vec2ConformerPreTrainedModel):
1238
- def __init__(self, config: Wav2Vec2ConformerConfig):
1239
- super().__init__(config)
1240
- self.config = config
1241
- self.feature_extractor = Wav2Vec2ConformerFeatureEncoder(config)
1242
- self.feature_projection = Wav2Vec2ConformerFeatureProjection(config)
1243
-
1244
- # model only needs masking vector if mask prob is > 0.0
1245
- if config.mask_time_prob > 0.0 or config.mask_feature_prob > 0.0:
1246
- self.masked_spec_embed = nn.Parameter(torch.FloatTensor(config.hidden_size).uniform_())
1247
-
1248
- self.encoder = Wav2Vec2ConformerEncoder(config)
1249
-
1250
- self.adapter = Wav2Vec2ConformerAdapter(config) if config.add_adapter else None
1251
-
1252
- # Initialize weights and apply final processing
1253
- self.post_init()
1254
-
1255
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2Model.freeze_feature_encoder
1256
- def freeze_feature_encoder(self):
1257
- """
1258
- Calling this function will disable the gradient computation for the feature encoder so that its parameter will
1259
- not be updated during training.
1260
- """
1261
- self.feature_extractor._freeze_parameters()
1262
-
1263
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2Model._mask_hidden_states
1264
- def _mask_hidden_states(
1265
- self,
1266
- hidden_states: torch.FloatTensor,
1267
- mask_time_indices: Optional[torch.FloatTensor] = None,
1268
- attention_mask: Optional[torch.LongTensor] = None,
1269
- ):
1270
- """
1271
- Masks extracted features along time axis and/or along feature axis according to
1272
- [SpecAugment](https://arxiv.org/abs/1904.08779).
1273
- """
1274
-
1275
- # `config.apply_spec_augment` can set masking to False
1276
- if not getattr(self.config, "apply_spec_augment", True):
1277
- return hidden_states
1278
-
1279
- # generate indices & apply SpecAugment along time axis
1280
- batch_size, sequence_length, hidden_size = hidden_states.size()
1281
-
1282
- if mask_time_indices is not None:
1283
- # apply SpecAugment along time axis with given mask_time_indices
1284
- hidden_states[mask_time_indices] = self.masked_spec_embed.to(hidden_states.dtype)
1285
- elif self.config.mask_time_prob > 0 and self.training:
1286
- mask_time_indices = _compute_mask_indices(
1287
- (batch_size, sequence_length),
1288
- mask_prob=self.config.mask_time_prob,
1289
- mask_length=self.config.mask_time_length,
1290
- attention_mask=attention_mask,
1291
- min_masks=self.config.mask_time_min_masks,
1292
- )
1293
- mask_time_indices = torch.tensor(mask_time_indices, device=hidden_states.device, dtype=torch.bool)
1294
- hidden_states[mask_time_indices] = self.masked_spec_embed.to(hidden_states.dtype)
1295
-
1296
- if self.config.mask_feature_prob > 0 and self.training:
1297
- # generate indices & apply SpecAugment along feature axis
1298
- mask_feature_indices = _compute_mask_indices(
1299
- (batch_size, hidden_size),
1300
- mask_prob=self.config.mask_feature_prob,
1301
- mask_length=self.config.mask_feature_length,
1302
- min_masks=self.config.mask_feature_min_masks,
1303
- )
1304
- mask_feature_indices = torch.tensor(mask_feature_indices, device=hidden_states.device, dtype=torch.bool)
1305
- mask_feature_indices = mask_feature_indices[:, None].expand(-1, sequence_length, -1)
1306
- hidden_states[mask_feature_indices] = 0
1307
-
1308
- return hidden_states
1309
-
1310
- @add_start_docstrings_to_model_forward(WAV2VEC2_CONFORMER_INPUTS_DOCSTRING)
1311
- @add_code_sample_docstrings(
1312
- checkpoint=_CHECKPOINT_FOR_DOC,
1313
- output_type=Wav2Vec2BaseModelOutput,
1314
- config_class=_CONFIG_FOR_DOC,
1315
- modality="audio",
1316
- expected_output=_EXPECTED_OUTPUT_SHAPE,
1317
- )
1318
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2Model.forward with wav2vec2->wav2vec2_conformer
1319
- def forward(
1320
- self,
1321
- input_values: Optional[torch.Tensor],
1322
- attention_mask: Optional[torch.Tensor] = None,
1323
- mask_time_indices: Optional[torch.FloatTensor] = None,
1324
- output_attentions: Optional[bool] = None,
1325
- output_hidden_states: Optional[bool] = None,
1326
- return_dict: Optional[bool] = None,
1327
- ) -> Union[Tuple, Wav2Vec2BaseModelOutput]:
1328
- output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
1329
- output_hidden_states = (
1330
- output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
1331
- )
1332
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1333
-
1334
- extract_features = self.feature_extractor(input_values)
1335
- extract_features = extract_features.transpose(1, 2)
1336
-
1337
- if attention_mask is not None:
1338
- # compute reduced attention_mask corresponding to feature vectors
1339
- attention_mask = self._get_feature_vector_attention_mask(
1340
- extract_features.shape[1], attention_mask, add_adapter=False
1341
- )
1342
-
1343
- hidden_states, extract_features = self.feature_projection(extract_features)
1344
- hidden_states = self._mask_hidden_states(
1345
- hidden_states, mask_time_indices=mask_time_indices, attention_mask=attention_mask
1346
- )
1347
-
1348
- encoder_outputs = self.encoder(
1349
- hidden_states,
1350
- attention_mask=attention_mask,
1351
- output_attentions=output_attentions,
1352
- output_hidden_states=output_hidden_states,
1353
- return_dict=return_dict,
1354
- )
1355
-
1356
- hidden_states = encoder_outputs[0]
1357
-
1358
- if self.adapter is not None:
1359
- hidden_states = self.adapter(hidden_states)
1360
-
1361
- if not return_dict:
1362
- return (hidden_states, extract_features) + encoder_outputs[1:]
1363
-
1364
- return Wav2Vec2BaseModelOutput(
1365
- last_hidden_state=hidden_states,
1366
- extract_features=extract_features,
1367
- hidden_states=encoder_outputs.hidden_states,
1368
- attentions=encoder_outputs.attentions,
1369
- )
1370
-
1371
-
1372
- @add_start_docstrings(
1373
- """Wav2Vec2Conformer Model with a quantizer and `VQ` head on top.""", WAV2VEC2_CONFORMER_START_DOCSTRING
1374
- )
1375
- class Wav2Vec2ConformerForPreTraining(Wav2Vec2ConformerPreTrainedModel):
1376
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForPreTraining.__init__ with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer
1377
- def __init__(self, config: Wav2Vec2ConformerConfig):
1378
- super().__init__(config)
1379
- self.wav2vec2_conformer = Wav2Vec2ConformerModel(config)
1380
- self.dropout_features = nn.Dropout(config.feat_quantizer_dropout)
1381
-
1382
- self.quantizer = Wav2Vec2ConformerGumbelVectorQuantizer(config)
1383
-
1384
- self.project_hid = nn.Linear(config.hidden_size, config.proj_codevector_dim)
1385
- self.project_q = nn.Linear(config.codevector_dim, config.proj_codevector_dim)
1386
-
1387
- # Initialize weights and apply final processing
1388
- self.post_init()
1389
-
1390
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForPreTraining.set_gumbel_temperature
1391
- def set_gumbel_temperature(self, temperature: int):
1392
- """
1393
- Set the Gumbel softmax temperature to a given value. Only necessary for training
1394
- """
1395
- self.quantizer.temperature = temperature
1396
-
1397
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForPreTraining.freeze_feature_encoder with wav2vec2->wav2vec2_conformer
1398
- def freeze_feature_encoder(self):
1399
- """
1400
- Calling this function will disable the gradient computation for the feature encoder so that its parameter will
1401
- not be updated during training.
1402
- """
1403
- self.wav2vec2_conformer.feature_extractor._freeze_parameters()
1404
-
1405
- @staticmethod
1406
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForPreTraining.compute_contrastive_logits
1407
- def compute_contrastive_logits(
1408
- target_features: torch.FloatTensor,
1409
- negative_features: torch.FloatTensor,
1410
- predicted_features: torch.FloatTensor,
1411
- temperature: int = 0.1,
1412
- ):
1413
- """
1414
- Compute logits for contrastive loss based using cosine similarity as the distance measure between
1415
- `[positive_feature, negative_features]` and `[predicted_features]`. Additionally, temperature can be applied.
1416
- """
1417
- target_features = torch.cat([target_features, negative_features], dim=0)
1418
-
1419
- logits = torch.cosine_similarity(predicted_features.float(), target_features.float(), dim=-1).type_as(
1420
- target_features
1421
- )
1422
-
1423
- # apply temperature
1424
- logits = logits / temperature
1425
- return logits
1426
-
1427
- @add_start_docstrings_to_model_forward(WAV2VEC2_CONFORMER_INPUTS_DOCSTRING)
1428
- @replace_return_docstrings(output_type=Wav2Vec2ConformerForPreTrainingOutput, config_class=_CONFIG_FOR_DOC)
1429
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForPreTraining.forward with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer,wav2vec2_conformer-base->wav2vec2-conformer-rel-pos-large
1430
- def forward(
1431
- self,
1432
- input_values: Optional[torch.Tensor],
1433
- attention_mask: Optional[torch.Tensor] = None,
1434
- mask_time_indices: Optional[torch.BoolTensor] = None,
1435
- sampled_negative_indices: Optional[torch.BoolTensor] = None,
1436
- output_attentions: Optional[bool] = None,
1437
- output_hidden_states: Optional[bool] = None,
1438
- return_dict: Optional[bool] = None,
1439
- ) -> Union[Tuple, Wav2Vec2ConformerForPreTrainingOutput]:
1440
- r"""
1441
- mask_time_indices (`torch.BoolTensor` of shape `(batch_size, sequence_length)`, *optional*):
1442
- Indices to mask extracted features for contrastive loss. When in training mode, model learns to predict
1443
- masked extracted features in *config.proj_codevector_dim* space.
1444
- sampled_negative_indices (`torch.BoolTensor` of shape `(batch_size, sequence_length, num_negatives)`, *optional*):
1445
- Indices indicating which quantized target vectors are used as negative sampled vectors in contrastive loss.
1446
- Required input for pre-training.
1447
-
1448
- Returns:
1449
-
1450
- Example:
1451
-
1452
- ```python
1453
- >>> import torch
1454
- >>> from transformers import AutoFeatureExtractor, Wav2Vec2ConformerForPreTraining
1455
- >>> from transformers.models.wav2vec2_conformer.modeling_wav2vec2_conformer import (
1456
- ... _compute_mask_indices,
1457
- ... _sample_negative_indices,
1458
- ... )
1459
- >>> from datasets import load_dataset
1460
-
1461
- >>> feature_extractor = AutoFeatureExtractor.from_pretrained("facebook/wav2vec2-conformer-rel-pos-large")
1462
- >>> model = Wav2Vec2ConformerForPreTraining.from_pretrained("facebook/wav2vec2-conformer-rel-pos-large")
1463
-
1464
- >>> ds = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")
1465
- >>> input_values = feature_extractor(ds[0]["audio"]["array"], return_tensors="pt").input_values # Batch size 1
1466
-
1467
- >>> # compute masked indices
1468
- >>> batch_size, raw_sequence_length = input_values.shape
1469
- >>> sequence_length = model._get_feat_extract_output_lengths(raw_sequence_length).item()
1470
- >>> mask_time_indices = _compute_mask_indices(
1471
- ... shape=(batch_size, sequence_length), mask_prob=0.2, mask_length=2
1472
- ... )
1473
- >>> sampled_negative_indices = _sample_negative_indices(
1474
- ... features_shape=(batch_size, sequence_length),
1475
- ... num_negatives=model.config.num_negatives,
1476
- ... mask_time_indices=mask_time_indices,
1477
- ... )
1478
- >>> mask_time_indices = torch.tensor(data=mask_time_indices, device=input_values.device, dtype=torch.long)
1479
- >>> sampled_negative_indices = torch.tensor(
1480
- ... data=sampled_negative_indices, device=input_values.device, dtype=torch.long
1481
- ... )
1482
-
1483
- >>> with torch.no_grad():
1484
- ... outputs = model(input_values, mask_time_indices=mask_time_indices)
1485
-
1486
- >>> # compute cosine similarity between predicted (=projected_states) and target (=projected_quantized_states)
1487
- >>> cosine_sim = torch.cosine_similarity(outputs.projected_states, outputs.projected_quantized_states, dim=-1)
1488
-
1489
- >>> # show that cosine similarity is much higher than random
1490
- >>> cosine_sim[mask_time_indices.to(torch.bool)].mean() > 0.5
1491
- tensor(True)
1492
-
1493
- >>> # for contrastive loss training model should be put into train mode
1494
- >>> model = model.train()
1495
- >>> loss = model(
1496
- ... input_values, mask_time_indices=mask_time_indices, sampled_negative_indices=sampled_negative_indices
1497
- ... ).loss
1498
- ```"""
1499
-
1500
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1501
-
1502
- if mask_time_indices is not None:
1503
- mask_time_indices = mask_time_indices.to(torch.bool)
1504
-
1505
- outputs = self.wav2vec2_conformer(
1506
- input_values,
1507
- attention_mask=attention_mask,
1508
- output_attentions=output_attentions,
1509
- output_hidden_states=output_hidden_states,
1510
- mask_time_indices=mask_time_indices,
1511
- return_dict=return_dict,
1512
- )
1513
-
1514
- # 1. project all transformed features (including masked) to final vq dim
1515
- transformer_features = self.project_hid(outputs[0])
1516
-
1517
- # 2. quantize all (unmasked) extracted features and project to final vq dim
1518
- extract_features = self.dropout_features(outputs[1])
1519
-
1520
- if attention_mask is not None:
1521
- # compute reduced attention_mask correponding to feature vectors
1522
- attention_mask = self._get_feature_vector_attention_mask(
1523
- extract_features.shape[1], attention_mask, add_adapter=False
1524
- )
1525
-
1526
- quantized_features, codevector_perplexity = self.quantizer(
1527
- extract_features, mask_time_indices=mask_time_indices
1528
- )
1529
- quantized_features = self.project_q(quantized_features)
1530
-
1531
- loss = contrastive_loss = diversity_loss = None
1532
- if sampled_negative_indices is not None:
1533
- batch_size, sequence_length, hidden_size = quantized_features.shape
1534
-
1535
- # for training, we sample negatives
1536
- # 3. sample K negatives (distractors) quantized states for contrastive loss
1537
- # if attention_mask is passed, make sure that padded feature vectors cannot be sampled
1538
- # sample negative quantized vectors BTC => (BxT)C
1539
- negative_quantized_features = quantized_features.view(-1, hidden_size)[
1540
- sampled_negative_indices.long().view(-1)
1541
- ]
1542
- negative_quantized_features = negative_quantized_features.view(
1543
- batch_size, sequence_length, -1, hidden_size
1544
- ).permute(2, 0, 1, 3)
1545
-
1546
- # 4. compute logits, corresponding to `logs = sim(c_t, [q_t, \sim{q}_t]) / \kappa`
1547
- # of equation (3) in https://arxiv.org/pdf/2006.11477.pdf
1548
- logits = self.compute_contrastive_logits(
1549
- quantized_features[None, :],
1550
- negative_quantized_features,
1551
- transformer_features,
1552
- self.config.contrastive_logits_temperature,
1553
- )
1554
-
1555
- # 5. if a negative vector is identical to the positive (i.e. when codebook utilization is low),
1556
- # its cosine similarity will be masked
1557
- neg_is_pos = (quantized_features == negative_quantized_features).all(-1)
1558
-
1559
- if neg_is_pos.any():
1560
- logits[1:][neg_is_pos] = float("-inf")
1561
-
1562
- # 6. compute contrastive loss \mathbf{L}_m = cross_entropy(logs) =
1563
- # -log(exp(sim(c_t, q_t)/\kappa) / \sum_{\sim{q}} exp(sim(c_t, \sim{q})/\kappa))
1564
- logits = logits.transpose(0, 2).reshape(-1, logits.size(0))
1565
- target = ((1 - mask_time_indices.long()) * -100).transpose(0, 1).flatten()
1566
-
1567
- contrastive_loss = nn.functional.cross_entropy(logits.float(), target, reduction="sum")
1568
- # 7. compute diversity loss: \mathbf{L}_d
1569
- num_codevectors = self.config.num_codevectors_per_group * self.config.num_codevector_groups
1570
- diversity_loss = ((num_codevectors - codevector_perplexity) / num_codevectors) * mask_time_indices.sum()
1571
-
1572
- # 8. \mathbf{L} = \mathbf{L}_m + \alpha * \mathbf{L}_d
1573
- loss = contrastive_loss + self.config.diversity_loss_weight * diversity_loss
1574
-
1575
- if not return_dict:
1576
- if loss is not None:
1577
- return (loss, transformer_features, quantized_features, codevector_perplexity) + outputs[2:]
1578
- return (transformer_features, quantized_features, codevector_perplexity) + outputs[2:]
1579
-
1580
- return Wav2Vec2ConformerForPreTrainingOutput(
1581
- loss=loss,
1582
- projected_states=transformer_features,
1583
- projected_quantized_states=quantized_features,
1584
- codevector_perplexity=codevector_perplexity,
1585
- hidden_states=outputs.hidden_states,
1586
- attentions=outputs.attentions,
1587
- contrastive_loss=contrastive_loss,
1588
- diversity_loss=diversity_loss,
1589
- )
1590
-
1591
-
1592
- @add_start_docstrings(
1593
- """Wav2Vec2Conformer Model with a `language modeling` head on top for Connectionist Temporal Classification (CTC).""",
1594
- WAV2VEC2_CONFORMER_START_DOCSTRING,
1595
- )
1596
- class Wav2Vec2ConformerForCTC(Wav2Vec2ConformerPreTrainedModel):
1597
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForCTC.__init__ with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer
1598
- def __init__(self, config):
1599
- super().__init__(config)
1600
-
1601
- self.wav2vec2_conformer = Wav2Vec2ConformerModel(config)
1602
- self.dropout = nn.Dropout(config.final_dropout)
1603
-
1604
- if config.vocab_size is None:
1605
- raise ValueError(
1606
- f"You are trying to instantiate {self.__class__} with a configuration that "
1607
- "does not define the vocabulary size of the language model head. Please "
1608
- "instantiate the model as follows: `Wav2Vec2ConformerForCTC.from_pretrained(..., vocab_size=vocab_size)`. "
1609
- "or define `vocab_size` of your model's configuration."
1610
- )
1611
- output_hidden_size = (
1612
- config.output_hidden_size if hasattr(config, "add_adapter") and config.add_adapter else config.hidden_size
1613
- )
1614
- self.lm_head = nn.Linear(output_hidden_size, config.vocab_size)
1615
-
1616
- # Initialize weights and apply final processing
1617
- self.post_init()
1618
-
1619
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForCTC.freeze_feature_encoder with wav2vec2->wav2vec2_conformer
1620
- def freeze_feature_encoder(self):
1621
- """
1622
- Calling this function will disable the gradient computation for the feature encoder so that its parameter will
1623
- not be updated during training.
1624
- """
1625
- self.wav2vec2_conformer.feature_extractor._freeze_parameters()
1626
-
1627
- @add_start_docstrings_to_model_forward(WAV2VEC2_CONFORMER_INPUTS_DOCSTRING)
1628
- @add_code_sample_docstrings(
1629
- checkpoint=_CHECKPOINT_FOR_DOC,
1630
- output_type=CausalLMOutput,
1631
- config_class=_CONFIG_FOR_DOC,
1632
- expected_output=_CTC_EXPECTED_OUTPUT,
1633
- expected_loss=_CTC_EXPECTED_LOSS,
1634
- )
1635
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForCTC.forward with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer
1636
- def forward(
1637
- self,
1638
- input_values: Optional[torch.Tensor],
1639
- attention_mask: Optional[torch.Tensor] = None,
1640
- output_attentions: Optional[bool] = None,
1641
- output_hidden_states: Optional[bool] = None,
1642
- return_dict: Optional[bool] = None,
1643
- labels: Optional[torch.Tensor] = None,
1644
- ) -> Union[Tuple, CausalLMOutput]:
1645
- r"""
1646
- labels (`torch.LongTensor` of shape `(batch_size, target_length)`, *optional*):
1647
- Labels for connectionist temporal classification. Note that `target_length` has to be smaller or equal to
1648
- the sequence length of the output logits. Indices are selected in `[-100, 0, ..., config.vocab_size - 1]`.
1649
- All labels set to `-100` are ignored (masked), the loss is only computed for labels in `[0, ...,
1650
- config.vocab_size - 1]`.
1651
- """
1652
-
1653
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1654
-
1655
- outputs = self.wav2vec2_conformer(
1656
- input_values,
1657
- attention_mask=attention_mask,
1658
- output_attentions=output_attentions,
1659
- output_hidden_states=output_hidden_states,
1660
- return_dict=return_dict,
1661
- )
1662
-
1663
- hidden_states = outputs[0]
1664
- hidden_states = self.dropout(hidden_states)
1665
-
1666
- logits = self.lm_head(hidden_states)
1667
-
1668
- loss = None
1669
- if labels is not None:
1670
- if labels.max() >= self.config.vocab_size:
1671
- raise ValueError(f"Label values must be <= vocab_size: {self.config.vocab_size}")
1672
-
1673
- # retrieve loss input_lengths from attention_mask
1674
- attention_mask = (
1675
- attention_mask if attention_mask is not None else torch.ones_like(input_values, dtype=torch.long)
1676
- )
1677
- input_lengths = self._get_feat_extract_output_lengths(attention_mask.sum(-1)).to(torch.long)
1678
-
1679
- # assuming that padded tokens are filled with -100
1680
- # when not being attended to
1681
- labels_mask = labels >= 0
1682
- target_lengths = labels_mask.sum(-1)
1683
- flattened_targets = labels.masked_select(labels_mask)
1684
-
1685
- # ctc_loss doesn't support fp16
1686
- log_probs = nn.functional.log_softmax(logits, dim=-1, dtype=torch.float32).transpose(0, 1)
1687
-
1688
- with torch.backends.cudnn.flags(enabled=False):
1689
- loss = nn.functional.ctc_loss(
1690
- log_probs,
1691
- flattened_targets,
1692
- input_lengths,
1693
- target_lengths,
1694
- blank=self.config.pad_token_id,
1695
- reduction=self.config.ctc_loss_reduction,
1696
- zero_infinity=self.config.ctc_zero_infinity,
1697
- )
1698
-
1699
- if not return_dict:
1700
- output = (logits,) + outputs[_HIDDEN_STATES_START_POSITION:]
1701
- return ((loss,) + output) if loss is not None else output
1702
-
1703
- return CausalLMOutput(
1704
- loss=loss, logits=logits, hidden_states=outputs.hidden_states, attentions=outputs.attentions
1705
- )
1706
-
1707
-
1708
- @add_start_docstrings(
1709
- """
1710
- Wav2Vec2Conformer Model with a sequence classification head on top (a linear layer over the pooled output) for
1711
- tasks like SUPERB Keyword Spotting.
1712
- """,
1713
- WAV2VEC2_CONFORMER_START_DOCSTRING,
1714
- )
1715
- class Wav2Vec2ConformerForSequenceClassification(Wav2Vec2ConformerPreTrainedModel):
1716
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForSequenceClassification.__init__ with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer
1717
- def __init__(self, config):
1718
- super().__init__(config)
1719
-
1720
- if hasattr(config, "add_adapter") and config.add_adapter:
1721
- raise ValueError(
1722
- "Sequence classification does not support the use of Wav2Vec2Conformer adapters (config.add_adapter=True)"
1723
- )
1724
- self.wav2vec2_conformer = Wav2Vec2ConformerModel(config)
1725
- num_layers = config.num_hidden_layers + 1 # transformer layers + input embeddings
1726
- if config.use_weighted_layer_sum:
1727
- self.layer_weights = nn.Parameter(torch.ones(num_layers) / num_layers)
1728
- self.projector = nn.Linear(config.hidden_size, config.classifier_proj_size)
1729
- self.classifier = nn.Linear(config.classifier_proj_size, config.num_labels)
1730
-
1731
- # Initialize weights and apply final processing
1732
- self.post_init()
1733
-
1734
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForSequenceClassification.freeze_feature_encoder with wav2vec2->wav2vec2_conformer
1735
- def freeze_feature_encoder(self):
1736
- """
1737
- Calling this function will disable the gradient computation for the feature encoder so that its parameter will
1738
- not be updated during training.
1739
- """
1740
- self.wav2vec2_conformer.feature_extractor._freeze_parameters()
1741
-
1742
- def freeze_base_model(self):
1743
- """
1744
- Calling this function will disable the gradient computation for the base model so that its parameters will not
1745
- be updated during training. Only the classification head will be updated.
1746
- """
1747
- for param in self.wav2vec2_conformer.parameters():
1748
- param.requires_grad = False
1749
-
1750
- @add_start_docstrings_to_model_forward(WAV2VEC2_CONFORMER_INPUTS_DOCSTRING)
1751
- @add_code_sample_docstrings(
1752
- checkpoint=_CHECKPOINT_FOR_DOC,
1753
- output_type=SequenceClassifierOutput,
1754
- config_class=_CONFIG_FOR_DOC,
1755
- modality="audio",
1756
- )
1757
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForSequenceClassification.forward with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer,WAV_2_VEC_2->WAV2VEC2_CONFORMER
1758
- def forward(
1759
- self,
1760
- input_values: Optional[torch.Tensor],
1761
- attention_mask: Optional[torch.Tensor] = None,
1762
- output_attentions: Optional[bool] = None,
1763
- output_hidden_states: Optional[bool] = None,
1764
- return_dict: Optional[bool] = None,
1765
- labels: Optional[torch.Tensor] = None,
1766
- ) -> Union[Tuple, SequenceClassifierOutput]:
1767
- r"""
1768
- labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1769
- Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
1770
- config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
1771
- `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
1772
- """
1773
-
1774
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1775
- output_hidden_states = True if self.config.use_weighted_layer_sum else output_hidden_states
1776
-
1777
- outputs = self.wav2vec2_conformer(
1778
- input_values,
1779
- attention_mask=attention_mask,
1780
- output_attentions=output_attentions,
1781
- output_hidden_states=output_hidden_states,
1782
- return_dict=return_dict,
1783
- )
1784
-
1785
- if self.config.use_weighted_layer_sum:
1786
- hidden_states = outputs[_HIDDEN_STATES_START_POSITION]
1787
- hidden_states = torch.stack(hidden_states, dim=1)
1788
- norm_weights = nn.functional.softmax(self.layer_weights, dim=-1)
1789
- hidden_states = (hidden_states * norm_weights.view(-1, 1, 1)).sum(dim=1)
1790
- else:
1791
- hidden_states = outputs[0]
1792
-
1793
- hidden_states = self.projector(hidden_states)
1794
- if attention_mask is None:
1795
- pooled_output = hidden_states.mean(dim=1)
1796
- else:
1797
- padding_mask = self._get_feature_vector_attention_mask(hidden_states.shape[1], attention_mask)
1798
- hidden_states[~padding_mask] = 0.0
1799
- pooled_output = hidden_states.sum(dim=1) / padding_mask.sum(dim=1).view(-1, 1)
1800
-
1801
- logits = self.classifier(pooled_output)
1802
-
1803
- loss = None
1804
- if labels is not None:
1805
- loss_fct = CrossEntropyLoss()
1806
- loss = loss_fct(logits.view(-1, self.config.num_labels), labels.view(-1))
1807
-
1808
- if not return_dict:
1809
- output = (logits,) + outputs[_HIDDEN_STATES_START_POSITION:]
1810
- return ((loss,) + output) if loss is not None else output
1811
-
1812
- return SequenceClassifierOutput(
1813
- loss=loss,
1814
- logits=logits,
1815
- hidden_states=outputs.hidden_states,
1816
- attentions=outputs.attentions,
1817
- )
1818
-
1819
-
1820
- @add_start_docstrings(
1821
- """
1822
- Wav2Vec2Conformer Model with a frame classification head on top for tasks like Speaker Diarization.
1823
- """,
1824
- WAV2VEC2_CONFORMER_START_DOCSTRING,
1825
- )
1826
- class Wav2Vec2ConformerForAudioFrameClassification(Wav2Vec2ConformerPreTrainedModel):
1827
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForAudioFrameClassification.__init__ with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer,WAV_2_VEC_2->WAV2VEC2_CONFORMER
1828
- def __init__(self, config):
1829
- super().__init__(config)
1830
-
1831
- if hasattr(config, "add_adapter") and config.add_adapter:
1832
- raise ValueError(
1833
- "Audio frame classification does not support the use of Wav2Vec2Conformer adapters (config.add_adapter=True)"
1834
- )
1835
- self.wav2vec2_conformer = Wav2Vec2ConformerModel(config)
1836
- num_layers = config.num_hidden_layers + 1 # transformer layers + input embeddings
1837
- if config.use_weighted_layer_sum:
1838
- self.layer_weights = nn.Parameter(torch.ones(num_layers) / num_layers)
1839
- self.classifier = nn.Linear(config.hidden_size, config.num_labels)
1840
- self.num_labels = config.num_labels
1841
-
1842
- self.init_weights()
1843
-
1844
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForAudioFrameClassification.freeze_feature_encoder with wav2vec2->wav2vec2_conformer
1845
- def freeze_feature_encoder(self):
1846
- """
1847
- Calling this function will disable the gradient computation for the feature encoder so that its parameter will
1848
- not be updated during training.
1849
- """
1850
- self.wav2vec2_conformer.feature_extractor._freeze_parameters()
1851
-
1852
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForAudioFrameClassification.freeze_base_model with wav2vec2->wav2vec2_conformer
1853
- def freeze_base_model(self):
1854
- """
1855
- Calling this function will disable the gradient computation for the base model so that its parameters will not
1856
- be updated during training. Only the classification head will be updated.
1857
- """
1858
- for param in self.wav2vec2_conformer.parameters():
1859
- param.requires_grad = False
1860
-
1861
- @add_start_docstrings_to_model_forward(WAV2VEC2_CONFORMER_INPUTS_DOCSTRING)
1862
- @add_code_sample_docstrings(
1863
- checkpoint=_CHECKPOINT_FOR_DOC,
1864
- output_type=TokenClassifierOutput,
1865
- config_class=_CONFIG_FOR_DOC,
1866
- modality="audio",
1867
- )
1868
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForAudioFrameClassification.forward with wav2vec2->wav2vec2_conformer
1869
- def forward(
1870
- self,
1871
- input_values: Optional[torch.Tensor],
1872
- attention_mask: Optional[torch.Tensor] = None,
1873
- labels: Optional[torch.Tensor] = None,
1874
- output_attentions: Optional[bool] = None,
1875
- output_hidden_states: Optional[bool] = None,
1876
- return_dict: Optional[bool] = None,
1877
- ) -> Union[Tuple, TokenClassifierOutput]:
1878
- r"""
1879
- labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1880
- Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
1881
- config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
1882
- `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
1883
- """
1884
-
1885
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1886
- output_hidden_states = True if self.config.use_weighted_layer_sum else output_hidden_states
1887
-
1888
- outputs = self.wav2vec2_conformer(
1889
- input_values,
1890
- attention_mask=attention_mask,
1891
- output_attentions=output_attentions,
1892
- output_hidden_states=output_hidden_states,
1893
- return_dict=return_dict,
1894
- )
1895
-
1896
- if self.config.use_weighted_layer_sum:
1897
- hidden_states = outputs[_HIDDEN_STATES_START_POSITION]
1898
- hidden_states = torch.stack(hidden_states, dim=1)
1899
- norm_weights = nn.functional.softmax(self.layer_weights, dim=-1)
1900
- hidden_states = (hidden_states * norm_weights.view(-1, 1, 1)).sum(dim=1)
1901
- else:
1902
- hidden_states = outputs[0]
1903
-
1904
- logits = self.classifier(hidden_states)
1905
-
1906
- loss = None
1907
- if labels is not None:
1908
- loss_fct = CrossEntropyLoss()
1909
- loss = loss_fct(logits.view(-1, self.num_labels), torch.argmax(labels.view(-1, self.num_labels), axis=1))
1910
-
1911
- if not return_dict:
1912
- output = (logits,) + outputs[_HIDDEN_STATES_START_POSITION:]
1913
- return output
1914
-
1915
- return TokenClassifierOutput(
1916
- loss=loss,
1917
- logits=logits,
1918
- hidden_states=outputs.hidden_states,
1919
- attentions=outputs.attentions,
1920
- )
1921
-
1922
-
1923
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.AMSoftmaxLoss
1924
- class AMSoftmaxLoss(nn.Module):
1925
- def __init__(self, input_dim, num_labels, scale=30.0, margin=0.4):
1926
- super(AMSoftmaxLoss, self).__init__()
1927
- self.scale = scale
1928
- self.margin = margin
1929
- self.num_labels = num_labels
1930
- self.weight = nn.Parameter(torch.randn(input_dim, num_labels), requires_grad=True)
1931
- self.loss = nn.CrossEntropyLoss()
1932
-
1933
- def forward(self, hidden_states, labels):
1934
- labels = labels.flatten()
1935
- weight = nn.functional.normalize(self.weight, dim=0)
1936
- hidden_states = nn.functional.normalize(hidden_states, dim=1)
1937
- cos_theta = torch.mm(hidden_states, weight)
1938
- psi = cos_theta - self.margin
1939
-
1940
- onehot = nn.functional.one_hot(labels, self.num_labels)
1941
- logits = self.scale * torch.where(onehot.bool(), psi, cos_theta)
1942
- loss = self.loss(logits, labels)
1943
-
1944
- return loss
1945
-
1946
-
1947
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.TDNNLayer
1948
- class TDNNLayer(nn.Module):
1949
- def __init__(self, config, layer_id=0):
1950
- super().__init__()
1951
- self.in_conv_dim = config.tdnn_dim[layer_id - 1] if layer_id > 0 else config.tdnn_dim[layer_id]
1952
- self.out_conv_dim = config.tdnn_dim[layer_id]
1953
- self.kernel_size = config.tdnn_kernel[layer_id]
1954
- self.dilation = config.tdnn_dilation[layer_id]
1955
-
1956
- self.kernel = nn.Linear(self.in_conv_dim * self.kernel_size, self.out_conv_dim)
1957
- self.activation = nn.ReLU()
1958
-
1959
- def forward(self, hidden_states):
1960
- hidden_states = hidden_states.unsqueeze(1)
1961
- hidden_states = nn.functional.unfold(
1962
- hidden_states,
1963
- (self.kernel_size, self.in_conv_dim),
1964
- stride=(1, self.in_conv_dim),
1965
- dilation=(self.dilation, 1),
1966
- )
1967
- hidden_states = hidden_states.transpose(1, 2)
1968
- hidden_states = self.kernel(hidden_states)
1969
-
1970
- hidden_states = self.activation(hidden_states)
1971
- return hidden_states
1972
-
1973
-
1974
- @add_start_docstrings(
1975
- """
1976
- Wav2Vec2Conformer Model with an XVector feature extraction head on top for tasks like Speaker Verification.
1977
- """,
1978
- WAV2VEC2_CONFORMER_START_DOCSTRING,
1979
- )
1980
- class Wav2Vec2ConformerForXVector(Wav2Vec2ConformerPreTrainedModel):
1981
- def __init__(self, config):
1982
- super().__init__(config)
1983
-
1984
- self.wav2vec2_conformer = Wav2Vec2ConformerModel(config)
1985
- num_layers = config.num_hidden_layers + 1 # transformer layers + input embeddings
1986
- if config.use_weighted_layer_sum:
1987
- self.layer_weights = nn.Parameter(torch.ones(num_layers) / num_layers)
1988
- self.projector = nn.Linear(config.hidden_size, config.tdnn_dim[0])
1989
-
1990
- tdnn_layers = [TDNNLayer(config, i) for i in range(len(config.tdnn_dim))]
1991
- self.tdnn = nn.ModuleList(tdnn_layers)
1992
-
1993
- self.feature_extractor = nn.Linear(config.tdnn_dim[-1] * 2, config.xvector_output_dim)
1994
- self.classifier = nn.Linear(config.xvector_output_dim, config.xvector_output_dim)
1995
-
1996
- self.objective = AMSoftmaxLoss(config.xvector_output_dim, config.num_labels)
1997
-
1998
- self.init_weights()
1999
-
2000
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForXVector.freeze_feature_encoder with wav2vec2->wav2vec2_conformer
2001
- def freeze_feature_encoder(self):
2002
- """
2003
- Calling this function will disable the gradient computation for the feature encoder so that its parameter will
2004
- not be updated during training.
2005
- """
2006
- self.wav2vec2_conformer.feature_extractor._freeze_parameters()
2007
-
2008
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForXVector.freeze_base_model with wav2vec2->wav2vec2_conformer
2009
- def freeze_base_model(self):
2010
- """
2011
- Calling this function will disable the gradient computation for the base model so that its parameters will not
2012
- be updated during training. Only the classification head will be updated.
2013
- """
2014
- for param in self.wav2vec2_conformer.parameters():
2015
- param.requires_grad = False
2016
-
2017
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForXVector._get_tdnn_output_lengths with wav2vec2->wav2vec2_conformer
2018
- def _get_tdnn_output_lengths(self, input_lengths: Union[torch.LongTensor, int]):
2019
- """
2020
- Computes the output length of the TDNN layers
2021
- """
2022
-
2023
- def _conv_out_length(input_length, kernel_size, stride):
2024
- # 1D convolutional layer output length formula taken
2025
- # from https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html
2026
- return (input_length - kernel_size) // stride + 1
2027
-
2028
- for kernel_size in self.config.tdnn_kernel:
2029
- input_lengths = _conv_out_length(input_lengths, kernel_size, 1)
2030
-
2031
- return input_lengths
2032
-
2033
- @add_start_docstrings_to_model_forward(WAV2VEC2_CONFORMER_INPUTS_DOCSTRING)
2034
- @add_code_sample_docstrings(
2035
- checkpoint=_CHECKPOINT_FOR_DOC,
2036
- output_type=XVectorOutput,
2037
- config_class=_CONFIG_FOR_DOC,
2038
- modality="audio",
2039
- )
2040
- # Copied from transformers.models.wav2vec2.modeling_wav2vec2.Wav2Vec2ForXVector.forward with Wav2Vec2->Wav2Vec2Conformer,wav2vec2->wav2vec2_conformer,WAV_2_VEC_2->WAV2VEC2_CONFORMER
2041
- def forward(
2042
- self,
2043
- input_values: Optional[torch.Tensor],
2044
- attention_mask: Optional[torch.Tensor] = None,
2045
- output_attentions: Optional[bool] = None,
2046
- output_hidden_states: Optional[bool] = None,
2047
- return_dict: Optional[bool] = None,
2048
- labels: Optional[torch.Tensor] = None,
2049
- ) -> Union[Tuple, XVectorOutput]:
2050
- r"""
2051
- labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
2052
- Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
2053
- config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
2054
- `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
2055
- """
2056
-
2057
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
2058
- output_hidden_states = True if self.config.use_weighted_layer_sum else output_hidden_states
2059
-
2060
- outputs = self.wav2vec2_conformer(
2061
- input_values,
2062
- attention_mask=attention_mask,
2063
- output_attentions=output_attentions,
2064
- output_hidden_states=output_hidden_states,
2065
- return_dict=return_dict,
2066
- )
2067
-
2068
- if self.config.use_weighted_layer_sum:
2069
- hidden_states = outputs[_HIDDEN_STATES_START_POSITION]
2070
- hidden_states = torch.stack(hidden_states, dim=1)
2071
- norm_weights = nn.functional.softmax(self.layer_weights, dim=-1)
2072
- hidden_states = (hidden_states * norm_weights.view(-1, 1, 1)).sum(dim=1)
2073
- else:
2074
- hidden_states = outputs[0]
2075
-
2076
- hidden_states = self.projector(hidden_states)
2077
-
2078
- for tdnn_layer in self.tdnn:
2079
- hidden_states = tdnn_layer(hidden_states)
2080
-
2081
- # Statistic Pooling
2082
- if attention_mask is None:
2083
- mean_features = hidden_states.mean(dim=1)
2084
- std_features = hidden_states.std(dim=1)
2085
- else:
2086
- feat_extract_output_lengths = self._get_feat_extract_output_lengths(attention_mask.sum(dim=1))
2087
- tdnn_output_lengths = self._get_tdnn_output_lengths(feat_extract_output_lengths)
2088
- mean_features = []
2089
- std_features = []
2090
- for i, length in enumerate(tdnn_output_lengths):
2091
- mean_features.append(hidden_states[i, :length].mean(dim=0))
2092
- std_features.append(hidden_states[i, :length].std(dim=0))
2093
- mean_features = torch.stack(mean_features)
2094
- std_features = torch.stack(std_features)
2095
- statistic_pooling = torch.cat([mean_features, std_features], dim=-1)
2096
-
2097
- output_embeddings = self.feature_extractor(statistic_pooling)
2098
- logits = self.classifier(output_embeddings)
2099
-
2100
- loss = None
2101
- if labels is not None:
2102
- loss = self.objective(logits, labels)
2103
-
2104
- if not return_dict:
2105
- output = (logits, output_embeddings) + outputs[_HIDDEN_STATES_START_POSITION:]
2106
- return ((loss,) + output) if loss is not None else output
2107
-
2108
- return XVectorOutput(
2109
- loss=loss,
2110
- logits=logits,
2111
- embeddings=output_embeddings,
2112
- hidden_states=outputs.hidden_states,
2113
- attentions=outputs.attentions,
2114
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq/modules/random_quantizer.py DELETED
@@ -1,68 +0,0 @@
1
- import torch
2
- from torch import nn, einsum
3
- from einops import rearrange
4
-
5
-
6
- class RandomProjectionQuantizer(nn.Module):
7
- """
8
- Random projection and codebook lookup module
9
-
10
- Some code is borrowed from:
11
- https://github.com/lucidrains/vector-quantize-pytorch/blob/master/vector_quantize_pytorch/random_projection_quantizer.py
12
- But I did normalization using pre-computed global mean & variance instead of using layer norm.
13
- """
14
-
15
- def __init__(
16
- self,
17
- input_dim,
18
- codebook_dim,
19
- codebook_size,
20
- seed=142,
21
- ):
22
- super().__init__()
23
-
24
- # random seed
25
- torch.manual_seed(seed)
26
-
27
- # randomly initialized projection
28
- random_projection = torch.empty(input_dim, codebook_dim)
29
- nn.init.xavier_normal_(random_projection)
30
- self.register_buffer("random_projection", random_projection)
31
-
32
- # randomly initialized codebook
33
- codebook = torch.empty(codebook_size, codebook_dim)
34
- nn.init.normal_(codebook)
35
- self.register_buffer("codebook", codebook)
36
-
37
- def codebook_lookup(self, x):
38
- # reshape
39
- b = x.shape[0]
40
- x = rearrange(x, "b n e -> (b n) e")
41
-
42
- # L2 normalization
43
- normalized_x = nn.functional.normalize(x, dim=1, p=2)
44
- normalized_codebook = nn.functional.normalize(self.codebook, dim=1, p=2)
45
-
46
- # compute distances
47
- distances = torch.cdist(normalized_codebook, normalized_x)
48
-
49
- # get nearest
50
- nearest_indices = torch.argmin(distances, dim=0)
51
-
52
- # reshape
53
- xq = rearrange(nearest_indices, "(b n) -> b n", b=b)
54
-
55
- return xq
56
-
57
- @torch.no_grad()
58
- def forward(self, x):
59
- # always eval
60
- self.eval()
61
-
62
- # random projection [batch, length, input_dim] -> [batch, length, codebook_dim]
63
- x = einsum("b n d, d e -> b n e", x, self.random_projection)
64
-
65
- # codebook lookup
66
- xq = self.codebook_lookup(x)
67
-
68
- return xq
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq/modules/rvq.py DELETED
@@ -1,314 +0,0 @@
1
-
2
- from typing import Union
3
-
4
- import numpy as np
5
- import torch
6
- import torch.nn as nn
7
- import torch.nn.functional as F
8
- from einops import rearrange
9
- try:
10
- from torch.nn.utils import weight_norm
11
- except:
12
- try:
13
- from torch.nn.utils.parametrizations import weight_norm
14
- except:
15
- from torch.nn.utils.parametrize import weight_norm
16
-
17
- def WNConv1d(*args, **kwargs):
18
- return weight_norm(nn.Conv1d(*args, **kwargs))
19
-
20
-
21
- class VectorQuantize(nn.Module):
22
- """
23
- Implementation of VQ similar to Karpathy's repo:
24
- https://github.com/karpathy/deep-vector-quantization
25
- Additionally uses following tricks from Improved VQGAN
26
- (https://arxiv.org/pdf/2110.04627.pdf):
27
- 1. Factorized codes: Perform nearest neighbor lookup in low-dimensional space
28
- for improved codebook usage
29
- 2. l2-normalized codes: Converts euclidean distance to cosine similarity which
30
- improves training stability
31
- """
32
-
33
- def __init__(self, input_dim: int, codebook_size: int, codebook_dim: int, stale_tolerance: int = 1000, mfcc_clustering=False, n_layer=1):
34
- super().__init__()
35
- self.codebook_size = codebook_size
36
- self.codebook_dim = codebook_dim
37
- self.mfcc_clustering = mfcc_clustering
38
-
39
- ProjClass = nn.Identity if mfcc_clustering else WNConv1d
40
- if n_layer==1:
41
- self.in_proj = ProjClass(input_dim, codebook_dim, kernel_size=1)
42
- self.out_proj = ProjClass(codebook_dim, input_dim, kernel_size=1)
43
- elif n_layer >= 2:
44
- ndim_hidden = 128
45
- self.in_proj = nn.Sequential(
46
- ProjClass(input_dim, ndim_hidden, kernel_size=1),
47
- *[nn.Sequential(nn.ReLU(), ProjClass(ndim_hidden, ndim_hidden, kernel_size=1),) for _ in range(n_layer-2)],
48
- nn.ReLU(),
49
- ProjClass(ndim_hidden, codebook_dim, kernel_size=1)
50
- )
51
- self.out_proj = nn.Sequential(
52
- ProjClass(codebook_dim, ndim_hidden, kernel_size=1),
53
- nn.ReLU(),
54
- *[nn.Sequential(ProjClass(ndim_hidden, ndim_hidden, kernel_size=1), nn.ReLU()) for _ in range(n_layer-2)],
55
- ProjClass(ndim_hidden, input_dim, kernel_size=1),
56
- )
57
- self.codebook = nn.Embedding(codebook_size, codebook_dim)
58
- self.register_buffer("stale_counter", torch.zeros(self.codebook_size,))
59
- self.stale_tolerance = stale_tolerance
60
-
61
- def forward(self, z):
62
- """Quantized the input tensor using a fixed codebook and returns
63
- the corresponding codebook vectors
64
-
65
- Parameters
66
- ----------
67
- z : Tensor[B x D x T]
68
-
69
- Returns
70
- -------
71
- Tensor[B x D x T]
72
- Quantized continuous representation of input
73
- Tensor[1]
74
- Commitment loss to train encoder to predict vectors closer to codebook
75
- entries
76
- Tensor[1]
77
- Codebook loss to update the codebook
78
- Tensor[B x T]
79
- Codebook indices (quantized discrete representation of input)
80
- Tensor[B x D x T]
81
- Projected latents (continuous representation of input before quantization)
82
- """
83
-
84
- # Factorized codes (ViT-VQGAN) Project input into low-dimensional space
85
-
86
- z_e = self.in_proj(z) # z_e : (B x D x T)
87
- z_q, indices = self.decode_latents(z_e)
88
-
89
- commitment_loss = F.mse_loss(z_e, z_q.detach(), reduction="none").mean([1, 2])
90
- codebook_loss = F.mse_loss(z_q, z_e.detach(), reduction="none").mean([1, 2])
91
-
92
- z_q = (
93
- z_e + (z_q - z_e).detach()
94
- ) # noop in forward pass, straight-through gradient estimator in backward pass
95
-
96
- z_q = self.out_proj(z_q)
97
-
98
- return z_q, commitment_loss, codebook_loss, indices, z_e
99
-
100
- def embed_code(self, embed_id):
101
- return F.embedding(embed_id, self.codebook.weight)
102
-
103
- def decode_code(self, embed_id):
104
- return self.embed_code(embed_id).transpose(1, 2)
105
-
106
- def decode_latents(self, latents):
107
- encodings = rearrange(latents, "b d t -> (b t) d")
108
- codebook = self.codebook.weight # codebook: (N x D)
109
-
110
- # L2 normalize encodings and codebook (ViT-VQGAN)
111
- encodings = F.normalize(encodings)
112
- codebook = F.normalize(codebook)
113
-
114
- # Compute euclidean distance with codebook
115
- dist = (
116
- encodings.pow(2).sum(1, keepdim=True)
117
- - 2 * encodings @ codebook.t()
118
- + codebook.pow(2).sum(1, keepdim=True).t()
119
- )
120
- indices = rearrange((-dist).max(1)[1], "(b t) -> b t", b=latents.size(0))
121
- z_q = self.decode_code(indices)
122
-
123
- if(self.training):
124
- onehots = torch.nn.functional.one_hot(indices, self.codebook_size).float() # B, T, codebook_size
125
- stale_codes = (onehots.sum(0).sum(0) == 0).float()
126
- self.stale_counter = self.stale_counter * stale_codes + stale_codes
127
-
128
- # random replace codes that haven't been used for a while
129
- replace_code = (self.stale_counter == self.stale_tolerance).float() # codebook_size
130
- if replace_code.sum(-1) > 0:
131
- print("Replace {} codes".format(replace_code.sum(-1)))
132
- random_input_idx = torch.randperm(encodings.shape[0])
133
- random_input = encodings[random_input_idx].view(encodings.shape)
134
- if random_input.shape[0] < self.codebook_size:
135
- random_input = torch.cat([random_input]*(self.codebook_size // random_input.shape[0] + 1), 0)
136
- random_input = random_input[:self.codebook_size,:].contiguous() # codebook_size, dim
137
-
138
- self.codebook.weight.data = self.codebook.weight.data * (1 - replace_code).unsqueeze(-1) + random_input * replace_code.unsqueeze(-1)
139
- self.stale_counter = self.stale_counter * (1 - replace_code)
140
-
141
- return z_q, indices
142
-
143
-
144
- class ResidualVectorQuantize(nn.Module):
145
- """
146
- Introduced in SoundStream: An end2end neural audio codec
147
- https://arxiv.org/abs/2107.03312
148
- """
149
-
150
- def __init__(
151
- self,
152
- input_dim: int = 512,
153
- n_codebooks: int = 9,
154
- codebook_size: int = 1024,
155
- codebook_dim: Union[int, list] = 8,
156
- quantizer_dropout: float = 0.0,
157
- stale_tolerance: int = 100,
158
- use_multi_layer_num:int = 1,
159
- ):
160
- super().__init__()
161
- if isinstance(codebook_dim, int):
162
- codebook_dim = [codebook_dim for _ in range(n_codebooks)]
163
-
164
- self.n_codebooks = n_codebooks
165
- self.codebook_dim = codebook_dim
166
- self.codebook_size = codebook_size
167
-
168
- self.quantizers = nn.ModuleList(
169
- [
170
- VectorQuantize(input_dim, codebook_size, codebook_dim[i], stale_tolerance=stale_tolerance, n_layer=use_multi_layer_num)
171
- for i in range(n_codebooks)
172
- ]
173
- )
174
- self.quantizer_dropout = quantizer_dropout
175
-
176
- def forward(self, z, n_quantizers: int = None):
177
- """Quantized the input tensor using a fixed set of `n` codebooks and returns
178
- the corresponding codebook vectors
179
- Parameters
180
- ----------
181
- z : Tensor[B x D x T]
182
- n_quantizers : int, optional
183
- No. of quantizers to use
184
- (n_quantizers < self.n_codebooks ex: for quantizer dropout)
185
- Note: if `self.quantizer_dropout` is True, this argument is ignored
186
- when in training mode, and a random number of quantizers is used.
187
- Returns
188
- -------
189
- dict
190
- A dictionary with the following keys:
191
-
192
- "z" : Tensor[B x D x T]
193
- Quantized continuous representation of input
194
- "codes" : Tensor[B x N x T]
195
- Codebook indices for each codebook
196
- (quantized discrete representation of input)
197
- "latents" : Tensor[B x N*D x T]
198
- Projected latents (continuous representation of input before quantization)
199
- "vq/commitment_loss" : Tensor[1]
200
- Commitment loss to train encoder to predict vectors closer to codebook
201
- entries
202
- "vq/codebook_loss" : Tensor[1]
203
- Codebook loss to update the codebook
204
- """
205
- z_q = 0
206
- residual = z
207
- commitment_loss = 0
208
- codebook_loss = 0
209
-
210
- codebook_indices = []
211
- latents = []
212
-
213
- if n_quantizers is None:
214
- n_quantizers = self.n_codebooks
215
- if self.training:
216
- n_quantizers = torch.ones((z.shape[0],)) * self.n_codebooks + 1
217
- dropout = torch.randint(1, self.n_codebooks + 1, (z.shape[0],))
218
- n_dropout = int(z.shape[0] * self.quantizer_dropout)
219
- n_quantizers[:n_dropout] = dropout[:n_dropout]
220
- n_quantizers = n_quantizers.to(z.device)
221
- else:
222
- n_quantizers = torch.ones((z.shape[0],)) * n_quantizers + 1
223
- n_quantizers = n_quantizers.to(z.device)
224
-
225
- for i, quantizer in enumerate(self.quantizers):
226
- # if self.training is False and i >= n_quantizers:
227
- # break
228
-
229
- z_q_i, commitment_loss_i, codebook_loss_i, indices_i, z_e_i = quantizer(
230
- residual
231
- )
232
-
233
- # Create mask to apply quantizer dropout
234
- mask = (
235
- torch.full((z.shape[0],), fill_value=i, device=z.device) < n_quantizers
236
- )
237
- z_q = z_q + z_q_i * mask[:, None, None]
238
- residual = residual - z_q_i
239
-
240
- # Sum losses
241
- commitment_loss += (commitment_loss_i * mask).mean()
242
- codebook_loss += (codebook_loss_i * mask).mean()
243
-
244
- codebook_indices.append(indices_i)
245
- latents.append(z_e_i)
246
-
247
- codes = torch.stack(codebook_indices, dim=1)
248
- latents = torch.cat(latents, dim=1)
249
-
250
- encodings = F.one_hot(codes, self.codebook_size).float() # B N T 1024
251
-
252
- return z_q, codes, latents, commitment_loss, codebook_loss, n_quantizers.clamp(max=self.n_codebooks).long() - 1
253
-
254
- def get_loss(self, x, quantized_prompt_embeds, commitment_loss, codebook_loss):
255
- final_loss = commitment_loss * 0.25 + codebook_loss * 1.0 + (x - quantized_prompt_embeds).abs().mean()
256
- return final_loss
257
-
258
- def from_codes(self, codes: torch.Tensor):
259
- """Given the quantized codes, reconstruct the continuous representation
260
- Parameters
261
- ----------
262
- codes : Tensor[B x N x T]
263
- Quantized discrete representation of input
264
- Returns
265
- -------
266
- Tensor[B x D x T]
267
- Quantized continuous representation of input
268
- """
269
- z_q = 0.0
270
- z_p = []
271
- n_codebooks = codes.shape[1]
272
- for i in range(n_codebooks):
273
- z_p_i = self.quantizers[i].decode_code(codes[:, i, :])
274
- z_p.append(z_p_i)
275
-
276
- z_q_i = self.quantizers[i].out_proj(z_p_i)
277
- z_q = z_q + z_q_i
278
- return z_q, torch.cat(z_p, dim=1), codes
279
-
280
- def from_latents(self, latents: torch.Tensor):
281
- """Given the unquantized latents, reconstruct the
282
- continuous representation after quantization.
283
-
284
- Parameters
285
- ----------
286
- latents : Tensor[B x N x T]
287
- Continuous representation of input after projection
288
-
289
- Returns
290
- -------
291
- Tensor[B x D x T]
292
- Quantized representation of full-projected space
293
- Tensor[B x D x T]
294
- Quantized representation of latent space
295
- """
296
- z_q = 0
297
- z_p = []
298
- codes = []
299
- dims = np.cumsum([0] + [q.codebook_dim for q in self.quantizers])
300
-
301
- n_codebooks = np.where(dims <= latents.shape[1])[0].max(axis=0, keepdims=True)[
302
- 0
303
- ]
304
- for i in range(n_codebooks):
305
- j, k = dims[i], dims[i + 1]
306
- z_p_i, codes_i = self.quantizers[i].decode_latents(latents[:, j:k, :])
307
- z_p.append(z_p_i)
308
- codes.append(codes_i)
309
-
310
- z_q_i = self.quantizers[i].out_proj(z_p_i)
311
- z_q = z_q + z_q_i
312
-
313
- return z_q, torch.cat(z_p, dim=1), torch.stack(codes, dim=1)
314
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq/muq.py DELETED
@@ -1,90 +0,0 @@
1
- import torch.nn as nn
2
- import torch
3
- from .models.muq_model import MuQModel
4
- from dataclasses import dataclass, field
5
- from typing import List, Optional
6
- from transformers.modeling_outputs import BaseModelOutput
7
- from huggingface_hub import PyTorchModelHubMixin
8
-
9
- @dataclass
10
- class MuQConfig:
11
- label_rate:int = field(default=25)
12
- num_codebooks:int = field(default=1)
13
- codebook_dim:int = field(default=16)
14
- codebook_size:int = field(default=4096)
15
- features:List[str] = field(default_factory=lambda:["melspec_2048"])
16
- hop_length:int = field(default=240)
17
- n_mels:int = field(default=128)
18
- conv_dim:int = field(default=512)
19
- encoder_dim:int = field(default=1024)
20
- encoder_depth:int = field(default=12)
21
- mask_hop:float = field(default=0.4)
22
- mask_prob:float = field(default=0.6)
23
- is_flash:bool = field(default=False)
24
- stat:Optional[dict] = field(default_factory=dict)
25
- w2v2_config:Optional[dict] = field(default_factory=dict)
26
- use_rvq_target:bool = field(default=False)
27
- use_vq_target:bool = field(default=False)
28
- use_encodec_target:bool = field(default=False)
29
- rvq_ckpt_path: Optional[str] = field(default=None)
30
- recon_loss_ratio: Optional[float] = field(default=None)
31
- resume_checkpoint: Optional[str] = None
32
- rvq_n_codebooks:int = field(default=8)
33
- rvq_multi_layer_num:int = field(default=1)
34
-
35
- class MuQ(nn.Module, PyTorchModelHubMixin):
36
- def __init__(self, config: MuQConfig):
37
- super().__init__()
38
- if isinstance(config, dict):
39
- config = MuQConfig(**config)
40
- self.config = config
41
- self.model = MuQModel(
42
- num_codebooks=config.num_codebooks,
43
- codebook_dim=config.codebook_dim,
44
- codebook_size=config.codebook_size,
45
- features=config.features,
46
- hop_length=config.hop_length,
47
- n_mels=config.n_mels,
48
- conv_dim=config.conv_dim,
49
- encoder_dim=config.encoder_dim,
50
- encoder_depth=config.encoder_depth,
51
- mask_hop=config.mask_hop,
52
- mask_prob=config.mask_prob,
53
- is_flash=config.is_flash,
54
- stat=config.stat,
55
- w2v2_config=config.w2v2_config,
56
- use_rvq_target=config.use_rvq_target,
57
- use_vq_target=config.use_vq_target,
58
- use_encodec_target=config.use_encodec_target,
59
- rvq_ckpt_path=config.rvq_ckpt_path,
60
- recon_loss_ratio=config.recon_loss_ratio,
61
- label_rate=config.label_rate,
62
- rvq_n_codebooks=config.rvq_n_codebooks,
63
- rvq_multi_layer_num=config.rvq_multi_layer_num,
64
- )
65
-
66
- def forward(self, x, attention_mask:Optional[torch.Tensor]=None, output_hidden_states:bool=True) ->BaseModelOutput:
67
- """
68
- Forward pass through the MuQ model and extract features.
69
-
70
- Args:
71
- x (torch.Tensor): Input waveform tensor of shape (batch_size, time).
72
- attention_mask (torch.Tensor, optional): Mask to avoid performing attention on padding token indices.
73
- Default is None.
74
- output_hidden_states (bool, optional): Whether to return all hidden states or only the last one.
75
- Default is False.
76
-
77
- Returns:
78
- BaseModelOutput: An object containing the last hidden state and optionally all hidden states.
79
- - last_hidden_state (torch.Tensor): The last hidden state of the model, i.e. extracted MuQ features, of shape (batch_size, sequence_length, hidden_size).
80
- - hidden_states (tuple(torch.Tensor), optional): A tuple containing all hidden states produced by the model,
81
- each of shape (batch_size, sequence_length, hidden_size). Only returned if output_hidden_states is True.
82
- """
83
- _, hidden_states = self.model.get_predictions(x, attention_mask=attention_mask, is_features_only=True)
84
- last_hidden_state = hidden_states[-1]
85
- if not output_hidden_states:
86
- return BaseModelOutput(last_hidden_state=last_hidden_state)
87
- return BaseModelOutput(
88
- last_hidden_state=last_hidden_state,
89
- hidden_states=hidden_states
90
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/__init__.py DELETED
@@ -1 +0,0 @@
1
- from .muq_mulan import MuQMuLan, MuQMuLanConfig, MuLanConfig, ModalModelConfig, TextTransformerConfig, AudioTransformerConfig
 
 
src/third_party/MuQ/src/muq/muq_mulan/models/__init__.py DELETED
File without changes
src/third_party/MuQ/src/muq/muq_mulan/models/audio.py DELETED
@@ -1,294 +0,0 @@
1
- from contextlib import suppress
2
- import torch
3
- import torch.nn as nn
4
- from einops import rearrange, repeat, reduce
5
- from einops.layers.torch import Rearrange
6
- from torchaudio.transforms import Spectrogram, TimeStretch, FrequencyMasking, TimeMasking
7
- from transformers import Wav2Vec2FeatureExtractor,AutoModel
8
- from ..modules.transformer import Transformer, LayerNorm, posemb_sincos_2d
9
- from ..modules.utils import print_once, round_down_nearest_multiple, frozen_params, Sequential
10
-
11
-
12
- def pair(t):
13
- return (t, t) if not isinstance(t, tuple) else t
14
-
15
- class AudioSpectrogramTransformer(nn.Module):
16
- def __init__(
17
- self,
18
- dim,
19
- depth,
20
- patch_size = 16,
21
- dim_head = 64,
22
- heads = 8,
23
- attn_dropout = 0.,
24
- ff_mult = 4,
25
- ff_dropout = 0.,
26
- accept_spec = False,
27
- accept_spec_time_first = True,
28
- spec_n_fft = 128,
29
- spec_power = 2,
30
- spec_win_length = 24,
31
- spec_hop_length = None,
32
- spec_pad = 0,
33
- spec_center = True,
34
- spec_pad_mode = 'reflect',
35
- spec_aug_stretch_factor = 0.8,
36
- spec_aug_freq_mask = 80,
37
- spec_aug_time_mask = 80,
38
- patch_dropout_prob = 0.25
39
- ):
40
- super().__init__()
41
- self.dim = dim
42
- self.depth = depth
43
-
44
- self.patch_size = pair(patch_size)
45
- patch_input_dim = self.patch_size[0] * self.patch_size[1]
46
-
47
- self.to_patch_tokens = Sequential(
48
- Rearrange('b (h p1) (w p2) -> b h w (p1 p2)', p1 = self.patch_size[0], p2 = self.patch_size[1]),
49
- nn.LayerNorm(patch_input_dim),
50
- nn.Linear(patch_input_dim, dim),
51
- nn.LayerNorm(dim)
52
- )
53
-
54
- self.accept_spec = accept_spec
55
- self.accept_spec_time_first = accept_spec_time_first
56
-
57
- self.spec = Spectrogram(
58
- n_fft = spec_n_fft,
59
- power = spec_power,
60
- win_length = spec_win_length,
61
- hop_length = spec_hop_length,
62
- pad = spec_pad,
63
- center = spec_center,
64
- pad_mode = spec_pad_mode
65
- )
66
-
67
- # SpecAugment - seems to be widely used in audio field https://arxiv.org/abs/1904.08779
68
-
69
- self.aug = torch.nn.Sequential(
70
- TimeStretch(spec_aug_stretch_factor, fixed_rate = True),
71
- FrequencyMasking(freq_mask_param = spec_aug_freq_mask),
72
- TimeMasking(time_mask_param = spec_aug_time_mask),
73
- )
74
-
75
- self.transformer = Transformer(
76
- dim = dim,
77
- depth = depth,
78
- dim_head = dim_head,
79
- heads = heads,
80
- attn_dropout = attn_dropout,
81
- ff_mult = ff_mult,
82
- ff_dropout = ff_dropout
83
- )
84
-
85
- self.norm = LayerNorm(dim)
86
-
87
- # patch dropout
88
-
89
- self.patch_dropout_prob = patch_dropout_prob
90
-
91
- # 2d dynamic positional bias
92
-
93
- mlp_hidden_dim = dim // 4
94
-
95
- self.dynamic_pos_bias_mlp = nn.Sequential(
96
- nn.Linear(2, mlp_hidden_dim),
97
- nn.SiLU(),
98
- nn.Linear(mlp_hidden_dim, mlp_hidden_dim),
99
- nn.SiLU(),
100
- nn.Linear(mlp_hidden_dim, heads),
101
- Rearrange('... i j h -> ... h i j')
102
- )
103
-
104
- def forward(
105
- self,
106
- x,
107
- force_no_patch_dropout = False,
108
- return_all_layers = False
109
- ):
110
- batch, device = x.shape[0], x.device
111
- assert (self.accept_spec and x.ndim == 3) or (not self.accept_spec and x.ndim == 2)
112
-
113
- if self.accept_spec and self.accept_spec_time_first:
114
- x = rearrange(x, 'b t f -> b f t')
115
-
116
- if not self.accept_spec:
117
- x = self.spec(x)
118
-
119
- if self.training:
120
- x = self.aug(x)
121
-
122
- # automatically crop if audio does not yield a 2d spectrogram that is divisible by patch sizes
123
-
124
- height, width = x.shape[-2:]
125
- patch_height, patch_width = self.patch_size
126
-
127
- rounded_height, rounded_width = map(lambda args: round_down_nearest_multiple(*args), ((height, patch_height), (width, patch_width)))
128
-
129
- if (height, width) != (rounded_height, rounded_width): # just keep printing to be annoying until it is fixed
130
- print_once(f'spectrogram yielded shape of {(height, width)}, but had to be cropped to {(rounded_height, rounded_width)} to be patchified for transformer')
131
-
132
- x = x[..., :rounded_height, :rounded_width]
133
-
134
- # to patches
135
-
136
- x = self.to_patch_tokens(x)
137
-
138
- # get number of patches along height and width
139
-
140
- _, num_patch_height, num_patch_width, _ = x.shape
141
-
142
- # get 2d relative positions
143
-
144
- grid = torch.stack(torch.meshgrid(
145
- torch.arange(num_patch_height, device = device),
146
- torch.arange(num_patch_width, device = device)
147
- , indexing = 'ij'), dim = -1)
148
-
149
- grid = rearrange(grid, '... c -> (...) c')
150
-
151
- # 2d sinusoidal positional embedding
152
-
153
- x = x + posemb_sincos_2d(x)
154
-
155
- x = rearrange(x, 'b ... c -> b (...) c')
156
-
157
- # patch dropout
158
-
159
- if self.training and self.patch_dropout_prob > 0. and not force_no_patch_dropout:
160
- n, device = x.shape[1], x.device
161
-
162
- batch_indices = torch.arange(batch, device = device)
163
- batch_indices = rearrange(batch_indices, '... -> ... 1')
164
- num_patches_keep = max(1, int(n * (1 - self.patch_dropout_prob)))
165
- patch_indices_keep = torch.randn(batch, n, device = device).topk(num_patches_keep, dim = -1).indices
166
-
167
- x = x[batch_indices, patch_indices_keep]
168
-
169
- grid = repeat(grid, '... -> b ...', b = batch)
170
- grid = grid[batch_indices, patch_indices_keep]
171
-
172
- # 2d relative positional bias
173
-
174
- rel_dist = rearrange(grid, '... i c -> ... i 1 c') - rearrange(grid, '... j c -> ... 1 j c')
175
- rel_pos_bias = self.dynamic_pos_bias_mlp(rel_dist.float())
176
-
177
- # attention, what else
178
-
179
- x, all_layers = self.transformer(x, rel_pos_bias = rel_pos_bias, return_all_layers = True)
180
-
181
- # final global average and norm (most recent papers show this is superior to CLS token)
182
-
183
- x = reduce(x, 'b n d -> b d', 'mean')
184
-
185
- out = self.norm(x)
186
-
187
- if not return_all_layers:
188
- return out
189
-
190
- return out, all_layers
191
-
192
- class AudioSpectrogramTransformerPretrained(nn.Module):
193
- def __init__(
194
- self,
195
- model_name = 'm-a-p/MERT-v1-330M',
196
- dim = 768,
197
- model_dim = 1024,
198
- sr = 24000,
199
- tf_depth = 12,
200
- dim_head = 64,
201
- heads = 8,
202
- attn_dropout = 0.,
203
- ff_dropout = 0.,
204
- ff_mult = 4,
205
- use_layer_idx = -1,
206
- frozen_pretrained = True,
207
- hf_hub_cache_dir = None,
208
- ):
209
- super().__init__()
210
- self.model_name = model_name
211
- self.dim = dim
212
- self.sr = sr
213
- self.use_layer_idx = use_layer_idx # Which layer's features should be used
214
- self.hf_hub_cache_dir = hf_hub_cache_dir
215
-
216
- self.model_name
217
-
218
- self._init_pretrained_model(model_name)
219
-
220
- self.aggregator = nn.Conv1d(in_channels=25, out_channels=1, kernel_size=1)
221
-
222
-
223
- self.transformer = Transformer(
224
- dim = dim,
225
- depth = tf_depth,
226
- dim_head = dim_head,
227
- heads = heads,
228
- attn_dropout = attn_dropout,
229
- ff_dropout = ff_dropout,
230
- ff_mult = ff_mult
231
- ) # if tf_depth > 0 else torch.nn.Identity()
232
-
233
- self.proj = nn.Linear(model_dim, dim)
234
-
235
- if frozen_pretrained:
236
- frozen_params(self.model)
237
- frozen_params(self.aggregator)
238
- self.frozen_pretrained = frozen_pretrained
239
-
240
- def _init_pretrained_model(self, model_name):
241
- if 'muq' in model_name.lower():
242
- from muq import MuQ
243
- self.model = MuQ.from_pretrained(model_name, cache_dir=self.hf_hub_cache_dir)
244
- else:
245
- self.model = AutoModel.from_pretrained(model_name, trust_remote_code=True, cache_dir=self.hf_hub_cache_dir)
246
- self.processor = Wav2Vec2FeatureExtractor.from_pretrained(model_name,trust_remote_code=True, cache_dir=self.hf_hub_cache_dir)
247
-
248
- assert self.processor.sampling_rate == self.sr
249
-
250
- @property
251
- def device(self):
252
- return next(self.model.parameters()).device
253
-
254
- @property
255
- def dtype(self):
256
- return next(self.model.parameters()).dtype
257
-
258
- def _forward_pretrained_model(self, x):
259
- if 'muq' in self.model_name.lower():
260
- outputs = self.model(x, output_hidden_states=True)
261
- return outputs.hidden_states # 13 layer x [batch_size, Time steps, 1024 feature_dim]
262
- else:
263
- inputs = self.processor(x, sampling_rate=self.sr, return_tensors="pt")
264
- input_values = inputs['input_values'].squeeze(0).to(self.device, dtype = self.dtype)
265
- outputs = self.model(input_values, output_hidden_states=True) # [25 layer, batch_size, Time steps, 1024 feature_dim]
266
- return outputs.hidden_states
267
-
268
- def forward(
269
- self,
270
- x,
271
- return_all_layers = False,
272
- return_mean = True,
273
- no_proj = False,
274
- ):
275
- batch, device = x.shape[0], x.device
276
-
277
- with torch.no_grad() if self.frozen_pretrained else suppress():
278
- outputs = self._forward_pretrained_model(x)
279
- layer_hidden_states = outputs[self.use_layer_idx]
280
-
281
- if no_proj:
282
- outputs = layer_hidden_states
283
- else:
284
- outputs = self.proj(layer_hidden_states)
285
- outputs, layer_results = self.transformer(outputs, return_all_layers=True)
286
-
287
- if return_mean:
288
- outputs = outputs.mean(dim = -2)
289
-
290
- if return_all_layers:
291
- return outputs, layer_results
292
- return outputs
293
-
294
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/models/mulan.py DELETED
@@ -1,148 +0,0 @@
1
- import math
2
- from typing import List, Optional, Union
3
- from collections import OrderedDict
4
- from functools import partial
5
-
6
- import torch
7
- from torch import nn, einsum
8
-
9
- from .audio import AudioSpectrogramTransformer, AudioSpectrogramTransformerPretrained
10
- from .text import TextTransformer, TextTransformerPretrained
11
- from ..modules.contrastive import RankSoftmaxContrastiveLearning, SoftmaxContrastiveLearning, SigmoidContrastiveLearning, MultiLayerContrastiveLoss, interspersed_indices
12
- from ..modules.utils import exists, default, l2norm
13
-
14
- class MuLanModel(nn.Module):
15
- def __init__(
16
- self,
17
- audio_transformer: Union[AudioSpectrogramTransformer, AudioSpectrogramTransformerPretrained],
18
- text_transformer: Union[TextTransformer, TextTransformerPretrained],
19
- dim_latent = 128, # they use 128
20
- decoupled_contrastive_learning = True, # think this was used, make it optional
21
- hierarchical_contrastive_loss = False,
22
- hierarchical_contrastive_loss_layers = None,
23
- sigmoid_contrastive_loss = False,
24
- rank_contrast = False, # apply contrast on rank dimension
25
- proj_to_latent = True,
26
- norm_type = 'l2norm',
27
- **kwargs,
28
- ):
29
- super().__init__()
30
- self.dim_latent = dim_latent
31
-
32
- # audio and text transformer
33
- self.audio = audio_transformer
34
- self.text = text_transformer
35
-
36
- # two linear layers to project embeddings to latent space
37
- if proj_to_latent:
38
- self.text_to_latents = nn.Linear(self.text.dim, dim_latent)
39
- self.audio_to_latents = nn.Linear(self.audio.dim, dim_latent)
40
-
41
- self.sigmoid_contrastive_loss = sigmoid_contrastive_loss
42
- self.decoupled_contrastive_learning = decoupled_contrastive_learning
43
- self.rank_contrast = rank_contrast
44
- self.norm_type = norm_type
45
-
46
- # use decoupled contrastive learning or not, where self.contrast is loss module for contrastive learning
47
- if sigmoid_contrastive_loss:
48
- klass = SigmoidContrastiveLearning
49
- else:
50
- if rank_contrast:
51
- klass = partial(RankSoftmaxContrastiveLearning, decoupled_contrastive_learning = decoupled_contrastive_learning)
52
- else:
53
- klass = partial(SoftmaxContrastiveLearning, decoupled_contrastive_learning = decoupled_contrastive_learning)
54
-
55
- self.contrast = klass()
56
-
57
- self.multi_layer_contrastive_learning = None
58
-
59
- if hierarchical_contrastive_loss:
60
- num_layers = default(hierarchical_contrastive_loss_layers, min(audio_transformer.depth, text_transformer.depth) - 1)
61
- assert num_layers > 0
62
-
63
- self.register_buffer('text_layers_indices', interspersed_indices(num_layers, text_transformer.depth))
64
- self.register_buffer('audio_layers_indices', interspersed_indices(num_layers, audio_transformer.depth))
65
-
66
- self.multi_layer_contrastive_learning = MultiLayerContrastiveLoss(
67
- audio_dim = self.audio.dim,
68
- text_dim = self.text.dim,
69
- dim_latent = dim_latent,
70
- layers = num_layers,
71
- decoupled_contrastive_learning = decoupled_contrastive_learning,
72
- sigmoid_contrastive_loss = sigmoid_contrastive_loss
73
- )
74
-
75
- def get_audio_latents(
76
- self,
77
- wavs,
78
- return_all_layers = False,
79
- ):
80
- audio_embeds, audio_layers = self.audio(wavs, return_all_layers = True)
81
- audio_latents = self.audio_to_latents(audio_embeds)
82
- out = self._norm_latents(audio_latents) #->[Batch, Feat=128]
83
-
84
- if not return_all_layers:
85
- return out
86
-
87
- return out, audio_layers #[nLayer=5, Batch=2, 15, 512]
88
-
89
- def get_text_latents(
90
- self,
91
- texts = None,
92
- raw_texts: Optional[List[str]] = None,
93
- return_all_layers = False
94
- ):
95
- text_embeds, text_layers = self.text(texts, raw_texts = raw_texts, return_all_layers = True)
96
- text_latents = self.text_to_latents(text_embeds)
97
- out = self._norm_latents(text_latents)
98
-
99
- if not return_all_layers:
100
- return out
101
-
102
- return out, text_layers
103
-
104
- def _norm_latents(self, latents):
105
- if self.norm_type == 'l2norm':
106
- return l2norm(latents)
107
- else:
108
- return self.norm(latents)
109
-
110
- def forward(
111
- self,
112
- wavs,
113
- texts = None,
114
- raw_texts: Optional[List[str]] = None,
115
- return_latents = False,
116
- return_similarities = False,
117
- return_pairwise_similarities = False,
118
- ):
119
- batch, device = wavs.shape[0], wavs.device
120
-
121
- # both latents are of [Batch, Feat=128]
122
- audio_latents, audio_layers = self.get_audio_latents(wavs, return_all_layers = True)
123
- text_latents, text_layers = self.get_text_latents(texts, raw_texts = raw_texts, return_all_layers = True)
124
-
125
- if return_latents: # used in inference
126
- return audio_latents, text_latents
127
-
128
- if return_similarities:
129
- return einsum('i d, i d -> i', audio_latents, text_latents)
130
-
131
- if return_pairwise_similarities:
132
- cosine_sim = einsum('i d, j d -> i j', audio_latents, text_latents)
133
- return cosine_sim
134
-
135
- cl_loss = self.contrast(audio_latents, text_latents) #contrastive loss
136
-
137
- if not exists(self.multi_layer_contrastive_learning):
138
- return cl_loss
139
-
140
- audio_layers = audio_layers[self.audio_layers_indices]
141
- text_layers = text_layers[self.text_layers_indices]
142
-
143
- hierarchical_cl_loss = self.multi_layer_contrastive_learning(
144
- audio_layers = audio_layers,
145
- text_layers = text_layers
146
- )
147
-
148
- return cl_loss + hierarchical_cl_loss
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/models/text.py DELETED
@@ -1,241 +0,0 @@
1
- from typing import Optional, List
2
-
3
- import torch
4
- import torch.nn as nn
5
- import torch.nn.functional as F
6
- from x_clip.tokenizer import tokenizer
7
- from einops import rearrange, repeat, reduce, pack, unpack
8
- from transformers import AutoTokenizer,XLMRobertaModel,AutoModelForCausalLM
9
-
10
- from ..modules.utils import *
11
- from ..modules.transformer import Transformer, LayerNorm
12
- from ..modules.utils import frozen_params
13
-
14
-
15
- # text transformer
16
-
17
- class TextTransformer(nn.Module):
18
- def __init__(
19
- self,
20
- dim,
21
- depth,
22
- num_tokens = tokenizer.vocab_size,
23
- max_seq_len = 256,
24
- dim_head = 64,
25
- heads = 8,
26
- attn_dropout = 0.,
27
- ff_dropout = 0.,
28
- ff_mult = 4,
29
- pad_id = 0
30
- ):
31
- super().__init__()
32
- self.dim = dim
33
-
34
- self.token_emb = nn.Embedding(num_tokens, dim)
35
- self.pos_emb = nn.Embedding(max_seq_len, dim)
36
-
37
- self.depth = depth
38
- self.max_seq_len = max_seq_len
39
-
40
- self.cls_token = nn.Parameter(torch.randn(dim))
41
-
42
- self.transformer = Transformer(
43
- dim = dim,
44
- depth = depth,
45
- dim_head = dim_head,
46
- heads = heads,
47
- attn_dropout = attn_dropout,
48
- ff_dropout = ff_dropout,
49
- ff_mult = ff_mult
50
- )
51
-
52
- self.pad_id = pad_id
53
- self.norm = LayerNorm(dim)
54
-
55
- @property
56
- def device(self):
57
- return next(self.parameters()).device
58
-
59
- def forward(
60
- self,
61
- x = None,
62
- raw_texts: Optional[List[str]] = None,
63
- mask = None,
64
- return_all_layers = False
65
- ):
66
- assert exists(x) ^ exists(raw_texts)
67
-
68
- if exists(raw_texts):
69
- x = tokenizer.tokenize(raw_texts).to(self.device)
70
-
71
- if not exists(mask):
72
- mask = x != self.pad_id
73
-
74
- b, n, device = *x.shape, x.device
75
-
76
- # token embedding + positional embedding
77
-
78
- x = self.token_emb(x)
79
-
80
- assert n <= self.max_seq_len, f'text sequence length {n} must be less than {self.max_seq_len}'
81
-
82
- x = x + self.pos_emb(torch.arange(n, device = device))
83
-
84
- # cls tokens, as in bert
85
-
86
- cls_tokens = repeat(self.cls_token, 'd -> b d', b = b)
87
- x, ps = pack([cls_tokens, x], 'b * d')
88
-
89
- # account for attending to cls token with self attention mask
90
-
91
- mask = F.pad(mask, (1, 0), value = True)
92
-
93
- # attention
94
-
95
- x, all_layers = self.transformer(x, mask = mask, return_all_layers = True)
96
-
97
- # unpack the cls tokens
98
-
99
- cls_tokens, _ = unpack(x, ps, 'b * d')
100
-
101
- out = self.norm(cls_tokens)
102
-
103
- if not return_all_layers:
104
- return out
105
-
106
- return out, all_layers
107
-
108
- class TextPretrainedModelType:
109
- Roberta = 'roberta'
110
- Qwen = 'qwen'
111
-
112
- class TextTransformerPretrained(nn.Module):
113
- def __init__(
114
- self,
115
- model_name = 'xlm-roberta-base',
116
- dim = 768,
117
- model_dim = None,
118
- max_seq_len = 256,
119
- tf_depth = 12,
120
- dim_head = 64,
121
- heads = 8,
122
- attn_dropout = 0.,
123
- ff_dropout = 0.,
124
- ff_mult = 4,
125
- frozen_pretrained = True,
126
- hf_hub_cache_dir = None,
127
- ):
128
- super().__init__()
129
- self.dim = dim
130
-
131
- self.model_name = model_name
132
-
133
- self.hf_hub_cache_dir = hf_hub_cache_dir
134
-
135
- self.pretrained_model_type = self._get_pretrained_model_type(model_name)
136
-
137
- self.model = self._init_pretrained_model()
138
-
139
- self._tokenizer = None
140
-
141
- self.max_seq_len = max_seq_len
142
-
143
- self.transformer = Transformer(
144
- dim = dim,
145
- depth = tf_depth,
146
- dim_head = dim_head,
147
- heads = heads,
148
- attn_dropout = attn_dropout,
149
- ff_dropout = ff_dropout,
150
- ff_mult = ff_mult
151
- ) # if tf_depth > 0 else torch.nn.Identity()
152
-
153
- is_proj = exists(model_dim) and model_dim != dim
154
-
155
- self.proj = nn.Linear(model_dim, dim) if is_proj else torch.nn.Identity()
156
- if frozen_pretrained:
157
- frozen_params(self.model)
158
- self.frozen_pretrained = frozen_pretrained
159
-
160
- @staticmethod
161
- def _get_pretrained_model_type(model_name):
162
- if 'xlm-roberta' in model_name:
163
- return TextPretrainedModelType.Roberta
164
- elif 'Qwen' in model_name:
165
- return TextPretrainedModelType.Qwen
166
- else:
167
- raise ValueError(f"Unknown pretrained model named: {model_name}")
168
-
169
- def _init_pretrained_model(self):
170
- if self.pretrained_model_type == TextPretrainedModelType.Roberta:
171
- model = XLMRobertaModel.from_pretrained(self.model_name, trust_remote_code=True, cache_dir=self.hf_hub_cache_dir)
172
- elif self.pretrained_model_type == TextPretrainedModelType.Qwen:
173
- model = AutoModelForCausalLM.from_pretrained(self.model_name, trust_remote_code=True, fp16=True, cache_dir=self.hf_hub_cache_dir)
174
- else:
175
- raise ValueError(f"Failed to init pretrained model type: {self.pretrained_model_type}")
176
- return model
177
-
178
- def _init_tokenizer(self):
179
- if self.pretrained_model_type == TextPretrainedModelType.Roberta:
180
- tokenizer = AutoTokenizer.from_pretrained(self.model_name, trust_remote_code=True, cache_dir=self.hf_hub_cache_dir)
181
- elif self.pretrained_model_type == TextPretrainedModelType.Qwen:
182
- tokenizer = AutoTokenizer.from_pretrained(self.model_name, trust_remote_code=True, cache_dir=self.hf_hub_cache_dir)
183
- tokenizer.pad_token = '<|im_end|>'
184
- else:
185
- raise ValueError(f"Failed to init tokenizer of pretrained model type: {self.pretrained_model_type}")
186
- return tokenizer
187
-
188
- @property
189
- def tokenizer(self):
190
- if not exists(self._tokenizer):
191
- self._tokenizer = self._init_tokenizer()
192
- return self._tokenizer
193
-
194
- @property
195
- def device(self):
196
- return next(self.model.parameters()).device
197
-
198
- @property
199
- def dtype(self):
200
- return next(self.transformer.parameters()).dtype
201
-
202
-
203
- def pred_pretrained_model_hidden(self, **kw):
204
- if self.pretrained_model_type == TextPretrainedModelType.Roberta:
205
- outputs = self.model(**kw)
206
- outputs = outputs.last_hidden_state
207
- elif self.pretrained_model_type == TextPretrainedModelType.Qwen:
208
- last_hidden_state = self.model(**kw, output_hidden_states=True)['hidden_states'][-1]
209
- outputs = last_hidden_state.to(dtype = self.dtype)
210
- else:
211
- raise ValueError(f"Unknown pretrained model type: {self.pretrained_model_type}")
212
- return outputs
213
-
214
- def forward(
215
- self,
216
- x = None,
217
- raw_texts: Optional[List[str]] = None,
218
- mask = None,
219
- return_all_layers = False,
220
- return_mean = True
221
- ):
222
- assert exists(x) ^ exists(raw_texts)
223
- with torch.no_grad():
224
- if exists(raw_texts):
225
- inputs = self.tokenizer(raw_texts, return_tensors='pt', padding=True)
226
- inputs = inputs.to(self.device)
227
-
228
- if exists(mask):
229
- inputs['attention_mask'] = mask
230
-
231
- outputs = self.pred_pretrained_model_hidden(**inputs)
232
-
233
- outputs = self.proj(outputs)
234
-
235
- outputs, layer_results = self.transformer(outputs, return_all_layers=True)
236
- if return_mean:
237
- outputs = outputs.mean(dim = -2)
238
-
239
- if return_all_layers:
240
- return outputs, layer_results
241
- return outputs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/modules/__init__.py DELETED
File without changes
src/third_party/MuQ/src/muq/muq_mulan/modules/contrastive.py DELETED
@@ -1,238 +0,0 @@
1
- import math
2
- from functools import partial
3
-
4
- import torch.nn as nn
5
- import torch
6
- import torch.nn.functional as F
7
- from einops import rearrange, reduce
8
- from torch import einsum
9
- import torch.distributed as dist
10
-
11
- from .utils import exists, l2norm, log, print_once
12
- from .distributed import AllGather
13
- from .extend_distributed import all_gather
14
- from .transformer import LayerNorm
15
-
16
- def matrix_diag(t):
17
- device = t.device
18
- i, j = t.shape[-2:]
19
- num_diag_el = min(i, j)
20
- i_range = torch.arange(i, device = device)
21
- j_range = torch.arange(j, device = device)
22
- diag_mask = rearrange(i_range, 'i -> i 1') == rearrange(j_range, 'j -> 1 j')
23
- diag_el = t.masked_select(diag_mask)
24
- return rearrange(diag_el, '(b d) -> b d', d = num_diag_el)
25
-
26
- # contrastive losses
27
-
28
- class SoftmaxContrastiveLearning(nn.Module):
29
- def __init__(
30
- self,
31
- *,
32
- layers = 1,
33
- decoupled_contrastive_learning = False,
34
- init_temp = 10
35
- ):
36
- super().__init__()
37
- self.temperatures = nn.Parameter(torch.ones(layers, 1, 1) * math.log(init_temp))
38
- self.decoupled_contrastive_learning = decoupled_contrastive_learning
39
-
40
- self.all_gather = AllGather(dim = 2)
41
-
42
- @property
43
- def device(self):
44
- return next(self.parameters()).device
45
-
46
- def forward(self, audio_latents, text_latents):
47
- if audio_latents.ndim == 2:
48
- audio_latents = rearrange(audio_latents, '... -> 1 ...')
49
-
50
- if text_latents.ndim == 2:
51
- text_latents = rearrange(text_latents, '... -> 1 ...')
52
-
53
- batch = audio_latents.shape[1]
54
-
55
- if self.all_gather.is_distributed:
56
- latents = torch.stack((audio_latents, text_latents))
57
- latents, _ = self.all_gather(latents)
58
- audio_latents, text_latents = latents
59
-
60
- sims = einsum('l i d, l j d -> l i j', audio_latents, text_latents)
61
-
62
- sims = sims * self.temperatures.exp()
63
-
64
- cosine_sims_exp = sims.exp() # Similarity matrix [Rank, N, N]
65
-
66
- numerator = matrix_diag(cosine_sims_exp) # Take diagonal elements, that is, for t [l, i, j], take all elements of i==j to obtain a array of l * min (i, j)
67
-
68
- if self.decoupled_contrastive_learning:
69
- eye = torch.eye(batch, device = self.device, dtype = torch.bool)
70
- cosine_sims_exp = cosine_sims_exp.masked_fill(eye, 0.) # Set the diagonal to 0
71
-
72
- denominator_i = reduce(cosine_sims_exp, 'l i j -> l i', 'sum')
73
- denominator_j = reduce(cosine_sims_exp, 'l i j -> l j', 'sum')
74
-
75
- contrastive_loss = -log(numerator) + 0.5 * (log(denominator_i) + log(denominator_j))
76
-
77
- contrastive_loss = reduce(contrastive_loss, 'l n -> l', 'mean')
78
- return contrastive_loss.sum()
79
-
80
-
81
- class RankSoftmaxContrastiveLearning(nn.Module):
82
- def __init__(
83
- self,
84
- *,
85
- layers = 1,
86
- decoupled_contrastive_learning = False,
87
- init_temp = 10,
88
- ):
89
- super().__init__()
90
- self.temperatures = nn.Parameter(torch.ones(layers, 1, 1) * math.log(init_temp))
91
- self.decoupled_contrastive_learning = decoupled_contrastive_learning
92
-
93
-
94
- @property
95
- def device(self):
96
- return next(self.parameters()).device
97
-
98
- def forward(self, audio_latents, text_latents):
99
- if audio_latents.ndim == 2:
100
- audio_latents = rearrange(audio_latents, '... -> 1 ...')
101
-
102
- if text_latents.ndim == 2:
103
- text_latents = rearrange(text_latents, '... -> 1 ...')
104
-
105
- audio_latents = all_gather(audio_latents, None)
106
- text_latents = all_gather(text_latents, None)
107
-
108
- print_once("audio_latents:"+str(audio_latents.shape) + "text_latents:" + str(text_latents.shape))
109
-
110
-
111
- batch = audio_latents.shape[1]
112
- rank = audio_latents.shape[0]
113
-
114
- audio_latents = rearrange(audio_latents, 'l i d -> (l i) d')
115
- text_latents = rearrange(text_latents, 'l j d -> (l j) d')
116
-
117
- sims = einsum('i d, j d -> i j', audio_latents, text_latents)
118
-
119
- sims = sims * self.temperatures.exp()
120
-
121
- sims = rearrange(sims, '1 i j -> i j')
122
-
123
- cosine_sims_exp = sims.exp() # Similarity matrix [Rank, N, N]
124
-
125
-
126
- numerator = matrix_diag(cosine_sims_exp) # Take diagonal elements, that is, for t [l, i, j], take all elements of i==j to obtain a array of l * min (i, j)
127
-
128
- if self.decoupled_contrastive_learning:
129
- eye = torch.eye(batch*rank, device = self.device, dtype = torch.bool)
130
- cosine_sims_exp = cosine_sims_exp.masked_fill(eye, 0.) # Set the diagonal to 0
131
-
132
- denominator_i = reduce(cosine_sims_exp, 'i j -> i', 'sum')
133
- denominator_j = reduce(cosine_sims_exp, 'i j -> j', 'sum')
134
-
135
- contrastive_loss = -log(numerator) + 0.5 * (log(denominator_i) + log(denominator_j))
136
-
137
- contrastive_loss = reduce(contrastive_loss, '1 n -> 1', 'mean')
138
- return contrastive_loss
139
-
140
-
141
- class SigmoidContrastiveLearning(nn.Module):
142
- """ https://arxiv.org/abs/2303.15343 """
143
-
144
- def __init__(
145
- self,
146
- *,
147
- layers = 1,
148
- init_temp = 10,
149
- init_bias = -10
150
- ):
151
- super().__init__()
152
- self.temperatures = nn.Parameter(torch.ones(layers, 1, 1) * math.log(init_temp))
153
- self.bias = nn.Parameter(torch.ones(layers, 1, 1) * init_bias)
154
-
155
- self.all_gather = AllGather(dim = 1, all_reduce_grads = True)
156
-
157
- @property
158
- def device(self):
159
- return next(self.parameters()).device
160
-
161
- def forward(self, audio_latents, text_latents):
162
- device = self.device
163
-
164
- if audio_latents.ndim == 2:
165
- audio_latents = rearrange(audio_latents, '... -> 1 ...') # To [Rank, Batch, Latent]
166
-
167
- if text_latents.ndim == 2:
168
- text_latents = rearrange(text_latents, '... -> 1 ...')
169
-
170
- text_latents, rank_sizes = self.all_gather(text_latents)
171
-
172
- n = text_latents.shape[1]
173
-
174
- sims = einsum('l i d, l j d -> l i j', audio_latents, text_latents) # Calculate dot product similarity between pairs
175
-
176
- sims = sims * self.temperatures.exp() + self.bias
177
-
178
- labels = torch.eye(n, device = device)
179
-
180
- if exists(rank_sizes):
181
- labels_by_ranks = labels.split(rank_sizes.tolist(), dim = 0)
182
- labels = labels_by_ranks[dist.get_rank()] # labels to the n elements of the current rank
183
-
184
- labels = 2 * rearrange(labels, 'i j -> 1 i j') - torch.ones_like(sims)
185
-
186
- return -F.logsigmoid(labels * sims).sum() / n
187
-
188
-
189
-
190
-
191
- # hierarchical cl loss
192
-
193
- def interspersed_indices(layers, total_layers):
194
- assert total_layers >= layers
195
- step = total_layers / layers
196
- return (torch.arange(0, layers) * step).floor().long()
197
-
198
- class MultiLayerContrastiveLoss(nn.Module):
199
- def __init__(
200
- self,
201
- *,
202
- audio_dim,
203
- text_dim,
204
- dim_latent,
205
- layers,
206
- decoupled_contrastive_learning = False,
207
- sigmoid_contrastive_loss = False
208
- ):
209
- super().__init__()
210
- self.layers = layers
211
-
212
- self.audio_norm = LayerNorm(audio_dim, scale = False)
213
- self.audio_gamma = nn.Parameter(torch.ones(layers, 1, audio_dim))
214
- self.audio_latent_weight = nn.Parameter(torch.randn(layers, audio_dim, dim_latent))
215
- self.audio_latent_bias = nn.Parameter(torch.randn(layers, 1, dim_latent))
216
-
217
- self.text_norm = LayerNorm(text_dim, scale = False)
218
- self.text_gamma = nn.Parameter(torch.ones(layers, 1, text_dim))
219
- self.text_latent_weight = nn.Parameter(torch.randn(layers, text_dim, dim_latent))
220
- self.text_latent_bias = nn.Parameter(torch.randn(layers, 1, dim_latent))
221
-
222
- klass = SigmoidContrastiveLearning if sigmoid_contrastive_loss else partial(SoftmaxContrastiveLearning, decoupled_contrastive_learning = decoupled_contrastive_learning)
223
- self.contrast = klass(layers = layers)
224
-
225
- def forward(self, *, audio_layers, text_layers):
226
- device, batch = audio_layers.device, audio_layers.shape[1]
227
-
228
- audio_gap = reduce(audio_layers, 'l b n d -> l b d', 'mean')
229
- audio_embeds = self.audio_norm(audio_gap) * self.audio_gamma
230
- audio_latents = einsum('l b d, l d e -> l b e', audio_embeds, self.audio_latent_weight) + self.audio_latent_bias
231
- audio_latents = l2norm(audio_latents)
232
-
233
- text_cls_tokens = text_layers[:, :, 0]
234
- text_embeds = self.text_norm(text_cls_tokens) * self.text_gamma
235
- text_latents = einsum('l b d, l d e -> l b e', text_embeds, self.text_latent_weight) + self.text_latent_bias
236
- text_latents = l2norm(text_latents)
237
-
238
- return self.contrast(audio_latents, text_latents)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/modules/distributed.py DELETED
@@ -1,83 +0,0 @@
1
- import torch
2
- from torch import nn
3
- from torch.autograd import Function
4
- import torch.distributed as dist
5
-
6
- from einops import rearrange
7
-
8
- def exists(val):
9
- return val is not None
10
-
11
- # distributed helpers
12
-
13
- def all_gather_same_dim(t):
14
- world_size = dist.get_world_size()
15
- gathered_tensors = [torch.empty_like(t, device = t.device, dtype = t.dtype) for i in range(world_size)]
16
- dist.all_gather(gathered_tensors, t)
17
- return gathered_tensors
18
-
19
- def all_gather_variable_dim(t, dim = 0, sizes = None):
20
- device, rank, world_size = t.device, dist.get_rank(), dist.get_world_size()
21
-
22
- if not exists(sizes):
23
- size = torch.tensor(t.shape[dim], device = device, dtype = torch.long)
24
- sizes = all_gather_same_dim(size)
25
- sizes = torch.stack(sizes)
26
-
27
- if torch.unique(sizes).numel() == 1:
28
- gathered_tensors = all_gather_same_dim(t)
29
- return torch.cat(gathered_tensors, dim = dim), sizes
30
-
31
- max_size = sizes.amax().item()
32
-
33
- padded_t = pad_dim_to(t, max_size, dim = dim)
34
- gathered_tensors = all_gather_same_dim(padded_t)
35
-
36
- gathered_tensor = torch.cat(gathered_tensors, dim = dim)
37
- seq = torch.arange(max_size, device = device)
38
-
39
- mask = rearrange(seq, 'j -> 1 j') < rearrange(sizes, 'i -> i 1')
40
- mask = rearrange(mask, 'i j -> (i j)')
41
- seq = torch.arange(mask.shape[-1], device = device)
42
- indices = seq[mask]
43
-
44
- gathered_tensor = gathered_tensor.index_select(dim, indices)
45
-
46
- return gathered_tensor, sizes
47
-
48
- class AllGatherFunction(Function):
49
- @staticmethod
50
- def forward(ctx, x, dim, sizes, all_reduce_grads):
51
- x, batch_sizes = all_gather_variable_dim(x, dim = dim, sizes = sizes)
52
- ctx.dim = dim
53
- ctx.all_reduce_grads = all_reduce_grads
54
- ctx.batch_sizes = batch_sizes.tolist()
55
- return x, batch_sizes
56
-
57
- @staticmethod
58
- def backward(ctx, grads, _):
59
- batch_sizes, rank = ctx.batch_sizes, dist.get_rank()
60
- if ctx.all_reduce_grads:
61
- dist.all_reduce(grads)
62
-
63
- grads_by_rank = grads.split(batch_sizes, dim = ctx.dim)
64
- return grads_by_rank[rank], None, None, None
65
-
66
- class AllGather(nn.Module):
67
- def __init__(
68
- self,
69
- dim,
70
- *,
71
- all_reduce_grads = False
72
- ):
73
- super().__init__()
74
- self.dim = dim
75
- self.all_reduce_grads = all_reduce_grads
76
- self.is_distributed = dist.is_initialized() and dist.get_world_size() > 1
77
-
78
- def forward(
79
- self,
80
- x,
81
- sizes = None
82
- ):
83
- return AllGatherFunction.apply(x, self.dim, sizes, self.all_reduce_grads)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/modules/extend_distributed.py DELETED
@@ -1,604 +0,0 @@
1
-
2
- import builtins
3
- import os
4
- import sys
5
-
6
- import torch
7
- import torch.distributed as dist
8
- from torch.autograd import Function
9
- from torch.autograd.profiler import record_function
10
- from torch.nn.parallel import DistributedDataParallel as DDP
11
-
12
- import torch.distributed as dist
13
-
14
- try:
15
- import torch_ccl
16
- except ImportError as e:
17
- # print(e)
18
- torch_ccl = False
19
-
20
- try:
21
- import torch_ucc
22
- except ImportError as e:
23
- torch_ucc = False
24
-
25
-
26
- my_rank = -1
27
- my_size = -1
28
- my_local_rank = 1
29
- my_local_size = 1
30
- alltoall_supported = False
31
- a2a_impl = os.environ.get("DLRM_ALLTOALL_IMPL", "")
32
-
33
- myreq = None
34
-
35
-
36
- def env2int(env_list, default=-1):
37
- for e in env_list:
38
- val = int(os.environ.get(e, -1))
39
- if val >= 0:
40
- return val
41
- return default
42
-
43
-
44
- def get_my_slice(n):
45
- k, m = divmod(n, my_size)
46
- return slice(
47
- my_rank * k + min(my_rank, m), (my_rank + 1) * k + min(my_rank + 1, m), 1
48
- )
49
-
50
-
51
- def get_split_lengths(n):
52
- k, m = divmod(n, my_size)
53
- if m == 0:
54
- splits = None
55
- my_len = k
56
- else:
57
- splits = [(k + 1) if i < m else k for i in range(my_size)]
58
- my_len = splits[my_rank]
59
- return (my_len, splits)
60
-
61
-
62
- def init_distributed(rank=-1, local_rank=-1, size=-1, use_gpu=False, backend=""):
63
- global myreq
64
- global my_rank
65
- global my_size
66
- global my_local_rank
67
- global my_local_size
68
- global a2a_impl
69
- global alltoall_supported
70
-
71
- # guess MPI ranks from env (works for IMPI, OMPI and MVAPICH2)
72
- num_mpi_ranks = env2int(
73
- ["PMI_SIZE", "OMPI_COMM_WORLD_SIZE", "MV2_COMM_WORLD_SIZE", "WORLD_SIZE"]
74
- )
75
- if backend == "" and num_mpi_ranks > 1:
76
- if torch_ccl and env2int(["CCL_WORKER_COUNT"]) > 0:
77
- backend = "ccl"
78
- elif use_gpu and dist.is_nccl_available():
79
- backend = "nccl"
80
- elif dist.is_mpi_available():
81
- backend = "mpi"
82
- else:
83
- print(
84
- "WARNING: MPI multi-process launch detected but PyTorch MPI backend not available."
85
- )
86
- backend = "gloo"
87
-
88
- if backend != "":
89
- # guess Rank and size
90
- if rank == -1:
91
- rank = env2int(
92
- ["PMI_RANK", "OMPI_COMM_WORLD_RANK", "MV2_COMM_WORLD_RANK", "RANK"], 0
93
- )
94
- if size == -1:
95
- size = env2int(
96
- [
97
- "PMI_SIZE",
98
- "OMPI_COMM_WORLD_SIZE",
99
- "MV2_COMM_WORLD_SIZE",
100
- "WORLD_SIZE",
101
- ],
102
- 1,
103
- )
104
- if not os.environ.get("RANK", None) and rank != -1:
105
- os.environ["RANK"] = str(rank)
106
- if not os.environ.get("WORLD_SIZE", None) and size != -1:
107
- os.environ["WORLD_SIZE"] = str(size)
108
- if not os.environ.get("MASTER_PORT", None):
109
- os.environ["MASTER_PORT"] = "29500"
110
- if not os.environ.get("MASTER_ADDR", None):
111
- local_size = env2int(
112
- [
113
- "MPI_LOCALNRANKS",
114
- "OMPI_COMM_WORLD_LOCAL_SIZE",
115
- "MV2_COMM_WORLD_LOCAL_SIZE",
116
- ],
117
- 1,
118
- )
119
- if local_size != size and backend != "mpi":
120
- print(
121
- "Warning: Looks like distributed multinode run but MASTER_ADDR env not set, using '127.0.0.1' as default"
122
- )
123
- print(
124
- "If this run hangs, try exporting rank 0's hostname as MASTER_ADDR"
125
- )
126
- os.environ["MASTER_ADDR"] = "127.0.0.1"
127
-
128
- if size > 1:
129
- if local_rank == -1:
130
- my_local_rank = env2int(
131
- [
132
- "MPI_LOCALRANKID",
133
- "OMPI_COMM_WORLD_LOCAL_RANK",
134
- "MV2_COMM_WORLD_LOCAL_RANK",
135
- "LOCAL_RANK",
136
- ],
137
- 0,
138
- )
139
- else:
140
- my_local_rank = local_rank
141
- my_local_size = env2int(
142
- [
143
- "MPI_LOCALNRANKS",
144
- "OMPI_COMM_WORLD_LOCAL_SIZE",
145
- "MV2_COMM_WORLD_LOCAL_SIZE",
146
- ],
147
- 1,
148
- )
149
- if use_gpu:
150
- if my_local_size > torch.cuda.device_count():
151
- print(
152
- "Not sufficient GPUs available... local_size = %d, ngpus = %d"
153
- % (my_local_size, torch.cuda.device_count())
154
- )
155
- sys.exit(1)
156
- torch.cuda.set_device(my_local_rank)
157
- dist.init_process_group(backend, rank=rank, world_size=size)
158
- my_rank = dist.get_rank()
159
- my_size = dist.get_world_size()
160
- if my_rank == 0:
161
- print("Running on %d ranks using %s backend" % (my_size, backend))
162
- if hasattr(dist, "all_to_all_single"):
163
- try:
164
- t = torch.zeros([4])
165
- if use_gpu:
166
- t = t.cuda()
167
- dist.all_to_all_single(t, t)
168
- alltoall_supported = True
169
- except RuntimeError as err:
170
- print("fail to enable all_to_all_single primitive: %s" % err)
171
- if a2a_impl == "alltoall" and alltoall_supported == False:
172
- print(
173
- "Requested DLRM_ALLTOALL_IMPL=%s but backend %s does not support it, use scatter/gather based alltoall"
174
- % (a2a_impl, backend)
175
- )
176
- a2a_impl = "scatter"
177
- if a2a_impl != "":
178
- print("Using DLRM_ALLTOALL_IMPL=%s" % a2a_impl)
179
- else:
180
- my_rank = 0
181
- my_size = 1
182
- my_local_rank = 0
183
- my_local_size = 1
184
- print_all(
185
- "world size: %d, current rank: %d, local rank: %d"
186
- % (my_size, my_rank, my_local_rank)
187
- )
188
- myreq = Request()
189
-
190
-
191
- class Request(object):
192
- def __init__(self):
193
- self.req = None
194
- self.tensor = None
195
- self.WaitFunction = All2All_Scatter_Wait
196
-
197
- def wait(self):
198
- ret = self.WaitFunction.apply(*self.tensor)
199
- self.req = None
200
- self.tensor = None
201
- return ret
202
-
203
-
204
- class All2All_ScatterList_Req(Function):
205
- @staticmethod
206
- def forward(ctx, a2a_info, *inputs):
207
- global myreq
208
- batch_split_lengths = (
209
- a2a_info.global_batch_partition_slices
210
- if a2a_info.global_batch_partition_slices
211
- else a2a_info.local_batch_num
212
- )
213
- table_split_lengths = (
214
- a2a_info.global_table_wise_parition_slices
215
- if a2a_info.global_table_wise_parition_slices
216
- else [a2a_info.local_table_num] * my_size
217
- )
218
- gather_list = []
219
- req_list = []
220
- for i in range(my_size):
221
- for j in range(table_split_lengths[i]):
222
- out_tensor = inputs[0].new_empty(
223
- [a2a_info.local_batch_num, a2a_info.emb_dim]
224
- )
225
- scatter_list = (
226
- list(inputs[j].split(batch_split_lengths, dim=0))
227
- if i == my_rank
228
- else []
229
- )
230
- req = dist.scatter(out_tensor, scatter_list, src=i, async_op=True)
231
- gather_list.append(out_tensor)
232
- req_list.append(req)
233
- myreq.req = req_list
234
- myreq.tensor = tuple(gather_list)
235
- myreq.a2a_info = a2a_info
236
- return myreq.tensor
237
-
238
- @staticmethod
239
- def backward(ctx, *grad_output):
240
- global myreq
241
- for r in myreq.req:
242
- r.wait()
243
- myreq.req = None
244
- grad_inputs = myreq.tensor
245
- myreq.tensor = None
246
- return (None, *grad_inputs)
247
-
248
-
249
- class All2All_ScatterList_Wait(Function):
250
- @staticmethod
251
- def forward(ctx, *output):
252
- global myreq
253
- ctx.a2a_info = myreq.a2a_info
254
- for r in myreq.req:
255
- r.wait()
256
- myreq.req = None
257
- myreq.tensor = None
258
- return output
259
-
260
- @staticmethod
261
- def backward(ctx, *grad_output):
262
- global myreq
263
- a2a_info = ctx.a2a_info
264
- grad_output = [t.contiguous() for t in grad_output]
265
- batch_split_lengths = (
266
- a2a_info.global_batch_partition_slices
267
- if a2a_info.global_batch_partition_slices
268
- else [a2a_info.local_batch_num] * my_size
269
- )
270
- per_rank_table_splits = (
271
- a2a_info.global_table_wise_parition_slices
272
- if a2a_info.global_table_wise_parition_slices
273
- else [a2a_info.local_table_num] * my_size
274
- )
275
- grad_inputs = [
276
- grad_output[0].new_empty([ctx.a2a_info.batch_size, ctx.a2a_info.emb_dim])
277
- for _ in range(a2a_info.local_table_num)
278
- ]
279
- req_list = []
280
- ind = 0
281
- for i in range(my_size):
282
- for j in range(per_rank_table_splits[i]):
283
- gather_list = (
284
- list(grad_inputs[j].split(batch_split_lengths, dim=0))
285
- if i == my_rank
286
- else None
287
- )
288
- req = dist.gather(grad_output[ind], gather_list, dst=i, async_op=True)
289
- req_list.append(req)
290
- ind += 1
291
- myreq.req = req_list
292
- myreq.tensor = grad_inputs
293
- return tuple(grad_output)
294
-
295
-
296
- class All2All_Scatter_Req(Function):
297
- @staticmethod
298
- def forward(ctx, a2a_info, *inputs):
299
- global myreq
300
- batch_split_lengths = (
301
- a2a_info.global_batch_partition_slices
302
- if a2a_info.global_batch_partition_slices
303
- else a2a_info.local_batch_num
304
- )
305
- table_split_lengths = (
306
- a2a_info.global_table_wise_parition_slices
307
- if a2a_info.global_table_wise_parition_slices
308
- else [a2a_info.local_table_num] * my_size
309
- )
310
- input = torch.cat(inputs, dim=1)
311
- scatter_list = list(input.split(batch_split_lengths, dim=0))
312
- gather_list = []
313
- req_list = []
314
- for i in range(my_size):
315
- out_tensor = input.new_empty(
316
- [a2a_info.local_batch_num, table_split_lengths[i] * a2a_info.emb_dim]
317
- )
318
- req = dist.scatter(
319
- out_tensor, scatter_list if i == my_rank else [], src=i, async_op=True
320
- )
321
- gather_list.append(out_tensor)
322
- req_list.append(req)
323
- myreq.req = req_list
324
- myreq.tensor = tuple(gather_list)
325
- myreq.a2a_info = a2a_info
326
- ctx.a2a_info = a2a_info
327
- return myreq.tensor
328
-
329
- @staticmethod
330
- def backward(ctx, *grad_output):
331
- global myreq
332
- for r in myreq.req:
333
- r.wait()
334
- myreq.req = None
335
- grad_input = myreq.tensor
336
- grad_inputs = grad_input.split(ctx.a2a_info.emb_dim, dim=1)
337
- myreq.tensor = None
338
- return (None, *grad_inputs)
339
-
340
-
341
- class All2All_Scatter_Wait(Function):
342
- @staticmethod
343
- def forward(ctx, *output):
344
- global myreq
345
- ctx.a2a_info = myreq.a2a_info
346
- for r in myreq.req:
347
- r.wait()
348
- myreq.req = None
349
- myreq.tensor = None
350
- return output
351
-
352
- @staticmethod
353
- def backward(ctx, *grad_output):
354
- global myreq
355
- assert len(grad_output) == my_size
356
- scatter_list = [t.contiguous() for t in grad_output]
357
- a2a_info = ctx.a2a_info
358
- batch_split_lengths = (
359
- a2a_info.global_batch_partition_slices
360
- if a2a_info.global_batch_partition_slices
361
- else a2a_info.local_batch_num
362
- )
363
- table_split_lengths = (
364
- a2a_info.global_table_wise_parition_slices
365
- if a2a_info.global_table_wise_parition_slices
366
- else [a2a_info.local_table_num] * my_size
367
- )
368
- grad_input = grad_output[0].new_empty(
369
- [a2a_info.batch_size, a2a_info.emb_dim * a2a_info.local_table_num]
370
- )
371
- gather_list = list(grad_input.split(batch_split_lengths, dim=0))
372
- req_list = []
373
- for i in range(my_size):
374
- req = dist.gather(
375
- scatter_list[i],
376
- gather_list if i == my_rank else [],
377
- dst=i,
378
- async_op=True,
379
- )
380
- req_list.append(req)
381
- myreq.req = req_list
382
- myreq.tensor = grad_input
383
- return grad_output
384
-
385
-
386
- class All2All_Req(Function):
387
- @staticmethod
388
- def forward(ctx, a2a_info, *inputs):
389
- global myreq
390
- with record_function("DLRM alltoall_req_fwd_single"):
391
- batch_split_lengths = a2a_info.global_batch_partition_slices
392
- if batch_split_lengths:
393
- batch_split_lengths = [
394
- m * a2a_info.emb_dim * a2a_info.local_table_num
395
- for m in batch_split_lengths
396
- ]
397
- table_split_lengths = a2a_info.global_table_wise_parition_slices
398
- if table_split_lengths:
399
- table_split_lengths = [
400
- a2a_info.local_batch_num * e * a2a_info.emb_dim
401
- for e in table_split_lengths
402
- ]
403
- input = torch.cat(inputs, dim=1).view([-1])
404
- output = input.new_empty(
405
- [
406
- a2a_info.global_table_num
407
- * a2a_info.local_batch_num
408
- * a2a_info.emb_dim
409
- ]
410
- )
411
- req = dist.all_to_all_single(
412
- output, input, table_split_lengths, batch_split_lengths, async_op=True
413
- )
414
-
415
- myreq.req = req
416
- myreq.tensor = []
417
- myreq.tensor.append(output)
418
- myreq.tensor = tuple(myreq.tensor)
419
- a2a_info.batch_split_lengths = batch_split_lengths
420
- a2a_info.table_split_lengths = table_split_lengths
421
- myreq.a2a_info = a2a_info
422
- ctx.a2a_info = a2a_info
423
- return myreq.tensor
424
-
425
- @staticmethod
426
- def backward(ctx, *grad_output):
427
- global myreq
428
- with record_function("DLRM alltoall_req_bwd_single"):
429
- a2a_info = ctx.a2a_info
430
- myreq.req.wait()
431
- myreq.req = None
432
- grad_input = myreq.tensor
433
- grad_inputs = grad_input.view([a2a_info.batch_size, -1]).split(
434
- a2a_info.emb_dim, dim=1
435
- )
436
- grad_inputs = [gin.contiguous() for gin in grad_inputs]
437
- myreq.tensor = None
438
- return (None, *grad_inputs)
439
-
440
-
441
- class All2All_Wait(Function):
442
- @staticmethod
443
- def forward(ctx, *output):
444
- global myreq
445
- with record_function("DLRM alltoall_wait_fwd_single"):
446
- a2a_info = myreq.a2a_info
447
- ctx.a2a_info = a2a_info
448
- myreq.req.wait()
449
- myreq.req = None
450
- myreq.tensor = None
451
- table_split_lengths = (
452
- a2a_info.table_split_lengths
453
- if a2a_info.table_split_lengths
454
- else a2a_info.local_table_num
455
- * a2a_info.local_batch_num
456
- * a2a_info.emb_dim
457
- )
458
- outputs = output[0].split(table_split_lengths)
459
- outputs = tuple(
460
- [out.view([a2a_info.local_batch_num, -1]) for out in outputs]
461
- )
462
- return outputs
463
-
464
- @staticmethod
465
- def backward(ctx, *grad_outputs):
466
- global myreq
467
- with record_function("DLRM alltoall_wait_bwd_single"):
468
- a2a_info = ctx.a2a_info
469
- grad_outputs = [gout.contiguous().view([-1]) for gout in grad_outputs]
470
- grad_output = torch.cat(grad_outputs)
471
- grad_input = grad_output.new_empty(
472
- [a2a_info.batch_size * a2a_info.local_table_num * a2a_info.emb_dim]
473
- )
474
- req = dist.all_to_all_single(
475
- grad_input,
476
- grad_output,
477
- a2a_info.batch_split_lengths,
478
- a2a_info.table_split_lengths,
479
- async_op=True,
480
- )
481
- myreq.req = req
482
- myreq.tensor = grad_input
483
- return (grad_output,)
484
-
485
-
486
- class AllGather(Function):
487
- @staticmethod
488
- def forward(ctx, input, global_lengths, dim=0):
489
- if not isinstance(global_lengths, (list, tuple)):
490
- global_lengths = [global_lengths] * my_size
491
-
492
- assert len(global_lengths) == my_size
493
- assert global_lengths[my_rank] == input.size(dim)
494
- local_start = sum(global_lengths[:my_rank])
495
-
496
- output_size = list(input.size())
497
-
498
- ctx.dim = dim
499
- ctx.local_start = local_start
500
- ctx.local_length = global_lengths[my_rank]
501
-
502
- input = input.contiguous()
503
- if dim == 0:
504
- out_len = sum(global_lengths)
505
- output_size[dim] = out_len
506
- output = input.new_empty(output_size)
507
- gather_list = list(output.split(global_lengths, dim=0))
508
- else:
509
- gather_list = [torch.empty_like(input) for _ in range(my_size)]
510
- gather_list = []
511
- for length in global_lengths:
512
- output_size[dim] = length
513
- gather_list.append(input.new_empty(output_size))
514
-
515
- dist.all_gather(gather_list, input)
516
-
517
- if dim != 0:
518
- output = torch.cat(gather_list, dim=dim)
519
-
520
- return output
521
-
522
- @staticmethod
523
- def backward(ctx, grad_output):
524
- # print("Inside All2AllBackward")
525
- dim = ctx.dim
526
- start = ctx.local_start
527
- length = ctx.local_length
528
-
529
- grad_input = grad_output.narrow(dim, start, length)
530
-
531
- return (grad_input, None, None)
532
-
533
-
534
- class All2AllInfo(object):
535
- pass
536
-
537
-
538
- def alltoall(inputs, per_rank_table_splits):
539
- global myreq
540
- batch_size, emb_dim = inputs[0].size()
541
- a2a_info = All2AllInfo()
542
- a2a_info.local_table_num = len(inputs)
543
- a2a_info.global_table_wise_parition_slices = per_rank_table_splits
544
- (
545
- a2a_info.local_batch_num,
546
- a2a_info.global_batch_partition_slices,
547
- ) = get_split_lengths(batch_size)
548
- a2a_info.emb_dim = emb_dim
549
- a2a_info.batch_size = batch_size
550
- a2a_info.global_table_num = (
551
- sum(per_rank_table_splits)
552
- if per_rank_table_splits
553
- else a2a_info.local_table_num * my_size
554
- )
555
-
556
- if a2a_impl == "" and alltoall_supported or a2a_impl == "alltoall":
557
- # print("Using All2All_Req")
558
- output = All2All_Req.apply(a2a_info, *inputs)
559
- myreq.WaitFunction = All2All_Wait
560
- elif a2a_impl == "" or a2a_impl == "scatter":
561
- # print("Using All2All_Scatter_Req")
562
- output = All2All_Scatter_Req.apply(a2a_info, *inputs)
563
- myreq.WaitFunction = All2All_Scatter_Wait
564
- elif a2a_impl == "scatter_list":
565
- # print("Using All2All_ScatterList_Req")
566
- output = All2All_ScatterList_Req.apply(a2a_info, *inputs)
567
- myreq.WaitFunction = All2All_ScatterList_Wait
568
- else:
569
- print(
570
- "Unknown value set for DLRM_ALLTOALL_IMPL (%s), "
571
- "please use one of [alltoall, scatter, scatter_list]" % a2a_impl
572
- )
573
- return myreq
574
-
575
-
576
- def all_gather(input, lengths, dim=0):
577
- global my_rank, my_size
578
- if my_size == -1:
579
- my_size = dist.get_world_size()
580
- my_rank = dist.get_rank()
581
- if not lengths:
582
- lengths = [input.size(0)] * my_size
583
- return AllGather.apply(input, lengths, dim)
584
-
585
-
586
- def barrier():
587
- if my_size > 1:
588
- dist.barrier()
589
-
590
-
591
- # Override builtin print function to print only from rank 0
592
- orig_print = builtins.print
593
-
594
-
595
- def rank0_print(*args, **kwargs):
596
- if my_rank <= 0 or kwargs.get("print_all", False):
597
- orig_print(*args, **kwargs)
598
-
599
-
600
- # builtins.print = rank0_print
601
-
602
- # Allow printing from all rank with explicit print_all
603
- def print_all(*args, **kwargs):
604
- orig_print(*args, **kwargs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/modules/transformer.py DELETED
@@ -1,185 +0,0 @@
1
- # attention
2
- from torch import einsum
3
- import torch.nn as nn
4
- import torch.nn.functional as F
5
- import torch
6
- from einops import rearrange
7
- from .utils import l2norm, default, exists
8
-
9
- # 2d sinusoidal positional embedding
10
- # simple vit paper shows it is good enough compared to learned
11
-
12
- def posemb_sincos_2d(patches, temperature = 10000, dtype = torch.float32):
13
- _, h, w, dim, device, dtype = *patches.shape, patches.device, patches.dtype
14
-
15
- y, x = torch.meshgrid(torch.arange(h, device = device), torch.arange(w, device = device), indexing = 'ij')
16
- assert (dim % 4) == 0, 'feature dimension must be multiple of 4 for sincos emb'
17
-
18
- omega = torch.arange(dim // 4, device = device) / (dim // 4 - 1)
19
- omega = 1. / (temperature ** omega)
20
-
21
- y = y.flatten()[:, None] * omega[None, :]
22
- x = x.flatten()[:, None] * omega[None, :]
23
-
24
- pe = torch.cat((x.sin(), x.cos(), y.sin(), y.cos()), dim = 1)
25
- pe = pe.type(dtype)
26
-
27
- return rearrange(pe, '(h w) d -> h w d', h = h, w = w)
28
-
29
- # biasless layernorm
30
-
31
- class LayerNorm(nn.Module):
32
- def __init__(self, dim, scale = True):
33
- super().__init__()
34
- self.learned_gamma = nn.Parameter(torch.ones(dim)) if scale else None
35
-
36
- self.register_buffer('gamma', torch.ones(dim), persistent = False)
37
- self.register_buffer('beta', torch.zeros(dim), persistent = False)
38
-
39
- def forward(self, x):
40
- return F.layer_norm(x, x.shape[-1:], default(self.learned_gamma, self.gamma), self.beta)
41
-
42
- # feedforward
43
-
44
- class GEGLU(nn.Module):
45
- def forward(self, x):
46
- x, gate = x.chunk(2, dim = -1)
47
- return F.gelu(gate) * x
48
-
49
- def FeedForward(dim, mult = 4, dropout = 0.):
50
- dim_hidden = int(dim * mult * 2 / 3)
51
-
52
- return nn.Sequential(
53
- LayerNorm(dim),
54
- nn.Linear(dim, dim_hidden * 2, bias = False),
55
- GEGLU(),
56
- nn.Dropout(dropout),
57
- nn.Linear(dim_hidden, dim, bias = False)
58
- )
59
-
60
- class Attention(nn.Module):
61
- def __init__(
62
- self,
63
- dim,
64
- causal = False,
65
- dim_head = 64,
66
- heads = 8,
67
- dropout = 0.,
68
- scale = 8
69
- ):
70
- super().__init__()
71
- self.heads = heads
72
- self.scale = scale
73
- self.causal = causal
74
- inner_dim = dim_head * heads
75
-
76
- self.norm = LayerNorm(dim)
77
-
78
- self.attn_dropout = nn.Dropout(dropout)
79
-
80
- self.to_q = nn.Linear(dim, inner_dim, bias = False)
81
- self.to_kv = nn.Linear(dim, inner_dim * 2, bias = False)
82
-
83
- self.q_scale = nn.Parameter(torch.ones(dim_head))
84
- self.k_scale = nn.Parameter(torch.ones(dim_head))
85
-
86
- self.to_out = nn.Sequential(
87
- nn.Linear(inner_dim, dim, bias = False),
88
- nn.Dropout(dropout)
89
- )
90
-
91
- def forward(
92
- self,
93
- x,
94
- rel_pos_bias = None,
95
- mask = None
96
- ):
97
- b, n, _, device = *x.shape, x.device
98
-
99
- # prenorm
100
-
101
- x = self.norm(x)
102
-
103
- # project for queries, keys, values
104
-
105
- q, k, v = self.to_q(x), *self.to_kv(x).chunk(2, dim = -1)
106
-
107
- # split for multi-headed attention
108
-
109
- q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = self.heads), (q, k, v))
110
-
111
- # qk rmsnorm, technique circulating within brain used to stabilize a 22B parameter vision model training
112
-
113
- q, k = map(l2norm, (q, k))
114
- q = q * self.q_scale
115
- k = k * self.k_scale
116
-
117
- # similarities
118
-
119
- sim = einsum('b h i d, b h j d -> b h i j', q, k) * self.scale
120
-
121
- if exists(rel_pos_bias):
122
- sim = sim + rel_pos_bias
123
-
124
- if exists(mask):
125
- mask = rearrange(mask, 'b j -> b 1 1 j')
126
- sim = sim.masked_fill(~mask, -torch.finfo(sim.dtype).max)
127
-
128
- if self.causal:
129
- i, j = sim.shape[-2:]
130
- causal_mask = torch.ones((i, j), dtype = torch.bool, device = x.device).triu(j - i + 1)
131
- sim = sim.masked_fill(causal_mask, -torch.finfo(sim.dtype).max)
132
-
133
- # attention
134
-
135
- attn = sim.softmax(dim = -1)
136
- attn = self.attn_dropout(attn)
137
-
138
- # aggregate
139
-
140
- out = einsum('b h i j, b h j d -> b h i d', attn, v)
141
-
142
- # merge heads
143
-
144
- out = rearrange(out, 'b h n d -> b n (h d)')
145
- return self.to_out(out)
146
-
147
- # transformer
148
-
149
- class Transformer(nn.Module):
150
- def __init__(
151
- self,
152
- dim,
153
- depth,
154
- dim_head = 64,
155
- heads = 8,
156
- attn_dropout = 0.,
157
- ff_mult = 4,
158
- ff_dropout = 0.
159
- ):
160
- super().__init__()
161
- self.layers = nn.ModuleList([])
162
- for _ in range(depth):
163
- self.layers.append(nn.ModuleList([
164
- Attention(dim = dim, dim_head = dim_head, heads = heads, dropout = attn_dropout),
165
- FeedForward(dim = dim, mult = ff_mult, dropout = ff_dropout),
166
- ]))
167
-
168
- def forward(
169
- self,
170
- x,
171
- rel_pos_bias = None,
172
- mask = None,
173
- return_all_layers = False
174
- ):
175
- layers = []
176
-
177
- for attn, ff in self.layers:
178
- x = attn(x, rel_pos_bias = rel_pos_bias, mask = mask) + x
179
- x = ff(x) + x
180
- layers.append(x)
181
-
182
- if not return_all_layers:
183
- return x
184
-
185
- return x, torch.stack(layers[:-1]) if len(self.layers)>1 else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/modules/utils.py DELETED
@@ -1,45 +0,0 @@
1
- from functools import wraps
2
- import torch
3
- from torch import nn
4
- import torch.nn.functional as F
5
-
6
- def exists(val):
7
- return val is not None
8
-
9
- def first(it):
10
- return it[0]
11
-
12
- def default(val, d):
13
- return val if exists(val) else d
14
-
15
- def round_down_nearest_multiple(n, divisor):
16
- return n // divisor * divisor
17
-
18
- def Sequential(*modules):
19
- return nn.Sequential(*filter(exists, modules))
20
-
21
-
22
- def once(fn):
23
- called = False
24
- @wraps(fn)
25
- def inner(x):
26
- nonlocal called
27
- if called:
28
- return
29
- called = True
30
- return fn(x)
31
- return inner
32
-
33
- print_once = once(print)
34
-
35
- # tensor functions
36
-
37
- def log(t, eps = 1e-20):
38
- return torch.log(t.clamp(min = eps))
39
-
40
- def l2norm(t):
41
- return F.normalize(t, p = 2, dim = -1)
42
-
43
- def frozen_params(model:nn.Module):
44
- for param in model.parameters():
45
- param.requires_grad = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/muq/muq_mulan/muq_mulan.py DELETED
@@ -1,271 +0,0 @@
1
- from typing import List, Optional
2
- from dataclasses import dataclass, field
3
- import os
4
-
5
- from torch.nn.parallel.distributed import DistributedDataParallel
6
- import torch
7
- import torch.nn as nn
8
- from torch import einsum
9
- from einops import rearrange
10
- from huggingface_hub import PyTorchModelHubMixin
11
- from easydict import EasyDict
12
-
13
- from .models.mulan import MuLanModel
14
- from .models.audio import AudioSpectrogramTransformerPretrained
15
- from .models.text import TextTransformerPretrained
16
- from .modules.utils import exists, frozen_params
17
-
18
-
19
- @dataclass
20
- class MuLanConfig:
21
- sr:int = field(default=24000)
22
- clip_secs:float = field(default=10)
23
- dim_latent:int = field(default=512)
24
- decoupled_contrastive_learning:bool = field(default=True)
25
- hierarchical_contrastive_loss:bool = field(default=False)
26
- hierarchical_contrastive_loss_layers:Optional[List] = field(default=None)
27
- sigmoid_contrastive_loss:bool = field(default=False)
28
- rank_contrast:bool = field(default=True)
29
-
30
- @dataclass
31
- class AudioTransformerConfig:
32
- dim:int = field(default=768)
33
- tf_depth:int = field(default=8)
34
- heads:int = field(default=8)
35
- dim_head:int = field(default=64)
36
- attn_dropout:float = field(default=0.)
37
- ff_dropout:float = field(default=0.)
38
- ff_mult:int = field(default=4)
39
-
40
- @dataclass
41
- class TextTransformerConfig:
42
- dim:int = field(default=768)
43
- tf_depth:int = field(default=8)
44
- max_seq_len:int = field(default=1024)
45
- dim_head:int = field(default=64)
46
- heads:int = field(default=8)
47
- attn_dropout:float = field(default=0.)
48
- ff_dropout:float = field(default=0.)
49
- ff_mult:int = field(default=4)
50
-
51
- @dataclass
52
- class ModalModelConfig:
53
- name:str = field(default='')
54
- model_dim: Optional[int] = field(default=None)
55
- use_layer_idx: int = field(default=-1)
56
-
57
-
58
- @dataclass
59
- class MuQMuLanConfig:
60
- mulan: MuLanConfig
61
- audio_model: ModalModelConfig
62
- text_model: ModalModelConfig
63
- audio_transformer: AudioTransformerConfig
64
- text_transformer: TextTransformerConfig
65
-
66
- class MuQMuLan(nn.Module, PyTorchModelHubMixin):
67
- def __init__(self, config: MuQMuLanConfig, hf_hub_cache_dir=None):
68
- super().__init__()
69
- config = self._to_obj(config)
70
- self.config = config
71
- self.mulan = self.create_MuLan_from_config(config, hf_hub_cache_dir)
72
- self.sr = config.mulan.sr
73
- self.clip_secs = config.mulan.clip_secs
74
-
75
- def _to_obj(self, config):
76
- if isinstance(config, MuQMuLanConfig):
77
- config = EasyDict(
78
- mulan = config.mulan,
79
- audio_model = config.audio_model,
80
- text_model = config.text_model,
81
- audio_transformer = config.audio_transformer,
82
- text_transformer = config.text_transformer,
83
- )
84
- else:
85
- config = EasyDict(config)
86
- return config
87
-
88
- @classmethod
89
- def from_pretrained(cls, *args, cache_dir=None, **kwargs):
90
- kwargs['hf_hub_cache_dir'] = cache_dir
91
- return super().from_pretrained(*args, cache_dir=cache_dir, **kwargs)
92
-
93
-
94
- @classmethod
95
- def create_MuLan_from_config(cls, config:MuQMuLanConfig, hf_hub_cache_dir=None) -> MuLanModel:
96
-
97
- audio_transformer = AudioSpectrogramTransformerPretrained(
98
- model_name = config.audio_model.name,
99
- model_dim = config.audio_model.model_dim,
100
- use_layer_idx = config.audio_model.use_layer_idx,
101
- **config.audio_transformer,
102
- frozen_pretrained = False,
103
- hf_hub_cache_dir = hf_hub_cache_dir,
104
- )
105
- text_transformer = TextTransformerPretrained(
106
- model_name = config.text_model.name,
107
- model_dim = config.text_model.model_dim,
108
- **config.text_transformer,
109
- frozen_pretrained = False,
110
- hf_hub_cache_dir = hf_hub_cache_dir,
111
- )
112
-
113
- mulan = MuLanModel(
114
- audio_transformer = audio_transformer,
115
- text_transformer = text_transformer,
116
- **config.mulan
117
- )
118
-
119
- return mulan
120
-
121
- def frozen(self):
122
- frozen_params(self)
123
-
124
- @property
125
- def device(self):
126
- return next(self.parameters()).device
127
-
128
- @property
129
- def mulan_module(self):
130
- if isinstance(self.mulan, DistributedDataParallel):
131
- return self.mulan.module
132
- else:
133
- return self.mulan
134
-
135
- def forward(self,
136
- wavs: Optional[torch.Tensor] = None,
137
- texts: Optional[List[str]] = None,
138
- *,
139
- parallel_processing = False,
140
- ) -> torch.Tensor:
141
- """
142
- Extract audio or text features, takes audio OR texts batch as input.
143
- Note that if the audio is longer than 10s, it will be crop to multi cips and returns the average latent.
144
- The param `parallel_processing` is used to control whether to use parallel processing or not.
145
- If set to True, it uses parallel processing extractraction, which is faster but uses more GPU memory.
146
- If set to False(the default), it uses serial processing extraction, which is slower but memory-friendly.
147
-
148
- Args:
149
- wavs (Optional[torch.Tensor]): Audio waveform tensor. Defaults to None.
150
- texts (Optional[List[str]]): List of text strings. Defaults to None.
151
- parallel_processing (bool): Whether to use parallel processing. Defaults to False.
152
-
153
- Returns:
154
- torch.Tensor: Latent representation of audio or text input.
155
-
156
- Raises:
157
- AssertionError: If both wavs and texts are provided or if neither is provided.
158
-
159
- Note:
160
- - Either wavs or texts must be provided, but not both.
161
- - If wavs is provided, it calls extract_audio_latents method to process audio.
162
- - If texts is provided, it calls extract_text_latents method to process text.
163
- """
164
- assert exists(wavs) ^ exists(texts), "Please provide either wavs or texts, but not both"
165
-
166
- if exists(wavs):
167
- return self.extract_audio_latents(wavs = wavs, parallel_processing = parallel_processing)
168
- else:
169
- return self.extract_text_latents(texts = texts)
170
-
171
- def calc_similarity(self, audio_latents: torch.Tensor, text_latents: torch.Tensor) -> torch.Tensor:
172
- """
173
- Calculate the dot-product similarity between audio and text latent representations.
174
- It supports various dimensions of input tensors (with/without batch dimension) for both audio and text.
175
-
176
- Note:
177
- The effect of this function is basically equivalent to the dot product.
178
- mulan.calc_similarity(lat_a, lat_t) <==> einsum('i d, j d -> i j', lat_a, lat_t)
179
-
180
- Args:
181
- audio_latents (torch.Tensor): Latent representation of audio.
182
- text_latents (torch.Tensor): Latent representation of text.
183
-
184
- Returns:
185
- torch.Tensor: Similarity scores between audio and text latent representations.
186
-
187
- """
188
- dim_a, dim_t = len(audio_latents.shape), len(text_latents.shape)
189
- if dim_a == 2 and dim_t == 2:
190
- return einsum('i d, j d -> i j', audio_latents, text_latents)
191
- elif dim_a == 1 and dim_t == 1:
192
- return torch.dot(audio_latents, text_latents)
193
- elif dim_a == 2 and dim_t == 1:
194
- return einsum('i d, d -> i', audio_latents, text_latents)
195
- elif dim_a == 1 and dim_t == 2:
196
- return einsum('d, j d -> j', audio_latents, text_latents)
197
-
198
- raise RuntimeError(f"Invalid dimensions: audio {dim_a}, text {dim_t}")
199
-
200
-
201
- def extract_audio_latents(self, wavs:torch.Tensor, *, parallel_processing = False) -> torch.Tensor:
202
- """
203
- Extract latent representations from audio waveforms.
204
-
205
- This function processes a batch of audio waveforms and extracts their latent representations.
206
- It supports parallel processing for faster computation but uses more GPU memory.
207
-
208
- Args:
209
- wavs (torch.Tensor): A batch of audio waveform tensors.
210
- parallel_processing (bool): Flag to enable parallel processing. Defaults to False.
211
-
212
- Returns:
213
- torch.Tensor: A tensor containing the latent representations of the input audio waveforms.
214
- """
215
- audio_latents = []
216
-
217
- def audio_to_latent(wav):
218
- return self.mulan_module.get_audio_latents(wav)
219
- for wav in wavs:
220
- wav_tensors = []
221
- if isinstance(wav, torch.Tensor):
222
- wav_tensors = self._get_all_clips(wav)
223
- else:
224
- raise TypeError('wavs must be a Tensor')
225
-
226
- if parallel_processing:
227
- wav_tensors = wav_tensors.to(self.device)
228
- audio_latent = audio_to_latent(wav_tensors)
229
- audio_latent = audio_latent.mean(dim=0)
230
- else:
231
- wav_tensors = rearrange(wav_tensors, "i j -> i 1 j")
232
- audio_latent = []
233
- for wav_tensor in wav_tensors:
234
- audio_latent.append(audio_to_latent(wav_tensor).squeeze(0))
235
- del wav_tensor
236
- audio_latent = torch.stack(audio_latent, dim=0)
237
- audio_latent = audio_latent.mean(dim=0).to(self.device)
238
-
239
- audio_latents.append(audio_latent)
240
- audio_latents = torch.stack(audio_latents, dim=0)
241
- return audio_latents
242
-
243
- def extract_text_latents(self, texts: List[str]) -> torch.Tensor:
244
- """
245
- Extract latent representations from text inputs.
246
-
247
- This function processes a list of text strings and extracts their latent representations
248
- using the MuLan model's text tower.
249
-
250
- Args:
251
- texts (List[str]): A list of text strings to be processed.
252
-
253
- Returns:
254
- torch.Tensor: A tensor containing the latent representations of the input texts.
255
- """
256
- return self.mulan_module.get_text_latents(raw_texts=texts)
257
-
258
- def _get_all_clips(self, audio):
259
- origin_length = len(audio)
260
- accum_length = 0
261
- delta = self.sr * self.clip_secs
262
- audio_clips = []
263
- while accum_length + delta <= origin_length:
264
- clip = audio[accum_length:accum_length + delta]
265
- audio_clips.append(clip)
266
- accum_length += delta
267
- if accum_length < origin_length:
268
- audio_clips.append(torch.cat([audio[accum_length:], audio[0:delta - (origin_length - accum_length)]]))
269
-
270
- return torch.stack(audio_clips, dim=0)
271
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/README.md DELETED
@@ -1,107 +0,0 @@
1
- `
2
- # Guidance on MuQ-MuLan Training (Contrastive Learning)
3
-
4
- This guide provides instructions for training **MuQ-MuLan**, a contrastive learning model that jointly encodes music and text.
5
-
6
- We recommend training on **32 GPUs**, each with **at least 32 GB of memory**. Training typically takes **1–2 days**, depending on hardware environment.
7
-
8
- ---
9
-
10
- ## Step 1: Environment Setup
11
-
12
- First, install all required dependencies listed in [requirements.txt](./requirements.txt):
13
-
14
- ```bash
15
- pip install -r requirements.txt
16
- ```
17
-
18
- Then, install this repository as a library in editable mode:
19
-
20
- ```bash
21
- pip install -e .
22
- ```
23
-
24
-
25
- ---
26
-
27
- ## Step 2: Data Preparation
28
-
29
- We provide an example setup using the [MTG-Jamendo](https://github.com/MTG/mtg-jamendo-dataset) open-source music dataset.
30
-
31
- Please download our preprocessed data split files from [this link](https://drive.google.com/file/d/1PMCUpmtw8JwUv9Y-bNl8WIAZDtX8UaFY/view?usp=sharing) and place them in the appropriate directory.
32
-
33
- If you wish to use your own dataset, ensure that your data follow the required format.
34
-
35
- ---
36
-
37
- ## Step 3: Run Training
38
-
39
- ### Option 1: Manual Configuration
40
-
41
- This method will prompt you to configure distributed training manually via `accelerate`:
42
-
43
- ```bash
44
- accelerate config
45
- accelerate launch train.py
46
- ```
47
-
48
- ### Option 2: Using a Predefined Config File
49
-
50
- We provide example config files for multi-node multi-GPU training. For example, to launch training on **4 nodes**, each with **8 GPUs**, run:
51
-
52
- ```bash
53
- accelerate launch \
54
- --config_file config/accelerate/32gpu4node_fp16.yaml \
55
- --machine_rank $NODE_RANK \
56
- --main_process_ip $CHIEF_IP \
57
- --main_process_port 29500 \
58
- train.py
59
- ```
60
-
61
- * `$NODE_RANK`: Index of the current machine (0 for the main node).
62
- * `$CHIEF_IP`: IP address of the main (rank-0) node.
63
- * Adjust the config file as needed for different hardware setups.
64
-
65
- If you are training on a **single machine**, simply set `--main_process_ip=127.0.0.1` and `--machine_rank=0`.
66
-
67
- If you wish to use your own pretrained MuQ model for initialization, simply modify the `model.mulan.audio_model.name` field in `config/model/muq_mulan.yaml`.
68
-
69
- ---
70
-
71
- ## Step 4: Convert to HF Checkpoint
72
-
73
- After training, convert your Fairseq-style checkpoint to HuggingFace format:
74
-
75
- ```bash
76
- python scripts/convert_muqmulan_fairseq_ckpt_to_huggingface.py \
77
- --checkpoint_path outputs/YYYY-MM-DD/hh-mm-ss/ckpt/mulan.1100.pt \
78
- --save_dir outputs/hf-username/My-MuQ-MuLan-large
79
- ```
80
-
81
- You can then load and use the model via the HuggingFace-style interface:
82
-
83
- ```python
84
- from muq import MuQMuLan
85
-
86
- # Load from local HuggingFace-style checkpoint
87
- mulan = MuQMuLan.from_pretrained("outputs/hf-username/My-MuQ-MuLan-large")
88
- mulan = mulan.to(device).eval()
89
-
90
- # Extract music embeddings
91
- audio_embeds = mulan(wavs=wavs)
92
-
93
- # Extract text embeddings (in English or Chinese)
94
- text_embeds = mulan(texts=texts)
95
-
96
- # Compute similarity
97
- sim = mulan.calc_similarity(audio_embeds, text_embeds)
98
- ```
99
-
100
- You can also upload your converted checkpoint to the Hugging Face Hub using `huggingface-cli`. The uploaded model will remain fully compatible with the `MuQMuLan.from_pretrained()` interface.
101
-
102
- ---
103
-
104
- ## Evaluation
105
-
106
- For evaluation, we recommend using the [sota-music-tagging-models](https://github.com/minzwon/sota-music-tagging-models/) toolkit. It supports various metrics and datasets widely used in music tagging and retrieval.
107
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/2gpu.yaml DELETED
@@ -1,17 +0,0 @@
1
- compute_environment: LOCAL_MACHINE
2
- debug: false
3
- distributed_type: MULTI_GPU
4
- downcast_bf16: 'no'
5
- enable_cpu_affinity: false
6
- gpu_ids: all
7
- machine_rank: 0
8
- main_training_function: main
9
- mixed_precision: 'no'
10
- num_machines: 1
11
- num_processes: 2
12
- rdzv_backend: static
13
- same_network: true
14
- tpu_env: []
15
- tpu_use_cluster: false
16
- tpu_use_sudo: false
17
- use_cpu: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/32gpu4node.yaml DELETED
@@ -1,19 +0,0 @@
1
- compute_environment: LOCAL_MACHINE
2
- debug: false
3
- distributed_type: MULTI_GPU
4
- downcast_bf16: 'no'
5
- enable_cpu_affinity: false
6
- gpu_ids: all
7
- machine_rank: 0
8
- main_process_ip: '11.214.123.26'
9
- main_process_port: 25545
10
- main_training_function: main
11
- mixed_precision: 'no'
12
- num_machines: 4
13
- num_processes: 32
14
- rdzv_backend: static
15
- same_network: true
16
- tpu_env: []
17
- tpu_use_cluster: false
18
- tpu_use_sudo: false
19
- use_cpu: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/32gpu4node_fp16.yaml DELETED
@@ -1,19 +0,0 @@
1
- compute_environment: LOCAL_MACHINE
2
- debug: false
3
- distributed_type: MULTI_GPU
4
- downcast_bf16: 'no'
5
- enable_cpu_affinity: false
6
- gpu_ids: all
7
- machine_rank: 0
8
- main_process_ip: $INDEX
9
- main_process_port: 25529
10
- main_training_function: main
11
- mixed_precision: fp16
12
- num_machines: 4
13
- num_processes: 32
14
- rdzv_backend: static
15
- same_network: true
16
- tpu_env: []
17
- tpu_use_cluster: false
18
- tpu_use_sudo: false
19
- use_cpu: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/32gpu4node_zero2.yaml DELETED
@@ -1,25 +0,0 @@
1
- compute_environment: LOCAL_MACHINE
2
- debug: false
3
- deepspeed_config:
4
- deepspeed_multinode_launcher: standard
5
- gradient_accumulation_steps: 1
6
- offload_optimizer_device: none
7
- offload_param_device: none
8
- zero3_init_flag: false
9
- zero_stage: 2
10
- distributed_type: DEEPSPEED
11
- downcast_bf16: 'no'
12
- enable_cpu_affinity: false
13
- machine_rank: 0
14
- main_process_ip: $CHIEF_IP
15
- main_process_port: 25520
16
- main_training_function: main
17
- mixed_precision: 'no'
18
- num_machines: 4
19
- num_processes: 32
20
- rdzv_backend: static
21
- same_network: true
22
- tpu_env: []
23
- tpu_use_cluster: false
24
- tpu_use_sudo: false
25
- use_cpu: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/8gpu_fp16.yaml DELETED
@@ -1,17 +0,0 @@
1
- compute_environment: LOCAL_MACHINE
2
- debug: false
3
- distributed_type: MULTI_GPU
4
- downcast_bf16: 'no'
5
- enable_cpu_affinity: false
6
- gpu_ids: all
7
- machine_rank: 0
8
- main_training_function: main
9
- mixed_precision: fp16
10
- num_machines: 1
11
- num_processes: 8
12
- rdzv_backend: static
13
- same_network: true
14
- tpu_env: []
15
- tpu_use_cluster: false
16
- tpu_use_sudo: false
17
- use_cpu: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/third_party/MuQ/src/recipes/contrastive_learning/config/accelerate/8gpu_fp16_zero2.yaml DELETED
@@ -1,22 +0,0 @@
1
- compute_environment: LOCAL_MACHINE
2
- debug: false
3
- deepspeed_config:
4
- gradient_accumulation_steps: 1
5
- offload_optimizer_device: none
6
- offload_param_device: none
7
- zero3_init_flag: false
8
- zero_stage: 2
9
- distributed_type: DEEPSPEED
10
- downcast_bf16: 'no'
11
- enable_cpu_affinity: false
12
- machine_rank: 0
13
- main_training_function: main
14
- mixed_precision: fp16
15
- num_machines: 1
16
- num_processes: 8
17
- rdzv_backend: static
18
- same_network: true
19
- tpu_env: []
20
- tpu_use_cluster: false
21
- tpu_use_sudo: false
22
- use_cpu: false