Text-to-Speech
English
German
voice-acting
qwen3
moss-audio-tokenizer-v2
audio-generation
ChristophSchuhmann commited on
Commit
3365015
·
verified ·
1 Parent(s): f1fa0b4

Set merged 12-Oct-2026 Humaneness Voice Small as default

Browse files
README.md CHANGED
@@ -19,15 +19,23 @@ model-index:
19
 
20
  # Humaneness Voice Small
21
 
22
- Humaneness Voice Small is an experimental English/German text-to-speech and voice-acting model. This repository contains **ten stage-local checkpoints from one continuous S1→S10 training run** plus a later S3-based full-parameter, long-reference continuation. It also contains training and inference source, run statistics, and a guide to the exact prompt surfaces. It is not a standard `AutoModel.from_pretrained()` checkpoint: it uses a Qwen3-0.6B semantic backbone, a MOSS-style local Talker, and the separately loaded [MOSS Audio Tokenizer v2](https://huggingface.co/OpenMOSS-Team/MOSS-Audio-Tokenizer-v2). Read the inference instructions before loading weights.
23
 
24
- ## Current default: S3 Ref15 Full FT (9 October 2026)
25
 
26
- The new **reference-conditioned default checkpoint** is [`checkpoints/S3-Ref15-FullFT/model_bf16.pt`](checkpoints/S3-Ref15-FullFT/model_bf16.pt), the final **step 26,774** of a full-parameter continuation from the **original S3 step 32,337**. The user chose it because it sounded acoustically preferable in listening. The updated `code/infer.py` loads it when `--stage` is omitted or set to `default`. The ten original stage checkpoints are unchanged; `--stage S3` explicitly loads the original S3 and is still the comparison to try for **reference-free** generation. For the new default, supply a clean, distinct 5–15-second reference WAV; every accepted retraining example had a reference.
 
 
 
 
 
 
 
 
27
 
28
  The key fix is the historical **2.96-second reference crop**: the original training loader and earlier example inference code used at most 37 × 80-ms codec frames even though the stored reference sequences were longer. This continuation used up to **187 frames / 14.96 seconds**, with a 55/20/20/5 family of script, historical structured, caption-transcript, and transcript-only prompts. It fully updated the active semantic backbone and Talker after a conservative target-text repair gate; it is **not** the Rank-128 LoRA and did not start from that adapter. There were **3,255,217 accepted training targets**, each presented once with its distinct reference. Read the detailed [data selection, repair, training and evaluation report](docs/S3_REF15_FULL_FT.md) and the [checkpoint metadata](checkpoints/S3-Ref15-FullFT/metadata.json) before reusing it.
29
 
30
- This is a **listening-selected acoustic default**, not an objectively established all-round winner. In a matched 50-case non-speaker-disjoint test, final-step WER was 0.096 versus 0.088 for original S3; Orange cosine to the supplied reference was 0.707 versus 0.745, while cosine to the intended target improved from 0.658 to 0.734. Step 25,000 had better measured WER and target cosine than the final step. These learned/ASR proxies and the user's listening impression answer different questions; further blinded and speaker-disjoint evaluation is needed. No optimizer state is uploaded for this continuation.
31
 
32
  The semantic backbone was **initialized from pretrained Qwen3-0.6B**, not from random weights. The **112,764,928-parameter Talker and bridge were initialized fresh** for this run. This corrects the loose description “trained from scratch” sometimes applied to earlier experiments. The Qwen hidden width is 1,024 and the local Talker width is 2,560. One semantic frame spans 80 ms; the Talker predicts **12 RVQ codebook indices per frame**, or 150 codebook indices per second. It does not predict 32 codebooks per frame. The codec reconstructs 48-kHz audio. The full state is approximately 715 million parameters, including the frozen, unused score conditioner; the model-only exports are BF16 PyTorch state dictionaries.
33
 
@@ -48,7 +56,7 @@ The `checkpoints/S*/model_bf16.pt` files are the **lowest independent held-out v
48
  | S9 | tier1 | 1,439 | 41,878 | 4.7198 | 0.4540 |
49
  | S10 | tier0 | 578 | 41,936 | 4.7278 | 0.4572 |
50
 
51
- ¹ These are **training presentations**, not unique source-audio hours. Multi-format prompts repeat some recordings. The ladder tiers are nested and paired with sidecar material; do not add these hours to estimate unique audio. ² Target-aware automatic reward on 1,651 shared valid acting cells per stage, not a human MOS. The S3–S10 paired difference is +0.00557 with a 95% challenge-bootstrap CI of −0.00701 to +0.01899: **the benchmark does not establish S3 as significantly better than S10**. Among the **original ladder stages**, S3 was a reasonable provisional choice; inspect the same prompts in S3/S10 before choosing for a production voice. The later Ref15 Full FT is now the reference-conditioned default by listening choice, under the caveats above. S4 has the best held-out validation loss. All underlying values and intervals are in `stats/acting_findings.json` and `stats/acting_analysis.json.gz`.
52
 
53
  The run used 32 JUPITER Booster nodes × 4 GPUs/node, batch 4,096, **one** 2,097-step linear warmup followed by one cosine decay across all 41,936 updates—**no stage LR restarts**. Peak LRs were 8e-5 for the semantic backbone and 2.4e-4 for the Talker, ending at 5% of peak. The source `code/plan_caption_production_32nodes.json` records the exact historical manifests and hashes; its absolute JUPITER paths are provenance, not portable defaults.
54
 
@@ -139,7 +147,7 @@ Throughput was measured on an **RTX 3090**, using S3 at revision `5de86032771c37
139
  - `code/continuous_train.py`, `code/train.sbatch`, `code/plan_caption_production_32nodes.json` and adjacent modules: the historical JUPITER source and training contract. The raw plan refers to local manifests and intentionally fails its hash checks if edited; regenerate plans and manifests for new data. `code/infer.py` is the standalone example for the exported weights. The code bundles MOSS export modules, tokenizer configuration and score schema; the audio codec is fetched separately from its upstream Hugging Face repository.
140
  - `stats/training_steps.jsonl`: every logged training-step scalar line across all four Slurm invocations. `stats/invocation_summaries.json`, `stats/run_configuration.json`, `stats/stage_winners.json`, `stats/validation_loss_full.tar.gz`, `stats/acting_findings.json`, and `stats/acting_analysis.json.gz` hold the remaining scalar summaries, all held-out checkpoint-sweep records, and the public acting aggregate. `stats/CONTENTS.json` gives hashes.
141
 
142
- Example using the **new reference-conditioned default** (requires a CUDA environment with PyTorch, Transformers, NumPy, SoundFile, Torchaudio, Safetensors, and permission to download the separate codec):
143
 
144
  ```bash
145
  python code/infer.py --stage default --reference-wav reference.wav --language en --frames 75 \
@@ -147,7 +155,7 @@ python code/infer.py --stage default --reference-wav reference.wav --language en
147
  --text 'I cannot believe we made it.' --output example.wav
148
  ```
149
 
150
- Do not use the target recording as its own reference. For reference-free inference, compare `--stage S3`; the new default was trained only with references. **Correction (8 October 2026):** an earlier public `code/infer.py` rejected any reference over 37 MOSS codec frames (2.96 s). This was an inference-code limit as well as the historical S1–S10 training-loader crop, not an architectural limit. The updated example accepts a configurable reference ceiling (15 s default, 20 s maximum), reports a warning below 5 s and no longer silently blocks longer audio. The new Full FT was trained with up to 14.96 s; using longer than 15 s at inference is out of distribution and warned about. The original S1–S10 weights were trained with at most 2.96 s references, so longer-reference behavior from an original stage is experimental, not a guaranteed improvement. The separate [S3 Ref15 Rank-128 LoRA](https://huggingface.co/laion/Humaneness-Voice-Small-Ref15-LoRA-r128) is a different, frozen-backbone comparison. The example is a source-level starting point; the public listening Space is the already-audited playback path. This repository does **not** contain the large original SFT-3 model weights or original Qwen checkpoint separately, because the exported Humaneness Voice Small states include every trained model weight required for inference.
151
 
152
  ## Training data, license and responsible use
153
 
@@ -167,7 +175,7 @@ Both collections use the S3 checkpoint at step 32,337; consult each adapter card
167
  <!-- hvs-small-dpo-default-study-2026-10-04 -->
168
  ## Default DPO-adapter recommendation (matched speaker/emotion study)
169
 
170
- For **the historical S3 DPO comparison, use the unadapted S3 checkpoint rather than activating a DPO LoRA by default**. This is an adapter-level recommendation, **not** a reversal of the newer Ref15 Full FT checkpoint default above; the DPO LoRAs were trained against S3 and should not be assumed compatible or beneficial on Ref15 Full FT. The historical S3 prompt/CFG recommendations above also refer to S3, not a completed Ref15 tuning sweep. The [matched generated-audio study
171
  with references and listening examples](https://laion-humaneness-voice-small-acting-eval-space.static.hf.space/dpo_lora_speaker_emotion_2026-10-02.html) compared the same 12
172
  English/German acting challenges, prompts and seeds for each condition (432
173
  outputs per condition, including 198 reference-conditioned outputs). Its
@@ -197,7 +205,7 @@ exact prompts, paired audio players and methodological limits.
197
 
198
  **None for general use on the historical S3 base:** unadapted S3 achieved
199
  lower WER than any DPO LoRA in that matched study. The repository-wide
200
- reference-conditioned checkpoint default is now Ref15 Full FT, which was
201
  evaluated under a different 50-case protocol; this DPO result does not rank it.
202
  If an application explicitly prioritizes similarity to a provided speaker
203
  reference, the **best-tested optional DPO LoRA at this date** is
 
19
 
20
  # Humaneness Voice Small
21
 
22
+ ## Current default: Humaneness Voice Small (12-10-26)
23
 
24
+ **[Humaneness Voice Small (12-10-26)](checkpoints/HVS-Small-2026-10-12/model_bf16.pt)** is the current, complete BF16 checkpoint. It is the Real+Gemini full-parameter model after two more Gemini acting epochs with a Rank-64 semantic attention LoRA, **merged into the base weights**. You do not need to load an adapter. The Talker weights come from Real+Gemini and were not changed by this final LoRA. `python code/infer.py --stage default` loads this checkpoint; `--stage S3-Ref15-FullFT` still selects the preceding default. All original S1–S10 and Ref15 checkpoints remain available.
25
 
26
+ The user selected this default by listening, particularly for natural/organic acting. Audition the [matched five-checkpoint acting grid](https://laion-humaneness-voice-small-four-checkpoint-acting.static.hf.space/) with seven emotions, five voices, 12-second references and no-reference cases. The grid audio was generated with the unmerged LoRA; the merged export is checked for structural compatibility and should be numerically close, but no blinded perceptual or universal metric win is claimed. For lineage, exact training recipe, conditioning and caveats, see the [12 October release note](docs/HVS_SMALL_2026_10_12.md).
27
+
28
+ For speaker identity, start with a **distinct, clean 5–15-second reference**. The acting continuation also saw pseudonym-conditioned examples; use its companion acting inference path for pseudonym-only prompts. The older `code/infer.py` remains the general single-clip example and now resolves `default` to the merged weight file. The historical prompt and benchmark sections below describe earlier S1–S10 experiments and should not be mistaken for a fresh evaluation of this new default.
29
+
30
+ Humaneness Voice Small is an experimental English/German text-to-speech and voice-acting model. This repository contains **ten stage-local checkpoints from one continuous S1→S10 training run** plus an S3-based long-reference continuation, Gemini/real-speech acting continuations, and the merged 12 October default. It also contains training and inference source, run statistics, and a guide to the exact prompt surfaces. It is not a standard `AutoModel.from_pretrained()` checkpoint: it uses a Qwen3-0.6B semantic backbone, a MOSS-style local Talker, and the separately loaded [MOSS Audio Tokenizer v2](https://huggingface.co/OpenMOSS-Team/MOSS-Audio-Tokenizer-v2). Read the inference instructions before loading weights.
31
+
32
+ ## Previous default: S3 Ref15 Full FT (9 October 2026)
33
+
34
+ The previous **reference-conditioned default checkpoint** is [`checkpoints/S3-Ref15-FullFT/model_bf16.pt`](checkpoints/S3-Ref15-FullFT/model_bf16.pt), the final **step 26,774** of a full-parameter continuation from the **original S3 step 32,337**. The user chose it because it sounded acoustically preferable in listening. Use `code/infer.py --stage S3-Ref15-FullFT` to load it explicitly. The ten original stage checkpoints are unchanged; `--stage S3` explicitly loads the original S3 and is still the comparison to try for **reference-free** generation. For this Ref15 checkpoint, supply a clean, distinct 5–15-second reference WAV; every accepted retraining example had a reference.
35
 
36
  The key fix is the historical **2.96-second reference crop**: the original training loader and earlier example inference code used at most 37 × 80-ms codec frames even though the stored reference sequences were longer. This continuation used up to **187 frames / 14.96 seconds**, with a 55/20/20/5 family of script, historical structured, caption-transcript, and transcript-only prompts. It fully updated the active semantic backbone and Talker after a conservative target-text repair gate; it is **not** the Rank-128 LoRA and did not start from that adapter. There were **3,255,217 accepted training targets**, each presented once with its distinct reference. Read the detailed [data selection, repair, training and evaluation report](docs/S3_REF15_FULL_FT.md) and the [checkpoint metadata](checkpoints/S3-Ref15-FullFT/metadata.json) before reusing it.
37
 
38
+ This was a **listening-selected acoustic default**, not an objectively established all-round winner. In a matched 50-case non-speaker-disjoint test, final-step WER was 0.096 versus 0.088 for original S3; Orange cosine to the supplied reference was 0.707 versus 0.745, while cosine to the intended target improved from 0.658 to 0.734. Step 25,000 had better measured WER and target cosine than the final step. These learned/ASR proxies and the user's listening impression answer different questions; further blinded and speaker-disjoint evaluation is needed. No optimizer state is uploaded for this continuation.
39
 
40
  The semantic backbone was **initialized from pretrained Qwen3-0.6B**, not from random weights. The **112,764,928-parameter Talker and bridge were initialized fresh** for this run. This corrects the loose description “trained from scratch” sometimes applied to earlier experiments. The Qwen hidden width is 1,024 and the local Talker width is 2,560. One semantic frame spans 80 ms; the Talker predicts **12 RVQ codebook indices per frame**, or 150 codebook indices per second. It does not predict 32 codebooks per frame. The codec reconstructs 48-kHz audio. The full state is approximately 715 million parameters, including the frozen, unused score conditioner; the model-only exports are BF16 PyTorch state dictionaries.
41
 
 
56
  | S9 | tier1 | 1,439 | 41,878 | 4.7198 | 0.4540 |
57
  | S10 | tier0 | 578 | 41,936 | 4.7278 | 0.4572 |
58
 
59
+ ¹ These are **training presentations**, not unique source-audio hours. Multi-format prompts repeat some recordings. The ladder tiers are nested and paired with sidecar material; do not add these hours to estimate unique audio. ² Target-aware automatic reward on 1,651 shared valid acting cells per stage, not a human MOS. The S3–S10 paired difference is +0.00557 with a 95% challenge-bootstrap CI of −0.00701 to +0.01899: **the benchmark does not establish S3 as significantly better than S10**. Among the **original ladder stages**, S3 was a reasonable provisional choice; inspect the same prompts in S3/S10 before choosing for a production voice. The later Ref15 Full FT was the previous reference-conditioned default by listening choice, under the caveats above. S4 has the best held-out validation loss. All underlying values and intervals are in `stats/acting_findings.json` and `stats/acting_analysis.json.gz`.
60
 
61
  The run used 32 JUPITER Booster nodes × 4 GPUs/node, batch 4,096, **one** 2,097-step linear warmup followed by one cosine decay across all 41,936 updates—**no stage LR restarts**. Peak LRs were 8e-5 for the semantic backbone and 2.4e-4 for the Talker, ending at 5% of peak. The source `code/plan_caption_production_32nodes.json` records the exact historical manifests and hashes; its absolute JUPITER paths are provenance, not portable defaults.
62
 
 
147
  - `code/continuous_train.py`, `code/train.sbatch`, `code/plan_caption_production_32nodes.json` and adjacent modules: the historical JUPITER source and training contract. The raw plan refers to local manifests and intentionally fails its hash checks if edited; regenerate plans and manifests for new data. `code/infer.py` is the standalone example for the exported weights. The code bundles MOSS export modules, tokenizer configuration and score schema; the audio codec is fetched separately from its upstream Hugging Face repository.
148
  - `stats/training_steps.jsonl`: every logged training-step scalar line across all four Slurm invocations. `stats/invocation_summaries.json`, `stats/run_configuration.json`, `stats/stage_winners.json`, `stats/validation_loss_full.tar.gz`, `stats/acting_findings.json`, and `stats/acting_analysis.json.gz` hold the remaining scalar summaries, all held-out checkpoint-sweep records, and the public acting aggregate. `stats/CONTENTS.json` gives hashes.
149
 
150
+ Example using the **current merged default with a reference** (requires a CUDA environment with PyTorch, Transformers, NumPy, SoundFile, Torchaudio, Safetensors, and permission to download the separate codec):
151
 
152
  ```bash
153
  python code/infer.py --stage default --reference-wav reference.wav --language en --frames 75 \
 
155
  --text 'I cannot believe we made it.' --output example.wav
156
  ```
157
 
158
+ Do not use the target recording as its own reference. For reference-free inference, compare `--stage S3` as a historical control; the current merged default also saw pseudonym and no-reference acting conditions, while the preceding Ref15-only stage was trained with references. **Correction (8 October 2026):** an earlier public `code/infer.py` rejected any reference over 37 MOSS codec frames (2.96 s). This was an inference-code limit as well as the historical S1–S10 training-loader crop, not an architectural limit. The updated example accepts a configurable reference ceiling (15 s default, 20 s maximum), reports a warning below 5 s and no longer silently blocks longer audio. The new Full FT was trained with up to 14.96 s; using longer than 15 s at inference is out of distribution and warned about. The original S1–S10 weights were trained with at most 2.96 s references, so longer-reference behavior from an original stage is experimental, not a guaranteed improvement. The separate [S3 Ref15 Rank-128 LoRA](https://huggingface.co/laion/Humaneness-Voice-Small-Ref15-LoRA-r128) is a different, frozen-backbone comparison. The example is a source-level starting point; the public listening Space is the already-audited playback path. This repository does **not** contain the large original SFT-3 model weights or original Qwen checkpoint separately, because the exported Humaneness Voice Small states include every trained model weight required for inference.
159
 
160
  ## Training data, license and responsible use
161
 
 
175
  <!-- hvs-small-dpo-default-study-2026-10-04 -->
176
  ## Default DPO-adapter recommendation (matched speaker/emotion study)
177
 
178
+ For **the historical S3 DPO comparison, use the unadapted S3 checkpoint rather than activating a DPO LoRA by default**. This is an adapter-level recommendation, **not** a reversal of the newer 12 October merged checkpoint default above; the DPO LoRAs were trained against S3 and should not be assumed compatible or beneficial on Ref15 Full FT. The historical S3 prompt/CFG recommendations above also refer to S3, not a completed Ref15 tuning sweep. The [matched generated-audio study
179
  with references and listening examples](https://laion-humaneness-voice-small-acting-eval-space.static.hf.space/dpo_lora_speaker_emotion_2026-10-02.html) compared the same 12
180
  English/German acting challenges, prompts and seeds for each condition (432
181
  outputs per condition, including 198 reference-conditioned outputs). Its
 
205
 
206
  **None for general use on the historical S3 base:** unadapted S3 achieved
207
  lower WER than any DPO LoRA in that matched study. The repository-wide
208
+ reference-conditioned checkpoint default was Ref15 Full FT before the 12 October release, which was
209
  evaluated under a different 50-case protocol; this DPO result does not rank it.
210
  If an application explicitly prioritizes similarity to a provided speaker
211
  reference, the **best-tested optional DPO LoRA at this date** is
checkpoints/HVS-Small-2026-10-12/metadata.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "title": "Humaneness Voice Small (12-10-26)",
3
+ "checkpoint": "HVS-Small-2026-10-12",
4
+ "format": "complete adapter-free BF16 PyTorch state_dict",
5
+ "base": "/e/scratch/laionize/schuhmann1/acting_challenges_finetune/small/real_gemini_training/checkpoints/step-00015238/model.safetensors",
6
+ "base_sha256": "b1ad2bcbe94a5760c37b4e57a752a894db0033adc28fc62cb885d532cf4e46f2",
7
+ "adapter": "/e/scratch/laionize/schuhmann1/acting_challenges_finetune/small/gemini_lora_r64_2ep_v1/training/adapter.safetensors",
8
+ "adapter_sha256": "3c9e01bc448b0732785240a64dd47f8ea7a7b4d09106b0d24a208e35677a85fe",
9
+ "model_file": "model_bf16.pt",
10
+ "model_bytes": 1431106131,
11
+ "model_sha256": "45f1de4bcc62bef038afc812ac8681b3e6507d9d97e4c18d27518f5a5a346bd4",
12
+ "rank": 64,
13
+ "alpha": 64,
14
+ "merged_modules": 112,
15
+ "target_modules": "Qwen3 semantic attention q_proj, k_proj, v_proj, o_proj",
16
+ "source_step": 15238,
17
+ "adapter_step": 2298,
18
+ "optimizer_state_included": false,
19
+ "merge_validation": "112 nonzero finite deltas; saved state strict-loads without adapter; prompt-only generation canary passed",
20
+ "generation_canary_frames": 24,
21
+ "delta_max_abs": 0.039547283202409744,
22
+ "lineage": "S3 Ref15 Full FT -> Gemini acting full FT -> Real+Gemini full FT -> two-epoch Gemini Rank-64 LoRA -> merged export"
23
+ }
checkpoints/default.json CHANGED
@@ -1,10 +1,13 @@
1
  {
2
- "checkpoint": "S3-Ref15-FullFT",
3
- "model_file": "checkpoints/S3-Ref15-FullFT/model_bf16.pt",
4
- "metadata_file": "checkpoints/S3-Ref15-FullFT/metadata.json",
5
- "chosen_on": "2026-10-09",
6
- "selection_basis": "user listening preference for acoustic quality",
7
- "reference_conditioned": true,
8
- "trained_reference_max_seconds": 14.96,
 
 
 
9
  "reference_free_fallback": "S3"
10
  }
 
1
  {
2
+ "checkpoint": "HVS-Small-2026-10-12",
3
+ "display_name": "Humaneness Voice Small (12-10-26)",
4
+ "model_file": "checkpoints/HVS-Small-2026-10-12/model_bf16.pt",
5
+ "metadata_file": "checkpoints/HVS-Small-2026-10-12/metadata.json",
6
+ "chosen_on": "2026-10-12",
7
+ "selection_basis": "user listening preference on matched acting grid",
8
+ "merged_adapter": true,
9
+ "reference_or_alias_conditioning": true,
10
+ "reference_max_seconds_recommended": 15,
11
+ "previous_default": "S3-Ref15-FullFT",
12
  "reference_free_fallback": "S3"
13
  }
code/acting_2026_10_12/lora_layers.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Rank-configurable Qwen3 semantic q/k/v/o LoRA for Humaneness Voice Small.
2
+
3
+ Training used rank=alpha=64 and adapted 28 × 4 linear projections. The model
4
+ loader and data mappings remain described in the release note; this is only
5
+ the adapter layer definition, not a standalone training pipeline.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import math
10
+ import torch
11
+ from torch import nn
12
+
13
+
14
+ class LoRALinear(nn.Module):
15
+ def __init__(self, base: nn.Linear, rank: int = 64, alpha: int = 64):
16
+ super().__init__()
17
+ self.base = base
18
+ for parameter in self.base.parameters():
19
+ parameter.requires_grad_(False)
20
+ self.a = nn.Linear(base.in_features, rank, bias=False, dtype=torch.float32,
21
+ device=base.weight.device)
22
+ self.b = nn.Linear(rank, base.out_features, bias=False, dtype=torch.float32,
23
+ device=base.weight.device)
24
+ nn.init.kaiming_uniform_(self.a.weight, a=math.sqrt(5))
25
+ nn.init.zeros_(self.b.weight)
26
+ self.scale = alpha / rank
27
+ self.enabled = True
28
+
29
+ def forward(self, x):
30
+ result = self.base(x)
31
+ if self.enabled:
32
+ result = result + self.scale * self.b(self.a(x.float())).to(result.dtype)
33
+ return result
34
+
35
+
36
+ def attach(model, rank: int = 64, alpha: int = 64):
37
+ suffixes = ('.self_attn.q_proj', '.self_attn.k_proj',
38
+ '.self_attn.v_proj', '.self_attn.o_proj')
39
+ wrapped = []
40
+ for name, module in list(model.named_modules()):
41
+ if not name.startswith('transformer.layers.') or not name.endswith(suffixes):
42
+ continue
43
+ assert isinstance(module, nn.Linear), name
44
+ parent, leaf = name.rsplit('.', 1)
45
+ adapted = LoRALinear(module, rank=rank, alpha=alpha)
46
+ setattr(model.get_submodule(parent), leaf, adapted)
47
+ wrapped.append((name, adapted))
48
+ assert len(wrapped) == 112, len(wrapped)
49
+ return wrapped
code/acting_2026_10_12/merge_lora.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Merge the finished Gemini Rank-64 semantic LoRA into Real+Gemini Small.
3
+
4
+ Run on a JUPITER Booster node. The export is a complete BF16 PyTorch state
5
+ dict in the same format as the original Humaneness Voice Small checkpoints.
6
+ No optimizer state or source checkpoint is changed.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+ import sys
15
+
16
+ import torch
17
+ from safetensors.torch import load_file
18
+
19
+ HOME = Path('/e/home/jusers/schuhmann1/jupiter')
20
+ DATA = Path('/e/scratch/laionize/schuhmann1/acting_challenges_finetune')
21
+ SOURCE = DATA / 'small/real_gemini_training/checkpoints/step-00015238/model.safetensors'
22
+ ADAPTER = DATA / 'small/gemini_lora_r64_2ep_v1/training/adapter.safetensors'
23
+ OUT = DATA / 'small/hf_release_2026_10_12/checkpoints/HVS-Small-2026-10-12'
24
+ sys.path[:0] = [str(DATA), str(HOME / 'hvs_ref_only_r128_v1/public_release')]
25
+ import train_fullft as ft
26
+ from lora_layers import attach
27
+
28
+
29
+ def sha(path: Path) -> str:
30
+ digest = hashlib.sha256()
31
+ with path.open('rb') as stream:
32
+ for block in iter(lambda: stream.read(8 << 20), b''):
33
+ digest.update(block)
34
+ return digest.hexdigest()
35
+
36
+
37
+ def main() -> None:
38
+ torch.set_num_threads(8)
39
+ assert torch.cuda.is_available(), 'Run merge on a GPU compute node'
40
+ assert SOURCE.is_file() and ADAPTER.is_file()
41
+ done = json.loads((ADAPTER.parent / 'DONE.json').read_text())
42
+ assert done['status'] == 'COMPLETE', done
43
+ OUT.mkdir(parents=True, exist_ok=True)
44
+ target = OUT / 'model_bf16.pt'
45
+ metadata = OUT / 'metadata.json'
46
+ if target.exists() or metadata.exists():
47
+ raise FileExistsError('Release export already exists; inspect it, do not overwrite it')
48
+
49
+ model, config = ft.load_model('small')
50
+ source = load_file(str(SOURCE), device='cpu')
51
+ assert source and all(name.startswith('base.') for name in source)
52
+ missing, unexpected = model.load_state_dict(
53
+ {name[5:]: value for name, value in source.items()}, strict=False)
54
+ tied = {'text_lm_head.weight'} | {f'audio_lm_heads.{i}.weight' for i in range(12)}
55
+ assert not unexpected and set(missing) <= tied, (missing, unexpected)
56
+ model.tie_weights()
57
+ del source
58
+ wrapped = attach(model, rank=64, alpha=64)
59
+ assert len(wrapped) == 112
60
+ adapter = load_file(str(ADAPTER), device='cpu')
61
+ expected = {name + suffix for name, _ in wrapped for suffix in ('.a.weight', '.b.weight')}
62
+ assert set(adapter) == expected, (len(adapter), len(expected))
63
+
64
+ changes = []
65
+ with torch.no_grad():
66
+ for name, module in wrapped:
67
+ a = adapter[name + '.a.weight'].to('cuda', dtype=torch.float32)
68
+ b = adapter[name + '.b.weight'].to('cuda', dtype=torch.float32)
69
+ old = module.base.weight.to('cuda', dtype=torch.float32)
70
+ delta = (b @ a) * module.scale
71
+ assert torch.isfinite(delta).all() and torch.count_nonzero(delta) > 0, name
72
+ merged = (old + delta).to('cpu', dtype=torch.bfloat16)
73
+ module.base.weight.copy_(merged.to(module.base.weight.dtype))
74
+ parent, leaf = name.rsplit('.', 1)
75
+ setattr(model.get_submodule(parent), leaf, module.base)
76
+ changes.append({'name': name, 'max_abs_delta': float(delta.abs().max().item())})
77
+ if len(changes) % 28 == 0:
78
+ print(json.dumps({'event': 'merged', 'modules': len(changes)}), flush=True)
79
+ assert len(changes) == 112
80
+ assert not any('.a.weight' in key or '.b.weight' in key for key in model.state_dict())
81
+ model = model.to('cpu', dtype=torch.bfloat16).eval()
82
+ state = model.state_dict()
83
+ assert state and all(value.dtype == torch.bfloat16 for value in state.values()
84
+ if value.is_floating_point())
85
+ temp = target.with_suffix('.pt.partial')
86
+ torch.save(state, temp)
87
+ del state
88
+ # The saved artifact must load strictly as an ordinary, adapter-free model.
89
+ check = torch.load(temp, map_location='cpu', weights_only=True)
90
+ model.load_state_dict(check, strict=True)
91
+ del check
92
+ # Exact public-grid prompt path, without loading or writing loose audio.
93
+ from packing import generated_audio
94
+ processor, _ = ft.load_processor('small')
95
+ user = processor.build_user_message(
96
+ text='Hello there.',
97
+ instruction='GENERAL: warm, natural speech.\nSCRIPT:\n(warm, conversational) Hello there.',
98
+ tokens=24, reference=None, language='English')
99
+ ids = processor([[user]], mode='generation')['input_ids'][0].long().unsqueeze(0).to('cuda')
100
+ mask = torch.ones(ids.shape[:2], dtype=torch.long, device='cuda')
101
+ model = model.to('cuda', dtype=torch.bfloat16).eval()
102
+ torch.manual_seed(20261012)
103
+ with torch.inference_mode(), torch.autocast('cuda', dtype=torch.bfloat16):
104
+ generated = model.generate(
105
+ input_ids=ids, attention_mask=mask, max_new_frames=45,
106
+ do_sample=True, audio_temperature=1.0, audio_top_p=.95,
107
+ audio_top_k=50, audio_repetition_penalty=1.0, use_kv_cache=True)
108
+ codes = generated_audio(generated, config)
109
+ assert len(codes) >= 2 and codes.shape[-1] == 12, codes.shape
110
+ del generated
111
+ os.replace(temp, target)
112
+ record = {
113
+ 'title': 'Humaneness Voice Small (12-10-26)',
114
+ 'checkpoint': 'HVS-Small-2026-10-12',
115
+ 'format': 'complete adapter-free BF16 PyTorch state_dict',
116
+ 'base': str(SOURCE), 'base_sha256': sha(SOURCE),
117
+ 'adapter': str(ADAPTER), 'adapter_sha256': sha(ADAPTER),
118
+ 'model_file': target.name, 'model_bytes': target.stat().st_size,
119
+ 'model_sha256': sha(target), 'rank': 64, 'alpha': 64,
120
+ 'merged_modules': len(changes),
121
+ 'target_modules': 'Qwen3 semantic attention q_proj, k_proj, v_proj, o_proj',
122
+ 'source_step': 15238, 'adapter_step': 2298,
123
+ 'optimizer_state_included': False,
124
+ 'merge_validation': '112 nonzero finite deltas; saved state strict-loads without adapter; prompt-only generation canary passed',
125
+ 'generation_canary_frames': len(codes),
126
+ 'delta_max_abs': max(row['max_abs_delta'] for row in changes),
127
+ 'lineage': 'S3 Ref15 Full FT -> Gemini acting full FT -> Real+Gemini full FT -> two-epoch Gemini Rank-64 LoRA -> merged export',
128
+ }
129
+ metadata.write_text(json.dumps(record, indent=2) + '\n')
130
+ print(json.dumps({'status': 'EXPORTED', 'output': str(target),
131
+ 'bytes': target.stat().st_size, 'sha256': record['model_sha256']}), flush=True)
132
+
133
+
134
+ if __name__ == '__main__':
135
+ main()
code/acting_2026_10_12/train_lora.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Two Gemini-only epochs of q/k/v/o Rank-64 LoRA above Real+Gemini Small.
3
+
4
+ Each target is presented once per epoch. Its conditioning alternates between
5
+ same-voice reference audio and pseudonym, so both are seen exactly once over
6
+ two epochs. The three frozen prompt views are balanced by UID; no loose audio
7
+ files are produced. Training and resume checkpoints include optimizer/RNG.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import hashlib
13
+ import json
14
+ import math
15
+ import os
16
+ from pathlib import Path
17
+ import signal
18
+ import sys
19
+ import time
20
+
21
+ import numpy as np
22
+ import torch
23
+ from safetensors.torch import load_file, save_file
24
+
25
+ HOME = Path('/e/home/jusers/schuhmann1/jupiter')
26
+ DATA = Path('/e/scratch/laionize/schuhmann1/acting_challenges_finetune')
27
+ BASE = DATA / 'small/real_gemini_training/checkpoints/step-00015238/model.safetensors'
28
+ OUT = DATA / 'small/gemini_lora_r64_2ep_v1'
29
+ sys.path[:0] = [str(DATA), str(HOME / 'hvs_ref_only_r128_v1/public_release')]
30
+ import train_fullft as ft
31
+ from acting_data import ActingData, pack_example
32
+ from lora_layers import attach
33
+
34
+ RANK = 64
35
+ PEAK_LR = 1.0e-4
36
+ SEED = 20261012
37
+ ACCUM = 2
38
+ STOP = False
39
+
40
+
41
+ def _signal(_number, _frame):
42
+ global STOP
43
+ STOP = True
44
+
45
+
46
+ def atomic_json(path, data):
47
+ temp = path.with_suffix(path.suffix + '.partial')
48
+ temp.write_text(json.dumps(data, indent=2, sort_keys=True) + '\n')
49
+ os.replace(temp, path)
50
+
51
+
52
+ def load_base():
53
+ model, config = ft.load_model('small')
54
+ wrapped = load_file(str(BASE), device='cpu')
55
+ assert wrapped and all(k.startswith('base.') for k in wrapped)
56
+ state = {k[5:]: v for k, v in wrapped.items()}
57
+ missing, unexpected = model.load_state_dict(state, strict=False)
58
+ allowed = {'text_lm_head.weight'} | {f'audio_lm_heads.{i}.weight' for i in range(12)}
59
+ assert not unexpected and set(missing) <= allowed, (missing, unexpected)
60
+ model.tie_weights()
61
+ del wrapped, state
62
+ for p in model.parameters():
63
+ p.requires_grad_(False)
64
+ adapted = attach(model, rank=RANK, alpha=RANK)
65
+ assert len(adapted) == 112
66
+ # Some Gemini targets exceed 1,900 codec frames. Checkpoint semantic
67
+ # decoder activations as in the proven full-FT path; a frozen backbone
68
+ # still needs its input activations to compute LoRA gradients.
69
+ assert ft.enable_grad_ckpt(model) >= 20
70
+ return model, config
71
+
72
+
73
+ def view(data, uid, epoch, index):
74
+ # Both reference and alias paths occur exactly once for every UID across
75
+ # the two epochs, while the 3 GENERAL/SCRIPT views stay balanced.
76
+ cond = 'reference' if (index + epoch) % 2 == 0 else 'alias'
77
+ prompt_epoch = 6 + epoch + sum(uid.encode()) % 3
78
+ return data.example_condition(uid, prompt_epoch, cond)
79
+
80
+
81
+ def main():
82
+ parser = argparse.ArgumentParser()
83
+ parser.add_argument('--smoke-steps', type=int, default=0)
84
+ args = parser.parse_args()
85
+ assert BASE.is_file() and (BASE.parent / 'READY.json').is_file()
86
+ assert json.loads((DATA / 'PROMPTS_READY.json').read_text())['status'] == 'READY'
87
+ from accelerate import Accelerator, DistributedDataParallelKwargs
88
+ from accelerate.utils import set_seed
89
+ accelerator = Accelerator(gradient_accumulation_steps=ACCUM, mixed_precision='bf16',
90
+ kwargs_handlers=[DistributedDataParallelKwargs(
91
+ find_unused_parameters=False, gradient_as_bucket_view=True)])
92
+ world, rank = accelerator.num_processes, accelerator.process_index
93
+ assert world == (4 if args.smoke_steps else 16), world
94
+ set_seed(SEED, device_specific=True)
95
+ torch.set_num_threads(4)
96
+ signal.signal(signal.SIGUSR1, _signal)
97
+ signal.signal(signal.SIGTERM, _signal)
98
+ data = ActingData('train')
99
+ validation = ActingData('validation')
100
+ proc, _ = ft.load_processor('small')
101
+ model, config = load_base()
102
+ parameters = [(n, p) for n, p in model.named_parameters() if p.requires_grad]
103
+ assert len(parameters) == 224 and all(n.endswith(('.a.weight', '.b.weight')) for n, _ in parameters)
104
+ optimizer = torch.optim.AdamW([p for _, p in parameters], lr=PEAK_LR,
105
+ betas=(.9, .95), weight_decay=0., foreach=False)
106
+ wrapper, optimizer = accelerator.prepare(ft.TrainStep(model), optimizer)
107
+ result = OUT / ('smoke' if args.smoke_steps else 'training')
108
+ result.mkdir(parents=True, exist_ok=True)
109
+ n = len(data.ids)
110
+ longest_index = max(range(n), key=lambda i: data.by_id[data.ids[i]][0][2])
111
+ steps_per_epoch = math.ceil(n / (world * ACCUM))
112
+ total = steps_per_epoch * 2
113
+ contract = {'kind': 'humaneness-small-real-gemini-plus-gemini-lora-r64-two-epochs-v1',
114
+ 'base': str(BASE), 'base_bytes': BASE.stat().st_size,
115
+ 'assignment_sha256': ft.file_sha(DATA / 'acting_challenges_three_view_v2.sqlite'),
116
+ 'prompt_cache_sha256': ft.file_sha(DATA / 'prompt_cache.sqlite'),
117
+ 'rank': RANK, 'alpha': RANK, 'target': 'semantic Qwen3 attention q,k,v,o only',
118
+ 'adapted_linear_modules': 112, 'trainable_parameters': sum(p.numel() for _, p in parameters),
119
+ 'dataset_rows_per_epoch': n, 'world': world, 'accum': ACCUM,
120
+ 'steps_per_epoch': steps_per_epoch, 'total_steps': total,
121
+ 'peak_lr': PEAK_LR, 'warmup_fraction': .05, 'floor_factor': .1,
122
+ 'seed': SEED, 'conditioning': 'each UID once reference and once alias over two epochs',
123
+ 'prompt_views': 'balanced rotating GENERAL/SCRIPT views and original 10% full caption dropout'}
124
+ digest = hashlib.sha256(json.dumps(contract, sort_keys=True).encode()).hexdigest()
125
+ if accelerator.is_main_process:
126
+ atomic_json(result / 'RUN_CONTRACT.json', {**contract, 'contract_sha256': digest})
127
+ accelerator.wait_for_everyone()
128
+ ckpts = sorted((result / 'checkpoints').glob('step-*/READY.json'))
129
+ step = 0
130
+ if ckpts:
131
+ latest = ckpts[-1]
132
+ meta = json.loads(latest.read_text())
133
+ assert meta['contract_sha256'] == digest
134
+ accelerator.load_state(str(latest.parent))
135
+ step = int(meta['step'])
136
+ if accelerator.is_main_process:
137
+ print(json.dumps({'event': 'start', 'world': world, 'step': step, 'total': total,
138
+ 'steps_per_epoch': steps_per_epoch, 'parameters': contract['trainable_parameters']}), flush=True)
139
+ begun = time.monotonic()
140
+ last_save = begun
141
+ orders = [np.random.default_rng(SEED + e).permutation(n) for e in range(2)]
142
+ warm = max(1, round(total * .05))
143
+
144
+ def save(current, reason):
145
+ path = result / 'checkpoints' / f'step-{current:08d}'
146
+ temp = result / 'checkpoints' / f'step-{current:08d}.partial'
147
+ conflict = torch.tensor([int(accelerator.is_main_process and (path.exists() or temp.exists()))],
148
+ dtype=torch.int32, device=accelerator.device)
149
+ if world > 1:
150
+ torch.distributed.broadcast(conflict, src=0)
151
+ if conflict.item():
152
+ raise FileExistsError(path)
153
+ accelerator.save_state(str(temp))
154
+ accelerator.wait_for_everyone()
155
+ if accelerator.is_main_process:
156
+ atomic_json(temp / 'READY.json', {'step': current, 'reason': reason,
157
+ 'contract_sha256': digest,
158
+ 'optimizer_and_rng_saved': True})
159
+ os.replace(temp, path)
160
+ print(json.dumps({'event': 'checkpoint', 'step': current, 'path': str(path),
161
+ 'reason': reason}), flush=True)
162
+ accelerator.wait_for_everyone()
163
+
164
+ for current in range(step + 1, total + 1):
165
+ epoch, offset = divmod(current - 1, steps_per_epoch)
166
+ valid = min(world * ACCUM, n - offset * world * ACCUM)
167
+ assert valid > 0
168
+ if current <= warm:
169
+ factor = current / warm
170
+ else:
171
+ frac = min(1., (current - warm) / max(1, total - warm))
172
+ factor = .1 + .9 * .5 * (1 + math.cos(math.pi * frac))
173
+ optimizer.param_groups[0]['lr'] = PEAK_LR * factor
174
+ last_loss = None
175
+ for micro in range(ACCUM):
176
+ position = (offset * ACCUM + micro) * world + rank
177
+ active = position < n
178
+ index = int(orders[epoch][position]) if active else 0
179
+ if args.smoke_steps and current == 2 and micro == 0 and rank == 0:
180
+ index = longest_index # Exercise worst-case attention memory.
181
+ uid = data.ids[index]
182
+ row = view(data, uid, epoch, index)
183
+ batch = {k: v.to(accelerator.device, non_blocking=True)
184
+ for k, v in pack_example(proc, config, row).items()}
185
+ with accelerator.accumulate(wrapper):
186
+ loss = wrapper(batch['input_ids'], batch['attention_mask'], batch['labels'])
187
+ if not bool(torch.isfinite(loss).item()):
188
+ raise FloatingPointError((current, uid, 'loss'))
189
+ accelerator.backward(loss * (world * ACCUM / valid if active else 0.))
190
+ if accelerator.sync_gradients:
191
+ norm = accelerator.clip_grad_norm_(wrapper.parameters(), 1.)
192
+ if not bool(torch.isfinite(norm).item()):
193
+ raise FloatingPointError((current, uid, 'norm'))
194
+ optimizer.step()
195
+ optimizer.zero_grad(set_to_none=True)
196
+ last_loss = loss.detach().float()
197
+ if current <= 3 or current % 20 == 0 or current == total:
198
+ mean_loss = float(accelerator.gather(last_loss.reshape(1)).mean())
199
+ if accelerator.is_main_process:
200
+ event = {'event': 'step', 'step': current, 'total': total,
201
+ 'epoch': epoch+1, 'loss_last_micro': mean_loss,
202
+ 'lr': optimizer.param_groups[0]['lr'],
203
+ 'elapsed_s': round(time.monotonic()-begun, 1),
204
+ 'peak_gpu_gb': round(torch.cuda.max_memory_allocated()/1e9, 2)}
205
+ with (result / 'steps.jsonl').open('a') as log:
206
+ log.write(json.dumps(event) + '\n')
207
+ print(json.dumps(event), flush=True)
208
+ names = [None, 'smoke', 'final', 'epoch', 'periodic', 'runtime', 'signal']
209
+ reason = ('smoke' if args.smoke_steps and current >= args.smoke_steps else
210
+ 'final' if current == total else 'epoch' if current % steps_per_epoch == 0 else
211
+ 'periodic' if time.monotonic()-last_save >= 1800 else
212
+ 'runtime' if time.monotonic()-begun >= 33000 else
213
+ 'signal' if STOP else None) if accelerator.is_main_process else None
214
+ code = torch.tensor([names.index(reason)], device=accelerator.device, dtype=torch.int32)
215
+ if world > 1:
216
+ torch.distributed.broadcast(code, src=0)
217
+ reason = names[int(code.item())]
218
+ if reason:
219
+ save(current, reason)
220
+ last_save = time.monotonic()
221
+ if args.smoke_steps and current >= args.smoke_steps:
222
+ return
223
+ if reason in ('runtime', 'signal'):
224
+ sys.exit(75)
225
+ accelerator.wait_for_everyone()
226
+ if accelerator.is_main_process:
227
+ bare = accelerator.unwrap_model(wrapper).base
228
+ adapter = {name.replace('._checkpoint_wrapped_module', ''):
229
+ p.detach().float().cpu().contiguous()
230
+ for name, p in bare.named_parameters() if p.requires_grad}
231
+ assert len(adapter) == len(parameters)
232
+ temp = result / 'adapter.safetensors.partial'
233
+ save_file(adapter, str(temp), metadata={'format': 'hvs-gemini-lora-r64-2ep-v1',
234
+ 'contract_sha256': digest})
235
+ os.replace(temp, result / 'adapter.safetensors')
236
+ atomic_json(result / 'DONE.json', {'status': 'COMPLETE', 'step': total,
237
+ 'adapter': str(result / 'adapter.safetensors'),
238
+ 'contract_sha256': digest})
239
+ print(json.dumps({'event': 'done', 'step': total,
240
+ 'adapter': str(result / 'adapter.safetensors')}), flush=True)
241
+
242
+
243
+ if __name__ == '__main__':
244
+ main()
code/infer.py CHANGED
@@ -19,9 +19,9 @@ sys.path.insert(0, str(ROOT / 'code'))
19
 
20
  def main() -> None:
21
  parser = argparse.ArgumentParser(description=__doc__)
22
- parser.add_argument('--stage', choices=['default', 'S3-Ref15-FullFT'] +
23
  [f'S{i}' for i in range(1, 11)], default='default',
24
- help='default = S3 Ref15 Full FT; S1–S10 retain the original ladder weights')
25
  parser.add_argument('--prompt', required=True, help='Literal GENERAL/SCRIPT, CAPTION/TRANSCRIPT or TRANSCRIPT text')
26
  parser.add_argument('--text', required=True, help='The exact spoken transcript')
27
  parser.add_argument('--frames', type=int, required=True, help='Frame budget at 12.5 frames/second')
@@ -36,7 +36,7 @@ def main() -> None:
36
  if not 0 < args.reference_max_seconds <= 20:
37
  parser.error('--reference-max-seconds must be in (0, 20]')
38
  if args.stage in ('default', 'S3-Ref15-FullFT') and args.reference_max_seconds > 15:
39
- print('Warning: Ref15 Full FT trained with at most 14.96 s of reference; '
40
  'longer inference references are out of distribution', file=sys.stderr)
41
 
42
  import numpy as np
@@ -57,9 +57,9 @@ def main() -> None:
57
  moss_small.load_qwen_backbone = lambda model, log=print: None
58
  schema = json.loads((ROOT / 'assets/score_schema.json').read_text())
59
  model, config = build_fresh(schema, log=lambda _: None)
60
- checkpoint_name = 'S3-Ref15-FullFT' if args.stage == 'default' else args.stage
61
  if checkpoint_name == 'S3-Ref15-FullFT' and args.reference_wav is None:
62
- print('Warning: the Ref15 default was tuned only with reference audio; '
63
  'for reference-free inference compare --stage S3', file=sys.stderr)
64
  state = torch.load(ROOT / 'checkpoints' / checkpoint_name / 'model_bf16.pt',
65
  map_location='cpu', weights_only=True)
 
19
 
20
  def main() -> None:
21
  parser = argparse.ArgumentParser(description=__doc__)
22
+ parser.add_argument('--stage', choices=['default', 'HVS-Small-2026-10-12', 'S3-Ref15-FullFT'] +
23
  [f'S{i}' for i in range(1, 11)], default='default',
24
+ help='default = Humaneness Voice Small (12-10-26); original stages remain available')
25
  parser.add_argument('--prompt', required=True, help='Literal GENERAL/SCRIPT, CAPTION/TRANSCRIPT or TRANSCRIPT text')
26
  parser.add_argument('--text', required=True, help='The exact spoken transcript')
27
  parser.add_argument('--frames', type=int, required=True, help='Frame budget at 12.5 frames/second')
 
36
  if not 0 < args.reference_max_seconds <= 20:
37
  parser.error('--reference-max-seconds must be in (0, 20]')
38
  if args.stage in ('default', 'S3-Ref15-FullFT') and args.reference_max_seconds > 15:
39
+ print('Warning: these continuations trained with at most about 15 s of reference; '
40
  'longer inference references are out of distribution', file=sys.stderr)
41
 
42
  import numpy as np
 
57
  moss_small.load_qwen_backbone = lambda model, log=print: None
58
  schema = json.loads((ROOT / 'assets/score_schema.json').read_text())
59
  model, config = build_fresh(schema, log=lambda _: None)
60
+ checkpoint_name = 'HVS-Small-2026-10-12' if args.stage == 'default' else args.stage
61
  if checkpoint_name == 'S3-Ref15-FullFT' and args.reference_wav is None:
62
+ print('Warning: the Ref15 checkpoint was tuned only with reference audio; '
63
  'for reference-free inference compare --stage S3', file=sys.stderr)
64
  state = torch.load(ROOT / 'checkpoints' / checkpoint_name / 'model_bf16.pt',
65
  map_location='cpu', weights_only=True)
docs/HVS_SMALL_2026_10_12.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Humaneness Voice Small (12-10-26)
2
+
3
+ This is the current, adapter-free Humaneness Voice Small default. The complete model state is `checkpoints/HVS-Small-2026-10-12/model_bf16.pt`; `checkpoints/default.json` and `code/infer.py` resolve `default` to it. The previous S3 Ref15 Full FT and the original ten ladder checkpoints are retained, not overwritten. This release is under the repository's CC BY 4.0 license; upstream codec and foundation-model dependencies retain their own terms.
4
+
5
+ ## What was trained
6
+
7
+ The lineage is: original S3 ladder checkpoint → full-parameter Ref15 retraining with longer, distinct speaker references → three Gemini acting epochs → real-speech/Gemini full-parameter continuation → two Gemini-only epochs of a Rank-64 LoRA on the last continuation. The final LoRA trained **18,350,080 parameters** in **112 Qwen3 semantic q/k/v/o attention projections** (28 layers × 4 projections). The MOSS-style Talker was not updated by these final two LoRA epochs. The Real+Gemini parent is step 15,238; the LoRA completed step 2,298. Its training contract used 36,754 Gemini rows per epoch, a 5% warmup, peak learning rate 1e-4, and a cosine-like decay to 10% of peak. Every target appeared once per epoch; across two epochs it was conditioned once on another same-voice reference and once on a voice pseudonym. Three GENERAL/SCRIPT views rotated, with the existing 10% full-caption dropout. This checkpoint contains weights only, **not optimizer or RNG state**.
8
+
9
+ The export added each LoRA delta to the Real+Gemini base projection in FP32, cast the result to BF16, removed all adapter modules, then saved a complete ordinary PyTorch state dict. All 112 deltas were finite and nonzero, and the saved export strictly loaded without an adapter. The SHA-256 and exact local source hashes are in `checkpoints/HVS-Small-2026-10-12/metadata.json`. The final BF16 rounding may differ slightly from running the adapter unmerged; the public grid was generated from the unmerged adapter at the same training step.
10
+
11
+ ## Listening and use
12
+
13
+ The [public, matched acting grid](https://laion-humaneness-voice-small-four-checkpoint-acting.static.hf.space/) compares original S10, Ref15, Gemini acting, Real+Gemini and this final stage using the same rows, references, prompts, seeds and decoding settings. Its 105 conditions include 35 natural/spontaneous prompt cases and 70 original reference/no-reference cases; audio is on-demand MP3. It is a listening aid, not a blinded preference test. The checkpoint was chosen by user listening; no claim is made that it improves WER, speaker similarity or every emotion across a representative benchmark. Different weights can take different stochastic paths despite matched seeds.
14
+
15
+ Use the model repository's `code/infer.py --stage default` for a reference-conditioned single-clip example. A clean, distinct 5–15 s reference is recommended; do not use the target audio as its own reference. The model was also trained with pseudonym conditioning, but the historical general inference example is not the exact alias-demo pathway. For the exact pseudonym/reference prompt construction used in the grid, inspect the linked demo's manifests and the acting-code release. A `GENERAL:` / `SCRIPT:` instruction with exact spoken `--text` is the principal acting format of the final two-epoch continuation. Longer references, unseen names and out-of-domain languages are not guaranteed.
16
+
17
+ ## Reproducibility and limits
18
+
19
+ The LoRA training source and merge recipe are under `code/acting_2026_10_12/`. The exact original training depends on the separate Gemini acting dataset and JUPITER-side mappings described by the source; it is not reproduced from weights alone. The model remains experimental speech synthesis. Evaluate identity rights, consent, linguistic performance and potential deception before use. Do not impersonate people.