首页
学习
活动
专区
工具
TVP
发布

Simple Audio Recognition(简单的音频识别)

本教程将向您展示如何构建识别十个不同文字的基本语音识别网络。知道真正的语音和音频识别系统要复杂得多是很重要的,但像MNIST这样的图像,它应该让你对所涉及的技术有一个基本的了解。一旦你完成了本教程,你将有一个模型试图将一秒音频片段分类为无声,未知单词,“yes”,“no”,“up”,“down”,“left” ,“right”,“on”,“off”,“stop”或“go”。您还可以使用此模型并在Android应用程序中运行。

准备

您应该确保安装了TensorFlow,并且由于该脚本下载了超过1GB的操练数据,因此您的计算机上需要良好的互联网连接和足够的可用空间。操练过程本身可能需要几个小时,因此请确保您有一台可以使用很长时间的机器。

操练

要开始操练过程,请转到TensorFlow源代码树并运行:

python tensorflow/examples/speech_commands/train.py

该脚本将首先下载Speech Commands数据集,该数据集包含65,000个WAVE音频文件,其中包含30个不同单词的人。这些数据由Google收集并在CC BY许可下发布,您可以通过贡献五分钟的自己的声音来帮助改进。归档大于1GB,因此这部分可能需要一段时间,但您应该看到进度日志,并且一旦您下载完成后就不需要再执行此步骤。

下载完成后,您会看到如下所示的日志记录信息:

I0730 16:53:44.766740   55030 train.py:176] Training from step: 1
I0730 16:53:47.289078   55030 train.py:217] Step #1: rate 0.001000, accuracy 7.0%, cross entropy 2.611571

这表明初始化过程完成并且操练循环已经开始。你会看到它为每个操练步骤输出信息。以下是它的含义:

Step #1表明我们正处于操练循环的第一步。在这种情况下,总共需要18,000步,因此您可以查看步骤编号,以了解它的完成程度。

rate 0.001000是控制网络重量更新速度的学习速率。在此之前,这是一个相对较高的数字(0.001),但对于以后的操练周期,它将减少10倍,达到0.0001。

accuracy 7.0%是在这个操练步骤中正确预测了多少类。这个值通常会波动很多,但是随着操练的进展,这个值应该会平均增加。该模型输出一个数字数组,每个标签一个数字,每个数字表示该输入是该类别的预测可能性。预测标签是通过选择分数最高的条目来挑选的。分数总是在0到1之间,较高的值表示对结果更有信心。

cross entropy 2.611571是我们用来指导操练过程的损失函数的结果。这是通过比较当前操练运行和正确标签的得分得出的分数,并且在训练期间这应该趋向下降。

经过一百个步骤之后,您应该看到如下所示的一行:

I0730 16:54:41.813438 55030 train.py:252] Saving to "/tmp/speech_commands_train/conv.ckpt-100"

这是将目前操练过的权重保存到检查点文件。如果您的训练脚本被中断,您可以查找上次保存的检查点,然后重新启动脚本,--start_checkpoint=/tmp/speech_commands_train/conv.ckpt-100并将其作为命令行参数从该点开始。

混淆矩阵

经过四百步之后,这些信息将被记录下来:

I0730 16:57:38.073667   55030 train.py:243] Confusion Matrix:
 [[258   0   0   0   0   0   0   0   0   0   0   0]
 [  7   6  26  94   7  49   1  15  40   2   0  11]
 [ 10   1 107  80  13  22   0  13  10   1   0   4]
 [  1   3  16 163   6  48   0   5  10   1   0  17]
 [ 15   1  17 114  55  13   0   9  22   5   0   9]
 [  1   1   6  97   3  87   1  12  46   0   0  10]
 [  8   6  86  84  13  24   1   9   9   1   0   6]
 [  9   3  32 112   9  26   1  36  19   0   0   9]
 [  8   2  12  94   9  52   0   6  72   0   0   2]
 [ 16   1  39  74  29  42   0   6  37   9   0   3]
 [ 15   6  17  71  50  37   0   6  32   2   1   9]
 [ 11   1   6 151   5  42   0   8  16   0   0  20]]

