摘要
本項目的目標是使用深度學習來檢測數字 0-9 的語音。
我們將利用深度學習技術,把語音文件轉譯為文本數據,例如把英語 three 的發音轉譯為文本 "three"。
我們將使用 TensorFlow/Keras 來創建模型、訓練模型,再使用測試數據評估它的性能。
本教程的 Jupyter 文件地址:
https://openbayes.com/console/public/tutorials/Y9mnsBbqufM
本教程的視頻地址:
https://www.bilibili.com/video/BV16n4y197zD
運行環境
因為 AI 需要在 GPU 上運算,而一般的 PC 是沒有 GPU 的,所以我們要借用雲端的算力。
我選擇的雲端算力平台是 OpenBayes,你使用下面的鏈接進行註冊,將可以獲得免費 4 小時 RTX-4090 顯卡的使用時長。
註冊鏈接如下:
https://openbayes.com/console/signup?r=comehope_JrJj
點擊它,會出現下面的登錄界面,填寫用户名、郵箱、密碼、手機號和短信驗證碼,就可以完成註冊,如下圖所示。
克隆教程
註冊成功之後,進入到個人控制枱,在左側菜單中選擇“公共資源/公共教程”,搜索“0-9”,找到“語音識別入門教程:用 TensorFlow 識別數字 0-9”這篇教程,如下圖所示。
打開這篇教程,點擊右上角的“克隆”按鈕,如下圖所示。
在接下來“從模板創建:基本信息”的界面中點擊“下一步:選擇算力”按鈕,如下圖所示。
所謂算力,就是顯卡類型,請選擇“RTX 4090”,這裏會顯示您獲贈的時長。再選擇鏡像類型中,選擇“TensorFlow”,然後,顯示右下角的“下一步:審核”按鈕,如下圖所示。
接下來列出了剛才選擇過的算力和鏡像,點擊右下角的“繼續執行”按鈕,如下圖所示。
接下來,系統會自動分配資源,稍待片刻,等到頁面中顯示“打開工作空間”按鈕,點擊它,就可以進入運行中的教程了,如下圖所示。
工作空間的界面如下圖所示。
接下來,運行“教程.jpynb”文件即可。以下內容與 “教程.jpynb” 的內容完全相同,建議您在 Jupyter 中運行,可以實時看到運行結果。
教程正文
先安裝庫文件,所有依賴都保存在 requirements.txt 中,其中主要是 librosa 庫,是一個提取語音特徵的庫。
!pip install -r /openbayes/home/requirements.txt -q
數據集包含 1700 個語音文件,錄製了多個人的英語數字發音,每個錄音大約 1 秒到 2 秒時長。
數據集已經下載到 data 文件夾中。
接下來我們開始正式編碼。
首先,定義數據集的文件夾路徑,每個數字的語音都保存在以它的英語單詞命名的文件夾中。
file_paths = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
'5': 'five',
'6': 'six',
'7': 'seven',
'8': 'eight',
'9': 'nine'
}
初始化 2 個列表 audio_data 和 labels,用於保存音頻數據和音頻對應的標籤。
audio_data = []
labels = []
使用 librosa 庫提取語音特徵,即所有音頻文件的 MFCC 特徵,並且使幀長固定為 40。MFCC(梅爾頻率倒譜系數)適合描繪人類的語音特徵。
使用 librosa 庫提取語音特徵,即所有音頻文件的 MFCC 特徵,並且使幀長固定為 40。MFCC(梅爾頻率倒譜系數)適合描繪人類的語音特徵。
import os
import numpy as np
n_frames = 40
for label, file_path in file_paths.items():
print("processing: ", label, file_path)
for file_name in os.listdir('data/' + file_path):
file_path_full = os.path.join('data/' + file_path, file_name)
audio, sr = librosa.load(file_path_full, sr=16000)
mfccs = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=n_frames)
if mfccs.shape[1] < n_frames:
mfccs = np.pad(mfccs, ((0, 0), (0, n_frames - mfccs.shape[1])), mode='constant')
else:
mfccs = mfccs[:, :n_frames]
mfccs_normalized = (mfccs - np.mean(mfccs)) / np.std(mfccs)
mfccs_reshaped = mfccs_normalized.reshape((mfccs_normalized.shape[0], mfccs_normalized.shape[1], 1))
audio_data.append(mfccs_reshaped)
labels.append(label)
將之前定義的列表 audio_data 和 labels 轉換成 numpy 數組,便於後續進行機器學習。
並且提高數據的精度,把數據類型轉為 float64。
audio_data = np.array(audio_data, dtype=object)
labels = np.array(labels)
audio_data = audio_data.astype('float64')
labels = labels.astype('float64')
將 labels 列表轉換成獨熱編碼(one-hot encoding)。獨熱編碼用於在分類任務中對數據進行預處理,本項目要處理的問題可以看作是一個對語音進行分類的問題,數字 0-9 一共是 10 個分類。
from tensorflow import keras
labels_one_hot = keras.utils.to_categorical(labels)
把數據分為訓練集和測試集,測試集佔比 20%。其中,X_train 和 y_train 是訓練集的特徵數據和標籤數據,X_test 和 y_test 是測試集的特徵數據和標籤數據。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(audio_data, labels_one_hot, test_size=0.2, random_state=42)
構建卷積神經網絡模型,模型一共 9 層,第一層的輸出會成為下一層的輸入。
先是一個二維卷積層,接着是一個二維最大池化層。接下來的幾層重複了卷積和池化的過程,卷積核的數量從 32 增加到 64,再到 128,這有助於模型學習更復雜的特徵。
再接下來將二維的輸入一維化,交給下一個全連接層處理,使用 relu 激活函數。
最後一層也是全連接層,它有10個神經元,對應於10個分類類別,使用 softmax 激活函數,將輸出轉換為概率分佈。
model = keras.models.Sequential([
keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=X_train.shape[1:]),
keras.layers.MaxPooling2D((2,2)),
keras.layers.Conv2D(64, (3,3), activation='relu'),
keras.layers.MaxPooling2D((2,2)),
keras.layers.Conv2D(128, (3,3), activation='relu'),
keras.layers.MaxPooling2D((2,2)),
keras.layers.Flatten(),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dense(10, activation='softmax')
])
編譯模型。優化器為 adam,這是一種流行的梯度下降優化算法;損失函數為與獨熱編碼匹配的 categorical_crossentropy;評估指標只使用準確率 accuracy。
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
定義 2 個回調函數,用於在模型訓練過程中應用特定的策略,ReduceLROnPlateau 用於調整學習率,EarlyStopping 用於防止過擬合。
from tensorflow.keras.callbacks import ReduceLROnPlateau
from tensorflow.keras.callbacks import EarlyStopping
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=3, verbose=1, min_lr=0.0001)
early_stop = EarlyStopping(monitor='val_loss', patience=5, verbose=1)
定義數據生成器。儘管我們處理的是音頻而不是圖像,但此前已經將音頻特徵轉換為二維圖像數據,所以使用圖像數據增強工具來提高模型的泛化能力。
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
width_shift_range=0.1,
height_shift_range=0.1,
zoom_range=0.1,
shear_range=0.1,
rotation_range=10
)
接下來開始訓練模型。
訓練中使用了數據增強和回調函數來優化訓練過程,一共訓練 30 輪。
訓練過程中的所有信息記錄在 history 變量中,後面我們可以用它來查看訓練過程中的性能變化。
在 RTX 4090 顯卡上,訓練大約需要 3 分鐘。
history = model.fit(
datagen.flow(X_train, y_train, batch_size=32),
epochs=30,
validation_data=(X_test, y_test),
steps_per_epoch=len(X_train) // 32,
callbacks=[reduce_lr, early_stop]
)
在測試集上進行性能評估,並查看準確率。
_, test_accuracy = model.evaluate(X_test, y_test)
print('Test accuracy:', test_accuracy)
準確率大約 95%。
再把訓練過程中關鍵指標的變化數據繪製成圖看一看。
import matplotlib.pyplot as plt
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
可以看到準確率一直在提升,而損失值一直在下降,這説明我們的訓練是有效的。
現在,我們可以用自己錄製的音頻來實測一下效果了。
定義一個函數 predict() 實現語音到文字的轉譯,它先提取出音頻文件的特徵,然後調用模型進行預測,返回概率最高的分類。
def predict(audio, sr):
mfccs = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=n_frames)
if mfccs.shape[1] < n_frames:
mfccs = np.pad(mfccs, ((0, 0), (0, n_frames - mfccs.shape[1])), mode='constant')
else:
mfccs = mfccs[:, :n_frames]
mfccs_normalized = (mfccs - np.mean(mfccs)) / np.std(mfccs)
mfccs_reshaped = mfccs_normalized.reshape((1, mfccs_normalized.shape[0], mfccs_normalized.shape[1], 1))
predictions = model.predict(mfccs_reshaped)
predicted_class = np.argmax(predictions)
return 'Predicted class: ' + str(predicted_class)
我一共錄製了 3 小段音頻,下面是測試其中 1 段音頻的代碼。
audio, sr = librosa.load('audio1.wav', sr=16000)
predict(audio, sr)
可以看到,打印出的結果和讀音是一致的。
是不是很有意思?
你也來試試吧!
記得使用我的註冊鏈接哦,可以獲得免費 4 小時 4090 時長呢~
https://openbayes.com/console/signup?r=comehope_JrJj