使用Transformer进行语音分析实践

2023年10月10日 由 alex 发表 463 0

介绍


人的声音是一种令人难以置信的表达性沟通工具。而且这不仅仅是我们说的内容,还包括我们说话的方式。我们能够通过声音的语调听出一个人是悲伤还是快乐、男性还是女性,甚至有时候能够判断一个人是否健康。声音分析通过对声音录音使用机器学习(ML)来确定这些洞察结果。


语音分析已经成为许多领域中人工智能系统的重要组成部分:客户服务、医疗保健和学术研究。最重要的是,语音分析有助于构建更具同情心和更强大的基于ML的助手。


虽然语音分析的整个领域非常广泛,但有一些核心学科:情感分析以及性别和年龄检测。在本文中,我们展示了如何选择适合的预训练模型进行性别检测。在这个过程中,我们还强调了用于建模以及模型调试和比较的有用策略和开源工具。


在emodb数据集上进行性别检测


在本文中,我们使用emodb数据集。由于其小巧的大小和丰富的元数据,该数据集很容易使用。我们安装所需的依赖项:


!pip install renumics-spotlight sliceguard audb audeer audformat audonnx audinterface


然后,我们使用audb加载数据集,并通过将其加载到Spotlight中对其进行初步查看:


import os
from renumics import spotlight
import audb
import audeer
import audformat

cache_root = audeer.mkdir('cache')
db = audb.load('emodb', version='1.3.0', format='wav', mixdown=True, sampling_rate=16000,
    full_path=False, cache_root=cache_root)
df = audformat.utils.concat([
    db['files'].get(map={'speaker': ['age', 'gender']}),
    db['emotion'].get(),
])
df['age'] = df['age'].astype('int')
df = df[['age', 'gender', 'emotion']]
df['audio'] = db.root + os.path.sep + df.index
#visualize it and hear some samples
spotlight.show(df.reset_index())


由于交互式可视化,我们可以快速熟悉数据集:该数据集包含德国舞台演员所说的不同短语。演员以不同的情绪状态传达这些短语。每次表达都会提供演员的年龄和性别。


1


在下一步中,我们加载一个小型的预训练Transformer模型,从原始音频中预测每个说话者的性别。我们使用audonnx软件包提供的w2v2-L-robust-6-age-gender模型。该模型是一个简化版本的较大模型,该模型基于aGender、Mozilla Common Voice、Timit和Voxceleb 2数据集进行训练。我们下载了这个小型Transformer模型。


model_name = 'gender_age_small''gender_age_small'
model_url = 'https://zenodo.org/record/7761387/files/w2v2-L-robust-6-age-gender.25c844af-1.1.1.zip'
# def cache_model(model_root, cache_root, model_file, model_url):
def cache_model(model_name, model_url): 
    model_root = audeer.path(cache_root, model_name)
    dst_path = audeer.path(cache_root, f'{model_name}.zip')
    if not os.path.exists(dst_path):
        audeer.download_url(model_url, dst_path, verbose=True)
    if not os.path.exists(model_root):
        audeer.extract_archive(dst_path, model_root, verbose=True)
# cache_model(model_root, cache_root, model_file, model_url)
cache_model(model_name, model_url)


并对数据集进行推断:


import audonnx
import audinterface

def run_model(model_name, outputs):
    model_root = audeer.path(cache_root, model_name)
    model = audonnx.load(model_root)
    
    #handles pre-processing
    interface = audinterface.Feature(model.labels(outputs), process_func=model,
        process_func_args={'outputs': outputs, 'concat': True},
        sampling_rate=16000, resample=True, verbose=True)
    pred = interface.process_index(df.index, root=db.root,
        preserve_index=True, cache_root=model_root)
    return pred
outputs = ['logits_age', 'logits_gender', 'hidden_states']
pred = run_model(model_name, outputs)


现在我们可以看一下预测结果。由于audinterface类返回一个具有logits的数据帧,我们需要计算softmax概率和预测类别。我们还使用Spotlight中预先配置的布局,为我们的模型调试过程提供一个良好的起点。


from scipy.special import softmax
from renumics.spotlight.layouts import debug_classification

df['m1_probabilities'] = pred[['female', 'male', 'child']].apply(softmax, axis=1)
df['m1_gender_prediction'] = pred[['female', 'male', 'child']].idxmax(axis=1).astype("category")
df['m1_embedding'] = pred.values.tolist()
layout = debug_classification(label='gender', prediction='m1_gender_prediction', embedding='m1_embedding', inspect={'audio': spotlight.Audio}, features=['age', 'emotion'])
spotlight.show(df.reset_index(), layout=layout)


识别语音AI故障模式


当我们检查模型结果时(可用交互式数据集),我们发现准确率达到了95.7%。听起来还不错。然而,一旦我们使用混淆矩阵来深入分析数据,我们可以确定两个关键的数据切片:大部分失败发生在[女性,愤怒]切片中,而[男性,快乐]切片也很重要。具体来说,在[女性,愤怒]切片上,准确率下降到了71.6%。


2


在识别出这些切片后,我们通常在实际应用中有很多选项来改善我们的AI系统:我们可以收集更多与该切片相似的数据,我们可以改变特征或规范化,或者我们可以将我们的系统限制在不具有问题条件下运行。


