編者按:Kanda機(jī)器學(xué)習(xí)工程師Daniel Rothmann撰寫(xiě)的機(jī)器聽(tīng)覺(jué)系列第四篇,講解如何在聲音頻譜嵌入中加入記憶機(jī)制。
歡迎回來(lái)!這一系列文章將詳細(xì)介紹奧胡斯大學(xué)和智能揚(yáng)聲器生產(chǎn)商Dynaudio A/S合作開(kāi)發(fā)的實(shí)時(shí)音頻信號(hào)處理框架。
如果你錯(cuò)過(guò)了之前的文章,可以點(diǎn)擊下面的鏈接查看:
AI在音頻處理上的潛力
基于頻譜圖和CNN處理音頻有何問(wèn)題?
基于自編碼器學(xué)習(xí)聲音嵌入表示
在上一篇文章中,我們介紹了人類(lèi)是如何體驗(yàn)聲音的,在耳蝸形成頻譜印象,接著由腦干核團(tuán)“編碼”,并借鑒其思路,基于自編碼器學(xué)習(xí)聲音頻譜嵌入。在這篇文章中,我們將探索如何構(gòu)建用來(lái)理解聲音的人工神經(jīng)網(wǎng)絡(luò),并在頻譜聲音嵌入生成中集成記憶。
余音記憶
聲音事件的含義,很大程度上源于頻譜特征間的時(shí)域相互關(guān)系。
有一個(gè)事實(shí)可以作為例證,取決于語(yǔ)音的時(shí)域上下文的不同,人類(lèi)聽(tīng)覺(jué)系統(tǒng)會(huì)以不同方式編碼同樣的音位1。這意味著,取決于之前的語(yǔ)音,音位/e/在神經(jīng)方面可能意味著完全不同的東西。
進(jìn)行聲音分析時(shí),記憶很關(guān)鍵。因?yàn)橹挥性谀程帉?shí)際存儲(chǔ)了之前的印象,才可能將之前的印象與“此刻”的印象相比較。
人類(lèi)的短期記憶組件既包括感官記憶也包括工作記憶2。人類(lèi)對(duì)聲音的感知依靠聽(tīng)覺(jué)感官記憶(有時(shí)稱為余音記憶)。C. Alain等將聽(tīng)覺(jué)感官記憶描述為“聽(tīng)覺(jué)感知的關(guān)鍵初始階段,使聽(tīng)者可以整合傳入的聽(tīng)覺(jué)信息和存儲(chǔ)的之前的聽(tīng)覺(jué)事件的表示”2。
從計(jì)算的角度出發(fā),我們可以將余音記憶視作即刻聽(tīng)覺(jué)印象的短期緩沖區(qū)。
余音記憶的持續(xù)時(shí)間有所爭(zhēng)議。D. Massaro基于純音和語(yǔ)音元音掩碼的研究主張持續(xù)時(shí)間約為250毫秒,而A. Treisman則根據(jù)雙耳分聽(tīng)試驗(yàn)主張持續(xù)時(shí)間約為4秒3。從在神經(jīng)網(wǎng)絡(luò)中借鑒余音記憶思路的角度出發(fā),我們不必糾結(jié)感官存儲(chǔ)的余音記憶的固定時(shí)長(zhǎng)。我們可以在神經(jīng)網(wǎng)絡(luò)上測(cè)試幾秒鐘范圍內(nèi)的記憶的效果。
進(jìn)入循環(huán)
在數(shù)字化頻譜表示中可以相當(dāng)直截了當(dāng)?shù)貙?shí)現(xiàn)感官記憶。我們可以簡(jiǎn)單地分配一個(gè)環(huán)形緩沖區(qū)用來(lái)儲(chǔ)存之前時(shí)步的頻譜,儲(chǔ)存頻譜的數(shù)量預(yù)先定義。
環(huán)形緩沖區(qū)是一種數(shù)據(jù)結(jié)構(gòu),其中包含一個(gè)視作環(huán)形的數(shù)組,窮盡數(shù)組長(zhǎng)度后索引循環(huán)往復(fù)為04.
在我們的例子中,可以使用一個(gè)長(zhǎng)度由所需記憶量決定的多維數(shù)組,環(huán)形緩沖區(qū)的每個(gè)索引指向某一特定時(shí)步的完整頻率頻譜。計(jì)算出新頻譜后,將其寫(xiě)入緩沖區(qū),如果緩沖區(qū)已滿,就覆蓋最舊的時(shí)步。
在填充緩沖區(qū)的過(guò)程中,我們會(huì)更新兩個(gè)指針:標(biāo)記最新加入元素的尾指針,和標(biāo)記最舊元素的頭指針(即緩沖區(qū)的開(kāi)始)4。
下面是一個(gè)環(huán)形緩沖區(qū)的Python實(shí)現(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()
降低輸入尺寸
為了存儲(chǔ)一整秒的頻率頻譜(每時(shí)步5毫秒),我們需要一個(gè)包含200個(gè)元素的緩沖區(qū),其中每個(gè)元素包含頻率幅度的數(shù)組。如果我們需要類(lèi)人的頻譜解析度,這些數(shù)組將包含3500個(gè)值。然后200個(gè)時(shí)步就需要處理700000個(gè)值。
將長(zhǎng)度為700000的輸入傳給人工神經(jīng)網(wǎng)絡(luò),在算力上太昂貴了。降低頻譜和時(shí)域解析度,或者保存更短時(shí)期的頻譜信息,可以緩解這一問(wèn)題。
我們也可以借鑒Wavenet架構(gòu),使用空洞因果卷積(dilated causal convolutions)以優(yōu)化原始音頻樣本中的大量序列數(shù)據(jù)的分析。如A. Van Den Oord等所言,空洞卷積是應(yīng)用于大于自身長(zhǎng)度的區(qū)域,以特定步驟跳過(guò)輸入值的過(guò)濾器5。
最近傳入的頻率數(shù)據(jù)在瞬時(shí)聲音分析中起決定性作用,根據(jù)這一假定,我們可以用空洞頻譜緩沖區(qū)來(lái)降低算力記憶的大小。
空洞緩沖區(qū)的值可以直接選擇單個(gè)值,也可以組合若干時(shí)步,提取平均數(shù)或中位數(shù)。
空洞頻譜緩沖區(qū)背后的動(dòng)機(jī)是在記憶中保留最近的頻譜印象的同時(shí),以高效的方式同時(shí)保持關(guān)于上下文的部分信息。
下面是使用Gammatone濾波器組構(gòu)造空洞頻譜的代碼片段。注意這里使用的是離線處理,不過(guò)濾波器組也同樣可以實(shí)時(shí)應(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ū)
在人類(lèi)記憶的許多模式中,感官記憶經(jīng)過(guò)選擇性記憶這個(gè)過(guò)濾器,以避免短時(shí)記憶信息過(guò)載3。由于人類(lèi)的認(rèn)知資源有限,分配注意力到特定聽(tīng)覺(jué)感知以優(yōu)化心智能量的消耗是一項(xiàng)優(yōu)勢(shì)。
我們可以通過(guò)擴(kuò)展自編碼器神經(jīng)網(wǎng)絡(luò)架構(gòu)實(shí)現(xiàn)這一方法?;谶@一架構(gòu),我們可以給它傳入空洞頻率頻譜緩沖,以生成嵌入,而不是僅僅傳入瞬時(shí)頻率信息,這就結(jié)合了感官聲音記憶和選擇性注意瓶頸。為了處理序列化信息,我們可以使用序列到序列自編碼器架構(gòu)6。
序列到序列(Seq2Seq)模型通常使用LSTM單元編碼序列數(shù)據(jù)(例如,一個(gè)英語(yǔ)句子)為內(nèi)部表示(該表示包含整個(gè)句子的壓縮“含義”)。這個(gè)內(nèi)部表示之后可以解碼回一個(gè)序列(例如,一個(gè)含義相同的西班牙語(yǔ)句子)7。
以這種方式得到的聲音嵌入,可以使用算力負(fù)擔(dān)低的簡(jiǎn)單前饋神經(jīng)網(wǎng)絡(luò)分析、處理。
據(jù)下圖所示訓(xùn)練完網(wǎng)絡(luò)后,右半部分(解碼部分)可以“砍掉”,從而得到一個(gè)編碼時(shí)域頻率信息至壓縮空間的網(wǎng)絡(luò)。在這一領(lǐng)域,Y. Chung等的Audio Word2Vec取得了優(yōu)秀的結(jié)果,通過(guò)應(yīng)用Seq2Seq自動(dòng)編碼器架構(gòu)成功生成了可以描述語(yǔ)音錄音的序列化語(yǔ)音結(jié)構(gòu)的嵌入6。使用更多樣化的輸入數(shù)據(jù),它可以生成以更一般的方式描述聲音的嵌入。
基于Keras
依照之前描述的方法,我們用Keras實(shí)現(xiàn)一個(gè)生成音頻嵌入的Seq2Seq自動(dòng)編碼器。我將它稱為聽(tīng)者網(wǎng)絡(luò),因?yàn)樗哪康氖恰奥?tīng)取”傳入的聲音序列,并將它壓縮為一個(gè)更緊湊的有意義表示,以供分析和處理。
我們使用了UrbanSound8K數(shù)據(jù)集(包含3小時(shí)左右的音頻)訓(xùn)練這個(gè)網(wǎng)絡(luò)。UrbanSound8K數(shù)據(jù)集包含歸好類(lèi)的環(huán)境音片段。我們使用Gammatone濾波器組處理聲音,并將其分割為8時(shí)步的空洞頻譜緩沖區(qū)(每個(gè)包含100個(gè)頻譜濾波器)。
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)
聽(tīng)者網(wǎng)絡(luò)的損失函數(shù)使用均方誤差,優(yōu)化算法使用Adagrad,在一張NVIDIA GTX 1070上訓(xùn)練了50個(gè)epoch,達(dá)到了42%的重建精確度。因?yàn)橛?xùn)練耗時(shí)比較長(zhǎng),所以我在訓(xùn)練進(jìn)度看起來(lái)還沒(méi)有飽和的時(shí)候就停止了訓(xùn)練。我很想知道,基于更大的數(shù)據(jù)集和更多算力資源訓(xùn)練后這一模型表現(xiàn)如何。
肯定還有很多可以改進(jìn)的地方,不過(guò)下面的圖像表明,在3.2的壓縮率下,模型捕捉到了輸入序列的大致結(jié)構(gòu)。
-
頻譜
+關(guān)注
關(guān)注
7文章
869瀏覽量
45459 -
數(shù)字化
+關(guān)注
關(guān)注
8文章
8351瀏覽量
61382 -
機(jī)器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8306瀏覽量
131841
原文標(biāo)題:機(jī)器聽(tīng)覺(jué):四、在自編碼器架構(gòu)中加入記憶機(jī)制
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論