第一部分是混淆矩阵。为了理解它的含义,首先需要知道所使用的标签,在这种情况下是“ silence”,“unknown”,“yes”,“no”,“up",“down”,“left”,“right”,”on”,”off”,”stop”和”go”。每列代表一组被预测为每个标签的样本,所以第一列代表所有被预测为无声的剪辑,第二列代表被预测为未知单词的所有剪辑,第三个”yes”,等等。

每一行都用正确的真实的标签表示剪辑。第一行是所所有无声的剪辑,第二个剪辑是未知的单词,第三个是“yes”等。

这个矩阵可能比单一的准确分数更有用,因为它可以很好地总结网络出现的错误。在这个例子中,你可以看到第一行中的所有条目都是零,除了最初的一条。因为第一行是所有实际上是无声的剪辑,这意味着它们中没有一个被错误地标记为单词,所以我们对无声没有任何错误的否定。这表明网络已经非常善于区分silence与单词。

如果我们往下看第一列,我们会看到很多非零值。该列代表所有被预测为无声的剪辑,因此第一个单元外的正数是错误。这意味着一些真正口语单词的剪辑实际上被预测为无声,所以我们确实有不少误报。

一个完美的模型会产生一个混淆矩阵,其中所有条目与通过中心的对角线相距零。发现与该模式的偏差可以帮助您找出模型最容易混淆的方式,一旦找出问题,您可以通过添加更多数据或清理类别来解决问题。

验证

在混淆矩阵之后,你应该看到如下一行:

I0730 16:57:38.073777 55030 train.py:245] Step 400: Validation accuracy = 26.3% (N=3093)

将数据集分为三类是一种很好的做法。最大的(在这种情况下大约80%的数据)用于操练网络,一个较小的组(在这里称为“验证”)被保留用于评估操练期间的准确性,另一组(最后一组10%,“测试”)用于评估操练完成后的准确度。

这种分裂的原因是,网络在操练过程中始终存在着记忆输入的危险。通过保持验证集不同,您可以确保该模型能够处理以前从未见过的数据。测试集是一种额外的安全措施,可以确保您不仅以适合培训和验证集的方式对您的模型进行调整,而且还可以确保您的模型不会涉及范围更广的输入。

操练脚本自动将数据集分成这三个类别,上面的记录行显示了在验证集上运行时模型的准确性。理想情况下,这应该相当接近操练的准确性。如果操练准确性提高但验证不成立,那么这就表明过度操练正在发生,而且你的模型只是在学习关于训练片段的事情,而不是泛化的更广泛的模式。

Tensorboard

可视化培训进展的一个好方法是使用Tensorboard。默认情况下,该脚本将事件保存到/ tmp / retrain_logs,并且您可以通过运行以下命令来加载这些事件:

tensorboard --logdir /tmp/retrain_logs

然后在浏览器中导航到http:// localhost:6006,您将看到显示模型进度的图表和图表。

操练完成

经过几个小时的操练(取决于机器的速度),脚本应该已经完成​​了所有18,000步。它将打印出最终的混淆矩阵,以及准确度分数,所有这些都在测试集上运行。使用默认设置时,您应该看到85%和90%之间的准确度。

由于音频识别在移动设备上特别有用,因此接下来我们会将其导出为易于在这些平台上使用的紧凑格式。为此,运行以下命令行:

python tensorflow/examples/speech_commands/freeze.py \
--start_checkpoint=/tmp/speech_commands_train/conv.ckpt-18000 \
--output_file=/tmp/my_frozen_graph.pb

冻结模型创建完成后,您可以使用label_wav.py脚本对其进行测试,如下所示:

python tensorflow/examples/speech_commands/label_wav.py \
--graph=/tmp/my_frozen_graph.pb \
--labels=/tmp/speech_commands_train/conv_labels.txt \
--wav=/tmp/speech_dataset/left/a5d485dc_nohash_0.wav

这应该打印出三个标签:

left (score = 0.81477)
right (score = 0.14139)
_unknown_ (score = 0.03808)

希望“left”是最高分,因为这是正确的标签,但由于训练是随机的,因此可能不适合您尝试的第一个文件。尝试使用同一文件夹中的其他一些.wav文件来查看它的效果。

分数介于零和一之间,较高的值表示该模型对其预测更有信心。

在Android应用程序中运行模型