然而,在语音AI的真实数据集中,要识别这样的切片比在emodb上要困难得多。有两个原因:通常有可用的元数据,但往往不清楚哪些元数据是有意义的。而且,表达性的元数据经常缺失。在这里,我们演示了两种可以在这些情况下帮助的策略:


自动切片检测算法可以快速识别数据切片的候选项。我们可以使用sliceguard在emodb上执行此操作:


from sliceguard import SliceGuard
from sklearn.metrics import accuracy_score
import numpy as np
sg = SliceGuard()
sg.find_issues(df, features=['emotion', 'age', 'gender'], y='gender', y_pred='m1_gender_prediction',
                        metric=accuracy_score, min_support=10, min_drop=0.15)
df_slices, issues, dtypes, sg_layout  = sg.report(no_browser=True)
spotlight.show(df.reset_index(), issues=issues, layout=layout)


Sliceguard识别到了8个数据片段。在检查这些数据片段时,我们发现了一些错误的发现,同时也确认了两个关键的数据片段[女性,愤怒]和[男性,幸福],这些我们已经知道。


实际上,将数据片段挖掘技术与交互式检查相结合,是快速发现数据模式和模型失败模式的强大方式。


根据emodb中关于年龄和情绪的元数据信息对于发现我们的数据片段非常有帮助。如果这些信息不可用怎么办?


一个选择是通过部署其他预训练模型来丰富数据。在我们的案例中,我们使用了一个预训练的情绪检测模型,并为每个数据样本计算情绪嵌入。


model_name = 'emotion''emotion'
model_url = 'https://zenodo.org/record/6221127/files/w2v2-L-robust-12.6bc4a7fd-1.1.0.zip'
cache_model(model_name, model_url)
outputs = ['hidden_states']
pred_emo = run_model(model_name, outputs)
df['emotion_embedding'] = pred_emo.values.tolist()


在我们手动检查嵌入之前,我们直接在嵌入上使用 slice guard 计算切片候选项。


sg = SliceGuard()

sg.find_issues(df, features=['embedding', 'gender'], y='gender', y_pred='m1_gender_prediction','embedding', 'gender'], y='gender', y_pred='m1_gender_prediction',
                        metric=accuracy_score, min_support=10, min_drop=0.15, precomputed_embeddings={'embedding': pred_emo.values})
df_slices, issues, dtypes, sg_layout  = sg.report(no_browser=True)
spotlight.show(df.reset_index(), issues=issues, layout=layout)


我们可以看到,嵌入式中识别出的聚类主要包含了[女性,愤怒]这个部分,但也有一些来自快乐和恐惧情绪的其他数据样本。正如预期的那样,基于嵌入的切片并不能清晰地区分元数据类别。然而,它仍然有助于理解模型的潜在失败模式。


3


管理模型大小和性能之间的权衡


语音分析的一些应用要求模型实时运行在边缘设备上。在这些用例中,模型的大小非常重要。建立较小模型有不同的策略可供选择:量化、修剪或知识蒸馏。其中一些技术可以结合使用,每种技术在准确性方面都有自己的权衡。


在这种情况下,一个关键任务是比较不同的模型,并找到大小和准确性之间的合适权衡。在这里,仅靠全局指标来判断模型的鲁棒性也是不够的。相反,比较必须在更细粒度的基于切片的层面上进行。此外,最佳权衡还严重依赖于用例的具体要求。这意味着,这些细粒度的发现必须与领域专家一起审查。


在这里,我们简要展示如何比较两个不同大小的模型在emodb上进行性别检测。我们下载并运行一个具有12层变压器模型进行性别检测的模型。


from renumics.spotlight.layouts import compare_classification
model_name = 'gender_age_big'
model_url = 'https://zenodo.org/record/7761387/files/w2v2-L-robust-24-age-gender.728d5a4c-1.1.1.zip'
cache_model(model_name, model_url)
outputs = ['logits_age', 'logits_gender', 'hidden_states']
pred = run_model(model_name, outputs)
df['m2_probabilities'] = pred[['female', 'male', 'child']].apply(softmax, axis=1)
df['m2_gender_prediction'] = pred[['female', 'male', 'child']].idxmax(axis=1)
df['m2_embedding'] = pred.values.tolist()
#compare models with Spotlight
layout = compare_classification(label='gender', model1_prediction='m1_gender_prediction', model1_embedding='m1_embedding', model1_correct='m1_correct', model2_prediction='m2_gender_prediction', model2_embedding='m2_embedding', model2_correct='m2_correct', inspect={'audio': spotlight.Audio})
spotlight.show(df.reset_index(), issues=issues, layout=layout)


对模型结果进行比较的一个非常简单的方法是查看模型错误的混淆矩阵(图4)。在本例中,我们可以看到较大的模型比较小的模型少犯错误。我们还可以看到,在[female,anger]片段中的错误从19个减少到4个,将准确度从71.6%提高到94%。


4


结论


我们展示了如何使用和分析预训练模型进行性别检测。这些以数据为中心的方法为自定义数据和经过微调的模型的实际应用提供了一个很好的起点。它们还可以推广到其他声音分析用例,例如情感分析。

文章来源:https://medium.com/itnext/hands-on-voice-analytics-with-transformers-dbfd923a5a79
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消