资讯详情

RIS辅助D2D资源分配:基于DDPG的联合优化系统复现与调优

📅 2026/10/11 1:08:30 | 华诺云谱 👁 阅读
RIS辅助D2D资源分配:基于DDPG的联合优化系统复现与调优
简介这是RIS辅助D2D通信网络资源分配算法的完整复现资料面向具备通信工程基础、关注无线通信与深度强化学习交叉方向的研究人员。资料基于一篇智能超表面(RIS)辅助蜂窝NOMA-D2D系统论文构建D2D用户和速率最大化问题并分解为三个子问题采用二分图最大匹配完成D2D簇信道分配基于DDPG算法联合优化D2D发射功率与RIS相移矩阵通过交替迭代收敛以抑制干扰、提升传输速率。压缩包共1个PDF文件大小763KB内含系统参数初始化、信道模型、二分图匹配、DDPG训练及性能评估等模块的详细可运行代码与逐段解释并覆盖动态优先级机制、混合奖励设计、LSTM增强等创新点实现。从混合整数非线性问题分解到QoS感知信道分配再到功率与RIS交替更新资料完整呈现“分解-协调”优化框架的落地过程便于读者复现论文结果并迁移至实际通信场景。目前已有83人学习。1. 为什么大家都能跑通仿真却复现不出RIS辅助D2D的资源分配曲线找一个空闲的周末我把“无线通信RIS辅助D2D通信网络资源分配算法复现基于DDPG的联合优化系统设计与性能评估”这个标题当成一个实际项目来对待结果第一次训练就翻了车DDPG的奖励曲线几乎是一条平线功率输出迅速归零RIS相移始终停在初始值附近。后来我把整个系统拆成“信道仿真、状态动作设计、奖励塑造、训练稳定性”四块分别验证才把曲线拉起来。这类复现的核心不是跑通一份代码而是理解联合优化系统里每个变量如何影响梯度信号。简单说这个方向解决的是这样一个问题多个D2D用户复用蜂窝频谱彼此产生同频干扰同时RIS反射面能通过调整相位来增强有用信号或抑制干扰但功率、信道和RIS相位三个变量的可行域互相耦合组合复杂度很高。DDPG作为连续动作空间的深度强化学习算法天然适配“功率连续、相位连续、信道分配可离散化”的联合策略学习任务。适合想快速把深度强化学习落到无线资源调度上的从业者也适合做无线通信系统级仿真和算法对比的研究性复现。2. 复现前的第一件事把RIS与D2D共存场景建造成可计算的优化问题2.1 从蜂窝频谱复用场景出发定义所有参与者在动手写DDPG之前先把无线场景固定下来。我采用的模型是单蜂窝场景一个蜂窝用户占据上行频谱资源K条D2D链路复用这些频谱RIS由N个反射单元组成部署在蜂窝边缘的楼宇表面。所有接收端感知到的是直射链路加RIS反射路径的合成信道而D2D用户之间的同频干扰会在信道矩阵中体现出强烈的互耦关系。这个场景的物理假设要提前说清楚否则后续奖励函数无从设计。D2D用户位置固定每个用户装备单天线RIS反射单元只能调整相位不能放大信号。蜂窝用户以固定功率发射D2D发射功率有最大值限制。信道模型采用大尺度路径损耗加小尺度瑞利衰落RIS反射链路的相位则取决于入射角、反射角以及RIS单元的相移配置。下面这段代码是我用来生成信道矩阵的核心部分它把直射链路和RIS反射链路统一到一个等效信道增益里。def generate_effective_channel(K, C, N, pos_d2d, pos_cu, pos_ris, lambda_c): 生成每个D2D接收端在每条信道上的等效信道增益 pos_d2d: (K,2) 发射端位置; 接收端沿法线偏移距离 d_recv 返回 h_eff: (K, C) 复数信道矩阵 h_eff np.zeros((K, C), dtypenp.complex128) for k in range(K): d_direct np.linalg.norm(pos_d2d[k] - pos_d2d[k] 1e-6) # 直接链路路径损耗指数2.2 h_direct (np.random.randn() 1j * np.random.randn()) / np.sqrt(2) h_direct h_direct * np.sqrt(lambda_c / (4*np.pi*d_direct)**2.2) # RIS反射链路RIS位于pos_risD2D发射端和接收端分别到RIS d_tx_ris np.linalg.norm(pos_d2d[k] - pos_ris) # 接收端稍作偏移 d_rx_ris np.linalg.norm(d_direct 0.5) g_tx (np.random.randn() 1j * np.random.randn()) / np.sqrt(2) g_rx (np.random.randn() 1j * np.random.randn()) / np.sqrt(2) # N个单元的反射系数初始为1后续由相移向量theta控制 g_ris g_tx * g_rx * np.sqrt(lambda_c / (4*np.pi*d_tx_ris*d_rx_ris)**2.2) h_eff[k, :] h_direct N * g_ris # 简化模型RIS照射面积增益与N成正比 return h_eff这段代码里h_eff是核心输出DDPG的所有决策最终都作用在这个矩阵上并形成SINR。需要注意这里的RIS增益用了近似倍数N实际要更严谨可以逐单元累加复数反射系数但作为DDPG复现的第一步用等效信道模型已经能观察策略学习的趋势。2.2 联合优化问题怎么表达决定了状态和动作设计的边界完成信道生成后要把资源分配抽象成数学问题。D2D链路集合记为K复用信道集合C定义三组变量D2D发射功率向量PRIS相移向量θ信道分配指示矩阵X。目标是在蜂窝用户干扰温度约束和每个D2D用户最大功率约束下最大化系统总吞吐量。SINR表达式为[ \text{SINR}{k,c} \frac{P_k |h{k,c}^{eff}(\theta)|^2}{\sigma^2 \sum_{i\neq k} P_i |h_{i,c}^{eff}|^2 P_{cu}|g_{k,c}^{cu}|^2} ]其中g_{k,c}^{cu}是D2D接收端收到的蜂窝用户干扰信道σ2是噪声功率。吞吐量是B_c * log2(1SINR)所以整个优化目标可以写成带惩罚项的求和式。由于X是二值离散P和θ连续而且RIS相位具有周期性这一个混合整数非凸规划问题在传统优化里非常难解。但如果把X改造成从连续“信道偏好分数”通过离散映射得到问题就转化成连续动作空间的强化学习问题。我在实现里让DDPG的Actor网络直接输出三部分K维功率注入到(0, P_max)N维相移注入到(0, 2π)以及K乘以C维信道偏好分数。环境端在收到这些连续值后使用argmax把偏好分数转成复用关系。这样联合优化并没有把信道分配完全交给枚举而是让策略网络根据当前信道状态学习偏好这也更贴近标题里“联合优化系统”的意图。这个建模方式有一个隐藏的好处它天然适合后续扩展到更复杂的多用户场景。当用户数量增加时枚举全部信道分配不再可行但DDPG的输出维度只线性增长训练时间可以接受。所以我在复现时强烈建议用这种“连续输出离散映射”的结构而不是把信道分配放到环境里随机生成。3. DDPG联合优化设计状态、动作、奖励如何映射成网络训练3.1 为什么不用Q学习或策略梯度而选DDPG先做一个直白的对比。传统的深度Q网络输出的是离散动作Q值只能处理信道分配这类离散变量但功率和RIS相位一旦离散化动作空间就会爆炸。假设每个功率量化为5档每相移量化为10档10个RIS单元就产生了10的10次方组合这还没算信道分配。策略梯度方法可以处理连续动作但REINFORCE在高维连续空间里的方差非常大训练起来很痛苦。DDPG用Actor网络直接输出确定性的连续动作再用Critic网络评价状态价值刚好解决连续动作和方差两个问题。DDPG并不是没有短板它最大的弱点是超参数敏感我后面会单独拿出一章写避坑。但在RIS辅助D2D这种动作维度适中、奖励计算明确的任务里它比在线策略的PPO更容易复现比SAC更容易理解内部机理。对于需要快速验证算法效果的研究型任务DDPG仍然是性价比最高的起点。3.2 状态空间设计直接给网络原始信道不是好选择很多初学者直接把信道矩阵展平作为网络输入这会让网络学习困难。因为我这里的信道矩阵是复矩阵直接输入实部和虚部又会让维度翻倍而且不同用户之间的信道增益数值差异极大距离近的可能是距离远的100倍。我的做法是对状态做特征提取把每个D2D链路当前得到的有用信号强度、干扰等级、信噪比估计以及上一时刻的动作值打包成一个向量。def build_state(h_eff, p, action_prev, cu_interf): 组装状态向量, h_eff: (K,C) 复数信道, p: (K,) 当前发射功率 返回 state: (feature_dim,) 归一化后的状态 state_inner np.zeros(K * C) for k in range(K): for c in range(C): # 有用信号功率 signal p[k] * np.abs(h_eff[k,c]) ** 2 # 已被其他D2D占用时的干扰实际在环境中计算 interfer cu_interf[k,c] state_inner[k*C c] signal / (interfer 1e-6) # 历史动作和功率状态拼接并归一化到[-1,1] state np.concatenate([state_inner, p / P_max, action_prev]) state 2.0 * (state - state.min() 1e-6) / (state.max() - state.min() 1e-6) - 1.0 return state.astype(np.float32)状态向量的选择会直接影响收敛速度。这个build_state函数里我不仅放了SINR估计还放入了上一时刻动作原因是在DDPG的马尔可夫决策过程中下一时刻状态部分依赖于当前动作引入的干扰变化。如果你在复现时发现训练曲线震荡先检查状态里是否遗漏了功率或动作信息。因为没有历史动作Critic很难估计动作的效果。3.3 动作空间与奖励函数这是整个系统设计的灵魂我的Actor输出层使用了三个分支功率分支用sigmoid映射到(0, P_max)相移分支用mod映射到[0, 2π)信道偏好分支用softmax归一化到概率分布的形状。前两个分支好理解第三个分支在环境内部被转化为离散信道选择。奖励函数不能只写吞吐量否则网络会倾向于把功率调到最高把频谱复用到最多却忽略了蜂窝用户受到的干扰。我使用的奖励函数是[ R \sum_{k,c} x_{k,c} \log_2(1\text{SINR}{k,c}) - \alpha \max(0, \text{SINR}\text{cu} - \text{threshold}) - \beta \sum_k (P_k / P_{\max})^2 ]其中第二项是对蜂窝用户的干扰惩罚第三项是功率正则。这里α和β是超参数我一般取α2.0β0.1如果惩罚权重过大会导致网络保守到输出零功率。很多人复现时只保留第一项结果曲线看起来吞吐量很高实际场景根本不成立。下面的代码是环境内的step函数核心逻辑把Actor输出的连续动作转换为实际系统吞吐量和干扰惩罚。def step(env_state, action_raw): # 动作拆分 p np.clip(action_raw[:K], 0, P_max) theta np.mod(action_raw[K:KN], 2 * np.pi) pref action_raw[KN:].reshape(K, C) # 信道分配由偏好分数argmax得到 x np.zeros((K, C), dtypenp.float32) for k in range(K): c_sel np.argmax(pref[k, :]) x[k, c_sel] 1.0 # 计算等效信道: 加入RIS相移的影响 h_ref h_direct h_ris * np.exp(1j * theta[0]) # 简化示例实际要逐单元累加 # SINR矩阵 sinr compute_sinr(h_effh_ref, pp, xx, p_cuP_CU, sigma2NOISE_POWER) # 吞吐量 rate np.sum(x * np.log2(1 sinr)) # 蜂窝用户的干扰接收SINR sinr_cu compute_cu_sinr(p, x, channel_gain_cu) # 惩罚项 penalty alpha * max(0, SINR_CU_T - sinr_cu) beta * np.sum((p / P_MAX) ** 2) reward rate - penalty next_state build_state(h_ref, p, action_raw, cu_interf) return next_state, reward, False这个step函数看起来简单但有很多细节。h_ref的计算必须和DDPG的动作关联如果在环境内把RIS相移降维了网络就可能无法学习到相位的作用。信道分配用argmax会让梯度无法从奖励回流到偏好分数不过DDPG是策略梯度方法只要Actor输出的偏好分数经过探索噪声环境内的不可微操作不会阻断训练。你如果发现更新失败检查动作和奖励的dtype是否一致以及奖励是否被限制在合适的数值范围。4. 从零跑通实现环境安装、信道仿真与训练主循环4.1 最小运行环境与依赖清单复现这个系统不需要高配置硬件纯CPU也可以训练小规模场景但我建议你还是准备一块显卡因为DDPG的Critic更新比较频繁。我使用的环境配置是Python 3.9PyTorch作为深度学习框架NumPy用于信道生成。不要一次性安装太多库我遇到过因安装某通信库导致numpy版本冲突的问题浪费了大半天。pip install numpy1.23.5 torch1.13.1版本是我当时验证过的更高版本通常也能运行但torch的小版本差异可能影响随机数生成和某些算子的行为复现时最好固定主版本。另外训练日志我会用tqdm来观察episode进度这是一个轻量依赖安装成本很低。4.2 自定义DDPG算法组件Actor、Critic和软更新下面这段代码是DDPG两个网络的定义结构不复杂但输入维度必须严格对应状态和动作维度。Actor使用3层全连接网络隐藏层包含批量归一化输出层用tanh加缩放的方式控制功率范围。Critic输入状态加动作输出一个Q值。注意Critic的输入拼接要放在维度正确的位置。import torch import torch.nn as nn import torch.optim as optim import numpy as np class Actor(nn.Module): def __init__(self, state_dim, action_dim, p_max): super().__init__() self.p_max p_max self.fc1 nn.Linear(state_dim, 256) self.fc2 nn.Linear(256, 256) self.fc3 nn.Linear(256, action_dim) def forward(self, state): x torch.relu(self.fc1(state)) x torch.relu(self.fc2(x)) raw self.fc3(x) # 功率部分与相移部分自由缩放 p_out torch.sigmoid(raw[:, :K]) * self.p_max theta_out torch.remainder(raw[:, K:KN], 2 * np.pi) pref_raw raw[:, KN:] # 信道偏好使用softmax输出范围0~1 pref_out torch.softmax(pref_raw, dim-1) return torch.cat([p_out, theta_out, pref_out], dim1) class Critic(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.fc1 nn.Linear(state_dim action_dim, 256) self.fc2 nn.Linear(256, 256) self.fc3 nn.Linear(256, 1) def forward(self, state, action): x torch.cat([state, action], dim1) x torch.relu(self.fc1(x)) x torch.relu(self.fc2(x)) return self.fc3(x)网络结构本身不是难点难在输出层的约束。theta_out使用torch.remainder会带来不连续点但我这里刻意保留这种原始写法因为可以让读者看到不足实际优化时我会在代码里用正弦和余弦两个分量来表示相位避免2π跳变。标准DDPG里噪声添加也应该在环境外完成我后面还会讨论。4.3 DDPG训练主循环经验回放、软更新和策略噪声训练主循环要处理几个关键点经验回放池的存储格式更新时目标网络的软更新系数以及动作探索噪声的衰减。下面代码是一个可跑通的训练骨架它对每个episode都会重置环境并在一定步数内收集数据。def train_ddpg(): env D2DEnv(...) actor Actor(state_dim, act_dim, P_MAX) critic Critic(state_dim, act_dim) actor_target Actor(state_dim, act_dim, P_MAX) critic_target Critic(state_dim, act_dim) # 冷启动时用随机动作探索 noise_std 0.6 batch_size 64 replay_buffer [] for episode in range(500): state env.reset() ep_reward 0 for t_step in range(100): state_tensor torch.FloatTensor(state).unsqueeze(0) action actor(state_tensor).detach().numpy()[0] # 加入OU噪声或高斯噪声 action action np.random.normal(0, noise_std, sizeact_dim) action np.clip(action, 0, 1) next_state, reward, done env.step(action) replay_buffer.append((state, action, reward, next_state, done)) if len(replay_buffer) batch_size: sample random.sample(replay_buffer, batch_size) update_network(sample, actor, critic, actor_target, critic_target) state next_state ep_reward reward if done: break noise_std max(0.05, noise_std * 0.995) if episode % 20 0: print(fepisode {episode}, reward {ep_reward:.3f})这段代码中动作被裁剪到[0,1]区间但在实际环境里相移的残余部分会在环境外再次取模。噪声标准差从0.6指数衰减到0.05这是很多通信资源分配复现中的常用做法。如果你发现探索不足可以调高初始噪声但小心过大的噪声会把Actor刚学到的稳定策略带偏。4.4 参数说明探索噪声、软更新系数、经验回放容量我手上的经验值可以给出一组参考软更新系数tau0.005Actor学习率1e-4Critic学习率1e-3经验回放容量10万条。这里最重要的是Actor和Critic学习率要保持一个数量级的差异如果两者相同Critic收敛快会让Actor梯度变得不稳定。经验回放容量也不能太小否则样本相关性强训练容易陷入局部最优。tau控制目标网络的更新速度太小导致目标网络更新滞后太大又会使目标网络追着当前网络跑失去稳定作用。一般从0.005开始调如果训练震荡明显就降到0.001。我自己曾经把tau设成0.1结果曲线直接爆炸这个参数值得认真调。5. 复现过程中最容易翻车的五个细节现象、原因与解法5.1 DDPG奖励曲线收敛到零附近现象训练几百个episode后平均奖励仍然接近0DDPG输出的功率普遍低于0.1毫瓦。原因奖励函数里含过大惩罚项或者状态中没有进行归一化导致梯度消失。解决先把惩罚系数α和β都置零用纯吞吐量训练确认环境本身可学习。然后逐渐添加惩罚观察每个惩罚项对平均奖励的影响。我在一个模拟项目X里就因为α10把网络压死了换成α2就正常了。解决的具体手段是对奖励做动态尺度缩放比如reward rate / (R_max 1e-6)让奖励在0到1区间内。虽然这改变了原始优化目标但DDPG对奖励量级非常敏感宁可先压缩量级再训练。5.2 RIS相移输出固定值且不随信道变化现象Actor输出的相移在训练过程中逐渐固定到某个常数吞吐量不再随RIS相位变化。原因网络探索不足或者相移的周期性表达导致梯度朝向错误的相位。解决把相移动作从单个角度改为cosθ和sinθ两个分量让场景的驱动力通过复数反射系数传递。这样Actor输出的维度会增加N但训练稳定性大幅提升。另一个原因是环境里对RIS建模过于简单比如把h_ris当成与θ无关。如果复现时发现相移对SINR没有影响先检查代码里是否存在这样的硬接短路。5.3 信道分配偏好与功率纠缠在一起导致训练震荡现象在同一个状态下反复评估信道分配有时把信道0分给D2D用户1有时分给用户2吞吐量波动很大。原因信道偏好分数是连续的但网络同时学功率和相位功率改变会反转SINR排序导致偏好分数也来回横跳。解决采用课程学习先固定信道分配只训练功率和相移等这两个变量稳定后再放开信道偏好。也可以对偏好分数加一个温度退火的softmax让分配在早期更随机后期更尖锐。我一般会在代码里准备一个channel_trainable标志前一半episode把该标志置为False。这个技巧能让收敛速度提升约一倍而且最终分配结果更稳定。5.4 测试阶段性能与训练阶段差异巨大现象训练奖励很高但用固定策略评估时吞吐量只有训练值的一半。原因DDPG训练时加入了噪声和随机采样评估时如果直接去掉噪声而Actor输出的策略本身不稳定表现就会下降。解决评估时也保持一个小噪声比如噪声标准差0.02或者每20个episode使用当前策略跑一次评估。另外评估时不要使用贪心信道分配而是让网络输出偏好并加上微小的噪声来打破平局。5.5 随机种子不一致导致AC学习率无法复现现象换了服务器或随机种子后曲线形态与之前完全不同明明代码是同一份。原因状态初始化时信道生成使用随机种子PyTorch和NumPy的随机性没有统一固定另外不同的BLAS线程数也会引起浮点误差。解决在脚本开头固定torch.manual_seed(0)、np.random.seed(0)和random.seed(0)并设置torch.set_num_threads(1)。多跑几个种子取中位数是通信强化学习论文里常见的做法。6. 把仿真结果做实多随机种子、状态归一化与可复现验证技巧训练完DDPG之后如果你只给出一条奖励曲线很难让人信服。一个比较扎实的验证习惯是固定三个随机种子记录每个种子的平均吞吐量、蜂窝用户SINR满足率以及RIS相位对吞吐量增益的贡献。复现时要尤其注意只用一条曲线很容易被DDPG的随机性欺骗看到偶然的好结果就以为算法成功。另一个实用技巧是状态归一化。通信信道增益的数值经常跨越几个数量级如果不做归一化Critic的输入会让权重更新陷入震荡。我的做法是在环境初始化时采样一些信道样本统计它们的最小最大范围然后把状态转换函数里的线性映射参数固化下来。这样训练和评估时使用同标度不会出现评估时因瞬时信道过强而异常。我还习惯在训练结束前跑一次“固定Actor策略的对比实验”把DDPG优化后的功率与相移与以下方案做对比随机功率、最大功率、无RIS、以及RIS相移随机。这个对比能直观告诉你RIS和DDPG各自贡献了多少增益。如果DDPG训练出来的效果只比随机功率好一点点那么很可能是奖励函数或状态设计有问题建议回去重新审视动作到环境映射的环节。调优过程中有一个教训是不要同时修改奖励系数、网络层数和学习率。一次只改一个参数并保存好训练曲线否则即使最终取得了不错的结果也无法定位哪个变更起了作用。比如有一次我发现同一个场景下给Actor加一层隐藏层后训练时间变长但性能没有提升后来才想到是噪声衰减过快早期探索不够。把噪声衰减系数从0.995改为0.99后性能才明显改善。从可复现的角度我最后会额外记录每个episode的探索噪声标准差以及用于生成初始信道的随机种子。这些看似零碎的信息恰恰是决定结果能否复现的关键。希望这些踩坑换来的经验能帮你少走一段弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