Upload eval_fad.py with huggingface_hub
Browse files- 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()
|