26

我想构建一个程序来检测用户的录音与另一个录音的接近程度,以纠正用户的发音。例如:

  1. 我录下自己说“早上好”
  2. 我让一个留学生录“早安”
  3. 比较他的录音和我的录音,看看他的发音是否足够好。

我在一些语言学习工具中看到了这一点(我相信 Rosetta Stone 会这样做),但它是如何完成的呢?请注意,我们只处理语音(而不是音乐)。我应该研究哪些算法或库?

4

8 回答 8

81

很多人似乎都在建议某种编辑距离,IMO 是一种完全错误的方法来确定两个语音模式的相似性,特别是对于像 OP 所暗示的那样短的模式。实际上,语音识别使用的特定算法几乎与您想在这里使用的相反。语音识别中的问题是将许多相似的发音解析为相同的表示。这里的问题是采用一些略有不同的发音并在它们之间获得某种有意义的距离。

我已经为大规模数据科学做了很多这样的事情,虽然我不能确切地评论专有程序是如何做到的,但我可以评论它在学术界是如何完成的,并提供一个简单的解决方案,并且会给您需要这种方法的强大功能和灵活性。

首先:假设您拥有的是一些音频,没有对其进行任何过滤。就像从麦克风中获取它一样。第一步是消除背景噪音。有许多不同的方法可以做到这一点,但我会假设你想要的东西可以很好地工作而不会非常难以实现。

  • 在此处使用 scipy 的过滤模块过滤音频。麦克风拾取的许多频率对于语音分类根本没有用处。我建议使用 Bessel 或 Butterworth 滤波器,以确保您的波形通过滤波得以保留。日常语音的基本频率通常在 800 到 2000 赫兹(参考)之间,因此合理的截止频率应该是 300 到 4000 赫兹,以确保您不会丢失任何东西。
  • 寻找语音中最不活跃的部分,并假设这是背景噪声的合理表示。此时,您将要对数据进行一系列傅立叶变换(或生成频谱图),并找到语音记录中具有最低平均频率响应的部分。获得该快照后,您应该从音频样本中的所有其他点中减去它。
  • 此时应该有一个音频文件,主要是您的用户的语音,并且应该准备好与另一个经过此过程的文件进行比较。现在,我们要实际剪辑声音并将此剪辑与某个主剪辑进行比较。

其次:你会想出两个语音模式之间的距离度量,有很多方法可以做到这一点,但我会假设我们有第一部分的输出和一些主文件经过类似处理。

  • 生成相关音频文件的频谱图(示例)。其输出最终将是一个可以表示为频率响应值的二维数组的图像。频谱图本质上是随时间变化的傅立叶变换,其中颜色对应于强度。

  • 使用 OpenCV(具有 python 绑定,示例)在您的频谱图上运行 blob 检测。实际上,这将在你的频谱图中间寻找大的彩色斑点,并给你一些限制。实际上,这应该做的是返回原始二维数组的一个明显更稀疏的版本,该版本仅代表所讨论的语音。(假设您的音频文件在录音的前端和后端会有一些尾随内容)

  • 规范化这两个 blob 以说明语速的差异。每个人都以不同的速度说话,因此您的 blob 沿 x 轴(时间)可能会有不同的大小。这最终将在您的算法中引入您不希望语音速度的检查级别。如果您还想确保他们以与主副本相同的速度说话,则不需要此步骤,但我建议您这样做。基本上,您想通过将时间轴乘以某个常数来扩展较短的版本,该常数只是两个 blob 长度的比率。

  • 您还应该根据最大和最小强度对两个 blob 进行归一化,以考虑以不同音量说话的人。同样,这取决于您的判断,但要解决此问题,您应该为您拥有的总强度跨度以及两个记录的最大强度找到相似的比率,并确保这两个值在您的二维阵列之间匹配.

第三:既然你有代表你的两个语音事件的二维数组,理论上应该包含它们的所有有用信息,是时候直接比较它们了。幸运的是,比较两个矩阵是一个很好解决的问题,并且有很多方法可以推进。

  • 就我个人而言,我建议使用诸如余弦相似度之类的指标来确定两个 blob 之间的差异,但这不是唯一的解决方案,虽然它可以为您提供快速验证,但您可以做得更好。

  • 您可以尝试从另一个矩阵中减去一个矩阵,并评估它们之间有多少差异,这可能比简单的余弦距离更准确。

  • 这可能有点矫枉过正,但您可以假设某些语音区域或多或少对于评估 blob 之间的差异很重要(如果有人使用长 i 而不是短 i 可能无关紧要,但 ag 而不是 ak 可能是完全不同的词)。对于类似的事情,您需要在上一步中为差异数组开发一个掩码,并将所有值乘以该值。

  • 无论您选择哪种方法,您现在都可以简单地设置一些差异阈值,并确保两个 blob 之间的差异低于您想要的阈值。如果是,则捕获的语音足够相似以至于正确。否则让他们再试一次。

