完整版請微信關注「大數據技術宅」python
序言:語音識別做爲人工智能領域重要研究方向,近幾年發展迅猛,其中RNN的貢獻尤其突出。RNN設計的目的就是讓神經網絡能夠處理序列化的數據。本文筆者將陪同小夥伴們一起踏上語音識別之夢幻旅途,相信此處風景獨好。git
環境準備微信
RNN與LSTM介紹RNNLSTM語音識別介紹聲學特徵提取聲學特徵轉換成音素(聲學模型)音素轉文本(語言模型+解碼)語音識別簡單實現提取WAV文件中特徵將WAV文件對應的文本文件轉換成音素分類定義雙向LSTM網絡
模型訓練和測試dom
一、win10
二、python3.6.4
三、pip3
四、tensorflow1.12.0
(在運行代碼的時候若是顯示缺乏python模塊,直接用pip3安裝便可)機器學習
循環神經網絡(RNN)是神經網絡模型中的一種,其中部分神經元的鏈接組成了有向環,有向環使得RNN中出現了內部狀態或帶記憶的結構,賦予了RNN對動態序列進行建模的能力。在接下來的兩小節中筆者將詳細的介紹一下RNN,以及RNN的變種長短時間記憶(Long Short Term Memory,LSTM)網絡。分佈式
圖1中描繪了一個簡單循環神經網絡,叫作Elman網絡,一共包含三層:輸入層(input)、隱藏層(hidden)以及輸出層(output)。context unit用來存儲上一次的隱藏層的值,與下一次的輸入一塊兒輸入到隱藏層(圖中實線表示直接複製,虛線表示須要經過學習得到)。Elman網絡和喬丹網絡是循環神經網絡中最簡單的形態,本文只介紹Elman網絡,感興趣的讀者可自行查閱喬丹網絡。函數
圖1 Elman網絡學習
Elman網絡的數學表達式以下:
假設:測試
x(t):在t時間點的輸入向量;
h(t):在t時間點的隱藏向量;
y(t):在t時間點的輸出向量;
W、U和b:參數矩陣;
sigma(h)和sigma(y):激活函數。
隱藏層向量和輸出層向量能夠表示爲:
長短時間記憶(Long Short Term Memory,LSTM)是RNN的一種,最先由Hochreiter和Schmidhuber(1977)年提出,該模型克服了一下RNN的不足,經過刻意的設計來避免長期依賴的問題。如今不少大公司的翻譯和語音識別技術核心都以LSTM爲主。下邊就詳細的介紹一下LSTM的構成。圖2描繪了LSTM單元的結構。
圖2 LSTM單元結構
爲了不RNN中梯度消失和梯度爆炸的問題,LSTM相對於普通RNN單元有比較大的區別,主要的核心思想是:
①採用叫「細胞狀態」(state)的通道貫穿整個時間序列,如圖3中從C(t-1)到C(t),這條線上只有乘法操做和加法操做。
②經過設計「門」的結構來去除或者增長信息到細胞狀態,LSML中有三個門,分別是「忘記門」、「輸入門」和「輸出門」。
圖3 「細胞狀態」通道示意圖
下邊詳細闡述一下LSML中的三個門。
(1)忘記門
圖4中紅色加粗部分爲LSML單元中「忘記門」的位置,「忘記門」決定以前狀態中的信息有多少應該捨棄。它會輸出一個0和1之間的數,表明C(t-1)中保留的部分。「忘記門」的計算公式以下:
「忘記門」的輸入是x(t),上一時刻的隱藏層輸出h(t-1)、W(f)和U(f)是「忘記門的參數」,須要經過訓練獲取。
圖4 「忘記門」
(2)輸入門
圖5中紅色加粗部分爲「輸入門」,輸入門決定什麼樣的輸入信息應該保留在「細胞狀態」C(t)中。它會讀取h(t-1)和x(t)的內容,其計算公式爲:
其中輸入是h(t-1)和x(t),W(i)、U(i)、W(c)、U(c)是要訓練的參數。
圖5 「輸入門」
接下來,研究一下「細胞狀態」的值是如何更新的。首先通過忘記門,算出舊的細胞狀態中有多少被遺棄,接着輸入門將所得的結果加入到細胞狀態,表示新的輸入信息中有多少加入到細胞狀態中。計算公式以下:
細胞狀態的更新過程如圖6所示:
圖6 「細胞狀態」更新
(3) 輸出門
在細胞狀態更新以後,將會基於細胞狀態計算輸出。首先輸入數據h(t-1)和x(t),經過sigmoid激活函數獲得「輸出門」的值。而後,把細胞狀態通過tanh處理,並與輸出門的值相乘獲得細胞的輸出結果。輸出門的公式以下:
輸出門的計算流程如圖7紅色加粗部分所示:
圖7 「輸出門」
語音識別的最主要過程是:(1)從聲音波形中提取聲學特徵;(2)將聲學特徵轉換成發音的因素;(3)使用語言模型等解碼技術轉變成咱們能讀懂的文本。語音識別系統的典型結構如圖8所示:
圖8 語音識別結構
聲音實際上一種波,原始的音頻文件叫WAV文件,WAV文件中存儲的除了一個文件頭之外,就是聲音波形的一個個點。如圖9所示:
圖9 聲音波形示意圖
要對聲音進行分析,首先對聲音進行分幀,把聲音切分紅不少小的片斷,幀與幀之間有必定的交疊,如圖10,每一幀長度是25ms,幀移是10ms,兩幀之間有25-10=15ms的交疊。
圖10 幀切割圖
分幀後,音頻數據就變成了不少小的片斷,而後針對小片斷進行特徵提取,常見的提取特徵的方法有:線性預測編碼(Linear Predictive Coding,LPC),梅爾頻率倒譜系數(Mel-frequency Cepstrum),把一幀波形變成一個多維向量的過程就是聲學特徵提取。
音素是人發音的基本單位。對於英文,經常使用的音素是一套39個音素組成的集合。對於漢語,基本就是漢語拼音的生母和韻母組成的音素集合。本文例子中LSTM+CTC神經網絡就是聲學特徵轉換成音素這個階段,該階段的模型被稱爲聲學模型。
獲得聲音的音素序列後,就可使用語言模型等解碼技術將音素序列轉換成咱們能夠讀懂的文本。解碼過程對給定的音素序列和若干假設詞序列計算聲學模型和語言模型分數,將整體輸出分數最高的序列做爲識別的結果(這部分是比較複雜的,感興趣的讀者能夠查閱相關資料)。
本文經過一個簡單的例子演示如何用tensorflow的LSTM+CTC完成一個端到端的語音識別,爲了簡化操做,本例子中的語音識別只訓練一句話,這句話中的音素分類也簡化成對應的字母(與真實因素的訓練過程原理一致)。計算過程以下圖所示:
首先讀者確定會有疑問?什麼是WAV文件?筆者在此簡單的介紹一下,WAV格式是微軟公司開發的一種聲音文件格式,也叫波形聲音文件,是最先的數字音頻格式,被Windows平臺及其應用程序普遍支持,是一種無損的音頻數據存放格式。
本文在讀取WAV的特徵數據後,採用python_speech_features包中的方法來讀取文件的MFCC特徵,詳細代碼以下:
def get_audio_feature():
'''
獲取wav文件提取mfcc特徵以後的數據
'''
audio_filename = "audio.wav"
#讀取wav文件內容,fs爲採樣率, audio爲數據
fs, audio = wav.read(audio_filename)
#提取mfcc特徵
inputs = mfcc(audio, samplerate=fs)
# 對特徵數據進行歸一化,減去均值除以方差
feature_inputs = np.asarray(inputs[np.newaxis, :])
feature_inputs = (feature_inputs - np.mean(feature_inputs))/np.std(feature_inputs)
#特徵數據的序列長度
feature_seq_len = [feature_inputs.shape[1]]
return feature_inputs, feature_seq_len
函數的返回值feature_seq_len表示這段語音被分割成了多少幀,一幀數據計算出一個13維長度的特徵值。返回值feature_inputs是一個二維矩陣,矩陣行數是feature_seq_len長度,列數是13。
本文音素的數量是28,分別對應26個英文字母、空白符和沒有分到類狀況。WAV文件對應的文本文件的內容是she had your dark suit in greasy wash water all year。如今把這句話轉換成整數表示的序列,空白用0表示,a-z分別用數字1-26表示,則轉換的結果爲:[19 8 5 0 8 1 4 0 25 15 21 18 0 4 1 18 110 19 21 9 20 0 9 14 0 7 18 5 1 19 25 0 231 19 8 0 23 1 20 5 18 0 1 12 12 0 25 5 118],最後將整個序列轉換成稀疏三元組結構,這樣就能夠直接用在tensorflow的tf.sparse_placeholder上。轉換代碼以下:
with open(target_filename, 'r') as f:
#原始文本爲「she had your dark suit in greasy wash water all year」
line = f.readlines()[0].strip()
targets = line.replace(' ', ' ')
targets = targets.split(' ')
targets = np.hstack([SPACE_TOKEN if x == '' else list(x) for x in targets])
targets = np.asarray([SPACE_INDEX if x == SPACE_TOKEN else ord(x) - FIRST_INDEX
for x in targets])
# 將列表轉換成稀疏三元組
train_targets = sparse_tuple_from([targets])
print(train_targets)
return train_targets
定義雙向LSTM及LSTM以後的特徵映射的代碼以下:
def inference(inputs, seq_len):
#定義一個向前計算的LSTM單元,40個隱藏單元
cell_fw = tf.contrib.rnn.LSTMCell(num_hidden,
initializer=tf.random_normal_initializer(
mean=0.0, stddev=0.1),
state_is_tuple=True)
# 組成一個有2個cell的list
cells_fw = [cell_fw] * num_layers
# 定義一個向後計算的LSTM單元,40個隱藏單元
cell_bw = tf.contrib.rnn.LSTMCell(num_hidden,
initializer=tf.random_normal_initializer(
mean=0.0, stddev=0.1),
state_is_tuple=True)
# 組成一個有2個cell的list
cells_bw = [cell_bw] * num_layers
outputs, _, _ = tf.contrib.rnn.stack_bidirectional_dynamic_rnn(cells_fw,
cells_bw,
inputs,
dtype=tf.float32,
sequence_length=seq_len)
shape = tf.shape(inputs)
batch_s, max_timesteps = shape[0], shape[1]
outputs = tf.reshape(outputs, [-1, num_hidden])
W = tf.Variable(tf.truncated_normal([num_hidden,
num_classes],
stddev=0.1))
b = tf.Variable(tf.constant(0., shape=[num_classes]))
# 進行全鏈接線性計算
logits = tf.matmul(outputs, W) + b
# 將全鏈接計算的結果,由寬度40變成寬度80,
# 即最後的輸入給CTC的數據寬度必須是26+2的寬度
logits = tf.reshape(logits, [batch_s, -1, num_classes])
# 轉置,將第一維和第二維交換。
# 變成序列的長度放第一維,batch_size放第二維。
# 也是爲了適應Tensorflow的CTC的輸入格式
logits = tf.transpose(logits, (1, 0, 2))
return logits
最後將讀取數據、構建LSTM+CTC網絡及訓練過程結合起來,在完成500次迭代訓練後,進行測試,並將結果輸出,部分代碼以下(完整代碼,請讀者關注本公衆號「大數據技術宅」,輸入「語音識別demo」獲取):
def main():
# 輸入特徵數據,形狀爲:[batch_size, 序列長度,一幀特徵數]
inputs = tf.placeholder(tf.float32, [None, None, num_features])
# 輸入數據的label,定義成稀疏sparse_placeholder會生成稀疏的tensor:SparseTensor
# 這個結構能夠直接輸入給ctc求loss
targets = tf.sparse_placeholder(tf.int32)
# 序列的長度,大小是[batch_size]大小
# 表示的是batch中每一個樣本的有效序列長度是多少
seq_len = tf.placeholder(tf.int32, [None])
# 向前計算網絡,定義網絡結構,輸入是特徵數據,輸出提供給ctc計算損失值。
logits = inference(inputs, seq_len)
# ctc計算損失
# 參數targets必須是一個值爲int32的稀疏tensor的結構:tf.SparseTensor
# 參數logits是前面lstm網絡的輸出
# 參數seq_len是這個batch的樣本中,每一個樣本的序列長度。
loss = tf.nn.ctc_loss(targets, logits, seq_len)
# 計算損失的平均值
cost = tf.reduce_mean(loss)
訓練過程及結果以下圖:
從上圖訓練結果能夠清洗的看出通過500次的迭代訓練,語音文件基本已經能夠徹底識別,本例只演示了一個簡單的LSTM+CTC的端到端的訓練,實際的語音識別系統還須要大量訓練樣本以及將音素轉換成文本的解碼過程。後續文章中,筆者會繼續深刻語音識別。
最後,在2019開年之際,筆者祝各位愛學習的小哥哥,小姐姐,騎豬當先,萬豬奔騰,豬年行大運,發大財。
閱讀本文的人還看了:
深度學習(Deep Learning)資料大全(不斷更新)
用TensorFlow教你作手寫字識別(準確率94.09%)
掃碼關注.大數據技術宅