Vansh Chugh commited on
Commit
550f5fe
·
1 Parent(s): 05f1e67

cleanup: remove dead code (training/, audio modality, chunk_inference, unused imports)

Browse files
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import sys
2
- sys.stdout.reconfigure(line_buffering=True)
3
 
4
  import os
5
  import tempfile
@@ -8,7 +8,6 @@ import traceback
8
 
9
  import gradio as gr
10
  import librosa
11
- import numpy as np
12
  import torch
13
  import soundfile as sf
14
  from huggingface_hub import hf_hub_download
@@ -17,12 +16,8 @@ from pyharp import ModelCard, build_endpoint
17
  from models.resunet import ResUNet30
18
  from models.clap_encoder import CLAP_Encoder
19
 
20
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
21
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
22
 
23
- REPO_ID = "Audio-AGI/AudioSep"
24
- REPO_TYPE = "space"
25
-
26
  # Model loading state — populated by the background thread.
27
  ss_model = None
28
  query_encoder = None
@@ -33,24 +28,19 @@ model_error = None
33
  def load_model():
34
  global ss_model, query_encoder, model_loading, model_error
35
  try:
36
- print(f"Downloading AudioSep checkpoint from {REPO_ID}...", flush=True)
37
  main_ckpt_path = hf_hub_download(
38
- repo_id=REPO_ID,
39
- repo_type=REPO_TYPE,
40
  filename="checkpoint/audiosep_base_4M_steps.ckpt",
41
  )
42
-
43
- print(f"Downloading CLAP checkpoint from {REPO_ID}...", flush=True)
44
  clap_ckpt_path = hf_hub_download(
45
- repo_id=REPO_ID,
46
- repo_type=REPO_TYPE,
47
  filename="checkpoint/music_speech_audioset_epoch_15_esc_89.98.pt",
48
  )
49
 
50
- print("Loading CLAP encoder...", flush=True)
51
  query_encoder = CLAP_Encoder(pretrained_path=clap_ckpt_path).eval()
52
-
53
- print("Loading ResUNet30 separation model...", flush=True)
54
  _ss_model = ResUNet30(input_channels=1, output_channels=1, condition_size=512)
55
 
56
  # Load weights from the Lightning checkpoint — keys are prefixed with "ss_model."
@@ -64,10 +54,10 @@ def load_model():
64
  _ss_model.eval().to(DEVICE)
65
 
66
  ss_model = _ss_model
67
- print("Model loaded successfully.", flush=True)
68
  except Exception as e:
69
  model_error = str(e)
70
- print(f"Error loading model: {traceback.format_exc()}", flush=True)
71
  finally:
72
  model_loading = False
73
 