我希望这会有所帮助,而且我不能向您保证这是公司使用的确切算法,因为该信息是高度专有的并且不对公众开放,但我可以向您保证类似于这些的方法用于学术界最好的论文,并且这些方法将使您在准确性和易于实施之间取得很好的平衡。如果您有任何问题,请告诉我,祝您在未来的数据科学开发中好运!

于 2013-06-19T16:40:51.670 回答
6

musicg api https://code.google.com/p/musicg/ 有一个音频指纹生成器和记分器以及源代码来显示它是如何完成的。

我认为它会在每条轨道中寻找最相似的点,然后根据它可以匹配的距离来评分。

它可能看起来像

import com.musicg.wave.Wave
   com.musicg.fingerprint.FingerprintSimilarity
   com.musicg.fingerprint.FingerprintSimilarityComputer
   com.musicg.fingerprint.FingerprintManager

double score =
new FingerprintsSimilarity(
    new Wave("voice1.wav").getFingerprint(),
    new Wave("voice2.wav").getFingerprint() ).getSimilarity();
于 2013-06-13T18:03:15.017 回答
6

想法: 生物技术人员排列两个蛋白质序列的方式如下:每个序列在字母表上表示为一个字符串(A/C/G/T - 这些是不同类型的蛋白质,与我们无关),其中每个字母(这里,一个条目)代表一个特定的氨基酸。对齐的质量(其分数)是根据每对对应条目的相似性以及需要插入以产生对齐的空白条目的数量和长度来计算的。

相同的算法 ( http://en.wikipedia.org/wiki/Needleman-Wunsch_algorithm ) 可用于发音,来自一组替代发音中的替换频率。然后你可以计算对齐分数,以一种对音素差异敏感的方式来衡量两个发音之间的相似性。此处可以使用的相似性度量是 Levenshtein 距离、音素错误率和单词错误率。

算法 将一个序列转换为另一个序列所需的最小插入、删除和替换次数是 Levenshtein 距离。更多信息请访问http://php.net/manual/en/function.levenshtein.php 音素错误率 (PER) 是预测发音与参考发音之间的 Levenshtein 距离除以参考发音中的音素数。单词错误率(WER)是预测的至少有一个音素错误的发音占发音总数的比例。

资料来源:在威斯康星大学麦迪逊分校实习过

于 2013-06-14T02:17:14.747 回答
2

精心配置的Levenshtein 距离应该可以解决问题。

于 2013-06-14T18:32:52.687 回答
2

我知道这个问题已经过时了,但是......

为了解决类似的问题,我使用 Google Speech Recognized API 来检查所说的内容,并通过视觉比较音量变化的缩放波形来检测节奏的差异。

结果的代码视频

于 2018-09-06T19:13:25.457 回答
1

您可以按照 roy zhang 的建议使用 Musicg https://code.google.com/p/musicg/ 。在 android 中,只需在您的 android 项目中包含 musicg jar 文件并使用它。一个经过测试的例子:

import com.musicg.wave.Wave;
import com.musicg.fingerprint.FingerprintSimilarity;


        //somewhere in your code add
        String file1 = Environment.getExternalStorageDirectory().getAbsolutePath();
        file1 += "/test.wav";

        String file2 = Environment.getExternalStorageDirectory().getAbsolutePath();
        file2 += "/test.wav";

        Wave w1 = new Wave(file1);
        Wave w2 = new Wave(file2);


        FingerprintSimilarity fps = w1.getFingerprintSimilarity(w2);
        float score = fps.getScore();
        float sim = fps.getSimilarity();



        Log.d("score", score+"");
        Log.d("similarities", sim+"");

祝你好运

于 2014-06-12T02:33:04.567 回答
0

您必须研究语音识别算法。我知道您不需要将语音翻译为文本(由语音识别算法完成),但是,在您的情况下,许多算法都是相同的。

可能,HMM 在这里会有所帮助(隐藏马尔可夫模型)。也看看这里:http ://htk.eng.cam.ac.uk/

于 2013-06-09T14:22:05.620 回答
0

如果这只是为了检查发音[当然有不同的口音],你可以这样做:

步骤1:使用一些语音工具[说龙听写],您可以随身携带文字。

第 2 步:比较字符串或形成的单词,并将其与实际要发音的字符串进行比较。

第 3 步:如果您发现字符串中有任何差异,则表示该单词拼写不正确。你可以建议正确的发音。

于 2013-06-17T10:47:49.450 回答