简介:JADE盲源分离算法的原理讲解与MATLAB程序实现,面向已具备数字信号处理与线性代数基础、希望动手复现高阶累积量类盲分离方法的高年级本科生、研究生及工程技术人员。内容从盲信号分离的基本问题切入:在源信号与混合方式均未知的前提下,仅凭多路传感器观测恢复原始信号,进而说明JADE如何利用信号的四阶累积量(也有采用三阶累积量的做法)刻画非高斯性,并交代这类方法的适用边界——源信号需统计独立、至多允许一个高斯信号,对非白与非平稳特性不作考虑。压缩包约426KB,以zip形式组织,主体为MATLAB程序及配套说明,可在MATLAB环境中直接运行调试、观察分离效果。已有953人学习。读者可据此理解累积量矩阵的构造思路、掌握算法流程与参数设置,并把方法迁移到语音等实际信号的分离实验中。
1. JADE盲源分离算法原理以及MATLAB程序:从混合录音里把三路源信号拆开
JADE盲源分离算法原理以及MATLAB程序,落到工程里就是:多路传感器录到混合信号,没有混合矩阵,也没有干净参考源,仍想把不同源拆开。JADE 走高阶累积量路线,先白化去掉二阶相关,再用四阶累积量切片和联合对角化找旋转。它适合非高斯源、观测数不少于源数、样本量够的离线分析;高斯源占主导时,别指望它变魔术。
这套方法最容易被误解成跑一个函数就完事。真正决定结果的是白化后的维度、累积量切片估计质量,以及联合对角化有没有把非对角元素压下去。做声学、振动、生物电或通信混合信号分析,手里有 MATLAB,想从波形和相关系数矩阵判断结果是否可信,可以照着复现。
我一般把 JADE 当离线分析工具,不是实时黑匣子:先小样本跑通,再看参数边界,最后才上真实数据。
2. JADE的数学骨架:白化把混合矩阵压成旋转,四阶累积量再把它拧回来
2.1 中心化与白化:为什么二阶统计量只能走到正交混合
观测模型先写成x = A*s + n,x是n x T的观测矩阵,s是m x T的源矩阵,A是n x m的混合矩阵。JADE 要求n >= m,并且A列满秩。第一步永远是中心化,把每个观测通道减去自己的均值;这一步不做,后面的四阶累积量会把均值项混进去,切片矩阵看起来“有值”,但联合对角化对不齐。
中心化之后做白化。协方差矩阵Rxx = E[x*x'],做特征分解Rxx = E*D*E',白化矩阵取W = D^(-1/2)*E',白化数据z = W*x。如果源之间二阶不相关、噪声可忽略,白化后的混合关系变成z = U*s,U = W*A是正交矩阵。也就是说,白化把“未知矩阵”压成了“未知旋转”。这一步很关键,JADE 后面的搜索空间从任意满秩矩阵缩小到正交矩阵,参数少了很多。
但二阶统计量只能走到这里。任意正交旋转Q作用在z上,协方差仍然是单位阵:E[Q*z*z'*Q'] = Q*Q' = I。所以只看协方差,无法判断应该选哪一个旋转。JADE 的切入点就是四阶累积量:非高斯源在四阶统计量上留下方向信息,而高斯源的四阶累积量理论为零,白化后的旋转对高斯源没有可辨方向。实际程序里,白化阈值不要拍脑袋,先看特征值降序排列,保留明显大于噪声底的那几个维度;保留多了,噪声维度会进累积量切片;保留少了,源被强行压掉,后面怎么调都分离不回来。
2.2 四阶累积量切片:非高斯源的“指纹”怎么变成矩阵
对零均值随机变量,四阶累积量写成:
cum(y1,y2,y3,y4) = E[y1*y2*y3*y4] - E[y1*y2]*E[y3*y4] - E[y1*y3]*E[y2*y4] - E[y1*y4]*E[y2*y3]
白化后的z已经零均值、协方差接近单位阵。固定一个通道索引k,构造切片矩阵C_k(i,j) = cum(z_i,z_j,z_k,z_k)。这个矩阵的尺寸是n x n,n是白化后保留的维度。每个k给一个切片,n个切片组成一组矩阵。它们不是随便选的:如果z = U*s,且源之间四阶累积量互不耦合,那么C_k可以写成U*diag(kappa_l*U(k,l)^2)*U',kappa_l是第l个源的四阶累积量。不同k的切片都被同一个U对角化,这就是联合对角化的理论依据。
标准 JADE 的全称是 Joint Approximate Diagonalization of Eigenmatrices,直译是“特征矩阵的联合近似对角化”。标准实现会先算四阶累积量张量,再对张量展开矩阵做特征分解,取前m个特征矩阵M_i,构造Q(M_i),再联合对角化。工程上如果通道数不多,直接用上面的切片矩阵集合C_k更省内存,也更容易在 MATLAB 里写清楚。两者共享同一个核心:找同一个正交矩阵,让一组矩阵同时接近对角。切片版对非高斯源有效,但对高斯源仍然无解,因为kappa_l = 0时切片矩阵接近零矩阵,联合对角化没有方向可找。
这里有一个常见误判:有人看到切片矩阵元素很小,就以为程序写错了。实际上如果源接近高斯,或者样本太短,切片矩阵本身就会小且抖。判断时要看相对结构,不是看绝对值大小;把切片矩阵打印出来,非对角元素如果和噪声一个量级,联合对角化压不下去是正常的。
2.3 联合对角化:JADE 里的 Jacobi 旋转在优化什么
给定一组对称矩阵C_1...C_n,联合对角化要找正交矩阵V,让每个V'*C_k*V的非对角元素平方和尽量小。目标函数可以写成:
J(V) = sum_k off(V'*C_k*V)^2
off()表示取非对角部分。V的列就是估计的源方向,分离信号y = V'*z。因为z是白化数据,V是正交矩阵,所以y的协方差也接近单位阵;源的幅值、排序和相位不可能靠白化恢复,这是盲源分离的固有不确定性,不是程序 bug。
Jacobi 方法把V拆成一串平面旋转。每次只作用在两个通道p、q上,旋转矩阵是[c -s; s c],c = cos(theta),s = sin(theta)。对每一对(p,q),在[-pi/4, pi/4]里扫角度,选让总非对角平方和最小的角度,更新V,再处理下一对。这个做法比直接对V做梯度下降更直观,代码也短。代价是计算量随通道数、矩阵数、角度网格数上升;通道数到 8 以上时,扫描网格要收一收,或者改用闭式角度更新。
联合对角化的收敛判据不要只看迭代次数。每次迭代打印off_now,正常情况下它应该单调下降并逐渐变平;如果上下跳,常见原因是切片矩阵没有对称化、旋转方向写反,或者白化后维度里混进了强噪声通道。tol是绝对阈值,矩阵元素量级不同时要按数据尺度调,不要把所有数据都套同一个1e-8。
3. MATLAB程序从零跑通:三路仿真源、白化、累积量切片和联合对角化
3.1 先造可复现的混合信号与观测矩阵
先给一个三源三观测的仿真脚本。源信号选正弦、方波、稀疏脉冲:正弦的四阶累积量不为零但偏小,方波和脉冲的非高斯性更明显,三路混在一起能看出 JADE 是不是真的在找方向。混合矩阵固定,噪声只加一点点,方便第一次跑通时把问题锁在算法而不是数据上。
%% demo_jade.m clear; clc; close all; fs = 1000; T = 4000; t = (0:T-1)/fs; s1 = sin(2*pi*50*t); s2 = sign(sin(2*pi*7*t)); u = rand(1,T); s3 = (u < 0.05) .* randn(1,T) * 3; s = [s1; s2; s3]; s = s - mean(s,2); A = [1 0.6 0.3; 0.4 1 0.5; 0.2 0.7 1]; x = A*s + 0.01*randn(size(s)); [y,V,W,z,C] = jade_sep(x, 30, 1e-8, 91); R = corrcoef([s' y']); disp('源与分离信号的相关系数矩阵(行:源,列:分离)'); disp(R(1:3,4:6)); figure; for i = 1:3 subplot(3,1,i); plot(t,s(i,:),'b'); hold on; plot(t,y(i,:),'r'); title(['源 ',num2str(i),'(蓝)与分离 ',num2str(i),'(红)']); xlabel('时间/s'); ylabel('幅值'); end这个脚本里fs、T只决定时间轴和样本长度,A是人为设定的混合矩阵,0.01*randn是观测噪声。jade_sep的四个输入分别是观测矩阵、最大迭代次数、联合对角化阈值、角度扫描网格数。输出里y是分离信号,V是联合对角化旋转,W是白化矩阵,z是白化数据,C是累积量切片。第一次跑不要急着看波形顺序,先看相关系数矩阵:每一行源和某一列分离信号的相关绝对值应该明显高于其他列,排序错位是正常的,幅值缩放也是正常的。
3.2 白化与四阶累积量切片的 MATLAB 实现
把下面这段存成jade_sep.m,它包含主函数、白化局部函数和累积量切片局部函数。MATLAB 的函数文件里可以带局部函数,调用时只暴露jade_sep。
function [y, V, W, z, C] = jade_sep(x, max_iter, tol, grid) % JADE 盲源分离的切片版实现 % x: n x T,行是观测通道,列是采样点 if nargin < 2 || isempty(max_iter), max_iter = 30; end if nargin < 3 || isempty(tol), tol = 1e-8; end if nargin < 4 || isempty(grid), grid = 91; end [z, W] = whiten_local(x); n = size(z,1); C = cell(n,1); for k = 1:n C{k} = cum4_slice_local(z, k); C{k} = (C{k} + C{k}') / 2; end [V, ~] = joint_diag_local(C, max_iter, tol, grid); y = V <p> <a href="https://download.csdn.net/download/wzz110011/87607398" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>