Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
我還是個(gè)孩子的時(shí)候,幾乎每個(gè)超級(jí)英雄都有一臺(tái)語(yǔ)音控制的計(jì)算機(jī)。所以你可以想象我第一次遇到Alexa對(duì)我來(lái)說(shuō)是一次深刻的經(jīng)歷。我心里的孩子非常高興和興奮。當(dāng)然,然后我的工程直覺(jué)開始發(fā)揮作用,我分析了這些設(shè)備是如何工作的。
事實(shí)證明,他們有神經(jīng)網(wǎng)絡(luò)來(lái)處理這個(gè)復(fù)雜的問(wèn)題。事實(shí)上,神經(jīng)網(wǎng)絡(luò)大大簡(jiǎn)化了這個(gè)問(wèn)題,以至于今天使用Python在計(jì)算機(jī)上制作這些應(yīng)用程序之一非常容易。但情況并非總是如此。第一次嘗試是在 1952 年進(jìn)行的。由三位貝爾實(shí)驗(yàn)室研究人員撰寫。
他們建立了一個(gè)具有10個(gè)單詞詞匯的單揚(yáng)聲器數(shù)字識(shí)別系統(tǒng)。然而,到1980年代,這一數(shù)字急劇增長(zhǎng)。詞匯量增長(zhǎng)到20,000個(gè)單詞,第一批商業(yè)產(chǎn)品開始出現(xiàn)。Dragon Dictate是首批此類產(chǎn)品之一,最初售價(jià)為9,000美元。Alexa今天更實(shí)惠,對(duì)吧?
但是,今天我們可以在瀏覽器中使用Tensorflo.js執(zhí)行語(yǔ)音識(shí)別。在本文中,我們將介紹:
從歷史上看,圖像分類是普及深度神經(jīng)網(wǎng)絡(luò)的問(wèn)題,尤其是視覺(jué)類型的神經(jīng)網(wǎng)絡(luò)——卷積神經(jīng)網(wǎng)絡(luò)(CNN)。今天,遷移學(xué)習(xí)用于其他類型的機(jī)器學(xué)習(xí)任務(wù),如NLP和語(yǔ)音識(shí)別。我們不會(huì)詳細(xì)介紹什么是 CNN 以及它們是如何工作的。然而,我們可以說(shuō)CNN在2012年打破了ImageNet大規(guī)模視覺(jué)識(shí)別挑戰(zhàn)賽(ILSVRC)的記錄后得到了普及。
該競(jìng)賽評(píng)估大規(guī)模對(duì)象檢測(cè)和圖像分類的算法。他們提供的數(shù)據(jù)集包含 1000 個(gè)圖像類別和超過(guò) 1 萬(wàn)張圖像。圖像分類算法的目標(biāo)是正確預(yù)測(cè)對(duì)象屬于哪個(gè)類。自2年以來(lái)。本次比賽的每位獲勝者都使用了CNN。
訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)可能具有計(jì)算性和耗時(shí)性。要獲得真正好的結(jié)果,您需要大量的計(jì)算能力,這意味著大量的GPU,這意味著......嗯,很多錢。當(dāng)然,您可以訓(xùn)練這些大型架構(gòu)并在云環(huán)境中獲得SOTA結(jié)果,但這也非常昂貴。
有一段時(shí)間,這些架構(gòu)對(duì)普通開發(fā)人員不可用。然而,遷移學(xué)習(xí)的概念改變了這種情況。特別是,對(duì)于這個(gè)問(wèn)題,我們今天正在解決 - 圖像分類。今天,我們可以使用最先進(jìn)的架構(gòu),這些架構(gòu)在 ImageNet 競(jìng)賽中獲勝,這要?dú)w功于遷移學(xué)習(xí)和預(yù)訓(xùn)練模型。
此時(shí),人們可能會(huì)想知道“什么是預(yù)訓(xùn)練模型?從本質(zhì)上講,預(yù)訓(xùn)練模型是以前在大型數(shù)據(jù)集(例如 ImageNet 數(shù)據(jù)集)上訓(xùn)練的保存網(wǎng)絡(luò) 。 有兩種方法可以使用它們。您可以將其用作開箱即用的解決方案,也可以將其與遷移學(xué)習(xí)一起使用。 由于大型數(shù)據(jù)集通常用于某些全局解決方案,因此您可以自定義預(yù)先訓(xùn)練的模型并將其專門用于某些問(wèn)題。
通過(guò)這種方式,您可以利用一些最著名的神經(jīng)網(wǎng)絡(luò),而不會(huì)在訓(xùn)練上浪費(fèi)太多時(shí)間和資源。此外,您還可以 通過(guò)修改所選圖層的行為來(lái)微調(diào)這些模型。整個(gè)想法圍繞著使用較低層的預(yù)訓(xùn)練CNN模型,并添加額外的層,這些層將為特定問(wèn)題定制架構(gòu)。
從本質(zhì)上講,嚴(yán)肅的遷移學(xué)習(xí)模型通常由兩部分組成。我們稱它們?yōu)楣歉珊皖^腦。 主干通常是在 ImageNet 數(shù)據(jù)集上預(yù)先訓(xùn)練的深度架構(gòu),沒(méi)有頂層。Head 是圖像分類模型的一部分,用于預(yù)測(cè)自定義類。
這些層將添加到預(yù)訓(xùn)練模型的頂部。有了這些系統(tǒng),我們有兩個(gè)階段:瓶頸和培訓(xùn)階段。在瓶頸階段,特定數(shù)據(jù)集的圖像通過(guò)主干架構(gòu)運(yùn)行,并存儲(chǔ)結(jié)果。在訓(xùn)練階段,來(lái)自主干的存儲(chǔ)輸出用于訓(xùn)練自定義層。
有幾個(gè)領(lǐng)域適合使用預(yù)先訓(xùn)練的模型,語(yǔ)音識(shí)別就是其中之一。此模型稱為語(yǔ)音命令識(shí)別器。從本質(zhì)上講,它是一個(gè)JavaScript模塊,可以識(shí)別由簡(jiǎn)單英語(yǔ)單詞組成的口語(yǔ)命令。
默認(rèn)詞匯“18w”包括以下單詞:從“零”到“九”、“向上”、“向下”、“向左”、“向右”、“開始”、“停止”、“是”、“否”的數(shù)字。還提供其他類別的“未知單詞”和“背景噪音”。除了已經(jīng)提到的“18w”字典之外,還有更小的字典“directional4w”可用。它只包含四個(gè)方向詞(“上”、“下”、“左”、“右”)。
當(dāng)涉及到神經(jīng)網(wǎng)絡(luò)和音頻的組合時(shí),有很多方法。語(yǔ)音通常使用某種遞歸神經(jīng)網(wǎng)絡(luò)或LSTM來(lái)處理。但是,語(yǔ)音命令識(shí)別器使用稱為卷積神經(jīng)網(wǎng)絡(luò)的簡(jiǎn)單體系結(jié)構(gòu),用于小占用量關(guān)鍵字發(fā)現(xiàn)。
這種方法基于我們?cè)谏弦黄恼轮醒芯康膱D像識(shí)別和卷積神經(jīng)網(wǎng)絡(luò)。乍一看,這可能會(huì)令人困惑,因?yàn)橐纛l是一個(gè)跨時(shí)間的一維連續(xù)信號(hào),而不是 2D 空間問(wèn)題。
此體系結(jié)構(gòu)使用頻譜圖。這是信號(hào)頻率頻譜隨時(shí)間變化的視覺(jué)表示。從本質(zhì)上講,定義了單詞應(yīng)該適合的時(shí)間窗口。
這是通過(guò)將音頻信號(hào)樣本分組到段來(lái)完成的。完成此操作后,將分析頻率的強(qiáng)度,并定義具有可能單詞的段。然后將這些片段轉(zhuǎn)換為頻譜圖,例如用于單詞識(shí)別的單通道圖像:
然后,使用這種預(yù)處理制作的圖像被饋送到多層卷積神經(jīng)網(wǎng)絡(luò)中。
您可能已經(jīng)注意到,此頁(yè)面要求您允許使用麥克風(fēng)。這是因?yàn)槲覀冊(cè)诖隧?yè)面中嵌入了實(shí)現(xiàn)演示。為了使此演示正常工作,您必須允許它使用麥克風(fēng)。
現(xiàn)在,您可以使用命令“向上”,“向下”,“向左”和“右”在下面的畫布上繪制。繼續(xù)嘗試一下:
首先,讓我們看一下我們實(shí)現(xiàn)的 index.html 文件。在上一篇文章中,我們介紹了幾種安裝TensorFlow.js的方法。其中之一是將其集成到HTML文件的腳本標(biāo)記中。這也是我們?cè)谶@里的做法。除此之外,我們需要為預(yù)訓(xùn)練的模型添加一個(gè)額外的腳本標(biāo)記。以下是索引.html的外觀:
<html>
<head>
<script src="https://unpkg.com/@tensorflow/tfjs@0.15.3/dist/tf.js"></script>
<script src="https://unpkg.com/@tensorflow-models/speech-commands@0.3.0/dist/speech-commands.min.js"></script>
</head>
<body>
<section class='title-area'>
<h1>TensorFlow.js Speech Recognition</h1>
<p class='subtitle'>Using pretrained models for speech recognition</p>
</section>
<canvas id="canvas" width="1000" height="800" style="border:1px solid #c3c3c3;"></canvas>
<script src="script.js"></script>
</body>
</html>
包含此實(shí)現(xiàn)的 JavaScript 代碼位于 script.js 中。此文件應(yīng)與 index.html 文件位于同一文件夾中。為了運(yùn)行整個(gè)過(guò)程,您所要做的就是在瀏覽器中打開索引.html并允許它使用您的麥克風(fēng)。
現(xiàn)在,讓我們檢查整個(gè)實(shí)現(xiàn)所在的 script.js 文件。以下是主運(yùn)行函數(shù)的外觀:
async function run() {
recognizer = speechCommands.create('BROWSER_FFT', 'directional4w');
await recognizer.ensureModelLoaded();
var canvas = document.getElementById("canvas");
var contex = canvas.getContext("2d");
contex.lineWidth = 10;
contex.lineJoin = 'round';
var positionx = 400;
var positiony = 500;
predict(contex, positionx, positiony);
}
在這里我們可以看到應(yīng)用程序的工作流程。首先,我們創(chuàng)建模型的實(shí)例并將其分配給全局變量識(shí)別器。我們使用“directional4w”字典,因?yàn)槲覀冎恍枰?/span>up”,“down”,“l(fā)eft”和“right”命令。
然后我們等待模型加載完成。如果您的互聯(lián)網(wǎng)連接速度較慢,這可能需要一些時(shí)間。完成后,我們初始化執(zhí)行繪圖的畫布。最后,調(diào)用預(yù)測(cè)方法。以下是該函數(shù)內(nèi)部發(fā)生的情況:
function calculateNewPosition(positionx, positiony, direction)
{
return {
'up' : [positionx, positiony - 10],
'down': [positionx, positiony + 10],
'left' : [positionx - 10, positiony],
'right' : [positionx + 10, positiony],
'default': [positionx, positiony]
}[direction];
}
function predict(contex, positionx, positiony) {
const words = recognizer.wordLabels();
recognizer.listen(({scores}) => {
scores = Array.from(scores).map((s, i) => ({score: s, word: words[i]}));
scores.sort((s1, s2) => s2.score - s1.score);
var direction = scores[0].word;
var [x1, y1] = calculateNewPosition(positionx, positiony, direction);
contex.moveTo(positionx,positiony);
contex.lineTo(x1, y1);
contex.closePath();
contex.stroke();
positionx = x1;
positiony = y1;
}, {probabilityThreshold: 0.75});
}
這種方法正在做繁重的工作。從本質(zhì)上講,它運(yùn)行一個(gè)無(wú)限循環(huán),其中識(shí)別器正在傾聽您正在說(shuō)的話。請(qǐng)注意,我們正在使用參數(shù) probabilityThreshold。
此參數(shù)定義是否應(yīng)調(diào)用回調(diào)函數(shù)。實(shí)質(zhì)上,僅當(dāng)最大概率分?jǐn)?shù)大于此閾值時(shí),才會(huì)調(diào)用回調(diào)函數(shù)。當(dāng)我們得到這個(gè)詞時(shí),我們就得到了我們應(yīng)該畫的方向。
然后我們使用函數(shù) calculateNewPosition 計(jì)算線尾的坐標(biāo)。該步長(zhǎng)為 10 像素,這意味著行的長(zhǎng)度將為 10 像素。您可以同時(shí)使用概率閾值和此長(zhǎng)度值。獲得新坐標(biāo)后,我們使用畫布繪制線條。就是這樣。很簡(jiǎn)單,對(duì)吧?
在本文中,我們看到了如何輕松使用預(yù)先訓(xùn)練的 TensorFlow.js 模型。它們是一些簡(jiǎn)單應(yīng)用程序的良好起點(diǎn)。我們甚至構(gòu)建了一個(gè)此類應(yīng)用程序的示例,您可以使用它使用語(yǔ)音命令進(jìn)行繪制。這很酷,可能性是無(wú)窮無(wú)盡的。當(dāng)然,您可以進(jìn)一步訓(xùn)練這些模型,獲得更好的結(jié)果,并將它們用于更復(fù)雜的解決方案。這意味著,您可以真正利用遷移學(xué)習(xí)。然而,這是另一個(gè)時(shí)代的故事。
原文標(biāo)題:Speech Recognition with TensorFlow.js – Voice Commands
原文鏈接:https://rubikscode.net/2022/05/11/drawing-with-voice-speech-recognition-with-tensorflow-js/
作者:Nikola M. Zivkovic
編譯:LCR
文共6655字,預(yù)計(jì)學(xué)習(xí)時(shí)長(zhǎng)13分鐘
本文闡述了如何利用Tensorflow編寫一個(gè)基本的端到端自動(dòng)語(yǔ)音識(shí)別(Automatic Speech Recognition,ASR)系統(tǒng),詳細(xì)介紹了最小神經(jīng)網(wǎng)絡(luò)的各個(gè)組成部分以及可將音頻轉(zhuǎn)為可讀文本的前綴束搜索解碼器。
雖然當(dāng)下關(guān)于如何搭建基礎(chǔ)機(jī)器學(xué)習(xí)系統(tǒng)的文獻(xiàn)或資料有很多,但是大部分都是圍繞計(jì)算機(jī)視覺(jué)和自然語(yǔ)言處理展開的,極少有文章就語(yǔ)音識(shí)別展開介紹。本文旨在填補(bǔ)這一空缺,幫助初學(xué)者降低入門難度,提高學(xué)習(xí)自信。
前提
初學(xué)者需要熟練掌握:
· 神經(jīng)網(wǎng)絡(luò)的組成
· 如何訓(xùn)練神經(jīng)網(wǎng)絡(luò)
· 如何利用語(yǔ)言模型求得詞序的概率
概述
· 音頻預(yù)處理:將原始音頻轉(zhuǎn)換為可用作神經(jīng)網(wǎng)絡(luò)輸入的數(shù)據(jù)
· 神經(jīng)網(wǎng)絡(luò):搭建一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò),用于將音頻特征轉(zhuǎn)換為文本中可能出現(xiàn)的字符的概率分布
· CTC損失:計(jì)算不使用相應(yīng)字符標(biāo)注音頻時(shí)間步長(zhǎng)的損失
· 解碼:利用前綴束搜索和語(yǔ)言模型,根據(jù)各個(gè)時(shí)間步長(zhǎng)的概率分布生成文本
本文重點(diǎn)講解了神經(jīng)網(wǎng)絡(luò)、CTC損失和解碼。
音頻預(yù)處理
搭建語(yǔ)音識(shí)別系統(tǒng),首先需要將音頻轉(zhuǎn)換為特征矩陣,并輸入到神經(jīng)網(wǎng)絡(luò)中。完成這一步的簡(jiǎn)單方法就是創(chuàng)建頻譜圖。
def create_spectrogram(signals): stfts = tf.signal.stft(signals, fft_length=256) spectrograms = tf.math.pow(tf.abs(stfts), 0.5) return spectrograms
這一方法會(huì)計(jì)算出音頻信號(hào)的短時(shí)傅里葉變換(Short-time Fourier Transform)以及功率譜,其最終輸出可直接用作神經(jīng)網(wǎng)絡(luò)輸入的頻譜圖矩陣。其他方法包括濾波器組和MFCC(Mel頻率倒譜系數(shù))等。
了解更多音頻預(yù)處理知識(shí):https://haythamfayek.com/2016/04/21/speech-processing-for-machine-learning.html
神經(jīng)網(wǎng)絡(luò)
下圖展現(xiàn)了一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。
語(yǔ)音識(shí)別基本結(jié)構(gòu)
頻譜圖輸入可以看作是每個(gè)時(shí)間步長(zhǎng)的向量。1D卷積層從各個(gè)向量中提取出特征,形成特征向量序列,并輸入LSTM層進(jìn)一步處理。LSTM層(或雙LSTM層)的輸入則傳遞至全連接層。利用softmax激活函數(shù),可得出每個(gè)時(shí)間步長(zhǎng)的字符概率分布。整個(gè)網(wǎng)絡(luò)將會(huì)用CTC損失函數(shù)進(jìn)行訓(xùn)練(CTC即Connectionist Temporal Classification,是一種時(shí)序分類算法)。熟悉整個(gè)建模流程后可嘗試使用更復(fù)雜的模型。
class ASR(tf.keras.Model): def __init__(self, filters, kernel_size, conv_stride, conv_border, n_lstm_units, n_dense_units): super(ASR, self).__init__() self.conv_layer = tf.keras.layers.Conv1D(filters, kernel_size, strides=conv_stride, padding=conv_border, activation='relu') self.lstm_layer = tf.keras.layers.LSTM(n_lstm_units, return_sequences=True, activation='tanh') self.lstm_layer_back = tf.keras.layers.LSTM(n_lstm_units, return_sequences=True, go_backwards=True, activation='tanh') self.blstm_layer = tf.keras.layers.Bidirectional(self.lstm_layer, backward_layer=self.lstm_layer_back) self.dense_layer = tf.keras.layers.Dense(n_dense_units) def call(self, x): x = self.conv_layer(x) x = self.blstm_layer(x) x = self.dense_layer(x) return x
為什么使用CTC呢?搭建神經(jīng)網(wǎng)絡(luò)旨在預(yù)測(cè)每個(gè)時(shí)間步長(zhǎng)的字符。然而現(xiàn)有的標(biāo)簽并不是各個(gè)時(shí)間步長(zhǎng)的字符,僅僅是音頻的轉(zhuǎn)換文本。而文本的各個(gè)字符可能橫跨多個(gè)步長(zhǎng)。如果對(duì)音頻的各個(gè)時(shí)間步長(zhǎng)進(jìn)行標(biāo)記,C-A-T就會(huì)變成C-C-C-A-A-T-T。而每隔一段時(shí)間,如10毫秒,對(duì)音頻數(shù)據(jù)集進(jìn)行標(biāo)注,并不是一個(gè)切實(shí)可行的方法。CTC則解決上了上述問(wèn)題。CTC并不需要標(biāo)記每個(gè)時(shí)間步長(zhǎng)。它忽略了文本中每個(gè)字符的位置和實(shí)際相位差,把神經(jīng)網(wǎng)絡(luò)的整個(gè)概率矩陣輸入和相應(yīng)的文本作為輸入。
CTC 損失計(jì)算
輸出矩陣示例
假設(shè)真實(shí)的數(shù)據(jù)標(biāo)簽為CAT,在四個(gè)時(shí)間步長(zhǎng)中,有序列C-C-A-T,C-A-A-T,C-A-T-T,_-C-A-T,C-A-T-_與真實(shí)數(shù)據(jù)相對(duì)應(yīng)。將這些序列的概率相加,可得到真實(shí)數(shù)據(jù)的概率。根據(jù)輸出的概率矩陣,將序列的各個(gè)字符的概率相乘,可得到單個(gè)序列的概率。則上述序列的總概率為0.0288+0.0144+0.0036+0.0576+0.0012=0.1056。CTC損失則為該概率的負(fù)對(duì)數(shù)。Tensorflow自帶損失函數(shù)文件。
解碼
由上文的神經(jīng)網(wǎng)絡(luò),可輸出一個(gè)CTC矩陣。這一矩陣給出了各個(gè)時(shí)間步長(zhǎng)中每個(gè)字符在其字符集中的概率。利用前綴束搜索,可從CTC矩陣中得出所需的文本。
除了字母和空格符,CTC矩陣的字符集還包括兩種特別的標(biāo)記(token,也稱為令牌)——空白標(biāo)記和字符串結(jié)束標(biāo)記。
空白標(biāo)記的作用:CTC矩陣中的時(shí)間步長(zhǎng)通常比較小,如10毫秒。因此,句子中的一個(gè)字符會(huì)橫跨多個(gè)時(shí)間步長(zhǎng)。如,C-A-T會(huì)變成C-C-C-A-A-T-T。所以,需要將CTC矩陣中出現(xiàn)該問(wèn)題的字符串中的重復(fù)部分折疊,消除重復(fù)。那么像FUNNY這種本來(lái)就有兩個(gè)重復(fù)字符(N)的詞要怎么辦呢?在這種情況下,就可以使用空白標(biāo)記,將其插入兩個(gè)N中間,就可以防止N被折疊。而這么做實(shí)際上并沒(méi)有在文本中添加任何東西,也就不會(huì)影響其內(nèi)容或形式。因此,F(xiàn)-F-U-N-[空白]-N-N-Y最終會(huì)變成FUNNY。
結(jié)束標(biāo)記的作用:字符串的結(jié)束表示著一句話的結(jié)束。對(duì)字符串結(jié)束標(biāo)記后的時(shí)間步長(zhǎng)進(jìn)行解碼不會(huì)給候選字符串增加任何內(nèi)容。
步驟
初始化
· 準(zhǔn)備一個(gè)初始列表。列表包括多個(gè)候選字符串,一個(gè)空白字符串,以及各個(gè)字符串在不同時(shí)間步長(zhǎng)以空白標(biāo)記結(jié)束的概率,和以非空白標(biāo)記結(jié)束的概率。在時(shí)刻0,空白字符串以空白標(biāo)記結(jié)束的概率為1,以非空白標(biāo)記結(jié)束的概率則為0。
迭代
· 選擇一個(gè)候選字符串,將字符一個(gè)一個(gè)添加進(jìn)去。計(jì)算拓展后的字符串在時(shí)刻1以空白標(biāo)記和非空白標(biāo)記結(jié)束的概率。將拓展字符串及其概率記錄到列表中。將拓展字符串作為新的候選字符串,在下一時(shí)刻重復(fù)上述步驟。
· 情況A:如果添加的字符是空白標(biāo)記,則保持候選字符串不變。
· 情況B:如果添加的字符是空格符,則根據(jù)語(yǔ)言模型將概率與和候選字符串的概率成比例的數(shù)字相乘。這一步可以防止錯(cuò)誤拼寫變成最佳候選字符串。如,避免COOL被拼成KUL輸出。
· 情況C:如果添加的字符和候選字符串的最后一個(gè)字符相同,(以候選字符串FUN和字符N為例),則生成兩個(gè)新的候選字符串,F(xiàn)UNN和FUN。生成FUN的概率取決于FUN以空白標(biāo)記結(jié)束的概率。生成FUNN的概率則取決于FUN以非空白標(biāo)記結(jié)束的概率。因此,如果FUN以非空白標(biāo)記結(jié)束,則去除額外的字符N。
輸出
經(jīng)過(guò)所有時(shí)間步長(zhǎng)迭代得出的最佳候選字符串就是輸出。
為了加快這一過(guò)程,可作出如下兩個(gè)修改。
1.在每一個(gè)時(shí)間步長(zhǎng),去除其他字符串,僅留下最佳的K個(gè)候選字符串。具體操作為:根據(jù)字符串以空白和非空白標(biāo)記結(jié)束的概率之和,對(duì)候選字符串進(jìn)行分類。
2.去除矩陣中概率之和低于某個(gè)閾值(如0.001)的字符。
具體操作細(xì)節(jié)可參考如下代碼。
def prefix_beam_search(ctc, alphabet, blank_token, end_token, space_token, lm, k=25, alpha=0.30, beta=5, prune=0.001): ''' function to perform prefix beam search on output ctc matrix and return the best string :param ctc: output matrix :param alphabet: list of strings in the order their probabilties are present in ctc output :param blank_token: string representing blank token :param end_token: string representing end token :param space_token: string representing space token :param lm: function to calculate language model probability of given string :param k: threshold for selecting the k best prefixes at each timestep :param alpha: language model weight (b/w 0 and 1) :param beta: language model compensation (should be proportional to alpha) :param prune: threshold on the output matrix probability of a character. If the probability of a character is less than this threshold, we do not extend the prefix with it :return: best string ''' zero_pad = np.zeros((ctc.shape[0]+1,ctc.shape[1])) zero_pad[1:,:] = ctc ctc = zero_pad total_timesteps = ctc.shape[0] # #### Initialization #### null_token = '' Pb, Pnb = Cache(), Cache() Pb.add(0,null_token,1) Pnb.add(0,null_token,0) prefix_list = [null_token] # #### Iterations #### for timestep in range(1, total_timesteps): pruned_alphabet = [alphabet[i] for i in np.where(ctc[timestep] > prune)[0]] for prefix in prefix_list: if len(prefix) > 0 and prefix[-1] == end_token: Pb.add(timestep,prefix,Pb.get(timestep - 1,prefix)) Pnb.add(timestep,prefix,Pnb.get(timestep - 1,prefix)) continue for character in pruned_alphabet: character_index = alphabet.index(character) # #### Iterations : Case A #### if character == blank_token: value = Pb.get(timestep,prefix) + ctc[timestep][character_index] * (Pb.get(timestep - 1,prefix) + Pnb.get(timestep - 1,prefix)) Pb.add(timestep,prefix,value) else: prefix_extended = prefix + character # #### Iterations : Case C #### if len(prefix) > 0 and character == prefix[-1]: value = Pnb.get(timestep,prefix_extended) + ctc[timestep][character_index] * Pb.get(timestep-1,prefix) Pnb.add(timestep,prefix_extended,value) value = Pnb.get(timestep,prefix) + ctc[timestep][character_index] * Pnb.get(timestep-1,prefix) Pnb.add(timestep,prefix,value) # #### Iterations : Case B #### elif len(prefix.replace(space_token, '')) > 0 and character in (space_token, end_token): lm_prob = lm(prefix_extended.strip(space_token + end_token)) ** alpha value = Pnb.get(timestep,prefix_extended) + lm_prob * ctc[timestep][character_index] * (Pb.get(timestep-1,prefix) + Pnb.get(timestep-1,prefix)) Pnb.add(timestep,prefix_extended,value) else: value = Pnb.get(timestep,prefix_extended) + ctc[timestep][character_index] * (Pb.get(timestep-1,prefix) + Pnb.get(timestep-1,prefix)) Pnb.add(timestep,prefix_extended,value) if prefix_extended not in prefix_list: value = Pb.get(timestep,prefix_extended) + ctc[timestep][-1] * (Pb.get(timestep-1,prefix_extended) + Pnb.get(timestep-1,prefix_extended)) Pb.add(timestep,prefix_extended,value) value = Pnb.get(timestep,prefix_extended) + ctc[timestep][character_index] * Pnb.get(timestep-1,prefix_extended) Pnb.add(timestep,prefix_extended,value) prefix_list = get_k_most_probable_prefixes(Pb,Pnb,timestep,k,beta) # #### Output #### return prefix_list[0].strip(end_token)
這樣,一個(gè)基礎(chǔ)的語(yǔ)音識(shí)別系統(tǒng)就完成了。對(duì)上述步驟進(jìn)行復(fù)雜化,可以得到更優(yōu)的結(jié)果,如,搭建更大的神經(jīng)網(wǎng)絡(luò)和利用音頻預(yù)處理技巧。
完整代碼:https://github.com/apoorvnandan/speech-recognition-primer
注意事項(xiàng):
1. 文中代碼使用的是TensorFlow2.0系統(tǒng),舉例使用的音頻文件選自LibriSpeech數(shù)據(jù)庫(kù)(http://www.openslr.org/12)。
2. 文中代碼并不包括訓(xùn)練音頻數(shù)據(jù)集的批量處理生成器。讀者需要自己編寫。
3. 讀者亦需自己編寫解碼部分的語(yǔ)言模型函數(shù)。最簡(jiǎn)單的方法就是基于語(yǔ)料庫(kù)生成一部二元語(yǔ)法字典并計(jì)算字符概率。
留言 點(diǎn)贊 關(guān)注
我們一起分享AI學(xué)習(xí)與發(fā)展的干貨
如需轉(zhuǎn)載,請(qǐng)后臺(tái)留言,遵守轉(zhuǎn)載規(guī)范
者:xmanlin
轉(zhuǎn)發(fā)鏈接:https://segmentfault.com/a/1190000022268377
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。