查看这个模型在真实应用程序中的工作原理的最简单方法是下载预构建的Android演示应用程序并将它们安装到您的手机上。你会看到'TF Speech'出现在你的应用列表中,打开它会显示出我们刚刚操练过我们的模型的相同动作词列表,从“Yes”和“No”开始。一旦您向应用授予使用麦克风的权限,您应该可以尝试说出这些字词,并在模型识别其中一个字词时在UI中看到它们。

你也可以自己构建这个应用程序,因为它是开源的,可以作为Github上的TensorFlow存储库的一部分。默认情况下,它从tensorflow.org下载预操练模型,但您可以轻松地将其替换为您自己操练过的模型。如果你这样做,你需要确保在常量主要SpeechActivity Java源文件,比如SAMPLE_RATESAMPLE_DURATION,符合你操练时的默认设置所做的任何更改。您还会看到有一个Java版本的RecognizeCommands模块这与本教程中的C ++版本非常相似。如果您已经调整了参数,您还可以在SpeechActivity中更新它们以获得与服务器测试中相同的结果。

演示应用程序会根据您复制到资产旁边的标签文本文件自动更新其UI结果列表,这意味着您可以轻松地尝试不同的模型,而无需进行任何代码更改。如果您更改路径,则需要更新LABEL_FILENAMEMODEL_FILENAME指向已添加的文件。

这个模型如何工作?

本教程中使用的体系结构基于卷积神经网络中关于小尺寸关键字点检的一些内容。选择它是因为它比较简单,快速操练和易于理解,而不是最先进的技术。有许多不同的方法来建立神经网络模型来处理音频,包括经常性网络扩张(有害的)卷积。本教程基于卷积网络,对于任何使用图像识别的人员都会感到非常熟悉。不过,这听起来可能令人惊讶,因为音频本质上是一维时间上的连续信号,而不是2D空间问题。

