一、最優尺度不變信噪比(OSISNR)損失函數
??參考:論文解讀 --Optimal scale-invariant signal-to-noise ratio and curriculum learning for monaural multi-spea
??最優尺度不變信噪比(OSI-SNR)是一種用于評估信號質量的指標,特別是在語音分離和增強任務中。OSI-SNR 通過優化估計信號與目標信號之間的相似性,提供了一種更穩定和可靠的信號質量度量。OSI-SNR 的計算步驟如下: s s s表示原始語音信號, s ^ \hat{s} s^表示重建的語音信號。
? s , s ^ ? = ∑ t = 1 T s [ t ] ? s ^ [ t ] \langle s, \hat{s} \rangle = \sum_{t=1}^{T} s[t] \cdot \hat{s}[t] ?s,s^?=t=1∑T?s[t]?s^[t]
∣ s ^ ∣ 2 = ∑ t = 1 T ∣ s ^ [ t ] ∣ 2 |s^{\hat{}}|^2 = \sum_{t=1}^{T} |\hat{s}[t]|^2 ∣s^∣2=t=1∑T?∣s^[t]∣2
λ = ∣ s ^ ∣ 2 ? s , s ^ ? \lambda = \frac{|s^{\hat{}}|^2}{\langle s, \hat{s} \rangle} λ=?s,s^?∣s^∣2?
s target = λ ? s ^ s_{\text{target}} = \lambda \cdot \hat{s} starget?=λ?s^
e noise = s ^ ? s target e_{\text{noise}} = \hat{s} - s_{\text{target}} enoise?=s^?starget?
OSI-SNR = 10 log ? 10 ( ∥ s target ∥ 2 ∥ e noise ∥ 2 ) \text{OSI-SNR} = 10 \log_{10} \left( \frac{\| s_{\text{target}} \|^2}{\| e_{\text{noise}} \|^2} \right) OSI-SNR=10log10?(∥enoise?∥2∥starget?∥2?)
??將 最優尺度不變信噪比(OSI-SNR) 的倒數作為損失函數是一個合理的想法,尤其是在某些情況下,可能會更好地反映模型的性能。OSI-SNR 是一個衡量信號質量的指標,值越高表示信號質量越好。在訓練過程中,我們通常希望最小化損失函數,因此可以考慮將 OSI-SNR 的倒數作為損失函數。參考:基于深層聲學特征的端到端語音分離
方法 1:每一幀的 OSI-SNR 取倒數計算損失,再取均值
- 計算每一幀的 OSI-SNR:
OSI-SNR i = 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) \text{OSI-SNR}_i = 10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) OSI-SNRi?=10log10?(∥enoise,i?∥2∥starget,i?∥2?) - 計算損失:
Loss i = 1 OSI-SNR i + ? = 1 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) + ? \text{Loss}_i = \frac{1}{\text{OSI-SNR}_i + \epsilon} = \frac{1}{10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) + \epsilon} Lossi?=OSI-SNRi?+?1?=10log10?(∥enoise,i?∥2∥starget,i?∥2?)+?1? - 取均值:
Final?Loss = 1 N ∑ i = 1 N Loss i = 1 N ∑ i = 1 N 1 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) + ? \text{Final Loss} = \frac{1}{N} \sum_{i=1}^{N} \text{Loss}_i = \frac{1}{N} \sum_{i=1}^{N} \frac{1}{10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) + \epsilon} Final?Loss=N1?i=1∑N?Lossi?=N1?i=1∑N?10log10?(∥enoise,i?∥2∥starget,i?∥2?)+?1?
方法 2:每一幀的 OSI-SNR 取均值,再取倒數計算損失
- 計算每一幀的 OSI-SNR:
OSI-SNR i = 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) \text{OSI-SNR}_i = 10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) OSI-SNRi?=10log10?(∥enoise,i?∥2∥starget,i?∥2?) - 取均值:
Mean?OSI-SNR = 1 N ∑ i = 1 N OSI-SNR i = 1 N ∑ i = 1 N 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) \text{Mean OSI-SNR} = \frac{1}{N} \sum_{i=1}^{N} \text{OSI-SNR}_i = \frac{1}{N} \sum_{i=1}^{N} 10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) Mean?OSI-SNR=N1?i=1∑N?OSI-SNRi?=N1?i=1∑N?10log10?(∥enoise,i?∥2∥starget,i?∥2?) - 計算損失:
Final?Loss = 1 Mean?OSI-SNR + ? = 1 1 N ∑ i = 1 N 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) + ? \text{Final Loss} = \frac{1}{\text{Mean OSI-SNR} + \epsilon} = \frac{1}{\frac{1}{N} \sum_{i=1}^{N} 10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) + \epsilon} Final?Loss=Mean?OSI-SNR+?1?=N1?∑i=1N?10log10?(∥enoise,i?∥2∥starget,i?∥2?)+?1?
- 方法 1 的最終損失函數是對每一幀的 OSI-SNR 值取倒數后再取均值,強調了每一幀的信號質量。
- 方法 2 的最終損失函數是先計算所有幀的 OSI-SNR 的均值,然后取倒數,提供了一個整體的信號質量評估。
import numpy as np
def calculate_osi_snr_frame(target, estimated):# 獲取幀數和頻點數num_bins, num_frames = target.shapeosi_snr_frames = np.zeros(num_frames)for frame in range(num_frames):dot_product = np.sum(target[:, frame] * estimated[:, frame])estimated_energy = np.sum(np.abs(estimated[:, frame]) ** 2)lambda_opt = estimated_energy / (dot_product + 1e-10) # 防止除以零target_adjusted = lambda_opt * target[:, frame]noise = estimated[:, frame] - target_adjustedtarget_energy = np.sum(np.abs(target_adjusted) ** 2)noise_energy = np.sum(np.abs(noise) ** 2)osi_snr_frames[frame] = 10 * np.log10(target_energy / (noise_energy + 1e-8))return osi_snr_frames
def loss_method_1(osi_snr_frames, epsilon=1e-8):# 每一幀的 OSI-SNR 取倒數計算損失,再取均值losses = 1 / (osi_snr_frames + epsilon)final_loss = np.mean(losses)return final_loss
def loss_method_2(osi_snr_frames, epsilon=1e-8):# 每一幀的 OSI-SNR 取均值,再取倒數計算損失mean_osi_snr = np.mean(osi_snr_frames)final_loss = 1 / (mean_osi_snr + epsilon)return final_loss
# 示例掩蔽矩陣 M1 和 M2
M1 = np.array([[0.5, 0.6, 0.7],[0.8, 0.9, 1.0],[1.1, 1.2, 1.3]])M2 = np.array([[0.4, 0.5, 0.6],[0.7, 0.8, 0.9],[1.0, 1.1, 1.2]])
# 計算每一幀的 OSI-SNR
osi_snr_frames = calculate_osi_snr_frame(M1, M2)
# 計算損失
loss1 = loss_method_1(osi_snr_frames)
loss2 = loss_method_2(osi_snr_frames)
print(f"Loss Method 1 (Frame-wise OSI-SNR): {loss1:.4f}")
print(f"Loss Method 2 (Mean OSI-SNR): {loss2:.4f}")
Loss Method 1 (Frame-wise OSI-SNR): 0.03342
Loss Method 2 (Mean OSI-SNR): 0.03333
二、幅度冪律壓縮均方誤差(MC-MSE)損失函數
??在音頻信號處理和深度學習領域,幅度冪律壓縮均方誤差(Magnitude Compression Mean Squared Error, MC-MSE)是一種重要的損失函數,特別適用于語音增強和音頻分離任務。MC-MSE通過引入幅度壓縮的概念,能夠更有效地處理具有大動態范圍的音頻信號,從而提高模型的性能。
2.1 、原理
??在傳統的均方誤差(MSE)損失函數中,模型對大幅度信號的敏感性可能導致對小幅度信號的學習不足。這種情況在音頻信號處理中尤為明顯,因為音頻信號的幅度通常具有很大的動態范圍。為了解決這個問題,MC-MSE引入了幅度壓縮的機制,通過對信號幅度進行非線性變換,使得模型在訓練過程中能夠更好地關注小幅度信號的特征。
MC-MSE的核心思想是通過冪律壓縮函數對信號幅度進行處理,從而使得損失函數在計算時能夠更均衡地反映不同幅度信號的影響。具體來說,MC-MSE通過對目標信號和預測信號的幅度進行壓縮,來計算它們之間的均方誤差。
2.2、公式
MC-MSE Loss的計算公式可以表示為:
L MC-MSE = 1 N ∑ i = 1 N ( compress ( y i ) ? compress ( y ^ i ) ) 2 L_{\text{MC-MSE}}= \frac{1}{N} \sum_{i=1}^{N} \left( \text{compress}(y_i) - \text{compress}(\hat{y}_i) \right)^2 LMC-MSE?=N1?i=1∑N?(compress(yi?)?compress(y^?i?))2
其中:
- y i y_i yi? 是目標信號的幅度。
- y ^ i \hat{y}_i y^?i? 是模型預測的幅度。
- compress ( ? ) \text{compress}(\cdot) compress(?) 是幅度壓縮函數,通常采用冪律壓縮形式。
幅度壓縮函數的形式為:
compress ( x ) = sign ( x ) ? ∣ x ∣ α \text{compress}(x) = \text{sign}(x) \cdot |x|^\alpha compress(x)=sign(x)?∣x∣α
??在這里, α \alpha α是壓縮因子,通常取值在 0 < α < 1 0 < \alpha < 1 0<α<1 之間。較小的 α \alpha α值會導致更強的壓縮效果,從而使得模型在訓練時能夠更好地學習小幅度信號的特征。通過這種方式,MC-MSE損失函數能夠有效地提高模型在音頻處理任務中的性能,尤其是在復雜的噪聲環境中。它不僅改善了模型對小幅度信號的學習能力,還增強了模型的魯棒性,使其在實際應用中表現得更加出色。
??通過一個具體的例子來說明如何計算兩個掩蔽之間的幅度冪律壓縮均方誤差(MC-MSE)。假設我們有兩個掩蔽 M 1 M_1 M1? 和 M 2 M_2 M2?,它們的維度都是 3 × 3 3 \times 3 3×3(即3個時間幀和3個頻率bin),并且我們選擇壓縮指數 p = 0.3 p = 0.3 p=0.3。
??首先,我們對兩個掩蔽應用冪律壓縮。假設 M 1 M_1 M1? 和 M 2 M_2 M2?的值如下:
M 1 = [ 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 ] M_1 = \begin{bmatrix} 0.5 & 0.6 & 0.7 \\ 0.8 & 0.9 & 1.0 \\ 1.1 & 1.2 & 1.3 \end{bmatrix} M1?= ?0.50.81.1?0.60.91.2?0.71.01.3? ?
M 2 = [ 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 ] M_2 = \begin{bmatrix} 0.4 & 0.5 & 0.6 \\ 0.7 & 0.8 & 0.9 \\ 1.0 & 1.1 & 1.2 \end{bmatrix} M2?= ?0.40.71.0?0.50.81.1?0.60.91.2? ?
應用冪律壓縮 p = 0.3 p = 0.3 p=0.3后,我們得到:
M 1 p = [ 0. 5 0.3 0. 6 0.3 0. 7 0.3 0. 8 0.3 0. 9 0.3 1. 0 0.3 1. 1 0.3 1. 2 0.3 1. 3 0.3 ] M_1^p = \begin{bmatrix} 0.5^{0.3} & 0.6^{0.3} & 0.7^{0.3} \\ 0.8^{0.3} & 0.9^{0.3} & 1.0^{0.3} \\ 1.1^{0.3} & 1.2^{0.3} & 1.3^{0.3} \end{bmatrix} M1p?= ?0.50.30.80.31.10.3?0.60.30.90.31.20.3?0.70.31.00.31.30.3? ?
M 2 p = [ 0. 4 0.3 0. 5 0.3 0. 6 0.3 0. 7 0.3 0. 8 0.3 0. 9 0.3 1. 0 0.3 1. 1 0.3 1. 2 0.3 ] M_2^p = \begin{bmatrix} 0.4^{0.3} & 0.5^{0.3} & 0.6^{0.3} \\ 0.7^{0.3} & 0.8^{0.3} & 0.9^{0.3} \\ 1.0^{0.3} & 1.1^{0.3} & 1.2^{0.3} \end{bmatrix} M2p?= ?0.40.30.70.31.00.3?0.50.30.80.31.10.3?0.60.30.90.31.20.3? ?
??接下來,我們計算兩個壓縮后的掩蔽之間的均方誤差。具體地,對于每個元素,我們計算差的平方,然后對所有元素求平均。
L MC-MSE = 1 9 ( ( 0. 5 0.3 ? 0. 4 0.3 ) 2 + ( 0. 6 0.3 ? 0. 5 0.3 ) 2 + … + ( 1. 3 0.3 ? 1. 2 0.3 ) 2 ) = 0.00135 L_{\text{MC-MSE}} = \frac{1}{9} \left( (0.5^{0.3} - 0.4^{0.3})^2 \\+ (0.6^{0.3} - 0.5^{0.3})^2 \\+ \ldots + (1.3^{0.3} - 1.2^{0.3})^2 \right) = 0.00135 LMC-MSE?=91?((0.50.3?0.40.3)2+(0.60.3?0.50.3)2+…+(1.30.3?1.20.3)2)=0.00135
import numpy as npdef calculate_mc_mse(M1, M2, p=0.3):compressed_M1 = M1 ** pcompressed_M2 = M2 ** pmse = np.mean((compressed_M1 - compressed_M2) ** 2)return mseM1 = np.array([[0.5, 0.6, 0.7], [0.8, 0.9, 1.0], [1.1, 1.2, 1.3]])
M2 = np.array([[0.4, 0.5, 0.6], [0.7, 0.8, 0.9], [1.0, 1.1, 1.2]])mc_mse = calculate_mc_mse(M1, M2, p=0.3)
print("MC-MSE:", mc_mse)
MC-MSE: 0.00135
三、融合損失函數
參考:AEC論文解讀 – A Deep Hierarchical Fusion Network for Fullband Acoustic Echo Cancellation
??結合 最優尺度不變信噪比(OSI-SNR) 和 幅度冪律壓縮均方誤差(MC-MSE) 的損失函數,可以通過超參數 γ \gamma γ 加權來形成最終的損失函數。根據您提供的公式,最終的損失計算公式可以表示為:
L = L OSI-SNR + γ L MC-MSE L = L_{\text{OSI-SNR}} + \gamma L_{\text{MC-MSE}} L=LOSI-SNR?+γLMC-MSE?
-
OSI-SNR 損失【方法 1】:
L OSI-SNR = 1 N ∑ i = 1 N Loss i = 1 N ∑ i = 1 N 1 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) + ? L_{\text{OSI-SNR}}= \frac{1}{N} \sum_{i=1}^{N} \text{Loss}_i = \frac{1}{N} \sum_{i=1}^{N} \frac{1}{10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) + \epsilon} LOSI-SNR?=N1?i=1∑N?Lossi?=N1?i=1∑N?10log10?(∥enoise,i?∥2∥starget,i?∥2?)+?1? -
MC-MSE 損失:
L MC-MSE = 1 N ∑ i = 1 N ( compress ( y i ) ? compress ( y ^ i ) ) 2 L_{\text{MC-MSE}}= \frac{1}{N} \sum_{i=1}^{N} \left( \text{compress}(y_i) - \text{compress}(\hat{y}_i) \right)^2 LMC-MSE?=N1?i=1∑N?(compress(yi?)?compress(y^?i?))2 -
最終損失公式:
L = L OSI-SNR + γ ? L MC-MSE = 1 N ∑ i = 1 N Loss i = 1 N ∑ i = 1 N 1 10 log ? 10 ( ∥ s target , i ∥ 2 ∥ e noise , i ∥ 2 ) + ? + γ ? 1 N ∑ i = 1 N ( compress ( y i ) ? compress ( y ^ i ) ) 2 L = L_{\text{OSI-SNR}} + \gamma \cdot L_{\text{MC-MSE}}= \frac{1}{N} \sum_{i=1}^{N} \text{Loss}_i \\= \frac{1}{N} \sum_{i=1}^{N} \frac{1}{10 \log_{10} \left( \frac{\| s_{\text{target}, i} \|^2}{\| e_{\text{noise}, i} \|^2} \right) + \epsilon} +\gamma \cdot\frac{1}{N} \sum_{i=1}^{N} \left( \text{compress}(y_i) - \text{compress}(\hat{y}_i) \right)^2 L=LOSI-SNR?+γ?LMC-MSE?=N1?i=1∑N?Lossi?=N1?i=1∑N?10log10?(∥enoise,i?∥2∥starget,i?∥2?)+?1?+γ?N1?i=1∑N?(compress(yi?)?compress(y^?i?))2
- γ \gamma γ:需要調試的經驗值。
- ? \epsilon ?:一個小常數,用于防止除以零的情況。
- 這個損失函數通過加權結合了 OSI-SNR 和 MC-MSE,能夠同時考慮信號的重建質量和相似性。
- 通過調整 γ \gamma γ 的值,可以控制兩種損失在最終損失中的相對重要性,從而優化模型的性能。
import numpy as np
def calculate_osi_snr_frame(target, estimated):num_bins, num_frames = target.shapeosi_snr_frames = np.zeros(num_frames)for frame in range(num_frames):dot_product = np.sum(target[:, frame] * estimated[:, frame])estimated_energy = np.sum(np.abs(estimated[:, frame]) ** 2)lambda_opt = estimated_energy / (dot_product + 1e-10) # 防止除以零target_adjusted = lambda_opt * target[:, frame]noise = estimated[:, frame] - target_adjustedtarget_energy = np.sum(np.abs(target_adjusted) ** 2)noise_energy = np.sum(np.abs(noise) ** 2)osi_snr_frames[frame] = 10 * np.log10(target_energy / (noise_energy + 1e-8))return osi_snr_frames
def mc_mse_loss(target, estimated, gamma=0.3):compressed_target = np.power(np.abs(target), gamma)compressed_estimated = np.power(np.abs(estimated), gamma)loss = np.mean((compressed_estimated - compressed_target) ** 2)return loss
def combined_loss(target, estimated, gamma=15):# 計算每一幀的 OSI-SNRosi_snr_frames = calculate_osi_snr_frame(target, estimated)# 計算 OSI-SNR 損失osi_snr_loss = np.mean(1 / (osi_snr_frames + 1e-10))print('osi_snr_loss:', osi_snr_loss)# 計算 MC-MSE 損失mc_mse_value = mc_mse_loss(target, estimated)print('mc_mse_value:', mc_mse_value)# 計算最終損失final_loss = osi_snr_loss + gamma * mc_mse_valuereturn final_loss
# 示例掩蔽矩陣 M1 和 M2
M1 = np.array([[0.5, 0.6, 0.7],[0.8, 0.9, 1.0],[1.1, 1.2, 1.3]])M2 = np.array([[0.4, 0.5, 0.6],[0.7, 0.8, 0.9],[1.0, 1.1, 1.2]])
# 計算最終損失
loss_value = combined_loss(M1, M2, gamma=15)
print(f'Final Loss: {loss_value:.5f}')
osi_snr_loss: 0.033421230071106235
mc_mse_value: 0.0013543901266690674
Final Loss: 0.05374