# 架构设计文档 / Architecture Document ## 系统全局视图 / System Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ train.py (入口) │ │ CLI 解析 → 加载配置 → 初始化环境+算法 → 训练循环 → 保存模型 │ └──────────┬──────────────────────────────────┬───────────────────┘ │ │ ▼ ▼ ┌─────────────────────┐ ┌──────────────────────────┐ │ envs/ (环境层) │ │ agents/ + baselines/ │ │ │ │ (算法层) │ │ WirelessEnv │◄────────►│ CoMADDPG / 7 baselines │ │ ├─ ChannelModel │ obs, │ ├─ Actor (策略网络) │ │ ├─ SemanticModule │ reward, │ ├─ Critic (价值网络) │ │ └─ step/reset │ done │ ├─ ReplayBuffer │ └─────────────────────┘ │ └─ OUNoise │ └──────────────────────────┘ │ ▼ ┌──────────────────────────┐ │ utils/ (工具层) │ │ metrics.py (评估指标) │ │ visualization.py (绘图) │ └──────────────────────────┘ │ ▼ ┌──────────────────────────┐ │ evaluate.py (评估入口) │ │ 8 场景 × 12+ 张图 │ └──────────────────────────┘ ``` --- ## 模块依赖关系 / Module Dependencies ``` configs/default.yaml │ ├──► envs/channel_model.py (读取 env.carrier_freq, env.noise_psd, env.subcarrier_spacing) ├──► envs/semantic_module.py (读取 env.rho_max, env.rho_min, env.w1, env.w2) ├──► envs/wireless_env.py (读取 env.* 和 training.max_steps) │ ├── uses ChannelModel │ └── uses SemanticModule │ ├──► agents/co_maddpg.py (读取 env.num_subcarriers, training.*, network.*, reward.*) │ ├── uses Actor (agents/actor.py) │ ├── uses Critic (agents/critic.py) │ ├── uses ReplayBuffer (agents/replay_buffer.py) │ └── uses OUNoise (agents/noise.py) │ ├──► baselines/*.py (各基线复用 Actor, Critic, ReplayBuffer, OUNoise) │ └──► utils/metrics.py (读取 reward.* 权重) utils/visualization.py (独立,无配置依赖) ``` --- ## 数据流 / Data Flow ### 训练循环(单个 Episode) ``` ┌─── Episode 开始 ───┐ │ │ ▼ │ env.reset() │ → (obs_s, obs_b) │ │ │ ┌───► Step 循环 (200 步) ◄────┐ │ │ │ │ │ │ agent.select_action │ │ │ (obs_s) → act_s │ │ │ (obs_b) → act_b │ │ │ │ │ │ │ env.step(act_s, act_b) │ │ │ → (obs_s', obs_b', │ │ │ rew_s, rew_b, │ │ │ done, info) │ │ │ │ │ │ │ agent.compute_rewards │ │ │ (info) → (r_s, r_b) │ │ │ │ │ │ │ buffer.push( │ │ │ obs_s, obs_b, │ │ │ act_s, act_b, │ │ │ r_s, r_b, │ │ │ obs_s', obs_b', │ │ │ done) │ │ │ │ │ │ │ agent.update() │ │ │ │ │ │ │ not done ────────────────┘ │ │ │ └─── done ─── noise.decay() ──────┘ │ save model + log ``` ### 环境 step() 内部流程(8 步) ``` Action Decode Subcarrier Alloc Power Alloc (act_s, act_b) → Greedy by channel → Equal within group │ │ │ ▼ ▼ ▼ n_sub_s, n_sub_b sem_subs, trad_subs power_matrix (K×N) │ │ ▼ ▼ ┌─── SNR = p·|h|²/σ² ───┐ │ │ ┌─────────┴──────┐ ┌──────────┴──────┐ │ Traditional │ │ Semantic │ │ Rate = Σ Δf· │ │ avg_SNR → │ │ log₂(1+γ) │ │ SSim(γ̄, ρ) → │ │ QoE_b = min │ │ QoE_s = w1·SSim │ │ (R/R_req, 1) │ │ + w2·(1-ρ/ρ_max)│ └───────┬────────┘ └────────┬─────────┘ │ │ └────────┬───────────────┘ ▼ QoE_sys = mean(all QoE) │ Regenerate channel (block fading) │ Build (obs_s', obs_b', rew_s, rew_b, done, info) ``` --- ## Co-MADDPG Stackelberg 更新机制 / Stackelberg Update Mechanism 这是本算法的核心创新。更新顺序体现了 Leader-Follower 博弈结构: ``` ┌─────────────────────────────────────────────────────────────────┐ │ Stackelberg Update │ │ │ │ PHASE 1: 更新 Follower (Agent B) / Update Follower First │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ 1. Critic B: L_B = (Q_B(s,a) - y_B)² │ │ │ │ y_B = r_B + γ·Q_B'(s', a_s'_target, a_b'_target) │ │ │ │ 2. Actor B: max Q_B(s, a_s_current, π_B(o_b)) │ │ │ │ 3. Soft update: θ_B_target ← τ·θ_B + (1-τ)·θ_B_tgt │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ (B's policy is now updated) │ │ │ │ PHASE 2: 更新 Leader (Agent S) / Update Leader with B's BR │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ 1. Get B's best response: a_b_br = π_B_updated(o_b) │ │ │ │ .detach() — 不反传梯度给 B │ │ │ │ 2. Critic S: L_S = (Q_S(s, a) - y_S)² │ │ │ │ 3. Actor S: max Q_S(s, π_S(o_s), a_b_br) │ │ │ │ Leader 优化时考虑了 Follower 的最优响应 │ │ │ │ 4. Soft update: θ_S_target ← τ·θ_S + (1-τ)·θ_S_tgt │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ PHASE 3: 更新动态 λ / Update Dynamic λ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ λ(t) = sigmoid(β · (QoE_sys - Q_th)) │ │ │ │ β=5.0 控制切换陡度,Q_th=0.6 为切换阈值 │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 奖励计算流程 / Reward Computation Flow ``` env.step() 输出 info dict │ ┌─────────┴─────────┐ │ qoe_semantic │ │ qoe_traditional │ │ qoe_sys │ └─────────┬─────────┘ │ agent.compute_rewards(info) │ ┌────────────┴────────────┐ ▼ ▼ r_coop_s = r_coop_b = 0.5·qoe_s + 0.5·qoe_b + 0.3·qoe_b + 0.3·qoe_s + 0.2·qoe_sys 0.2·qoe_sys │ │ r_comp_s = r_comp_b = 0.8·qoe_s + 0.8·qoe_b + 0.2·qoe_sys 0.2·qoe_sys │ │ └────────────┬────────────┘ │ λ = sigmoid(β·(qoe_sys - Q_th)) │ r_s = λ·r_coop_s + (1-λ)·r_comp_s r_b = λ·r_coop_b + (1-λ)·r_comp_b ``` --- ## 观察空间与动作空间 / Observation & Action Spaces ### 观察空间 (obs_dim = N + 4 = 68) | 维度 | 内容 (语义 Agent S) | 内容 (传统 Agent B) | |---|---|---| | [0 : N] | 语义用户平均信道功率 (归一化) | 传统用户平均信道功率 (归一化) | | [N] | qoe_avg_s (滚动平均 QoE) | qoe_avg_b (滚动平均 QoE) | | [N+1] | content_sensitivity | business_priority | | [N+2] | alloc_s (当前子载波分配比) | alloc_b (当前子载波分配比) | | [N+3] | load_s (流量负载) | load_b (流量负载) | ### 动作空间 (act_dim = 3, 连续 [0,1]) | 维度 | 含义 (语义 Agent S) | 含义 (传统 Agent B) | |---|---|---| | [0] | 请求子载波比例 n_sub_frac | 请求子载波比例 n_sub_frac | | [1] | 功率分配比例 p_frac | 功率分配比例 p_frac | | [2] | 压缩率 ρ (映射到 [ρ_min, ρ_max]) | 冗余参数 (未使用) | --- ## 网络架构 / Network Architecture ### Actor Network ``` Input: obs (68,) │ ├─ Linear(68 → 256) + ReLU ├─ Linear(256 → 256) + ReLU ├─ Linear(256 → 128) + ReLU ├─ Linear(128 → 3) └─ (Tanh + 1) / 2 → output ∈ [0, 1]³ ``` ### Critic Network (Joint, CTDE) ``` Input: concat(obs_s, obs_b, act_s, act_b) = (142,) │ ├─ Linear(142 → 512) + ReLU ├─ Linear(512 → 512) + ReLU ├─ Linear(512 → 256) + ReLU └─ Linear(256 → 1) → Q-value (scalar) ``` --- ## 经验回放 / Replay Buffer 9-field transitions: ``` Transition = (obs_s, obs_b, act_s, act_b, rew_s, rew_b, next_obs_s, next_obs_b, done) (68,) (68,) (3,) (3,) (1,) (1,) (68,) (68,) (1,) ``` - 容量: 100,000 transitions - 采样: 均匀随机采样 batch_size=256 - 存储: deque 结构,FIFO 淘汰 --- ## Agent 接口契约 / Agent Interface Contract 所有 8 个算法必须实现以下接口,以兼容 `train.py` 的训练循环: ```python class AgentInterface: # 必须有以下属性之一 self.buffer: ReplayBuffer # 优先检查 self.replay_buffer: ReplayBuffer # 备选 # 选择动作 def select_action(obs_s, obs_b, explore=True) -> (act_s, act_b) # 计算奖励 def compute_rewards(info) -> (rew_s, rew_b) # 更新网络 def update() -> dict or None # 保存/加载模型 def save(path) def load(path) # 噪声衰减 (可选,train.py 通过 hasattr 检查) self.noise_s: OUNoise # 需有 decay_sigma(episode) 方法 self.noise_b: OUNoise ``` --- ## 信道模型 / Channel Model 基于 3GPP Urban Micro (UMi) NLOS 模型: ``` 距离: d ~ U(50, 500) 米 路径损耗: PL(d) = 36.7·log₁₀(d) + 22.7 + 26·log₁₀(fc) [dB] 信道增益: h_{k,n} ~ CN(0, 10^{-PL/10}) (Rayleigh fading) 噪声功率: σ² = 10^{(N₀_dBm - 30)/10} · Δf [W] SNR: γ_{k,n} = p_{k,n} · |h_{k,n}|² / σ² ``` - 块衰落模型 (Block fading): 每个 step 重新生成信道 - K_s=3 语义用户 + K_b=3 传统用户 = 6 users - N=64 个 OFDM 子载波 - 子载波分配: 贪婪算法 (语义用户优先) - 功率分配: 组内均分 --- ## 设计决策 / Design Decisions ### 1. 为什么用 Stackelberg 而不是 Nash? - Stackelberg 适合异构场景:语义用户 (Leader) 先决策,传统用户 (Follower) 最优响应 - 保证了均衡存在性(定理 1-2 在论文中证明) ### 2. 为什么 λ(t) 用 sigmoid? - 连续可微,适合梯度训练 - β 参数控制切换陡度,Q_th 控制切换点 - 系统 QoE 高时偏合作 (λ→1),低时偏竞争 (λ→0) ### 3. 为什么观察空间包含额外 4 维? - 仅信道信息不够:agent 需要知道当前 QoE 水平、流量负载、分配状况 - 这些额外信息帮助 agent 做出更有环境感知的决策 ### 4. 为什么 Critic 是联合的 (CTDE)? - 集中训练时可访问所有信息,解决非平稳性问题 - 分散执行时只用各自的 Actor,降低通信开销 ### 5. 为什么语义用户优先分配子载波? - 体现 Leader 的先动优势 (First-mover advantage) - 与 Stackelberg 博弈结构一致