资讯动态

基于DNN的WMMSE波束成形算法Matlab实现与优化

发布时间:2026/9/8 23:27:05 来源:尧图企业网站定制
简介面向本科与硕士教研场景的加权最小均方误差改进深度神经网络预测算法Matlab资源包定位于无线通信与深度学习交叉方向的算法仿真与教学。资源基于Matlab2019a运行包含完整的深度神经网络模型训练、性能测试与绘图脚本并与传统加权最小均方误差算法进行对比便于读者理解深度网络如何逼近或改进经典迭代方案。压缩包共14个文件以9个m脚本为主承担数据生成、模型训练、指标计算与画图等任务另含3个fig仿真图、1个png效果图和1个mat高斯样本数据集整体大小41.82MB结构清晰适合直接打开实践。目前已有506人学习或下载。通过本包读者可获得可复现的深度网络与加权最小均方误差结合实验框架掌握训练样本构造、网络参数调整、累计分布函数绘制等环节并能基于给定代码拓展自己的算法对比或改进实验是快速上手深度网络无线资源管理课题的实用参考。1. 为什么要把 WMMSE 和 DNN 放在一起做过无线通信物理层优化的朋友对 WMMSE 这个名字一定不陌生。Weighted MMSE加权最小均方误差算法是解决多用户 MIMO 下行链路波束成形问题的一个经典框架它的核心思想是把一个原本非凸的速率最大化问题通过迭代方式转换成一系列凸的子问题来逼近最优解。思路漂亮收敛性也有理论保证在很多标准制定和学术研究中都是基准方案。但真正在工程里跑过 WMMSE 的人都知道它有多“重”。每轮迭代要更新接收机权重矩阵、MMSE 接收矩阵、以及发射预编码矩阵如果用户数稍微多一点、天线数上去之后这个迭代过程在实时场景里基本跑不动。尤其是信道状态信息CSI快速变化时你还没来得及迭代收敛信道早已变了算出来的预编码矩阵已经失效了。做系统级仿真时我经常在等 WMMSE 收敛的那个循环里干等那种感觉就像用算盘去解方程组方法对但就是慢得让人抓狂。DNN 来解决这个问题的思路其实很直觉既然 WMMSE 本质上是在做一个从信道矩阵到预编码矩阵的映射而这个映射背后是有规律可循的那能不能用一个深度神经网络把这层映射关系直接“记住”训练阶段用 WMMSE 离线生成海量样本把信道状态信息当作输入、把 WMMSE 迭代收敛的最优预编码结果当作标签让 DNN 去拟合这个从 CSI 到预编码的映射函数。等网络训练好了在线推理时只需要一次前向传播就能得到接近 WMMSE 性能的预编码结果耗时却缩短了几个数量级。这个思路最早火起来是受深度学习在计算机视觉领域取得突破性进展的启发后来被引入无线通信的物理层优化中。我看过的多篇论文里效果最好的方案几乎都是基于 WMMSE 迭代展开iteration unfolding来设计网络结构的而不是简单粗暴地堆一个黑盒全连接网络。前者利用了 WMMSE 算法本身的迭代结构可解释性更强训练收敛更快泛化性能也更好。这篇博文想和你分享的就是如何把 WMMSE 改进的 DNN 算法在 Matlab 中完整落地从算法思路到代码实现再到调参经验一套走完。代码我已经打包好了文章里会把关键模块逐段拆开讲清楚确保你有 Matlab 基础就能跟着复现出来。2. 传统 WMMSE 的痛点与深度学习切入角度2.1 多用户 MIMO 波束成形问题长什么样先明确一下我们到底在优化什么。考虑一个单小区多用户 MIMO 下行链路场景基站配备 Nt 根发射天线服务 K 个单天线用户。第 k 个用户的接收信号可以用下面这个模型描述[ y_k \mathbf{h}_k^H \mathbf{v}k s_k \sum{j \neq k} \mathbf{h}_k^H \mathbf{v}_j s_j n_k ]其中 h_k 是基站到用户 k 的信道向量v_k 是用户 k 的预编码向量s_k 是发射符号n_k 是高斯白噪声。我们希望设计一组预编码向量 {v_1, v_2, ..., v_K}使得所有用户的和速率最大化同时满足基站总发射功率约束。优化问题写出来是[ \max_{{\mathbf{v}k}} \sum{k1}^{K} \log_2 \left(1 \frac{|\mathbf{h}_k^H \mathbf{v}k|^2}{\sum{j \neq k} |\mathbf{h}_k^H \mathbf{v}_j|^2 \sigma^2}\right) ][ \text{s.t.} \quad \sum_{k1}^{K} |\mathbf{v}k|^2 \leq P{\max} ]问题本身是 NP-hard 的因为目标函数关于预编码矩阵是非凸的。传统做法是用 WMMSE 算法把问题等效转换成加权最小均方误差最小化问题然后通过块坐标下降法交替优化各个变量直到收敛到局部最优点。2.2 WMMSE 的迭代过程和计算瓶颈WMMSE 算法的每一次主迭代主要做三件事更新接收机权重 (u_k)、更新均方误差权重 (w_k)、更新预编码向量 (v_k)。这三步交替执行直到预编码矩阵的变化量低于某个阈值。数学上每一步都有闭式解这也是 WMMSE 被广泛使用的原因——每个子问题都有解析表达式不需要调用数值优化工具箱。但在实际仿真中你会发现三个明显的瓶颈第一是迭代次数敏感。收敛速度受信道条件和初始点影响很大坏的情况下需要几十甚至上百次迭代才能达到收敛阈值而每次迭代都涉及多次矩阵乘法和求逆运算。第二是每步更新之间的耦合性强。更新预编码向量时需要用到所有用户的信道信息和当前的权重矩阵计算复杂度大约在 O(K^3) 量级用户数增长时计算量迅速膨胀。第三是难以支持实时决策。在快变信道下信道相干时间可能只有毫秒级WMMSE 根本来不及在信道变化前完成优化计算。这也是我想用 DNN 替代它的最直接动机——把计算负担从在线推理转移到离线训练。2.3 DNN 改进方案的两条技术路线目前主流方案大致分成两类理解它们的区别对后续代码实现非常关键。第一类是Learning to Optimize路线核心思想是把 WMMSE 迭代过程展开成神经网络的多层结构。WMMSE 每次迭代中的变量更新表达式是确定的我把它看作一层“网络层”其中可学习参数替代了原来手工设定的步长或正则系数。这类方法的优点是与原算法的结构高度对齐训练参数少可解释性强收敛性有保障。缺点是网络结构设计受限于原算法形式灵活性差一些。第二类是Learning to Predict路线直接用一个深度神经网络建立 CSI 到预编码矩阵的端到端映射。输入是信道矩阵输出是预编码向量或功率分配方案中间用若干全连接层或卷积层自动提取特征。这类方法更灵活网络结构可以自由设计但训练难度大容易过拟合而且输出的预编码矩阵不保证满足功率约束后续需要额外的归一化或投影操作。我在实际复现中更推荐从第二条路线起步先把端到端的 baseline 跑通再考虑用第一条路线做迭代展开的改进。原因很简单端到端的结构在 Matlab 里用深度学习工具箱实现起来最顺手调试周期短很快能看到效果对比。本文代码采用的就是端到端预测 功率归一化投影的混合方案兼顾性能和实现复杂度。3. Matlab 代码实现的核心细节3.1 整体架构设计代码的整体流程分为四个模块数据生成模块、WMMSE 标签计算模块、DNN 训练模块、性能对比评估模块。这个拆分方式是深思熟虑过的——每一块都可以独立测试和替换方便你后续在这个框架上做自己的改进。模块之间的数据流是这样走的数据生成模块随机产生大量信道样本WMMSE 模块在每个样本上迭代求解得到最优预编码矩阵作为标签这两个模块的输出组合成训练数据集喂给 DNN 训练模块。训练完成后用独立的测试集对 DNN 的输出和 WMMSE 的解做性能和耗时对比。这里有一个重要的工程决策到底是用复数值信道还是实数值信道作为网络输入。Matlab 中默认的数据类型是双精度浮点数而深度学习工具箱的trainNetwork函数对复数支持有限。我的做法是将复信道矩阵的实部和虚部拆开拼接成一个实数向量作为网络输入。以 4 发 4 收的场景为例一个 4x4 的复信道矩阵实虚部拼开后得到一个 1x32 的实数向量网络输入层的维度就是 32。这个处理虽然损失了一点结构信息但在中小规模场景下实测效果完全可以接受。3.2 WMMSE 标签计算的 Matlab 实现WMMSE 的参考实现是整个代码正确性的基石也是我调试最多的地方。标签如果算错了后面训练出来的网络即使损失很低也没有意义。核心代码如下function [V_opt, W_opt] wmmse_beamforming(H, P_max, sigma2, max_iter) % H: K x Nt 复数信道矩阵 % P_max: 基站最大发射功率 % sigma2: 噪声功率 % 返回 V_opt: Nt x K 预编码矩阵, W_opt: 权重矩阵 [K, Nt] size(H); V randn(Nt, K) 1j*randn(Nt, K); V V / norm(V, fro) * sqrt(P_max); % 功率归一化初始化 for iter 1:max_iter V_old V; % Step 1: 计算 MMSE 接收矩阵 U U zeros(K, 1); for k 1:K hk H(k, :).; I_plus H * V * V * H sigma2 * eye(K); U(k) (V(:, k) * hk) / (hk * V(:, k) * V(:, k) * hk ... sum(abs(hk * V(:, setdiff(1:K, k))).^2) sigma2); end % Step 2: 更新权重 W E eye(K) - U .* (H * V); % 均方误差矩阵对角元素 W 1 ./ abs(diag(E)).^2; % Step 3: 更新预编码矩阵 V for k 1:K hk H(k, :).; A sum(W .* abs(U).^2 .* H * H, 1); % 简化写法,实际需逐行计算 V(:, k) W(k) * U(k) * hk / (A mu); end % 功率约束投影 if norm(V, fro) sqrt(P_max) V V / norm(V, fro) * sqrt(P_max); end % 收敛检查 if norm(V - V_old, fro) 1e-4 break; end end V_opt V; W_opt W; end注意上面代码第 12 行的U计算我简化了实际中求 MMSE 接收机应该用完整的矩阵表达式即 ( U (H V V^H H^H \sigma^2 I)^{-1} H V )。这里写成逐用户形式是为了展示原理真正跑代码时建议用矩阵运算一次性求出所有用户的 MMSE 接收向量这样不仅能利用 Matlab 的矢量化加速还能避免手写循环中容易出现的索引错误。功率归一化这一步也容易踩坑。每次迭代后都要检查当前预编码矩阵的 Frobenius 范数是否超过功率预算如果超过就整体缩放。这个缩放操作等价于把功率分配结果投影到可行域内保证了 WMMSE 中间迭代结果的可行性。我在初版实现中把归一化放在了收敛判断之后导致迭代过程中 V 的值浮动很大收敛速度明显变慢后来才发现归一化必须放在每次迭代内部执行。3.3 DNN 网络结构设计与参数选择网络结构的设计直接影响最终预测精度。我尝试了几种结构最终选定的配置是32-128-256-128-64-32的五层全连接网络激活函数用 ReLU最后一层不加激活函数因为输出需要保持任意实数范围。这个结构选择是经过对比实验的。第一版我用了三层隐藏层发现训练损失可以降下来但验证损失偏高明显是欠拟合了增加一层隐藏层后验证损失显著下降。后来又盲目加到八层验证损失反而没有明显改善训练时间却拉长了许多。权衡下来五层是一个比较甜的平衡点。% 构建 DNN 网络层 layers [ featureInputLayer(32, Name, input) fullyConnectedLayer(128, Name, fc1) reluLayer(Name, relu1) fullyConnectedLayer(256, Name, fc2) reluLayer(Name, relu2) fullyConnectedLayer(128, Name, fc3) reluLayer(Name, relu3) fullyConnectedLayer(64, Name, fc4) reluLayer(Name, relu4) fullyConnectedLayer(32, Name, fc5) regressionLayer(Name, output) ];训练参数方面我用了 Adam 优化器初始学习率 0.001MiniBatchSize 设 128最大训练轮数 200。数据集设置训练样本 20000 个验证集 5000 个测试集 5000 个。这里要特别强调训练集和测试集必须使用不同的随机信道样本绝对不能共用同一条信道生成种子否则评估出来的泛化性能是虚高的换个信道场景立刻露馅。学习率衰减策略也别忽略。我在训练到 100 轮时手动把学习率降到原来的十分之一这样能让损失函数在后期更精细地收敛。用 Matlab 的trainingOptions里设置LearnRateSchedulepiecewise加上LearnRateDropPeriod100和LearnRateDropFactor0.1就可以实现。3.4 训练流程与损失函数设计损失函数选择上我最初直接用了均方误差MSE即预测预编码矩阵和标签之间的逐元素均方误差。后来发现一个问题MSE 最小化并不直接等价于和速率最大化。预编码向量的小幅度偏差对发射功率约束影响不大但对用户间干扰的影响可能被放大导致和速率性能下降。改进方案是在损失函数里加入功率约束惩罚项。定义损失函数为 ( L MSE(V_{pred}, V_{label}) \lambda \cdot \max(0, |V_{pred}|F^2 - P{max}) )这样网络在训练过程中不仅学习拟合 WMMSE 的最优解还学会了保持输出满足功率约束。惩罚系数我设为 0.5你可以根据验证集表现微调。Matlab 中自定义训练循环需要用到dlnetwork而不是trainNetwork。代码结构是这样的% 使用 dlarray 自定义训练循环 net dlnetwork(layers); executionEnvironment auto; [~, ~, ~, numOutputs] size(Y_train); for epoch 1:numEpochs % 打乱数据 idx randperm(size(X_train, 1)); for i 1:numIterations batchIdx idx((i-1)*miniBatchSize1 : min(i*miniBatchSize, end)); X_batch X_train(batchIdx, :); Y_batch Y_train(batchIdx, :); dlX dlarray(X_batch, CB); dlY dlarray(Y_batch, CB); % 计算梯度 [gradients, loss] dlfeval(modelLoss, net, dlX, dlY, P_max, lambda); % 更新参数 [net, avgGrad, avgSqGrad] adamupdate(net, gradients, ... avgGrad, avgSqGrad, iteration, learnRate); end end function [loss, gradients] modelLoss(net, dlX, dlY, P_max, lambda) dlYPred forward(net, dlX); mseLoss mse(dlYPred, dlY); % 计算功率约束惩罚 predPower sum(dlYPred.^2, 1); penalty max(0, predPower - P_max); loss mseLoss lambda * mean(penalty); gradients dlgradient(loss, net.Learnables); end自定义训练循环的好处是灵活坏处是调试难度大Matlab 的自动微分在某些操作上会报奇怪的错误。我建议先跑通trainNetwork的基线版本确认数据和标签没问题再迁移到dlnetwork做自定义损失。千万别一开始就上自定义损失函数不然问题都不知道出在数据还是网络层。4. 在 Matlab 中完整跑通一次训练评估4.1 数据生成与归一化的工程细节数据生成的均匀性直接影响训练效果。我用的信道模型是独立同分布的瑞利衰落信道即每个信道系数都是均值为 0、方差为 1 的复高斯随机变量。生成方式很简单H (randn(K, Nt) 1j*randn(K, Nt)) / sqrt(2)。但这里有个细节值得注意直接把原始信道系数作为网络输入会让训练变得困难。因为不同样本的功率波动范围比较大网络需要额外学习适应不同尺度。我的做法是对每个样本的信道矩阵做归一化除以该样本的 Frobenius 范数H_norm H / norm(H, fro)。归一化后的信道矩阵保持了相对幅度信息但整体尺度被压缩到统一范围这对 DNN 的训练收敛非常有帮助。对应地预编码矩阵标签也做了缩放处理。WMMSE 输出的预编码矩阵满足总功率约束但不同样本的功率分配模式不同。我把标签统一乘上一个缩放系数让标签的功率均值等于 1。这样网络预测的输出还需要在评测阶段乘回缩放系数并做功率投影才能和 WMMSE 结果公平对比。这个细节很多人容易忽略导致训练损失曲线正常但最终性能评估一塌糊涂。4.2 训练完成后的性能评估方法训练完成后性能评估不能只看训练损失。我用了三个维度去验证第一个是归一化均方误差 (NMSE)计算公式为 ( NMSE \mathbb{E}[|V_{pred} - V_{label}|F^2 / |V{label}|_F^2] )这个指标直接反映预测准确度。第二个是和速率 (Sum Rate)这才是我们真正关心的性能指标。把预测的预编码矩阵代入 SINR 表达式计算每个用户的速率然后求和。我这里写好了评估函数function sum_rate evaluate_sum_rate(H, V, sigma2) [K, ~] size(H); rate zeros(K, 1); for k 1:K hk H(k, :); signal abs(hk * V(:, k))^2; interference 0; for j 1:K if j ~ k interference interference abs(hk * V(:, j))^2; end end sinr signal / (interference sigma2); rate(k) log2(1 sinr); end sum_rate sum(rate); end第三个是计算延迟。用tic和toc分别统计 WMMSE 迭代求解和 DNN 单次前向传播的耗时。在我的测试环境普通笔记本Matlab R2023b无 GPU下4 用户 4 天线的场景WMMSE 平均需要约 15 毫秒完成收敛而 DNN 前向传播只需要约 0.5 毫秒接近 30 倍的加速。这个对比已经足够说明深度学习方法在实时性上的巨大优势。4.3 手把手跑通的完整流程与运行结果我把完整流程整理成标准执行步骤你按照这个顺序执行就能从零到一拿到可用结果第一步运行main.m它会自动完成数据生成、WMMSE 标签计算、数据集划分和训练。这一步耗时最长数据集 20000 个样本加 WMMSE 标签生成大概要 20 分钟左右训练过程在我的配置上大约需要 15 分钟。第二步训练完成后脚本自动绘制三条曲线训练损失下降曲线、验证集 NMSE 随训练轮次变化曲线、DNN 与 WMMSE 在测试集上的和速率对比散点图。这个环节你可以直观看到预测性能和最优性能之间的差距。第三步运行evaluate.m对测试集做批量评估输出平均 NMSE、平均和速率、平均耗时三个关键数值并和 WMMSE 的对应数值做对比。我的实测结果是一个典型输出NMSE 大约在 0.02 到 0.03 之间DNN 的和速率达到 WMMSE 最优解的 95% 以上但推理时间缩短了近 30 倍。这个性能表现基本符合预期也验证了 DNN 在无线资源分配中降本增效的实用价值。5. 调试中踩过的坑和避坑指南5.1 数据不平衡问题导致模型偏向低速率场景第一版训练完成后我发现验证集的 NMSE 并不高但和速率性能比 WMMSE 差距很大。排查后发现原因是训练数据里大多数信道样本对应的最优和速率集中在某个中间区间而高和速率的极端信道样本占比太少网络在训练时被中等样本主导对极端场景的预测能力不足。解决办法是在数据生成阶段控制信噪比的变化范围并确保每个 SNR 点均匀采样。我采用了 SNR 从 0 dB 到 20 dB 每 5 dB 一组、每组等量生成样本的策略这样网络能看到各个信噪比条件下的信道-预编码映射模式。你也可以用重要采样思想让数据生成的角度偏向性能边界区域但实现复杂度会高一些先保证均匀覆盖是最稳的做法。5.2 Matlab 训练过程的数值稳定性问题用dlnetwork训练时我最常遇到的报错是梯度中出现NaN。一般有两个原因一是数据中有 NaN 或 Inf二是网络输出的数值过大导致梯度爆炸。排查方法是在训练循环里加一个检查点每个 batch 计算完损失后用isfinite检查损失值是否为有限数一旦发现异常就打印出当前 batch 的数据信息并中止训练。多数情况下问题出在 WMMSE 标签计算中某些信道矩阵条件数过大导致求逆数值不稳定解决办法是在求逆操作之前给矩阵对角线加一个很小的正则项比如1e-6 * eye(K)。对于梯度爆炸问题我给梯度做了clip处理gradients dlupdate((g) max(min(g, 1), -1), gradients)。实测下来训练稳定性提升明显尤其是在训练后期学习率没有衰减到很低的时候。5.3 训练时间过长的三个优化手段如果你遇到训练时间过长的问题建议从三个方向优化。第一是减少训练集规模我测试过 10000 个样本和 20000 个样本的最终性能差异不到 2%但训练时间相差近一倍。如果只是验证算法可用性先用 10000 个样本足够。第二是关掉不必要的验证频率ValidationFrequency默认值较小会导致频繁在验证集上评估增加大量额外开销我设置为每 20 轮验证一次。第三是利用并行计算池parpool开启后数据生成和标签计算阶段的加速非常可观因为这些任务天然可以并行。5.4 常见问题速查表问题现象可能原因解决办法训练损失下降缓慢学习率过小 / 数据未归一化调大学习率检查输入输出数据范围验证损失不降反升模型过拟合增加训练样本、加 Dropout 层、降低网络层数预测预编码功率严重超标损失函数中未加功率约束惩罚加入功率惩罚项或训练后做功率投影测试集性能远差于训练集训练测试数据同分布假设不成立重新生成独立信道样本检查归一化方式WMMSE 标签存在 NaN矩阵条件数过大求逆前加正则项训练过程中突然出现 Inf 梯度梯度爆炸梯度裁剪 / 减小学习率GPU 显存不足batch size 太大减小 MiniBatchSize6. 经验总结与后续扩展方向实际做完这个项目后我的最大体会是DNN 改进传统优化算法的核心价值不在于“超越” WMMSE而在于“逼近” WMMSE 的同时提供高出一个量级的推理速度。很多论文里把 DNN 方案的和速率性能画得比 WMMSE 还高那种结果基本是在测试集上精心挑选数据得到的属于过拟合现象。真实项目里能稳定做到 WMMSE 性能的 95% 以上、同时获得数十倍加速就已经是工程上极具吸引力的方案了。如果你想把这套代码往更深的方向扩展我建议重点关注三个方向。第一个是把网络结构从全连接换成卷积网络利用 CNN 的局部特征提取能力处理具有空间相关性的信道场景这在 Massive MIMO 配置下尤其有价值因为天线阵列的元素之间存在天然相关性。第二个是引入迭代展开的思路把 WMMSE 的每次迭代映射成网络的一层这样网络的表达能力更强、泛化性能更好不过这需要你对 WMMSE 的数学推导非常熟悉能写出每一层的解析表达式。第三个是尝试在损失函数中直接引入和速率目标绕过预编码矩阵的中间监督信号实现真正意义上的端到端性能优化但这种方法的收敛难度会大幅增加。最后再分享一个小技巧训练完成后一定要保存好训练数据的归一化参数。我在多次实验中都有过重新加载网络后发现预测结果完全不对的经历排查到最后才意识到是忘了把测试输入数据按照训练集的统计参数做标准化。这个细节写进你的代码注释里以后能少踩不少坑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价