SemanticCommunication/code/ARCHITECTURE.md

343 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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