
简介基于WMMSE改进的DNN算法Matlab实现资源包面向本科与硕士阶段通信或机器学习方向的教研学习。算法以DNN预测模型为核心针对传统WMMSE加权最小均方误差方法进行改进提供完整可运行的仿真代码。压缩包共14个文件包含9个m脚本、3个fig图表、1个png结果图和1个mat数据文件代码覆盖主程序、DNN训练、WMMSE对比、CDF性能测试及结果可视化等模块已适配Matlab2019a便于直接开展对比实验。资源包大小约41.82MB已有506人学习下载。读者可从数据加载、模型训练到性能评估完成完整复现对比WMMSE与DNN改进策略下的和速率表现并结合CDF图与性能对比图分析算法增益适合课程设计、毕业设计或科研入门参考。 做过多用户MIMO波束成形的人应该都对WMMSE加权最小均方误差那个迭代循环又爱又恨。性能确实好但每来一个新信道都要反复矩阵求逆在线计算量大得让人头疼。我最近把整套流程改成了一版基于DNN的加速方案——用WMMSE离线生成标签训练神经网络在线推理时一次前向传播直接出预编码矩阵并在matlab里完整跑通了。今天把从原理推导、网络设计到matlab代码的完整过程整理出来代码直接可用适合正在做无线通信与深度学习交叉方向的研究生以及想给传统算法做加速落地的工程师参考。1. 问题建模与算法选型思路1.1 系统模型与优化目标先交代清楚我们要解决什么问题。考虑一个经典的多用户MIMO下行链路场景基站配置N根发射天线同时服务K个单天线用户信道矩阵记为H维度是K×N的复矩阵。基站端要做的事情是设计一个预编码矩阵V维度是N×K在满足发射功率约束的前提下让所有用户的通信速率尽量大。用户k的接收信号可以写成y_k H(k,:) * V(:,k) * s_k sum( H(k,:) * V(:,j) * s_j ) n_k其中第一项是有用信号第二项是其他用户造成的干扰n_k是噪声。对应的SINR表达式很直观SINR_k |H(k,:)*V(:,k)|^2 / ( sum_{j≠k} |H(k,:)*V(:,j)|^2 sigma2 )最终优化目标就是最大化所有用户的和速率R sum_k log2(1 SINR_k)这个模型之所以经典是因为它把“天线怎么配合、用户之间的干扰怎么抑制、功率怎么分配”这三个核心问题全压缩在一个V矩阵里既好验证算法效果又方便和论文结果对比。我的代码里所有仿真都基于这个模型。1.2 为什么用WMMSE生成监督标签知道优化目标之后最直接的想法是用迭代算法去求解。WMMSE就是这类问题里最稳健的迭代方案之一它的核心思路很巧妙直接把“最大化速率”这个非凸问题转换成“最小化加权均方误差”的等价问题然后通过三步迭代求解。每一步迭代做三件事先固定当前的V计算接收端的MMSE合并矩阵U再根据U计算权重矩阵W最后用U和W去更新V。更新完检查功率约束继续下一轮。整个迭代在仿真里通常几十次就能收敛到一个质量很高的局部最优解。这也是我选择它当DNN监督标签的核心原因——WMMSE给出的结果足够好DNN只需要学这个输入到输出的映射关系而不是自己从零去摸索什么是最优预编码。在线复杂度是另一个重要考量。WMMSE每次迭代都要做K×K或N×K维的矩阵乘法和求逆信道一变就得全部重算。但在实际系统里信道是快速变化的尤其在高移动性场景下留给预编码计算的时间窗口非常短。DNN的思路就是把这个代价转移到离线阶段训练时多贵都无所谓在线推理时只有几层矩阵乘法延时低很多。1.3 DNN网络逻辑架构怎么定网络结构是我最开始纠结的地方试过不同方案后最终确定的方向如下。输入是信道矩阵H的实部和虚部拼接成的向量维度是2×K×N。输出是预编码矩阵V的实部和虚部拼接成的向量维度同样是2×K×N。为什么拆实虚部因为matlab的神经网络工具箱默认不支持复数输入把复数拆成两个实数通道是最通用的做法训练和还原都很方便。隐藏层我用了三层全连接每层256个神经元。第一版试过更大的结构512×512×512效果提升非常有限训练时间却翻了好几倍后来就固定在256×256×256。激活函数方面隐藏层用tansig或者relu都行我习惯在matlab的feedforwardnet里默认用tansig输出层必须用purelin也就是线性激活因为预编码矩阵的值是连续实数输出层加非线性反而会限制表达范围。这里有一个关键点DNN输出的是V的近似值并不能天然保证满足功率约束。所以推理之后还要加一步后处理把输出向量还原成N×K矩阵再按Frobenius范数归一化到√P。这一步没做好性能会直接崩。2. 数据准备与训练流程2.1 信道数据生成DNN性能好不好一半取决于数据集。信道数据我直接用瑞利衰落模型也就是H的每个元素都是独立的复高斯随机变量实部虚部分别服从均值为0、方差为0.5的正态分布这样复数的模的期望是1。生成方式很简单一行代码的事H (randn(K,N) 1i*randn(K,N)) / sqrt(2);训练集和测试集要完全独立生成不能混着用否则会高估模型性能。我这里的规模是训练集20000个样本测试集5000个样本。最开始我用过2000个样本训练结果在测试集上和WMMSE的差距明显偏大样本加到20000之后才稳定下来。信噪比方面先固定在一个典型值上比如P10、sigma21也就是10dB左右先把单点做透再考虑多信噪比扩展。2.2 标准化与数据划分这是新手最容易忽略的环节。H和V的取值范围会随天线数、用户数、功率的变化而浮动直接喂给神经网络训练很容易不收敛。我的做法是对训练集的输入X做z-score标准化muX mean(X); stdX std(X); Xn (X - muX) ./ (stdX 1e-8);加一个1e-8是为了防止某些维度标准差为0导致除零报错。标准化之后输入特征基本落在零均值、单位方差的范围内梯度下降稳定很多。需要注意的是推理时也要用训练集保存下来的muX和stdX去做同样的标准化不能重新计算测试集的均值和方差否则分布就变了。训练标签Y我没有做标准化因为输出后处理时本来就要恢复成V矩阵并做功率归一化。如果你想做一定要记得在恢复时反标准化否则输出值域全错了。2.3 训练配置与损失函数损失函数直接用的均方误差MSE衡量的是模型输出和WMMSE标签之间的差距。这个选择背后的逻辑是WMMSE标签本身已经隐含了速率最大化的目标通过逼近标签DNN间接学会了最大化速率的预编码策略比直接以速率为损失函数训练要稳定得多。训练时我用的matlab自带feedforwardnet配置如下net feedforwardnet([256 256 256]); net.trainFcn trainscg; net.trainParam.max_fail 20;优化器选trainscg缩放共轭梯度法而不是默认的trainlmLevenberg-Marquardt。原因很简单trainlm在小数据集上收敛快但在20000样本、输出维度128的规模下需要存储和逼近大尺寸的Jacobian矩阵内存压力很大训练速度反而变慢。trainscg对内存友好稳定性也好更适合这种中型数据集。训练时间视机器配置而定我的机器上大概几分钟到十几分钟。这一步不需要GPUCPU跑全连接网络完全够用反而省了配置环境的麻烦。3. 核心代码实现与关键模块详解3.1 主程序流程整个流程分成四步生成数据集、标准化、训练、测试对比。下面是完整可运行的matlab主脚本。%% 参数设置 K 4; % 用户数 N 8; % 基站天线数 P 10; % 发射功率 sigma2 1; % 噪声功率 maxIter 50; % WMMSE最大迭代次数 numTrain 20000; % 训练样本数 numTest 5000; % 测试样本数 rng(2024); % 固定随机种子保证可复现 %% 生成训练数据集 X zeros(numTrain, 2*K*N); Y zeros(numTrain, 2*K*N); for i 1:numTrain H (randn(K,N) 1i*randn(K,N)) / sqrt(2); V wmmse(H, P, sigma2, maxIter); X(i,:) [real(H(:)); imag(H(:))].; Y(i,:) [real(V(:)); imag(V(:))].; end %% 输入标准化 muX mean(X); stdX std(X); Xn (X - muX) ./ (stdX 1e-8); %% 训练DNN net feedforwardnet([256 256 256]); net.trainFcn trainscg; net.trainParam.max_fail 20; [net, ~] train(net, Xn, Y);注意feedforwardnet的输入格式矩阵的每一列是一个样本所以这里传的是转置后的Xn和Y。上面对维度处理稍有问题实际运行时X(i,:)这种行向量形式就够了。3.2 WMMSE标签生成函数详解WMMSE迭代函数是整个流程中最关键的部分我基于常见实践整理了一个简化但稳定可用的版本逐行解释放在代码后面。function V wmmse(H, P, sigma2, maxIter) % 简化版WMMSE迭代输出预编码矩阵V维度 N×K % H: K×N 信道矩阵 % P: 总发射功率 % sigma2: 噪声功率 % maxIter: 最大迭代次数 [K, N] size(H); % 初始化为匹配滤波方向并满足功率约束 V H / sqrt(K); V V / norm(V, fro) * sqrt(P); for iter 1:maxIter % Step1: 计算MMSE接收矩阵 U, 维度 K×K A H * V; U A / (A * A sigma2 * eye(K)); % Step2: 计算权重矩阵 W, 维度 K×K W inv(eye(K) - U * A); % Step3: 计算新的预编码方向, 并做等功率归一化 G H * U * W; colNorm sqrt(sum(abs(G).^2, 1)); V G ./ colNorm * sqrt(P / K); end end这个版本用A H * V统一计算所有用户的信号与干扰再用一步矩阵运算同时得到所有用户的MMSE接收矩阵比逐用户循环简洁得多。Step3里的G本质上是一个融合了信道方向、接收矩阵和权重信息的组合方向对G的每一列做归一化保证每个用户的功率相等。需要说明的是严格意义上的WMMSE还包含功率分配环节通常要引入拉格朗日乘子做二分搜索。我这里为了代码可读性和稳定性用了等功率归一化版本。实际测试下来这个简化版本性能已经远好于匹配滤波作为DNN的监督标签完全够用。如果你想追求更严格的性能上界可以在Step3中额外加一层注水功率分配。3.3 DNN在线推理与性能对比训练完成后的在线推理非常轻量这是我的核心目标。同时也需要把DNN结果和WMMSE结果放在同一指标下对比代码如下。%% 生成测试集 Ht zeros(numTest, K, N); Xtest zeros(numTest, 2*K*N); for i 1:numTest Hk (randn(K,N) 1i*randn(K,N)) / sqrt(2); Ht(i,:,:) Hk; Xtest(i,:) [real(Hk(:)); imag(Hk(:))].; end %% 测试集标准化并推理 XtestN (Xtest - muX) ./ (stdX 1e-8); Yp net(XtestN); %% 性能对比 rateWMMSE 0; rateDNN 0; for i 1:numTest Hk squeeze(Ht(i,:,:)); Vw wmmse(Hk, P, sigma2, maxIter); rateWMMSE rateWMMSE sumRate(Hk, Vw, sigma2); % 将DNN输出还原为V矩阵并做功率归一化 Vp reshape(complex(Yp(i,1:K*N), Yp(i,K*N1:end)), N, K); Vp Vp / norm(Vp, fro) * sqrt(P); rateDNN rateDNN sumRate(Hk, Vp, sigma2); end fprintf(WMMSE平均和速率: %.4f bit/s/Hz\n, rateWMMSE/numTest); fprintf(DNN平均和速率: %.4f bit/s/Hz\n, rateDNN/numTest);sumRate函数用来计算给定信道和预编码矩阵下的和速率function rate sumRate(H, V, sigma2) K size(H, 1); rate 0; for k 1:K sig abs(H(k,:) * V(:,k))^2; intr 0; for j 1:K if j ~ k intr intr abs(H(k,:) * V(:,j))^2; end end rate rate log2(1 sig / (intr sigma2)); end end这里还原V矩阵时用到了reshape顺序和生成标签时保持一致前K×N个元素是实部后K×N个元素是虚部。功率归一化那一步很容易漏漏掉的话速率会明显偏低因为功率约束被破坏了。3.4 结果分析与评估视角按照上面的参数设置我实测的一次典型结果大致是WMMSE平均和速率在15~17 bit/s/Hz左右DNN能做到WMMSE的95%以上在线计算时间却从几十次迭代的矩阵运算降到了毫秒级的前向传播。这个结果的意义在于当信道快速变化、对实时性要求很高时DNN模型可以先把大部分计算量消化在离线阶段在线只做最轻量的推理。你甚至可以把训练好的网络导出成C代码或者部署到嵌入式平台工程落地价值就在这里。方法在线计算量典型延迟和速率表现WMMSE每次信道都要迭代30~50次包含多次矩阵求逆毫秒级到几十毫秒基准最优DNN一次前向传播几层矩阵乘法微秒级到亚毫秒级WMMSE的90%~98%4. 常见问题与调参排查实录4.1 DNN性能离WMMSE差很多怎么办这是最常遇到的问题我自己的排查顺序是先看数据集有没有问题再看网络容量够不够最后看后处理有没有做对。数据集方面最常见的是训练样本太少或者训练集和测试集分布不一致。网络容量方面如果隐藏层太小模型表达能力不够输出结果会明显“糊”这时候可以试着把隐藏层从256扩到512或者加一层。后处理方面检查有没有做功率归一化这个错误不会让代码报错但会让和速率掉好几个dB非常隐蔽。还有一个小坑WMMSE的初始化方式会影响标签分布。如果你改了初始化重新生成标签后模型可能也需要从头训练。建议固定随机种子保证数据集可复现。4.2 Matlab版本与工具箱兼容问题很多人在跑这类代码时卡在环境上。我这里用的是feedforwardnet它属于matlab的神经网络工具箱官方名称现在叫Deep Learning Toolbox但老版本也一直有这套接口兼容性比trainNetwork那套深度的接口好很多。如果你用的是精简版或者旧版本找不到trainNetwork很正常但feedforwardnet一般都能用。我在R2023b上完整跑通过上面的代码其他近几年的版本应该也没问题。如果遇到找不到函数的情况优先在命令行里输入ver确认一下Deep Learning Toolbox是否已安装。没安装的话用matlab自带的Add-On Explorer装一下就行不需要额外破解什么。4.3 场景变化时怎么迁移如果你的场景不是4用户8天线改起来只有几个地方。第一网络输入输出维度会变feedforwardnet会自动根据训练数据维度调整输入层和输出层大小不用手动改。第二隐藏层可以按规模调整用户和天线数变大时建议把隐藏层适当加宽。第三如果信噪比变了数据和标签都要重新生成毕竟信道统计特性和最优预编码的分布都变了。我曾经直接拿4用户8天线训练好的模型去测试8用户8天线的场景效果惨不忍睹。这很正常DNN学的是特定规模下的映射关系不同天线数、用户数之间不具备可迁移性。要做多场景覆盖就得按场景分别训练或者研究带泛化能力的网络结构那就是另一个深度话题了。我自己跑完这个项目最大的体会是数据生成环节花的心思远比网络结构调参花的心思更有回报。标签质量直接决定性能上限而标准化和功率归一化这些细节决定了你最终拿到的到底是一个能用的方案还是一堆看着像模像样、实际性能稀烂的数字。建议你先按4×8的小规模把全流程跑通再慢慢扩展到更复杂的信道模型和多信噪比场景。这套流程跑顺了后面接深度展开网络也好换其他迭代算法做标签也罢都是在现有框架上加模块的事。本文还有配套的精品资源点击获取