OpenSTBench 0.3.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openstbench-0.3.1 → openstbench-0.3.2}/PKG-INFO +1 -1
- {openstbench-0.3.1 → openstbench-0.3.2}/pyproject.toml +1 -1
- {openstbench-0.3.1 → openstbench-0.3.2}/src/OpenSTBench.egg-info/PKG-INFO +1 -1
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/__init__.py +1 -1
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/cli.py +84 -75
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/instance.py +8 -6
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/metrics.py +40 -28
- {openstbench-0.3.1 → openstbench-0.3.2}/README.md +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/setup.cfg +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/OpenSTBench.egg-info/SOURCES.txt +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/OpenSTBench.egg-info/dependency_links.txt +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/OpenSTBench.egg-info/requires.txt +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/OpenSTBench.egg-info/top_level.txt +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/_model_loading.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/dataset.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/emotion_evaluator.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/__init__.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/agent.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/basics.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/latency/utils.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/paralinguistic_evaluator.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/speaker_similarity_evaluator.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/speech_quality_evaluator.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/temporal_consistency_evaluator.py +0 -0
- {openstbench-0.3.1 → openstbench-0.3.2}/src/openstbench/translation_evaluator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OpenSTBench
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Open speech and translation benchmarking toolkit supporting MT, ASR, TTS, SimulST, VC, and paralinguistics with optimized CJK language support
|
|
5
5
|
Author-email: Yanjie An <691476922@qq.com>
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "OpenSTBench"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "Open speech and translation benchmarking toolkit supporting MT, ASR, TTS, SimulST, VC, and paralinguistics with optimized CJK language support"
|
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
10
|
requires-python = ">=3.9,<3.11"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OpenSTBench
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Open speech and translation benchmarking toolkit supporting MT, ASR, TTS, SimulST, VC, and paralinguistics with optimized CJK language support
|
|
5
5
|
Author-email: Yanjie An <691476922@qq.com>
|
|
6
6
|
License: MIT
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import importlib.util
|
|
3
3
|
import sys
|
|
4
|
+
import time
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from tqdm import tqdm
|
|
7
8
|
|
|
8
9
|
from .agent import GenericAgent
|
|
9
|
-
from .instance import
|
|
10
|
+
from .instance import SpeechToSpeechInstance, SpeechToTextInstance
|
|
10
11
|
from .metrics import SCORERS
|
|
11
12
|
from .utils import Visualizer, materialize_s2s_alignment_artifacts, submit_slurm
|
|
12
|
-
|
|
13
|
-
# --- 引入兄弟模块 (紧耦合) ---
|
|
14
|
-
# 注意:使用相对导入或包绝对导入
|
|
15
13
|
from ..translation_evaluator import TranslationEvaluator
|
|
16
14
|
|
|
15
|
+
|
|
17
16
|
class LatencyEvaluator:
|
|
18
|
-
"""
|
|
17
|
+
"""Generic latency evaluator for speech-input streaming tasks."""
|
|
18
|
+
|
|
19
19
|
def __init__(
|
|
20
20
|
self,
|
|
21
21
|
agent: GenericAgent,
|
|
22
22
|
segment_size=20,
|
|
23
|
+
poll_interval_ms=10.0,
|
|
23
24
|
latency_unit="word",
|
|
24
25
|
asr_fallback_for_s2s_alignment=True,
|
|
25
26
|
asr_model="medium",
|
|
@@ -29,6 +30,7 @@ class LatencyEvaluator:
|
|
|
29
30
|
):
|
|
30
31
|
self.agent = agent
|
|
31
32
|
self.segment_size = segment_size
|
|
33
|
+
self.poll_interval_ms = max(1.0, float(poll_interval_ms))
|
|
32
34
|
self.instances = {}
|
|
33
35
|
self.latency_unit = latency_unit
|
|
34
36
|
self.asr_fallback_for_s2s_alignment = asr_fallback_for_s2s_alignment
|
|
@@ -40,33 +42,62 @@ class LatencyEvaluator:
|
|
|
40
42
|
def run(self, source_files, ref_files, task="s2t", output_dir="./output", visualize=False):
|
|
41
43
|
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
42
44
|
visualizer = Visualizer(output_dir) if visualize else None
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
poll_interval_s = self.poll_interval_ms / 1000.0
|
|
46
|
+
chunk_interval_s = float(self.segment_size) / 1000.0
|
|
47
|
+
self.instances = {}
|
|
48
|
+
|
|
49
|
+
print(f"Running Latency Evaluation ({task}, {len(source_files)} samples)...")
|
|
45
50
|
for i, (src, ref) in tqdm(enumerate(zip(source_files, ref_files)), total=len(source_files)):
|
|
46
51
|
if task == "s2t":
|
|
47
52
|
ins = SpeechToTextInstance(i, src, ref, output_dir)
|
|
48
53
|
else:
|
|
49
54
|
ins = SpeechToSpeechInstance(i, src, ref, output_dir)
|
|
50
|
-
|
|
55
|
+
|
|
51
56
|
self.agent.reset()
|
|
57
|
+
sample_wall_start = time.perf_counter()
|
|
58
|
+
ins.start_time = sample_wall_start
|
|
59
|
+
send_start_time = None
|
|
60
|
+
sent_chunk_count = 0
|
|
61
|
+
source_finished = False
|
|
62
|
+
|
|
52
63
|
while not ins.finish_prediction:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
now = time.monotonic()
|
|
65
|
+
next_due_time = None
|
|
66
|
+
|
|
67
|
+
if not source_finished:
|
|
68
|
+
if send_start_time is None:
|
|
69
|
+
send_start_time = now
|
|
70
|
+
next_due_time = send_start_time + (sent_chunk_count + 1) * chunk_interval_s
|
|
71
|
+
if now >= next_due_time:
|
|
72
|
+
s_in = ins.send_source(self.segment_size)
|
|
73
|
+
self.agent.push(s_in)
|
|
74
|
+
if not getattr(s_in, "is_empty", False):
|
|
75
|
+
sent_chunk_count += 1
|
|
76
|
+
source_finished = bool(getattr(s_in, "finished", False))
|
|
77
|
+
|
|
78
|
+
s_out = self.agent.pop()
|
|
59
79
|
if hasattr(self.agent, "consume_model_inference_time"):
|
|
60
80
|
ins.add_model_inference_time(self.agent.consume_model_inference_time())
|
|
61
|
-
|
|
62
|
-
|
|
81
|
+
|
|
82
|
+
if not getattr(s_out, "is_empty", False) or getattr(s_out, "finished", False):
|
|
83
|
+
ins.receive_prediction(s_out)
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
if source_finished:
|
|
87
|
+
time.sleep(poll_interval_s)
|
|
88
|
+
elif next_due_time is not None:
|
|
89
|
+
sleep_time = max(0.0, min(poll_interval_s, next_due_time - time.monotonic()))
|
|
90
|
+
if sleep_time > 0:
|
|
91
|
+
time.sleep(sleep_time)
|
|
92
|
+
|
|
93
|
+
ins.total_inference_time = time.perf_counter() - sample_wall_start
|
|
63
94
|
self.instances[i] = ins
|
|
64
|
-
if visualizer:
|
|
65
|
-
|
|
66
|
-
|
|
95
|
+
if visualizer:
|
|
96
|
+
visualizer.plot(ins.summarize())
|
|
97
|
+
|
|
67
98
|
if task == "s2s":
|
|
68
99
|
self._prepare_s2s_transcripts(output_dir)
|
|
69
|
-
|
|
100
|
+
|
|
70
101
|
return self.instances
|
|
71
102
|
|
|
72
103
|
def _prepare_s2s_transcripts(self, output_dir):
|
|
@@ -79,10 +110,8 @@ class LatencyEvaluator:
|
|
|
79
110
|
asr_device=self.asr_device,
|
|
80
111
|
)
|
|
81
112
|
|
|
82
|
-
# 增加 show_all_metrics 开关,默认为 False
|
|
83
113
|
def compute_latency(self, computation_aware=False, output_dir="./output", show_all_metrics=False):
|
|
84
114
|
results = {}
|
|
85
|
-
# 1. 扫描并算出所有注册的 Scorer 成绩
|
|
86
115
|
for name, cls in SCORERS.items():
|
|
87
116
|
try:
|
|
88
117
|
if "Align" in name:
|
|
@@ -94,44 +123,36 @@ class LatencyEvaluator:
|
|
|
94
123
|
)
|
|
95
124
|
else:
|
|
96
125
|
scorer = cls(computation_aware=computation_aware)
|
|
97
|
-
|
|
126
|
+
|
|
98
127
|
score = scorer(self.instances)
|
|
99
128
|
key = f"{name}_CA" if computation_aware else name
|
|
100
129
|
results[key] = score
|
|
101
|
-
except Exception
|
|
130
|
+
except Exception:
|
|
102
131
|
pass
|
|
103
|
-
|
|
104
|
-
# 2. 智能化筛选最高级/最准确的一个版本展示给用户
|
|
132
|
+
|
|
105
133
|
cleaned_results = {}
|
|
106
|
-
|
|
107
|
-
# [A] 评估: 第一声开口延迟 (StartOffset)
|
|
134
|
+
|
|
108
135
|
so_key = "StartOffset_CA" if computation_aware else "StartOffset"
|
|
109
136
|
align_so_key = "StartOffset_SpeechAlign_CA" if computation_aware else "StartOffset_SpeechAlign"
|
|
110
|
-
|
|
111
137
|
if results.get(align_so_key) is not None and results[align_so_key] > 0:
|
|
112
138
|
cleaned_results["First_Audio_Delay_(StartOffset_ms)"] = results[align_so_key]
|
|
113
139
|
else:
|
|
114
140
|
cleaned_results["First_Audio_Delay_(StartOffset_ms)"] = results.get(so_key, 0)
|
|
115
|
-
|
|
116
|
-
# [B] 评估: 整句同传综合延迟 (标准版 ATD: 连同阅读/播放音频时间一起计算)
|
|
141
|
+
|
|
117
142
|
atd_key = "ATD_CA" if computation_aware else "ATD"
|
|
118
143
|
align_atd_key = "ATD_SpeechAlign_CA" if computation_aware else "ATD_SpeechAlign"
|
|
119
|
-
|
|
120
144
|
if results.get(align_atd_key) is not None and results[align_atd_key] > 0:
|
|
121
145
|
cleaned_results["Overall_Translation_Delay_(ATD_ms)"] = results[align_atd_key]
|
|
122
146
|
else:
|
|
123
147
|
cleaned_results["Overall_Translation_Delay_(ATD_ms)"] = results.get(atd_key, 0)
|
|
124
148
|
|
|
125
|
-
# [C] 评估: 模型结单同传延迟 (纯净版 CustomATD: 剔除音频合成自身的物理时长,只看模型推断何时结束)
|
|
126
149
|
catd_key = "CustomATD_CA" if computation_aware else "CustomATD"
|
|
127
150
|
align_catd_key = "CustomATD_SpeechAlign_CA" if computation_aware else "CustomATD_SpeechAlign"
|
|
128
|
-
|
|
129
151
|
if results.get(align_catd_key) is not None and results[align_catd_key] > 0:
|
|
130
152
|
cleaned_results["End_Action_Delay_(CustomATD_ms)"] = results[align_catd_key]
|
|
131
153
|
else:
|
|
132
154
|
cleaned_results["End_Action_Delay_(CustomATD_ms)"] = results.get(catd_key, 0)
|
|
133
155
|
|
|
134
|
-
# [D] 评估: 实时率指标 (RTF)
|
|
135
156
|
if "RTF" in results:
|
|
136
157
|
cleaned_results["Real_Time_Factor_(RTF)"] = results["RTF"]
|
|
137
158
|
elif "RTF_CA" in results:
|
|
@@ -142,21 +163,19 @@ class LatencyEvaluator:
|
|
|
142
163
|
elif "ModelGenerateRTF_CA" in results and results["ModelGenerateRTF_CA"] is not None:
|
|
143
164
|
cleaned_results["Model_Generate_RTF"] = results["ModelGenerateRTF_CA"]
|
|
144
165
|
|
|
145
|
-
# ================= 修改开始 =================
|
|
146
|
-
# 如果用户显式开启了展示开关,则将原始杂乱数据装入 "detailed_all_metrics"
|
|
147
166
|
if show_all_metrics:
|
|
148
167
|
cleaned_results["detailed_all_metrics"] = results
|
|
149
|
-
# ================= 修改结束 =================
|
|
150
168
|
|
|
151
169
|
return cleaned_results
|
|
152
170
|
|
|
171
|
+
|
|
153
172
|
def load_agent_from_file(path, class_name):
|
|
154
|
-
"""动态加载用户 Agent"""
|
|
155
173
|
spec = importlib.util.spec_from_file_location("user_agent", path)
|
|
156
174
|
mod = importlib.util.module_from_spec(spec)
|
|
157
175
|
spec.loader.exec_module(mod)
|
|
158
176
|
return getattr(mod, class_name)
|
|
159
177
|
|
|
178
|
+
|
|
160
179
|
def main():
|
|
161
180
|
parser = argparse.ArgumentParser(description="OpenSTBench Latency Evaluator")
|
|
162
181
|
parser.add_argument("--source", required=True)
|
|
@@ -166,6 +185,7 @@ def main():
|
|
|
166
185
|
parser.add_argument("--agent-script", required=True, help="Path to python file containing Agent class")
|
|
167
186
|
parser.add_argument("--agent-class", required=True, help="Name of the Agent class")
|
|
168
187
|
parser.add_argument("--segment-size", type=int, default=20)
|
|
188
|
+
parser.add_argument("--poll-interval-ms", type=float, default=10.0)
|
|
169
189
|
parser.add_argument("--latency-unit", choices=["word", "char"], default="word")
|
|
170
190
|
parser.add_argument("--disable-asr-fallback", action="store_true")
|
|
171
191
|
parser.add_argument("--asr-model", default="medium")
|
|
@@ -179,20 +199,20 @@ def main():
|
|
|
179
199
|
if args.slurm:
|
|
180
200
|
submit_slurm(args, __file__)
|
|
181
201
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
ref = [None]*len(src)
|
|
202
|
+
with open(args.source) as f:
|
|
203
|
+
src = [l.strip() for l in f if l.strip()]
|
|
204
|
+
ref = [None] * len(src)
|
|
185
205
|
if args.target:
|
|
186
|
-
with open(args.target) as f:
|
|
206
|
+
with open(args.target) as f:
|
|
207
|
+
ref = [l.strip() for l in f if l.strip()]
|
|
187
208
|
|
|
188
|
-
# 2. 加载用户 Agent
|
|
189
209
|
AgentClass = load_agent_from_file(args.agent_script, args.agent_class)
|
|
190
210
|
agent = AgentClass()
|
|
191
|
-
|
|
192
|
-
# 3. 运行 Latency 评测
|
|
211
|
+
|
|
193
212
|
evaluator = LatencyEvaluator(
|
|
194
213
|
agent,
|
|
195
214
|
args.segment_size,
|
|
215
|
+
poll_interval_ms=args.poll_interval_ms,
|
|
196
216
|
latency_unit=args.latency_unit,
|
|
197
217
|
asr_fallback_for_s2s_alignment=not args.disable_asr_fallback,
|
|
198
218
|
asr_model=args.asr_model,
|
|
@@ -200,58 +220,47 @@ def main():
|
|
|
200
220
|
alignment_dictionary_model=args.alignment_dictionary_model,
|
|
201
221
|
)
|
|
202
222
|
instances = evaluator.run(src, ref, args.task, args.output)
|
|
203
|
-
|
|
204
|
-
# 4. 计算 Latency 指标
|
|
223
|
+
|
|
205
224
|
print("\n--- Latency Metrics ---")
|
|
206
225
|
scores = evaluator.compute_latency(False, args.output)
|
|
207
226
|
if args.computation_aware:
|
|
208
227
|
scores.update(evaluator.compute_latency(True, args.output))
|
|
209
|
-
|
|
228
|
+
|
|
210
229
|
for k, v in scores.items():
|
|
211
230
|
print(f"{k:<25}: {v:.4f}")
|
|
212
231
|
|
|
213
|
-
# 5. [紧耦合] 运行 Quality 指标
|
|
214
232
|
if args.quality and args.target:
|
|
215
233
|
print("\n--- Quality Metrics (Integrated) ---")
|
|
216
|
-
|
|
217
|
-
# 收集预测结果
|
|
218
|
-
# 注意: 对于 s2s, instances[i].prediction 应该是音频文件路径
|
|
219
234
|
predictions = [ins.get_prediction_content() for ins in instances.values()]
|
|
220
|
-
|
|
235
|
+
|
|
221
236
|
if args.task == "s2t":
|
|
222
|
-
# [修改] 调用 TranslationEvaluator 算 BLEU
|
|
223
|
-
# 默认只开 BLEU 以保证速度
|
|
224
237
|
qual_eval = TranslationEvaluator(
|
|
225
|
-
use_bleu=True,
|
|
226
|
-
use_chrf=False,
|
|
227
|
-
use_comet=False,
|
|
228
|
-
use_whisper=False
|
|
238
|
+
use_bleu=True,
|
|
239
|
+
use_chrf=False,
|
|
240
|
+
use_comet=False,
|
|
241
|
+
use_whisper=False,
|
|
229
242
|
)
|
|
230
|
-
# 使用新的 evaluate_all 接口
|
|
231
243
|
q_res = qual_eval.evaluate_all(
|
|
232
|
-
target_text=predictions,
|
|
233
|
-
reference=ref,
|
|
234
|
-
source=src
|
|
244
|
+
target_text=predictions,
|
|
245
|
+
reference=ref,
|
|
246
|
+
source=src,
|
|
235
247
|
)
|
|
236
248
|
print(q_res)
|
|
237
|
-
|
|
249
|
+
|
|
238
250
|
elif args.task == "s2s":
|
|
239
|
-
# [修改] 调用 TranslationEvaluator 替代原 SpeechEvaluator
|
|
240
|
-
# 开启语音相关功能: use_wer=True, use_whisper=True
|
|
241
|
-
# predictions 是 wav 路径列表
|
|
242
251
|
speech_eval = TranslationEvaluator(
|
|
243
|
-
use_wer=True,
|
|
244
|
-
use_whisper=True,
|
|
245
|
-
whisper_model="tiny",
|
|
246
|
-
use_utmos=False
|
|
252
|
+
use_wer=True,
|
|
253
|
+
use_whisper=True,
|
|
254
|
+
whisper_model="tiny",
|
|
255
|
+
use_utmos=False,
|
|
247
256
|
)
|
|
248
|
-
# 使用 evaluate_all 接口传入 target_speech
|
|
249
257
|
s_res = speech_eval.evaluate_all(
|
|
250
|
-
target_speech=predictions,
|
|
258
|
+
target_speech=predictions,
|
|
251
259
|
reference=ref,
|
|
252
|
-
source=src
|
|
260
|
+
source=src,
|
|
253
261
|
)
|
|
254
262
|
print(s_res)
|
|
255
263
|
|
|
264
|
+
|
|
256
265
|
if __name__ == "__main__":
|
|
257
266
|
main()
|
|
@@ -80,7 +80,8 @@ class SpeechToTextInstance(Instance):
|
|
|
80
80
|
def len_sample_to_ms(self, length): return length * 1000 / self.sample_rate
|
|
81
81
|
|
|
82
82
|
def send_source(self, segment_size=10):
|
|
83
|
-
if self.step == 0
|
|
83
|
+
if self.step == 0 and self.start_time is None:
|
|
84
|
+
self.start_time = time.perf_counter()
|
|
84
85
|
num_samples = math.ceil(segment_size / 1000 * self.sample_rate)
|
|
85
86
|
|
|
86
87
|
if self.step < len(self.samples):
|
|
@@ -105,13 +106,13 @@ class SpeechToTextInstance(Instance):
|
|
|
105
106
|
if getattr(seg, "config", None):
|
|
106
107
|
self.add_model_inference_time(seg.config.get("model_inference_time"))
|
|
107
108
|
|
|
108
|
-
current_time = time.
|
|
109
|
+
current_time = time.perf_counter()
|
|
109
110
|
content_list = seg.content.strip().split()
|
|
110
111
|
self.prediction_list += content_list
|
|
111
112
|
self.append_prediction_text(seg.content, source="native_transcript")
|
|
112
113
|
|
|
113
114
|
curr_delay = self.len_sample_to_ms(self.step)
|
|
114
|
-
curr_elapsed =
|
|
115
|
+
curr_elapsed = (current_time - self.start_time) * 1000
|
|
115
116
|
|
|
116
117
|
self.delays += [curr_delay] * len(content_list)
|
|
117
118
|
self.elapsed += [curr_elapsed] * len(content_list)
|
|
@@ -134,7 +135,8 @@ class SpeechToSpeechInstance(SpeechToTextInstance):
|
|
|
134
135
|
self.finish_prediction = seg.finished
|
|
135
136
|
return
|
|
136
137
|
if not seg.content: return
|
|
137
|
-
if not self.start_time:
|
|
138
|
+
if not self.start_time:
|
|
139
|
+
self.start_time = time.perf_counter()
|
|
138
140
|
if getattr(seg, "config", None):
|
|
139
141
|
self.add_model_inference_time(seg.config.get("model_inference_time"))
|
|
140
142
|
transcript = seg.config.get("transcript")
|
|
@@ -142,7 +144,7 @@ class SpeechToSpeechInstance(SpeechToTextInstance):
|
|
|
142
144
|
source = seg.config.get("transcript_source", "native_transcript")
|
|
143
145
|
self.append_prediction_text(transcript, source=source)
|
|
144
146
|
|
|
145
|
-
current_time = time.
|
|
147
|
+
current_time = time.perf_counter()
|
|
146
148
|
duration_ms = len(seg.content) * 1000 / seg.sample_rate
|
|
147
149
|
self.target_sample_rate = seg.sample_rate
|
|
148
150
|
|
|
@@ -150,7 +152,7 @@ class SpeechToSpeechInstance(SpeechToTextInstance):
|
|
|
150
152
|
self.durations.append(duration_ms)
|
|
151
153
|
|
|
152
154
|
curr_delay = self.len_sample_to_ms(self.step)
|
|
153
|
-
curr_elapsed =
|
|
155
|
+
curr_elapsed = (current_time - self.start_time) * 1000
|
|
154
156
|
|
|
155
157
|
self.delays.append(curr_delay)
|
|
156
158
|
self.elapsed.append(curr_elapsed)
|
|
@@ -86,6 +86,30 @@ class LatencyScorer:
|
|
|
86
86
|
def subtract(self, arr1, arr2):
|
|
87
87
|
return [x - y for x, y in zip(arr1, arr2)]
|
|
88
88
|
|
|
89
|
+
def get_incremental_compute_times(self, ins, count):
|
|
90
|
+
if not self.computation_aware:
|
|
91
|
+
return [0.0] * count
|
|
92
|
+
|
|
93
|
+
elapsed = list(getattr(ins, "elapsed", []) or [])
|
|
94
|
+
delays = list(getattr(ins, "delays", []) or [])
|
|
95
|
+
if not elapsed or not delays:
|
|
96
|
+
return [0.0] * count
|
|
97
|
+
|
|
98
|
+
limit = min(len(elapsed), len(delays), count)
|
|
99
|
+
if limit <= 0:
|
|
100
|
+
return [0.0] * count
|
|
101
|
+
|
|
102
|
+
compute_elapsed = [max(0.0, e - d) for e, d in zip(elapsed[:limit], delays[:limit])]
|
|
103
|
+
incremental = []
|
|
104
|
+
prev = 0.0
|
|
105
|
+
for value in compute_elapsed:
|
|
106
|
+
incremental.append(max(0.0, value - prev))
|
|
107
|
+
prev = value
|
|
108
|
+
|
|
109
|
+
if limit < count:
|
|
110
|
+
incremental.extend([0.0] * (count - limit))
|
|
111
|
+
return incremental
|
|
112
|
+
|
|
89
113
|
def get_delays(self, ins):
|
|
90
114
|
override = getattr(ins, "_alignment_delays", None)
|
|
91
115
|
if override is not None:
|
|
@@ -226,12 +250,7 @@ class ATDScorer(LatencyScorer):
|
|
|
226
250
|
compute_times.append(0.0)
|
|
227
251
|
else:
|
|
228
252
|
base_emit_delays = list(getattr(ins, "delays", []) or [])
|
|
229
|
-
text_compute_times =
|
|
230
|
-
if self.computation_aware and getattr(ins, "elapsed", None):
|
|
231
|
-
compute_elapsed = self.subtract(ins.elapsed, ins.delays)
|
|
232
|
-
text_compute_times = self.subtract(compute_elapsed, [0] + compute_elapsed[:-1])
|
|
233
|
-
else:
|
|
234
|
-
text_compute_times = [0.0] * len(delays)
|
|
253
|
+
text_compute_times = self.get_incremental_compute_times(ins, len(delays))
|
|
235
254
|
|
|
236
255
|
for idx, delay in enumerate(delays):
|
|
237
256
|
emit_delay = base_emit_delays[idx] if idx < len(base_emit_delays) else delay
|
|
@@ -239,19 +258,18 @@ class ATDScorer(LatencyScorer):
|
|
|
239
258
|
target_delays.append(float(delay))
|
|
240
259
|
target_chunk_ids.append(source_chunk_id)
|
|
241
260
|
target_token_lens.append(0.0)
|
|
242
|
-
|
|
261
|
+
extra_compute = 0.0 if self.computation_aware else float(text_compute_times[idx])
|
|
262
|
+
compute_times.append(extra_compute)
|
|
243
263
|
else:
|
|
244
|
-
speech_compute_times =
|
|
245
|
-
if self.computation_aware and getattr(ins, "elapsed", None):
|
|
246
|
-
compute_elapsed = self.subtract(ins.elapsed, ins.delays)
|
|
247
|
-
speech_compute_times = self.subtract(compute_elapsed, [0] + compute_elapsed[:-1])
|
|
248
|
-
else:
|
|
249
|
-
speech_compute_times = [0.0] * len(delays)
|
|
264
|
+
speech_compute_times = self.get_incremental_compute_times(ins, len(delays))
|
|
250
265
|
|
|
251
266
|
for idx, duration_ms in enumerate(getattr(ins, "durations", []) or []):
|
|
252
267
|
source_chunk_id = self.source_chunk_id_from_delay(source_boundaries, ins.delays[idx])
|
|
253
268
|
token_lens = self.split_duration_into_tokens(duration_ms, token_len_ms=300.0)
|
|
254
|
-
|
|
269
|
+
if self.computation_aware:
|
|
270
|
+
per_token_compute = 0.0
|
|
271
|
+
else:
|
|
272
|
+
per_token_compute = float(speech_compute_times[idx]) / len(token_lens) if token_lens else 0.0
|
|
255
273
|
for token_len in token_lens:
|
|
256
274
|
target_delays.append(float(delays[idx]))
|
|
257
275
|
target_chunk_ids.append(source_chunk_id)
|
|
@@ -328,31 +346,25 @@ class CustomATD(ATDScorer):
|
|
|
328
346
|
compute_times.append(0.0)
|
|
329
347
|
else:
|
|
330
348
|
base_emit_delays = list(getattr(ins, "delays", []) or [])
|
|
331
|
-
text_compute_times =
|
|
332
|
-
if self.computation_aware and getattr(ins, "elapsed", None):
|
|
333
|
-
compute_elapsed = self.subtract(ins.elapsed, ins.delays)
|
|
334
|
-
text_compute_times = self.subtract(compute_elapsed, [0] + compute_elapsed[:-1])
|
|
335
|
-
else:
|
|
336
|
-
text_compute_times = [0.0] * len(delays)
|
|
349
|
+
text_compute_times = self.get_incremental_compute_times(ins, len(delays))
|
|
337
350
|
|
|
338
351
|
for idx, delay in enumerate(delays):
|
|
339
352
|
emit_delay = base_emit_delays[idx] if idx < len(base_emit_delays) else delay
|
|
340
353
|
source_chunk_id = self.source_chunk_id_from_delay(source_boundaries, emit_delay)
|
|
341
354
|
target_delays.append(float(delay))
|
|
342
355
|
target_chunk_ids.append(source_chunk_id)
|
|
343
|
-
|
|
356
|
+
extra_compute = 0.0 if self.computation_aware else float(text_compute_times[idx])
|
|
357
|
+
compute_times.append(extra_compute)
|
|
344
358
|
else:
|
|
345
|
-
speech_compute_times =
|
|
346
|
-
if self.computation_aware and getattr(ins, "elapsed", None):
|
|
347
|
-
compute_elapsed = self.subtract(ins.elapsed, ins.delays)
|
|
348
|
-
speech_compute_times = self.subtract(compute_elapsed, [0] + compute_elapsed[:-1])
|
|
349
|
-
else:
|
|
350
|
-
speech_compute_times = [0.0] * len(delays)
|
|
359
|
+
speech_compute_times = self.get_incremental_compute_times(ins, len(delays))
|
|
351
360
|
|
|
352
361
|
for idx, duration_ms in enumerate(getattr(ins, "durations", []) or []):
|
|
353
362
|
source_chunk_id = self.source_chunk_id_from_delay(source_boundaries, ins.delays[idx])
|
|
354
363
|
token_lens = self.split_duration_into_tokens(duration_ms, token_len_ms=300.0)
|
|
355
|
-
|
|
364
|
+
if self.computation_aware:
|
|
365
|
+
per_token_compute = 0.0
|
|
366
|
+
else:
|
|
367
|
+
per_token_compute = float(speech_compute_times[idx]) / len(token_lens) if token_lens else 0.0
|
|
356
368
|
for _ in token_lens:
|
|
357
369
|
target_delays.append(float(delays[idx]))
|
|
358
370
|
target_chunk_ids.append(source_chunk_id)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|