本文代碼請見:https://github.com/Ryuk17/SpeechAlgorithmshtml
博客地址(轉載請指明出處):https://www.cnblogs.com/LXP-Never/p/14142108.htmlgit
若是你以爲寫得還不錯,點贊👍,關注是對我最大的支持,謝謝😃github
傳統的語音加強方法基於一些設定好的先驗假設,可是這些先驗假設存在必定的不合理之處。此外傳統語音加強依賴於參數的設定,人工經驗等。隨着深度學習的發展,愈來愈多的人開始注意使用深度學習來解決語音加強問題。因爲單通道使用場景較多,本文就以單通道語音加強爲例。網絡
目前基於深度神經網絡單通道語音加強方法大體能夠分爲兩類:oracle
基於映射的語音加強方法經過訓練神經網絡模型直接學習帶噪語音和純淨語音之間的映射關係,有兩種映射方案:dom
頻譜映射:使用模型預測語音的時頻域表示,以後再將語音的時頻域表示經過波形合成技術恢復成時域信號。使用最多的時頻域表示特徵是短時傅里葉變換譜,利用人耳對相位不敏感的特性,通常只預測短時傅里葉變換幅度譜,並使用混合語音的相位合成預測語音的波形。ide
波形映射:直接將帶噪語音波形輸入到模型,模型直接輸出純淨語音波形的方法。函數
咱們以頻譜映射舉例說明一下:性能
輸入:這裏採用較爲簡單地特徵,即帶噪聲語音信號的幅度譜,也能夠採用其餘的特徵。值得一提的是,若是你的輸入是一幀,對應輸出也是一幀的話效果通常不會很好。所以通常採用擴幀的技術,以下圖所示,即每次輸入除了當前幀外還須要輸入當前幀的前幾幀和後幾幀。這是由於語音具備短時相關性,對輸入幾幀是爲了更好的學習這種相關性學習
spectrum = magnitude * np.exp(1.0j * phase)
Mask這個單詞有的地方翻譯成掩蔽有的地方翻譯成掩膜,我我的傾向於翻譯成「掩蔽」,本文就用掩蔽做爲Mask的翻譯。
咱們都知道語音信號能夠經過時域波形或者頻域的各類頻譜表示,此外語譜圖能夠同時展現時域和頻域的信息,所以被普遍應用,以下圖所示。語譜圖上的像素點就能夠稱爲 時頻單元。
如今咱們假設有兩段語音信號,一段是純淨信號,另外一段是噪聲,他們混合在一塊兒了,時域波形和對應的語譜圖分別以下圖所示:
若是咱們想將純淨語音信號從混合信號中抽離在時域方面是很難作到的。如今咱們從語譜圖(語音的時頻單元)角度入手去解決語音分離問題。首先咱們提出兩個假設:
一、咱們假設信號能量稀疏的,即對於大多數時頻區域它的能量爲0,以下圖所示,咱們能夠看到大多數區域的值,即頻域能量爲0。
二、咱們假設信號能量不相交的,即它們的時頻區域不重疊或者重疊較少,以下圖所示,咱們能夠看到時頻區域不爲0的地方不重疊或者有較少部分的重疊。
基於以上兩點假設,咱們就能夠分離咱們想要的信號和噪聲信號。給可能屬於一個信號源的區域分配掩碼爲1,其他的分配掩碼0,以下圖所示。
咱們經過0和1的二值掩碼而後乘以混合信號的語譜圖就能夠獲得咱們想要喜愛的語譜圖了,以下圖所示。
神經模型通常直接預測時頻掩蔽$M(t,f)$,以後再經過$M(t,f)$與混合語音$Y(t,f)$相乘獲得預測的純淨語音$\hat{S}(t,f)=\hat{M}(t,f)\otimesY(t,y)$,其中$\otimes$表明哈達瑪乘積(Hadamard Product)。在語音加強研究的發展過程當中,研究人員提出了一系列的時頻掩蔽做爲訓練目標:
原理:因爲語音在時頻域上是稀疏分佈的,對於一個具體的時頻單元,語音和噪聲的能量差別一般比較大,所以大多數時頻單元上的信噪比極高或極低。IBM 是對這種現實狀況的簡化描述,將連續的時頻單元信噪比離散化爲兩種狀態 1 和0,在一個時頻單元內:若是語音佔主導(高信噪比),則被標記爲 1;反之若是噪聲佔主導(低信噪比),則標記爲 0。最後將 IBM 和帶噪語音相乘,實際上就是將低信噪比的時頻單元置零,以此達到消除噪聲的目的。
所以,IBM 的值由時頻單元上的信噪比SNR(t,f)和設定的閾值比較以後決定:
$$公式1:I B M(t, f)=\left\{\begin{array}{l}
1, \operatorname{SNR}(t, f)>L C \\
0, \text { else }
\end{array}\right.$$
其中LC爲閾值,通常取0,SNR計算公式爲:
$$公式2:\operatorname{SNR}(t, f)=10 * \log 10\left(\frac{|S(t, f)|^{2}}{|N(t, f)|^{2}}\right)$$
我看到過不少種寫法
def IBM(clean_speech, noise): """計算 ideal binary mask (IBM) Erdogan, Hakan, et al. "Phase-sensitive and recognition-boosted speech separation using deep recurrent neural networks." ICASSP, 2015. :param clean_speech: 純淨語音 STFT :param noise: 噪聲 STFT :return: 純淨語音的理想二值掩膜 IBM """ mask = np.zeros(np.shape(clean_speech), dtype=np.float32) mask[np.abs(clean_speech) >= np.abs(noise)] = 1.0 return mask
第二種
def IBM_SNR(clean_speech, noise_speech): """計算 ideal binary mask (IBM) Erdogan, Hakan, et al. "Phase-sensitive and recognition-boosted speech separation using deep recurrent neural networks." ICASSP, 2015. :param clean_speech: 純淨語音 STFT :param noise_speech: 帶噪語音 STFT :return: 純淨語音的理想二值掩膜 IBM """ _eps = np.finfo(np.float).eps # 避免除以0 theta = 0.5 # a majority vote alpha = 1 # ratio of magnitudes mask = np.divide(np.abs(clean_speech) ** alpha, (_eps + np.abs(noise_speech) ** alpha)) mask[np.where(mask >= theta)] = 1 mask[np.where(mask < theta)] = 0 return mask
第三種
def IBM_SNR(clean_speech, noise_speech,delta_size): """計算 ideal binary mask (IBM) Erdogan, Hakan, et al. "Phase-sensitive and recognition-boosted speech separation using deep recurrent neural networks." ICASSP, 2015. :param clean_speech: 純淨語音 STFT :param noise_speech: 帶噪語音 STFT :return: 純淨語音的理想二值掩膜 IBM """ _eps = np.finfo(np.float).eps # 避免除以0 local_snr = 0 ibm = np.where(10. * np.log10(np.abs(clean_speech) ** 2 / np.abs(noise_speech) ** 2) >= local_snr, 1., 0.) if delta_size > 0: ibm = ibm[:, delta_size: -delta_size] return ibm
原理:基於語音和噪聲正交,即不相關的假設下,即$S(t,f) ⋅ N(t,f) = 0$,IRM直接刻畫了時頻單元內純淨語音能量和帶噪語音能量的比值,是目前使用很是普遍的一種掩蔽方法。
在這個假設下帶噪語音的能量能夠表示爲:
$$公式2:|\boldsymbol{Y}(t, f)|^{2}=|\boldsymbol{S}(t, f)+\boldsymbol{N}(t, f)|^{2}=|\boldsymbol{S}(t, f)|^{2}+|\boldsymbol{N}(t, f)|^{2}$$
所以獲得 IRM 爲:
$$公式3:I R M(t, f)=\left(\frac{|S(t, f)|^{2}}{|Y(t, f)|^{2}}\right)^{\beta} =\left(\frac{|S(t, f)|^{2}}{|S(t, f)|^{2}+|N(t, f)|^{2}}\right)^{\beta}$$
其中$\beta$爲可調節尺度因子,通常取0.5。IRM取值在 0 到 1 之間,值越大表明時頻單元內語音佔的比重越高。另外,IRM 的平方形式就是經典的維納濾波器(Wiener Filter),它是均方偏差意義上的最優濾波器。
def IRM(clean_speech, noise): """計算Compute ideal ratio mask (IRM) "Phase-sensitive and recognition-boosted speech separation using deep recurrent neural networks," in ICASSP 2015, Brisbane, April, 2015. :param clean_speech: 純淨語音 STFT :param noise: 噪音 STFT :return: 在原始音頻域中分離(恢復)的語音 """ _eps = np.finfo(np.float).eps # 防止分母出現0 mask = np.abs(clean_speech) / (np.abs(clean_speech) + np.abs(noise) + _eps) return mask def Wiener_like(clean_speech, noise): """計算Wiener-like Mask "Phase-sensitive and recognition-boosted speech separation using deep recurrent neural networks," in ICASSP 2015, Brisbane, April, 2015. :param clean_speech: 純淨語音 STFT :param noise: 噪音 STFT :return: 在原始音頻域中分離(恢復)的語音 """ _eps = np.finfo(np.float).eps # 防止分母出現0 mask = np.divide((np.abs(clean_speech) ** 2 + _eps), (np.abs(clean_speech) ** 2 + np.abs(noise) ** 2) + _eps) return mask
原理:IAM也稱爲Spectral Magnitude Mask(SMM),不對噪聲和語音作出正交假設,IAM刻畫的也是純淨語音和帶噪語音的能量比值
$$公式4:\operatorname{IAM}(t, f)=\frac{|S(t, f)|}{|Y(t, f)|}$$
因爲在語音和噪聲疊加的過程當中,存在反相相消的狀況,所以並不能保證帶噪語音的幅值老是大於純淨語音的幅值,所以 IAM 的範圍是$[0,+\infty ]$。若是目標中出現很是大的數值,會致使訓練過程出現異常。爲了穩定訓練,通常會將 IAM 進行截斷到必定的範圍內。爲了肯定合適的截斷範圍,咱們能夠在訓練數據上採樣 100 句語音並計算 IAM,就能夠對IAM 的數值範圍獲得一個近似的估計,獲得如圖 3.4 的結果。通常將 IAM 截斷到[0, 1]或者[0, 2]便可,由於只有很是少部分的 IAM 落在了$[2,+\infty ]$的區間內。
圖* IAM數值分佈直方圖
def IAM(clean_speech, noise_speech): """計算ideal amplitude mask (IAM) "Phase-sensitive and recognition-boosted speech separation using deep recurrent neural networks," in ICASSP 2015, Brisbane, April, 2015. :param clean_speech: 純淨語音 STFT :param noise_speech: 帶噪語音 STFT :return: """ _eps = np.finfo(np.float).eps # 避免除以0 mask = np.abs(clean_speech) / (np.abs(noise_speech) + _eps) return mask
原理:PSM考慮到相位偏差的時頻掩蔽
PSM在形式上是 IAM 乘上純淨語音和帶噪語音之間的餘弦類似度
$$公式5:P S M(t, f)=\frac{|S(t, f)|}{|Y(t, f)|} \cos \left(\theta^{S}-\theta^{Y}\right)$$
式中$\theta^{S}-\theta^{Y}$表示純淨語音和帶噪語音的相位差,不難看出,PSM 的取值範圍是$[-\infty,+\infty]$,所以也須要截斷,咱們一樣使用直方圖統計PSM的數值分佈範圍,從下圖能夠看出在0 和 1 附近出現兩個明顯的峯值,這也再次說明了 IBM 目標設計的合理性。爲了方便,通常將 PSM 截斷到[0, 1],或者是適當將截斷的區間放大到[-1, 2]。
PSM數值分佈直方圖
def PSM(clean_speech, noise_speech): """計算ideal phase-sensitive mask (PSM) :param clean_speech: 純淨語音 STFT :param noise_speech:帶噪語音 STFT :return: """ _eps = np.finfo(np.float).eps # 防止分母出現0 clean_speech_phase = np.angle(clean_speech) noise_speech_phase = np.angle(noise_speech) mask = np.abs(clean_speech) / np.abs(noise_speech) * np.cos(clean_speech_phase - noise_speech_phase) # Truncated Phase Sensitive Masking # Theta = np.clip(np.cos(clean_speech_phase-noise_speech_phase), a_min=0., a_max=1.) # mask = np.divide(np.abs(clean_speech), _eps + np.abs(noise_speech)) * Theta return mask
參考文獻:2015_Complex ratio masking for monaural speech separation
原理:在複數域的理想浮值掩膜,同時加強幅度譜和相位譜
$條件:\left\{ \begin{array}{l}Y = {Y_r} + i{Y_i}\\M = {M_r} + i{M_i}\\S = {S_r} + i{S_i}\\{S_{t,f}} = {M_{t,f}}*{Y_{t,f}}\end{array} \right.$==>${S_r} + i{S_i} = ({M_r} + i{M_i})*({Y_r} + i{Y_i}) = ({M_r}{Y_r} - {M_i}{Y_i}) + i({M_r}{Y_i} + {M_i}{Y_r})$,
那麼:$\left\{ \begin{array}{l}{S_r} = {M_r}{Y_r} - {M_i}{Y_i}\\{S_i} = {M_r}{Y_i} + {M_i}{Y_r}\end{array} \right.$ 解方程得:$\left\{ \begin{array}{l}{M_r} = \frac{{{Y_r}{S_r} + {Y_i}{S_i}}}{{Y_r^2 + Y_i^2}}\\{M_i} = \frac{{{Y_r}{S_i} - {Y_i}{S_r}}}{{Y_r^2 + Y_i^2}}\end{array} \right.$
最終:$M_{cIRM} = {M_r} + i{M_i} = {\frac{{{Y_r}{S_r} + {Y_i}{S_i}}}{{Y_r^2 + Y_i^2}}} + i\frac{{{Y_r}{S_i} - {Y_i}{S_r}}}{{Y_r^2 + Y_i^2}}$
式中,$Y$是帶噪語音,$S$是純淨語音。
def cIRM(clean_speech, noise_speech): """使用復理想比率掩碼將語音從源信號的短時傅里葉變換和混合源信號的短時傅里葉變換中分離出來 :param clean_speech:純淨語音 :param noise_speech:帶噪語音 :return: """ cIRM_r = (np.real(noise_speech) * np.real(clean_speech) + np.imag(noise_speech) * np.imag(clean_speech)) / \ (np.real(noise_speech) ** 2 + np.imag(noise_speech) ** 2) cIRM_i = (np.real(noise_speech) * np.imag(clean_speech) - np.imag(noise_speech) * np.real(clean_speech)) / \ (np.real(noise_speech) ** 2 + np.imag(noise_speech) ** 2) mask = cIRM_r + cIRM_i * 1j return mask
語音加強中的大部分掩蔽類方法,均可以當作在特定的假設條件下cIRM 的近似。若是將 cIRM 在直角座標系下分解,cIRM 在實數軸上的投影就是 PSM。若是再將 cIRM在極座標系下分解,cIRM 的模值就是 IAM。而 IRM 又是 IAM 在噪聲語音不相關假設下的簡化形式,IBM 則能夠認爲是 IRM 的二值版本。
各類理想掩蔽的性能比較
度量 | IBM | IRM | IAM | PSM | cIRM |
PESQ | 2.47 | 3.33 | 3.45 | 3.71 | 4.49 |
STOI | 0.91 | 0.94 | 0.97 | 0.97 | 1 |
從上表中咱們能夠看到 cIRM 能夠實現對純淨語音幾乎無損地重構,其餘掩蔽因爲進行了某些特定的假設,因此都會在必定程度上形成性能損失。雖然 cIRM 是最優掩蔽,可是使用其餘簡化的掩蔽方法能夠下降預測的難度。這也是早期的語音加強研究選擇使用 IBM 或者是 IRM 等簡單掩蔽目標的緣由。在模型容量有限的狀況下,cIRM 常常並非最好的選擇,選擇和模型建模能力匹配的目標才能得到最優的加強性能。
可是,這裏存在一個問題,咱們沒法從語譜圖中還原語音信號。爲了解決這一問題,咱們首先還原全部的頻率份量,即對二值掩碼作個鏡像後拼接。假設咱們計算語譜圖時使用的是512點SFTF,咱們通常去前257點進行分析和處理,在這裏咱們將前257點的後255作鏡像,而後拼接在一塊兒獲得512點頻率份量,以下圖所示。
而後根據這個還原語音信號。這裏指的一提的是,在進行STFT後的相位信息要保存,用於還原語音信號。
基於掩蔽的語音加強和基於映射的語音加強模型訓練和加強過程相似,這裏只提幾個重要的地方,其他地方參考上面內容。
enhance_magnitude = np.multiply(magnitude, mask)
首先看下實驗效果,首先是基於映射語音加強的結果:
基於IBM語音加強的結果:
基於IRM語音加強的結果:
訓練代碼:
""" @FileName: IBM.py @Description: Implement IBM @Author: Ryuk @CreateDate: 2020/05/08 @LastEditTime: 2020/05/08 @LastEditors: Please set LastEditors @Version: v0.1 """ import numpy as np import librosa from sklearn.preprocessing import StandardScaler from keras.layers import * from keras.models import Sequential def generateDataset(): mix, sr = librosa.load("./mix.wav", sr=8000) clean,sr = librosa.load("./clean.wav", sr=8000) win_length = 256 hop_length = 128 nfft = 512 mix_spectrum = librosa.stft(mix, win_length=win_length, hop_length=hop_length, n_fft=nfft) clean_spectrum = librosa.stft(clean, win_length=win_length, hop_length=hop_length, n_fft=nfft) mix_mag = np.abs(mix_spectrum).T clean_mag = np.abs(clean_spectrum).T frame_num = mix_mag.shape[0] - 4 feature = np.zeros([frame_num, 257*5]) k = 0 for i in range(frame_num - 4): frame = mix_mag[k:k+5] feature[i] = np.reshape(frame, 257*5) k += 1 snr = np.divide(clean_mag, mix_mag) mask = np.around(snr, 0) mask[np.isnan(mask)] = 1 mask[mask > 1] = 1 label = mask[2:-2] ss = StandardScaler() feature = ss.fit_transform(feature) return feature, label def getModel(): model = Sequential() model.add(Dense(2048, input_dim=1285)) model.add(BatchNormalization()) model.add(LeakyReLU(alpha=0.1)) model.add(Dropout(0.1)) model.add(Dense(2048)) model.add(BatchNormalization()) model.add(LeakyReLU(alpha=0.1)) model.add(Dropout(0.1)) model.add(Dense(2048)) model.add(BatchNormalization()) model.add(LeakyReLU(alpha=0.1)) model.add(Dropout(0.1)) model.add(Dense(257)) model.add(BatchNormalization()) model.add(Activation('sigmoid')) return model def train(feature, label, model): model.compile(optimizer='adam', loss='mse', metrics=['mse']) model.fit(feature, label, batch_size=128, epochs=20, validation_split=0.1) model.save("./model.h5") def main(): feature, label = generateDataset() model = getModel() train(feature, label, model) if __name__ == "__main__": main()
加強代碼:
""" @FileName: Inference.py @Description: Implement Inference @Author: Ryuk @CreateDate: 2020/05/08 @LastEditTime: 2020/05/08 @LastEditors: Please set LastEditors @Version: v0.1 """ import librosa import numpy as np from basic_functions import * import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from keras.models import load_model def show(data, s): plt.figure(1) ax1 = plt.subplot(2, 1, 1) ax2 = plt.subplot(2, 1, 2) plt.sca(ax1) plt.plot(data) plt.sca(ax2) plt.plot(s) plt.show() model = load_model("./model.h5") data, fs = librosa.load("./test.wav", sr=8000) win_length = 256 hop_length = 128 nfft = 512 spectrum = librosa.stft(data, win_length=win_length, hop_length=hop_length, n_fft=nfft) magnitude = np.abs(spectrum).T phase = np.angle(spectrum).T frame_num = magnitude.shape[0] - 4 feature = np.zeros([frame_num, 257 * 5]) k = 0 for i in range(frame_num - 4): frame = magnitude[k:k + 5] feature[i] = np.reshape(frame, 257 * 5) k += 1 ss = StandardScaler() feature = ss.fit_transform(feature) mask = model.predict(feature) mask[mask > 0.5] = 1 mask[mask <= 0.5] = 0 fig = plt.figure() plt.imshow(mask, cmap='Greys', interpolation='none') plt.show() plt.close(fig) magnitude = magnitude[2:-2] en_magnitude = np.multiply(magnitude, mask) phase = phase[2:-2] en_spectrum = en_magnitude.T * np.exp(1.0j * phase.T) frame = librosa.istft(en_spectrum, win_length=win_length, hop_length=hop_length) show(data, frame) librosa.output.write_wav("./output.wav",frame, sr=8000)
【論文】2020_李勁東_基於深度學習的單通道語音加強研究
【博客文章】DNN單通道語音加強(附Demo代碼)
【博客文章】基於Mask的語音分離
【github代碼】speech-segmentation-project/masks.py
【github代碼】ASP/MaskingMethods.py
【github代碼】DC-TesNet/time_domain_mask.py
【github代碼】ASC_baseline/compute_mask.py
值得作一作的項目: