語音相關知識點梳理
語音不像文字,可以看得見,僅有對應的音訊,需要對語音有一個“可以看見”的過程,於是有了下列的幾種音訊檔案的表示方法:
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)