voidful commited on
Commit
1804ac1
·
1 Parent(s): 511c805

Catch weak stop signals near sentence end

Browse files
Files changed (3) hide show
  1. README.md +2 -2
  2. app.py +4 -4
  3. tests/test_production.py +22 -8
README.md CHANGED
@@ -38,7 +38,7 @@ Demo 預設採用目前通過長文穩定性評估的推論設定:
38
  | Target pace | 4.0 speech units/sec |
39
  | Generation safety pace | 5.2 CJK / 4.6 ASCII-mixed units/sec + 1 latent step |
40
  | Short-text guidance | minimum CFG 3.0 below 6 speech units |
41
- | Stop policy | 0.50 → 0.20 from 65% to 90% predicted progress, 1 hit |
42
  | Endpoint cue | append terminal punctuation for model input when missing, except very short text |
43
  | Hard stop | native-pace target steps, independent of playback pace |
44
  | Pace correction | pitch-preserving stretch after completion; onset split only at punctuation |
@@ -54,7 +54,7 @@ Demo 預設採用目前通過長文穩定性評估的推論設定:
54
 
55
  自然 stop 在目前 checkpoint 上仍可能過快或錯過句尾。Demo 不再用播放目標語速決定生成長度;
56
  模型使用原生語速的安全上限,完成後才做保音高語速校正。缺少句末標點時只在模型輸入補上句號,
57
- 少於 6 個 speech units 的短句會使用最低 CFG 3.0,並在預估進度 65% 後逐步降低 stop threshold。
58
  首段不會在無標點處硬切。進階參數可供研究比較,但 release
59
  profile 是 CFG 2.0、NFE 10、後處理語速 1.0。
60
 
 
38
  | Target pace | 4.0 speech units/sec |
39
  | Generation safety pace | 5.2 CJK / 4.6 ASCII-mixed units/sec + 1 latent step |
40
  | Short-text guidance | minimum CFG 3.0 below 6 speech units |
41
+ | Stop policy | 0.50 → 0.05 from 75% to 95% predicted progress, 1 hit |
42
  | Endpoint cue | append terminal punctuation for model input when missing, except very short text |
43
  | Hard stop | native-pace target steps, independent of playback pace |
44
  | Pace correction | pitch-preserving stretch after completion; onset split only at punctuation |
 
54
 
55
  自然 stop 在目前 checkpoint 上仍可能過快或錯過句尾。Demo 不再用播放目標語速決定生成長度;
56
  模型使用原生語速的安全上限,完成後才做保音高語速校正。缺少句末標點時只在模型輸入補上句號,
57
+ 少於 6 個 speech units 的短句會使用最低 CFG 3.0;stop threshold 只在預估進度 75% 後逐步降低,並於 95% 進度降至 0.05,以捕捉句末弱 stop 訊號而不影響前段內容。
58
  首段不會在無標點處硬切。進階參數可供研究比較,但 release
59
  profile 是 CFG 2.0、NFE 10、後處理語速 1.0。
60
 
app.py CHANGED
@@ -66,9 +66,9 @@ CROSSFADE_MS = 80.0
66
  CHUNK_EDGE_FADE_MS = 80.0
67
  CHUNK_RMS_MATCH_DB = 4.0
68
  STOP_THRESHOLD = 0.50
69
- STOP_LATE_THRESHOLD = 0.20
70
- STOP_LATE_START_RATIO = 0.65
71
- STOP_LATE_FULL_RATIO = 0.90
72
  STOP_CONSECUTIVE = 1
73
  HARD_STOP_MARGIN_STEPS = 1
74
  MIN_PACE_SPEED = 0.80
@@ -377,7 +377,7 @@ HEADER = f"""
377
  台灣華語與中英混合文字轉語音。模型版本:`{CHECKPOINT}`。
378
 
379
  目前預設採用穩定推論設定:`CFG 2.0`(極短句最低 `3.0`)、`NFE 10`、目標語速 `4.0 字/秒`、
380
- 生成上限依模型原生語速計算、生成完成後校正至目標語速、補齊句末提示、單次高信心 stop、
381
  只在自然標點切開首段、每 80 字切段,且不使用 retry 或 rerank。
382
  """
383
 
 
66
  CHUNK_EDGE_FADE_MS = 80.0
67
  CHUNK_RMS_MATCH_DB = 4.0
68
  STOP_THRESHOLD = 0.50
69
+ STOP_LATE_THRESHOLD = 0.05
70
+ STOP_LATE_START_RATIO = 0.75
71
+ STOP_LATE_FULL_RATIO = 0.95
72
  STOP_CONSECUTIVE = 1
73
  HARD_STOP_MARGIN_STEPS = 1
74
  MIN_PACE_SPEED = 0.80
 
377
  台灣華語與中英混合文字轉語音。模型版本:`{CHECKPOINT}`。
378
 
379
  目前預設採用穩定推論設定:`CFG 2.0`(極短句最低 `3.0`)、`NFE 10`、目標語速 `4.0 字/秒`、
380
+ 生成上限依模型原生語速計算、生成完成後校正至目標語速、補齊句末提示、尾端 weak-stop 保護、
381
  只在自然標點切開首段、每 80 字切段,且不使用 retry 或 rerank。
382
  """
383
 
tests/test_production.py CHANGED
@@ -81,16 +81,30 @@ def test_stop_controller_relaxes_near_endpoint_and_enforces_hard_stop():
81
 
82
  def test_stop_controller_can_relax_before_the_text_pace_cap():
83
  controller = StopHysteresisController(
84
- _SequenceStopHead([0.4] * 9),
85
- threshold=0.65,
86
- late_threshold=0.20,
87
- consecutive=2,
88
- late_start_ratio=0.65,
89
- late_full_ratio=0.90,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  )
91
  controller.begin(min_len=2, expected_steps=10, hard_stop_steps=11)
92
- decisions = [controller(torch.zeros(1, 2)).argmax(dim=-1).item() for _ in range(9)]
93
- assert decisions == [0, 0, 0, 0, 0, 0, 0, 0, 1]
94
 
95
 
96
  def test_split_preserves_punctuation_and_minimum_chunk_size():
 
81
 
82
  def test_stop_controller_can_relax_before_the_text_pace_cap():
83
  controller = StopHysteresisController(
84
+ _SequenceStopHead([0.04] * 9 + [0.06]),
85
+ threshold=0.50,
86
+ late_threshold=0.05,
87
+ consecutive=1,
88
+ late_start_ratio=0.75,
89
+ late_full_ratio=0.95,
90
+ )
91
+ controller.begin(min_len=2, expected_steps=10, hard_stop_steps=11)
92
+ decisions = [controller(torch.zeros(1, 2)).argmax(dim=-1).item() for _ in range(10)]
93
+ assert decisions == [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
94
+
95
+
96
+ def test_stop_controller_does_not_accept_weak_signal_before_late_window():
97
+ controller = StopHysteresisController(
98
+ _SequenceStopHead([0.10] * 8),
99
+ threshold=0.50,
100
+ late_threshold=0.05,
101
+ consecutive=1,
102
+ late_start_ratio=0.75,
103
+ late_full_ratio=0.95,
104
  )
105
  controller.begin(min_len=2, expected_steps=10, hard_stop_steps=11)
106
+ decisions = [controller(torch.zeros(1, 2)).argmax(dim=-1).item() for _ in range(8)]
107
+ assert decisions == [0, 0, 0, 0, 0, 0, 0, 0]
108
 
109
 
110
  def test_split_preserves_punctuation_and_minimum_chunk_size():