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 日韩亚洲视频,国产福利福利视频,国产粉嫩粉嫩的18在线播放91

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          使用TensorFlow進行語音識別.js – 語音命令

          我還是個孩子的時候,幾乎每個超級英雄都有一臺語音控制的計算機。所以你可以想象我第一次遇到Alexa對我來說是一次深刻的經(jīng)歷。我心里的孩子非常高興和興奮。當然,然后我的工程直覺開始發(fā)揮作用,我分析了這些設(shè)備是如何工作的。

          事實證明,他們有神經(jīng)網(wǎng)絡(luò)來處理這個復(fù)雜的問題。事實上,神經(jīng)網(wǎng)絡(luò)大大簡化了這個問題,以至于今天使用Python在計算機上制作這些應(yīng)用程序之一非常容易。但情況并非總是如此。第一次嘗試是在 1952 年進行的。由三位貝爾實驗室研究人員撰寫。

          他們建立了一個具有10個單詞詞匯的單揚聲器數(shù)字識別系統(tǒng)。然而,到1980年代,這一數(shù)字急劇增長。詞匯量增長到20,000個單詞,第一批商業(yè)產(chǎn)品開始出現(xiàn)。Dragon Dictate是首批此類產(chǎn)品之一,最初售價為9,000美元。Alexa今天更實惠,對吧?

          但是,今天我們可以在瀏覽器中使用Tensorflo.js執(zhí)行語音識別。在本文中,我們將介紹:

          1. 遷移學(xué)習
          2. 語音識別如何工作?
          3. 演示
          4. 使用Tensorflow實現(xiàn).js

          1. 遷移學(xué)習

          從歷史上看,圖像分類是普及深度神經(jīng)網(wǎng)絡(luò)的問題,尤其是視覺類型的神經(jīng)網(wǎng)絡(luò)——卷積神經(jīng)網(wǎng)絡(luò)(CNN)。今天,遷移學(xué)習用于其他類型的機器學(xué)習任務(wù),如NLP和語音識別。我們不會詳細介紹什么是 CNN 以及它們是如何工作的。然而,我們可以說CNN在2012年打破了ImageNet大規(guī)模視覺識別挑戰(zhàn)賽(ILSVRC)的記錄后得到了普及。

          該競賽評估大規(guī)模對象檢測和圖像分類的算法。他們提供的數(shù)據(jù)集包含 1000 個圖像類別和超過 1 萬張圖像。圖像分類算法的目標是正確預(yù)測對象屬于哪個類。自2年以來。本次比賽的每位獲勝者都使用了CNN。

          訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)可能具有計算性和耗時性。要獲得真正好的結(jié)果,您需要大量的計算能力,這意味著大量的GPU,這意味著......嗯,很多錢。當然,您可以訓(xùn)練這些大型架構(gòu)并在云環(huán)境中獲得SOTA結(jié)果,但這也非常昂貴。

          有一段時間,這些架構(gòu)對普通開發(fā)人員不可用。然而,遷移學(xué)習的概念改變了這種情況。特別是,對于這個問題,我們今天正在解決 - 圖像分類。今天,我們可以使用最先進的架構(gòu),這些架構(gòu)在 ImageNet 競賽中獲勝,這要歸功于遷移學(xué)習和預(yù)訓(xùn)練模型。

          1.1 預(yù)訓(xùn)練模型

          此時,人們可能會想知道“什么是預(yù)訓(xùn)練模型?從本質(zhì)上講,預(yù)訓(xùn)練模型是以前在大型數(shù)據(jù)集(例如 ImageNet 數(shù)據(jù)集)上訓(xùn)練的保存網(wǎng)絡(luò) 。 有兩種方法可以使用它們。您可以將其用作開箱即用的解決方案,也可以將其與遷移學(xué)習一起使用。 由于大型數(shù)據(jù)集通常用于某些全局解決方案,因此您可以自定義預(yù)先訓(xùn)練的模型并將其專門用于某些問題。

          通過這種方式,您可以利用一些最著名的神經(jīng)網(wǎng)絡(luò),而不會在訓(xùn)練上浪費太多時間和資源。此外,您還可以 通過修改所選圖層的行為來微調(diào)這些模型。整個想法圍繞著使用較低層的預(yù)訓(xùn)練CNN模型,并添加額外的層,這些層將為特定問題定制架構(gòu)。

          從本質(zhì)上講,嚴肅的遷移學(xué)習模型通常由兩部分組成。我們稱它們?yōu)楣歉珊皖^腦。 主干通常是在 ImageNet 數(shù)據(jù)集上預(yù)先訓(xùn)練的深度架構(gòu),沒有頂層。Head 是圖像分類模型的一部分,用于預(yù)測自定義類。

          這些層將添加到預(yù)訓(xùn)練模型的頂部。有了這些系統(tǒng),我們有兩個階段:瓶頸和培訓(xùn)階段。在瓶頸階段,特定數(shù)據(jù)集的圖像通過主干架構(gòu)運行,并存儲結(jié)果。在訓(xùn)練階段,來自主干的存儲輸出用于訓(xùn)練自定義層。

          有幾個領(lǐng)域適合使用預(yù)先訓(xùn)練的模型,語音識別就是其中之一。此模型稱為語音命令識別器。從本質(zhì)上講,它是一個JavaScript模塊,可以識別由簡單英語單詞組成的口語命令

          默認詞匯18w”包括以下單詞:從“零”到“九”、“向上”、“向下”、“向左”、“向右”、“開始”、“停止”、“是”、“否”的數(shù)字。還提供其他類別的“未知單詞”和“背景噪音”。除了已經(jīng)提到的“18w”字典之外,還有更小的字典“directional4w”可用。它只包含四個方向詞(“上”、“下”、“左”、“右”)。

          2. 語音識別如何工作?

          當涉及到神經(jīng)網(wǎng)絡(luò)和音頻的組合時,有很多方法。語音通常使用某種遞歸神經(jīng)網(wǎng)絡(luò)或LSTM來處理。但是,語音命令識別器使用稱為卷積神經(jīng)網(wǎng)絡(luò)的簡單體系結(jié)構(gòu),用于小占用量關(guān)鍵字發(fā)現(xiàn)。

          這種方法基于我們在上一篇文章中研究的圖像識別和卷積神經(jīng)網(wǎng)絡(luò)。乍一看,這可能會令人困惑,因為音頻是一個跨時間的一維連續(xù)信號,而不是 2D 空間問題。

          2.1 譜圖

          此體系結(jié)構(gòu)使用頻譜圖。這是信號頻率頻譜隨時間變化的視覺表示。從本質(zhì)上講,定義了單詞應(yīng)該適合的時間窗口。

          這是通過將音頻信號樣本分組到段來完成的。完成此操作后,將分析頻率的強度,并定義具有可能單詞的。然后將這些片段轉(zhuǎn)換為頻譜圖,例如用于單詞識別的單通道圖像:

          然后,使用這種預(yù)處理制作的圖像被饋送到多層卷積神經(jīng)網(wǎng)絡(luò)中。

          3. 演示

          您可能已經(jīng)注意到,此頁面要求您允許使用麥克風。這是因為我們在此頁面中嵌入了實現(xiàn)演示。為了使此演示正常工作,您必須允許它使用麥克風。

          現(xiàn)在,您可以使用命令“向上”,“向下”,“向左”和“右”在下面的畫布上繪制。繼續(xù)嘗試一下:

          4. 使用TensorFlow實現(xiàn).js

          4.1 網(wǎng)頁文件

          首先,讓我們看一下我們實現(xiàn)的 index.html 文件。在上一篇文章中,我們介紹了幾種安裝TensorFlow.js的方法。其中之一是將其集成到HTML文件的腳本標記中。這也是我們在這里的做法。除此之外,我們需要為預(yù)訓(xùn)練的模型添加一個額外的腳本標記。以下是索引.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>

          包含此實現(xiàn)的 JavaScript 代碼位于 script.js 中。此文件應(yīng)與 index.html 文件位于同一文件夾中。為了運行整個過程,您所要做的就是在瀏覽器中打開索引.html并允許它使用您的麥克風。

          4.2 腳本文件

          現(xiàn)在,讓我們檢查整個實現(xiàn)所在的 script.js 文件。以下是主運行函數(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)建模型的實例并將其分配給全局變量識別器。我們使用“directional4w字典,因為我們只需要“up”,“down”,“l(fā)eft”和“right”命令。

          然后我們等待模型加載完成。如果您的互聯(lián)網(wǎng)連接速度較慢,這可能需要一些時間。完成后,我們初始化執(zhí)行繪圖畫布。最后,調(diào)用預(yù)測方法。以下是該函數(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ì)上講,它運行一個無限循環(huán),其中識別器正在傾聽您正在說的話。請注意,我們正在使用參數(shù) probabilityThreshold

          此參數(shù)定義是否應(yīng)調(diào)用回調(diào)函數(shù)。實質(zhì)上,僅當最大概率分數(shù)大于此閾值時,才會調(diào)用回調(diào)函數(shù)。當我們得到這個詞時,我們就得到了我們應(yīng)該畫的方向。

          然后我們使用函數(shù) calculateNewPosition 計算線尾的坐標。該步長為 10 像素,這意味著行的長度將為 10 像素。您可以同時使用概率閾值和此長度值。獲得新坐標后,我們使用畫布繪制線條。就是這樣。很簡單,對吧?

          結(jié)論

          在本文中,我們看到了如何輕松使用預(yù)先訓(xùn)練的 TensorFlow.js 模型。它們是一些簡單應(yīng)用程序的良好起點。我們甚至構(gòu)建了一個此類應(yīng)用程序的示例,您可以使用它使用語音命令進行繪制。這很酷,可能性是無窮無盡的。當然,您可以進一步訓(xùn)練這些模型,獲得更好的結(jié)果,并將它們用于更復(fù)雜的解決方案。這意味著,您可以真正利用遷移學(xué)習。然而,這是另一個時代的故事。



          原文標題: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ù)計學(xué)習時長13分鐘


          本文闡述了如何利用Tensorflow編寫一個基本的端到端自動語音識別(Automatic Speech Recognition,ASR)系統(tǒng),詳細介紹了最小神經(jīng)網(wǎng)絡(luò)的各個組成部分以及可將音頻轉(zhuǎn)為可讀文本的前綴束搜索解碼器。

          雖然當下關(guān)于如何搭建基礎(chǔ)機器學(xué)習系統(tǒng)的文獻或資料有很多,但是大部分都是圍繞計算機視覺和自然語言處理展開的,極少有文章就語音識別展開介紹。本文旨在填補這一空缺,幫助初學(xué)者降低入門難度,提高學(xué)習自信。

          前提

          初學(xué)者需要熟練掌握:

          · 神經(jīng)網(wǎng)絡(luò)的組成

          · 如何訓(xùn)練神經(jīng)網(wǎng)絡(luò)

          · 如何利用語言模型求得詞序的概率

          概述

          · 音頻預(yù)處理:將原始音頻轉(zhuǎn)換為可用作神經(jīng)網(wǎng)絡(luò)輸入的數(shù)據(jù)

          · 神經(jīng)網(wǎng)絡(luò):搭建一個簡單的神經(jīng)網(wǎng)絡(luò),用于將音頻特征轉(zhuǎn)換為文本中可能出現(xiàn)的字符的概率分布

          · CTC損失:計算不使用相應(yīng)字符標注音頻時間步長的損失

          · 解碼:利用前綴束搜索和語言模型,根據(jù)各個時間步長的概率分布生成文本

          本文重點講解了神經(jīng)網(wǎng)絡(luò)、CTC損失和解碼。

          音頻預(yù)處理

          搭建語音識別系統(tǒng),首先需要將音頻轉(zhuǎn)換為特征矩陣,并輸入到神經(jīng)網(wǎng)絡(luò)中。完成這一步的簡單方法就是創(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
          


          這一方法會計算出音頻信號的短時傅里葉變換(Short-time Fourier Transform)以及功率譜,其最終輸出可直接用作神經(jīng)網(wǎng)絡(luò)輸入的頻譜圖矩陣。其他方法包括濾波器組和MFCC(Mel頻率倒譜系數(shù))等。

          了解更多音頻預(yù)處理知識:https://haythamfayek.com/2016/04/21/speech-processing-for-machine-learning.html


          神經(jīng)網(wǎng)絡(luò)

          下圖展現(xiàn)了一個簡單的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。

          語音識別基本結(jié)構(gòu)


          頻譜圖輸入可以看作是每個時間步長的向量。1D卷積層從各個向量中提取出特征,形成特征向量序列,并輸入LSTM層進一步處理。LSTM層(或雙LSTM層)的輸入則傳遞至全連接層。利用softmax激活函數(shù),可得出每個時間步長的字符概率分布。整個網(wǎng)絡(luò)將會用CTC損失函數(shù)進行訓(xùn)練(CTC即Connectionist Temporal Classification,是一種時序分類算法)。熟悉整個建模流程后可嘗試使用更復(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ù)測每個時間步長的字符。然而現(xiàn)有的標簽并不是各個時間步長的字符,僅僅是音頻的轉(zhuǎn)換文本。而文本的各個字符可能橫跨多個步長。如果對音頻的各個時間步長進行標記,C-A-T就會變成C-C-C-A-A-T-T。而每隔一段時間,如10毫秒,對音頻數(shù)據(jù)集進行標注,并不是一個切實可行的方法。CTC則解決上了上述問題。CTC并不需要標記每個時間步長。它忽略了文本中每個字符的位置和實際相位差,把神經(jīng)網(wǎng)絡(luò)的整個概率矩陣輸入和相應(yīng)的文本作為輸入。

          CTC 損失計算

          輸出矩陣示例


          假設(shè)真實的數(shù)據(jù)標簽為CAT,在四個時間步長中,有序列C-C-A-T,C-A-A-T,C-A-T-T,_-C-A-T,C-A-T-_與真實數(shù)據(jù)相對應(yīng)。將這些序列的概率相加,可得到真實數(shù)據(jù)的概率。根據(jù)輸出的概率矩陣,將序列的各個字符的概率相乘,可得到單個序列的概率。則上述序列的總概率為0.0288+0.0144+0.0036+0.0576+0.0012=0.1056。CTC損失則為該概率的負對數(shù)。Tensorflow自帶損失函數(shù)文件。

          解碼

          由上文的神經(jīng)網(wǎng)絡(luò),可輸出一個CTC矩陣。這一矩陣給出了各個時間步長中每個字符在其字符集中的概率。利用前綴束搜索,可從CTC矩陣中得出所需的文本。

          除了字母和空格符,CTC矩陣的字符集還包括兩種特別的標記(token,也稱為令牌)——空白標記和字符串結(jié)束標記。

          空白標記的作用:CTC矩陣中的時間步長通常比較小,如10毫秒。因此,句子中的一個字符會橫跨多個時間步長。如,C-A-T會變成C-C-C-A-A-T-T。所以,需要將CTC矩陣中出現(xiàn)該問題的字符串中的重復(fù)部分折疊,消除重復(fù)。那么像FUNNY這種本來就有兩個重復(fù)字符(N)的詞要怎么辦呢?在這種情況下,就可以使用空白標記,將其插入兩個N中間,就可以防止N被折疊。而這么做實際上并沒有在文本中添加任何東西,也就不會影響其內(nèi)容或形式。因此,F(xiàn)-F-U-N-[空白]-N-N-Y最終會變成FUNNY。

          結(jié)束標記的作用:字符串的結(jié)束表示著一句話的結(jié)束。對字符串結(jié)束標記后的時間步長進行解碼不會給候選字符串增加任何內(nèi)容。


          步驟


          初始化

          · 準備一個初始列表。列表包括多個候選字符串,一個空白字符串,以及各個字符串在不同時間步長以空白標記結(jié)束的概率,和以非空白標記結(jié)束的概率。在時刻0,空白字符串以空白標記結(jié)束的概率為1,以非空白標記結(jié)束的概率則為0。

          迭代

          · 選擇一個候選字符串,將字符一個一個添加進去。計算拓展后的字符串在時刻1以空白標記和非空白標記結(jié)束的概率。將拓展字符串及其概率記錄到列表中。將拓展字符串作為新的候選字符串,在下一時刻重復(fù)上述步驟。

          · 情況A:如果添加的字符是空白標記,則保持候選字符串不變。

          · 情況B:如果添加的字符是空格符,則根據(jù)語言模型將概率與和候選字符串的概率成比例的數(shù)字相乘。這一步可以防止錯誤拼寫變成最佳候選字符串。如,避免COOL被拼成KUL輸出。

          · 情況C:如果添加的字符和候選字符串的最后一個字符相同,(以候選字符串FUN和字符N為例),則生成兩個新的候選字符串,F(xiàn)UNN和FUN。生成FUN的概率取決于FUN以空白標記結(jié)束的概率。生成FUNN的概率則取決于FUN以非空白標記結(jié)束的概率。因此,如果FUN以非空白標記結(jié)束,則去除額外的字符N。

          輸出

          經(jīng)過所有時間步長迭代得出的最佳候選字符串就是輸出。

          為了加快這一過程,可作出如下兩個修改。

          1.在每一個時間步長,去除其他字符串,僅留下最佳的K個候選字符串。具體操作為:根據(jù)字符串以空白和非空白標記結(jié)束的概率之和,對候選字符串進行分類。

          2.去除矩陣中概率之和低于某個閾值(如0.001)的字符。

          具體操作細節(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)
          


          這樣,一個基礎(chǔ)的語音識別系統(tǒng)就完成了。對上述步驟進行復(fù)雜化,可以得到更優(yōu)的結(jié)果,如,搭建更大的神經(jīng)網(wǎng)絡(luò)和利用音頻預(yù)處理技巧。

          完整代碼:https://github.com/apoorvnandan/speech-recognition-primer


          注意事項:

          1. 文中代碼使用的是TensorFlow2.0系統(tǒng),舉例使用的音頻文件選自LibriSpeech數(shù)據(jù)庫(http://www.openslr.org/12)。

          2. 文中代碼并不包括訓(xùn)練音頻數(shù)據(jù)集的批量處理生成器。讀者需要自己編寫。

          3. 讀者亦需自己編寫解碼部分的語言模型函數(shù)。最簡單的方法就是基于語料庫生成一部二元語法字典并計算字符概率。


          留言 點贊 關(guān)注

          我們一起分享AI學(xué)習與發(fā)展的干貨

          如需轉(zhuǎn)載,請后臺留言,遵守轉(zhuǎn)載規(guī)范

          家好,很高興又見面了,我是"高級前端?進階?",由我?guī)е蠹乙黄痍P(guān)注前端前沿、深入前端底層技術(shù),大家一起進步,也歡迎大家關(guān)注、點贊、收藏、轉(zhuǎn)發(fā)!

          什么是 PocketSphinx.js

          PocketSphinx.js 是一個完全在 Web 瀏覽器中運行語音識別器,建立在以下核心能力之上:

          • 用 C (PocketSphinx) 編寫的語音識別器使用 Emscripten 轉(zhuǎn)換為 JavaScript 或 WebAssembly
          • 使用 web audio API 的 audio recorder, 錄音機可以獨立使用來構(gòu)建其他類型的與音頻相關(guān)的 Web 應(yīng)用程序

          PocketSphinx.js 項目包括幾個可以獨立使用的組件:

          • pocketsphinx.js:一個由 emscripten 生成的 JavaScript 庫,基本上是 PocketSphinx 包裝以提供更簡單的 API,并編譯成 JavaScript 或 WebAssembly。
          • pocketsphinx.wasm:編譯后的 WebAssembly 文件(如果編譯成 WebAssembly)。
          • recognizer.js:Web Worker 內(nèi) pocketsphinx.js 的包裝器,用于卸載 UI 線程,免于下載和運行大型 JavaScript 文件以及運行成本高昂的語音識別過程。
          • audioRecorder.js:一個基于 Recorderjs 的錄音庫,將 recorder 的樣本轉(zhuǎn)換為適當?shù)牟蓸勇什⑵鋫鬟f給識別器。
          • callbackManager.js:一個小實用程序,用于通過調(diào)用和回調(diào)而不是消息傳遞與 Web Workers 進行交互。

          目前 PocketSphinx.js 已經(jīng)在 Github 通過 MIT 協(xié)議開源,有超過 1.5k 的 star,是一個值得關(guān)注的前端優(yōu)質(zhì)開源項目。

          如何使用 PocketSphinx.js

          pocketsphinx.js 使用規(guī)范

          pocketsphinx.js 文件可以直接包含到 HTML 文件中,但由于其相當大(幾 MB,取決于編譯和打包文件時使用的優(yōu)化級別),下載和加載它需要時間并影響 UI 線程。 因此,大多數(shù)情況應(yīng)該在 Web Worker 中使用,例如使用 recognizer.js。

          該 API 基于 embind,可能應(yīng)該查看 emscripten 文檔中的該部分,以了解如何與 emscripten 生成的 JavaScript 進行交互。 Pocketsphinx.js 的早期版本使用 C 風格的 API,現(xiàn)已棄用,但仍然可以在 OBSOLETE_API 分支中使用。

          請注意,如果使用 WebAssembly 版本,則需要 pocketsphinx.js 和 pocketsphinx.wasm。 一旦 pocketsphinx.js 加載到頁面中,它將通過查看根文件夾中的文件開始加載和編譯 pocketsphinx.wasm。 可以在加載 pocketsphinx.js 之前設(shè)置備用 URL:

          var Module = {
              locateFile: function() {return "/path/to/pocketsphinx.wasm";}
              }

          另外,由于 pocketsphinx.wasm 是異步加載,因此需要等到其加載并編譯后才能執(zhí)行任何操作。一種方法是為 onRuntimeInitialized 添加回調(diào):

            <script type="text/javascript">
              var Module = {
                  locateFile: function() {return "/path/to/pocketsphinx.wasm";},
                  onRuntimeInitialized: function() {
                  // 開始使用具體方法
                  }
                };
           </script>
           <script src="path/to/pocketsphinx.js"></script>

          作為第一個示例,以下代碼創(chuàng)建一個新的識別器:

          var recognizer = new Module.Recognizer();
          /* ..創(chuàng)建一個識別器... */
          recognizer.delete();

          對 pocketsphinx.js 函數(shù)的調(diào)用是同步的,這也是可能需要將其加載到 Web Worker 中的原因。

          大多數(shù)調(diào)用返回 ResultType 對象,該對象可以是以下之一:

          • SUCCESS,如果操作執(zhí)行成功。
          • BAD_STATE,如果當前狀態(tài)不允許該操作。
          • BAD_ARGUMENT,如果提供的參數(shù)無效。
          • RUNTIME_ERROR,如果識別器中存在運行時錯誤。

          在 JavaScript 中,這些值可以稱為 Module.ReturnType.SUCCESS、Module.ReturnType.BAD_STATE 等。例如:

          var recognizer = new Module.Recognizer();
          /* ..創(chuàng)建一個識別器.. */
          if (recognizer.reInit(config) != Module.ReturnType.SUCCESS)
              alert("Error while recognizer is re-initialized");

          Recognizer 對象

          pocketsphinx.js 的入口是 Recognizer 對象。 開發(fā)者可以根據(jù)需要創(chuàng)建任意數(shù)量的實例,但很多情況下可能不需要并且希望節(jié)省內(nèi)存。 創(chuàng)建新實例時,可以給出一個可選的 Config 對象,該對象將用于設(shè)置用于初始化 Pocketsphinx 的參數(shù)。

          var config = new Module.Config();
          config.push_back(["-fwdflat", "no"]);
          var recognizer = new Module.recognizer(config);
          config.delete();
          /* ..刪除一個識別器. */
          recognizer.delete();

          此時將初始化一個 recognizer,并將 “-fwdflat” 設(shè)置為 “no”。

          如果在編譯 pocketsphinx.js 時包含了多個聲學(xué)模型,則可以通過設(shè)置 “-hmm” 參數(shù)來選擇應(yīng)使用哪一個。 假設(shè)有兩個模型,一種用于英語,一種用于法語,并且已使用 -DHMM_FOLDERS="english;french" 編譯了庫,則可以通過在 Config 對象中設(shè)置正確的值來使用法語模型初始化識別器:

          var config = new Module.Config();
          config.push_back(["-hmm", "french"]);
          var recognizer = new Module.recognizer(config);

          如果沒有給出 “-hmm” 參數(shù),或者給它一個無效值,則將使用列表中的第一個模型(此處為英語)。

          同樣,應(yīng)該使用 recognizer 配置參數(shù)來加載之前打包在 pocketshinx.js 中的統(tǒng)計語言模型(“-lm”)或字典(“-dict”)。 請注意,如果要使用 SLM,則還必須有一個包含 SLM 中使用的單詞的詞典文件。

          此外,在創(chuàng)建實例后,可以通過調(diào)用 reInit 來使用新參數(shù)重新初始化識別器對象,例如:

          var config_english = new Module.Config();
          config_english.push_back(["-hmm", "english"]);
          var config_french = new Module.Config();
          config_french.push_back(["-hmm", "french"]);
          var recognizer = new Module.recognizer(config_english);
          /* ..個性化識別參數(shù). */
          if (recognizer.reInit(config_french) != Module.ReturnType.SUCCESS)
              alert("Error while recognizer is re-initialized");

          識別音頻 Recognizing audio

          要識別音頻,必須首先調(diào)用 start 來初始化識別,然后通過調(diào)用來向識別器提供音頻數(shù)據(jù)以進行處理,最后在完成后調(diào)用 stop。 在識別期間和識別之后,可以通過調(diào)用 getHyp 來檢索已識別的字符串。

          在調(diào)用 start 之前,必須確保當前的語言模型是正確的,主要是最后發(fā)生的情況:

          • 如果剛剛向識別器提供語法或關(guān)鍵字搜索,它將自動用作當前語言模型。
          • 如果對 switchSearch 的調(diào)用成功,則將在下一次調(diào)用中使用指定的搜索來啟動。
          • 如果 SLM 打包在 pocketsphinx.js 中,并通過將參數(shù)添加到實例化(或重新初始化)識別器時使用的 Config 對象來加載,則該模型就是當前的語言模型。

          對 process 的調(diào)用必須包含 AudioBuffer 對象形式的音頻緩沖區(qū)。 AudioBuffer 對象可以重復(fù)使用,但必須包含以 16kHz 錄制的 2 字節(jié)整數(shù)形式的音頻樣本(除非聲學(xué)模型使用不同的特性)。

          var array = ... // array that contains an audio buffer
          var buffer = new Module.AudioBuffer();
          for (var i = 0 ; i < array.length ; i++)
              buffer.push_back(array[i]); // Feed the array with audio data
          var output = recognizer.start(); // Starts recognition on current language model
          output = recognizer.process(buffer); // Processes the buffer
          var hyp = recognizer.getHyp(); // Gets the current recognized string (hypothesis)
          /* ... */
          for (var i = 0 ; i < array.length ; i++)
              buffer.set(i, array[i]); // Feed buffer with new data
          output = recognizer.process(buffer);
          hyp = recognizer.getHyp();
          /* ... */
          output = recognizer.stop();
          // Gets the final recognized string:
          var final_hyp = recognizer.getHyp();
          buffer.delete();

          將 recognizer.js 連接到錄音機

          PocketSphinx.js 包含一個基于 Web Audio API 的音頻錄制庫,該庫訪問麥克風、獲取音頻樣本、將其轉(zhuǎn)換為適當?shù)牟蓸勇剩J聲學(xué)模型為 16kHz),并將其發(fā)送到識別器, 該庫源自 Recorderjs。

          在 HTML 文件中包含 audioRecorder.js,并確保 audioRecorderWorker.js 位于同一文件夾中。 要使用它,請創(chuàng)建一個 AudioRecorder 的新實例,并將 MediaStreamSource 作為參數(shù)。 截至今天,Google Chrome 和 Firefox (25+) 都實現(xiàn)了,還需要將識別器屬性設(shè)置為識別器工作線程,如上所述。

          // Deal with prefixed APIs
          window.AudioContext = window.AudioContext || window.webkitAudioContext;
          
          // Instantiating AudioContext
          try {
              var audioContext = new AudioContext();
          } catch (e) {
              console.log("Error initializing Web Audio");
          }
          
          var recorder;
          // Callback once the user authorizes access to the microphone:
          function startUserMedia(stream) {
              var input = audioContext.createMediaStreamSource(stream);
              recorder = new AudioRecorder(input);
              // We can, for instance, add a recognizer as consumer
              if (recognizer) recorder.consumers.push(recognizer);
            };
          
          // Call getUserMedia
          if (navigator.mediaDevices.getUserMedia)
              navigator.mediaDevices.getUserMedia({audio: true})
                                      .then(startUserMedia)
                                      .catch(function(e) {
                                          console.log("No live audio input in this browser");
                                      });
          else console.log("No web audio support in this browser");

          recorder 啟動并運行后,可以通過以下方式開始和停止錄音和識別:

          // To start recording:
          recorder.start();
          // The hypothesis is periodically sent by the recognizer, as described previously
          // To stop recording:
          recorder.stop();  // The final hypothesis is sent

          AudioRecorder 的構(gòu)造函數(shù)可以采用可選的配置對象。 此配置可以包含一個回調(diào)函數(shù),該函數(shù)在錄制過程中出現(xiàn)錯誤時執(zhí)行。 截至目前,唯一可能的錯誤是輸入樣本靜音時,還可以包括輸出采樣率,如果使用 8kHz 音頻的聲學(xué)模型可能需要設(shè)置該輸出采樣率。

          var audioRecorderConfig = {
              errorCallback: function(x) {alert("Error from recorder: " + x);},
              outputSampleRate: 8000
              };
          recorder = new AudioRecorder(input, audioRecorderConfig);

          本文總結(jié)

          本文主要和大家介紹 PocketSphinx.js,其是一個完全在 Web 瀏覽器中運行語音識別器,建立在 PocketSphinx,使用 web audio API 的 audio recorder 之上。 因為篇幅問題,關(guān)于 PocketSphinx.js 只是做了一個簡短的介紹,但是文末的參考資料以及個人主頁提供了大量優(yōu)秀文檔以供學(xué)習,如果有興趣可以自行閱讀。如果大家有什么疑問歡迎在評論區(qū)留言。

          參考資料

          https://github.com/syl22-00/pocketsphinx.js

          https://www.instructables.com/Introduction-to-Pocketsphinx-for-Voice-Controled-A/

          https://www.youtube.com/watch?v=J-bQQiCMV5k

          https://levelup.gitconnected.com/how-to-use-web-workers-api-b1ef96c46fc0


          主站蜘蛛池模板: 成人一区专区在线观看| 一区二区三区免费在线视频| 日韩精品无码一区二区三区四区 | 无码aⅴ精品一区二区三区浪潮| 一区二区3区免费视频| 国产伦精品一区二区免费| 91麻豆精品国产自产在线观看一区 | 成人区精品一区二区不卡亚洲| 无码日韩精品一区二区免费| 国产一区二区三区播放| 亚洲av片一区二区三区 | 精品视频无码一区二区三区| 亚洲电影唐人社一区二区| 日韩在线一区二区三区视频| 色综合视频一区二区三区| 久久久久人妻精品一区蜜桃| 另类国产精品一区二区| 国产免费一区二区三区免费视频| 无码人妻精品一区二区三18禁 | 亚洲国产一区二区三区| 亚洲爆乳精品无码一区二区三区| 一色一伦一区二区三区| 一区二区三区久久精品| 国产成人精品日本亚洲专一区| 国产成人综合亚洲一区| 亚洲AV本道一区二区三区四区| 国产精品久久亚洲一区二区| 国产免费一区二区三区不卡| 精品国产一区二区三区| 国产亚洲福利精品一区| 日本高清天码一区在线播放| 亚洲AV无码一区二区乱孑伦AS | 岛国无码av不卡一区二区| 亚洲丶国产丶欧美一区二区三区| 中日韩一区二区三区| 人妻夜夜爽天天爽一区| 亚洲AV福利天堂一区二区三| 国产精品亚洲不卡一区二区三区| 国产精品福利一区二区| 麻豆精品久久久一区二区| 精品理论片一区二区三区|