Views
No views yet
merge_and_unload(), merge_adapter(), and any offline "bake the LoRA into the checkpoint"
script will destroy the model irrecoverably. This is not a performance caveat. Read this
before you write a deployment script.audio_lm_heads.0 … audio_lm_heads.11 and text_lm_head — 12 of its 23
target modules. In this architecture those output heads are weight-tied to the input
embeddings: tie_weights() setsaudio_lm_heads[i].weight IS audio_embeddings[i].weight # the same tensor, not a copy
text_lm_head.weight IS transformer.embed_tokens.weightB @ A * (alpha/r) into the head
weight, it writes that delta straight into the embedding table at the same time. The model
then reads its own inputs through a matrix that has been shifted by an output-side correction.
Generation does not fail loudly — it degrades into noise or into a fixed babble, and the damage
is inside the checkpoint you just saved. There is nothing to unmerge afterwards, because the
original values are gone.1m = base.model if hasattr(base, "model") else base
2print(m.audio_lm_heads[0].weight.data_ptr() == m.audio_embeddings[0].weight.data_ptr())
3# True -> same storage, merging corrupts the embeddings1from peft import PeftModel
2
3model = PeftModel.from_pretrained(base, "<this repo>", adapter_name="a").to(dev).eval()
4# do NOT call model.merge_and_unload()
5
6def set_weight(model, name, w):
7 """Scale one named adapter's contribution. alpha/r is its own base scaling."""
8 for module in model.modules():
9 scaling = getattr(module, "scaling", None)
10 if isinstance(scaling, dict) and name in scaling:
11 if not hasattr(module, "_base_scaling"):
12 module._base_scaling = {}
13 module._base_scaling.setdefault(name, scaling[name])
14 scaling[name] = module._base_scaling[name] * float(w)
15
16set_weight(model, "a", 1.0)
17model.base_model.set_adapter(["a"]) # several adapters can be active at onceWx + (B @ A)x * (alpha/r),
which is exactly what the merged weight W + B @ A * (alpha/r) would compute — the same
arithmetic, in a different order. You give up a small amount of inference speed and you keep the
ability to change the weight, stack several adapters, or turn one off. Nothing about the sound
changes.model.base_model.set_adapter([...]). Note that stacking is not free: in our own measurements a
deep stack held audio quality but destroyed intelligibility (word error 0.063 → 0.554). Add
adapters deliberately and measure.weight.data_ptr() and refuse to merge into any group with more than one member.
lora_bank.py in LAION-AI/Humaneness-Voice-Demo-Server does this and asserts on the merge path.<voice>/report.json carries that voice's before/after numbers, the epoch curve that chose the
checkpoint, and the cost columns.Orange/Speaker-wavLM-tbr, published same-speaker threshold
0.472) puts 75.4 % of the takes selected as "failures" above its own threshold.report.json reports its own cost columns.emolia_c1699) measured that simply re-rolling these takes with no voice adapter lifts them
from 0.2 % to 14.0 % above the floor -- they are the unlucky tail of their groups, so regression
to the mean alone moves them. The adapter took the same population to 43.7 %. A per-voice
vs_orig delta in these reports therefore contains that re-roll component and is not the
adapter's effect on its own.