优秀的编程知识分享平台

网站首页 > 技术文章 正文

人工智能算法:语音识别实践教程

nanyue 2024-11-24 19:42:44 技术文章 1 ℃

语音相关知识点梳理

语音不像文本,可以看得见,仅有对应的音频,需要对语音有一个“可以看见”的过程,于是有了下列的几种音频文件的表示方法:

1)波形图

语音的保存形式可用波形图展现,可以看作是上下摆动的数字序列,每一秒的音频用16000个电压数值表示,采样率即为16kHz。



2)采样点

采样点是对波形图的放大,可以看到的更细的单位



3)频谱图

可以变为频谱图,颜色代表频带能量大小,语音的傅立叶变换是按帧进行,短的窗口有着高时域和低频域,长时窗口有低时域和高频域

4)基本单位

对于语音而言,基本单位是帧(对应文本的token),一帧即是一个向量,整条语音可以表示为以帧为单位的向量组。帧是由ASR的前端声学特征提取模块产生,提取的技术设计“离散傅立叶变换”和”梅尔滤波器组“

整体解决思路

在我的理解认知中,对于ASR的解决方法可以分为两种,一种是声学模型加语言模型的组合,另外一种是端到端的解决方式。

第一种方式:

路线的个人理解大约是,有一个音频,先有声学模型,将对应的音频信号处理为对应的声学特征,再有语言模型,将声学特征的结果得到概率最大的输出字符串。

在上图中,代表的是声学特征向量,代表输出的文本序列,在(2.1)中,代表的是声学模型,代表的是语言模型

第二种方式:

端到端的解决手段,个人印象中在吴恩达的课程里提到,ASR在CTC提出后有一个较大的提升。个人理解是在CTC之前,seq2seq的建模方式比较难处理输出序列远短于输入序列的情况,以及在不同帧出现的相同音素的输出

其他术语

声学模型:常用的话,包括了HMM,GMM,DNN-HM的声学模型。

语言模型:常用的语言模型这里包括了n-gram语言模型以及RNN的语言模型。

解码器:最终目的是取得最大概率的字符输出,解码本质上是一个搜索问题,并可借助加权有限状态转换器(Weighted Finite State Transducer,WFST) 统一进行最优路径搜索。

端到端的方法:seq2seq+CTC 损失函数, RNN Transducer, Transforme,这里需要补充的话 应该要去看李宏毅2020年的人类语言处理的课程。

完整实践代码

本代码已经部署到天池DSW实验平台上,可直接免配置环境运行,对于DSW不熟悉的学习者可参考:小白如何用免费GPU跑天池算法大赛!

赛题介绍: 有20种不同食物的咀嚼声音,给出对应的音频,对声音的数据进行建模,判断是哪种食物的咀嚼声音

Baseline思路:将对应的音频文件,使用librosa转化为梅尔谱作为输入的特征,用CNN对梅尔谱的特征进行建模分类预测。

Baseline地址:https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.3.78ac14e9eP5wk4&postId=198902

环境要求:TensorFlow的版本:2.0 + keras sklearn librosa


#基本库
import pandas as pd 
import numpy as np
from sklearn.model_selection import train_test_split #数据分割
from sklearn.metrics import classification_report #评价
from sklearn.model_selection import GridSearchCV #网格搜索,最优模型选择
from sklearn.preprocessing import MinMaxScaler #特征标准化

# 加载深度学习框架
# 搭建分类模型所需要的库
from tensorflow.keras.models import Sequential 
from tensorflow.keras.layers import Conv2D, Flatten, Dense, MaxPool2D, Dropout
from tensorflow.keras.utils import to_categorical
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC

# 其他库
#音频处理
import librosa
import librosa.display
#文件处理
import glob
import os

0.特征提取与数据集建立

