登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

JiWER 怎样输出语音识别结果的逐词对齐

来源:17golang原创

时间:2026-10-30 18:41:09 392浏览 收藏

如果只调用 jiwer.wer(),你得到的是一个指标数字,却看不到哪一个词被替换、删除或插入。JiWER 的直接做法是改用 jiwer.process_words(reference, hypothesis):返回的 WordOutput.alignments 按句保存 AlignmentChunk,每个块都记录操作类型和参考、识别两侧的词位区间。

要输出语音识别结果的逐词对齐,先用 process_words 取得 WordOutput,需要程序化处理时读取 alignments,需要人眼排查时再调用 visualize_alignment。

官方地址:https://jitsi.github.io/jiwer/usage/

只看 WER 时,问题出在哪里

WER、MER、WIL 和 WIP 适合汇总模型表现,但单个数字不能说明错误发生在什么词位。比如参考文本与识别文本的词数不同,数字只能告诉你存在插入或删除,不能直接告诉你对应的区间。

因此,指标计算和错误解释应当分成两个对象:用 wer 等属性做汇总,用 alignments 做逐词定位。JiWER 官方文档把这两部分放在同一次 process_words 结果里,避免为查看对齐再重复计算。

process_words 返回的对齐对象

process_words 接收一条或多条参考句子与识别句子,默认会对两侧执行 WER 变换,然后返回 WordOutput。其中 references、hypotheses 是变换后的词列表,alignments 是与每个句子对应的对齐块列表。

JiWER process_words 与 WordOutput、AlignmentChunk 的静态关系图
图1:process_words 与 WordOutput、AlignmentChunk 的静态关系图,不是运行截图。
import jiwer

# 参考文本表示人工标注,识别文本表示 ASR 模型输出。
reference = "short one here"
hypothesis = "shoe order one"

# 一次计算同时得到 WER 指标和逐句对齐结构。
output = jiwer.process_words(reference, hypothesis)

# alignments[0] 对应第一组参考句子与识别句子。
for chunk in output.alignments[0]:
    print(chunk.type, chunk.ref_start_idx, chunk.ref_end_idx,
          chunk.hyp_start_idx, chunk.hyp_end_idx)

这里的代码只展示接口关系。读取时要记住:alignments 的外层按输入句子分组,内层才是该句的多个 AlignmentChunk。多句输入时,不要把不同句子的块直接拼到一个全局词序列里。

AlignmentChunk 怎样解释四类词位差异

每个对齐块至少有五个关键信息:type、ref_start_idx、ref_end_idx、hyp_start_idx 和 hyp_end_idx。起始索引包含在区间内,结束索引按 Python 切片习惯排除。

类型含义区间特点
equal两侧词位相同参考区间与识别区间通常等长
substitute识别词替换了参考词两侧都有非空区间
delete识别结果缺少参考词参考区间非空,识别区间为空
insert识别结果多出词参考区间为空,识别区间非空
JiWER AlignmentChunk 索引区间与 equal、substitute、delete、insert 的静态关系图
图2:AlignmentChunk 索引区间与四类词位操作的静态对应图,不是运行截图。
def describe_alignment(output):
    """把每个句子的对齐块整理成便于业务日志消费的字典。"""
    rows = []
    for sentence_index, chunks in enumerate(output.alignments):
        for chunk in chunks:
            # 结束索引不包含在区间内,可直接交给列表切片。
            rows.append({
                "sentence": sentence_index,
                "type": chunk.type,
                "reference_span": [chunk.ref_start_idx, chunk.ref_end_idx],
                "hypothesis_span": [chunk.hyp_start_idx, chunk.hyp_end_idx],
            })
    return rows

程序化处理时,建议保留句子编号与两侧区间,而不是只保存 type。这样后续才能回到 output.references[sentence_index] 和 output.hypotheses[sentence_index] 截取实际词语。

需要可读文本时调用 visualize_alignment

如果目标是人工排查,直接打印索引对象不够直观。jiwer.visualize_alignment(output) 会把参考行、识别行、I(插入)、S(替换)、D(删除)以及 WER 等摘要组织成文本。默认会跳过正确匹配的词位,想看完整对齐时可传入 skip_correct=False。

import jiwer

# 使用同一个 WordOutput,避免指标与可视化来自两次不同计算。
output = jiwer.process_words(
    ["short one here", "quite a bit of longer sentence"],
    ["shoe order one", "quite bit of an even longest sentence here"],
)

# line_width 只控制长句的文本换行,不改变对齐对象。
print(jiwer.visualize_alignment(output, line_width=80))

命令行场景也可以使用 JiWER CLI 的 --align 选项输出逐句对齐;但 CLI 不支持自定义预处理。若参考文本与识别文本需要统一大小写、替换标点或处理特殊标记,优先在 Python API 中显式传入两侧的 transform。

多句输入和预处理的兼容边界

默认的 process_words 会把每个输入字符串转换为词列表;多句列表的外层配对关系仍然重要。两侧句子数量和语义边界不一致时,不要仅凭对齐图判断模型错误,先确认是句子切分不同,还是词级识别真的发生了插入、删除或替换。

另外,变换会改变用于对齐的词列表。你在业务层展示原始文本时,应同时保留原始句子与 JiWER 处理后的句子,避免拿处理后索引去直接索引未经清洗的字符串。对于需要跨句寻找最小对齐的场景,官方文档提供了连续文本变换思路,但这会改变“按句解释”的语义,应该在结果字段中明确记录。

采用建议:指标与对齐各自负责

日常评估可以固定成三层:第一层用 output.wer 等字段做汇总;第二层用 output.alignments 统计替换、删除和插入的词位;第三层用 visualize_alignment 输出人工复盘文本。这样既保留可聚合的数字,也能追溯到具体错误。

最小记忆点是:process_words 负责生成一次性的词级结果,AlignmentChunk 负责描述区间与操作,visualize_alignment 负责把结构变成可读文本。不要把可视化字符串当成结构化数据,也不要把 WER 数字当成逐词映射。

常见问题

为什么我只拿到了 WER,没有对齐信息? 通常是调用了 jiwer.wer()。改用 process_words(),再从返回对象的 alignments 读取。

delete 和 insert 的索引为什么有一侧为空? 因为删除表示参考侧有词而识别侧没有,插入则相反;空区间正是这两类操作的结构表达。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>