343 lines
16 KiB
Markdown
343 lines
16 KiB
Markdown
# 架构设计文档 / 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 博弈结构一致
|