feature = []
label = []
# 建立类别标签,不同类别对应不同的数字。
label_dict = {'aloe': 0, 'burger': 1, 'cabbage': 2,'candied_fruits':3, 'carrots': 4, 'chips':5,'chocolate': 6, 'drinks': 7, 'fries': 8, 'grapes': 9, 'gummies': 10, 'ice-cream':11,'jelly': 12, 'noodles': 13, 'pickles': 14, 'pizza': 15, 'ribs': 16,'salmon':17,'soup': 18, 'wings': 19}
# 不同数字对应不同的类别。
label_dict_inv = {v:k for k,v in label_dict.items()}


#特征提取
from tqdm import tqdm #提示进度条
def extract_features(parent_dir, sub_dirs, max_file=10, file_ext="*.wav"):#提取特征以及标签
    c = 0
    label, feature = [], []
    for sub_dir in sub_dirs:
        for fn in tqdm(glob.glob(os.path.join(parent_dir, sub_dir, file_ext))[:max_file]): # 遍历每个数据集的所有文件
            label_name = fn.split('/')[-2]
            label.extend([label_dict[label_name]])
            X, sample_rate = librosa.load(fn,res_type='kaiser_fast')
            mels = np.mean(librosa.feature.melspectrogram(y=X,sr=sample_rate).T,axis=0) #计算梅尔频谱(mel spectrogram),并把它作为特征
            feature.extend([mels])

    return [feature, label]


parent_dir = './train_sample/'
save_dir = "./"
folds = sub_dirs = np.array(['aloe','burger','cabbage','candied_fruits',
                             'carrots','chips','chocolate','drinks','fries',
                            'grapes','gummies','ice-cream','jelly','noodles','pickles',
                            'pizza','ribs','salmon','soup','wings'])


# 获取特征feature以及类别的label
temp = extract_features(parent_dir,sub_dirs,max_file=100)
temp = np.array(temp)#列表转换成矩阵
data = temp.transpose()#矩阵转置
# 获取特征
X = np.vstack(data[:, 0])
#X的特征尺寸是: (1000, 128)


# 获取标签
Y = np.array(data[:, 1])
#Y的特征尺寸是: (1000,)
#数据集划分
#训练集的大小 750
#测试集的大小 250
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state = 1, stratify=Y)
#random_state 是随机数的种子。

1.建立模型


model = Sequential()#多个网络层的线性堆叠
# 输入的大小
input_dim = (16, 8, 1)
model.add(Conv2D(64, (3, 3), padding = "same", activation = "tanh", input_shape = input_dim))# 卷积层
# 输入shape
model.add(MaxPool2D(pool_size=(2, 2)))# 最大池化


model.add(Conv2D(128, (3, 3), padding = "same", activation = "tanh")) #卷积层
model.add(MaxPool2D(pool_size=(2, 2))) # 最大池化层
model.add(Dropout(0.1))
#为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时按一定概率(rate)随机断开输入神经元,Dropout层用于防止过拟合。
model.add(Flatten()) # 展开
model.add(Dense(1024, activation = "tanh"))
model.add(Dense(20, activation = "softmax")) # 输出层:20个units输出20个类的概率
# 编译模型,设置损失函数,优化方法以及评价标准
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy’])


model.summary()
# 训练模型
model.fit(X_train, Y_train, epochs = 20, batch_size = 15, validation_data = (X_test, Y_test))

2. 预测


#提取测试集数据特征
def extract_features(test_dir, file_ext="*.wav"):
    feature = []
    for fn in tqdm(glob.glob(os.path.join(test_dir, file_ext))[:]): # 遍历数据集的所有文件
        X, sample_rate = librosa.load(fn,res_type='kaiser_fast')
        mels = np.mean(librosa.feature.melspectrogram(y=X,sr=sample_rate).T,axis=0) # 计算梅尔频谱(mel spectrogram),并把它作为特征
        feature.extend([mels])
    return feature

X_test = extract_features('./test_a/‘)

#输出测试结果
preds = np.argmax(predictions, axis = 1)
preds = [label_dict_inv[x] for x in preds]


path = glob.glob('./test_a/*.wav')
result = pd.DataFrame({'name':path, 'label': preds})

result['name'] = result['name'].apply(lambda x: x.split('/')[-1])
result.to_csv('submit.csv',index=None)
最近发表
标签列表