編者按:Kanda機器學習工程師Daniel Rothmann撰寫的機器聽覺系列第四篇,講解如何在聲音頻譜嵌入中加入記憶機制。
歡迎回來!這一系列文章將詳細介紹奧胡斯大學和智能揚聲器生產(chǎn)商Dynaudio A/S合作開發(fā)的實時音頻信號處理框架。
如果你錯過了之前的文章,可以點擊下面的鏈接查看:
AI在音頻處理上的潛力
基于頻譜圖和CNN處理音頻有何問題?
基于自編碼器學習聲音嵌入表示
在上一篇文章中,我們介紹了人類是如何體驗聲音的,在耳蝸形成頻譜印象,接著由腦干核團“編碼”,并借鑒其思路,基于自編碼器學習聲音頻譜嵌入。在這篇文章中,我們將探索如何構(gòu)建用來理解聲音的人工神經(jīng)網(wǎng)絡(luò),并在頻譜聲音嵌入生成中集成記憶。
余音記憶
聲音事件的含義,很大程度上源于頻譜特征間的時域相互關(guān)系。
有一個事實可以作為例證,取決于語音的時域上下文的不同,人類聽覺系統(tǒng)會以不同方式編碼同樣的音位1。這意味著,取決于之前的語音,音位/e/在神經(jīng)方面可能意味著完全不同的東西。
進行聲音分析時,記憶很關(guān)鍵。因為只有在某處實際存儲了之前的印象,才可能將之前的印象與“此刻”的印象相比較。
人類的短期記憶組件既包括感官記憶也包括工作記憶2。人類對聲音的感知依靠聽覺感官記憶(有時稱為余音記憶)。C. Alain等將聽覺感官記憶描述為“聽覺感知的關(guān)鍵初始階段,使聽者可以整合傳入的聽覺信息和存儲的之前的聽覺事件的表示”2。
從計算的角度出發(fā),我們可以將余音記憶視作即刻聽覺印象的短期緩沖區(qū)。
余音記憶的持續(xù)時間有所爭議。D. Massaro基于純音和語音元音掩碼的研究主張持續(xù)時間約為250毫秒,而A. Treisman則根據(jù)雙耳分聽試驗主張持續(xù)時間約為4秒3。從在神經(jīng)網(wǎng)絡(luò)中借鑒余音記憶思路的角度出發(fā),我們不必糾結(jié)感官存儲的余音記憶的固定時長。我們可以在神經(jīng)網(wǎng)絡(luò)上測試幾秒鐘范圍內(nèi)的記憶的效果。
進入循環(huán)
在數(shù)字化頻譜表示中可以相當直截了當?shù)貙崿F(xiàn)感官記憶。我們可以簡單地分配一個環(huán)形緩沖區(qū)用來儲存之前時步的頻譜,儲存頻譜的數(shù)量預(yù)先定義。
環(huán)形緩沖區(qū)是一種數(shù)據(jù)結(jié)構(gòu),其中包含一個視作環(huán)形的數(shù)組,窮盡數(shù)組長度后索引循環(huán)往復(fù)為04.
在我們的例子中,可以使用一個長度由所需記憶量決定的多維數(shù)組,環(huán)形緩沖區(qū)的每個索引指向某一特定時步的完整頻率頻譜。計算出新頻譜后,將其寫入緩沖區(qū),如果緩沖區(qū)已滿,就覆蓋最舊的時步。
在填充緩沖區(qū)的過程中,我們會更新兩個指針:標記最新加入元素的尾指針,和標記最舊元素的頭指針(即緩沖區(qū)的開始)4。
下面是一個環(huán)形緩沖區(qū)的Python實現(xiàn)的例子(作者為Eric Wieser):
class CircularBuffer(): # 初始化NumPy數(shù)組和頭/尾指針 def __init__(self, capacity, dtype=float): self._buffer = np.zeros(capacity, dtype) self._head_index = 0 self._tail_index = 0 self._capacity = capacity # 確保頭指針和尾指針循環(huán)往復(fù) def fix_indices(self): if self._head_index >= self._capacity: self._head_index -= self._capacity self._tail_index -= self._capacity elif self._head_index < 0: ? ? ? ? ? ?self._head_index += self._capacity ? ? ? ? ? ?self._tail_index += self._capacity ? ?# 在緩沖區(qū)中插入新值,如緩沖區(qū)已滿,覆蓋舊值 ? ?def insert(self, value): ? ? ? ?if self.is_full(): ? ? ? ? ? ?self._head_index += 1 ? ? ? ?self._buffer[self._tail_index % self._capacity] = value ? ? ? ?self._tail_index += 1 ? ? ? ?self.fix_indices()
降低輸入尺寸
為了存儲一整秒的頻率頻譜(每時步5毫秒),我們需要一個包含200個元素的緩沖區(qū),其中每個元素包含頻率幅度的數(shù)組。如果我們需要類人的頻譜解析度,這些數(shù)組將包含3500個值。然后200個時步就需要處理700000個值。
將長度為700000的輸入傳給人工神經(jīng)網(wǎng)絡(luò),在算力上太昂貴了。降低頻譜和時域解析度,或者保存更短時期的頻譜信息,可以緩解這一問題。
我們也可以借鑒Wavenet架構(gòu),使用空洞因果卷積(dilated causal convolutions)以優(yōu)化原始音頻樣本中的大量序列數(shù)據(jù)的分析。如A. Van Den Oord等所言,空洞卷積是應(yīng)用于大于自身長度的區(qū)域,以特定步驟跳過輸入值的過濾器5。
最近傳入的頻率數(shù)據(jù)在瞬時聲音分析中起決定性作用,根據(jù)這一假定,我們可以用空洞頻譜緩沖區(qū)來降低算力記憶的大小。
空洞緩沖區(qū)的值可以直接選擇單個值,也可以組合若干時步,提取平均數(shù)或中位數(shù)。
空洞頻譜緩沖區(qū)背后的動機是在記憶中保留最近的頻譜印象的同時,以高效的方式同時保持關(guān)于上下文的部分信息。
下面是使用Gammatone濾波器組構(gòu)造空洞頻譜的代碼片段。注意這里使用的是離線處理,不過濾波器組也同樣可以實時應(yīng)用,在環(huán)形緩沖區(qū)中插入頻譜幀。
from gammatone import gtgramimport numpy as npclass GammatoneFilterbank: def __init__(self, sample_rate, window_time, hop_time, num_filters, cutoff_low): self.sample_rate = sample_rate self.window_time = window_time self.hop_time = hop_time self.num_filters = num_filters self.cutoff_low = cutoff_low def make_spectrogram(self, audio_samples): return gtgram.gtgram(audio_samples, self.sample_rate, self.window_time, self.hop_time, self.num_filters, self.cutoff_low) def make_dilated_spectral_frames(self, audio_samples, num_frames, dilation_factor): spectrogram = self.make_spectrogram(audio_samples) spectrogram = np.swapaxes(spectrogram, 0, 1) dilated_frames = np.zeros((len(spectrogram), num_frames, len(spectrogram[0]))) for i in range(len(spectrogram)): for j in range(num_frames): dilation = np.power(dilation_factor, j) if i - dilation < 0: ? ? ? ? ? ? ? ? ? ?dilated_frames[i][j] = spectrogram[0] ? ? ? ? ? ? ? ?else: ? ? ? ? ? ? ? ? ? ?dilated_frames[i][j] = spectrogram[i - dilation] ? ? ? ?return dilated_frames
可視化空洞頻譜緩沖區(qū)
嵌入緩沖區(qū)
在人類記憶的許多模式中,感官記憶經(jīng)過選擇性記憶這個過濾器,以避免短時記憶信息過載3。由于人類的認知資源有限,分配注意力到特定聽覺感知以優(yōu)化心智能量的消耗是一項優(yōu)勢。
我們可以通過擴展自編碼器神經(jīng)網(wǎng)絡(luò)架構(gòu)實現(xiàn)這一方法?;谶@一架構(gòu),我們可以給它傳入空洞頻率頻譜緩沖,以生成嵌入,而不是僅僅傳入瞬時頻率信息,這就結(jié)合了感官聲音記憶和選擇性注意瓶頸。為了處理序列化信息,我們可以使用序列到序列自編碼器架構(gòu)6。
序列到序列(Seq2Seq)模型通常使用LSTM單元編碼序列數(shù)據(jù)(例如,一個英語句子)為內(nèi)部表示(該表示包含整個句子的壓縮“含義”)。這個內(nèi)部表示之后可以解碼回一個序列(例如,一個含義相同的西班牙語句子)7。
以這種方式得到的聲音嵌入,可以使用算力負擔低的簡單前饋神經(jīng)網(wǎng)絡(luò)分析、處理。
據(jù)下圖所示訓(xùn)練完網(wǎng)絡(luò)后,右半部分(解碼部分)可以“砍掉”,從而得到一個編碼時域頻率信息至壓縮空間的網(wǎng)絡(luò)。在這一領(lǐng)域,Y. Chung等的Audio Word2Vec取得了優(yōu)秀的結(jié)果,通過應(yīng)用Seq2Seq自動編碼器架構(gòu)成功生成了可以描述語音錄音的序列化語音結(jié)構(gòu)的嵌入6。使用更多樣化的輸入數(shù)據(jù),它可以生成以更一般的方式描述聲音的嵌入。
基于Keras
依照之前描述的方法,我們用Keras實現(xiàn)一個生成音頻嵌入的Seq2Seq自動編碼器。我將它稱為聽者網(wǎng)絡(luò),因為它的目的是“聽取”傳入的聲音序列,并將它壓縮為一個更緊湊的有意義表示,以供分析和處理。
我們使用了UrbanSound8K數(shù)據(jù)集(包含3小時左右的音頻)訓(xùn)練這個網(wǎng)絡(luò)。UrbanSound8K數(shù)據(jù)集包含歸好類的環(huán)境音片段。我們使用Gammatone濾波器組處理聲音,并將其分割為8時步的空洞頻譜緩沖區(qū)(每個包含100個頻譜濾波器)。
from keras.models import Modelfrom keras.layers import Input, LSTM, RepeatVectordef prepare_listener(timesteps, input_dim, latent_dim, optimizer_type, loss_type): inputs = Input(shape=(timesteps, input_dim)) encoded = LSTM(int(input_dim / 2), activation="relu", return_sequences=True)(inputs) encoded = LSTM(latent_dim, activation="relu", return_sequences=False)(encoded) decoded = RepeatVector(timesteps)(encoded) decoded = LSTM(int(input_dim / 2), activation="relu", return_sequences=True)(decoded) decoded = LSTM(input_dim, return_sequences=True)(decoded) autoencoder = Model(inputs, decoded) encoder = Model(inputs, encoded) autoencoder.compile(optimizer=optimizer_type, loss=loss_type, metrics=['acc']) return autoencoder, encoder
網(wǎng)絡(luò)架構(gòu)
聽者網(wǎng)絡(luò)的損失函數(shù)使用均方誤差,優(yōu)化算法使用Adagrad,在一張NVIDIA GTX 1070上訓(xùn)練了50個epoch,達到了42%的重建精確度。因為訓(xùn)練耗時比較長,所以我在訓(xùn)練進度看起來還沒有飽和的時候就停止了訓(xùn)練。我很想知道,基于更大的數(shù)據(jù)集和更多算力資源訓(xùn)練后這一模型表現(xiàn)如何。
肯定還有很多可以改進的地方,不過下面的圖像表明,在3.2的壓縮率下,模型捕捉到了輸入序列的大致結(jié)構(gòu)。
-
頻譜
+關(guān)注
關(guān)注
7文章
883瀏覽量
45663 -
數(shù)字化
+關(guān)注
關(guān)注
8文章
8770瀏覽量
61877 -
機器學習
+關(guān)注
關(guān)注
66文章
8423瀏覽量
132746
原文標題:機器聽覺:四、在自編碼器架構(gòu)中加入記憶機制
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論