点击上方关注,All in AI中国
作者:黄功详(Steeve Huang)
随着机器学习的迅速发展,尤其是深度学习的进步,语音识别技术得到了显著的提高。这种技术依赖于大量高质量的数据。但是,为非流行语言构建的模型比流行语言(英语等)的表现还要差。这是因为只有少数可用的训练数据集,并且很难有效地收集高质量的数据。这篇文章将向大家展示如何有效地收集任何语言的语音识别数据。
虽然Mozilla公司推出了一个名为Common Voice的开源项目,这个项目鼓励人们发表自己的意见,但大多数人或者不了解这个项目,或者不愿意参与其中。下图显示了Common Voice中不同语言的小数据收集进度。

Common Voice的小数据收集进度(https://voice.mozilla.org/en/languages) 由于视频网站Youtube上提供了丰富的电视节目和电视剧,因此可以高效地收集语音识别数据,通常通过人工参与来实施。

流星花园(https://www.youtube.com/watch?v=DsuN185H58I)
如上图所示,这些戏剧或节目中的一些嵌入了字幕,可以通过OCR提取。然后,提取的文本连同剪辑的音频可以形成语音识别数据的样本。
概述
下图显示了包含多个模块的整个数据收集管道的概述。我们首先使用Youtube V3 API搜索*载下**与指定的电视节目名称相关的视频。利用FFMPEG将视频分割成帧,每个帧由自我训练的Mask-RCNN处理(稍后更详细叙述)以仅保留图像的字幕区域。然后,处理后的图像将发送到Google Vision API,以获取预测的文本和置信度。使用Pandas库,我们按时间戳对结果进行排序,然后将它们聚合在一起,为每个视频生成一个SRT文件。以下内容将向大家展示如何实现这些模块。

先决条件
- Python==3.6
- FFMPEG
- joblib==0.12.0
- numpy==1.13.3
- pandas==0.23.3
- tensorflow-gpu==1.4.0
- keras==2.1.3
- google-cloud-vision==0.32.0
- pafy==0.5.4
- youtube-dl==2017.12.2
- tqdm==4.23.4
- editdistance==0.4
实施
此部分由数据收集管道中的模块划分,每个子部分对应一个模块。
*载下**视频和提取音频
转到Youtube搜索你感兴趣的电视剧。确保你可以找到该节目的*放播**列表,因为*放播**列表中的视频质量往往是统一的。检查第一个视频是否包含嵌入的字幕,我们可以假设整个*放播**列表都是字幕嵌入的。

*放播**列表中的Youtube视频

上面的代码显示了如何搜索Youtube*放播**列表。你需要申请API密钥才能使用Youtube V3 API客户端。在第27行

字面上执行Youtube搜索,查询是戏剧名称,并返回第一个结果的*放播**列表ID。使用*放播**列表ID,我们可以获取此*放播**列表中所有视频的视频ID。

同样,在第15行,

在给定*放播**列表ID的情况下,搜索最多50个视频的ID(受API限制)。然后,我们可以开始*载下**视频了。

Pafy Python包用于*载下**可用的最高分辨率的视频。*载下**视频后,我们利用FFMPEG从每个视频中提取音频。
由于FFMPEG不支持python SDK,我们需要在shell中调用它。通过触发subprocess.call()函数,可以执行参数中指定的shell命令。现在,已成功*载下**指定电视节目的视频和音频。
拆分视频
然后,我们将*载下**的视频拆分为运行OCR的帧。

只有中间60%的部分视频被分割成帧,因为第一部分和最后20%的部分可能包含开场或结尾的歌曲,不能用于语音识别。
同样,在子进程的shell中调用FFMPEG。分割视频的参数描述如下:
- ss:以秒为单位的开始时间
- t:以秒为单位分割视频的时间跨度
- r:采样率
采样率越高,预测的字幕时间跨度越准确。但是,需要在时间和准确性之间进行权衡。在这里,我把它设置为2。
训练和评估Mask-RCNN
现在,你已从每个视频中获得了帧。如果你在Google Vision API官方网站上试用一些图像进行OCR()(文档文本检测,因为字幕是键入的字符)。就会发现结果并不令人满意,因为这些图像的背景非常复杂。
这是Mask-RCNN发挥作用的时候。Mask-RCNN是2017年发布的用于对象检测和实例分割的RCNN系列的一部分。它能够在像素级别进行分类。

Mask-RCNN演示(https://github.com/matterport/Mask_RCN
我们的目标是训练Mask-RCNN,以帮助我们识别哪些像素是字幕。
为了确保训练有素的模型足够广泛以适应各种视频,我从十几个电视剧和动画中*载下**视频。应用不同的字体大小,字体类型和字体垂直位置以丰富训练数据集。底部40%的图像被裁剪,以便我们确保在训练数据上手动添加的字幕不与原始字幕重叠。这也确保了非字幕部分来自高度相似的色彩空间分布。

上面的代码利用Python PIL包将文本添加到图像上。由于Mask-RCNN将每个连接的组件视为一个实例,因此在某些语言中,一个字符可以由不同的实例组成。例如,字符"把"包括左和右两个分量。要查找连接的组件,我们可以在skimage包中使用label()函数,如以下代码所示。

生成的训练数据如下图所示。左侧显示输入图像,右侧显示地面实况。正如你可以从右侧某些字符的颜色差异中看到的那样,单个字符可以包含多个实例。

生成的Mask-RCNN训练数据
为了训练Mask-RCNN,我在这个Github Repo中使用了Keras和Tensorflow的优秀实现。我们需要做的是指定Training模型的配置,如下所示。



你只需要设置NUM_CLASSES=1+1(字幕+背景),并指定IMAGE_MIN_DIM以及IMAGE_MAX_DIM,这取决于你的训练数据的分辨率。需要手动计算MEAN_PIXEL才能在预处理阶段进行标准化。然后,你可以开始加载数据和训练。
评估
经过100个周期的训练后,让我们开始在一些图像上运行训练有素的模型。


不同电视节目的评估结果
顶部显示左侧显示输入图像,而右侧显示输出图像。您可以看到字幕已被清楚地识别出来。虽然上面有一些噪音,但在运行OCR时对下面的字幕预测没有影响。主要问题是有时Mask-RCNN太严格了,它删除了文本的某些部分。在上面的第二个例子中,第二个字符"难"被部分删除,这可能会降低OCR的性能。为了恢复这个错误,我决定使用Ensemble。

单一模型预测
集合是指使用多个模型进行预测。我没有使用单个模型进行预测,如上图所示,而是使用不同的配置训练另一个模型。输入图像被输入这两个模型,最终结果将是这两个模型预测的结合。

集合预测
由Mask-RCNN处理的结果帧如下所示。左下角是原始输入图像,右下角是仅有一个模型的预测,左上角是整体结果。 (右上角是测试时间增强结果,已被当前管道中的整体取代)

Mask-RCNN处理过的图像。左下角:原创。右下:单一模型。左上角:合成。右上:测试时间增加(未讨
运行OCR
使用Mask-RCNN模块处理图像后,我们就可以在这些图像上运行OCR。与Youtube API密钥不同,你需要在谷歌云平台上申请Vision API凭据,并将其存储在本地磁盘中。

上面的代码对你的图像文件进行编码,发送到OCR API并接收响应。响应包含预测字符,每个字符的边界框坐标以及每个字符的置信度。你可以决定自己的阈值来过滤低置信度结果。最终结果写入CSV文件,每行代表每个帧。

生成SRT最终结果
最后,这是管道的最后一个模块!通常一段字幕持续2-3秒,这意味着大约有4-6个连续帧包含相同的字幕。问题仍然是我们如何将不同帧的结果合并为一个,决定语音的开始时间、结束时间和副标题。我们只需要检查当前字幕是否与最后一个字幕匹配。一个主要挑战是有时两个帧具有相同的字幕,但OCR预测不同的结果。你需要确定两个字幕是否相同的算法应该能够处理这些情况。

上面的代码基于启发方法的。如果来自当前帧和最后一帧的这两个字符串中的字符与70%相同,则返回True,而不管每个字符的顺序如何。
结论
如果你正确遵循上述说明,则表明你已成功构建自动语音识别数据集收集管道。你可以根据FFMPEG指定的时间轻松分割音频,但有一个小问题,由于字幕出现的时间和相应语音的时间可能有些不同,因此语音和文本之间可能存在一些没有对齐的情况。这可以通过语言模型来解决,以基于语音稍微调整每个字幕的时间。
有关更多信息和详细信息,请参阅此https://github.com/khuangaf/ITRI-speech-recognition-dataset-generation
