Views
No views yet

facebook/audioseal
audioseal_wm_16bits generator and stays compatible with the unmodified
audioseal_detector_16bits detector.msaidov/audioseal-robust-sgmse-16bits,
the same experiment run against a structurally different diffusion attack
(SGMSE's OU-VE SDE speech enhancement).src/audioseal_robust)generator.pth, repackaged for stock AudioSeal (see Checkpoint format). The raw training checkpoint is kept alongside as generator_train_ckpt.pthtraining_config.yaml, read back out of the checkpoint's own embedded xp.cfgrequires_grad_(False), never taken out of eval()), and used purely as
a differentiable objective. Nothing about the detector, the architecture, the
16-bit payload, or the 16 kHz sample rate changes — so existing AudioSeal
detection code keeps working unchanged.nbits: 16, dimension: 128, n_filters: 32,
ratios: [8, 5, 4, 2], 2 LSTM layers), initialised from the pretrained
audioseal_wm_16bits weights — this is a fine-tune, not a from-scratch train.src/audioldm_train/), driven from the audioldm-s-full
pretrained checkpoint with the audioldm_original.yaml config and run fully
unconditionally.t* → reverse-diffuse back with the
pretrained UNet → decode → re-vocode to a waveform with HiFi-GAN. Because the
vocoder resynthesises phase from scratch, the output is essentially uncorrelated
with the input at the sample level — a far more destructive threat than a codec
or an enhancer.audioldm_mixed recipe:| branch | weight | meaning |
|---|---|---|
identity | 0.5 | unattacked — anchors bit accuracy on the easy case |
audioldm | 0.5 | full differentiable AudioLDM noise-and-regenerate attack |
0.698 against
ln 2 = 0.6931 — chance, i.e. zero recoverable bits — flat across hundreds of
steps, with no gradient signal to learn from. The identity half is what gives the
generator something to anchor on.attack.audioldm.strength_max: 0.02, lowered from the
project default of 0.08. t* is drawn uniformly from [0, 0.02], i.e. up to
~20 of the DDPM's 1000 timesteps. This ceiling exists because backprop through a
deeper reverse loop is not memory-tractable — and it caps the strength this
checkpoint was actually hardened against.attack.weights.sgmse: 0.0) so it
can serve as a generalization probe for a structurally different diffusion
attack.optim.normalize_grad: True — the generator's gradient is
rescaled to exactly max_norm every step rather than only clipped when it
exceeds it. This is load-bearing for a mixed recipe, not a cosmetic tweak.train-clean-100 split (~100 h, ~28.5 k utterances).dev-clean split — never seen in training.x_wm = x + scale · G(x, m)scale is set per example so the watermark lands at a target SNR drawn
uniformly from [24, 36] dB relative to the host signal, rather than using
whatever raw amplitude the generator happens to emit. The attack is then applied,
and the frozen detector scores the result.L = λ_det · (BCE(presence) + λ_bit · BCE(message bits)) + λ_perc · L_melL_mel is a psychoacoustic mel loss: L1 between log-mel spectrograms of x and
x_wm, with each mel bin weighted by a Terhardt absolute-threshold-of-hearing
curve so perturbation energy in less audible bands is penalised less.| hyperparameter | value |
|---|---|
λ_det | 1.0 |
λ_perc | 1.0 — perceptual loss active |
λ_bit | 1.0 |
| optimizer | Adam, lr 5e-5, betas (0.5, 0.9), weight decay 0.0 |
| gradient handling | normalize_grad: True, max_norm = 3.0, floor 1e-6 |
| activation-gradient clamp | max_x_wm_grad_norm = 1000.0 at the generator/attack boundary |
| precision | bf16 autocast on the forward pass; BCE and the VAE/UNet's sensitive ops forced back to fp32 |
| batch size | 4 per rank (config batch size is per-GPU under DDP) |
| mel loss | n_fft 1024, hop 256, win 1024, 80 mels, f_min 20 Hz |
| seed | 1234 |
epochs: 20 × updates_per_epoch: 250. On the full ~100 h split
the dataloader is far from exhausted at 250 batches, so the cap binds and every
epoch is exactly 250 optimizer steps. This checkpoint is saved at the end of
epoch index 18, i.e. after 19 completed epochs ≈ 4 750 optimizer steps.train-audioldm-gradnorm-0814-171739),
with MLflow tracking.generator.pth is a plain torch.save dict, in the same shape AudioSeal's own
generator_base.pth uses:{"model": <generator state_dict>, "xp.cfg": <architecture config, plain dicts>}xp.cfg describes the architecture, not the training run. It is the stock
audioseal_wm_16bits config (nbits, seanet, decoder) as plain dicts and
lists — exactly what AudioSeal.parse_config reads. It references no OmegaConf
or project-specific classes, so torch.load needs nothing but torch, and
nbits is picked up automatically.....conv.conv.weight). AudioSeal picks
its SEANet by interpreter version — AudioCraft's (flat) below Python 3.10,
Moshi's (an extra inner_conv level) at or above it — and its loader only
converts flat → inner_conv. Flat is therefore the only naming that loads on
both sides of that split, which is why upstream publishes it and why this
checkpoint does too."audioseal_robust" key the loader ignores — together with the source
checkpoint name and the exporting commit.
training_config.yaml is that same data as YAML.generator_train_ckpt.pth is the unmodified file the training run wrote
(generator_epoch18.pth), kept for provenance. Its tensors use the
inner_conv-wrapped naming, because it was written by a Python ≥ 3.10
process. It is not usable on its own: its xp.cfg pickles
audioseal_robust.config.TrainConfig and thirteen sibling dataclasses by
reference, so torch.load fails with
ModuleNotFoundError: No module named 'audioseal_robust' unless the training
repo is importable — and even then AudioSeal.load_generator rejects it, because
a TrainConfig has no seanet block. Reach for it only if you are reproducing
the run.The original file was distributed asgenerator_epoch18.pth.zip. That is not a zip wrapper — atorch.savefile already is a zip archive, and the suffix was simply appended.generator_train_ckpt.pthis that file, byte-identical.
audioseal_robust.export_checkpoint,
which reloads its own output through AudioSeal.load_generator and compares it
tensor-by-tensor against the source before writing the file.pip install audioseal huggingface_hub1import torch
2from audioseal import AudioSeal
3from huggingface_hub import hf_hub_download
4
5generator = AudioSeal.load_generator(
6 hf_hub_download("msaidov/audioseal-robust-audioldm-16bits", "generator.pth")
7)
8
9# Watermark exactly as with stock AudioSeal.
10wav, sr = ..., 16000 # (batch, channels, samples), 16 kHz
11msg = torch.randint(0, 2, (wav.shape[0], 16))
12watermarked = wav + generator.get_watermark(wav, sr, message=msg)
13
14# The stock detector is unchanged and still applies.
15detector = AudioSeal.load_detector("audioseal_detector_16bits")
16prob, decoded = detector.detect_watermark(watermarked, sr)nbits= to pass and no state-dict reconciliation to do: both come
out of the checkpoint itself (see Checkpoint format).huggingface_hub, AudioSeal will fetch the URL
itself through torch.hub:1generator = AudioSeal.load_generator(
2 "https://huggingface.co/msaidov/audioseal-robust-audioldm-16bits/resolve/main/generator.pth"
3)embed_watermark in
src/audioseal_robust/train.py.t* robustness curve:1PYTHONPATH=src python -m audioseal_robust.evaluate \
2 generator_checkpoint=generator.pth \
3 eval_dir=/path/to/LibriSpeech/test-clean-fixed \
4 segment_duration=10.24 \
5 recipe=after_audioldm_training \
6 attack.audioldm.checkpoint=/path/to/audioldm-s-full \
7 attack.audioldm.config=src/audioldm_train/config/2023_08_23_reproduce_audioldm/audioldm_original.yamlsegment_duration=10.24, matching AudioLDM's native
window. Otherwise the attack pads every shorter segment, which is a structurally
different input than anything it saw in pretraining, and the resulting numbers
are not comparable. The after_audioldm_training recipe reports identity,
bigvgan, dac and audioldm, holding sgmse and mbd out as unseen-attack
probes.t* at 0.02
(~20 of 1000 diffusion steps). This says nothing about robustness to a
full-strength AudioLDM regeneration, which discards the sample-level signal
almost entirely.
facebook/audioseal
(Meta Platforms), from which these weights are derived. AudioLDM
(haoheliu/AudioLDM-training-finetuning,
MIT) is used only as a training-time attack; its weights are not redistributed
here.1@article{sanroman2024proactive,
2 title = {Proactive Detection of Voice Cloning with Localized Watermarking},
3 author = {San Roman, Robin and Fernandez, Pierre and Elsahar, Hady and
4 D{\'e}fossez, Alexandre and Furon, Teddy and Tran, Tuan},
5 journal = {ICML},
6 year = {2024}
7}
8
9@article{liu2023audioldm,
10 title = {{AudioLDM}: Text-to-Audio Generation with Latent Diffusion Models},
11 author = {Liu, Haohe and Chen, Zehua and Yuan, Yi and Mei, Xinhao and
12 Liu, Xubo and Mandic, Danilo and Wang, Wenwu and Plumbley, Mark D.},
13 journal = {Proceedings of the International Conference on Machine Learning},
14 pages = {21450--21474},
15 year = {2023}
16}