@@ -84,15 +74,15 @@ model_card = ModelCard(
84
 
85
 
86
  @torch.inference_mode()
87
- def process_fn(audio_path: str, text_query: str) -> str:
88
  if model_loading:
89
  raise gr.Error("Model is still loading, please wait a moment and try again.")
90
  if ss_model is None:
91
  raise gr.Error(f"Model failed to load: {model_error}")
92
 
93
- print(f"Separating [{audio_path}] with query [{text_query}]", flush=True)
94
 
95
- mixture, _ = librosa.load(audio_path, sr=32000, mono=True)
96
 
97
  conditions = query_encoder.get_query_embed(
98
  modality="text",
 
1
  import sys
2
+ sys.stdout.reconfigure(line_buffering=True) # flush print() calls immediately so HF Space logs are live
3
 
4
  import os
5
  import tempfile
 
8
 
9
  import gradio as gr
10
  import librosa
 
11
  import torch
12
  import soundfile as sf
13
  from huggingface_hub import hf_hub_download
 
16
  from models.resunet import ResUNet30
17
  from models.clap_encoder import CLAP_Encoder
18
 
 
19
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
20
 
 
 
 
21
  # Model loading state — populated by the background thread.
22
  ss_model = None
23
  query_encoder = None
 
28
  def load_model():
29
  global ss_model, query_encoder, model_loading, model_error
30
  try:
31
+ print("Downloading checkpoints...")
32
  main_ckpt_path = hf_hub_download(
33
+ repo_id="Audio-AGI/AudioSep",
34
+ repo_type="space",
35
  filename="checkpoint/audiosep_base_4M_steps.ckpt",
36
  )
 
 
37
  clap_ckpt_path = hf_hub_download(
38
+ repo_id="Audio-AGI/AudioSep",
39
+ repo_type="space",
40
  filename="checkpoint/music_speech_audioset_epoch_15_esc_89.98.pt",
41
  )
42
 
 
43
  query_encoder = CLAP_Encoder(pretrained_path=clap_ckpt_path).eval()
 
 
44
  _ss_model = ResUNet30(input_channels=1, output_channels=1, condition_size=512)
45
 
46
  # Load weights from the Lightning checkpoint — keys are prefixed with "ss_model."
 
54
  _ss_model.eval().to(DEVICE)
55
 
56
  ss_model = _ss_model
57
+ print("Model loaded successfully.")
58
  except Exception as e:
59
  model_error = str(e)
60
+ print(f"Error loading model: {traceback.format_exc()}")
61
  finally:
62
  model_loading = False
63
 
 
74
 
75
 
76
  @torch.inference_mode()
77
+ def process_fn(audio_path: str, text_query: str):
78
  if model_loading:
79
  raise gr.Error("Model is still loading, please wait a moment and try again.")
80
  if ss_model is None:
81
  raise gr.Error(f"Model failed to load: {model_error}")
82
 
83
+ print(f"Separating [{audio_path}] with query [{text_query}]")
84
 
85
+ mixture, _ = librosa.load(audio_path, sr=32000, mono=True) # model expects 32k audio
86
 
87
  conditions = query_encoder.get_query_embed(
88
  modality="text",
models/CLAP/training/__init__.py DELETED
File without changes
models/CLAP/training/audioset_textmap.npy DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:bada103070d92f9eadd33e1b4f45ec8583f59080ef218c966b43294bd4c86d5b
3
- size 84448
 
 
 
 
models/CLAP/training/data.py DELETED
@@ -1,966 +0,0 @@
1
- import ast
2
- import json
3
- import logging
4
- import math
5
- import os
6
- import random
7
- import io
8
- import copy
9
- import tempfile
10
- from dataclasses import dataclass
11
- from functools import partial
12
- from pathlib import Path
13
- import numpy as np
14
- import torch
15
- import torch.nn as nn
16
- import torch.nn.functional as F
17
- import torchvision.transforms
18
- import braceexpand
19
- import soundfile as sf
20
- from PIL import Image
21
- from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler
22
- from torch.utils.data.distributed import DistributedSampler
23
-
24
- try:
25
- import horovod.torch as hvd
26
- except ImportError:
27
- hvd = None
28
-
29
- try:
30
- import torchaudio
31
- except ImportError:
32
- torchaudio = None
33
-
34
- from models.CLAP.open_clip import tokenize
35
-
36
-
37
- def tokenizer(text):
38
- return tokenize(text).squeeze(0)
39
-
40
-
41
- from transformers import RobertaTokenizer
42
-
43
- tokenize = RobertaTokenizer.from_pretrained("roberta-base")
44
-
45
-
46
- def tokenizer(text):
47
- result = tokenize(
48
- text,
49
- padding="max_length",
50
- truncation=True,
51
- max_length=77,
52
- return_tensors="pt",
53
- )
54
- return {k: v.squeeze(0) for k, v in result.items()}
55
-
56
-
57
- # initizlied the audioset map
58
- _AUDIOSET_MAP_PATH = os.path.join(Path(__file__).parent, "audioset_textmap.npy")
59
- _AUDIOSET_MAP = np.load(_AUDIOSET_MAP_PATH, allow_pickle=True)
60
-
61
-
62
- def int16_to_float32(x):
63
- return (x / 32767.0).astype(np.float32)
64
-
65
-
66
- def float32_to_int16(x):
67
- x = np.clip(x, a_min=-1.0, a_max=1.0)
68
- return (x * 32767.0).astype(np.int16)
69
-
70
-
71
- # For Toy Dataset
72
- class ToyDataset(Dataset):
73
- def __init__(self, index_path, ipc, config, eval_mode=False):
74
- """Toy Dataset for testing the audioset input with text labels
75
- Parameters
76
- ----------
77
- index_path: str
78
- the link to the h5 file of each audio
79
- idc: str
80
- the link to the npy file, the number of samples in each class
81
- config: dict
82
- the audio cfg file
83
- eval_model (bool): to indicate if the dataset is a testing dataset
84
- """
85
- self.audio_cfg = config["audio_cfg"]
86
- self.text_cfg = config["text_cfg"]
87
- self.fp = h5py.File(index_path, "r")
88
- self.ipc = np.load(ipc, allow_pickle=True)
89
- self.total_size = len(self.fp["audio_name"])
90
- self.classes_num = self.audio_cfg["class_num"]
91
- self.eval_mode = eval_mode
92
-
93
- if not eval_mode:
94
- self.generate_queue()
95
- else:
96
- self.queue = []
97
- for i in range(self.total_size):
98
- target = self.fp["target"][i]
99
- if np.sum(target) > 0:
100
- self.queue.append(i)
101
- self.total_size = len(self.queue)
102
- logging.info("total dataset size: %d" % (self.total_size))
103
- logging.info("class num: %d" % (self.classes_num))
104
-
105
- def time_shifting(self, x):
106
- frame_num = len(x)
107
- shift_len = random.randint(0, frame_num - 1)
108
- new_sample = np.concatenate([x[shift_len:], x[:shift_len]], axis=0)
109
- return new_sample
110
-
111
- def generate_queue(self):
112
- self.queue = []
113
- while len(self.queue) < self.total_size:
114
- class_set = [*range(self.classes_num)]
115
- random.shuffle(class_set)
116
- self.queue += [
117
- self.ipc[d][random.randint(0, len(self.ipc[d]) - 1)] for d in class_set
118
- ]
119
- self.queue = self.queue[: self.total_size]
120
-
121
- logging.info("queue regenerated:%s" % (self.queue[-5:]))
122
-
123
- def crop_wav(self, x):
124
- crop_size = self.audio_cfg["crop_size"]
125
- crop_pos = random.randint(0, len(x) - crop_size - 1)
126
- return x[crop_pos : crop_pos + crop_size]
127
-
128
- def prompt_text(self, target):
129
- events = _AUDIOSET_MAP[np.where(target > 0)]
130
- event_text = "The sounds of " + ", ".join(events[:-1]) + " and " + events[-1]
131
- text = tokenize(event_text)[0]
132
- return text
133
-
134
- def __getitem__(self, index):
135
- """Load waveform, text, and target of an audio clip
136
-
137
- Parameters
138
- ----------
139
- index: int
140
- the index number
141
- Return
142
- ------
143
- output: dict {
144
- "hdf5_path": str,
145
- "index_in_hdf5": int,
146
- "audio_name": str,
147
- "waveform": list (audio_length,),
148
- "target": list (class_num, ),
149
- "text": torch.tensor (context_length,)
150
- }
151
- the output dictionary
152
- """
153
- s_index = self.queue[index]
154
-
155
- audio_name = self.fp["audio_name"][s_index].decode()
156
- # Hardcode here CHANGE
157
- hdf5_path = (
158
- self.fp["hdf5_path"][s_index]
159
- .decode()
160
- .replace(
161
- "../workspace",
162
- "/home/la/kechen/Research/ke_zsasp/workspace",
163
- )
164
- )
165
- r_idx = self.fp["index_in_hdf5"][s_index]
166
- target = self.fp["target"][s_index].astype(np.float32)
167
- text = self.prompt_text(target)
168
- with h5py.File(hdf5_path, "r") as f:
169
- waveform = int16_to_float32(f["waveform"][r_idx])[
170
- : self.audio_cfg["clip_samples"]
171
- ]
172
- assert (
173
- len(waveform) == self.audio_cfg["clip_samples"]
174
- ), "The sample length is not match"
175
- # Time shift
176
- # if (self.config.enable_time_shift) and (not self.eval_mode):
177
- # waveform = self.time_shifting(waveform)
178
- # # Label Enhance
179
- # if (self.config.crop_size is not None) and (not self.eval_mode):
180
- # waveform = self.crop_wav(waveform)
181
- # # the label enhance rate is fixed 0.5
182
- # if (self.config.enable_label_enhance) and (not self.eval_mode) and random.random() < 0.5:
183
- # kidx = np.where(target)[0]
184
- # for k in kidx:
185
- # for add_key in self.class_map[k][1]:
186
- # target[add_key] = 1.0
187
- # if len(self.class_map[k][2]) > 0:
188
- # add_key = random.choice(self.class_map[k][2])
189
- # target[add_key] = 1.0
190
-
191
- # missing the text input
192
- mel_spec = get_mel(torch.from_numpy(waveform), self.audio_cfg)[None, :, :]
193
- mel_spec = (
194
- torch.cat(
195
- [mel_spec, mel_spec.clone(), mel_spec.clone(), mel_spec.clone()], dim=0
196
- )
197
- .cpu()
198
- .numpy()
199
- )
200
- longer = random.choice([True, False])
201
- if longer == False:
202
- mel_spec[1:, :, :] = 0.0
203
- data_dict = {
204
- "hdf5_path": hdf5_path,
205
- "index_in_hdf5": r_idx,
206
- "audio_name": audio_name,
207
- "waveform": waveform,
208
- "class_label": target,
209
- "text": text,
210
- "longer": longer,
211
- "mel_fusion": mel_spec,
212
- }
213
- return data_dict
214
-
215
- def __len__(self):
216
- return self.total_size
217
-
218
-
219
- class CsvDataset(Dataset):
220
- def __init__(self, input_filename, transforms, img_key, caption_key, sep="\t"):
221
- logging.debug(f"Loading csv data from {input_filename}.")
222
- df = pd.read_csv(input_filename, sep=sep)
223
-
224
- self.images = df[img_key].tolist()
225
- self.captions = df[caption_key].tolist()
226
- self.transforms = transforms
227
- logging.debug("Done loading data.")
228
-
229
- def __len__(self):
230
- return len(self.captions)
231
-
232
- def __getitem__(self, idx):
233
- images = self.transforms(Image.open(str(self.images[idx])))
234
- texts = tokenize([str(self.captions[idx])])[0]
235
- return images, texts
236
-
237
-
238
- @dataclass
239
- class DataInfo:
240
- dataloader: DataLoader
241
- sampler: DistributedSampler
242
-
243
-
244
- def preprocess_txt(text):
245
- return tokenize([str(text)])[0]
246
-
247
-
248
- def get_dataset_size(shards, sizefilepath_=None, is_local=True):
249
- if isinstance(shards, list):
250
- size_list = []
251
- for s in shards:
252
- size_list.append(
253
- get_dataset_size(s, sizefilepath_=sizefilepath_, is_local=is_local)[0]
254
- )
255
- else:
256
- if not is_local:
257
- for n in dataset_split.keys():
258
- if n in shards.split("/"):
259
- break
260
- for s in dataset_split[n]:
261
- if s in shards.split("/"):
262
- break
263
- sizefilepath_ = f"./json_files/{n}/{s}/sizes.json"
264
- shards_list = list(braceexpand.braceexpand(shards))
265
- dir_path = os.path.dirname(shards)
266
- if sizefilepath_ is not None:
267
- sizes = json.load(open(sizefilepath_, "r"))
268
- total_size = sum(
269
- [
270
- int(sizes[os.path.basename(shard.replace(".tar -", ".tar"))])
271
- for shard in shards_list
272
- ]
273
- )
274
- else:
275
- sizes_filename = os.path.join(dir_path, "sizes.json")
276
- len_filename = os.path.join(dir_path, "__len__")
277
- if os.path.exists(sizes_filename):
278
- sizes = json.load(open(sizes_filename, "r"))
279
- total_size = sum(
280
- [int(sizes[os.path.basename(shard)]) for shard in shards_list]
281
- )
282
- elif os.path.exists(len_filename):
283
- # FIXME this used to be eval(open(...)) but that seemed rather unsafe
284
- total_size = ast.literal_eval(open(len_filename, "r").read())
285
- else:
286
- raise Exception(
287
- "Cannot find sizes file for dataset. Please specify the path to the file."
288
- )
289
- # total_size = None # num samples undefined
290
- # some common dataset sizes (at time of authors last download)
291
- # cc3m-train: 2905954
292
- # cc12m: 10968539
293
- # LAION-400m: 407332084
294
- num_shards = len(shards_list)
295
- if isinstance(shards, list):
296
- return sum(size_list), len(shards)
297
- else:
298
- return total_size, num_shards
299
-
300
-
301
- def get_imagenet(args, preprocess_fns, split):
302
- assert split in ["train", "val", "v2"]
303
- is_train = split == "train"
304
- preprocess_train, preprocess_val = preprocess_fns
305
-
306
- if split == "v2":
307
- from imagenetv2_pytorch import ImageNetV2Dataset
308
-
309
- dataset = ImageNetV2Dataset(location=args.imagenet_v2, transform=preprocess_val)
310
- else:
311
- if is_train:
312
- data_path = args.imagenet_train
313
- preprocess_fn = preprocess_train
314
- else:
315
- data_path = args.imagenet_val
316
- preprocess_fn = preprocess_val
317
- assert data_path
318
-
319
- dataset = datasets.ImageFolder(data_path, transform=preprocess_fn)
320
-
321
- if is_train:
322
- idxs = np.zeros(len(dataset.targets))
323
- target_array = np.array(dataset.targets)
324
- k = 50
325
- for c in range(1000):
326
- m = target_array == c
327
- n = len(idxs[m])
328
- arr = np.zeros(n)
329
- arr[:k] = 1
330
- np.random.shuffle(arr)
331
- idxs[m] = arr
332
-
333
- idxs = idxs.astype("int")
334
- sampler = SubsetRandomSampler(np.where(idxs)[0])
335
- else:
336
- sampler = None
337
-
338
- dataloader = torch.utils.data.DataLoader(
339
- dataset,
340
- batch_size=args.batch_size,
341
- num_workers=args.workers,
342
- sampler=sampler,
343
- )
344
-
345
- return DataInfo(dataloader, sampler)
346
-
347
-
348
- def count_samples(dataloader):
349
- os.environ["WDS_EPOCH"] = "0"
350
- n_elements, n_batches = 0, 0
351
- for images, texts in dataloader:
352
- n_batches += 1
353
- n_elements += len(images)
354
- assert len(images) == len(texts)
355
- return n_elements, n_batches
356
-
357
-
358
- def filter_no_caption(sample):
359
- return "txt" in sample
360
-
361
-
362
- def log_and_continue(exn):
363
- """Call in an exception handler to ignore any exception, isssue a warning, and continue."""
364
- logging.warning(f"Handling webdataset error ({repr(exn)}). Ignoring.")
365
- return True
366
-
367
-
368
- _SHARD_SHUFFLE_SIZE = 2000
369
- _SHARD_SHUFFLE_INITIAL = 500
370
- _SAMPLE_SHUFFLE_SIZE = 5000
371
- _SAMPLE_SHUFFLE_INITIAL = 1000
372
-
373
-
374
- def sample_prop(sizefile, inputs, proportion, is_local=True):
375
- """
376
- Sample a proportion of the data.
377
- """
378
- file_path_dict = {
379
- os.path.split(inputs[i])[1]: os.path.split(inputs[i])[0]
380
- for i in range(len(inputs))
381
- }
382
- sampled_filepath_dict = {}
383
- sampled_size_dict = {}
384
- if not is_local:
385
- if os.path.exists("sizes.json"):
386
- os.remove("sizes.json")
387
- wget.download(sizefile, "sizes.json")
388
- sizefile = "sizes.json"
389
- with open(sizefile, "r", encoding="UTF-8") as f:
390
- load_dict = json.load(f)
391
- L = int(len(file_path_dict) * proportion)
392
- subkeys = random.sample(file_path_dict.keys(), L)
393
- for k in subkeys:
394
- sampled_size_dict[k] = load_dict[k]
395
- sampled_filepath_dict[k] = file_path_dict[k]
396
- return (
397
- sum(sampled_size_dict.values()),
398
- L,
399
- [os.path.join(v, k) for k, v in sampled_filepath_dict.items()],
400
- sampled_size_dict,
401
- )
402
-
403
-
404
- def get_mel(audio_data, audio_cfg):
405
- # mel shape: (n_mels, T)
406
- mel = torchaudio.transforms.MelSpectrogram(
407
- sample_rate=audio_cfg["sample_rate"],
408
- n_fft=audio_cfg["window_size"],
409
- win_length=audio_cfg["window_size"],
410
- hop_length=audio_cfg["hop_size"],
411
- center=True,
412
- pad_mode="reflect",
413
- power=2.0,
414
- norm=None,
415
- onesided=True,
416
- n_mels=64,
417
- f_min=audio_cfg["fmin"],
418
- f_max=audio_cfg["fmax"],
419
- ).to(audio_data.device)
420
- mel = mel(audio_data)
421
- # Align to librosa:
422
- # librosa_melspec = librosa.feature.melspectrogram(
423
- # waveform,
424
- # sr=audio_cfg['sample_rate'],
425
- # n_fft=audio_cfg['window_size'],
426
- # hop_length=audio_cfg['hop_size'],
427
- # win_length=audio_cfg['window_size'],
428
- # center=True,
429
- # pad_mode="reflect",
430
- # power=2.0,
431
- # n_mels=64,
432
- # norm=None,
433
- # htk=True,
434
- # f_min=audio_cfg['fmin'],
435
- # f_max=audio_cfg['fmax']
436
- # )
437
- # we use log mel spectrogram as input
438
- mel = torchaudio.transforms.AmplitudeToDB(top_db=None)(mel)
439
- return mel.T # (T, n_mels)
440
-
441
-
442
- def get_audio_features(
443
- sample, audio_data, max_len, data_truncating, data_filling, audio_cfg
444
- ):
445
- """
446
- Calculate and add audio features to sample.
447
- Sample: a dict containing all the data of current sample.
448
- audio_data: a tensor of shape (T) containing audio data.
449
- max_len: the maximum length of audio data.
450
- data_truncating: the method of truncating data.
451
- data_filling: the method of filling data.
452
- audio_cfg: a dict containing audio configuration. Comes from model_cfg['audio_cfg'].
453
- """
454
- with torch.no_grad():
455
- if len(audio_data) > max_len:
456
- if data_truncating == "rand_trunc":
457
- longer = torch.tensor([True])
458
- elif data_truncating == "fusion":
459
- # fusion
460
- mel = get_mel(audio_data, audio_cfg)
461
- # split to three parts
462
- chunk_frames = (
463
- max_len // audio_cfg["hop_size"] + 1
464
- ) # the +1 related to how the spectrogram is computed
465
- total_frames = mel.shape[0]
466
- if chunk_frames == total_frames:
467
- # there is a corner case where the audio length is
468
- # larger than max_len but smaller than max_len+hop_size.
469
- # In this case, we just use the whole audio.
470
- mel_fusion = torch.stack([mel, mel, mel, mel], dim=0)
471
- sample["mel_fusion"] = mel_fusion
472
- longer = torch.tensor([False])
473
- else:
474
- ranges = np.array_split(
475
- list(range(0, total_frames - chunk_frames + 1)), 3
476
- )
477
- # print('total_frames-chunk_frames:', total_frames-chunk_frames,
478
- # 'len(audio_data):', len(audio_data),
479
- # 'chunk_frames:', chunk_frames,
480
- # 'total_frames:', total_frames)
481
- if len(ranges[1]) == 0:
482
- # if the audio is too short, we just use the first chunk
483
- ranges[1] = [0]
484
- if len(ranges[2]) == 0:
485
- # if the audio is too short, we just use the first chunk
486
- ranges[2] = [0]
487
- # randomly choose index for each part
488
- idx_front = np.random.choice(ranges[0])
489
- idx_middle = np.random.choice(ranges[1])
490
- idx_back = np.random.choice(ranges[2])
491
- # select mel
492
- mel_chunk_front = mel[idx_front : idx_front + chunk_frames, :]
493
- mel_chunk_middle = mel[idx_middle : idx_middle + chunk_frames, :]
494
- mel_chunk_back = mel[idx_back : idx_back + chunk_frames, :]
495
-
496
- # shrink the mel
497
- mel_shrink = torchvision.transforms.Resize(size=[chunk_frames, 64])(
498
- mel[None]
499
- )[0]
500
- # logging.info(f"mel_shrink.shape: {mel_shrink.shape}")
501
-
502
- # stack
503
- mel_fusion = torch.stack(
504
- [mel_chunk_front, mel_chunk_middle, mel_chunk_back, mel_shrink],
505
- dim=0,
506
- )
507
- sample["mel_fusion"] = mel_fusion
508
- longer = torch.tensor([True])
509
- else:
510
- raise NotImplementedError(
511
- f"data_truncating {data_truncating} not implemented"
512
- )
513
- # random crop to max_len (for compatibility)
514
- overflow = len(audio_data) - max_len
515
- idx = np.random.randint(0, overflow + 1)
516
- audio_data = audio_data[idx : idx + max_len]
517
-
518
- else: # padding if too short
519
- if len(audio_data) < max_len: # do nothing if equal
520
- if data_filling == "repeatpad":
521
- n_repeat = int(max_len / len(audio_data))
522
- audio_data = audio_data.repeat(n_repeat)
523
- # audio_data = audio_data.unsqueeze(0).unsqueeze(0).unsqueeze(0)
524
- # audio_data = F.interpolate(audio_data,size=max_len,mode="bicubic")[0,0,0]
525
- audio_data = F.pad(
526
- audio_data,
527
- (0, max_len - len(audio_data)),
528
- mode="constant",
529
- value=0,
530
- )
531
- elif data_filling == "pad":
532
- audio_data = F.pad(
533
- audio_data,
534
- (0, max_len - len(audio_data)),
535
- mode="constant",
536
- value=0,
537
- )
538
- elif data_filling == "repeat":
539
- n_repeat = int(max_len / len(audio_data))
540
- audio_data = audio_data.repeat(n_repeat + 1)[:max_len]
541
- else:
542
- raise NotImplementedError(
543
- f"data_filling {data_filling} not implemented"
544
- )
545
- if data_truncating == "fusion":
546
- mel = get_mel(audio_data, audio_cfg)
547
- mel_fusion = torch.stack([mel, mel, mel, mel], dim=0)
548
- sample["mel_fusion"] = mel_fusion
549
- longer = torch.tensor([False])
550
-
551
- sample["longer"] = longer
552
- sample["waveform"] = audio_data
553
-
554
- return sample
555
-
556
-
557
- def preprocess(
558
- sample,
559
- audio_ext,
560
- text_ext,
561
- max_len,
562
- audio_cfg,
563
- class_index_dict=None,
564
- data_filling="pad",
565
- data_truncating="rand_trunc",
566
- text_augment_selection=None,
567
- ):
568
- """
569
- Preprocess a single sample for wdsdataloader.
570
- """
571
- audio_data, orig_sr = sf.read(io.BytesIO(sample[audio_ext]))
572
- audio_data = int16_to_float32(float32_to_int16(audio_data))
573
- audio_data = torch.tensor(audio_data).float()
574
-
575
- # TODO: (yusong) to be include in the future
576
- # # if torchaudio not installed, use soundfile to load audio
577
- # if torchaudio is None:
578
- # audio_data, orig_sr = sf.read(io.BytesIO(sample[audio_ext]))
579
- # audio_data = torch.tensor(audio_data).float()
580
- # else:
581
- # # https://github.com/webdataset/webdataset/blob/main/webdataset/autodecode.py
582
- # with tempfile.TemporaryDirectory() as dirname:
583
- # os.makedirs(dirname, exist_ok=True)
584
- # fname = os.path.join(dirname, f"file.flac")
585
- # with open(fname, "wb") as stream:
586
- # stream.write(sample[audio_ext])
587
- # audio_data, orig_sr = torchaudio.load(fname)
588
- # audio_data = audio_data[0, :].float()
589
-
590
- sample = get_audio_features(
591
- sample, audio_data, max_len, data_truncating, data_filling, audio_cfg
592
- )
593
- del sample[audio_ext]
594
-
595
- try:
596
- json_dict_raw = json.loads(sample[text_ext].decode("utf-8"))
597
- except:
598
- print("sample[__url__]:", sample["__url__"])
599
-
600
- # For selecting augmented text from dataset
601
- if text_augment_selection is None or text_augment_selection == "none":
602
- texts = json_dict_raw["text"]
603
- elif text_augment_selection == "all":
604
- if "text_augment_all" in json_dict_raw.keys():
605
- texts = json_dict_raw["text_augment_all"]
606
- else:
607
- texts = json_dict_raw["text"]
608
- elif text_augment_selection == "augment_only":
609
- if "text_augment_all" in json_dict_raw.keys():
610
- if json_dict_raw["text_augment_t5"] is None:
611
- texts = json_dict_raw["text"]
612
- else:
613
- texts = json_dict_raw["text_augment_t5"]
614
- else:
615
- texts = json_dict_raw["text"]
616
- else:
617
- raise NotImplementedError(
618
- f"text_augment_selection {text_augment_selection} not implemented"
619
- )
620
- sample["full_text"] = texts
621
-
622
- if isinstance(texts, list) and isinstance(texts[0], str) and len(texts) > 1:
623
- texts = random.choice(texts)
624
- sample["raw_text"] = texts
625
- sample["text"] = tokenizer(texts) # text shape: [num_token]
626
- if class_index_dict is not None:
627
- # https://stackoverflow.com/questions/48004243/how-to-share-large-read-only-dictionary-list-across-processes-in-multiprocessing
628
- # https://stackoverflow.com/questions/45693949/storing-strings-in-a-multiprocessing-sharedctypes-array
629
- # key, val = class_index_dict
630
- # key = key[:].split('\n')
631
- # _dict = {k: v for k, v in zip(key, val)}
632
- sample["class_label"] = np.zeros(len(class_index_dict.keys()))
633
- for x in json_dict_raw["tag"]:
634
- sample["class_label"][class_index_dict[x]] = 1
635
- sample["class_label"] = torch.tensor(sample["class_label"]).float()
636
- del sample[text_ext]
637
- sample["audio_name"] = sample["__key__"].split("/")[-1] + "." + audio_ext
638
- sample["text_name"] = sample["__key__"].split("/")[-1] + "." + text_ext
639
- sample["audio_orig_sr"] = orig_sr
640
- return sample
641
-
642
-
643
- def collate_fn(batch):
644
- """
645
- Collate function for wdsdataloader.
646
- batch: a list of dict, each dict is a sample
647
- """
648
- # concatenate values in each dictionary. if it is a tensor, concatenate. if it is a list, extend.
649
- batch_dict = {}
650
- for k in batch[0].keys():
651
- if isinstance(batch[0][k], dict): # dealwith bert tokenizer output
652
- batch_dict[k] = {}
653
- for kk in batch[0][k].keys():
654
- tmp = []
655
- for i in range(len(batch)):
656
- tmp.append(batch[i][k][kk])
657
- batch_dict[k][kk] = torch.vstack(tmp)
658
- elif isinstance(batch[0][k], torch.Tensor):
659
- batch_dict[k] = torch.stack([sample[k] for sample in batch])
660
- elif isinstance(batch[0][k], np.ndarray):
661
- batch_dict[k] = torch.tensor(np.stack([sample[k] for sample in batch]))
662
- else:
663
- batch_dict[k] = [sample[k] for sample in batch]
664
- return batch_dict
665
-
666
-
667
- def get_wds_dataset(
668
- args,
669
- model_cfg,
670
- is_train,
671
- audio_ext="flac",
672
- text_ext="json",
673
- max_len=480000,
674
- proportion=1.0,
675
- sizefilepath_=None,
676
- is_local=None,
677
- ):
678
- """
679
- Get a dataset for wdsdataloader.
680
- """
681
- if is_local is None and (not args.remotedata is None):
682
- is_local = not args.remotedata
683
-
684
- input_shards = args.train_data if is_train else args.val_data
685
- assert input_shards is not None
686
-
687
- if not sizefilepath_ is None:
688
- sizefilepath = sizefilepath_
689
- else:
690
- sizefilepath = os.path.join(os.path.dirname(input_shards[0]), "sizes.json")
691
-
692
- if proportion != 1.0:
693
- num_samples, num_shards, input_shards, _ = sample_prop(
694
- sizefilepath, input_shards, proportion, is_local=is_local
695
- )
696
- else:
697
- num_samples, num_shards = get_dataset_size(
698
- input_shards, sizefilepath_=sizefilepath_, is_local=is_local
699
- )
700
-
701
- if not num_samples:
702
- if is_train:
703
- num_samples = args.train_num_samples
704
- if not num_samples:
705
- raise RuntimeError(
706
- "Currently, number of dataset samples must be specified for training dataset. "
707
- "Please specify via `--train-num-samples` if no dataset length info present."
708
- )
709
- else:
710
- num_samples = (
711
- args.val_num_samples or 0
712
- ) # eval will just exhaust the iterator if not specified
713
-
714
- pipeline = [wds.SimpleShardList(input_shards)]
715
- # at this point we have an iterator over all the shards
716
- # TODO: (yusong): add a if statement of distributed. If not, we don't need to split_by_node
717
- if is_train or args.parallel_eval:
718
- pipeline.extend(
719
- [
720
- wds.detshuffle(
721
- bufsize=_SHARD_SHUFFLE_SIZE,
722
- initial=_SHARD_SHUFFLE_INITIAL,
723
- seed=args.seed,
724
- ),
725
- wds.split_by_node,
726
- wds.split_by_worker,
727
- # at this point, we have an iterator over the shards assigned to each worker at each node
728
- wds.tarfile_to_samples(handler=log_and_continue),
729
- wds.shuffle(
730
- bufsize=_SAMPLE_SHUFFLE_SIZE,
731
- initial=_SAMPLE_SHUFFLE_INITIAL,
732
- rng=random.Random(args.seed),
733
- ),
734
- # wds.repeatedly, # FIXME determine if this is beneficial
735
- ]
736
- )
737
- else:
738
- pipeline.extend(
739
- [
740
- wds.split_by_worker,
741
- # at this point, we have an iterator over the shards assigned to each worker
742
- wds.tarfile_to_samples(handler=log_and_continue),
743
- ]
744
- )
745
- pipeline.append(
746
- wds.map(
747
- partial(
748
- preprocess,
749
- audio_ext=audio_ext,
750
- text_ext=text_ext,
751
- max_len=max_len,
752
- audio_cfg=model_cfg["audio_cfg"],
753
- class_index_dict=copy.deepcopy(args.class_index_dict),
754
- data_filling=args.data_filling,
755
- data_truncating=args.data_truncating,
756
- text_augment_selection=args.text_augment_selection,
757
- )
758
- ),
759
- )
760
-
761
- pipeline.append(
762
- wds.batched(
763
- args.batch_size,
764
- partial=not (is_train or args.parallel_eval),
765
- collation_fn=collate_fn,
766
- )
767
- )
768
-
769
- dataset = wds.DataPipeline(*pipeline)
770
- if is_train or args.parallel_eval:
771
- # (yusong): Currently parallel evaluation will be not precise as we are repeat the last few samples.
772
- # (yusong): See comments below.
773
- # roll over and repeat a few samples to get same number of full batches on each node
774
- global_batch_size = args.batch_size * args.world_size
775
- num_batches = math.ceil(num_samples / global_batch_size)
776
- num_workers = max(1, args.workers)
777
- num_worker_batches = math.ceil(
778
- num_batches / num_workers
779
- ) # per dataloader worker
780
- num_batches = num_worker_batches * num_workers
781
- num_samples = num_batches * global_batch_size
782
- dataset = dataset.with_epoch(
783
- num_worker_batches
784
- ) # each worker is iterating over this
785
- else:
786
- # last batches are partial, eval is done on single (master) node
787
- num_batches = math.ceil(num_samples / args.batch_size)
788
-
789
- kwargs = {}
790
- if args.horovod: # multi-node training on summit
791
- kwargs["multiprocessing_context"] = "forkserver"
792
-
793
- dataloader = wds.WebLoader(
794
- dataset, batch_size=None, shuffle=False, num_workers=args.workers, **kwargs
795
- )
796
-
797
- # FIXME not clear which approach is better, with_epoch before vs after dataloader?
798
- # hoping to resolve via https://github.com/webdataset/webdataset/issues/169
799
- # if is_train:
800
- # # roll over and repeat a few samples to get same number of full batches on each node
801
- # global_batch_size = args.batch_size * args.world_size
802
- # num_batches = math.ceil(num_samples / global_batch_size)
803
- # num_workers = max(1, args.workers)
804
- # num_batches = math.ceil(num_batches / num_workers) * num_workers
805
- # num_samples = num_batches * global_batch_size
806
- # dataloader = dataloader.with_epoch(num_batches)
807
- # else:
808
- # # last batches are partial, eval is done on single (master) node
809
- # num_batches = math.ceil(num_samples / args.batch_size)
810
-
811
- # add meta-data to dataloader instance for convenience
812
- dataloader.num_batches = num_batches
813
- dataloader.num_samples = num_samples
814
-
815
- return DataInfo(dataloader, None)
816
-
817
-
818
- def wds_batch_list2dict(
819
- batch,
820
- keys=[
821
- "__url__",
822
- "__key__",
823
- "waveform",
824
- "text",
825
- "raw_text",
826
- "audio_name",
827
- "text_name",
828
- "audio_orig_sr",
829
- ],
830
- ):
831
- """
832
- Return a dictionary of the batch, with keys as the names of the fields.
833
- """
834
- assert len(keys) == len(
835
- batch
836
- ), "batch must have same number of keys as keys argument"
837
- return {keys[i]: batch[i] for i in range(len(batch))}
838
-
839
-
840
- def get_csv_dataset(args, preprocess_fn, is_train):
841
- input_filename = args.train_data if is_train else args.val_data
842
- assert input_filename
843
- dataset = CsvDataset(
844
- input_filename,
845
- preprocess_fn,
846
- img_key=args.csv_img_key,
847
- caption_key=args.csv_caption_key,
848
- sep=args.csv_separator,
849
- )
850
- num_samples = len(dataset)
851
- sampler = DistributedSampler(dataset) if args.distributed and is_train else None
852
- shuffle = is_train and sampler is None
853
-
854
- dataloader = DataLoader(
855
- dataset,
856
- batch_size=args.batch_size,
857
- shuffle=shuffle,
858
- num_workers=args.workers,
859
- pin_memory=True,
860
- sampler=sampler,
861
- drop_last=is_train,
862
- )
863
- dataloader.num_samples = num_samples
864
- dataloader.num_batches = len(dataloader)
865
-
866
- return DataInfo(dataloader, sampler)
867
-
868
-
869
- def get_toy_dataset(args, model_cfg, is_train):
870
- index_path = args.train_data if is_train else args.val_data
871
- ipc_path = args.train_ipc if is_train else args.val_ipc
872
- assert index_path and ipc_path
873
- eval_mode = not is_train
874
- dataset = ToyDataset(index_path, ipc_path, model_cfg, eval_mode=eval_mode)
875
-
876
- num_samples = len(dataset)
877
- sampler = (
878
- DistributedSampler(dataset, shuffle=False)
879
- if args.distributed and is_train
880
- else None
881
- )
882
-
883
- dataloader = DataLoader(
884
- dataset,
885
- batch_size=args.batch_size,
886
- shuffle=False,
887
- num_workers=args.workers,
888
- sampler=sampler,
889
- drop_last=is_train,
890
- )
891
- dataloader.num_samples = num_samples
892
- dataloader.num_batches = len(dataloader)
893
-
894
- return DataInfo(dataloader, sampler)
895
-
896
-
897
- def get_dataset_fn(data_path, dataset_type):
898
- if dataset_type == "webdataset":
899
- return get_wds_dataset
900
- elif dataset_type == "csv":
901
- return get_csv_dataset
902
- elif dataset_type == "auto":
903
- ext = data_path.split(".")[-1]
904
- if ext in ["csv", "tsv"]:
905
- return get_csv_dataset
906
- elif ext in ["tar"]:
907
- return get_wds_dataset
908
- else:
909
- raise ValueError(
910
- f"Tried to figure out dataset type, but failed for extention {ext}."
911
- )
912
- elif dataset_type == "toy":
913
- return get_toy_dataset
914
- else:
915
- raise ValueError(f"Unsupported dataset type: {dataset_type}")
916
-
917
-
918
- def get_data(args, model_cfg):
919
- data = {}
920
-
921
- args.class_index_dict = load_class_label(args.class_label_path)
922
-
923
- if args.datasetinfos is None:
924
- args.datasetinfos = ["train", "unbalanced_train", "balanced_train"]
925
- if args.dataset_type == "webdataset":
926
- args.train_data = get_tar_path_from_dataset_name(
927
- args.datasetnames,
928
- args.datasetinfos,
929
- islocal=not args.remotedata,
930
- proportion=args.dataset_proportion,
931
- dataset_path=args.datasetpath,
932
- full_dataset=args.full_train_dataset,
933
- )
934
-
935
- if args.full_train_dataset is None:
936
- args.full_train_dataset = []
937
- if args.exclude_eval_dataset is None:
938
- args.exclude_eval_dataset = []
939
- excluded_eval_datasets = args.full_train_dataset + args.exclude_eval_dataset
940
-
941
- val_dataset_names = (
942
- [n for n in args.datasetnames if n not in excluded_eval_datasets]
943
- if excluded_eval_datasets
944
- else args.datasetnames
945
- )
946
- args.val_dataset_names = val_dataset_names
947
- args.val_data = get_tar_path_from_dataset_name(
948
- val_dataset_names,
949
- ["valid", "test", "eval"],
950
- islocal=not args.remotedata,
951
- proportion=1,
952
- dataset_path=args.datasetpath,
953
- full_dataset=None,
954
- )
955
-
956
- if args.train_data:
957
- data["train"] = get_dataset_fn(args.train_data, args.dataset_type)(
958
- args, model_cfg, is_train=True
959
- )
960
-
961
- if args.val_data:
962
- data["val"] = get_dataset_fn(args.val_data, args.dataset_type)(
963
- args, model_cfg, is_train=False
964
- )
965
-
966
- return data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/distributed.py DELETED
@@ -1,150 +0,0 @@
1
- import os
2
-
3
- import torch
4
- import socket
5
-
6
- try:
7
- import horovod.torch as hvd
8
- except ImportError:
9
- hvd = None
10
-
11
-
12
- def is_global_master(args):
13
- return args.rank == 0
14
-
15
-
16
- def is_local_master(args):
17
- return args.local_rank == 0
18
-
19
-
20
- def is_master(args, local=False):
21
- return is_local_master(args) if local else is_global_master(args)
22
-
23
-
24
- def is_using_horovod():
25
- # NOTE w/ horovod run, OMPI vars should be set, but w/ SLURM PMI vars will be set
26
- # Differentiating between horovod and DDP use via SLURM may not be possible, so horovod arg still required...
27
- ompi_vars = ["OMPI_COMM_WORLD_RANK", "OMPI_COMM_WORLD_SIZE"]
28
- pmi_vars = ["PMI_RANK", "PMI_SIZE"]
29
- if all([var in os.environ for var in ompi_vars]) or all(
30
- [var in os.environ for var in pmi_vars]
31
- ):
32
- return True
33
- else:
34
- return False
35
-
36
-
37
- def is_using_distributed():
38
- if "WORLD_SIZE" in os.environ:
39
- return int(os.environ["WORLD_SIZE"]) > 1
40
- if "SLURM_NTASKS" in os.environ:
41
- return int(os.environ["SLURM_NTASKS"]) > 1
42
- return False
43
-
44
-
45
- def world_info_from_env():
46
- local_rank = 0
47
- for v in (
48
- "SLURM_LOCALID",
49
- "MPI_LOCALRANKID",
50
- "OMPI_COMM_WORLD_LOCAL_RANK",
51
- "LOCAL_RANK",
52
- ):
53
- if v in os.environ:
54
- local_rank = int(os.environ[v])
55
- break
56
- global_rank = 0
57
- for v in ("SLURM_PROCID", "PMI_RANK", "OMPI_COMM_WORLD_RANK", "RANK"):
58
- if v in os.environ:
59
- global_rank = int(os.environ[v])
60
- break
61
- world_size = 1
62
- for v in ("SLURM_NTASKS", "PMI_SIZE", "OMPI_COMM_WORLD_SIZE", "WORLD_SIZE"):
63
- if v in os.environ:
64
- world_size = int(os.environ[v])
65
- break
66
-
67
- return local_rank, global_rank, world_size
68
-
69
-
70
- def init_distributed_device(args):
71
- # Distributed training = training on more than one GPU.
72
- # Works in both single and multi-node scenarios.
73
- args.distributed = False
74
- args.world_size = 1
75
- args.rank = 0 # global rank
76
- args.local_rank = 0
77
- if args.horovod:
78
- assert hvd is not None, "Horovod is not installed"
79
- hvd.init()
80
- world_size = int(os.environ["OMPI_COMM_WORLD_SIZE"])
81
- world_rank = int(os.environ["OMPI_COMM_WORLD_RANK"])
82
- local_rank = int(os.environ["OMPI_COMM_WORLD_LOCAL_RANK"])
83
- args.local_rank = local_rank
84
- args.rank = world_rank
85
- args.world_size = world_size
86
- # args.local_rank = int(hvd.local_rank())
87
- # args.rank = hvd.rank()
88
- # args.world_size = hvd.size()
89
- args.distributed = True
90
- os.environ["LOCAL_RANK"] = str(args.local_rank)
91
- os.environ["RANK"] = str(args.rank)
92
- os.environ["WORLD_SIZE"] = str(args.world_size)
93
- print(
94
- f"Distributed training: local_rank={args.local_rank}, "
95
- f"rank={args.rank}, world_size={args.world_size}, "
96
- f"hostname={socket.gethostname()}, pid={os.getpid()}"
97
- )
98
- elif is_using_distributed():
99
- if "SLURM_PROCID" in os.environ:
100
- # DDP via SLURM
101
- args.local_rank, args.rank, args.world_size = world_info_from_env()
102
- # SLURM var -> torch.distributed vars in case needed
103
- os.environ["LOCAL_RANK"] = str(args.local_rank)
104
- os.environ["RANK"] = str(args.rank)
105
- os.environ["WORLD_SIZE"] = str(args.world_size)
106
- torch.distributed.init_process_group(
107
- backend=args.dist_backend,
108
- init_method=args.dist_url,
109
- world_size=args.world_size,
110
- rank=args.rank,
111
- )
112
- elif "OMPI_COMM_WORLD_SIZE" in os.environ: # using Summit cluster
113
- world_size = int(os.environ["OMPI_COMM_WORLD_SIZE"])
114
- world_rank = int(os.environ["OMPI_COMM_WORLD_RANK"])
115
- local_rank = int(os.environ["OMPI_COMM_WORLD_LOCAL_RANK"])
116
- args.local_rank = local_rank
117
- args.rank = world_rank
118
- args.world_size = world_size
119
- torch.distributed.init_process_group(
120
- backend=args.dist_backend,
121
- init_method=args.dist_url,
122
- world_size=args.world_size,
123
- rank=args.rank,
124
- )
125
- else:
126
- # DDP via torchrun, torch.distributed.launch
127
- args.local_rank, _, _ = world_info_from_env()
128
- torch.distributed.init_process_group(
129
- backend=args.dist_backend, init_method=args.dist_url
130
- )
131
- args.world_size = torch.distributed.get_world_size()
132
- args.rank = torch.distributed.get_rank()
133
- args.distributed = True
134
- print(
135
- f"Distributed training: local_rank={args.local_rank}, "
136
- f"rank={args.rank}, world_size={args.world_size}, "
137
- f"hostname={socket.gethostname()}, pid={os.getpid()}"
138
- )
139
-
140
- if torch.cuda.is_available():
141
- if args.distributed and not args.no_set_device_rank:
142
- device = "cuda:%d" % args.local_rank
143
- else:
144
- device = "cuda:0"
145
- torch.cuda.set_device(device)
146
- else:
147
- device = "cpu"
148
- args.device = device
149
- device = torch.device(device)
150
- return device
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/imagenet_zeroshot_data.py DELETED
@@ -1,1088 +0,0 @@
1
- # NOTE: This script is currently not supported for CLAP.
2
-
3
- imagenet_classnames = [
4
- "tench",
5
- "goldfish",
6
- "great white shark",
7
- "tiger shark",
8
- "hammerhead shark",
9
- "electric ray",
10
- "stingray",
11
- "rooster",
12
- "hen",
13
- "ostrich",
14
- "brambling",
15
- "goldfinch",
16
- "house finch",
17
- "junco",
18
- "indigo bunting",
19
- "American robin",
20
- "bulbul",
21
- "jay",
22
- "magpie",
23
- "chickadee",
24
- "American dipper",
25
- "kite (bird of prey)",
26
- "bald eagle",
27
- "vulture",
28
- "great grey owl",
29
- "fire salamander",
30
- "smooth newt",
31
- "newt",
32
- "spotted salamander",
33
- "axolotl",
34
- "American bullfrog",
35
- "tree frog",
36
- "tailed frog",
37
- "loggerhead sea turtle",
38
- "leatherback sea turtle",
39
- "mud turtle",
40
- "terrapin",
41
- "box turtle",
42
- "banded gecko",
43
- "green iguana",
44
- "Carolina anole",
45
- "desert grassland whiptail lizard",
46
- "agama",
47
- "frilled-necked lizard",
48
- "alligator lizard",
49
- "Gila monster",
50
- "European green lizard",
51
- "chameleon",
52
- "Komodo dragon",
53
- "Nile crocodile",
54
- "American alligator",
55
- "triceratops",
56
- "worm snake",
57
- "ring-necked snake",
58
- "eastern hog-nosed snake",
59
- "smooth green snake",
60
- "kingsnake",
61
- "garter snake",
62
- "water snake",
63
- "vine snake",
64
- "night snake",
65
- "boa constrictor",
66
- "African rock python",
67
- "Indian cobra",
68
- "green mamba",
69
- "sea snake",
70
- "Saharan horned viper",
71
- "eastern diamondback rattlesnake",
72
- "sidewinder rattlesnake",
73
- "trilobite",
74
- "harvestman",
75
- "scorpion",
76
- "yellow garden spider",
77
- "barn spider",
78
- "European garden spider",
79
- "southern black widow",
80
- "tarantula",
81
- "wolf spider",
82
- "tick",
83
- "centipede",
84
- "black grouse",
85
- "ptarmigan",
86
- "ruffed grouse",
87
- "prairie grouse",
88
- "peafowl",
89
- "quail",
90
- "partridge",
91
- "african grey parrot",
92
- "macaw",
93
- "sulphur-crested cockatoo",
94
- "lorikeet",
95
- "coucal",
96
- "bee eater",
97
- "hornbill",
98
- "hummingbird",
99
- "jacamar",
100
- "toucan",
101
- "duck",
102
- "red-breasted merganser",
103
- "goose",
104
- "black swan",
105
- "tusker",
106
- "echidna",
107
- "platypus",
108
- "wallaby",
109
- "koala",
110
- "wombat",
111
- "jellyfish",
112
- "sea anemone",
113
- "brain coral",
114
- "flatworm",
115
- "nematode",
116
- "conch",
117
- "snail",
118
- "slug",
119
- "sea slug",
120
- "chiton",
121
- "chambered nautilus",
122
- "Dungeness crab",
123
- "rock crab",
124
- "fiddler crab",
125
- "red king crab",
126
- "American lobster",
127
- "spiny lobster",
128
- "crayfish",
129
- "hermit crab",
130
- "isopod",
131
- "white stork",
132
- "black stork",
133
- "spoonbill",
134
- "flamingo",
135
- "little blue heron",
136
- "great egret",
137
- "bittern bird",
138
- "crane bird",
139
- "limpkin",
140
- "common gallinule",
141
- "American coot",
142
- "bustard",
143
- "ruddy turnstone",
144
- "dunlin",
145
- "common redshank",
146
- "dowitcher",
147
- "oystercatcher",
148
- "pelican",
149
- "king penguin",
150
- "albatross",
151
- "grey whale",
152
- "killer whale",
153
- "dugong",
154
- "sea lion",
155
- "Chihuahua",
156
- "Japanese Chin",
157
- "Maltese",
158
- "Pekingese",
159
- "Shih Tzu",
160
- "King Charles Spaniel",
161
- "Papillon",
162
- "toy terrier",
163
- "Rhodesian Ridgeback",
164
- "Afghan Hound",
165
- "Basset Hound",
166
- "Beagle",
167
- "Bloodhound",
168
- "Bluetick Coonhound",
169
- "Black and Tan Coonhound",
170
- "Treeing Walker Coonhound",
171
- "English foxhound",
172
- "Redbone Coonhound",
173
- "borzoi",
174
- "Irish Wolfhound",
175
- "Italian Greyhound",
176
- "Whippet",
177
- "Ibizan Hound",
178
- "Norwegian Elkhound",
179
- "Otterhound",
180
- "Saluki",
181
- "Scottish Deerhound",
182
- "Weimaraner",
183
- "Staffordshire Bull Terrier",
184
- "American Staffordshire Terrier",
185
- "Bedlington Terrier",
186
- "Border Terrier",
187
- "Kerry Blue Terrier",
188
- "Irish Terrier",
189
- "Norfolk Terrier",
190
- "Norwich Terrier",
191
- "Yorkshire Terrier",
192
- "Wire Fox Terrier",
193
- "Lakeland Terrier",
194
- "Sealyham Terrier",
195
- "Airedale Terrier",
196
- "Cairn Terrier",
197
- "Australian Terrier",
198
- "Dandie Dinmont Terrier",
199
- "Boston Terrier",
200
- "Miniature Schnauzer",
201
- "Giant Schnauzer",
202
- "Standard Schnauzer",
203
- "Scottish Terrier",
204
- "Tibetan Terrier",
205
- "Australian Silky Terrier",
206
- "Soft-coated Wheaten Terrier",
207
- "West Highland White Terrier",
208
- "Lhasa Apso",
209
- "Flat-Coated Retriever",
210
- "Curly-coated Retriever",
211
- "Golden Retriever",
212
- "Labrador Retriever",
213
- "Chesapeake Bay Retriever",
214
- "German Shorthaired Pointer",
215
- "Vizsla",
216
- "English Setter",
217
- "Irish Setter",
218
- "Gordon Setter",
219
- "Brittany dog",
220
- "Clumber Spaniel",
221
- "English Springer Spaniel",
222
- "Welsh Springer Spaniel",
223
- "Cocker Spaniel",
224
- "Sussex Spaniel",
225
- "Irish Water Spaniel",
226
- "Kuvasz",
227
- "Schipperke",
228
- "Groenendael dog",
229
- "Malinois",
230
- "Briard",
231
- "Australian Kelpie",
232
- "Komondor",
233
- "Old English Sheepdog",
234
- "Shetland Sheepdog",
235
- "collie",
236
- "Border Collie",
237
- "Bouvier des Flandres dog",
238
- "Rottweiler",
239
- "German Shepherd Dog",
240
- "Dobermann",
241
- "Miniature Pinscher",
242
- "Greater Swiss Mountain Dog",
243
- "Bernese Mountain Dog",
244
- "Appenzeller Sennenhund",
245
- "Entlebucher Sennenhund",
246
- "Boxer",
247
- "Bullmastiff",
248
- "Tibetan Mastiff",
249
- "French Bulldog",
250
- "Great Dane",
251
- "St. Bernard",
252
- "husky",
253
- "Alaskan Malamute",
254
- "Siberian Husky",
255
- "Dalmatian",
256
- "Affenpinscher",
257
- "Basenji",
258
- "pug",
259
- "Leonberger",
260
- "Newfoundland dog",
261
- "Great Pyrenees dog",
262
- "Samoyed",
263
- "Pomeranian",
264
- "Chow Chow",
265
- "Keeshond",
266
- "brussels griffon",
267
- "Pembroke Welsh Corgi",
268
- "Cardigan Welsh Corgi",
269
- "Toy Poodle",
270
- "Miniature Poodle",
271
- "Standard Poodle",
272
- "Mexican hairless dog (xoloitzcuintli)",
273
- "grey wolf",
274
- "Alaskan tundra wolf",
275
- "red wolf or maned wolf",
276
- "coyote",
277
- "dingo",
278
- "dhole",
279
- "African wild dog",
280
- "hyena",
281
- "red fox",
282
- "kit fox",
283
- "Arctic fox",
284
- "grey fox",
285
- "tabby cat",
286
- "tiger cat",
287
- "Persian cat",
288
- "Siamese cat",
289
- "Egyptian Mau",
290
- "cougar",
291
- "lynx",
292
- "leopard",
293
- "snow leopard",
294
- "jaguar",
295
- "lion",
296
- "tiger",
297
- "cheetah",
298
- "brown bear",
299
- "American black bear",
300
- "polar bear",
301
- "sloth bear",
302
- "mongoose",
303
- "meerkat",
304
- "tiger beetle",
305
- "ladybug",
306
- "ground beetle",
307
- "longhorn beetle",
308
- "leaf beetle",
309
- "dung beetle",
310
- "rhinoceros beetle",
311
- "weevil",
312
- "fly",
313
- "bee",
314
- "ant",
315
- "grasshopper",
316
- "cricket insect",
317
- "stick insect",
318
- "cockroach",
319
- "praying mantis",
320
- "cicada",
321
- "leafhopper",
322
- "lacewing",
323
- "dragonfly",
324
- "damselfly",
325
- "red admiral butterfly",
326
- "ringlet butterfly",
327
- "monarch butterfly",
328
- "small white butterfly",
329
- "sulphur butterfly",
330
- "gossamer-winged butterfly",
331
- "starfish",
332
- "sea urchin",
333
- "sea cucumber",
334
- "cottontail rabbit",
335
- "hare",
336
- "Angora rabbit",
337
- "hamster",
338
- "porcupine",
339
- "fox squirrel",
340
- "marmot",
341
- "beaver",
342
- "guinea pig",
343
- "common sorrel horse",
344
- "zebra",
345
- "pig",
346
- "wild boar",
347
- "warthog",
348
- "hippopotamus",
349
- "ox",
350
- "water buffalo",
351
- "bison",
352
- "ram (adult male sheep)",
353
- "bighorn sheep",
354
- "Alpine ibex",
355
- "hartebeest",
356
- "impala (antelope)",
357
- "gazelle",
358
- "arabian camel",
359
- "llama",
360
- "weasel",
361
- "mink",
362
- "European polecat",
363
- "black-footed ferret",
364
- "otter",
365
- "skunk",
366
- "badger",
367
- "armadillo",
368
- "three-toed sloth",
369
- "orangutan",
370
- "gorilla",
371
- "chimpanzee",
372
- "gibbon",
373
- "siamang",
374
- "guenon",
375
- "patas monkey",
376
- "baboon",
377
- "macaque",
378
- "langur",
379
- "black-and-white colobus",
380
- "proboscis monkey",
381
- "marmoset",
382
- "white-headed capuchin",
383
- "howler monkey",
384
- "titi monkey",
385
- "Geoffroy's spider monkey",
386
- "common squirrel monkey",
387
- "ring-tailed lemur",
388
- "indri",
389
- "Asian elephant",
390
- "African bush elephant",
391
- "red panda",
392
- "giant panda",
393
- "snoek fish",
394
- "eel",
395
- "silver salmon",
396
- "rock beauty fish",
397
- "clownfish",
398
- "sturgeon",
399
- "gar fish",
400
- "lionfish",
401
- "pufferfish",
402
- "abacus",
403
- "abaya",
404
- "academic gown",
405
- "accordion",
406
- "acoustic guitar",
407
- "aircraft carrier",
408
- "airliner",
409
- "airship",
410
- "altar",
411
- "ambulance",
412
- "amphibious vehicle",
413
- "analog clock",
414
- "apiary",
415
- "apron",
416
- "trash can",
417
- "assault rifle",
418
- "backpack",
419
- "bakery",
420
- "balance beam",
421
- "balloon",
422
- "ballpoint pen",
423
- "Band-Aid",
424
- "banjo",
425
- "baluster / handrail",
426
- "barbell",
427
- "barber chair",
428
- "barbershop",
429
- "barn",
430
- "barometer",
431
- "barrel",
432
- "wheelbarrow",
433
- "baseball",
434
- "basketball",
435
- "bassinet",
436
- "bassoon",
437
- "swimming cap",
438
- "bath towel",
439
- "bathtub",
440
- "station wagon",
441
- "lighthouse",
442
- "beaker",
443
- "military hat (bearskin or shako)",
444
- "beer bottle",
445
- "beer glass",
446
- "bell tower",
447
- "baby bib",
448
- "tandem bicycle",
449
- "bikini",
450
- "ring binder",
451
- "binoculars",
452
- "birdhouse",
453
- "boathouse",
454
- "bobsleigh",
455
- "bolo tie",
456
- "poke bonnet",
457
- "bookcase",
458
- "bookstore",
459
- "bottle cap",
460
- "hunting bow",
461
- "bow tie",
462
- "brass memorial plaque",
463
- "bra",
464
- "breakwater",
465
- "breastplate",
466
- "broom",
467
- "bucket",
468
- "buckle",
469
- "bulletproof vest",
470
- "high-speed train",
471
- "butcher shop",
472
- "taxicab",
473
- "cauldron",
474
- "candle",
475
- "cannon",
476
- "canoe",
477
- "can opener",
478
- "cardigan",
479
- "car mirror",
480
- "carousel",
481
- "tool kit",
482
- "cardboard box / carton",
483
- "car wheel",
484
- "automated teller machine",
485
- "cassette",
486
- "cassette player",
487
- "castle",
488
- "catamaran",
489
- "CD player",
490
- "cello",
491
- "mobile phone",
492
- "chain",
493
- "chain-link fence",
494
- "chain mail",
495
- "chainsaw",
496
- "storage chest",
497
- "chiffonier",
498
- "bell or wind chime",
499
- "china cabinet",
500
- "Christmas stocking",
501
- "church",
502
- "movie theater",
503
- "cleaver",
504
- "cliff dwelling",
505
- "cloak",
506
- "clogs",
507
- "cocktail shaker",
508
- "coffee mug",
509
- "coffeemaker",
510
- "spiral or coil",
511
- "combination lock",
512
- "computer keyboard",
513
- "candy store",
514
- "container ship",
515
- "convertible",
516
- "corkscrew",
517
- "cornet",
518
- "cowboy boot",
519
- "cowboy hat",
520
- "cradle",
521
- "construction crane",
522
- "crash helmet",
523
- "crate",
524
- "infant bed",
525
- "Crock Pot",
526
- "croquet ball",
527
- "crutch",
528
- "cuirass",
529
- "dam",
530
- "desk",
531
- "desktop computer",
532
- "rotary dial telephone",
533
- "diaper",
534
- "digital clock",
535
- "digital watch",
536
- "dining table",
537
- "dishcloth",
538
- "dishwasher",
539
- "disc brake",
540
- "dock",
541
- "dog sled",
542
- "dome",
543
- "doormat",
544
- "drilling rig",
545
- "drum",
546
- "drumstick",
547
- "dumbbell",
548
- "Dutch oven",
549
- "electric fan",
550
- "electric guitar",
551
- "electric locomotive",
552
- "entertainment center",
553
- "envelope",
554
- "espresso machine",
555
- "face powder",
556
- "feather boa",
557
- "filing cabinet",
558
- "fireboat",
559
- "fire truck",
560
- "fire screen",
561
- "flagpole",
562
- "flute",
563
- "folding chair",
564
- "football helmet",
565
- "forklift",
566
- "fountain",
567
- "fountain pen",
568
- "four-poster bed",
569
- "freight car",
570
- "French horn",
571
- "frying pan",
572
- "fur coat",
573
- "garbage truck",
574
- "gas mask or respirator",
575
- "gas pump",
576
- "goblet",
577
- "go-kart",
578
- "golf ball",
579
- "golf cart",
580
- "gondola",
581
- "gong",
582
- "gown",
583
- "grand piano",
584
- "greenhouse",
585
- "radiator grille",
586
- "grocery store",
587
- "guillotine",
588
- "hair clip",
589
- "hair spray",
590
- "half-track",
591
- "hammer",
592
- "hamper",
593
- "hair dryer",
594
- "hand-held computer",
595
- "handkerchief",
596
- "hard disk drive",
597
- "harmonica",
598
- "harp",
599
- "combine harvester",
600
- "hatchet",
601
- "holster",
602
- "home theater",
603
- "honeycomb",
604
- "hook",
605
- "hoop skirt",
606
- "gymnastic horizontal bar",
607
- "horse-drawn vehicle",
608
- "hourglass",
609
- "iPod",
610
- "clothes iron",
611
- "carved pumpkin",
612
- "jeans",
613
- "jeep",
614
- "T-shirt",
615
- "jigsaw puzzle",
616
- "rickshaw",
617
- "joystick",
618
- "kimono",
619
- "knee pad",
620
- "knot",
621
- "lab coat",
622
- "ladle",
623
- "lampshade",
624
- "laptop computer",
625
- "lawn mower",
626
- "lens cap",
627
- "letter opener",
628
- "library",
629
- "lifeboat",
630
- "lighter",
631
- "limousine",
632
- "ocean liner",
633
- "lipstick",
634
- "slip-on shoe",
635
- "lotion",
636
- "music speaker",
637
- "loupe magnifying glass",
638
- "sawmill",
639
- "magnetic compass",
640
- "messenger bag",
641
- "mailbox",
642
- "tights",
643
- "one-piece bathing suit",
644
- "manhole cover",
645
- "maraca",
646
- "marimba",
647
- "mask",
648
- "matchstick",
649
- "maypole",
650
- "maze",
651
- "measuring cup",
652
- "medicine cabinet",
653
- "megalith",
654
- "microphone",
655
- "microwave oven",
656
- "military uniform",
657
- "milk can",
658
- "minibus",
659
- "miniskirt",
660
- "minivan",
661
- "missile",
662
- "mitten",
663
- "mixing bowl",
664
- "mobile home",
665
- "ford model t",
666
- "modem",
667
- "monastery",
668
- "monitor",
669
- "moped",
670
- "mortar and pestle",
671
- "graduation cap",
672
- "mosque",
673
- "mosquito net",
674
- "vespa",
675
- "mountain bike",
676
- "tent",
677
- "computer mouse",
678
- "mousetrap",
679
- "moving van",
680
- "muzzle",
681
- "metal nail",
682
- "neck brace",
683
- "necklace",
684
- "baby pacifier",
685
- "notebook computer",
686
- "obelisk",
687
- "oboe",
688
- "ocarina",
689
- "odometer",
690
- "oil filter",
691
- "pipe organ",
692
- "oscilloscope",
693
- "overskirt",
694
- "bullock cart",
695
- "oxygen mask",
696
- "product packet / packaging",
697
- "paddle",
698
- "paddle wheel",
699
- "padlock",
700
- "paintbrush",
701
- "pajamas",
702
- "palace",
703
- "pan flute",
704
- "paper towel",
705
- "parachute",
706
- "parallel bars",
707
- "park bench",
708
- "parking meter",
709
- "railroad car",
710
- "patio",
711
- "payphone",
712
- "pedestal",
713
- "pencil case",
714
- "pencil sharpener",
715
- "perfume",
716
- "Petri dish",
717
- "photocopier",
718
- "plectrum",
719
- "Pickelhaube",
720
- "picket fence",
721
- "pickup truck",
722
- "pier",
723
- "piggy bank",
724
- "pill bottle",
725
- "pillow",
726
- "ping-pong ball",
727
- "pinwheel",
728
- "pirate ship",
729
- "drink pitcher",
730
- "block plane",
731
- "planetarium",
732
- "plastic bag",
733
- "plate rack",
734
- "farm plow",
735
- "plunger",
736
- "Polaroid camera",
737
- "pole",
738
- "police van",
739
- "poncho",
740
- "pool table",
741
- "soda bottle",
742
- "plant pot",
743
- "potter's wheel",
744
- "power drill",
745
- "prayer rug",
746
- "printer",
747
- "prison",
748
- "missile",
749
- "projector",
750
- "hockey puck",
751
- "punching bag",
752
- "purse",
753
- "quill",
754
- "quilt",
755
- "race car",
756
- "racket",
757
- "radiator",
758
- "radio",
759
- "radio telescope",
760
- "rain barrel",
761
- "recreational vehicle",
762
- "fishing casting reel",
763
- "reflex camera",
764
- "refrigerator",
765
- "remote control",
766
- "restaurant",
767
- "revolver",
768
- "rifle",
769
- "rocking chair",
770
- "rotisserie",
771
- "eraser",
772
- "rugby ball",
773
- "ruler measuring stick",
774
- "sneaker",
775
- "safe",
776
- "safety pin",
777
- "salt shaker",
778
- "sandal",
779
- "sarong",
780
- "saxophone",
781
- "scabbard",
782
- "weighing scale",
783
- "school bus",
784
- "schooner",
785
- "scoreboard",
786
- "CRT monitor",
787
- "screw",
788
- "screwdriver",
789
- "seat belt",
790
- "sewing machine",
791
- "shield",
792
- "shoe store",
793
- "shoji screen / room divider",
794
- "shopping basket",
795
- "shopping cart",
796
- "shovel",
797
- "shower cap",
798
- "shower curtain",
799
- "ski",
800
- "balaclava ski mask",
801
- "sleeping bag",
802
- "slide rule",
803
- "sliding door",
804
- "slot machine",
805
- "snorkel",
806
- "snowmobile",
807
- "snowplow",
808
- "soap dispenser",
809
- "soccer ball",
810
- "sock",
811
- "solar thermal collector",
812
- "sombrero",
813
- "soup bowl",
814
- "keyboard space bar",
815
- "space heater",
816
- "space shuttle",
817
- "spatula",
818
- "motorboat",
819
- "spider web",
820
- "spindle",
821
- "sports car",
822
- "spotlight",
823
- "stage",
824
- "steam locomotive",
825
- "through arch bridge",
826
- "steel drum",
827
- "stethoscope",
828
- "scarf",
829
- "stone wall",
830
- "stopwatch",
831
- "stove",
832
- "strainer",
833
- "tram",
834
- "stretcher",
835
- "couch",
836
- "stupa",
837
- "submarine",
838
- "suit",
839
- "sundial",
840
- "sunglasses",
841
- "sunglasses",
842
- "sunscreen",
843
- "suspension bridge",
844
- "mop",
845
- "sweatshirt",
846
- "swim trunks / shorts",
847
- "swing",
848
- "electrical switch",
849
- "syringe",
850
- "table lamp",
851
- "tank",
852
- "tape player",
853
- "teapot",
854
- "teddy bear",
855
- "television",
856
- "tennis ball",
857
- "thatched roof",
858
- "front curtain",
859
- "thimble",
860
- "threshing machine",
861
- "throne",
862
- "tile roof",
863
- "toaster",
864
- "tobacco shop",
865
- "toilet seat",
866
- "torch",
867
- "totem pole",
868
- "tow truck",
869
- "toy store",
870
- "tractor",
871
- "semi-trailer truck",
872
- "tray",
873
- "trench coat",
874
- "tricycle",
875
- "trimaran",
876
- "tripod",
877
- "triumphal arch",
878
- "trolleybus",
879
- "trombone",
880
- "hot tub",
881
- "turnstile",
882
- "typewriter keyboard",
883
- "umbrella",
884
- "unicycle",
885
- "upright piano",
886
- "vacuum cleaner",
887
- "vase",
888
- "vaulted or arched ceiling",
889
- "velvet fabric",
890
- "vending machine",
891
- "vestment",
892
- "viaduct",
893
- "violin",
894
- "volleyball",
895
- "waffle iron",
896
- "wall clock",
897
- "wallet",
898
- "wardrobe",
899
- "military aircraft",
900
- "sink",
901
- "washing machine",
902
- "water bottle",
903
- "water jug",
904
- "water tower",
905
- "whiskey jug",
906
- "whistle",
907
- "hair wig",
908
- "window screen",
909
- "window shade",
910
- "Windsor tie",
911
- "wine bottle",
912
- "airplane wing",
913
- "wok",
914
- "wooden spoon",
915
- "wool",
916
- "split-rail fence",
917
- "shipwreck",
918
- "sailboat",
919
- "yurt",
920
- "website",
921
- "comic book",
922
- "crossword",
923
- "traffic or street sign",
924
- "traffic light",
925
- "dust jacket",
926
- "menu",
927
- "plate",
928
- "guacamole",
929
- "consomme",
930
- "hot pot",
931
- "trifle",
932
- "ice cream",
933
- "popsicle",
934
- "baguette",
935
- "bagel",
936
- "pretzel",
937
- "cheeseburger",
938
- "hot dog",
939
- "mashed potatoes",
940
- "cabbage",
941
- "broccoli",
942
- "cauliflower",
943
- "zucchini",
944
- "spaghetti squash",
945
- "acorn squash",
946
- "butternut squash",
947
- "cucumber",
948
- "artichoke",
949
- "bell pepper",
950
- "cardoon",
951
- "mushroom",
952
- "Granny Smith apple",
953
- "strawberry",
954
- "orange",
955
- "lemon",
956
- "fig",
957
- "pineapple",
958
- "banana",
959
- "jackfruit",
960
- "cherimoya (custard apple)",
961
- "pomegranate",
962
- "hay",
963
- "carbonara",
964
- "chocolate syrup",
965
- "dough",
966
- "meatloaf",
967
- "pizza",
968
- "pot pie",
969
- "burrito",
970
- "red wine",
971
- "espresso",
972
- "tea cup",
973
- "eggnog",
974
- "mountain",
975
- "bubble",
976
- "cliff",
977
- "coral reef",
978
- "geyser",
979
- "lakeshore",
980
- "promontory",
981
- "sandbar",
982
- "beach",
983
- "valley",
984
- "volcano",
985
- "baseball player",
986
- "bridegroom",
987
- "scuba diver",
988
- "rapeseed",
989
- "daisy",
990
- "yellow lady's slipper",
991
- "corn",
992
- "acorn",
993
- "rose hip",
994
- "horse chestnut seed",
995
- "coral fungus",
996
- "agaric",
997
- "gyromitra",
998
- "stinkhorn mushroom",
999
- "earth star fungus",
1000
- "hen of the woods mushroom",
1001
- "bolete",
1002
- "corn cob",
1003
- "toilet paper",
1004
- ]
1005
-
1006
-
1007
- openai_imagenet_template = [
1008
- lambda c: f"a bad photo of a {c}.",
1009
- lambda c: f"a photo of many {c}.",
1010
- lambda c: f"a sculpture of a {c}.",
1011
- lambda c: f"a photo of the hard to see {c}.",
1012
- lambda c: f"a low resolution photo of the {c}.",
1013
- lambda c: f"a rendering of a {c}.",
1014
- lambda c: f"graffiti of a {c}.",
1015
- lambda c: f"a bad photo of the {c}.",
1016
- lambda c: f"a cropped photo of the {c}.",
1017
- lambda c: f"a tattoo of a {c}.",
1018
- lambda c: f"the embroidered {c}.",
1019
- lambda c: f"a photo of a hard to see {c}.",
1020
- lambda c: f"a bright photo of a {c}.",
1021
- lambda c: f"a photo of a clean {c}.",
1022
- lambda c: f"a photo of a dirty {c}.",
1023
- lambda c: f"a dark photo of the {c}.",
1024
- lambda c: f"a drawing of a {c}.",
1025
- lambda c: f"a photo of my {c}.",
1026
- lambda c: f"the plastic {c}.",
1027
- lambda c: f"a photo of the cool {c}.",
1028
- lambda c: f"a close-up photo of a {c}.",
1029
- lambda c: f"a black and white photo of the {c}.",
1030
- lambda c: f"a painting of the {c}.",
1031
- lambda c: f"a painting of a {c}.",
1032
- lambda c: f"a pixelated photo of the {c}.",
1033
- lambda c: f"a sculpture of the {c}.",
1034
- lambda c: f"a bright photo of the {c}.",
1035
- lambda c: f"a cropped photo of a {c}.",
1036
- lambda c: f"a plastic {c}.",
1037
- lambda c: f"a photo of the dirty {c}.",
1038
- lambda c: f"a jpeg corrupted photo of a {c}.",
1039
- lambda c: f"a blurry photo of the {c}.",
1040
- lambda c: f"a photo of the {c}.",
1041
- lambda c: f"a good photo of the {c}.",
1042
- lambda c: f"a rendering of the {c}.",
1043
- lambda c: f"a {c} in a video game.",
1044
- lambda c: f"a photo of one {c}.",
1045
- lambda c: f"a doodle of a {c}.",
1046
- lambda c: f"a close-up photo of the {c}.",
1047
- lambda c: f"a photo of a {c}.",
1048
- lambda c: f"the origami {c}.",
1049
- lambda c: f"the {c} in a video game.",
1050
- lambda c: f"a sketch of a {c}.",
1051
- lambda c: f"a doodle of the {c}.",
1052
- lambda c: f"a origami {c}.",
1053
- lambda c: f"a low resolution photo of a {c}.",
1054
- lambda c: f"the toy {c}.",
1055
- lambda c: f"a rendition of the {c}.",
1056
- lambda c: f"a photo of the clean {c}.",
1057
- lambda c: f"a photo of a large {c}.",
1058
- lambda c: f"a rendition of a {c}.",
1059
- lambda c: f"a photo of a nice {c}.",
1060
- lambda c: f"a photo of a weird {c}.",
1061
- lambda c: f"a blurry photo of a {c}.",
1062
- lambda c: f"a cartoon {c}.",
1063
- lambda c: f"art of a {c}.",
1064
- lambda c: f"a sketch of the {c}.",
1065
- lambda c: f"a embroidered {c}.",
1066
- lambda c: f"a pixelated photo of a {c}.",
1067
- lambda c: f"itap of the {c}.",
1068
- lambda c: f"a jpeg corrupted photo of the {c}.",
1069
- lambda c: f"a good photo of a {c}.",
1070
- lambda c: f"a plushie {c}.",
1071
- lambda c: f"a photo of the nice {c}.",
1072
- lambda c: f"a photo of the small {c}.",
1073
- lambda c: f"a photo of the weird {c}.",
1074
- lambda c: f"the cartoon {c}.",
1075
- lambda c: f"art of the {c}.",
1076
- lambda c: f"a drawing of the {c}.",
1077
- lambda c: f"a photo of the large {c}.",
1078
- lambda c: f"a black and white photo of a {c}.",
1079
- lambda c: f"the plushie {c}.",
1080
- lambda c: f"a dark photo of a {c}.",
1081
- lambda c: f"itap of a {c}.",
1082
- lambda c: f"graffiti of the {c}.",
1083
- lambda c: f"a toy {c}.",
1084
- lambda c: f"itap of my {c}.",
1085
- lambda c: f"a photo of a cool {c}.",
1086
- lambda c: f"a photo of a small {c}.",
1087
- lambda c: f"a tattoo of the {c}.",
1088
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/infer_demo.py DELETED
@@ -1,109 +0,0 @@
1
- import sys
2
-
3
- sys.path.append(
4
- "/mnt/fast/nobackup/users/hl01486/projects/contrastive_pretraining/CLAP/src"
5
- )
6
-
7
- import os
8
- import torch
9
- import librosa
10
- from open_clip import create_model
11
- from training.data import get_audio_features
12
- from training.data import int16_to_float32, float32_to_int16
13
- from transformers import RobertaTokenizer
14
-
15
- tokenize = RobertaTokenizer.from_pretrained("roberta-base")
16
-
17
-
18
- def tokenizer(text):
19
- result = tokenize(
20
- text,
21
- padding="max_length",
22
- truncation=True,
23
- max_length=77,
24
- return_tensors="pt",
25
- )
26
- return {k: v.squeeze(0) for k, v in result.items()}
27
-
28
-
29
- PRETRAINED_PATH = "/mnt/fast/nobackup/users/hl01486/projects/contrastive_pretraining/CLAP/assets/checkpoints/epoch_top_0_audioset_no_fusion.pt"
30
- WAVE_48k_PATH = "/mnt/fast/nobackup/users/hl01486/projects/contrastive_pretraining/CLAP/assets/audio/machine.wav"
31
-
32
-
33
- def infer_text():
34
- device = "cuda:0" if torch.cuda.is_available() else "cpu"
35
- precision = "fp32"
36
- amodel = "HTSAT-tiny" # or 'PANN-14'
37
- tmodel = "roberta" # the best text encoder in our training
38
- enable_fusion = False # False if you do not want to use the fusion model
39
- fusion_type = "aff_2d"
40
- pretrained = PRETRAINED_PATH
41
-
42
- model, model_cfg = create_model(
43
- amodel,
44
- tmodel,
45
- pretrained,
46
- precision=precision,
47
- device=device,
48
- enable_fusion=enable_fusion,
49
- fusion_type=fusion_type,
50
- )
51
- # load the text, can be a list (i.e. batch size)
52
- text_data = ["I love the contrastive learning", "I love the pretrain model"]
53
- # tokenize for roberta, if you want to tokenize for another text encoder, please refer to data.py#L43-90
54
- text_data = tokenizer(text_data)
55
-
56
- text_embed = model.get_text_embedding(text_data)
57
- print(text_embed.size())
58
-
59
-
60
- def infer_audio():
61
-
62
- device = "cuda:0" if torch.cuda.is_available() else "cpu"
63
- precision = "fp32"
64
- amodel = "HTSAT-tiny" # or 'PANN-14'
65
- tmodel = "roberta" # the best text encoder in our training
66
- enable_fusion = False # False if you do not want to use the fusion model
67
- fusion_type = "aff_2d"
68
- pretrained = PRETRAINED_PATH
69
-
70
- model, model_cfg = create_model(
71
- amodel,
72
- tmodel,
73
- pretrained,
74
- precision=precision,
75
- device=device,
76
- enable_fusion=enable_fusion,
77
- fusion_type=fusion_type,
78
- )
79
-
80
- # load the waveform of the shape (T,), should resample to 48000
81
- audio_waveform, sr = librosa.load(WAVE_48k_PATH, sr=48000)
82
- # quantize
83
- audio_waveform = int16_to_float32(float32_to_int16(audio_waveform))
84
- audio_waveform = torch.from_numpy(audio_waveform).float()
85
- audio_dict = {}
86
-
87
- # the 'fusion' truncate mode can be changed to 'rand_trunc' if run in unfusion mode
88
- import ipdb
89
-
90
- ipdb.set_trace()
91
- audio_dict = get_audio_features(
92
- audio_dict,
93
- audio_waveform,
94
- 480000,
95
- data_truncating="fusion",
96
- data_filling="repeatpad",
97
- audio_cfg=model_cfg["audio_cfg"],
98
- )
99
- # can send a list to the model, to process many audio tracks in one time (i.e. batch size)
100
- audio_embed = model.get_audio_embedding([audio_dict])
101
- print(audio_embed.size())
102
- import ipdb
103
-
104
- ipdb.set_trace()
105
-
106
-
107
- if __name__ == "__main__":
108
- infer_text()
109
- infer_audio()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/logger.py DELETED
@@ -1,30 +0,0 @@
1
- import logging
2
-
3
-
4
- def setup_logging(log_file, level, include_host=False):
5
- if include_host:
6
- import socket
7
-
8
- hostname = socket.gethostname()
9
- formatter = logging.Formatter(
10
- f"%(asctime)s | {hostname} | %(levelname)s | %(message)s",
11
- datefmt="%Y-%m-%d,%H:%M:%S",
12
- )
13
- else:
14
- formatter = logging.Formatter(
15
- "%(asctime)s | %(levelname)s | %(message)s", datefmt="%Y-%m-%d,%H:%M:%S"
16
- )
17
-
18
- logging.root.setLevel(level)
19
- loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict]
20
- for logger in loggers:
21
- logger.setLevel(level)
22
-
23
- stream_handler = logging.StreamHandler()
24
- stream_handler.setFormatter(formatter)
25
- logging.root.addHandler(stream_handler)
26
-
27
- if log_file:
28
- file_handler = logging.FileHandler(filename=log_file)
29
- file_handler.setFormatter(formatter)
30
- logging.root.addHandler(file_handler)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/lp_main.py DELETED
@@ -1,670 +0,0 @@
1
- from cmath import cos
2
- from inspect import getargs
3
- import logging
4
- import os
5
- import random
6
- from datetime import datetime
7
- import bisect
8
- import copy
9
- from sched import scheduler
10
- import numpy as np
11
- import torch
12
- import torch.backends.cudnn as cudnn
13
- from torch import optim
14
- from torch.cuda.amp import GradScaler
15
- import faulthandler
16
- import pathlib
17
- import argparse
18
- import time
19
-
20
- try:
21
- import wandb
22
- except ImportError:
23
- wandb = None
24
-
25
- try:
26
- import torch.utils.tensorboard as tensorboard
27
- except ImportError:
28
- tensorboard = None
29
-
30
- try:
31
- import horovod.torch as hvd
32
- except ImportError:
33
- hvd = None
34
-
35
- from open_clip import create_model_and_transforms, trace_model, create_model
36
- from training.data import get_data
37
- from training.params import parse_args
38
- from training.distributed import is_master, init_distributed_device, world_info_from_env
39
- from training.logger import setup_logging
40
- from training.scheduler import cosine_lr
41
- from training.lp_train import train_one_epoch, evaluate
42
- from open_clip.utils import get_tar_path_from_dataset_name, dataset_split, get_optimizer
43
- from open_clip.utils import load_p, load_class_label
44
- from open_clip.linear_probe import LinearProbe
45
-
46
-
47
- def maintain_ckpts(args, startidx, all_idx_len):
48
- for i in reversed(range(startidx, all_idx_len)):
49
- if os.path.exists(os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt")):
50
- os.rename(
51
- os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
52
- os.path.join(args.checkpoint_path, f"epoch_top_{i+1}.pt"),
53
- )
54
- if os.path.exists(
55
- os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt")
56
- ):
57
- os.remove(os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt"))
58
- return
59
-
60
-
61
- def update_top_k_performance(
62
- new_metrics_inputs, current_top_k_ckpt_metrics, args, ckpt, bignumbetter=True
63
- ):
64
- """
65
- Record the top-k performance of the current epoch.
66
- current_top_k_metrics is a dictionary of the form: {1: top_1_ckpt_measure, 2: top_2_ckpt_measure, ...}
67
- """
68
- if isinstance(new_metrics_inputs, (list, tuple)):
69
- new_metrics_inputs = np.mean(new_metrics_inputs)
70
- return update_top_k_performance(
71
- new_metrics_inputs,
72
- current_top_k_ckpt_metrics,
73
- args=args,
74
- ckpt=ckpt,
75
- bignumbetter=bignumbetter,
76
- )
77
- elif isinstance(new_metrics_inputs, dict):
78
- new_metrics_inputs = np.mean(list(new_metrics_inputs.values()))
79
- return update_top_k_performance(
80
- new_metrics_inputs,
81
- current_top_k_ckpt_metrics,
82
- args=args,
83
- ckpt=ckpt,
84
- bignumbetter=bignumbetter,
85
- )
86
- elif isinstance(new_metrics_inputs, (float, int)):
87
- update_flag = {k: False for k in current_top_k_ckpt_metrics.keys()}
88
- sorted_keys = sorted(current_top_k_ckpt_metrics.keys())
89
- sorted_values = sorted(
90
- current_top_k_ckpt_metrics.values(), reverse=bignumbetter
91
- )
92
- sorted_values_ = copy.deepcopy(sorted_values)
93
- sorted_values.append(new_metrics_inputs)
94
- sorted_values = sorted(sorted_values, reverse=bignumbetter)
95
- sorted_values = sorted_values[:-1]
96
-
97
- if sorted_values == sorted_values_:
98
- return current_top_k_ckpt_metrics, new_metrics_inputs
99
- else:
100
- for i in range(len(sorted_keys)):
101
- if current_top_k_ckpt_metrics[sorted_keys[i]] != sorted_values[i]:
102
- current_top_k_ckpt_metrics[sorted_keys[i]] = sorted_values[i]
103
- update_flag[sorted_keys[i]] = True
104
- for i in range(len(update_flag)):
105
- if update_flag[i]:
106
- maintain_ckpts(args, i, len(sorted_keys))
107
- torch.save(
108
- ckpt,
109
- os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
110
- )
111
- break
112
- return current_top_k_ckpt_metrics, new_metrics_inputs
113
-
114
-
115
- # def updateifNone(a, b):
116
- # a = b if None else a
117
- # return a
118
-
119
-
120
- def is_pretrained_params(n):
121
- return (
122
- n.startswith("clap_model.transformer")
123
- or n in ["clap_model.positional_embedding", "clap_model.text_projection"]
124
- or n.startswith("clap_model.token_embedding")
125
- or n.startswith("clap_model.ln_final")
126
- or n.startswith("clap_model.logit_scale_t")
127
- )
128
-
129
-
130
- def random_seed(seed=42, rank=0):
131
- torch.manual_seed(seed + rank)
132
- np.random.seed(seed + rank)
133
- random.seed(seed + rank)
134
-
135
-
136
- def config_lp_optimizer(model, data, args):
137
- # set wd-related params to 0 if use adam optimizer
138
- if args.optimizer == "adam":
139
- args.wd = 0
140
- args.wd_pretrained = 0
141
- args.wd_new = 0
142
-
143
- in_clap = lambda n, p: n.startswith("clap_model")
144
-
145
- named_parameters = list(model.named_parameters())
146
-
147
- optimizer = {}
148
- scheduler = {}
149
-
150
- # freeze text encoder
151
- text_freeze_parameters = [
152
- p
153
- for n, p in named_parameters
154
- if n.startswith("clap_model.transformer")
155
- or n in ["clap_model.positional_embedding", "clap_model.text_projection"]
156
- or n.startswith("clap_model.token_embedding")
157
- or n.startswith("clap_model.ln_final")
158
- ]
159
-
160
- if args.freeze_text:
161
- logging.info("Freeze Text!!!!")
162
- for k in text_freeze_parameters:
163
- k.requires_grad = False
164
-
165
- if not args.lp_freeze:
166
- exclude = (
167
- lambda n, p: p.ndim < 2
168
- or "bn" in n
169
- or "ln" in n
170
- or "bias" in n
171
- or "logit_scale" in n
172
- )
173
- include = lambda n, p: not exclude(n, p)
174
-
175
- # (yusong): we do not split the learning rate anymore
176
- # p for n, p in named_parameters if in_clap(n,p) and exclude(n, p) and p.requires_grad
177
- gain_or_bias_params = [
178
- p for n, p in named_parameters if exclude(n, p) and p.requires_grad
179
- ]
180
- # rest_params = [p for n, p in named_parameters if in_clap(n,p) and include(n, p) and p.requires_grad]
181
- rest_params = [
182
- p for n, p in named_parameters if include(n, p) and p.requires_grad
183
- ]
184
-
185
- if args.train_data is None:
186
- optimizer = None
187
- scheduler = None
188
- else:
189
- total_steps = data["train"].dataloader.num_batches * args.epochs
190
-
191
- if args.split_opt:
192
- for x in ["lr", "beta1", "beta2", "eps", "wd"]:
193
- for y in ["_new", "_pretrained"]:
194
- if getattr(args, x + y) is None:
195
- setattr(args, x + y, getattr(args, x))
196
-
197
- gain_or_bias_pretrained_params = [
198
- p
199
- for n, p in named_parameters
200
- if (exclude(n, p) and p.requires_grad) and is_pretrained_params(n)
201
- ]
202
- rest_pretrained_params = [
203
- p
204
- for n, p in named_parameters
205
- if (include(n, p) and p.requires_grad) and is_pretrained_params(n)
206
- ]
207
- gain_or_bias_new_params = [
208
- p
209
- for n, p in named_parameters
210
- if (exclude(n, p) and p.requires_grad)
211
- and (not is_pretrained_params(n))
212
- ]
213
- rest_new_params = [
214
- p
215
- for n, p in named_parameters
216
- if (include(n, p) and p.requires_grad)
217
- and (not is_pretrained_params(n))
218
- ]
219
-
220
- pretrained_params_optimizer = get_optimizer(
221
- [
222
- {"params": gain_or_bias_pretrained_params, "weight_decay": 0.0},
223
- {
224
- "params": rest_pretrained_params,
225
- "weight_decay": args.wd_pretrained,
226
- },
227
- ],
228
- lr=args.lr_pretrained,
229
- betas=(args.beta1_pretrained, args.beta2_pretrained),
230
- eps=args.eps_pretrained,
231
- momentum=args.momentum_pretrained,
232
- optimizer_name=args.optimizer,
233
- )
234
- pretrained_params_scheduler = cosine_lr(
235
- pretrained_params_optimizer,
236
- args.lr_pretrained,
237
- args.warmup,
238
- total_steps,
239
- )
240
-
241
- new_params_optimizer = get_optimizer(
242
- [
243
- {"params": gain_or_bias_new_params, "weight_decay": 0.0},
244
- {"params": rest_new_params, "weight_decay": args.wd_new},
245
- ],
246
- lr=args.lr_new,
247
- betas=(args.beta1_new, args.beta2_new),
248
- eps=args.eps_new,
249
- momentum=args.momentum_new,
250
- optimizer_name=args.optimizer,
251
- )
252
- new_params_scheduler = cosine_lr(
253
- new_params_optimizer, args.lr_new, args.warmup, total_steps
254
- )
255
-
256
- optimizer["text"] = pretrained_params_optimizer
257
- optimizer["audio"] = new_params_optimizer
258
- scheduler["text"] = pretrained_params_scheduler
259
- scheduler["audio"] = new_params_scheduler
260
-
261
- if args.horovod:
262
- pretrained_params_optimizer = hvd.DistributedOptimizer(
263
- pretrained_params_optimizer,
264
- named_parameters=model.named_parameters(),
265
- )
266
- new_params_optimizer = hvd.DistributedOptimizer(
267
- new_params_optimizer, named_parameters=model.named_parameters()
268
- )
269
- hvd.broadcast_parameters(model.state_dict(), root_rank=0)
270
- hvd.broadcast_optimizer_state(
271
- pretrained_params_optimizer, root_rank=0
272
- )
273
- hvd.broadcast_optimizer_state(new_params_optimizer, root_rank=0)
274
- else:
275
-
276
- optimizer["clap"] = get_optimizer(
277
- [
278
- {"params": gain_or_bias_params, "weight_decay": 0.0},
279
- {"params": rest_params, "weight_decay": args.wd},
280
- ],
281
- lr=args.lr,
282
- betas=(args.beta1, args.beta2),
283
- eps=args.eps,
284
- momentum=args.momentum,
285
- optimizer_name=args.optimizer,
286
- )
287
- scheduler["clap"] = cosine_lr(
288
- optimizer["clap"], args.lr, args.warmup, total_steps
289
- )
290
-
291
- if args.horovod:
292
- optimizer["clap"] = hvd.DistributedOptimizer(
293
- optimizer["clap"], named_parameters=model.named_parameters()
294
- )
295
- hvd.broadcast_parameters(model.state_dict(), root_rank=0)
296
- hvd.broadcast_optimizer_state(optimizer["clap"], root_rank=0)
297
-
298
- # linear probe optimizer
299
- else:
300
- lp_params = [
301
- p for n, p in named_parameters if (not in_clap(n, p)) and p.requires_grad
302
- ]
303
- lp_optim = get_optimizer(
304
- lp_params,
305
- lr=args.lp_lr,
306
- betas=(args.beta1, args.beta2),
307
- eps=args.eps,
308
- momentum=0.9,
309
- optimizer_name=args.optimizer,
310
- )
311
- optimizer["lp"] = lp_optim
312
-
313
- return optimizer, scheduler, text_freeze_parameters
314
-
315
-
316
- def main():
317
- args = parse_args()
318
-
319
- time.sleep(args.sleep)
320
-
321
- # sanitize model name for filesystem / uri use, easier if we don't use / in name as a rule?
322
- args.amodel = args.amodel.replace("/", "-")
323
- # download sizes.json file
324
-
325
- # (yusong): the below two lines are for debug
326
- # print("setting up faulthandler")
327
- # faulthandler.register(10)
328
-
329
- random.seed(args.seed)
330
- torch.manual_seed(args.seed)
331
- torch.cuda.manual_seed(args.seed)
332
- torch.cuda.manual_seed_all(args.seed)
333
- np.random.seed(args.seed)
334
- args.class_index_dict = load_class_label(args.class_label_path)
335
-
336
- # get the name of the experiments
337
- if args.name is None:
338
- args.name = "-".join(
339
- [
340
- datetime.now().strftime("%Y_%m_%d-%H_%M_%S"),
341
- f"linear_probe" f"model_{args.amodel}",
342
- f"lr_{args.lr}",
343
- f"b_{args.batch_size}",
344
- f"j_{args.workers}",
345
- f"p_{args.precision}",
346
- ]
347
- )
348
-
349
- # discover initial world args early so we can log properly
350
- args.distributed = False
351
- args.local_rank, args.rank, args.world_size = world_info_from_env()
352
-
353
- if args.remotedata and is_master(args):
354
- for dataset_name in args.datasetnames:
355
- for split in dataset_split[dataset_name]:
356
- if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
357
- os.makedirs(f"./json_files/{dataset_name}/{split}")
358
- os.system(
359
- f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
360
- )
361
-
362
- args.log_path = None
363
- if is_master(args, local=args.log_local):
364
- log_base_path = os.path.join(args.logs, args.name)
365
- os.makedirs(log_base_path, exist_ok=True)
366
- log_filename = f"out-{args.rank}" if args.log_local else "out.log"
367
- args.log_path = os.path.join(log_base_path, log_filename)
368
-
369
- # avoid log dir in same name:
370
- postfix = 0
371
- while os.path.exists(args.log_path):
372
- postfix += 1
373
- log_base_path_new = log_base_path + "-" + str(postfix)
374
- os.makedirs(log_base_path_new, exist_ok=True)
375
- log_filename = f"out-{args.rank}" if args.log_local else "out.log"
376
- args.log_path = os.path.join(log_base_path_new, log_filename)
377
- # print(
378
- # "Error. Experiment already exists. Use --name {} to specify a new experiment."
379
- # )
380
- # return -1
381
-
382
- # Set logger
383
- args.log_level = logging.DEBUG if args.debug else logging.INFO
384
- setup_logging(args.log_path, args.log_level)
385
-
386
- # fully initialize distributed device environment
387
- device = init_distributed_device(args)
388
-
389
- args.wandb = "wandb" in args.report_to or "all" in args.report_to
390
- args.tensorboard = "tensorboard" in args.report_to or "all" in args.report_to
391
- if is_master(args):
392
- args.tensorboard_path = (
393
- os.path.join(args.logs, args.name, "tensorboard")
394
- if args.tensorboard
395
- else ""
396
- )
397
- args.checkpoint_path = os.path.join(args.logs, args.name, "checkpoints")
398
- for dirname in [args.tensorboard_path, args.checkpoint_path]:
399
- if dirname:
400
- os.makedirs(dirname, exist_ok=True)
401
- else:
402
- args.tensorboard_path = ""
403
- args.checkpoint_path = ""
404
-
405
- if args.copy_codebase:
406
- copy_codebase(args)
407
-
408
- assert args.precision in ["amp", "fp16", "fp32"]
409
- if args.precision == "fp16":
410
- logging.warning(
411
- "It is recommended to use AMP mixed-precision instead of FP16. "
412
- "FP16 support needs further verification and tuning, especially for train."
413
- )
414
-
415
- if args.horovod:
416
- logging.info(
417
- f"Running in horovod mode with multiple processes / nodes. Device: {args.device}."
418
- f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
419
- )
420
- elif args.distributed:
421
- logging.info(
422
- f"Running in distributed mode with multiple processes. Device: {args.device}."
423
- f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
424
- )
425
- else:
426
- logging.info(f"Running with a single process. Device {args.device}.")
427
-
428
- logging.info(f"openai cache dir: {os.path.expanduser(args.openai_model_cache_dir)}")
429
-
430
- # Create CLAP model
431
- clap_model, clap_model_cfg = create_model(
432
- args.amodel,
433
- args.tmodel,
434
- args.pretrained,
435
- precision=args.precision,
436
- device=device,
437
- jit=args.torchscript,
438
- force_quick_gelu=args.force_quick_gelu,
439
- openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
440
- skip_params=False,
441
- pretrained_audio=args.pretrained_audio,
442
- pretrained_text=args.pretrained_text,
443
- enable_fusion=args.enable_fusion,
444
- fusion_type=args.fusion_type,
445
- )
446
-
447
- args.lp_out_ch = len(list(args.class_index_dict.keys()))
448
- # Linear Probe
449
- logging.info(f"linear probe using mlp: {args.lp_mlp}")
450
- logging.info(f"linear probe using freeze: {args.lp_freeze}")
451
- logging.info(f"linear probe act layer: {args.lp_act}")
452
- logging.info(f"linear probe out ch: {args.lp_out_ch}")
453
- logging.info(f"linear probe learning rate (if applicable): {args.lp_lr}")
454
- logging.info(f"linear probe loss func: {args.lp_loss}")
455
- logging.info(f"linear probe lp_metrics: {args.lp_metrics}")
456
-
457
- model = LinearProbe(
458
- clap_model,
459
- mlp=args.lp_mlp,
460
- freeze=args.lp_freeze,
461
- in_ch=512,
462
- out_ch=args.lp_out_ch,
463
- act=args.lp_act,
464
- ) # in_ch is fixed (i.e., 512)
465
- model = model.to(device)
466
-
467
- if args.horovod:
468
- with torch.no_grad():
469
- for param in model.parameters():
470
- param.set_(param.contiguous())
471
-
472
- if args.trace:
473
- model = trace_model(model, batch_size=args.batch_size, device=device)
474
-
475
- if is_master(args):
476
- logging.info("Linear Probe CLAP Model:")
477
- logging.info(f"{str(clap_model)}")
478
- logging.info("Params:")
479
- params_file = os.path.join(args.logs, args.name, "params.txt")
480
- with open(params_file, "w") as f:
481
- for name in sorted(vars(args)):
482
- val = getattr(args, name)
483
- logging.info(f" {name}: {val}")
484
- f.write(f"{name}: {val}\n")
485
-
486
- if args.distributed and not args.horovod:
487
- if args.use_bn_sync:
488
- model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
489
- ddp_args = {}
490
- if args.ddp_static_graph:
491
- # this doesn't exist in older PyTorch, arg only added if enabled
492
- ddp_args["static_graph"] = True
493
- model = torch.nn.parallel.DistributedDataParallel(
494
- model, device_ids=[device], find_unused_parameters=True, **ddp_args
495
- )
496
-
497
- data = get_data(args, clap_model_cfg)
498
- assert len(data), "At least one train or eval dataset must be specified."
499
- if args.trace:
500
- assert "train" not in data, "Cannot train with traced model"
501
-
502
- optimizer, scheduler, text_freeze_parameters = config_lp_optimizer(
503
- model, data, args
504
- )
505
-
506
- scaler = GradScaler() if args.precision == "amp" else None
507
-
508
- # optionally resume from a checkpoint
509
- start_epoch = 0
510
- if args.resume is not None:
511
- if os.path.isfile(args.resume):
512
- checkpoint = torch.load(args.resume, map_location=device)
513
- if "epoch" in checkpoint:
514
- # resuming a train checkpoint w/ epoch and optimizer state
515
- start_epoch = checkpoint["epoch"]
516
- sd = checkpoint["state_dict"]
517
- if not args.distributed and next(iter(sd.items()))[0].startswith(
518
- "module"
519
- ):
520
- sd = {k[len("module.") :]: v for k, v in sd.items()}
521
- model.load_state_dict(sd)
522
- if args.split_opt:
523
- if optimizer is not None:
524
- for k, o_ in optimizer.items():
525
- o_.load_state_dict(checkpoint[k + "_" + "optimizer"])
526
- if optimizer is not None:
527
- optimizer.load_state_dict(checkpoint["optimizer"])
528
- if scaler is not None and "scaler" in checkpoint:
529
- scaler.load_state_dict(checkpoint["scaler"])
530
- logging.info(
531
- f"=> resuming checkpoint '{args.resume}' (epoch {start_epoch})"
532
- )
533
- else:
534
- # loading a bare (model only) checkpoint for fine-tune or evaluation
535
- model.load_state_dict(checkpoint)
536
- logging.info(
537
- f"=> loaded checkpoint '{args.resume}' (epoch {start_epoch})"
538
- )
539
- if args.freeze_text:
540
- print("Freeze Text!!!!")
541
- for k in text_freeze_parameters:
542
- k.requires_grad = False
543
- else:
544
- logging.info("=> no checkpoint found at '{}'".format(args.resume))
545
-
546
- cudnn.benchmark = True
547
- cudnn.deterministic = False
548
-
549
- # determine if this worker should save logs and checkpoints. only do so if it is rank == 0
550
- args.save_logs = args.logs and args.logs.lower() != "none" and is_master(args)
551
- writer = None
552
- if args.save_logs and args.tensorboard:
553
- assert tensorboard is not None, "Please install tensorboard."
554
- writer = tensorboard.SummaryWriter(args.tensorboard_path)
555
-
556
- if args.wandb and is_master(args):
557
- assert wandb is not None, "Please install wandb."
558
- logging.debug("Starting wandb.")
559
- args.train_sz = data["train"].dataloader.num_samples
560
- if args.val_data is not None:
561
- args.val_sz = data["val"].dataloader.num_samples
562
- # you will have to configure this for your project!
563
- wandb.init(
564
- project="clap",
565
- notes=args.wandb_notes,
566
- name=args.wandb_notes,
567
- tags=[],
568
- config=vars(args),
569
- )
570
- if args.debug:
571
- wandb.watch(model, log="all")
572
- wandb.save(params_file)
573
- logging.debug("Finished loading wandb.")
574
-
575
- if "train" not in data:
576
- evaluate(model, data, start_epoch, args, writer)
577
- return
578
- elif start_epoch == 0 and "val" in data and not args.no_eval:
579
- evaluate(model, data, 0, args, writer)
580
- if args.save_top_performance:
581
- current_top_k_ckpt_metrics = {
582
- i: 0 for i in range(args.save_top_performance)
583
- } # initialize the top-k metric for ckpts to 0
584
-
585
- for epoch in range(start_epoch, args.epochs):
586
- # freeze the text param after (include) args.freeze_text_after, this is -1 by default
587
- if epoch == args.freeze_text_after:
588
- print("Text pretrained parameters are freezed since this epoch.")
589
- for k in text_freeze_parameters:
590
- k.requires_grad = False
591
- if is_master(args):
592
- logging.info(f"Start epoch {epoch}")
593
-
594
- train_one_epoch(model, data, epoch, optimizer, scaler, scheduler, args, writer)
595
- completed_epoch = epoch + 1
596
-
597
- if (
598
- any(v in data for v in ("val", "imagenet-val", "imagenet-v2"))
599
- and not args.no_eval
600
- ):
601
- metrics = evaluate(model, data, completed_epoch, args, writer)
602
- if args.save_top_performance:
603
- top_k_dataset = args.top_k_checkpoint_select_dataset
604
- top_k_metric = args.top_k_checkpoint_select_metric
605
- filtered_metrics = [
606
- v
607
- for k, v in metrics.items()
608
- if top_k_metric in k and top_k_dataset in k
609
- ] # check all R@10 metrics (all dataset) and use it to update the ckpt
610
- # Saving checkpoints.
611
- if args.save_logs:
612
- opt_dict = {
613
- k + "_" + "optimizer": v.state_dict() for k, v in optimizer.items()
614
- }
615
- checkpoint_dict = {
616
- "epoch": completed_epoch,
617
- "name": args.name,
618
- "state_dict": model.state_dict(),
619
- }
620
- checkpoint_dict.update(opt_dict)
621
- if scaler is not None:
622
- checkpoint_dict["scaler"] = scaler.state_dict()
623
-
624
- if completed_epoch == args.epochs or (
625
- args.save_frequency > 0 and (completed_epoch % args.save_frequency) == 0
626
- ):
627
- torch.save(
628
- checkpoint_dict,
629
- os.path.join(args.checkpoint_path, f"epoch_{completed_epoch}.pt"),
630
- )
631
- if args.save_most_recent:
632
- torch.save(
633
- checkpoint_dict,
634
- os.path.join(args.checkpoint_path, f"epoch_latest.pt"),
635
- )
636
- if args.save_top_performance and not args.no_eval:
637
- update_top_k_performance(
638
- filtered_metrics,
639
- current_top_k_ckpt_metrics,
640
- args,
641
- checkpoint_dict,
642
- bignumbetter=True,
643
- )
644
-
645
- if args.wandb and is_master(args):
646
- wandb.finish()
647
-
648
-
649
- def copy_codebase(args):
650
- from shutil import copytree, ignore_patterns
651
-
652
- new_code_path = os.path.join(args.logs, args.name, "code")
653
- if os.path.exists(new_code_path):
654
- print(
655
- f"Error. Experiment already exists at {new_code_path}. Use --name to specify a new experiment."
656
- )
657
- return -1
658
- print(f"Copying codebase to {new_code_path}")
659
- current_code_path = os.path.realpath(__file__)
660
- for _ in range(3):
661
- current_code_path = os.path.dirname(current_code_path)
662
- copytree(
663
- current_code_path, new_code_path, ignore=ignore_patterns("log", "logs", "wandb")
664
- )
665
- print("Done copying code.")
666
- return 1
667
-
668
-
669
- if __name__ == "__main__":
670
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/lp_train.py DELETED
@@ -1,301 +0,0 @@
1
- import json
2
- import logging
3
- import math
4
- import os
5
- import time
6
- from contextlib import suppress
7
-
8
- import numpy as np
9
- import torch
10
- import torch.nn.functional as F
11
-
12
- try:
13
- import wandb
14
- except ImportError:
15
- wandb = None
16
-
17
- from open_clip import LPLoss, LPMetrics, lp_gather_features
18
- from open_clip.utils import do_mixup, get_mix_lambda
19
- from .distributed import is_master
20
- from .zero_shot import zero_shot_eval
21
-
22
-
23
- class AverageMeter(object):
24
- """Computes and stores the average and current value"""
25
-
26
- def __init__(self):
27
- self.reset()
28
-
29
- def reset(self):
30
- self.val = 0
31
- self.avg = 0
32
- self.sum = 0
33
- self.count = 0
34
-
35
- def update(self, val, n=1):
36
- self.val = val
37
- self.sum += val * n
38
- self.count += n
39
- self.avg = self.sum / self.count
40
-
41
-
42
- def unwrap_model(model):
43
- if hasattr(model, "module"):
44
- return model.module
45
- else:
46
- return model
47
-
48
-
49
- def train_one_epoch(
50
- model,
51
- data,
52
- epoch,
53
- optimizer,
54
- scaler,
55
- scheduler,
56
- args,
57
- tb_writer=None,
58
- extra_suffix="",
59
- ):
60
- device = torch.device(args.device)
61
- autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
62
- model.train()
63
- loss = LPLoss(args.lp_loss)
64
-
65
- dataloader, sampler = data["train"].dataloader, data["train"].sampler
66
- if args.distributed and sampler is not None:
67
- sampler.set_epoch(epoch)
68
- num_batches_per_epoch = dataloader.num_batches
69
- sample_digits = math.ceil(math.log(dataloader.num_samples + 1, 10))
70
-
71
- # for toy dataset
72
- if args.dataset_type == "toy":
73
- dataloader.dataset.generate_queue()
74
-
75
- loss_m = AverageMeter()
76
- batch_time_m = AverageMeter()
77
- data_time_m = AverageMeter()
78
- end = time.time()
79
-
80
- for i, batch in enumerate(dataloader):
81
- step = num_batches_per_epoch * epoch + i
82
-
83
- if isinstance(scheduler, dict):
84
- for s in scheduler.values():
85
- s(step)
86
- else:
87
- scheduler(step)
88
-
89
- audio = batch # contains mel_spec, wavform, and longer list
90
- class_label = batch["class_label"]
91
- # audio = audio.to(device=device, non_blocking=True)
92
- class_label = class_label.to(device=device, non_blocking=True)
93
-
94
- if args.mixup:
95
- # https://github.com/RetroCirce/HTS-Audio-Transformer/blob/main/utils.py#L146
96
- mix_lambda = torch.from_numpy(
97
- get_mix_lambda(0.5, len(audio["waveform"]))
98
- ).to(device)
99
- class_label = do_mixup(class_label, mix_lambda)
100
- else:
101
- mix_lambda = None
102
-
103
- data_time_m.update(time.time() - end)
104
- if isinstance(optimizer, dict):
105
- for o_ in optimizer.values():
106
- o_.zero_grad()
107
- else:
108
- optimizer.zero_grad()
109
-
110
- with autocast():
111
- pred = model(audio, mix_lambda=mix_lambda, device=device)
112
- total_loss = loss(pred, class_label)
113
-
114
- if isinstance(optimizer, dict):
115
- if scaler is not None:
116
- scaler.scale(total_loss).backward()
117
- for o_ in optimizer.values():
118
- if args.horovod:
119
- o_.synchronize()
120
- scaler.unscale_(o_)
121
- with o_.skip_synchronize():
122
- scaler.step(o_)
123
- else:
124
- scaler.step(o_)
125
- scaler.update()
126
- else:
127
- total_loss.backward()
128
- for o_ in optimizer.values():
129
- o_.step()
130
- else:
131
- if scaler is not None:
132
- scaler.scale(total_loss).backward()
133
- if args.horovod:
134
- optimizer.synchronize()
135
- scaler.unscale_(optimizer)
136
- with optimizer.skip_synchronize():
137
- scaler.step(optimizer)
138
- else:
139
- scaler.step(optimizer)
140
- scaler.update()
141
- else:
142
- total_loss.backward()
143
- optimizer.step()
144
-
145
- # Note: we clamp to 4.6052 = ln(100), as in the original paper.
146
- with torch.no_grad():
147
- unwrap_model(model).clap_model.logit_scale_a.clamp_(0, math.log(100))
148
- unwrap_model(model).clap_model.logit_scale_t.clamp_(0, math.log(100))
149
-
150
- batch_time_m.update(time.time() - end)
151
- end = time.time()
152
- batch_count = i + 1
153
-
154
- if is_master(args) and (i % 100 == 0 or batch_count == num_batches_per_epoch):
155
- if isinstance(audio, dict):
156
- batch_size = len(audio["waveform"])
157
- else:
158
- batch_size = len(audio)
159
- num_samples = batch_count * batch_size * args.world_size
160
- samples_per_epoch = dataloader.num_samples
161
- percent_complete = 100.0 * batch_count / num_batches_per_epoch
162
-
163
- # NOTE loss is coarsely sampled, just master node and per log update
164
- loss_m.update(total_loss.item(), batch_size)
165
- if isinstance(optimizer, dict):
166
- logging.info(
167
- f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
168
- f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
169
- f"Data (t): {data_time_m.avg:.3f} "
170
- f"Batch (t): {batch_time_m.avg:.3f} "
171
- f"LR: {[o_.param_groups[0]['lr'] for o_ in optimizer.values()]}"
172
- )
173
- log_data = {
174
- "loss": loss_m.val,
175
- "data_time": data_time_m.val,
176
- "batch_time": batch_time_m.val,
177
- "lr": [o_.param_groups[0]["lr"] for o_ in optimizer.values()],
178
- }
179
- else:
180
- logging.info(
181
- f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
182
- f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
183
- f"Data (t): {data_time_m.avg:.3f} "
184
- f"Batch (t): {batch_time_m.avg:.3f} "
185
- f"LR: {optimizer.param_groups[0]['lr']:5f} "
186
- )
187
-
188
- # Save train loss / etc. Using non avg meter values as loggers have their own smoothing
189
- log_data = {
190
- "loss": loss_m.val,
191
- "data_time": data_time_m.val,
192
- "batch_time": batch_time_m.val,
193
- "lr": optimizer.param_groups[0]["lr"],
194
- }
195
- for name, val in log_data.items():
196
- name = f"train{extra_suffix}/{name}"
197
- if tb_writer is not None:
198
- tb_writer.add_scalar(name, val, step)
199
- if args.wandb:
200
- assert wandb is not None, "Please install wandb."
201
- wandb.log({name: val, "step": step})
202
-
203
- # resetting batch / data time meters per log window
204
- batch_time_m.reset()
205
- data_time_m.reset()
206
- # end for
207
-
208
-
209
- def evaluate(model, data, epoch, args, tb_writer=None, extra_suffix=""):
210
- metrics = {}
211
- if not args.parallel_eval:
212
- if not is_master(args):
213
- return metrics
214
- device = torch.device(args.device)
215
- model.eval()
216
-
217
- # CHANGE
218
- # zero_shot_metrics = zero_shot_eval(model, data, epoch, args)
219
- # metrics.update(zero_shot_metrics)
220
- if is_master(args):
221
- print("Evaluating...")
222
- metric_names = args.lp_metrics.split(",")
223
- eval_tool = LPMetrics(metric_names=metric_names)
224
-
225
- autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
226
- if "val" in data and (
227
- args.val_frequency
228
- and ((epoch % args.val_frequency) == 0 or epoch == args.epochs)
229
- ):
230
- if args.parallel_eval:
231
- dataloader, sampler = data["val"].dataloader, data["val"].sampler
232
- if args.distributed and sampler is not None:
233
- sampler.set_epoch(epoch)
234
- samples_per_val = dataloader.num_samples
235
- else:
236
- dataloader = data["val"].dataloader
237
- num_samples = 0
238
- samples_per_val = dataloader.num_samples
239
-
240
- eval_info = {"pred": [], "target": []}
241
- with torch.no_grad():
242
- for i, batch in enumerate(dataloader):
243
- audio = batch # contains mel_spec, wavform, and longer list
244
- class_label = batch["class_label"]
245
-
246
- # audio = audio.to(device=device, non_blocking=True)
247
- class_label = class_label.to(device=device, non_blocking=True)
248
-
249
- with autocast():
250
- pred = model(audio, device=device)
251
- if args.parallel_eval:
252
- pred, class_label = lp_gather_features(
253
- pred, class_label, args.world_size, args.horovod
254
- )
255
- eval_info["pred"].append(pred)
256
- eval_info["target"].append(class_label)
257
-
258
- num_samples += class_label.shape[0]
259
-
260
- if (i % 100) == 0: # and i != 0:
261
- logging.info(
262
- f"Eval Epoch: {epoch} [{num_samples} / {samples_per_val}]"
263
- )
264
-
265
- if is_master(args):
266
- eval_info["pred"] = torch.cat(eval_info["pred"], 0).cpu()
267
- eval_info["target"] = torch.cat(eval_info["target"], 0).cpu()
268
- metric_dict = eval_tool.evaluate_mertics(
269
- eval_info["pred"], eval_info["target"]
270
- )
271
- metrics.update(metric_dict)
272
- if "epoch" not in metrics.keys():
273
- metrics.update({"epoch": epoch})
274
-
275
- if is_master(args):
276
- if not metrics:
277
- return metrics
278
-
279
- logging.info(
280
- f"Eval Epoch: {epoch} "
281
- + "\n".join(
282
- ["\t".join([f"{m}: {round(metrics[m], 4):.4f}"]) for m in metrics]
283
- )
284
- )
285
- if args.save_logs:
286
- for name, val in metrics.items():
287
- if tb_writer is not None:
288
- tb_writer.add_scalar(f"val{extra_suffix}/{name}", val, epoch)
289
-
290
- with open(os.path.join(args.checkpoint_path, "results.jsonl"), "a+") as f:
291
- f.write(json.dumps(metrics))
292
- f.write("\n")
293
-
294
- if args.wandb:
295
- assert wandb is not None, "Please install wandb."
296
- for name, val in metrics.items():
297
- wandb.log({f"val{extra_suffix}/{name}": val, "epoch": epoch})
298
-
299
- return metrics
300
- else:
301
- return metrics
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/main.py DELETED
@@ -1,596 +0,0 @@
1
- from inspect import getargs
2
- import logging
3
- import os
4
- import random
5
- from datetime import datetime
6
- import bisect
7
- import copy
8
- import numpy as np
9
- import torch
10
- import torch.backends.cudnn as cudnn
11
- from torch import optim
12
- from torch.cuda.amp import GradScaler
13
- import faulthandler
14
- import pathlib
15
-
16
- try:
17
- import wandb
18
- except ImportError:
19
- wandb = None
20
-
21
- try:
22
- import torch.utils.tensorboard as tensorboard
23
- except ImportError:
24
- tensorboard = None
25
-
26
- try:
27
- import horovod.torch as hvd
28
- except ImportError:
29
- hvd = None
30
-
31
- from open_clip import create_model_and_transforms, trace_model, create_model
32
- from training.data import get_data
33
- from training.distributed import is_master, init_distributed_device, world_info_from_env
34
- from training.logger import setup_logging
35
- from training.params import parse_args
36
- from training.scheduler import cosine_lr
37
- from training.train import train_one_epoch, evaluate
38
- from open_clip.utils import dataset_split, get_optimizer
39
-
40
-
41
- def maintain_ckpts(args, startidx, all_idx_len):
42
- for i in reversed(range(startidx, all_idx_len)):
43
- if os.path.exists(os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt")):
44
- os.rename(
45
- os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
46
- os.path.join(args.checkpoint_path, f"epoch_top_{i+1}.pt"),
47
- )
48
- if os.path.exists(
49
- os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt")
50
- ):
51
- os.remove(os.path.join(args.checkpoint_path, f"epoch_top_{all_idx_len}.pt"))
52
- return
53
-
54
-
55
- def update_top_k_performance(
56
- new_metrics_inputs, current_top_k_ckpt_metrics, args, ckpt, bignumbetter=True
57
- ):
58
- """
59
- Record the top-k performance of the current epoch.
60
- current_top_k_metrics is a dictionary of the form: {1: top_1_ckpt_measure, 2: top_2_ckpt_measure, ...}
61
- """
62
- if isinstance(new_metrics_inputs, (list, tuple)):
63
- new_metrics_inputs = np.mean(new_metrics_inputs)
64
- return update_top_k_performance(
65
- new_metrics_inputs,
66
- current_top_k_ckpt_metrics,
67
- args=args,
68
- ckpt=ckpt,
69
- bignumbetter=bignumbetter,
70
- )
71
- elif isinstance(new_metrics_inputs, dict):
72
- new_metrics_inputs = np.mean(list(new_metrics_inputs.values()))
73
- return update_top_k_performance(
74
- new_metrics_inputs,
75
- current_top_k_ckpt_metrics,
76
- args=args,
77
- ckpt=ckpt,
78
- bignumbetter=bignumbetter,
79
- )
80
- elif isinstance(new_metrics_inputs, (float, int)):
81
- update_flag = {k: False for k in current_top_k_ckpt_metrics.keys()}
82
- sorted_keys = sorted(current_top_k_ckpt_metrics.keys())
83
- sorted_values = sorted(
84
- current_top_k_ckpt_metrics.values(), reverse=bignumbetter
85
- )
86
- sorted_values_ = copy.deepcopy(sorted_values)
87
- sorted_values.append(new_metrics_inputs)
88
- sorted_values = sorted(sorted_values, reverse=bignumbetter)
89
- sorted_values = sorted_values[:-1]
90
-
91
- if sorted_values == sorted_values_:
92
- return current_top_k_ckpt_metrics, new_metrics_inputs
93
- else:
94
- for i in range(len(sorted_keys)):
95
- if current_top_k_ckpt_metrics[sorted_keys[i]] != sorted_values[i]:
96
- current_top_k_ckpt_metrics[sorted_keys[i]] = sorted_values[i]
97
- update_flag[sorted_keys[i]] = True
98
- for i in range(len(update_flag)):
99
- if update_flag[i]:
100
- maintain_ckpts(args, i, len(sorted_keys))
101
- torch.save(
102
- ckpt,
103
- os.path.join(args.checkpoint_path, f"epoch_top_{i}.pt"),
104
- )
105
- break
106
- return current_top_k_ckpt_metrics, new_metrics_inputs
107
-
108
-
109
- # def updateifNone(a, b):
110
- # a = b if None else a
111
- # return a
112
-
113
-
114
- def is_pretrained_params(n):
115
- return (
116
- n.startswith("transformer")
117
- or n in ["positional_embedding", "text_projection"]
118
- or n.startswith("token_embedding")
119
- or n.startswith("ln_final")
120
- or n.startswith("logit_scale_t")
121
- )
122
-
123
-
124
- def random_seed(seed=42, rank=0):
125
- torch.manual_seed(seed + rank)
126
- np.random.seed(seed + rank)
127
- random.seed(seed + rank)
128
-
129
-
130
- def main():
131
- args = parse_args()
132
- # sanitize model name for filesystem / uri use, easier if we don't use / in name as a rule?
133
- args.amodel = args.amodel.replace("/", "-")
134
- # download sizes.json file
135
-
136
- # (yusong): the below two lines are for debug
137
- # print("setting up faulthandler")
138
- # faulthandler.register(10)
139
-
140
- random.seed(args.seed)
141
- torch.manual_seed(args.seed)
142
- torch.cuda.manual_seed(args.seed)
143
- torch.cuda.manual_seed_all(args.seed)
144
- np.random.seed(args.seed)
145
- if args.tmodel == "bert" or args.tmodel == "roberta" or args.tmodel == "bart":
146
- assert (
147
- args.pretrained == "" or args.pretrained is None
148
- ), "bert/roberta/bart text encoder does not support pretrained models."
149
-
150
- # get the name of the experiments
151
- if args.name is None:
152
- args.name = "-".join(
153
- [
154
- datetime.now().strftime("%Y_%m_%d-%H_%M_%S"),
155
- f"model_{args.amodel}",
156
- f"lr_{args.lr}",
157
- f"b_{args.batch_size}",
158
- f"j_{args.workers}",
159
- f"p_{args.precision}",
160
- ]
161
- )
162
-
163
- # discover initial world args early so we can log properly
164
- args.distributed = False
165
- args.local_rank, args.rank, args.world_size = world_info_from_env()
166
-
167
- if args.remotedata and is_master(args):
168
- for dataset_name in args.datasetnames:
169
- for split in dataset_split[dataset_name]:
170
- if not os.path.exists(f"./json_files/{dataset_name}/{split}"):
171
- os.makedirs(f"./json_files/{dataset_name}/{split}")
172
- os.system(
173
- f"aws s3 cp s3://s-laion-audio/webdataset_tar/{dataset_name}/{split}/sizes.json ./json_files/{dataset_name}/{split}/sizes.json"
174
- )
175
-
176
- args.log_path = None
177
- if is_master(args, local=args.log_local):
178
- log_base_path = os.path.join(args.logs, args.name)
179
- os.makedirs(log_base_path, exist_ok=True)
180
- log_filename = f"out-{args.rank}" if args.log_local else "out.log"
181
- args.log_path = os.path.join(log_base_path, log_filename)
182
- if os.path.exists(args.log_path):
183
- print(
184
- "Error. Experiment already exists. Use --name {} to specify a new experiment."
185
- )
186
- return -1
187
-
188
- # Set logger
189
- args.log_level = logging.DEBUG if args.debug else logging.INFO
190
- setup_logging(args.log_path, args.log_level)
191
-
192
- # fully initialize distributed device environment
193
- device = init_distributed_device(args)
194
-
195
- args.wandb = "wandb" in args.report_to or "all" in args.report_to
196
- args.tensorboard = "tensorboard" in args.report_to or "all" in args.report_to
197
- if is_master(args):
198
- args.tensorboard_path = (
199
- os.path.join(args.logs, args.name, "tensorboard")
200
- if args.tensorboard
201
- else ""
202
- )
203
- args.checkpoint_path = os.path.join(args.logs, args.name, "checkpoints")
204
- for dirname in [args.tensorboard_path, args.checkpoint_path]:
205
- if dirname:
206
- os.makedirs(dirname, exist_ok=True)
207
- else:
208
- args.tensorboard_path = ""
209
- args.checkpoint_path = ""
210
-
211
- if args.copy_codebase:
212
- copy_codebase(args)
213
-
214
- assert args.precision in ["amp", "fp16", "fp32"]
215
- if args.precision == "fp16":
216
- logging.warning(
217
- "It is recommended to use AMP mixed-precision instead of FP16. "
218
- "FP16 support needs further verification and tuning, especially for train."
219
- )
220
-
221
- if args.horovod:
222
- logging.info(
223
- f"Running in horovod mode with multiple processes / nodes. Device: {args.device}."
224
- f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
225
- )
226
- elif args.distributed:
227
- logging.info(
228
- f"Running in distributed mode with multiple processes. Device: {args.device}."
229
- f"Process (global: {args.rank}, local {args.local_rank}), total {args.world_size}."
230
- )
231
- else:
232
- logging.info(f"Running with a single process. Device {args.device}.")
233
-
234
- logging.info(f"openai cache dir: {os.path.expanduser(args.openai_model_cache_dir)}")
235
-
236
- model, model_cfg = create_model(
237
- args.amodel,
238
- args.tmodel,
239
- args.pretrained,
240
- precision=args.precision,
241
- device=device,
242
- jit=args.torchscript,
243
- force_quick_gelu=args.force_quick_gelu,
244
- openai_model_cache_dir=os.path.expanduser(args.openai_model_cache_dir),
245
- skip_params=True,
246
- pretrained_audio=args.pretrained_audio,
247
- pretrained_text=args.pretrained_text,
248
- enable_fusion=args.enable_fusion,
249
- fusion_type=args.fusion_type,
250
- )
251
-
252
- if args.horovod:
253
- with torch.no_grad():
254
- for param in model.parameters():
255
- param.set_(param.contiguous())
256
-
257
- if args.trace:
258
- model = trace_model(model, batch_size=args.batch_size, device=device)
259
-
260
- if is_master(args):
261
- logging.info("Model:")
262
- logging.info(f"{str(model)}")
263
- logging.info("Params:")
264
- params_file = os.path.join(args.logs, args.name, "params.txt")
265
- with open(params_file, "w") as f:
266
- for name in sorted(vars(args)):
267
- val = getattr(args, name)
268
- logging.info(f" {name}: {val}")
269
- f.write(f"{name}: {val}\n")
270
-
271
- if args.distributed and not args.horovod:
272
- if args.use_bn_sync:
273
- model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
274
- ddp_args = {}
275
- if args.ddp_static_graph:
276
- # this doesn't exist in older PyTorch, arg only added if enabled
277
- ddp_args["static_graph"] = True
278
- model = torch.nn.parallel.DistributedDataParallel(
279
- model, device_ids=[device], find_unused_parameters=True, **ddp_args
280
- )
281
-
282
- data = get_data(args, model_cfg)
283
- assert len(data), "At least one train or eval dataset must be specified."
284
- if args.trace:
285
- assert "train" not in data, "Cannot train with traced model"
286
-
287
- exclude = (
288
- lambda n, p: p.ndim < 2
289
- or "bn" in n
290
- or "ln" in n
291
- or "bias" in n
292
- or "logit_scale" in n
293
- )
294
- include = lambda n, p: not exclude(n, p)
295
-
296
- named_parameters = list(model.named_parameters())
297
-
298
- # freeze text encoder
299
- text_freeze_parameters = [p for n, p in named_parameters if "text_branch" in n]
300
-
301
- if args.freeze_text:
302
- print("Freeze Text!!!!")
303
- for k in text_freeze_parameters:
304
- k.requires_grad = False
305
-
306
- gain_or_bias_params = [
307
- p for n, p in named_parameters if exclude(n, p) and p.requires_grad
308
- ]
309
- rest_params = [p for n, p in named_parameters if include(n, p) and p.requires_grad]
310
-
311
- # set wd-related params to 0 if use adam optimizer
312
- if args.optimizer == "adam":
313
- args.wd = 0
314
- args.wd_pretrained = 0
315
- args.wd_new = 0
316
-
317
- if args.train_data is None:
318
- optimizer = None
319
- scheduler = None
320
- else:
321
- total_steps = data["train"].dataloader.num_batches * args.epochs
322
-
323
- if args.split_opt:
324
- for x in ["lr", "beta1", "beta2", "eps", "wd"]:
325
- for y in ["_new", "_pretrained"]:
326
- if getattr(args, x + y) is None:
327
- setattr(args, x + y, getattr(args, x))
328
-
329
- gain_or_bias_pretrained_params = [
330
- p
331
- for n, p in named_parameters
332
- if (exclude(n, p) and p.requires_grad) and is_pretrained_params(n)
333
- ]
334
- rest_pretrained_params = [
335
- p
336
- for n, p in named_parameters
337
- if (include(n, p) and p.requires_grad) and is_pretrained_params(n)
338
- ]
339
- gain_or_bias_new_params = [
340
- p
341
- for n, p in named_parameters
342
- if (exclude(n, p) and p.requires_grad) and (not is_pretrained_params(n))
343
- ]
344
- rest_new_params = [
345
- p
346
- for n, p in named_parameters
347
- if (include(n, p) and p.requires_grad) and (not is_pretrained_params(n))
348
- ]
349
- pretrained_params_optimizer = get_optimizer(
350
- [
351
- {"params": gain_or_bias_pretrained_params, "weight_decay": 0.0},
352
- {
353
- "params": rest_pretrained_params,
354
- "weight_decay": args.wd_pretrained,
355
- },
356
- ],
357
- lr=args.lr_pretrained,
358
- betas=(args.beta1_pretrained, args.beta2_pretrained),
359
- eps=args.eps_pretrained,
360
- momentum=args.momentum_pretrained,
361
- optimizer_name=args.optimizer,
362
- )
363
- pretrained_params_scheduler = cosine_lr(
364
- pretrained_params_optimizer,
365
- args.lr_pretrained,
366
- args.warmup,
367
- total_steps,
368
- )
369
- new_params_optimizer = get_optimizer(
370
- [
371
- {"params": gain_or_bias_new_params, "weight_decay": 0.0},
372
- {"params": rest_new_params, "weight_decay": args.wd_new},
373
- ],
374
- lr=args.lr_new,
375
- betas=(args.beta1_new, args.beta2_new),
376
- eps=args.eps_new,
377
- momentum=args.momentum_new,
378
- optimizer_name=args.optimizer,
379
- )
380
-
381
- new_params_scheduler = cosine_lr(
382
- new_params_optimizer, args.lr_new, args.warmup, total_steps
383
- )
384
-
385
- optimizer = {
386
- "pretrained": pretrained_params_optimizer,
387
- "new": new_params_optimizer,
388
- }
389
- scheduler = {
390
- "pretrained": pretrained_params_scheduler,
391
- "new": new_params_scheduler,
392
- }
393
-
394
- if args.horovod:
395
- pretrained_params_optimizer = hvd.DistributedOptimizer(
396
- pretrained_params_optimizer,
397
- named_parameters=model.named_parameters(),
398
- )
399
- new_params_optimizer = hvd.DistributedOptimizer(
400
- new_params_optimizer, named_parameters=model.named_parameters()
401
- )
402
- hvd.broadcast_parameters(model.state_dict(), root_rank=0)
403
- hvd.broadcast_optimizer_state(pretrained_params_optimizer, root_rank=0)
404
- hvd.broadcast_optimizer_state(new_params_optimizer, root_rank=0)
405
- else:
406
- optimizer = get_optimizer(
407
- [
408
- {"params": gain_or_bias_params, "weight_decay": 0.0},
409
- {"params": rest_params, "weight_decay": args.wd},
410
- ],
411
- lr=args.lr,
412
- betas=(args.beta1, args.beta2),
413
- eps=args.eps,
414
- momentum=args.momentum,
415
- optimizer_name=args.optimizer,
416
- )
417
-
418
- scheduler = cosine_lr(optimizer, args.lr, args.warmup, total_steps)
419
-
420
- if args.horovod:
421
- optimizer = hvd.DistributedOptimizer(
422
- optimizer, named_parameters=model.named_parameters()
423
- )
424
- hvd.broadcast_parameters(model.state_dict(), root_rank=0)
425
- hvd.broadcast_optimizer_state(optimizer, root_rank=0)
426
-
427
- scaler = GradScaler() if args.precision == "amp" else None
428
-
429
- # optionally resume from a checkpoint
430
- start_epoch = 0
431
- if args.resume is not None:
432
- if os.path.isfile(args.resume):
433
- checkpoint = torch.load(args.resume, map_location=device)
434
- if "epoch" in checkpoint:
435
- # resuming a train checkpoint w/ epoch and optimizer state
436
- start_epoch = checkpoint["epoch"]
437
- sd = checkpoint["state_dict"]
438
- if not args.distributed and next(iter(sd.items()))[0].startswith(
439
- "module"
440
- ):
441
- sd = {k[len("module.") :]: v for k, v in sd.items()}
442
- model.load_state_dict(sd)
443
- if args.split_opt:
444
- if optimizer is not None:
445
- for k, o_ in optimizer.items():
446
- o_.load_state_dict(checkpoint[k + "_" + "optimizer"])
447
- if optimizer is not None:
448
- optimizer.load_state_dict(checkpoint["optimizer"])
449
- if scaler is not None and "scaler" in checkpoint:
450
- scaler.load_state_dict(checkpoint["scaler"])
451
- logging.info(
452
- f"=> resuming checkpoint '{args.resume}' (epoch {start_epoch})"
453
- )
454
- else:
455
- # loading a bare (model only) checkpoint for fine-tune or evaluation
456
- model.load_state_dict(checkpoint)
457
- logging.info(
458
- f"=> loaded checkpoint '{args.resume}' (epoch {start_epoch})"
459
- )
460
- if args.freeze_text:
461
- print("Freeze Text!!!!")
462
- for k in text_freeze_parameters:
463
- k.requires_grad = False
464
- else:
465
- logging.info("=> no checkpoint found at '{}'".format(args.resume))
466
-
467
- cudnn.benchmark = True
468
- cudnn.deterministic = False
469
-
470
- # determine if this worker should save logs and checkpoints. only do so if it is rank == 0
471
- args.save_logs = args.logs and args.logs.lower() != "none" and is_master(args)
472
- writer = None
473
- if args.save_logs and args.tensorboard:
474
- assert tensorboard is not None, "Please install tensorboard."
475
- writer = tensorboard.SummaryWriter(args.tensorboard_path)
476
-
477
- if args.wandb and is_master(args):
478
- assert wandb is not None, "Please install wandb."
479
- logging.debug("Starting wandb.")
480
- args.train_sz = data["train"].dataloader.num_samples
481
- if args.val_data is not None:
482
- args.val_sz = data["val"].dataloader.num_samples
483
- # you will have to configure this for your project!
484
- wandb.init(
485
- project="clap",
486
- notes=args.wandb_notes,
487
- name=args.wandb_notes,
488
- tags=[],
489
- config=vars(args),
490
- )
491
- if args.debug:
492
- wandb.watch(model, log="all")
493
- wandb.save(params_file)
494
- logging.debug("Finished loading wandb.")
495
-
496
- if "train" not in data:
497
- evaluate(model, data, start_epoch, args, writer)
498
- return
499
- elif start_epoch == 0 and "val" in data and not args.no_eval:
500
- evaluate(model, data, 0, args, writer)
501
- # print(f'rank {args.rank}, Start First Evaluation')# (yusong): for debug
502
- if args.save_top_performance:
503
- current_top_k_ckpt_metrics = {
504
- i: 0 for i in range(args.save_top_performance)
505
- } # initialize the top-k metric for ckpts to 0
506
-
507
- # print(f'rank {args.rank}, Start Training') # (yusong): for debug
508
- for epoch in range(start_epoch, args.epochs):
509
- # freeze the text param after (include) args.freeze_text_after, this is -1 by default
510
- if epoch == args.freeze_text_after:
511
- print("Text pretrained parameters are freezed since this epoch.")
512
- for k in text_freeze_parameters:
513
- k.requires_grad = False
514
- if is_master(args):
515
- logging.info(f"Start epoch {epoch}")
516
-
517
- train_one_epoch(model, data, epoch, optimizer, scaler, scheduler, args, writer)
518
- completed_epoch = epoch + 1
519
-
520
- if (
521
- any(v in data for v in ("val", "imagenet-val", "imagenet-v2"))
522
- and not args.no_eval
523
- ):
524
- metrics = evaluate(model, data, completed_epoch, args, writer)
525
- if args.save_top_performance:
526
- top_k_dataset = args.top_k_checkpoint_select_dataset
527
- top_k_metric = args.top_k_checkpoint_select_metric
528
- filtered_metrics = [
529
- v
530
- for k, v in metrics.items()
531
- if top_k_metric in k and top_k_dataset in k
532
- ] # check all R@10 metrics (all dataset) and use it to update the ckpt
533
- # Saving checkpoints.
534
- if args.save_logs:
535
- if args.split_opt:
536
- opt_dict = {
537
- k + "_" + "optimizer": v.state_dict() for k, v in optimizer.items()
538
- }
539
- else:
540
- opt_dict = {"optimizer": optimizer.state_dict()}
541
- checkpoint_dict = {
542
- "epoch": completed_epoch,
543
- "name": args.name,
544
- "state_dict": model.state_dict(),
545
- }
546
- checkpoint_dict.update(opt_dict)
547
- if scaler is not None:
548
- checkpoint_dict["scaler"] = scaler.state_dict()
549
-
550
- if completed_epoch == args.epochs or (
551
- args.save_frequency > 0 and (completed_epoch % args.save_frequency) == 0
552
- ):
553
- torch.save(
554
- checkpoint_dict,
555
- os.path.join(args.checkpoint_path, f"epoch_{completed_epoch}.pt"),
556
- )
557
- if args.save_most_recent:
558
- torch.save(
559
- checkpoint_dict,
560
- os.path.join(args.checkpoint_path, f"epoch_latest.pt"),
561
- )
562
- if args.save_top_performance and not args.no_eval:
563
- update_top_k_performance(
564
- filtered_metrics,
565
- current_top_k_ckpt_metrics,
566
- args,
567
- checkpoint_dict,
568
- bignumbetter=True,
569
- )
570
-
571
- if args.wandb and is_master(args):
572
- wandb.finish()
573
-
574
-
575
- def copy_codebase(args):
576
- from shutil import copytree, ignore_patterns
577
-
578
- new_code_path = os.path.join(args.logs, args.name, "code")
579
- if os.path.exists(new_code_path):
580
- print(
581
- f"Error. Experiment already exists at {new_code_path}. Use --name to specify a new experiment."
582
- )
583
- return -1
584
- print(f"Copying codebase to {new_code_path}")
585
- current_code_path = os.path.realpath(__file__)
586
- for _ in range(3):
587
- current_code_path = os.path.dirname(current_code_path)
588
- copytree(
589
- current_code_path, new_code_path, ignore=ignore_patterns("log", "logs", "wandb")
590
- )
591
- print("Done copying code.")
592
- return 1
593
-
594
-
595
- if __name__ == "__main__":
596
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/params.py DELETED
@@ -1,563 +0,0 @@
1
- import argparse
2
-
3
-
4
- def get_default_params(model_name):
5
- # Params from paper (https://arxiv.org/pdf/2103.00020.pdf)
6
- model_name = model_name.lower()
7
- if "vit" in model_name:
8
- return {"lr": 5.0e-4, "beta1": 0.9, "beta2": 0.98, "eps": 1.0e-6}
9
- else:
10
- return {"lr": 5.0e-4, "beta1": 0.9, "beta2": 0.999, "eps": 1.0e-8}
11
-
12
-
13
- def parse_args():
14
- parser = argparse.ArgumentParser()
15
- parser.add_argument(
16
- "--train-data",
17
- type=str,
18
- default=None,
19
- help="Path to h5 filewith training data",
20
- )
21
- parser.add_argument(
22
- "--val-data",
23
- type=str,
24
- default=None,
25
- help="Path to h5 file with validation data",
26
- )
27
- parser.add_argument(
28
- "--freeze-text",
29
- default=False,
30
- action="store_true",
31
- help="if you need to freeze the text encoder, make this True",
32
- )
33
- parser.add_argument(
34
- "--freeze-text-after",
35
- type=int,
36
- default=-1,
37
- help="if you need to freeze the text encoder after (include) epoch x, set this param to x. Set -1 to disable it",
38
- )
39
- parser.add_argument(
40
- "--train-ipc",
41
- type=str,
42
- default=None,
43
- help="Path to npy file of the number of instance per class in training data",
44
- )
45
- parser.add_argument(
46
- "--val-ipc",
47
- type=str,
48
- default=None,
49
- help="Path to npy file of the number of instance per class in validation data",
50
- )
51
- parser.add_argument(
52
- "--train-num-samples",
53
- type=int,
54
- default=None,
55
- help="Number of samples in dataset. Required for webdataset if not available in info file.",
56
- )
57
- parser.add_argument(
58
- "--val-num-samples",
59
- type=int,
60
- default=None,
61
- help="Number of samples in dataset. Useful for webdataset if not available in info file.",
62
- )
63
- parser.add_argument(
64
- "--dataset-type",
65
- choices=["webdataset", "csv", "auto", "toy"],
66
- default="auto",
67
- help="Which type of dataset to process.",
68
- )
69
- parser.add_argument(
70
- "--csv-separator",
71
- type=str,
72
- default="\t",
73
- help="For csv-like datasets, which separator to use.",
74
- )
75
- parser.add_argument(
76
- "--csv-img-key",
77
- type=str,
78
- default="filepath",
79
- help="For csv-like datasets, the name of the key for the image paths.",
80
- )
81
- parser.add_argument(
82
- "--csv-caption-key",
83
- type=str,
84
- default="title",
85
- help="For csv-like datasets, the name of the key for the captions.",
86
- )
87
- parser.add_argument(
88
- "--imagenet-val",
89
- type=str,
90
- default=None,
91
- help="Path to imagenet val set for conducting zero shot evaluation.",
92
- )
93
- parser.add_argument(
94
- "--imagenet-v2",
95
- type=str,
96
- default=None,
97
- help="Path to imagenet v2 for conducting zero shot evaluation.",
98
- )
99
- parser.add_argument(
100
- "--datasetnames",
101
- nargs="+",
102
- default=None,
103
- help="If loading webdataset, spedify the dataset names to load. Can be some of these: Clotho, audioset, audiocaps, BBCSoundEffects",
104
- )
105
- parser.add_argument(
106
- "--full-train-dataset",
107
- nargs="+",
108
- default=None,
109
- help="Which dataset will be trained with all the subsets. (train+test)",
110
- )
111
- parser.add_argument(
112
- "--exclude-eval-dataset",
113
- nargs="+",
114
- default=None,
115
- help="Which dataset will be excluded with evaluation",
116
- )
117
- parser.add_argument(
118
- "--datasetinfos",
119
- nargs="+",
120
- default=None,
121
- help="If loading webdataset, spedify the dataset types to load. Can be some of these: train, test, valid, unbalanced_train, balanced_train, eval",
122
- )
123
- parser.add_argument(
124
- "--dataset-proportion",
125
- type=float,
126
- default=1.0,
127
- help="How much proportion of dataset we want to train.",
128
- )
129
- parser.add_argument(
130
- "--remotedata",
131
- default=False,
132
- action="store_true",
133
- help="if the dataset is remote, set this flag",
134
- )
135
- parser.add_argument(
136
- "--class-label-path",
137
- type=str,
138
- default=None,
139
- help="The path of the class label pickle or csv.",
140
- )
141
- parser.add_argument(
142
- "--datasetpath",
143
- type=str,
144
- default="/mnt/audio_clip/webdataset_tar",
145
- help="The path to the dataset",
146
- )
147
- parser.add_argument(
148
- "--logs",
149
- type=str,
150
- default="./logs/",
151
- help="Where to store tensorboard logs. Use None to avoid storing logs.",
152
- )
153
- parser.add_argument(
154
- "--log-local",
155
- action="store_true",
156
- default=False,
157
- help="log files on local master, otherwise global master only.",
158
- )
159
- parser.add_argument(
160
- "--name",
161
- type=str,
162
- default=None,
163
- help="Optional identifier for the experiment when storing logs. Otherwise use current time.",
164
- )
165
- parser.add_argument(
166
- "--workers", type=int, default=1, help="Number of workers per GPU."
167
- )
168
- parser.add_argument(
169
- "--batch-size", type=int, default=64, help="Batch size per GPU."
170
- )
171
- parser.add_argument(
172
- "--epochs", type=int, default=32, help="Number of epochs to train for."
173
- )
174
- parser.add_argument("--lr", type=float, default=None, help="Learning rate.")
175
- parser.add_argument("--beta1", type=float, default=None, help="Adam beta 1.")
176
- parser.add_argument("--beta2", type=float, default=None, help="Adam beta 2.")
177
- parser.add_argument("--eps", type=float, default=None, help="Adam epsilon.")
178
- parser.add_argument("--momentum", type=float, default=None, help="SGD epsilon.")
179
- parser.add_argument("--wd", type=float, default=0.2, help="Weight decay.")
180
-
181
- parser.add_argument(
182
- "--split-opt",
183
- action="store_true",
184
- default=False,
185
- help="Use this flag to skip the learning rate decay.",
186
- )
187
- parser.add_argument(
188
- "--lr-pretrained", type=float, default=None, help="Learning rate for text."
189
- )
190
- parser.add_argument(
191
- "--beta1-pretrained", type=float, default=None, help="Adam beta 1 for text."
192
- )
193
- parser.add_argument(
194
- "--beta2-pretrained", type=float, default=None, help="Adam beta 2 for text."
195
- )
196
- parser.add_argument(
197
- "--eps-pretrained", type=float, default=None, help="Adam epsilon for text."
198
- )
199
- parser.add_argument(
200
- "--wd-pretrained", type=float, default=0.2, help="Weight decay for text."
201
- )
202
- parser.add_argument(
203
- "--momentum-pretrained", type=float, default=0.9, help="Momentum for text."
204
- )
205
- parser.add_argument(
206
- "--lr-new", type=float, default=None, help="Learning rate for audio."
207
- )
208
- parser.add_argument(
209
- "--beta1-new", type=float, default=None, help="Adam beta 1 for audio."
210
- )
211
- parser.add_argument(
212
- "--beta2-new", type=float, default=None, help="Adam beta 2 for audio."
213
- )
214
- parser.add_argument(
215
- "--eps-new", type=float, default=None, help="Adam epsilon for audio."
216
- )
217
- parser.add_argument(
218
- "--wd-new", type=float, default=0.2, help="Weight decay for audio."
219
- )
220
- parser.add_argument(
221
- "--momentum-new", type=float, default=0.9, help="Momentum for audio."
222
- )
223
- parser.add_argument(
224
- "--warmup", type=int, default=10000, help="Number of steps to warmup for."
225
- )
226
- parser.add_argument(
227
- "--use-bn-sync",
228
- default=False,
229
- action="store_true",
230
- help="Whether to use batch norm sync.",
231
- )
232
- parser.add_argument(
233
- "--skip-scheduler",
234
- action="store_true",
235
- default=False,
236
- help="Use this flag to skip the learning rate decay.",
237
- )
238
- parser.add_argument(
239
- "--save-frequency", type=int, default=1, help="How often to save checkpoints."
240
- )
241
- parser.add_argument(
242
- "--save-top-performance",
243
- type=int,
244
- default=0,
245
- help="Save the top x performance weights if the value >0",
246
- )
247
- parser.add_argument(
248
- "--save-most-recent",
249
- action="store_true",
250
- default=False,
251
- help="Always save the most recent model trained to epoch_latest.pt.",
252
- )
253
- parser.add_argument(
254
- "--zeroshot-frequency", type=int, default=2, help="How often to run zero shot."
255
- )
256
- parser.add_argument(
257
- "--val-frequency",
258
- type=int,
259
- default=1,
260
- help="How often to run evaluation with val data.",
261
- )
262
- parser.add_argument(
263
- "--resume",
264
- default=None,
265
- type=str,
266
- help="path to latest checkpoint (default: none)",
267
- )
268
- parser.add_argument(
269
- "--precision",
270
- choices=["amp", "fp16", "fp32"],
271
- default="amp",
272
- help="Floating point precision.",
273
- )
274
- parser.add_argument(
275
- "--amodel",
276
- type=str,
277
- default="RN50",
278
- help="Name of the audio backbone to use.",
279
- )
280
- parser.add_argument(
281
- "--tmodel",
282
- type=str,
283
- default="transformer",
284
- help="Name of the text backbone to use. Can be [transformer, bert, roberta, bart]",
285
- )
286
- parser.add_argument(
287
- "--pretrained-audio",
288
- default="",
289
- type=str,
290
- help="Use a pretrained audio model weights for the audio encoder of CLAP",
291
- )
292
- parser.add_argument(
293
- "--pretrained-text",
294
- default="",
295
- type=str,
296
- help="Use a pretrained text model weights for the text encoder of CLAP",
297
- )
298
- parser.add_argument(
299
- "--pretrained",
300
- default="",
301
- type=str,
302
- help="Use a pretrained CLIP model weights with the specified tag or file path.",
303
- )
304
- parser.add_argument(
305
- "--pretrained-image",
306
- default=False,
307
- action="store_true",
308
- help="Load imagenet pretrained weights for image tower backbone if available.",
309
- )
310
- parser.add_argument(
311
- "--lock-image",
312
- default=False,
313
- action="store_true",
314
- help="Lock full image tower by disabling gradients.",
315
- )
316
- parser.add_argument(
317
- "--lock-image-unlocked-groups",
318
- type=int,
319
- default=0,
320
- help="Leave last n image tower layer groups unlocked.",
321
- )
322
- parser.add_argument(
323
- "--lock-image-freeze-bn-stats",
324
- default=False,
325
- action="store_true",
326
- help="Freeze BatchNorm running stats in image tower for any locked layers.",
327
- )
328
- parser.add_argument(
329
- "--local-loss",
330
- default=False,
331
- action="store_true",
332
- help="calculate loss w/ local features @ global (instead of realizing full global @ global matrix)",
333
- )
334
- parser.add_argument(
335
- "--gather-with-grad",
336
- default=False,
337
- action="store_true",
338
- help="enable full distributed gradient for feature gather",
339
- )
340
- parser.add_argument(
341
- "--force-quick-gelu",
342
- default=False,
343
- action="store_true",
344
- help="Force use of QuickGELU activation for non-OpenAI transformer models.",
345
- )
346
- parser.add_argument(
347
- "--torchscript",
348
- default=False,
349
- action="store_true",
350
- help="torch.jit.script the model, also uses jit version of OpenAI models if pretrained=='openai'",
351
- )
352
- parser.add_argument(
353
- "--trace",
354
- default=False,
355
- action="store_true",
356
- help="torch.jit.trace the model for inference / eval only",
357
- )
358
- # arguments for distributed training
359
- parser.add_argument(
360
- "--dist-url",
361
- default="env://",
362
- type=str,
363
- help="url used to set up distributed training",
364
- )
365
- parser.add_argument(
366
- "--dist-backend", default="nccl", type=str, help="distributed backend"
367
- )
368
- parser.add_argument(
369
- "--report-to",
370
- default="",
371
- type=str,
372
- help="Options are ['wandb', 'tensorboard', 'wandb,tensorboard']",
373
- )
374
- parser.add_argument(
375
- "--wandb-notes", default="", type=str, help="Notes if logging with wandb"
376
- )
377
- parser.add_argument(
378
- "--C", type=float, default=3.16, help="inverse regularizer for logistic reg."
379
- )
380
- parser.add_argument(
381
- "--debug",
382
- default=False,
383
- action="store_true",
384
- help="If true, more information is logged.",
385
- )
386
- parser.add_argument(
387
- "--copy-codebase",
388
- default=False,
389
- action="store_true",
390
- help="If true, we copy the entire base on the log diretory, and execute from there.",
391
- )
392
- parser.add_argument(
393
- "--horovod",
394
- default=False,
395
- action="store_true",
396
- help="Use horovod for distributed training.",
397
- )
398
- parser.add_argument(
399
- "--ddp-static-graph",
400
- default=False,
401
- action="store_true",
402
- help="Enable static graph optimization for DDP in PyTorch >= 1.11.",
403
- )
404
- parser.add_argument(
405
- "--no-set-device-rank",
406
- default=False,
407
- action="store_true",
408
- help="Don't set device index from local rank (when CUDA_VISIBLE_DEVICES restricted to one per proc).",
409
- )
410
- parser.add_argument("--seed", type=int, default=4242, help="Default random seed.")
411
-
412
- parser.add_argument(
413
- "--top-k-checkpoint-select-dataset",
414
- type=str,
415
- default="all",
416
- help="The dataset of selecting top-k checkpoint.",
417
- )
418
-
419
- # @R10, @R@5, @R1, mAP@10
420
- parser.add_argument(
421
- "--top-k-checkpoint-select-metric",
422
- type=str,
423
- default="_R@10",
424
- help="The metric for selecting top-k checkpoint.",
425
- )
426
- parser.add_argument(
427
- "--openai-model-cache-dir",
428
- type=str,
429
- default="~/.cache/clip",
430
- help="Directory to download OpenAI models.",
431
- )
432
- parser.add_argument(
433
- "--optimizer",
434
- type=str,
435
- default="adamw",
436
- help="can be AdamW or SGD",
437
- )
438
- parser.add_argument(
439
- "--parallel-eval",
440
- default=False,
441
- action="store_true",
442
- help="Eval in parallel (multi-GPU, multi-node).",
443
- )
444
-
445
- parser.add_argument(
446
- "--no-eval",
447
- default=False,
448
- action="store_true",
449
- help="Training without evaluation.",
450
- )
451
-
452
- parser.add_argument(
453
- "--lp-mlp",
454
- default=False,
455
- action="store_true",
456
- help="Linear Probe using MLP layer or not.",
457
- )
458
-
459
- parser.add_argument(
460
- "--lp-freeze",
461
- default=False,
462
- action="store_true",
463
- help="Linear Probe using Freeze CLAP or not",
464
- )
465
-
466
- parser.add_argument(
467
- "--lp-act",
468
- default="None",
469
- type=str,
470
- help="Options are ['relu','elu','prelu','softmax','sigmoid']",
471
- )
472
-
473
- parser.add_argument(
474
- "--lp-loss", type=str, default="bce", help="Loss func of Linear Probe."
475
- )
476
-
477
- parser.add_argument(
478
- "--lp-metrics",
479
- type=str,
480
- default="map,mauc,acc",
481
- help="Metrics of Linear Probe.",
482
- )
483
-
484
- parser.add_argument(
485
- "--lp-lr", type=float, default=1e-4, help="learning rate of linear probe"
486
- )
487
- parser.add_argument(
488
- "--kappa",
489
- type=float,
490
- default=0,
491
- help="the kappa in the weighted contrastive loss, default is to turn off the weighted contrastive loss",
492
- )
493
-
494
- parser.add_argument(
495
- "--data-filling",
496
- type=str,
497
- default="pad",
498
- help="type of data filling when the audio length is shorter than the max length."
499
- "Can be one of the following: repeat, repeatpad, pad",
500
- )
501
- parser.add_argument(
502
- "--data-truncating",
503
- type=str,
504
- default="rand_trunc",
505
- help="type of data truncation when the audio length is longer than the max length."
506
- "Can be one of the following: rand_trunc, fusion",
507
- )
508
-
509
- parser.add_argument(
510
- "--clap-mlploss",
511
- default=False,
512
- action="store_true",
513
- help="Using MLP loss for CLAP model or not",
514
- )
515
-
516
- parser.add_argument(
517
- "--wandb-id",
518
- type=str,
519
- default=None,
520
- help="the id of wandb experiment to restore.",
521
- )
522
-
523
- parser.add_argument(
524
- "--sleep", type=float, default=0, help="sleep n seconds before start training"
525
- )
526
-
527
- # variable length processing
528
- parser.add_argument(
529
- "--enable-fusion",
530
- default=False,
531
- action="store_true",
532
- help="Enable feature funsion for variable-length data",
533
- )
534
-
535
- parser.add_argument(
536
- "--fusion-type",
537
- type=str,
538
- default="None",
539
- help="Type is among ['channel_map', 'daf_1d','aff_1d','iaff_1d','daf_2d','aff_2d','iaff_2d']",
540
- )
541
-
542
- parser.add_argument(
543
- "--mixup",
544
- default=False,
545
- action="store_true",
546
- help="Enable mixup in finetuning training.",
547
- )
548
- parser.add_argument(
549
- "--text-augment-selection",
550
- type=str,
551
- default=None,
552
- help="For selecting levels of augmented text. Type is among ['all', 'augment_only', 'none']",
553
- )
554
-
555
- args = parser.parse_args()
556
-
557
- # If some params are not passed, we use the default values based on model name.
558
- default_params = get_default_params(args.amodel)
559
- for name, val in default_params.items():
560
- if getattr(args, name) is None:
561
- setattr(args, name, val)
562
-
563
- return args
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/scheduler.py DELETED
@@ -1,24 +0,0 @@
1
- import numpy as np
2
-
3
-
4
- def assign_learning_rate(optimizer, new_lr):
5
- for param_group in optimizer.param_groups:
6
- param_group["lr"] = new_lr
7
-
8
-
9
- def _warmup_lr(base_lr, warmup_length, step):
10
- return base_lr * (step + 1) / warmup_length
11
-
12
-
13
- def cosine_lr(optimizer, base_lr, warmup_length, steps):
14
- def _lr_adjuster(step):
15
- if step < warmup_length:
16
- lr = _warmup_lr(base_lr, warmup_length, step)
17
- else:
18
- e = step - warmup_length
19
- es = steps - warmup_length
20
- lr = 0.5 * (1 + np.cos(np.pi * e / es)) * base_lr
21
- assign_learning_rate(optimizer, lr)
22
- return lr
23
-
24
- return _lr_adjuster
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/train.py DELETED
@@ -1,838 +0,0 @@
1
- import json
2
- import logging
3
- import math
4
- import os
5
- import time
6
- from contextlib import suppress
7
-
8
- import numpy as np
9
- import torch
10
- import torch.nn.functional as F
11
-
12
- try:
13
- import wandb
14
- except ImportError:
15
- wandb = None
16
-
17
- from open_clip import ClipLoss, gather_features
18
- from .distributed import is_master
19
- from .zero_shot import zero_shot_eval
20
-
21
-
22
- class AverageMeter(object):
23
- """Computes and stores the average and current value"""
24
-
25
- def __init__(self):
26
- self.reset()
27
-
28
- def reset(self):
29
- self.val = 0
30
- self.avg = 0
31
- self.sum = 0
32
- self.count = 0
33
-
34
- def update(self, val, n=1):
35
- self.val = val
36
- self.sum += val * n
37
- self.count += n
38
- self.avg = self.sum / self.count
39
-
40
-
41
- def unwrap_model(model):
42
- if hasattr(model, "module"):
43
- return model.module
44
- else:
45
- return model
46
-
47
-
48
- def train_one_epoch(
49
- model, data, epoch, optimizer, scaler, scheduler, args, tb_writer=None
50
- ):
51
- device = torch.device(args.device)
52
- autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
53
- model.train()
54
- loss = ClipLoss(
55
- local_loss=args.local_loss,
56
- gather_with_grad=args.gather_with_grad,
57
- cache_labels=True,
58
- rank=args.rank,
59
- world_size=args.world_size,
60
- use_horovod=args.horovod,
61
- mlp_loss=args.clap_mlploss,
62
- weight_loss_kappa=args.kappa,
63
- )
64
-
65
- dataloader, sampler = data["train"].dataloader, data["train"].sampler
66
- if args.distributed and sampler is not None:
67
- sampler.set_epoch(epoch)
68
- num_batches_per_epoch = dataloader.num_batches
69
- sample_digits = math.ceil(math.log(dataloader.num_samples + 1, 10))
70
-
71
- # for toy dataset
72
- if args.dataset_type == "toy":
73
- dataloader.dataset.generate_queue()
74
-
75
- loss_m = AverageMeter()
76
- batch_time_m = AverageMeter()
77
- data_time_m = AverageMeter()
78
- end = time.time()
79
-
80
- for i, batch in enumerate(dataloader):
81
- # logging.info(f"batch {i} of {num_batches_per_epoch}")
82
- step = num_batches_per_epoch * epoch + i
83
- if isinstance(scheduler, dict):
84
- for s in scheduler.values():
85
- s(step)
86
- else:
87
- scheduler(step)
88
- audios = batch # contains mel_spec, wavform, and longer list
89
- texts = batch["text"]
90
- # audios = audios.to(device=device, non_blocking=True)
91
- # texts = texts.to(device=device, non_blocking=True)
92
-
93
- data_time_m.update(time.time() - end)
94
- if isinstance(optimizer, dict):
95
- for o_ in optimizer.values():
96
- o_.zero_grad()
97
- else:
98
- optimizer.zero_grad()
99
-
100
- with autocast():
101
- (
102
- audio_features,
103
- text_features,
104
- audio_features_mlp,
105
- text_features_mlp,
106
- logit_scale_a,
107
- logit_scale_t,
108
- ) = model(audios, texts, device)
109
-
110
- if args.clap_mlploss:
111
- total_loss = loss(
112
- audio_features=audio_features,
113
- text_features=text_features,
114
- logit_scale_a=logit_scale_a,
115
- logit_scale_t=logit_scale_t,
116
- audio_features_mlp=audio_features_mlp,
117
- text_features_mlp=text_features_mlp,
118
- )
119
- else:
120
- total_loss = loss(
121
- audio_features=audio_features,
122
- text_features=text_features,
123
- logit_scale_a=logit_scale_a,
124
- )
125
- if isinstance(optimizer, dict):
126
- if scaler is not None:
127
- scaler.scale(total_loss).backward()
128
- for o_ in optimizer.values():
129
- if args.horovod:
130
- o_.synchronize()
131
- scaler.unscale_(o_)
132
- with o_.skip_synchronize():
133
- scaler.step(o_)
134
- else:
135
- scaler.step(o_)
136
- scaler.update()
137
- else:
138
- total_loss.backward()
139
- for o_ in optimizer.values():
140
- o_.step()
141
- else:
142
- if scaler is not None:
143
- scaler.scale(total_loss).backward()
144
- if args.horovod:
145
- optimizer.synchronize()
146
- scaler.unscale_(optimizer)
147
- with optimizer.skip_synchronize():
148
- scaler.step(optimizer)
149
- else:
150
- scaler.step(optimizer)
151
- scaler.update()
152
- else:
153
- total_loss.backward()
154
- optimizer.step()
155
-
156
- # Note: we clamp to 4.6052 = ln(100), as in the original paper.
157
- with torch.no_grad():
158
- unwrap_model(model).logit_scale_a.clamp_(0, math.log(100))
159
- if args.clap_mlploss:
160
- unwrap_model(model).logit_scale_t.clamp_(0, math.log(100))
161
-
162
- batch_time_m.update(time.time() - end)
163
- end = time.time()
164
- batch_count = i + 1
165
- if is_master(args) and (i % 100 == 0 or batch_count == num_batches_per_epoch):
166
- if isinstance(audios, dict):
167
- batch_size = len(audios["waveform"])
168
- else:
169
- batch_size = len(audios)
170
- num_samples = batch_count * batch_size * args.world_size
171
- samples_per_epoch = dataloader.num_samples
172
- percent_complete = 100.0 * batch_count / num_batches_per_epoch
173
-
174
- # NOTE loss is coarsely sampled, just master node and per log update
175
- loss_m.update(total_loss.item(), batch_size)
176
- logit_scale_scalar_a = logit_scale_a.item()
177
- logit_scale_scalar_t = logit_scale_t.item()
178
- if isinstance(optimizer, dict):
179
- if args.clap_mlploss:
180
- logging.info(
181
- f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
182
- f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
183
- f"Data (t): {data_time_m.avg:.3f} "
184
- f"Batch (t): {batch_time_m.avg:.3f} "
185
- f"LR: {[o_.param_groups[0]['lr'] for o_ in optimizer.values()]} "
186
- f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
187
- f"Logit Scale Text: {logit_scale_scalar_t:.3f}"
188
- )
189
- log_data = {
190
- "loss": loss_m.val,
191
- "data_time": data_time_m.val,
192
- "batch_time": batch_time_m.val,
193
- "scale_audio": logit_scale_scalar_a,
194
- "scale_text": logit_scale_scalar_t,
195
- "lr": [o_.param_groups[0]["lr"] for o_ in optimizer.values()],
196
- }
197
- else:
198
- logging.info(
199
- f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
200
- f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
201
- f"Data (t): {data_time_m.avg:.3f} "
202
- f"Batch (t): {batch_time_m.avg:.3f} "
203
- f"LR: {[o_.param_groups[0]['lr'] for o_ in optimizer.values()]} "
204
- f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
205
- )
206
- log_data = {
207
- "loss": loss_m.val,
208
- "data_time": data_time_m.val,
209
- "batch_time": batch_time_m.val,
210
- "scale_audio": logit_scale_scalar_a,
211
- "lr": [o_.param_groups[0]["lr"] for o_ in optimizer.values()],
212
- }
213
-
214
- else:
215
- if args.clap_mlploss:
216
- logging.info(
217
- f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
218
- f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
219
- f"Data (t): {data_time_m.avg:.3f} "
220
- f"Batch (t): {batch_time_m.avg:.3f} "
221
- f"LR: {optimizer.param_groups[0]['lr']:5f} "
222
- f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
223
- f"Logit Scale Text: {logit_scale_scalar_t:.3f}"
224
- )
225
-
226
- # Save train loss / etc. Using non avg meter values as loggers have their own smoothing
227
- log_data = {
228
- "loss": loss_m.val,
229
- "data_time": data_time_m.val,
230
- "batch_time": batch_time_m.val,
231
- "scale_audio": logit_scale_scalar_a,
232
- "scale_text": logit_scale_scalar_t,
233
- "lr": optimizer.param_groups[0]["lr"],
234
- }
235
- else:
236
- logging.info(
237
- f"Train Epoch: {epoch} [{num_samples:>{sample_digits}}/{samples_per_epoch} ({percent_complete:.0f}%)] "
238
- f"Loss: {loss_m.val:#.5g} ({loss_m.avg:#.4g}) "
239
- f"Data (t): {data_time_m.avg:.3f} "
240
- f"Batch (t): {batch_time_m.avg:.3f} "
241
- f"LR: {optimizer.param_groups[0]['lr']:5f} "
242
- f"Logit Scale Audio: {logit_scale_scalar_a:.3f}"
243
- )
244
-
245
- # Save train loss / etc. Using non avg meter values as loggers have their own smoothing
246
- log_data = {
247
- "loss": loss_m.val,
248
- "data_time": data_time_m.val,
249
- "batch_time": batch_time_m.val,
250
- "scale_audio": logit_scale_scalar_a,
251
- "lr": optimizer.param_groups[0]["lr"],
252
- }
253
- for name, val in log_data.items():
254
- name = "train/" + name
255
- if tb_writer is not None:
256
- tb_writer.add_scalar(name, val, step)
257
- if args.wandb:
258
- assert wandb is not None, "Please install wandb."
259
- wandb.log({name: val, "step": step})
260
-
261
- # resetting batch / data time meters per log window
262
- batch_time_m.reset()
263
- data_time_m.reset()
264
- # end for
265
-
266
-
267
- def evaluate(model, data, epoch, args, tb_writer=None):
268
- metrics = {}
269
- if not args.parallel_eval:
270
- if not is_master(args):
271
- return metrics
272
- device = torch.device(args.device)
273
- model.eval()
274
-
275
- # CHANGE
276
- # zero_shot_metrics = zero_shot_eval(model, data, epoch, args)
277
- # metrics.update(zero_shot_metrics)
278
- if is_master(args):
279
- print("Evaluating...")
280
- autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
281
- if args.val_dataset_names == ["Clotho", "audiocaps"]:
282
- # if only clotho and audiocaps are used, then we will use a different evaluation function.
283
- # This is because in the Clotho and audiocaps valid and test set, there are 5 text for 1 audio.
284
- if args.parallel_eval:
285
- # (yusong): just a hack here. Don't use parallel eval when evaluating only clotho and audiocaps.
286
- raise NotImplementedError(
287
- "Parallel evaluation not supported for eval only Clotho and audiocaps."
288
- )
289
- val_metrics_per_dataset = evaluate_clotho_audiocaps(
290
- model, data, epoch, args, autocast, device, tb_writer
291
- )
292
- for m in val_metrics_per_dataset.values():
293
- metrics.update(m)
294
- if "epoch" not in metrics.keys():
295
- metrics.update({"epoch": epoch})
296
- metrics = select_top_metric_clotho_audiocaps(
297
- metrics, val_metrics_per_dataset, args
298
- )
299
- elif "val" in data and (
300
- args.val_frequency
301
- and ((epoch % args.val_frequency) == 0 or epoch == args.epochs)
302
- ):
303
- dataloader = data["val"].dataloader
304
- num_samples = 0
305
- samples_per_val = dataloader.num_samples
306
-
307
- # FIXME this does not scale past small eval datasets
308
- # all_audio_features @ all_text_features will blow up memory and compute very quickly
309
- eval_info = {}
310
- if args.clap_mlploss:
311
- eval_info["all"] = {
312
- "cumulative_loss": 0.0,
313
- "num_samples": 0,
314
- "all_audio_features": [],
315
- "all_text_features": [],
316
- "all_audio_features_mlp": [],
317
- "all_text_features_mlp": [],
318
- } # cumulative_loss = 0.0
319
- else:
320
- eval_info["all"] = {
321
- "cumulative_loss": 0.0,
322
- "num_samples": 0,
323
- "all_audio_features": [],
324
- "all_text_features": [],
325
- } # cumu
326
- # all_audio_features, all_text_features, all_audio_features_mlp, all_text_features_mlp = [], [], [], []
327
- with torch.no_grad():
328
- for i, batch in enumerate(dataloader):
329
- audios = batch # contains mel_spec, wavform, and longer list
330
- texts = batch["text"]
331
- # audios = audios.to(device=device, non_blocking=True)
332
-
333
- all_names = list(
334
- set(["-".join(b.split("/")[-3:-1]) for b in batch["__url__"]])
335
- )
336
- for name in all_names:
337
- if name not in eval_info.keys():
338
- if args.clap_mlploss:
339
- eval_info[name] = {
340
- "cumulative_loss": 0.0,
341
- "num_samples": 0,
342
- "all_audio_features": [],
343
- "all_text_features": [],
344
- "all_audio_features_mlp": [],
345
- "all_text_features_mlp": [],
346
- }
347
- else:
348
- eval_info[name] = {
349
- "cumulative_loss": 0.0,
350
- "num_samples": 0,
351
- "all_audio_features": [],
352
- "all_text_features": [],
353
- }
354
- with autocast():
355
- (
356
- audio_features,
357
- text_features,
358
- audio_features_mlp,
359
- text_features_mlp,
360
- logit_scale_a,
361
- logit_scale_t,
362
- ) = model(audios, texts, device)
363
-
364
- if args.parallel_eval:
365
- # multi-GPU eval
366
- if args.clap_mlploss:
367
- (
368
- audio_features,
369
- text_features,
370
- audio_features_mlp,
371
- text_features_mlp,
372
- ) = gather_features(
373
- audio_features=audio_features,
374
- text_features=text_features,
375
- audio_features_mlp=audio_features_mlp,
376
- text_features_mlp=text_features_mlp,
377
- local_loss=False,
378
- gather_with_grad=False,
379
- rank=args.rank,
380
- world_size=args.world_size,
381
- use_horovod=args.horovod,
382
- mlp_loss=args.clap_mlploss,
383
- )
384
- else:
385
- (audio_features, text_features,) = gather_features(
386
- audio_features=audio_features,
387
- text_features=text_features,
388
- local_loss=False,
389
- gather_with_grad=False,
390
- rank=args.rank,
391
- world_size=args.world_size,
392
- use_horovod=args.horovod,
393
- mlp_loss=args.clap_mlploss,
394
- )
395
-
396
- if is_master(args):
397
- num_samples += audio_features.shape[0]
398
- for n in [*all_names, "all"]:
399
- if n == "all":
400
- eval_info[n]["all_audio_features"].append(
401
- audio_features.cpu()
402
- )
403
- eval_info[n]["all_text_features"].append(
404
- text_features.cpu()
405
- )
406
- if args.clap_mlploss:
407
- eval_info[n]["all_audio_features_mlp"].append(
408
- audio_features_mlp.cpu()
409
- )
410
- eval_info[n]["all_text_features_mlp"].append(
411
- text_features_mlp.cpu()
412
- )
413
- else:
414
- idx = np.where(
415
- np.array(
416
- [
417
- "-".join(b.split("/")[-3:-1])
418
- for b in batch["__url__"]
419
- ]
420
- )
421
- == n
422
- )[0]
423
- eval_info[n]["all_audio_features"].append(
424
- audio_features.cpu().index_select(
425
- 0, torch.tensor(idx).long()
426
- )
427
- )
428
- eval_info[n]["all_text_features"].append(
429
- text_features.cpu().index_select(
430
- 0, torch.tensor(idx).long()
431
- )
432
- )
433
- if args.clap_mlploss:
434
- eval_info[n]["all_audio_features_mlp"].append(
435
- audio_features_mlp.cpu().index_select(
436
- 0, torch.tensor(idx).long()
437
- )
438
- )
439
- eval_info[n]["all_text_features_mlp"].append(
440
- text_features_mlp.cpu().index_select(
441
- 0, torch.tensor(idx).long()
442
- )
443
- )
444
- # print(f'eval step {i}') # (yusong): for debug
445
-
446
- # cumulative_loss += total_loss * batch_size
447
- # num_samples += batch_size
448
- if is_master(args) and (i % 100) == 0: # and i != 0:
449
- logging.info(
450
- f"Eval Epoch: {epoch} [{num_samples} / {samples_per_val}]"
451
- )
452
- if is_master(args):
453
- val_metrics_per_dataset = {}
454
- for n in eval_info.keys():
455
- if args.clap_mlploss:
456
- metrics_single_dataset = get_metrics(
457
- audio_features=torch.cat(
458
- eval_info[n]["all_audio_features"]
459
- ),
460
- text_features=torch.cat(eval_info[n]["all_text_features"]),
461
- logit_scale_a=logit_scale_a.cpu(),
462
- audio_features_mlp=torch.cat(
463
- eval_info[n]["all_audio_features_mlp"]
464
- ),
465
- text_features_mlp=torch.cat(
466
- eval_info[n]["all_text_features_mlp"]
467
- ),
468
- logit_scale_t=logit_scale_t.cpu(),
469
- mlp_loss=args.clap_mlploss,
470
- )
471
- else:
472
- metrics_single_dataset = get_metrics(
473
- audio_features=torch.cat(
474
- eval_info[n]["all_audio_features"]
475
- ),
476
- text_features=torch.cat(eval_info[n]["all_text_features"]),
477
- logit_scale_a=logit_scale_a.cpu(),
478
- mlp_loss=args.clap_mlploss,
479
- )
480
- val_metrics_per_dataset[n] = {
481
- n + "/" + k: v for k, v in metrics_single_dataset.items()
482
- }
483
- metrics.update(val_metrics_per_dataset[n])
484
- if "epoch" not in metrics.keys():
485
- metrics.update({"epoch": epoch})
486
- if is_master(args):
487
- if not metrics:
488
- return metrics
489
-
490
- logging.info(
491
- f"Eval Epoch: {epoch} "
492
- + "\n".join(
493
- [
494
- "\t".join([f"{k}: {round(v, 4):.4f}" for k, v in m.items()])
495
- for m in val_metrics_per_dataset.values()
496
- ]
497
- )
498
- )
499
-
500
- if args.save_logs:
501
- for name, val in metrics.items():
502
- if tb_writer is not None:
503
- tb_writer.add_scalar(f"val/{name}", val, epoch)
504
-
505
- with open(os.path.join(args.checkpoint_path, "results.jsonl"), "a+") as f:
506
- f.write(json.dumps(metrics))
507
- f.write("\n")
508
-
509
- if args.wandb:
510
- assert wandb is not None, "Please install wandb."
511
- for name, val in metrics.items():
512
- wandb.log({f"val/{name}": val, "epoch": epoch})
513
-
514
- return metrics
515
- else:
516
- return metrics
517
-
518
-
519
- def get_metrics(
520
- audio_features,
521
- text_features,
522
- logit_scale_a,
523
- audio_features_mlp=None,
524
- text_features_mlp=None,
525
- logit_scale_t=None,
526
- mlp_loss=False,
527
- ):
528
- metrics = {}
529
- if mlp_loss:
530
- # Set up audio to text & text to audio similary matrice
531
- a_logits_per_audio = (
532
- (logit_scale_a * audio_features @ text_features_mlp.t()).detach().cpu()
533
- )
534
- a_logits_per_text = a_logits_per_audio.t().detach().cpu()
535
- t_logits_per_audio = (
536
- (logit_scale_t * audio_features_mlp @ text_features.t()).detach().cpu()
537
- )
538
- t_logits_per_text = t_logits_per_audio.t().detach().cpu()
539
-
540
- labels = torch.arange(audio_features.shape[0]).long()
541
- # Change the loss from two terms into four terms with 2x2 combined CE loss
542
- total_loss = (
543
- F.cross_entropy(a_logits_per_audio, labels)
544
- + F.cross_entropy(a_logits_per_text, labels)
545
- + F.cross_entropy(t_logits_per_audio, labels)
546
- + F.cross_entropy(t_logits_per_text, labels)
547
- ) / 4
548
-
549
- metrics[f"cumulative_loss"] = total_loss.item()
550
- metrics[f"num_samples"] = audio_features.shape[0]
551
-
552
- logits = {
553
- "audio_to_text": (a_logits_per_audio + t_logits_per_audio) / 2,
554
- "text_to_audio": (a_logits_per_text + t_logits_per_text) / 2,
555
- }
556
- ground_truth = torch.arange(len(text_features)).view(-1, 1)
557
-
558
- else:
559
- # print("text_features", text_features)
560
- # print("text_features.shape", text_features.shape)
561
- logits_per_audio = (
562
- (logit_scale_a * audio_features @ text_features.t()).detach().cpu()
563
- )
564
- logits_per_text = logits_per_audio.t().detach().cpu()
565
-
566
- labels = torch.arange(audio_features.shape[0]).long()
567
- # Change the loss from two terms into four terms with 2x2 combined CE loss
568
- total_loss = (
569
- F.cross_entropy(logits_per_audio, labels)
570
- + F.cross_entropy(logits_per_text, labels)
571
- ) / 2
572
-
573
- metrics[f"cumulative_loss"] = total_loss.item()
574
- metrics[f"num_samples"] = audio_features.shape[0]
575
-
576
- logits = {"audio_to_text": logits_per_audio, "text_to_audio": logits_per_text}
577
-
578
- ground_truth = torch.arange(len(text_features)).view(-1, 1)
579
-
580
- for name, logit in logits.items():
581
- ranking = torch.argsort(logit, descending=True)
582
- preds = torch.where(ranking == ground_truth)[
583
- 1
584
- ] # (yusong) this line is slow because it uses single thread
585
- preds = preds.detach().cpu().numpy()
586
- metrics[f"{name}_mean_rank"] = preds.mean() + 1
587
- metrics[f"{name}_median_rank"] = np.floor(np.median(preds)) + 1
588
- for k in [1, 5, 10]:
589
- metrics[f"{name}_R@{k}"] = np.mean(preds < k)
590
- # map@10
591
- metrics[f"{name}_mAP@10"] = np.mean(np.where(preds < 10, 1 / (preds + 1), 0.0))
592
-
593
- return metrics
594
-
595
-
596
- def evaluate_clotho_audiocaps(
597
- model, data, epoch, args, autocast, device, tb_writer=None
598
- ):
599
- """
600
- Adapted from https://github.com/XinhaoMei/audio-text_retrieval/blob/main/tools/utils.py.
601
- 1. for text-to-audio retrieval, do 5 times and average the results
602
- 2. for R@1, R@5, R@10 in audio-to-text retrieval, take the best rank among 5 text
603
- 3. for map@10 in audio-to-text retrieval:
604
- 3.1: sort the rank of 5 text
605
- 3.2: exclude the rank >=10 (0-index)
606
- 3.3: compute the map regarding the remaining ranks: np.mean(np.arange(1, len(ranks)+1) / ranks).
607
- (3.3) That is, take the top ranks of 5 text that is < 10, and assign the descending number as ground truth.
608
- (3.3) E.g.: the ground truth of first rank of the 5 text should be 1, the second rank should be 2, etc.
609
- """
610
- # TODO: (yusong) only support single GPU evaluation and only support non-mlp case for now.
611
- dataloader = data["val"].dataloader
612
- with torch.no_grad():
613
- eval_info = {}
614
- for i, batch in enumerate(dataloader):
615
- audios = batch # contains mel_spec, wavform, and longer list
616
-
617
- # each item in the list has 5 texts
618
- if args.tmodel == "transformer":
619
- from open_clip import tokenize
620
-
621
- texts = [tokenize(t) for t in batch["full_text"]]
622
- texts = torch.cat(texts)
623
- else:
624
- from .data import tokenizer
625
-
626
- texts = [
627
- tokenizer(t) for t in batch["full_text"]
628
- ] # 5 texts for each audio
629
- texts = {
630
- k: torch.cat([t[k] for t in texts]) for k in texts[0].keys()
631
- } # 5 x batch
632
-
633
- # audios = audios.to(device=device, non_blocking=True)
634
-
635
- all_names = list(
636
- set(["-".join(b.split("/")[-3:-1]) for b in batch["__url__"]])
637
- )
638
- for name in all_names:
639
- if name not in eval_info.keys():
640
- # we will not use mlp outputs even if args.clap_mlploss=True
641
- eval_info[name] = {
642
- "cumulative_loss": 0.0,
643
- "num_samples": 0,
644
- "all_audio_features": [],
645
- "all_text_features": [],
646
- }
647
- with autocast():
648
- audio_features = model(audios, None, device)
649
- text_features = model(None, texts, device)
650
- audio_features = F.normalize(audio_features, dim=-1)
651
- text_features = F.normalize(text_features, dim=-1)
652
-
653
- all_names = list(
654
- set(["-".join(b.split("/")[-3:-1]) for b in batch["__url__"]])
655
- )
656
- for n in all_names:
657
- idx = np.where(
658
- np.array(
659
- ["-".join(b.split("/")[-3:-1]) for b in batch["__url__"]]
660
- )
661
- == n
662
- )[0]
663
- eval_info[n]["all_audio_features"].append(
664
- audio_features.cpu().index_select(0, torch.tensor(idx).long())
665
- )
666
- # (yusong) please double-check. This is for selecting 5 text features at once.
667
- # because idx is a list of indices in size of num_samples,
668
- # and text_features is a tensor of size (5*num_samples, dim)
669
- # so we need to select 5 consecutive indices at once for a single index in idx.
670
- eval_info[n]["all_text_features"].append(
671
- text_features.cpu()
672
- .reshape([-1, 5, text_features.shape[1]])
673
- .index_select(0, torch.tensor(idx).long())
674
- .reshape([-1, text_features.shape[1]])
675
- )
676
-
677
- val_metrics_all = {}
678
-
679
- for n in eval_info.keys():
680
- logit_scale_a, logit_scale_t = model(None, None, device)
681
- logit_scale_a = logit_scale_a.cpu()
682
-
683
- audio_features = torch.cat(eval_info[n]["all_audio_features"], dim=0)
684
- text_features = torch.cat(eval_info[n]["all_text_features"], dim=0)
685
-
686
- logits_per_audio = (
687
- (logit_scale_a * audio_features @ text_features.t()).detach().cpu()
688
- )
689
- logits_per_text = logits_per_audio.t().detach().cpu()
690
-
691
- # logits_per_audio shape: [num_samples, num_samples*5]
692
- # logits_per_text shape: [num_samples*5, num_samples]
693
-
694
- logging.info(
695
- f"dataset {n}, logits_per_audio shape: {logits_per_audio.shape}, "
696
- f"logits_per_text shape: {logits_per_text.shape}"
697
- )
698
-
699
- metrics = {}
700
- num_samples = audio_features.shape[0]
701
- metrics[f"num_samples"] = num_samples
702
-
703
- # (yusong) the following code is very important, please double-check:
704
- # logits_per_audio.reshape(num_samples, num_samples, 5)[:, :, d]
705
- # logits_per_text.reshape(num_samples, 5, num_samples)[:, d, :]
706
- # Those two are retrieving one of the 5 text for each audio.
707
- labels = torch.arange(audio_features.shape[0]).long()
708
- audio_to_text_loss = [
709
- F.cross_entropy(
710
- logits_per_audio.reshape(num_samples, num_samples, 5)[:, :, d],
711
- labels,
712
- )
713
- for d in range(5)
714
- ]
715
- text_to_audio_loss = [
716
- F.cross_entropy(
717
- logits_per_text.reshape(num_samples, 5, num_samples)[:, d, :],
718
- labels,
719
- )
720
- for d in range(5)
721
- ]
722
- total_loss = (np.mean(audio_to_text_loss) + np.mean(text_to_audio_loss)) / 2
723
-
724
- metrics[f"cumulative_loss"] = total_loss.item()
725
-
726
- # text to audio: do 5 times
727
- pred_text = []
728
- for d in range(5):
729
- logit = logits_per_text.reshape(num_samples, 5, num_samples)[:, d, :]
730
- ground_truth = torch.arange(len(logit)).view(-1, 1)
731
- ranking = torch.argsort(
732
- logit, descending=True
733
- ) # [num_samples, num_samples]
734
- preds = torch.where(ranking == ground_truth)[1]
735
- pred_text.append(preds.detach().cpu().numpy())
736
- pred_text_concat = np.concatenate(pred_text, axis=0) # [5*num_samples]
737
- metrics[f"text_to_audio_mean_rank"] = pred_text_concat.mean() + 1
738
- metrics[f"text_to_audio_median_rank"] = (
739
- np.floor(np.median(pred_text_concat)) + 1
740
- )
741
- for k in [1, 5, 10]:
742
- metrics[f"text_to_audio_R@{k}"] = np.mean(pred_text_concat < k)
743
- # map@10
744
- metrics[f"text_to_audio_mAP@10"] = np.mean(
745
- np.where(pred_text_concat < 10, 1 / (pred_text_concat + 1), 0.0)
746
- )
747
-
748
- # audio to text: take the best result
749
- # for audio to text map 10, sort and assign descending ground truth.
750
- # see https://github.com/XinhaoMei/audio-text_retrieval/blob/main/tools/utils.py#L103
751
- # map@10
752
- map_all = []
753
- pred_audio_all = []
754
- for d in range(num_samples):
755
- # logits_per_audio: [num_samples, num_samples*5]
756
- logit_single = logits_per_audio[d, :] # [5*num_samples]
757
- # Ground-truth index: [d*5, d*5+1, d*5+2, d*5+3, d*5+4]
758
- ranking = torch.argsort(
759
- logit_single, descending=True
760
- ) # [5*num_samples]
761
- # ranking: the index of first match, second match, ...
762
- ground_truth = torch.arange(d * 5, d * 5 + 5)[None]
763
- all_pred = torch.where(
764
- torch.stack([ranking] * 5) == ground_truth.view(-1, 1)
765
- )[1]
766
- min_pred = torch.min(all_pred)
767
- pred_audio_all.append(min_pred.detach().cpu().numpy())
768
- all_pred_filter = all_pred[all_pred < 10].detach().cpu().numpy()
769
- # /5 because we have 5 text, so it means for the text rank >=10 we count as 0.
770
- map_single = (
771
- np.sum(
772
- (np.arange(1, len(all_pred_filter) + 1) / (all_pred_filter + 1))
773
- )
774
- / 5
775
- )
776
- map_all.append(map_single)
777
- metrics[f"audio_to_text_mAP@10"] = np.mean(map_all)
778
- for k in [1, 5, 10]:
779
- metrics[f"audio_to_text_R@{k}"] = np.mean(np.array(pred_audio_all) < k)
780
-
781
- val_metrics_all[n] = {n + "/" + k: v for k, v in metrics.items()}
782
- return val_metrics_all
783
-
784
-
785
- def calculate_selection_performance_clotho_audiocaps(val_metrics_per_dataset):
786
- """
787
- Calculate performance for Clotho+AudioCaps for model selection.
788
- """
789
- selection_performance_all = []
790
- for n in val_metrics_per_dataset.keys():
791
- selection_performance = (
792
- val_metrics_per_dataset[n][f"{n}/audio_to_text_mAP@10"]
793
- + val_metrics_per_dataset[n][f"{n}/text_to_audio_mAP@10"]
794
- ) / 2
795
- selection_performance_all.append(selection_performance)
796
- return np.mean(selection_performance_all)
797
-
798
-
799
- def select_top_metric_clotho_audiocaps(metrics, val_metrics_per_dataset, args):
800
- # val_metrics_per_dataset: dict, key: dataset name, value: dict, key: metric name, value: metric value
801
- # metrics: dict, key: metric name, value: metric value
802
- # Hack: use args to save the top performance
803
- if not hasattr(args, "top_selection_performance"):
804
- selection_performance = calculate_selection_performance_clotho_audiocaps(
805
- val_metrics_per_dataset
806
- )
807
- # TODO: write the if and else together
808
- metric_update = {}
809
- for n in val_metrics_per_dataset.keys():
810
- for k in val_metrics_per_dataset[n].keys():
811
- metric_update[
812
- k.split("/")[0] + "-top" + "/" + k.split("/")[1]
813
- ] = val_metrics_per_dataset[n][k]
814
- metric_update["top_selection_performance"] = selection_performance
815
- metric_update["top-selection-epoch"] = metrics["epoch"]
816
- metrics.update(metric_update)
817
- args.top_metric = metric_update
818
- args.top_selection_performance = selection_performance
819
- else:
820
- selection_performance_new = calculate_selection_performance_clotho_audiocaps(
821
- val_metrics_per_dataset
822
- )
823
- selection_performance_old = args.top_selection_performance
824
- if selection_performance_new > selection_performance_old:
825
- metric_update = {}
826
- for n in val_metrics_per_dataset.keys():
827
- for k in val_metrics_per_dataset[n].keys():
828
- metric_update[
829
- k.split("/")[0] + "-top" + "/" + k.split("/")[1]
830
- ] = val_metrics_per_dataset[n][k]
831
- metric_update["top_selection_performance"] = selection_performance_new
832
- metric_update["top-selection-epoch"] = metrics["epoch"]
833
- metrics.update(metric_update)
834
- args.top_metric = metric_update
835
- args.top_selection_performance = selection_performance_new
836
- else:
837
- metrics.update(args.top_metric)
838
- return metrics
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/CLAP/training/zero_shot.py DELETED
@@ -1,95 +0,0 @@
1
- # NOTE: This script is currently not supported for CLAP.
2
- import logging
3
- from contextlib import suppress
4
-
5
- import torch
6
- import torch.nn.functional as F
7
- from tqdm import tqdm
8
-
9
- from open_clip import tokenize
10
- from .imagenet_zeroshot_data import imagenet_classnames, openai_imagenet_template
11
-
12
-
13
- def zero_shot_classifier(model, classnames, templates, args):
14
- with torch.no_grad():
15
- zeroshot_weights = []
16
- for classname in tqdm(classnames):
17
- texts = [template(classname) for template in templates] # format with class
18
- texts = tokenize(texts).to(args.device) # tokenize
19
- if args.distributed and not args.horovod:
20
- class_embeddings = model.module.encode_text(texts)
21
- else:
22
- class_embeddings = model.encode_text(texts)
23
- class_embedding = F.normalize(class_embeddings, dim=-1).mean(dim=0)
24
- class_embedding /= class_embedding.norm()
25
- zeroshot_weights.append(class_embedding)
26
- zeroshot_weights = torch.stack(zeroshot_weights, dim=1).to(args.device)
27
- return zeroshot_weights
28
-
29
-
30
- def accuracy(output, target, topk=(1,)):
31
- pred = output.topk(max(topk), 1, True, True)[1].t()
32
- correct = pred.eq(target.view(1, -1).expand_as(pred))
33
- return [
34
- float(correct[:k].reshape(-1).float().sum(0, keepdim=True).cpu().numpy())
35
- for k in topk
36
- ]
37
-
38
-
39
- def run(model, classifier, dataloader, args):
40
- autocast = torch.cuda.amp.autocast if args.precision == "amp" else suppress
41
- with torch.no_grad():
42
- top1, top5, n = 0.0, 0.0, 0.0
43
- for images, target in tqdm(dataloader, unit_scale=args.batch_size):
44
- images = images.to(args.device)
45
- target = target.to(args.device)
46
-
47
- with autocast():
48
- # predict
49
- if args.distributed and not args.horovod:
50
- image_features = model.module.encode_image(images)
51
- else:
52
- image_features = model.encode_image(images)
53
- image_features = F.normalize(image_features, dim=-1)
54
- logits = 100.0 * image_features @ classifier
55
-
56
- # measure accuracy
57
- acc1, acc5 = accuracy(logits, target, topk=(1, 5))
58
- top1 += acc1
59
- top5 += acc5
60
- n += images.size(0)
61
-
62
- top1 = top1 / n
63
- top5 = top5 / n
64
- return top1, top5
65
-
66
-
67
- def zero_shot_eval(model, data, epoch, args):
68
- if "imagenet-val" not in data and "imagenet-v2" not in data:
69
- return {}
70
- if args.zeroshot_frequency == 0:
71
- return {}
72
- if (epoch % args.zeroshot_frequency) != 0 and epoch != args.epochs:
73
- return {}
74
-
75
- logging.info("Starting zero-shot imagenet.")
76
-
77
- logging.info("Building zero-shot classifier")
78
- classifier = zero_shot_classifier(
79
- model, imagenet_classnames, openai_imagenet_template, args
80
- )
81
-
82
- logging.info("Using classifier")
83
- results = {}
84
- if "imagenet-val" in data:
85
- top1, top5 = run(model, classifier, data["imagenet-val"].dataloader, args)
86
- results["imagenet-zeroshot-val-top1"] = top1
87
- results["imagenet-zeroshot-val-top5"] = top5
88
- if "imagenet-v2" in data:
89
- top1, top5 = run(model, classifier, data["imagenet-v2"].dataloader, args)
90
- results["imagenetv2-zeroshot-val-top1"] = top1
91
- results["imagenetv2-zeroshot-val-top5"] = top5
92
-
93
- logging.info("Finished zero-shot imagenet.")
94
-
95
- return results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/clap_encoder.py CHANGED
@@ -1,17 +1,14 @@
1
- import random
2
  import torch
3
  import torch.nn as nn
4
- import torchaudio
5
  from models.CLAP.open_clip import create_model
6
- from models.CLAP.training.data import get_audio_features
7
  from transformers import RobertaTokenizer
 
8
 
9
 
10
  class CLAP_Encoder(nn.Module):
11
  def __init__(
12
  self,
13
  pretrained_path='checkpoint/music_speech_audioset_epoch_15_esc_89.98.pt',
14
- sampling_rate=32000,
15
  amodel = "HTSAT-base",
16
  ):
17
  super().__init__()
@@ -22,7 +19,7 @@ class CLAP_Encoder(nn.Module):
22
  self.enable_fusion = False # False if you do not want to use the fusion model
23
  self.fusion_type = "aff_2d"
24
  self.pretrained = pretrained_path
25
- self.sampling_rate = sampling_rate
26
  self.tokenize = RobertaTokenizer.from_pretrained("roberta-base")
27
 
28
  self.model, self.model_cfg = create_model(
@@ -41,39 +38,7 @@ class CLAP_Encoder(nn.Module):
41
  self.model.eval()
42
  self.encoder_type = 'CLAP'
43
 
44
- def batch_to_list(self, batch):
45
- ret = []
46
- for i in range(batch.size(0)):
47
- ret.append(batch[i])
48
- return ret
49
-
50
- def _get_audio_embed(self, batch):
51
- # batch: [B, samples]
52
- with torch.no_grad():
53
- audio_dict_list = []
54
- assert (
55
- self.sampling_rate == 32000
56
- ), "We only support 32000 sampling rate"
57
-
58
- # batch: [bs, 1, t-samples]
59
- batch = torchaudio.functional.resample(
60
- batch, orig_freq=self.sampling_rate, new_freq=48000
61
- )
62
- for waveform in self.batch_to_list(batch):
63
- audio_dict = {}
64
- audio_dict = get_audio_features(
65
- audio_dict,
66
- waveform,
67
- 480000,
68
- data_truncating="fusion",
69
- data_filling="repeatpad",
70
- audio_cfg=self.model_cfg["audio_cfg"],
71
- )
72
- audio_dict_list.append(audio_dict)
73
- # [bs, 512]
74
- embed = self.model.get_audio_embedding(audio_dict_list)
75
-
76
- return embed.detach()
77
 
78
  def _get_text_embed(self, batch):
79
  double_batch = False
@@ -91,15 +56,9 @@ class CLAP_Encoder(nn.Module):
91
 
92
 
93
  def get_query_embed(self, modality, audio=None, text=None, use_text_ratio=0.5, device=None):
94
- if modality == 'audio':
95
- embed = self._get_audio_embed(audio)
96
- elif modality == 'text':
97
  embed = self._get_text_embed(text)
98
- elif modality == 'hybird':
99
- if random.random() > use_text_ratio:
100
- embed = self._get_audio_embed(audio)
101
- else:
102
- embed = self._get_text_embed(text)
103
  else:
104
  raise NotImplementedError("Please check flag 'training_modality'.")
105
 
 
 
1
  import torch
2
  import torch.nn as nn
 
3
  from models.CLAP.open_clip import create_model
 
4
  from transformers import RobertaTokenizer
5
+ # removed: import random, torchaudio, get_audio_features — only used by _get_audio_embed (audio modality), not needed for text inference
6
 
7
 
8
  class CLAP_Encoder(nn.Module):
9
  def __init__(
10
  self,
11
  pretrained_path='checkpoint/music_speech_audioset_epoch_15_esc_89.98.pt',
 
12
  amodel = "HTSAT-base",
13
  ):
14
  super().__init__()
 
19
  self.enable_fusion = False # False if you do not want to use the fusion model
20
  self.fusion_type = "aff_2d"
21
  self.pretrained = pretrained_path
22
+ # removed: self.sampling_rate only used by _get_audio_embed
23
  self.tokenize = RobertaTokenizer.from_pretrained("roberta-base")
24
 
25
  self.model, self.model_cfg = create_model(
 
38
  self.model.eval()
39
  self.encoder_type = 'CLAP'
40
 
41
+ # removed: batch_to_list, _get_audio_embed — audio modality not used in inference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  def _get_text_embed(self, batch):
44
  double_batch = False
 
56
 
57
 
58
  def get_query_embed(self, modality, audio=None, text=None, use_text_ratio=0.5, device=None):
59
+ # removed: audio and hybird modality branches — only text modality used in inference
60
+ if modality == 'text':
 
61
  embed = self._get_text_embed(text)
 
 
 
 
 
62
  else:
63
  raise NotImplementedError("Please check flag 'training_modality'.")
64
 
models/resunet.py CHANGED
@@ -652,65 +652,6 @@ class ResUNet30(nn.Module):
652
 
653
  return output_dict
654
 
655
- @torch.no_grad()
656
- def chunk_inference(self, input_dict):
657
- chunk_config = {
658
- 'NL': 1.0,
659
- 'NC': 3.0,
660
- 'NR': 1.0,
661
- 'RATE': 32000
662
- }
663
-
664
- mixtures = input_dict['mixture']
665
- conditions = input_dict['condition']
666
-
667
- film_dict = self.film(
668
- conditions=conditions,
669
- )
670
-
671
- NL = int(chunk_config['NL'] * chunk_config['RATE'])
672
- NC = int(chunk_config['NC'] * chunk_config['RATE'])
673
- NR = int(chunk_config['NR'] * chunk_config['RATE'])
674
-
675
- L = mixtures.shape[2]
676
-
677
- out_np = np.zeros([1, L])
678
-
679
- WINDOW = NL + NC + NR
680
- current_idx = 0
681
-
682
- while current_idx + WINDOW < L:
683
- chunk_in = mixtures[:, :, current_idx:current_idx + WINDOW]
684
-
685
- chunk_out = self.base(
686
- mixtures=chunk_in,
687
- film_dict=film_dict,
688
- )['waveform']
689
-
690
- chunk_out_np = chunk_out.squeeze(0).cpu().data.numpy()
691
-
692
- if current_idx == 0:
693
- out_np[:, current_idx:current_idx+WINDOW-NR] = \
694
- chunk_out_np[:, :-NR] if NR != 0 else chunk_out_np
695
- else:
696
- out_np[:, current_idx+NL:current_idx+WINDOW-NR] = \
697
- chunk_out_np[:, NL:-NR] if NR != 0 else chunk_out_np[:, NL:]
698
-
699
- current_idx += NC
700
-
701
- if current_idx < L:
702
- chunk_in = mixtures[:, :, current_idx:current_idx + WINDOW]
703
- chunk_out = self.base(
704
- mixtures=chunk_in,
705
- film_dict=film_dict,
706
- )['waveform']
707
-
708
- chunk_out_np = chunk_out.squeeze(0).cpu().data.numpy()
709
-
710
- seg_len = chunk_out_np.shape[1]
711
- out_np[:, current_idx + NL:current_idx + seg_len] = \
712
- chunk_out_np[:, NL:]
713
-
714
- return out_np
715
 
716
 
 
652
 
653
  return output_dict
654
 
655
+ # removed: chunk_inference — not called during inference; also had a latent self.sampling_rate AttributeError in the original source
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
 
657