将 B 站长视频音频转录为文本,自用为主。
未明子的长视频没有时间观看,是否可能用 AI 方法通过 NLP 处理字幕进行自动剪裁?
本仓库用 uv
管理依赖。不用 uv
的,我想你们肯定知道怎么做。
git clone https://github.com/alephpi/TransTrans.git
cd TransTrans
uv venv
uv sync --production
uv pip install -e .
git clone https://github.com/alephpi/TransTrans.git
cd TransTrans
uv venv
uv sync
uv pip install -e .
pytest src/test.py
以 BV1iddQYQE7D 为例,运行
source .venv/bin/activate
python main.py --bvid BV1iddQYQE7D --hotwords dict/热词
或者
uv python main.py --bvid BV1iddQYQE7D --hotwords dict/热词
这将首先调用 yutto
下载音频,然后经过 ffmpeg
转码,然后用 FunASR
转录得到文本(有带时间戳字幕和纯文本两种),期间处理结果均保存于 data/{bvid}
文件夹下。
可在 dicts/热词
中逐行添加音频中出现的高频词汇,以提高识别准确率。
- 系统自行安装 ffmpeg(windows 注意配置其安装路径到环境变量)
- GPU 显存不少于 4G,如果运行时超出,适当缩小
batch_size_s
即可,batch_size_s=300
时,显存占用约 2G。
转录:根据 论文 表格 6,Paraformer-zh 在 CER 和 RTF 上都达到最好。故不考虑 SenseVoice-small。
清理口语化表达:HanLP。
无论原音频编码如何,在 FunASR 中都以torchaudio
导入并重采样至 16khz 处理。
仍以 BV1iddQYQE7D 为例(时长两小时),yutto 提供三种码率的m4a
音频,编码均为 aac
,其属性值如下(torchaudio.info
)
码率 | 大小 | 采样率 | 帧数 |
---|---|---|---|
64kbps | 35M | 48khz | 180k |
128kbps | 77M | 48khz | 360k |
320kbps | 138M | 48khz | 360k |
对这些不同码率的音频,无论是 torchaudio
内部的重采样还是用 ffmpeg
的重采样,都得到 235M 的 wav
文件,且对识别结果无显著影响。
平均转录时长/音频时长比例(rtf_avg)为 0.008,即两小时音频一分钟转录完毕。
我们先不引入标点预测和句级别时间戳,而先只用字符级别时间戳,方便更精细的清理口语化表达。
采用 HanLP 模型,见下方模型的注意事项。
口语化特征:
- 填充词 filler:就是、什么的、他妈的
- 语气词:哎呀
- 重复、修正 repetition
- 成分省略
其中,1、2 可以通过简单正则匹配替换去除,而 3 不得不通过 POS tagging 来做。当然 1、2 也可以用 POS tagging 来做,只不过这样会加剧计算负担。
如果只是考虑总结文本大义,那么只需要把转录文本全部扔到 LLM 去做,但是 LLM 的生成性无法忠实还原言语顺序,因此我们择中选取这条较为繁琐的流程。
清理重复成分的策略很简单,即保留重复成分的最后一次出现,因为它相当于能指链滑行的终点、意义的锚定点。
- 使用时间戳信息我们可以找到口语停顿处,借此我们划分出音句。
- 使用句子成分分析我们可以意义停顿处,借此划分出意句。
-
词级别去重:先分句再去重还是先去重再分句?如果我们采用滑动窗口的办法去除重复就难以设定窗长。如果我们先分句,那么句子边界是自然的去重窗口。那么跨句的去重怎么办?
- 词级别的去重不需要分词也可以做,但是会误伤某些叠词,例如“虎视眈眈”会被改成“虎视眈”。因此分词的目的在于把叠词词组保护起来,不被视为两个词。
-
分词模型无论采用粗标准还是细标准都过于细碎,比如把“操你妈”拆分成三个词,而不是作为一个整体的詈语进行标记。
- 把这些词加入自定义词组?
- 干脆在分词前就清洗掉它们?
- 但是反过来想,切的细碎反而有好处,例如“曹操他妈的完蛋了”就不会匹配到“操他妈的”,而是“他妈的”,因为曹操被分词保护起来。也就是说我们仍然可以在分词的基础上清洗,只不过我们的匹配词逻辑一定是从某个词开始,例如上面这个例子匹配只能从“曹操”或者“他”开始,规避了从“操”开始从而误伤曹操。
-
句子级别的重复,考虑使用句子相似度模型?
-
先考虑怎么去重,最后再考虑怎么反查删去的索引
-
英文会破坏索引,因为对于 ASR 模型而言,每个英文词是一个字符,而对于分词模型而言,一个英文字符才是。为方便起见我们先假设文本中不含英文。
-
我们先把填充词都删除,这样就可以进一步划分音句(因为填充词可以视为停顿),然后非常简便的把过短的音句删除,填充词不需要过度匹配,例如不需要匹配“他妈的”只需要匹配“他妈”,这样句首的“的”就可以删掉(虚词除语气词、关联词外无法作句首)。填充词包括:
- 脏话
- 语气词:哎呀、啊(在 hanlp 中,前者被标为叹词,后者标为语气词)。
- 设问、反问
- 连词:就是、就是说(但要区分强调的就是和连词性的就是,后者往往反复连续出现)
- 连续的重复:即用词的犹豫
- 不连续的(广义的)重复:
- 人称的重复,为了陈述观点(我)、询唤听众(你)、提起注意(它、这个),表现为说了一半从头开始说,通常是从某个人称开始说,例如代词+插入语+相同代词:我觉得我,你是你,我这个我,你这个你。这里的插入语也是常见词汇,但是必须是与上下文组合在一起才能判定为重复。
- 谓语的重复:就是、是、它是、就是说、(简单来)讲、也就是说:这里的特点是谓语动词常为“说、讲、是”
- 核心词的重复:近义词,这种重复是有益的,可以保留,反映出斟酌的即时状态。
- 句尾的重复:某啊某。
-
但是我们发现一个尴尬之处,就是如果我们最后分音句,就会导致某些句过切,从而丧失掉关键信息,因此我们应该考虑先分句,即在某些特征词后添加标点,并且我们只用基于规则匹配的方式添加,彻底放弃用模型推理标点。规则如下:
- 在短停顿处添加逗号,在长停顿处添加句号。
- 在语气词后添加逗号
- 在“吗、呢、吧”后添加问号
- 在部分口头禅后添加逗号
- 在口水词后添加逗号
-
现在的问题在于仅仅停留于词性标注的去重不足以满足我们的需求,一些较为复杂的冗余文本,如断头断尾句需要句法分析来判断其成分是否完整,而 HanLP 的句法分析模型(DEP,CON 等等)无不需要首先分句,而即便按照 7 中的音句分法,我们还是会遇到非常棘手的短句情况。这一部分需要留待将来完成。
paraformer-zh
在 punc_model
空置,sentence_timestamp=True
时报错,这是因为模型需要标点来断句。
应使用 ct-punc
单独使用(而非接在 paraformer-zh
的流程中使用)的话,对英文的分词会有问题,可能与默认的 jieba 分词有关。ct-punc-c
而非 ct-punc
,ct-punc-c
默认跳过英文部分(将其视为整体),而且不进行额外分词(汉字单字、连续英文字符算一词),速度更快。
COARSE_ELECTRA_SMALL_ZH
,FINE_ELECTRA_SMALL_ZH
训练自 9970 万字的全世界最大中文分词(chinese word segmentation,CWS)语料库,F1 > 98%。
三万字的分词速度:4s
PKU_POS_ELECTRA_SMALL
:准确率 97.55%
CTB9_POS_ELECTRA_SMALL
:准确率 96.26%
C863_POS_ELECTRA_SMALL
:准确率 95.19%
我们选择 PKU 标准。
三万词的打标速度:11s
ritrieve_zh_v1
以 BV1iddQYQE7D 为例,文本原长 27748 字,音频原长 2:08:01.285
- 去掉英文片段和非说话片段,文本长为 27599 字,音频长为 1:28:58.469,压缩率:文本 0.54%,音频 30.50%
- 去掉填充词、口头禅、重复词,文本长为 21965 字, 音频长为 1:08:23.165,压缩率:文本 20.84%,音频 46.58%
- 文本转录
- 清理口语化表达
- 自动切片
- 语音增强