ASR 转录本合成
ASR 转录本合成
什么和为什么
最近我参与了一些工作,重点是转录呼叫中心的音频。应用很扎实;通过 ASR 提高流程透明度/QA,并在 ASR 之上应用下游分析,最终目标是更好地预测呼叫中心内繁忙时段的性质和数量。
但是,在获取音频方面存在显着延迟。该组织(自然)绑定到第三方呼叫中心软件,这导致尝试导出呼叫音频时出现延迟。这让我成为了一个紧张的工程师团队,他们构建了一个未经测试的 ASR 管道,我们很快就没有时间来测试它了。我们需要一种使用真实(足够)数据来测试管道的方法,以防止出现任何错误。
撇开项目延迟和客户动态不谈,我认为看看我们是否可以制作一些呼叫音频,这些音频具有我们在呼叫中心数据中预期的相同“轮廓”。在我看来,该过程大致如下所示:
要点是我们正在尝试对可用于 ASR 管道的单一转录音频源进行逆向工程,具有以下属性:
- 两个扬声器。 我们假设每个通道都被单个扬声器占用。这个假设很容易被背景噪声/干扰、扬声器切换等打破,但为了测试我们的 ASR 管道会这样做。
- 立体声通道分离。 类似地,假设每个扬声器的音频仅包含在左声道或右声道中。这种结构的变体可以定义音频的 n 通道来模拟电话会议和/或线路传输。
成绩单生成
我仔细阅读了一些成绩单生成选项:
-
Samsum 汇总数据集 .一个标记数据集,通常用于微调摘要模型,具有交错的信使风格的聊天记录,并带有简短的多句摘要标记。使用 samsum 数据集,我认为我们可以跳过合成并使用原始转录数据而不调用模型。到目前为止,一切都很好;数据看起来很棒!
[“汉娜:嘿,你有贝蒂的电话号码吗?”,
'阿曼达:让我检查',
'汉娜:<file_gif> ',
"阿曼达:抱歉,找不到。",
“阿曼达:问拉里”,
“阿曼达:上次我们一起去公园时,他给她打了电话”,
“汉娜:我不太了解他”,
'汉娜:<file_gif> ',
“阿曼达:别害羞,他很好”,
'汉娜:如果你这么说的话..',
“汉娜:我宁愿你给他发短信”,
'阿曼达:给他发短信',
'汉娜:呃……好吧',
'汉娜:再见',
'阿曼达:再见']
但是,我注意到 samsum 提供了非商业许可证,所以没有骰子。
-
微软/DialoGPT 中/大 . 可用于多轮对话的大规模预训练响应生成模型。听起来很有希望!尽管在对基本演示进行了一些修改后,我发现该模型对输入的变化没有反应并且有点沉闷。
网友:金钱能买到幸福吗?
DiaoGPT:金钱可以买到幸福,但它也能买到很多让你快乐的东西。
网友:厉害!像什么?
DiaoGPT:金钱可以买到幸福,但它也能买到很多让你快乐的东西。
用户:然后呢?
DialogGPT:那你会买更多的幸福。
用户:嗯,我想我要走了!
DialogGPT:我要走了!
用户:我刚才说的?
DialogGPT:我刚刚说过! -
说话 . Facebook 的对话模型训练框架。重要的是,我注意到 ParlAI 提供对模型动物园的访问,其中包含预先训练的模型、预先存在的任务定义和允许用户生成“自我聊天”片段的 CLI 界面。最后一个功能特别重要,因为它允许在两个代理之间自动生成转录本。模型本质上是在相互交谈。下面的示例转录,完整的启动上下文:
[背景]:你的角色:我有一个兄弟。
你的角色:我喜欢炸玉米饼。
你的角色:我有三个姐妹。
你的角色:我爱狗。
[背景]:你的角色:我的卧室是紫色和柠檬绿色。
你的角色:我是素食主义者。
你的角色:我喜欢骑马。
你的角色:我将在两年内从高中毕业。
[TransformerGenerator_1]:你好!我有一条狗。你有什么样的狗?我的是一只猫。
[TransformerGenerator_2]:我没有宠物。我是素食主义者。你喜欢吃肉吗?
[TransformerGenerator_1]:我愿意。我不吃肉 。你最喜欢的食物是什么 ?我的三个兄弟姐妹经常吃肉。
[TransformerGenerator_2]:我喜欢披萨。他们的房间里有紫色和绿色吗?我有紫色的。
[TransformerGenerator_1]:披萨很棒。他们都是素食主义者。我喜欢吃炸玉米饼和比萨饼。
[TransformerGenerator_2]:我有一匹马。你平时有什么娱乐活动 ?我在空闲时间骑马。
[TransformerGenerator_1]:这是一个不错的爱好。我喜欢和住在农场的三个姐妹一起出去玩。
[TransformerGenerator_2]:你上学吗?两年前我高中毕业了。我想分两次毕业。
[TransformerGenerator_1]:我是一名初中生。祝贺你毕业!我正在学习成为一名兽医。
[TransformerGenerator_2]:太棒了!我打算去兽医学校,但我决定成为一名动物兽医。 -
这绝对是.. 一种对话,如果不是有点笨拙,毫无意义,带有偏见( 某物 Meta > parlAI 似乎正在积极尝试缓解)。撇开一致性和偏见不谈,现在就足够了。您还会注意到模型的输出标记天真地连接在一起;这在下游 TTS 中应该不是什么大问题(除了由于使用
' '。加入()
)。
文字转语音
最初,我有一个“microsoft Sam”风格的程序来将转录话语转换为音频,我仔细阅读了一些可以在 python 脚本中调用的选项。
- 皮特x3 .一个备受推崇的包,可以离线工作并利用
说
,ffmpeg
和libspeak1
用于底层合成。最好和最坏的事情是它不需要互联网连接即可使用并使用本地图书馆。 - gTTS . 围绕 google 的文本转语音 API 的薄包装。 gTTS 的好处是它只需要一个网络连接,仅此而已。尽管有这个优势,我们可能需要
ffmpeg
在音频拼接方面做一些繁重的工作。无论如何,请注意 gTTS 还具有大量可能的声音(.google.com .google.ad .google.ae
等),这也可能有助于为一组转录情节中包含的演讲增添趣味。
音频拼接
所以现在我们有了不错的转录生成和文本到语音的机制,我们可以追踪原始计划。给定对话记录:
成绩单=“”“[TransformerGenerator_1]:你好!我有一只狗。你有什么样的狗?我的是一只猫。
[TransformerGenerator_2]:我没有宠物。我是素食主义者。你喜欢吃肉吗?
[TransformerGenerator_1]:我愿意。我不吃肉 。你最喜欢的食物是什么 ?我的三个兄弟姐妹经常吃肉。
[TransformerGenerator_2]:我喜欢披萨。他们的房间里有紫色和绿色吗?我有紫色的。
[TransformerGenerator_1]:披萨很棒。他们都是素食主义者。我喜欢吃炸玉米饼和比萨饼。
[TransformerGenerator_2]:我有一匹马。你平时有什么娱乐活动 ?我在空闲时间骑马。
[TransformerGenerator_1]:这是一个不错的爱好。我喜欢和住在农场的三个姐妹一起出去玩。
[TransformerGenerator_2]:你上学吗?两年前我高中毕业了。我想分两次毕业。
[TransformerGenerator_1]:我是一名初中生。祝贺你毕业!我正在学习成为一名兽医。
[TransformerGenerator_2]:太棒了!我打算去兽医学校,但我决定成为一名动物兽医。"""
我们想使用 gTTS 处理每个交错的说话者话语:
进口时间
从路径库导入路径
导入 gtts # 存储临时 TTS 话语音频
output_dir = Path("./output/synth_calls/sample_transcript")
output_dir.mkdir(exist_ok=True, parents=True) if output_dir.exists() == False else None # 每个话语的 TTS
对于 idx,枚举中的话语(transcript.split("\\n")):
time.sleep(1) # ip禁令?
如果 idx % 2 == 0,则扬声器 =“扬声器_1”,否则为“扬声器_2”
formatted_utterance = utterance.split(":")[1].strip()
output_file = output_dir / f"{speaker}_utterance_{idx}.mp3"
tts = gtts.gTTS(formatted_utterance, lang="en", tld="com.au", slow=True)
tts.save(输出文件)
创建临时的、连续的音频片段:
然后我们可以整理:
进口图书a
将熊猫导入为 pd
导入声音文件
从 pydub 导入 AudioSegment audio_fragment_records = []
对于列表中的文件(output_dir.glob(“./*.mp3”)):
# 将话语音频文件整理成原始样本
y, s = librosa.load(str(file)) # 仅供参考:分配默认采样率
audio_fragment_records.append(
{“文件”:文件名,“sample_array”:y,“sample_array_shape”:y.shape[0]}
) 音频片段 = (
pd.DataFrame(audio_fragment_records)
# 可能只是 df 索引;但可以肯定的是
.assign(sequence_idx=lambda x: x.file.apply(lambda y: int(y.split("_")[-1][0])))
.sort_values("sequence_idx")
# 扬声器作为通道
.assign(channel=lambda x: x.sequence_idx.apply(lambda y: 1 if y % 2 == 0 else 2))
.reset_index(drop=True)
)
并拼接成填充的、特定于通道的幅度数组,然后将它们汇总为一个单一的立体声音频文件:
将 numpy 导入为 np
从 pydub 导入 AudioSegment
导入声音文件 # 填充通道 1/2 块以确保交错模式
channel_1_segments = []
channel_2_segments = []
对于 idx, e 在 audio_fragments.iterrows() 中: 如果 e.channel == 1:
channel_1_segments.append(e.sample_array)
# 使用等效大小的零数组填充交替通道(通道 2)以创建交错
channel_2_segments.append(np.zeros(e.sample_array.shape[0], dtype=np.float32))
别的:
# 奇数索引是通道 2
channel_2_segments.append(e.sample_array)
# 否则,通道 2 长度为零的数组
channel_1_segments.append(np.zeros(e.sample_array.shape[0], dtype=np.float32)) # 临时保存通道 1/2 音频 - 保存为单声道
default_sr = 22050
channel_1_padded = np.concatenate(channel_1_segments)
soundfile.write(output_dir / "channel_1_temp.wav", channel_1_padded, default_sr) channel_2_padded = np.concatenate(channel_2_segments)
soundfile.write(output_dir / "channel_2_temp.wav", channel_2_padded, default_sr) # 合并成一个交错的、通道分离的源
left_channel = AudioSegment.from_wav(output_dir / "channel_1_temp.wav")
right_channel = AudioSegment.from_wav(output_dir / "channel_2_temp.wav") stereo_sound = AudioSegment.from_mono_audiosegments(left_channel, right_channel)
stereo_sound.export(output_dir / "consolidated_final.wav")
巴笨
就像这样,我们能够合成任意数量的 WAV 文件,我们可以在 ASR 数据管道中使用。我们创建的文件的好处是它们对 PII 不敏感,并且不受行业中常见的典型数据控制的约束。
你可以找到回购 这里 ,其中包含上述每个过程的分步笔记本、一些示例输出以及最终 脚本 如果您赶时间,这完全可以联系在一起。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明