alt.freeweights.speech / moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
From: laion <huggingface.co/laion>Newsgroups: alt.freeweights.speechSubject:

moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora

Date: 2026-11-14Commit: 4cf2338Signed: 2DC28CC7B26A283B ✓

Text to speech model. Safetensors weights, 550 MB in all. Published by laion under CC-BY-4.0.

speech550MBmoss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lorasafetensorspermissive
FactsParamsnot statedSize550 MBFormatssafetensorsTasktext to speechLicenseCC-BY-4.0From sourcepermissive
$ fw get laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
# manifest sha256 4b398e7d8e36… · 550 MB · 5 files
$ fw verify ./moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
From sourceHosted (signed)laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-loraLast check: 2026-11-14 21:34 UTC

Get help: custom fine-tune or private deploy. We review your setup before we quote.

Adapter of moss-tts-local-transformer-4.55b-voice-acting-v2-sft.

Works with

Overview

Size
550 MB
Weights
550 MB (safetensors)
Parameters
not stated
Formats
safetensors
Task
text to speech
License
CC-BY-4.0 (permissive)
Files
5, each with a sha256
Publisher
laion
Added
2026-11-14
Section
alt.freeweights.speech
Lineage

Original release from the publisher.

Base model: laion/moss-tts-local-transformer-4.55b-voice-acting-v2-sft

Where these bytes come from
Publisher at the pinned commit→You

Sources are tried in order. A file counts only when its size and sha256 match the manifest. Sources are checked when you ask for them, not on a timer.

Sources
BadgeHostLast checkResult
From sourcehuggingface.co2026-11-14 21:34 UTCpassed

Model card

From the publisher at commit 4cf2338. Shown as plain text with links; images open at the publisher.

MOSS Voice-Acting 4.55B — DPO LoRA (rank 64)#

⚠️ Do not merge this adapter into the base weights#

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.

Why#

This adapter targets 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() sets

audio_lm_heads[i].weight  IS  audio_embeddings[i].weight     # the same tensor, not a copy
text_lm_head.weight       IS  transformer.embed_tokens.weight

They are one allocation with two names. So when a merge adds B @ 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.

Verify it yourself in three lines#

Do not take our word for it:

m = base.model if hasattr(base, "model") else base
print(m.audio_lm_heads[0].weight.data_ptr() == m.audio_embeddings[0].weight.data_ptr())
# True  -> same storage, merging corrupts the embeddings
What to do instead#

Load with PEFT and leave the adapter unmerged. Set its strength through the scaling factor:

from peft import PeftModel

model = PeftModel.from_pretrained(base, "<this repo>", adapter_name="a").to(dev).eval()
# do NOT call model.merge_and_unload()

def set_weight(model, name, w):
    """Scale one named adapter's contribution.  alpha/r is its own base scaling."""
    for module in model.modules():
        scaling = getattr(module, "scaling", None)
        if isinstance(scaling, dict) and name in scaling:
            if not hasattr(module, "_base_scaling"):
                module._base_scaling = {}
            module._base_scaling.setdefault(name, scaling[name])
            scaling[name] = module._base_scaling[name] * float(w)

set_weight(model, "a", 1.0)
model.base_model.set_adapter(["a"])      # several adapters can be active at once

