Skip to content

Commit 85eee6c

Browse files
committed
add NbestCtcRescoreJob for joint-CTC n-best rescoring
1 parent 1f1d055 commit 85eee6c

1 file changed

Lines changed: 65 additions & 2 deletions

File tree

users/sheremeta/recognition/nbest_rescore.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
"""Recognition post-processing jobs: n-best LM rescoring."""
1+
"""Recognition post-processing jobs: n-best LM and CTC rescoring."""
22

3-
__all__ = ["NbestKenLmRescoreJob"]
3+
__all__ = ["NbestKenLmRescoreJob", "NbestCtcRescoreJob"]
44

55
import ast
66
import math
@@ -74,3 +74,66 @@ def run(self):
7474
for seq_tag, text in results.items():
7575
f.write(f"{repr(str(seq_tag))}: {repr(text)},\n")
7676
f.write("}\n")
77+
78+
79+
class NbestCtcRescoreJob(Job):
80+
"""Fuse n-best AM scores with per-hypothesis CTC sequence scores, best hyp per sequence, once per ``ctc_scale``."""
81+
82+
def __init__(
83+
self,
84+
*,
85+
nbest_file,
86+
ctc_scores_file,
87+
ctc_scales,
88+
am_scale: float = 1.0,
89+
):
90+
# both files are {seq_tag: [(score, text), ...]} with matching seq_tags and per-seq hyp order
91+
self.nbest_file = nbest_file
92+
self.ctc_scores_file = ctc_scores_file
93+
self.ctc_scales = tuple(float(s) for s in ctc_scales)
94+
self.am_scale = float(am_scale)
95+
96+
self.out_search_results = {
97+
cs: self.output_path(f"search_out.ctc{cs}.py") for cs in self.ctc_scales
98+
}
99+
100+
self.rqmt = {"cpu": 1, "mem": 4, "time": 1}
101+
102+
def tasks(self):
103+
yield Task("run", rqmt=self.rqmt, mini_task=True)
104+
105+
def run(self):
106+
with open(self.nbest_file.get_path(), "rt") as f:
107+
nbest = ast.literal_eval(f.read())
108+
with open(self.ctc_scores_file.get_path(), "rt") as f:
109+
ctc = ast.literal_eval(f.read())
110+
111+
assert set(nbest) == set(ctc), (
112+
f"seq_tag mismatch: {len(set(nbest) ^ set(ctc))} tags differ between nbest and ctc scores"
113+
)
114+
115+
joined = {}
116+
for seq_tag, am_entries in nbest.items():
117+
ctc_entries = ctc[seq_tag]
118+
assert len(am_entries) == len(ctc_entries), seq_tag
119+
lst = []
120+
for (am_score, am_text), (ctc_score, ctc_text) in zip(am_entries, ctc_entries):
121+
assert am_text == ctc_text, f"{seq_tag}: hyp text mismatch between nbest and ctc scores"
122+
lst.append((float(am_score), float(ctc_score), am_text))
123+
joined[seq_tag] = lst
124+
125+
for ctc_scale in self.ctc_scales:
126+
results = {}
127+
for seq_tag, lst in joined.items():
128+
best_text, best_score = "", None
129+
for am_score, ctc_score, text in lst:
130+
score = self.am_scale * am_score + ctc_scale * ctc_score
131+
if best_score is None or score > best_score:
132+
best_score, best_text = score, text
133+
results[seq_tag] = best_text
134+
135+
with open(self.out_search_results[ctc_scale].get_path(), "wt") as f:
136+
f.write("{\n")
137+
for seq_tag, text in results.items():
138+
f.write(f"{repr(str(seq_tag))}: {repr(text)},\n")
139+
f.write("}\n")

0 commit comments

Comments
 (0)