我们通过定义时间窗口来解决这个问题,相信我们说出的单词应该适应,并将该窗口中的音频信号转换为图像。这是通过将进入的音频样本分成短段,几毫秒长,并计算一组频段的频率强度来完成的。来自一个段的每组频率强度被视为一个数字向量,并且这些向量按时间顺序排列以形成一个二维数组。然后可以将这组数值视为单通道图像,并称为谱图。如果你想查看音频采样产生的图像类型,你可以运行`wav_to_spectrogram工具:

bazel run tensorflow/examples/wav_to_spectrogram:wav_to_spectrogram -- \
--input_wav=/tmp/speech_dataset/happy/ab00c4b2_nohash_0.wav \
--output_png=/tmp/spectrogram.png

如果你打开/tmp/spectrogram.png你应该看到如下:

由于TensorFlow的记忆顺序,这个图像中的时间从顶部到底部逐渐增加,频率从左到右,与时间从左到右的光谱图惯例不同。你应该能够看到几个不同的部分,第一个音节“Ha”与“ppy”不同。

由于人耳对某些频率比其他频率更敏感,因此在语音识别中对传统进行进一步处理以将其转换为一组Mel频率倒谱系数(简称MFCC)。这也是一个二维的单通道表示,因此它可以像图像一样处理。如果您的目标是一般声音而不是语音,则可能会发现您可以跳过此步骤并直接在谱图上进行操作。

然后将由这些处理步骤产生的图像馈送到多层卷积神经网络中,其中具有全连接层,最后是softmax。您可以在tensorflow / examples / speech_commands / models.py中看到此部分的定义。

流媒体的准确性

大多数音频识别应用程序需要在连续的音频流上运行,而不是在单个剪辑上运行。在此环境中使用模型的典型方法是在不同的偏移量处重复应用它,并在短时间内平均结果以产生平滑预测。如果您将输入视为图像,则它会不断沿着时间轴进行滚动。我们想要识别的单词可以随时开始,因此我们需要拍摄一系列快照以便有机会进行对齐,以捕捉我们输入模型的时间窗口中的大部分话语。如果我们以足够快的速度进行采样,那么我们很有可能在多个窗口中捕获单词,因此对结果进行平均可提高预测的总体置信度。

有关如何在流式数据上使用模型的示例,可以查看test_streaming_accuracy.cc。使用RecognizeCommands类来运行一个长形式的输入音频,尝试识别单词,并将这些预测与标签和时间的基本事实列表进行比较。这是随着时间的推移将模型应用于音频信号流的一个很好的例子。

你需要一个很长的音频文件来对它进行测试,并标注每个单词所在的位置。如果您不想自己录制一个,则可以使用该generate_streaming_test_wav实用程序生成一些综合测试数据。默认情况下,这将创建一个十分钟的.wav文件,大约每三秒钟记录一次单词,以及一个文本文件,其中包含每个单词说出时的基本事实。这些单词从当前数据集的测试部分拉出,与背景噪音混合在一起。要运行它,请使用:

bazel run tensorflow/examples/speech_commands:generate_streaming_test_wav

这将保存一个.wav文件/tmp/speech_commands_train/streaming_test.wav和一个文本文件,列出标签/tmp/speech_commands_train/streaming_test_labels.txt。然后您可以运行准确性测试:

bazel run tensorflow/examples/speech_commands:test_streaming_accuracy -- \
--graph=/tmp/my_frozen_graph.pb \
--labels=/tmp/speech_commands_train/conv_labels.txt \
--wav=/tmp/speech_commands_train/streaming_test.wav \
--ground_truth=/tmp/speech_commands_train/streaming_test_labels.txt \
--verbose

这将输出关于正确匹配的单词数量,多少错误标签以及当没有真正单词时模型触发多少次的信息。有多种参数可以控制信号平均的工作方式,其中包括--average_window_ms设置平均结果的时间长度,--clip_stride_ms这是模型应用程序之间的时间间隔,--suppression_ms它可以在初始的一个字词被发现后,阻止后续字词检测触发一段时间,并且--detection_threshold,它控制了平均分数在被认为是一个稳定的结果之前必须达到的高度。

您会看到流式精确度会输出三个数字,而不仅仅是操练中使用的一个度量。这是因为不同的应用程序有不同的要求,有些能够容忍频繁的错误结果,只要发现真正的词语(高回忆),而另一些应用程序则非常注重确保预测标签很可能是正确的,即使某些词语不正确,检测到(高精度)。该工具中的数字可以让您了解模型在应用程序中的执行方式,您可以尝试调整信号平均参数以调整它以提供所需的性能。要了解适用于您的应用程序的正确参数,您可以查看生成ROC曲线以帮助您了解权衡。

识别命令

流媒体精确度工具使用简单的解码器,该解码器包含在名为RecognizeCommands的小型C ++类中。这个类提供了随时间运行TensorFlow模型的输出,它对信号进行平均,并在有足够的证据认为已经找到识别的单词时返回有关标签的信息。实现相当小,只需跟踪最后几个预测并对它们进行平均即可,因此可以根据需要轻松移植到其他平台和语言。例如,在Android上的Java级别或在Raspberry Pi上的Python上执行类似的操作很方便。只要这些实现共享相同的逻辑,您就可以使用流测试工具调整控制平均的参数,然后将它们传送到您的应用程序以获得类似的结果。

高级操练

操练脚本的默认设计是为了在一个相对较小的文件中产生良好的端到端结果,但有很多选项可以更改以根据自己的要求自定义结果。

自定义操练数据

默认情况下,脚本将下载语音命令数据集,但您也可以提供自己的操练数据。要操练您自己的数据,您应该确保您至少有数百个您想识别的每个声音的录音,并按课程安排到文件夹中。例如,如果您试图识别猫咪的狗吠声,您可以创建一个名为的根文件夹animal_sounds,然后在两个名为barkand的子文件夹中miaow。然后,您会将音频文件组织到适当的文件夹中。

要将脚本指向新的音频文件,您需要设置--data_url=为禁止下载Speech Commands数据集,并--data_dir=/your/data/folder/查找刚创建的文件。

这些文件本身应该是16位小端PCM编码的WAVE格式。采样率默认为16,000,但只要所有音频始终保持相同的速率(该脚本不支持重新采样),您可以使用--sample_rate参数更改此参数。剪辑也应该大致相同。默认的预计持续时间是一秒钟,但您可以使用该--clip_duration_ms标志设置它。如果你在开始时有不同数量的无声片段,你可以看看字对齐工具来标准化它们(这里也是一种可以使用的快速和不太光彩的方法)。

需要注意的一个问题是,您的数据集中可能会有相同的声音重复,如果它们分布在您的操练,验证和测试集中,可能会产生误导性指标。例如,语音命令集让人们多次重复相同的单词。这些重复中的每一个都可能与其他人非常接近,所以如果操练过度并且记忆了一个,当它在测试集中看到非常相似的副本时,它可能表现得很不现实。为了避免这种危险,语音指令可以确保所有具有同一个单词的剪辑都被放入同一个分区。根据文件名的散列值将剪辑分配给操练,测试或验证集,以确保即使添加了新的剪辑,作业仍保持稳定,并避免任何操练样本迁移到其他集合中。为了确保给定发言者的所有单词都在同一个桶中,散列函数在计算赋值时忽略' nohash ' 后的文件名中的任何内容。这意味着如果你有文件名pete_nohash_0.wavpete_nohash_1.wav,保证它们在同一个集合中。

未知类

很可能您的应用程序会听到不在您的操练集中的声音,并且您希望该模型指示它在这些情况下不会识别噪音。为了帮助网络学习什么声音可以忽略,你需要提供一些不属于你的类的音频片段。要做到这一点,你需要创建quackoinkmoo子文件夹,并从您的用户可能会遇到其他动物的声音进行填充。--wanted_words脚本的参数定义了您关心的哪些类,在子文件夹名称中提到的所有其他_unknown_类将用于在操练期间填充类。Speech Commands数据集在其未知类中包含20个单词,包括零到九位数字和随机名称,如“Sheila”。

默认情况下,10%的操练样例是从未知类中挑选出来的,但是您可以用--unknown_percentage标志来控制它。增加这个数字会使得模型不太可能将未知单词误认为是想要的单词,但是将其过大可能会适得其反,因为模型可能会认为将所有单词分类为未知单词是最安全的!

背景噪音

即使在环境中发生其他不相关的声音时,真正的应用程序也必须识别音频。为了建立一个对这种干扰稳健的模型,我们需要对具有相似属性的录制音频进行操练。语音命令数据集中的文件被许多不同环境中的用户在各种设备上捕捉,而不是在工作室中捕捉,因此有助于增加培训的真实感。要添加更多,可以将随机环境音频段混合到操练输入中。在语音命令集中有一个特殊的文件夹_background_noise_,其中包含分钟长的WAVE文件,其中包含白噪声以及机器和日常家庭活动的录音。

随机选择这些文件的小片段,并在操练期间以低量混合到剪辑中。响度也是随机选择的,并由--background_volume参数控制,其中0表示无声,1表示全音量。并非所有剪辑都添加了背景,所以--background_frequency标志控制着混合的比例。

您自己的应用程序可能会在自己的环境中使用不同于这些默认值的背景噪声模式,因此您可以在_background_noise_文件夹中提供自己的音频剪辑。这些应该与您的主数据集采样率相同,但持续时间要长得多,以便可以从中选择一组好的随机分段。

无声

在大多数情况下,您关心的声音将是间歇性的,因此知道何时没有匹配的音频很重要。为了支持这一点,有一个特殊的_silence_标签,指示模型什么时候检测不到任何有趣的东西,因为在真实环境中从来没有完全无声,所以我们实际上必须提供安静和不相关音频的例子。为此,我们重复使用_background_noise_也混入到实际剪辑中的文件夹,拉取音频数据的短片段并将其输入到地面实况类别中_silence_。默认情况下,10%的操练数据是这样提供的,但是--silence_percentage可以用来控制比例。与未知的单词一样,设置这个较高的值可以减少模型结果,有利于保持无声的真实误差,但是会以牺牲文字的否定性为代价,但是过大的比例会导致它陷入总是猜测无声的陷阱。

时间转移

加入背景噪音是以真实方式扭曲操练数据的一种方式,以有效增加数据集的大小,从而提高总体准确性,时间偏移是另一种方式。这涉及操练样本数据的随机时间偏移,因此开始或结束的一小部分被截断并且相对部分被填充零。这模拟了操练数据中起始时间的自然变化,并且使用--time_shift_ms标志进行控制,该标志默认为100ms。增加此值将会提供更多的变化,但有可能会切断音频的重要部分。通过使用时间拉伸和音调缩放来增强数据与现实扭曲的相关方式,但这超出了本教程的范围。

定制化模型

该脚本使用的默认模型非常大,每次推断使用8亿多个FLOP并使用940,000个权重参数。这可以在台式机或现代手机上以可用的速度运行,但它涉及太多的计算,以便在具有更有限资源的设备上以交互式速度运行。为了支持这些用例,可以使用几种替代方法:

low_latency_conv基于卷积神经网络中关于小踪迹关键字点击论文中描述的'cnn-one-fstride4'拓扑。精确度略低于'conv',但重量参数的数量大致相同,并且只需要1100万FLOP来运行一次预测,使其更快。

要使用此模型,请--model_architecture=low_latency_conv在命令行中指定。您还需要更新操练速率和步骤数量,因此完整的命令将如下所示:

python tensorflow/examples/speech_commands/train \
--model_architecture=low_latency_conv \
--how_many_training_steps=20000,6000 \
--learning_rate=0.01,0.001

这就要求脚本以20,000步的学习速率进行操练,然后以小10倍的速度进行6,000步的微调。

low_latency_svdf基于压缩深度神经网络中使用等级约束拓扑论文中的拓扑结构。精确度也低于'conv',但它只使用了大约75万个参数,最重要的是,它允许在测试时间(即当您将在应用程序中实际使用它时)优化执行,从而产生750万个FLOP。

要使用此模型,请在命令行中指定--model_architecture=low_latency_svdf并更新操练速率和步数,因此完整的命令如下所示:

python tensorflow/examples/speech_commands/train \
--model_architecture=low_latency_svdf \
--how_many_training_steps=100000,35000 \
--learning_rate=0.01,0.005

请注意,尽管需要比前两种拓扑更多的步数,但计算数量的减少意味着操练应该大致相同,并且最终达到85%左右的精确度。您还可以通过更改SVDF图层中的这些参数来相当容易地进一步调整拓扑结构,以实现计算和准确性:

  • rank - 近似的等级(通常更高,但会导致更多计算)。
  • num_units - 与其他图层类型相似,指定图层中的节点数(更多节点的质量更好,计算更多)。

关于运行时,由于该层允许通过缓存一些内部神经网络激活来进行优化,因此在冻结图时以及在流模式下执行模型时都需要确保使用一致的步幅(例如'clip_stride_ms'标志) (如test_streaming_accuracy.cc)。

其他要自定义的参数如果要试验自定义模型,开始调整谱图创建参数是一个很好的开始。这具有将输入图像的大小改变为模型的效果,并且models.py中的创建代码将自动调整计算和权重的数量以适应不同的尺寸。如果你减小输入,模型将需要更少的计算来处理它,所以它可以是一个很好的方式来折衷一些准确性,以改善延迟。--window_stride_ms控制各频率分析样品分开和以前的距离。如果您增加此值,则在给定的持续时间内将采取更少的采样,并且输入的时间轴将缩短。--dct_coefficient_count标志控制使用多少桶进行频率计数,因此减小这个数值会缩小另一个维度的输入。--window_size_ms参数不影响大小,但并控制为用于为各样品计算频率的区域有多宽。--clip_duration_ms如果您正在寻找的声音很短,那么减少受其控制的操练样本的持续时间也会有帮助,因为这样也会缩短输入的时间维度。您需要确保所有操练数据在片段的最初部分都包含正确的音频。

如果你对于你的问题有一个完全不同的模型,你可能会发现你可以将它插入到models.py中,并让脚本的其余部分处理所有的预处理和操练和操练机制。您可以添加一个新的子句create_model,查找您的架构的名称,然后调用模型创建功能。这个函数被赋予了谱图输入的大小以及其他模型信息,并且预计会创建TensorFlow操作来读取并生成输出预测向量,以及占位符来控制丢失率。脚本的其余部分将把这个模型整合到一个更大的图中,进行输入计算并应用softmax和损失函数来操练它。

当您调整模型和操练超参数时,一个常见问题是由于数值精度问题,非数值可能会蔓延。通常情况下,您可以通过减少学习速率和权重初始化函数的大小来解决这些问题,但如果它们持续存在,则可以启用该--check_nans标记来追踪错误的来源。这将在TensorFlow中的大多数常规操作之间插入检查操作,并在遇到问题时使用有用的错误消息中止培训过程。

扫码关注腾讯云开发者

领取腾讯云代金券