TobiasLogic commited on
Commit
7a76816
·
verified ·
1 Parent(s): d5d6c88

Upload eval_fad.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. eval_fad.py +122 -0
eval_fad.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import shutil
7
+
8
+ import numpy as np
9
+ import soundfile as sf
10
+ import torch
11
+ from diffusers.pipelines.deprecated.audio_diffusion.mel import Mel
12
+ from frechet_audio_distance import FrechetAudioDistance
13
+
14
+ from sample import generate, image_from_tensor, load_clap, load_model
15
+
16
+ PANN_EMBED_DIM = 2048
17
+
18
+ def fad_score(frechet, background_dir, eval_dir):
19
+ audio_bg = frechet._FrechetAudioDistance__load_audio_files(background_dir, dtype="float32")
20
+ embds_bg = frechet.get_embeddings(audio_bg, sr=frechet.sample_rate).reshape(-1, PANN_EMBED_DIM)
21
+ audio_ev = frechet._FrechetAudioDistance__load_audio_files(eval_dir, dtype="float32")
22
+ embds_ev = frechet.get_embeddings(audio_ev, sr=frechet.sample_rate).reshape(-1, PANN_EMBED_DIM)
23
+ mu1, sigma1 = frechet.calculate_embd_statistics(embds_bg)
24
+ mu2, sigma2 = frechet.calculate_embd_statistics(embds_ev)
25
+ return frechet.calculate_frechet_distance(mu1, sigma1, mu2, sigma2)
26
+
27
+ def first_caption_per_clip(meta):
28
+ seen, order = {}, []
29
+ for pair_idx, ci in enumerate(meta["pair_clip_idx"]):
30
+ if ci not in seen:
31
+ seen[ci] = meta["captions"][pair_idx]
32
+ order.append(ci)
33
+ return order, seen
34
+
35
+ def main():
36
+ ap = argparse.ArgumentParser()
37
+ ap.add_argument("--data", default="/root/data")
38
+ ap.add_argument("--audio-dir", default="/root/clotho_raw/evaluation")
39
+ ap.add_argument("--weights", default="/root/runs/audio_v1/model_best.safetensors")
40
+ ap.add_argument("--config", default="/root/runs/audio_v1/config.json")
41
+ ap.add_argument("--clap", default="laion/clap-htsat-unfused")
42
+ ap.add_argument("--n-eval", type=int, default=300)
43
+ ap.add_argument("--cfg", type=float, default=4.0)
44
+ ap.add_argument("--steps", type=int, default=50)
45
+ ap.add_argument("--seed", type=int, default=0)
46
+ ap.add_argument("--work", default="/root/fad_work")
47
+ ap.add_argument("--out", default="/root/fad_results.json")
48
+ ap.add_argument("--pann-sr", type=int, default=32000)
49
+ args = ap.parse_args()
50
+ dev = "cuda"
51
+
52
+ meta = json.load(open(f"{args.data}/evaluation_meta.json"))
53
+ mel_arr = np.load(f"{args.data}/evaluation_mel.npy")
54
+ clip_order, caption_by_clip = first_caption_per_clip(meta)
55
+ rng = np.random.RandomState(args.seed)
56
+ n = min(args.n_eval, len(clip_order))
57
+ chosen = [clip_order[i] for i in rng.permutation(len(clip_order))[:n]]
58
+ prompts = [caption_by_clip[ci] for ci in chosen]
59
+ files = [meta["clip_files"][ci] for ci in chosen]
60
+ print(f"[fad] evaluating on {n} held-out clips from {args.audio_dir}", flush=True)
61
+
62
+ real_dir = os.path.join(args.work, "real")
63
+ gen_dir = os.path.join(args.work, "generated")
64
+ floor_dir = os.path.join(args.work, "griffinlim_floor")
65
+ uncond_dir = os.path.join(args.work, "no_prompt")
66
+ for d in (real_dir, gen_dir, floor_dir, uncond_dir):
67
+ shutil.rmtree(d, ignore_errors=True)
68
+ os.makedirs(d, exist_ok=True)
69
+
70
+ for fn in files:
71
+ shutil.copy(os.path.join(args.audio_dir, fn), os.path.join(real_dir, fn))
72
+
73
+ model, mel, _ = load_model(args.weights, args.config, dev)
74
+ enc = load_clap(args.clap, dev)
75
+ null_seq, null_pool = enc([""])
76
+
77
+ print("[fad] writing griffin-lim floor (real mel -> griffin-lim, isolates vocoder loss)", flush=True)
78
+ for ci, fn in zip(chosen, files):
79
+ img = image_from_tensor(torch.from_numpy(mel_arr[ci].astype(np.float32) / 127.5 - 1.0))
80
+ audio = mel.image_to_audio(img)
81
+ sf.write(os.path.join(floor_dir, fn), audio, mel.get_sample_rate())
82
+
83
+ print("[fad] generating from trained model", flush=True)
84
+ batch = 32
85
+ for i in range(0, n, batch):
86
+ p = prompts[i:i + batch]
87
+ seq, pool = enc(p)
88
+ x = generate(model, seq, pool, null_seq, null_pool, args.steps, args.cfg, dev)
89
+ for j, row in enumerate(x[:, 0]):
90
+ img = image_from_tensor(row)
91
+ audio = mel.image_to_audio(img)
92
+ sf.write(os.path.join(gen_dir, files[i + j]), audio, mel.get_sample_rate())
93
+ print(f"[fad] generated {min(i+batch,n)}/{n}", flush=True)
94
+
95
+ print("[fad] generating unconditional (no prompt) baseline", flush=True)
96
+ for i in range(0, n, batch):
97
+ m = min(batch, n - i)
98
+ ns = null_seq.expand(m, -1, -1)
99
+ npool = null_pool.expand(m, -1)
100
+ x = generate(model, ns, npool, null_seq, null_pool, args.steps, 1.0, dev)
101
+ for j, row in enumerate(x[:, 0]):
102
+ img = image_from_tensor(row)
103
+ audio = mel.image_to_audio(img)
104
+ sf.write(os.path.join(uncond_dir, files[i + j]), audio, mel.get_sample_rate())
105
+
106
+ frechet = FrechetAudioDistance(model_name="pann", sample_rate=args.pann_sr,
107
+ use_pca=False, use_activation=False, verbose=False)
108
+
109
+ results = {}
110
+ for name, d in [("generated_vs_real", gen_dir), ("griffinlim_floor_vs_real", floor_dir),
111
+ ("no_prompt_vs_real", uncond_dir)]:
112
+ score = fad_score(frechet, real_dir, d)
113
+ results[name] = score
114
+ print(f"[fad] FAD {name:<26} = {score:.4f}", flush=True)
115
+
116
+ results["config"] = {"n_eval": n, "cfg": args.cfg, "steps": args.steps,
117
+ "pann_sample_rate": args.pann_sr, "weights": args.weights}
118
+ json.dump(results, open(args.out, "w"), indent=2)
119
+ print(f"[fad] wrote {args.out}", flush=True)
120
+
121
+ if __name__ == "__main__":
122
+ main()