rtx-csm: --decoder-steering-layers — layer subset fixes repetition
Mirror of --steering-layers for the decoder: comma-separated layer indices to actually steer (decoder has 4 layers; useful subsets are [3], [2,3], [1,2]). Sweep at seed 7, [email protected]: [0,1,2,3] cos 0.86 WER 0.93 "I'm not that tall ×3" ← repetition [3] cos 0.58 WER 0.86 "I'll be off and offense..." [2,3] cos 0.80 WER 1.43 "My daughter, Penny Ryan, and I have…" [0,1] cos 0.82 WER 1.57 "And I'll check on them..." [1,2] cos 0.81 WER 1.43 "I'm going to call him an X-Man..." The repetition is specific to all-layers-at-once steering. Any 2-layer subset eliminates it while preserving most of the cosine boost. Same pattern as the backbone's [8,10,12] finding: partial perturbation lets the unsteered layers act as a stabilizing prior. [2,3] (decoder last 2) is the new recommended recipe — best cosine of the no-repetition subsets and the longest fluent transcript. Documented in docs/perf_history.md. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
@@ -209,6 +209,30 @@ Both fail differently; neither produces production-quality emotional
|
|||||||
speech in single-shot. The N-seed picker (`emotional_speech_n.sh`)
|
speech in single-shot. The N-seed picker (`emotional_speech_n.sh`)
|
||||||
is still the right consumer interface — it doesn't care which
|
is still the right consumer interface — it doesn't care which
|
||||||
failure mode produced the bad samples, just sorts by WER.
|
failure mode produced the bad samples, just sorts by WER.
|
||||||
|
|
||||||
|
**Decoder layer subset fixes the repetition.** The "I'm not that
|
||||||
|
tall ×3" failure at all-4-layers @ scale 1.0 disappears entirely
|
||||||
|
when steering is restricted to a 2-layer subset. Same pattern as
|
||||||
|
the backbone's `[8,10,12]` finding: perturbing all layers
|
||||||
|
simultaneously is the bad regime; partial perturbation lets the
|
||||||
|
unsteered layers act as a stabilizing prior.
|
||||||
|
|
||||||
|
layer subset cos WER repetition?
|
||||||
|
[0,1,2,3] (all) 0.86 0.93 yes ("I'm not that tall ×3")
|
||||||
|
[3] only 0.58 0.86 no
|
||||||
|
[2,3] last 2 0.80 1.43 no — "My daughter, Penny Ryan…"
|
||||||
|
[0,1] first 2 0.82 1.57 no
|
||||||
|
[1,2] middle 2 0.81 1.43 no
|
||||||
|
|
||||||
|
`[2,3]` (decoder's last two layers, the analogue of backbone's
|
||||||
|
`[8,10,12]` mid-deep recipe) is the best balance — preserves the
|
||||||
|
cosine boost while eliminating the repetition. New decoder recipe:
|
||||||
|
|
||||||
|
```
|
||||||
|
--decoder-steering-vec /path/<emotion>.safetensors
|
||||||
|
--decoder-steering-scale 1.0
|
||||||
|
--decoder-steering-layers 2,3
|
||||||
|
```
|
||||||
- *RAVDESS corpus shape biases output length*: clips are ~3 s
|
- *RAVDESS corpus shape biases output length*: clips are ~3 s
|
||||||
("Kids are talking by the door") so the steering biases the
|
("Kids are talking by the door") so the steering biases the
|
||||||
model toward terse outputs (one or two words). Longer-utterance
|
model toward terse outputs (one or two words). Longer-utterance
|
||||||
|
|||||||
@@ -150,6 +150,13 @@ struct Cli {
|
|||||||
#[arg(long, default_value_t = 1.0)]
|
#[arg(long, default_value_t = 1.0)]
|
||||||
decoder_steering_scale: f32,
|
decoder_steering_scale: f32,
|
||||||
|
|
||||||
|
/// Comma-separated layer indices to actually steer on the decoder
|
||||||
|
/// (e.g. "2,3"). The decoder has 4 layers (CSM-1B); useful subsets
|
||||||
|
/// to try: `3` (deepest), `2,3` (last two), `1,2` (middle two).
|
||||||
|
/// Defaults to all 4 layers.
|
||||||
|
#[arg(long, value_delimiter = ',')]
|
||||||
|
decoder_steering_layers: Option<Vec<usize>>,
|
||||||
|
|
||||||
/// Per-frame CFG scale schedule (Selective CFG, arXiv 2509.19668).
|
/// Per-frame CFG scale schedule (Selective CFG, arXiv 2509.19668).
|
||||||
/// Forms:
|
/// Forms:
|
||||||
/// - `const:<scale>` (constant scale, equivalent to --cfg-scale)
|
/// - `const:<scale>` (constant scale, equivalent to --cfg-scale)
|
||||||
@@ -247,11 +254,14 @@ fn main() -> Result<()> {
|
|||||||
let mut steering =
|
let mut steering =
|
||||||
rtx_csm::steering::LayerSteering::load_safetensors(dec_path, n, &device)?;
|
rtx_csm::steering::LayerSteering::load_safetensors(dec_path, n, &device)?;
|
||||||
steering.set_scale(cli.decoder_steering_scale);
|
steering.set_scale(cli.decoder_steering_scale);
|
||||||
|
if let Some(layers) = cli.decoder_steering_layers.as_ref() {
|
||||||
|
steering.restrict_to_layers(layers);
|
||||||
|
}
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"loaded decoder steering from {} (scale={}, layers={})",
|
"loaded decoder steering from {} (scale={}, active_layers={:?})",
|
||||||
dec_path.display(),
|
dec_path.display(),
|
||||||
cli.decoder_steering_scale,
|
cli.decoder_steering_scale,
|
||||||
n,
|
steering.active_layers(),
|
||||||
);
|
);
|
||||||
generator.set_decoder_steering(Some(steering))?;
|
generator.set_decoder_steering(Some(steering))?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user