我想开发一个程序来检测用户的音频录音与另一个录音的接近程度,以便纠正用户的发音。例如:
- 我录制自己说“Good morning”
- 让一个外国学生录制“Good morning”
- 将他的录音与我的录音进行比较,看看他的发音是否足够好。
我在一些语言学习工具中见过这种功能(我认为Rosetta Stone有这样的功能),但这是如何实现的呢?请注意我们只处理语音(而不是音乐)。我应该研究哪些算法或库呢?
回答:
很多人似乎建议使用某种编辑距离,但我认为这完全不是判断两个语音模式相似性的正确方法,特别是对于像提问者暗示的这么短的模式。实际上,语音识别中使用的具体算法几乎与你在这里想要使用的算法相反。语音识别的难题是将许多相似的发音解析为同一个表示。而这里的问题是将一些略有不同的发音进行比较,并得到它们之间的某种有意义的距离。
我在大规模数据科学领域做了很多这类工作,虽然我不能具体评论专有程序是如何做的,但我可以谈谈学术界是如何做的,并提供一个简单直接的解决方案,这个方案将为你提供你想要的这种方法的强大和灵活性。
首先:假设你有一段未经任何过滤的音频片段,就像从麦克风获取的那样。第一步是消除背景噪音。有许多不同的方法可以做到这一点,但我假设你想要的是一种效果好且不难实现的方法。
- 使用scipy的过滤模块这里来过滤音频。麦克风会拾取很多对分类语音无用的频率。我建议使用贝塞尔或巴特沃斯滤波器,以确保你的波形在过滤过程中得以保留。日常语音的基本频率通常在800到2000赫兹之间(参考),所以一个合理的截止频率可能是300到4000赫兹,以确保不丢失任何内容。
- 寻找语音中最不活跃的部分,并假设这是背景噪音的合理表示。此时,你需要对你的数据进行一系列的傅里叶变换(或生成频谱图),并找到语音录音中平均频率响应最低的部分。一旦你有了这个快照,你应该从音频样本中的所有其他点中减去它。
- 此时,你应该得到一个主要是用户语音的音频文件,并且已经准备好与经过同样处理的另一个文件进行比较。现在,我们实际上要剪辑声音,并将这个剪辑与某个主剪辑进行比较。
其次:你需要为两个语音模式设计一个距离度量,有很多方法可以做到这一点,但我假设我们有第一部分的输出和经过类似处理的某个主文件。
-
生成所讨论音频文件的频谱图(示例)。这个输出的最终结果将是一个可以表示为频率响应值的二维数组的图像。频谱图本质上是随时间进行的傅里叶变换,其中颜色对应于强度。
-
使用OpenCV(有Python绑定,示例)对你的频谱图进行斑点检测。这实际上将寻找频谱图中间的大彩色斑点,并为你提供一些限制。实际上,这应该会返回一个更加稀疏的原始二维数组的版本,仅代表所讨论的语音。(假设你的音频文件在录音的前后端有一些拖尾内容)
-
为了适应语速的差异,标准化两个斑点。每个人说话的速度不同,因此你的斑点在x轴(时间)上可能会有不同的尺寸。这最终会在你的算法中引入一级检查,你不希望因为语速而影响速度。如果你也希望确保他们与主拷贝的语速相同,则不需要这一步,但我建议这样做。基本上,你想通过将时间轴乘以某个常数来拉长较短的版本,这个常数只是你两个斑点长度的比率。
-
你还应该根据最大和最小强度来标准化两个斑点,以适应不同音量的说话者。这同样取决于你的判断,但要解决这个问题,你应该找到你拥有的总强度范围的类似比率,以及两个录音的最大强度,并确保这两个值在你的二维数组之间匹配。
第三:现在你有了代表两个语音事件的二维数组,理论上应该包含它们的所有有用信息,是时候直接比较它们了。幸运的是,比较两个矩阵是一个已经解决得很好的问题,有很多方法可以继续前进。
-
我个人建议使用像余弦相似度这样的度量来确定两个斑点之间的差异,但这不是唯一的解决方案,虽然它会给你一个快速的验证,但你可以做得更好。
-
你可以尝试从一个矩阵中减去另一个矩阵,并得到它们之间差异的评估,这可能会比简单的余弦距离更准确。
-
这可能有点过头,但你可以假设在评估斑点之间的差异时,某些语音区域更重要或不太重要(如果某人使用长i而不是短i可能无关紧要,但使用g而不是k可能完全是不同的词)。对于这种情况,你需要为前一步的差异数组开发一个掩码,并将所有值乘以该掩码。
-
无论你选择哪种方法,你现在可以简单地设置某个差异阈值,并确保两个斑点之间的差异低于你期望的阈值。如果是,捕获的语音足够相似,可以认为是正确的。否则,让他们再试一次。
希望这对你有帮助,再次强调,我不能保证这是公司使用的精确算法,因为这些信息是高度专有的,不对公众开放,但我可以保证,学术界最好的论文中使用了类似的方法,这些方法将为你提供准确性和实现便利性的良好平衡。如果你有任何问题,请告诉我,祝你在未来的数据科学探索中好运!