This sounds identical to a merge. An unmerged LoRA computes Wx + (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.

If you are stacking adapters#

Set each one's scaling separately and activate them together with 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.

If you maintain code that merges#

A regex over module names is not enough — the reliable test is identity of storage. Group the modules by 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.


A rank-64 DPO adapter for laion/moss-tts-local-transformer-4.55b-voice-acting-v2-sft, trained on 7,410,723 preference pairs of synthetic voice-profile and real English/German speech.

By Christoph Schuhmann and LAION.

A full-parameter DPO model trained on the same data and objective is published separately as …-v2-sft-dpo. It is a different run, not a merge of this adapter, and it is the one used as the base for further training.

Do not call merge_and_unload() on this adapter#

The 12 audio_lm_heads are weight-tied to the audio embeddings — tie_weights() assigns head.weight = embedding.weight, the same tensor object — and this adapter targets those heads. Folding W ← W + BA therefore writes the head update into the embeddings as well:

audio_lm_heads.0.weight          max|dW| = 6.103515625e-05    (the merge)
audio_embeddings.0.weight        max|dW| = 6.103515625e-05    (same tensor)
transformer.embed_tokens.weight  max|dW| = 0.0                (not a target, correct)

The merged result loads, generates and sounds like speech, and is a different function from the adapter: on identical input the final hidden state differs with rms 0.107 against a signal rms of 2.518 — 4.3 %. Keep the adapter unmerged.

This adapter composes with that SFT checkpoint and with nothing else. It was trained against those exact weights and against the same prompt library (format hash 3d8a696ccec4a98f). Applying it to the Apache-2.0 base model, or driving it with a different prompt surface, is not a supported configuration and will not reproduce anything measured here.


What it does, and the honest state of it#

In informal listening, SFT + this adapter is the best of the three (base, SFT, SFT+DPO) on most prompts. Two faults remain and are being worked on in a follow-up round: vocal bursts that run longer than they should, and occasional content that was never prompted.

The loss-space picture disagrees with the listening picture, and both are reported. DPO on this preference set over-optimises: preference accuracy climbs while reward(chosen) — the implicit reward on the preferred sequence — goes negative, meaning the policy is making the good audio less likely too, only less so than the bad audio.

stepval losspref accreward(chosen)reward(rejected)
1231.06010.8086−0.1107−7.2040
246 (shipped)0.90540.9336−0.3283−12.4435
3690.78710.9902−0.8465−17.1600
4920.74760.9961−1.5908−21.6908
861 (final)0.73290.9941−2.3850−26.4160

No checkpoint in this run reached reward(chosen) ≥ 0. Step 246 is the least damaged and is the one that was judged best by ear; shipping the final checkpoint — the default in most DPO pipelines — would have shipped the worst one. Preference accuracy is not a health metric here: it reached 0.994 exactly where the model was most degraded.


Training#

value
baselaion/…-voice-acting-v2-sft (the full fine-tune, all 4.13 B parameters trained)
rank / alpha / dropout64 / 128 / 0.05
trainable137.4 M of 4.267 B = 3.22 %
targetsq,k,v,o,gate,up,down across the 36-layer global stack, c_attn,c_proj,fc_in,fc_out in the local transformer, and all 12 audio_lm_heads
objectiveDPO, length-normalised, β = 30, chosen-NLL anchor weight 0.013
peak LR1e-6, cosine, 10 % warmup
epochs / steps1 / 988 (shipped checkpoint at step 246)
global batch2,048 pairs per optimizer step
hardware64 nodes × 4 GH200 = 256 GPUs
Preference data#
sourcepairsfamilies
laion/laion-voice-profiles-dpo3,451,531emotion, truncation, continuation
laion/tts-realspeech-dpo-en-de3,959,192truncation, continuation only

The four length families (truncation / continuation on both corpora) are 6,349,129 pairs = 85.6 % of the raw mix. They were downsampled to length_keep = 0.16, which keeps every one of the 1,064,594 emotion pairs and raises emotion from 14.4 % to ≈51 % of what the model sees.


Four things that went wrong first, and what each one cost#

This is the part that is usually left out of a model card. Every configuration below was run and measured before being abandoned.

1 — The objective was decided by sequence length. With an unnormalised summed log-probability, preference accuracy hit 1.000 at step 102 of 1194, 8.5 % into the run. That is not fast learning; 85.6 % of pairs differ from the chosen sequence mainly in LENGTH, and a sum over a different number of positions separates them by hundreds of nats before the model understands anything. Fix: divide by supervised positions. Check the composition of a preference set before choosing the objective.

2 — The job died on a rank-divergent collective. The heartbeat fired on now - last_hb >= 60, evaluated independently on every rank against its own drifting clock. Eventually one step landed with some ranks over the threshold and some under; the ones over called an all_gather the others never reached, and NCCL aborted after 10 minutes. Slurm reported the array element COMPLETED. Fix: rank 0 decides, broadcast tells everyone. Never gate a collective on per-rank wall-clock.

3 — The policy suppressed the good audio, not just the bad. Length-normalised, β = 30, no anchor: reward(chosen) rose to +0.652 and then went to −0.830 while accuracy sat at 1.000. Chosen and rejected are different recordings, and a 4 B model can always find something to separate two recordings — including by making both less likely. Fix: a chosen-NLL anchor (the RPO / DPO+SFT form), plus an explicit health check on reward(chosen), plus checkpoint selection by that health rather than by recency.

4 — The anchor was 20× too strong, from a units mistake. With nll_weight = 0.25 the step-1 loss came out at 14.29 instead of ≈0.7. sequence_logp normalises by supervised POSITIONS and each position carries 13 channels (12 codebooks + the binary stop head), so -logp ≈ 54 nats per position, not ≈4. Fix: 0.013, which puts the anchor at ≈0.7 — the same scale as the DPO term. Check a normalised quantity's measured magnitude before choosing its weight.


Usage#

import torch
from transformers import AutoProcessor, AutoModel
from peft import PeftModel

SFT  = "laion/moss-tts-local-transformer-4.55b-voice-acting-v2-sft"
LORA = "laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora"

proc = AutoProcessor.from_pretrained(SFT, trust_remote_code=True,
                                     codec_path="OpenMOSS-Team/MOSS-Audio-Tokenizer-v2")
model = AutoModel.from_pretrained(SFT, trust_remote_code=True,
                                  dtype=torch.bfloat16, attn_implementation="sdpa")
model = PeftModel.from_pretrained(model, LORA).cuda().eval()
# optional: model = model.merge_and_unload()   # fold the adapter in, drop the peft wrapper

msg = proc.build_user_message(
    text="[4.2 seconds duration] I really did not see that coming.",
    instruction="GENERAL: A young adult feminine voice; delivery is bright and quick; "
                "affect is positive, animated.\nSCRIPT:\n\"I really did not see that coming.\"",
    tokens=53, language="English")
batch = proc([[msg]], mode="generation")
out = model.generate(input_ids=batch["input_ids"].cuda(),
                     attention_mask=batch["attention_mask"].cuda(),
                     max_new_frames=400, do_sample=True,
                     audio_temperature=1.0, audio_top_p=0.95, audio_top_k=50)
wav = proc.decode(out)[0].audio_codes_list[0]      # 48 kHz

tokens is the target length in codec frames at 12.5 fps — 4.2 s ≈ 53 frames.

Prompt format#

Identical to the SFT model's; see its card for the full <user_inst> block and the training distribution of each field. In short: reference audio or Speaker: <name> 50/50, one of four instruction surfaces, an optional [7.3 seconds duration] prefix on the text (spoken duration, not clip duration), and parenthesised burst cues that are dropped entirely on 10 % of samples.


Limitations#

  • English and German only.
  • reward(chosen) < 0 at every checkpoint. The adapter improves preference discrimination at some cost to the likelihood of the preferred audio itself. This is a real, measured caveat, not a formality.
  • Chosen and rejected are different recordings, not the same utterance rendered two ways, so part of what DPO can learn here is content preference rather than delivery preference.
  • Burst length is not yet controlled. The training prompts named bursts ((sigh)) without stating how long they should be, and the model sometimes holds them far too long. A follow-up round adds explicit per-burst durations.
  • 11.0 % duration-tag coverage on the emotion family. Only 117,525 of 1,064,594 emotion pairs resolve against the SFT corpus, so 89 % of that family carried no duration tag.
  • No MOS study, intelligibility benchmark or speaker-similarity re-measurement has been run.

Intended use#

Research on expressive and controllable speech synthesis. Not validated for, and should not be used for, generating speech attributed to a real person without their consent.


Licence and attribution#

Released under CC-BY-4.0 by Christoph Schuhmann and LAION.

Derived from laion/moss-tts-local-transformer-4.55b-voice-acting-v2 (Apache-2.0), whose notice is preserved. The MOSS-TTS architecture and the MOSS-Audio-Tokenizer-v2 codec originate with the OpenMOSS team.

@misc{schuhmann2026mossva_dpo_lora,
  title  = {MOSS Voice-Acting 4.55B --- DPO LoRA (rank 64)},
  author = {Schuhmann, Christoph and {LAION}},
  year   = {2026},
  url    = {https://huggingface.co/laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora},
  note   = {Rank-64 DPO adapter over the SFT checkpoint; 7.41M preference pairs; CC-BY-4.0}
}

Files

5 files · 550 MBWeights 550 MB (safetensors). Sizes and sha256 from the signed manifest.
Hide all 5 files with their sha256
FileSizesha256
.gitattributes1.52 KB11ad7efa24975ee4b0c3c3a38ed18737f0658a5f75a0a96787b576a78a023361
README.md14.5 KB139dc88aa312eed6edb709fe437ac89f247a35c9dfcd4d110fd22fbcc42ce990
adapter_config.json1.57 KBba9d7b73817184a4466d75d1146d47b53554ae51dce05f6cbd420712b7a07d5d
adapter_model.safetensors550 MBb4ca6eafb991c95244a2713bdae8c8533f734b52097924caa731663f789aaaa2
selection.json336 Bb863da18123a2ca0d381eec0636fd143713322a038c3a4bbb92f1704f3ae296f
VersionsEach time the pinned upstream commit or file list changed in this repo. Newest first.
Upstream commitDateContents
4cf233819c pinned now manifest updated, same commit2026-11-14 21:34 UTC5 files · 550 MB
4cf233819c2026-11-14 21:34 UTC5 files · 550 MB

Use it

$ fw get laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
# manifest sha256 4b398e7d8e36… · 550 MB · 5 files
$ fw verify ./moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
fw.pyChecks the signature and every sha256, tries each source in order, resumes.
$ python3 fw.py get https://freeweights.allternit.com/manifests/laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json --dir ./moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora $ python3 fw.py verify ./moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora ./laion__moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json
hfThe Hugging Face CLI with one setting. Same repo id.
$ HF_ENDPOINT=https://freeweights.allternit.com hf download laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora --revision 4cf233819c18b4a0e5f57430feb6a011bfb89861 --local-dir ./moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora

License

Name
Creative Commons Attribution 4.0
Id
CC-BY-4.0
Class
Permissive
Redistribution
yes
Listing
Hosted (signed)
Decided
License as declared by the publisher. No person read it. 2026-11-14
Text in
README.md
Text sha256
139dc88aa312eed6edb709fe437ac89f247a35c9dfcd4d110fd22fbcc42ce990
Terms gate
No extra terms beyond the license.

Read the license text

Licenses are checked against the license table. A custom license passes only after a person reads it.

Provenance

From: laion <huggingface.co/laion>Newsgroups: alt.freeweights.speechSubject: moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-loraDate: 2026-11-14Commit: 4cf2338Signed: 2DC28CC7B26A283B ✓
Upstream
laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
Pinned commit
4cf233819c18b4a0e5f57430feb6a011bfb89861
Manifest
moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json · .minisig
Manifest sha256
4b398e7d8e365a53de5c8de09cca4419b39cfbea151e14a48c21870b6079fe3f
Signature
minisign, Ed25519, key 2DC28CC7B26A283B
Created
2026-11-14 21:34 UTC
Generator
freeweights-manifest/0.1.0
Takedowns
None.

How to verify

$ curl -O https://freeweights.allternit.com/manifests/laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json $ curl -O https://freeweights.allternit.com/manifests/laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json.minisig $ minisign -Vm moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json -P RWQ7KGqyx4zCLXtUFOqvfv8L3g+8fpKJ1FsuxPyOYMF+4KufbUxKq1Oq $ shasum -a 256 <file> # compare with the Files tab

A manifest whose signature fails must be rejected. A file whose size or sha256 does not match must be discarded. Verify a download

Model tree

Parent

laion/moss-tts-local-transformer-4.55b-voice-acting-v2-sft · this model is a adapter of it

No model in the catalog names this one as its parent.

Get it
Download
$ HF_ENDPOINT=https://freeweights.allternit.com hf download laion/moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora
Open in Freeweights Studio

Pull, verify and run it in the Allternit app on Desktop, web or phone.

Signed manifest
minisign, key 2DC28CC7B26A283B. moss-tts-local-transformer-4.55b-voice-acting-v2-dpo-lora.json. Provenance. Takedown history: none.
From: MIT <huggingface.co/MIT>Subject: ast-finetuned-audioset-10-10-0.4593
From: onnx-community <huggingface.co/onnx-community>Subject: ast-finetuned-audioset-10-10-0.4593-ONNX
From: Edge0 <huggingface.co/Edge0>Subject: Audio8-TTS-Preview-0.6b
From: Edge0 <huggingface.co/Edge0>Subject: Audio8-TTS-Realistic-Air
From: facebook <huggingface.co/facebook>Subject: audiobox-aesthetics
From: mlx-community <huggingface.co/mlx-community>Subject: chatterbox-4bit