Initial commit: add project materials and code

This commit is contained in:
hc
2026-02-28 16:17:42 +08:00
commit 5efb877df7
198 changed files with 17541 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
"""Agent modules for Co-MADDPG wireless resource allocation."""
from .noise import OUNoise
from .replay_buffer import ReplayBuffer
__all__ = ["OUNoise", "ReplayBuffer"]
+61
View File
@@ -0,0 +1,61 @@
"""
Actor Network for Wireless Resource Allocation / 无线资源分配中的 Actor 网络
This file defines the Actor network architecture for the Co-MADDPG project.
The Actor maps local observations to deterministic resource allocation actions.
本文档定义了 Co-MADDPG 项目中的 Actor 网络架构。
Actor 网络将局部观测值映射到确定性的资源分配动作。
Network Architecture / 网络架构:
FC(obs_dim \u2192 256 \u2192 256 \u2192 128 \u2192 act_dim)
Output Mapping / 输出映射: (Tanh + 1) / 2 \u2208 [0, 1]
Reference / 参考文献: Section 3.2.1 Actor-Critic Structure in the project paper.
"""
import torch
import torch.nn as nn
class Actor(nn.Module):
"""
Actor network for mapping observations to deterministic actions in [0, 1].
Actor 网络,将观测值映射到 [0, 1] 范围内的确定性动作。
Architecture / 架构: FC(obs_dim \u2192 256 \u2192 256 \u2192 128 \u2192 act_dim)
Paper Ref / 论文参考: Section 3.2.1 - Policy Network implementation.
Args / 参数:
obs_dim (int): Dimension of the observation space. / 观测空间的维度。
act_dim (int): Dimension of the action space. / 动作空间的维度。
hidden_sizes (list): Sizes of the three hidden layers (default: [256, 256, 128]). / 三个隐藏层的维度(默认:[256, 256, 128])。
"""
def __init__(self, obs_dim, act_dim, hidden_sizes=[256, 256, 128]):
super(Actor, self).__init__()
# Ensure exactly 3 hidden layers as per model design / 确保按照模型设计包含恰好 3 个隐藏层
assert len(hidden_sizes) == 3, "Actor requires exactly 3 hidden layer sizes"
# Define the feedforward neural network / 定义前馈神经网络
# FC(obs_dim \u2192 256 \u2192 256 \u2192 128 \u2192 act_dim)
self.net = nn.Sequential(
nn.Linear(obs_dim, hidden_sizes[0]),
nn.ReLU(),
nn.Linear(hidden_sizes[0], hidden_sizes[1]),
nn.ReLU(),
nn.Linear(hidden_sizes[1], hidden_sizes[2]),
nn.ReLU(),
nn.Linear(hidden_sizes[2], act_dim)
)
def forward(self, obs):
"""
Forward pass for the Actor network. / Actor 网络的前向传播。
Args / 参数:
obs (torch.Tensor): Local observation tensor. / 局部观测张量。
Returns / 返回:
torch.Tensor: Actions mapped to the [0, 1] range. / 映射到 [0, 1] 范围的动作。
"""
# Pass observations through the linear layers / 将观测值传入线性层
out = self.net(obs)
# Formula / 公式: Output (Tanh + 1) / 2 maps result to [0, 1] range / 将输出结果映射到 [0, 1] 范围
return (torch.tanh(out) + 1.0) / 2.0
+376
View File
@@ -0,0 +1,376 @@
"""
Co-MADDPG Algorithm for Wireless Resource Allocation / 无线资源分配中的 Co-MADDPG 算法
This file implements the Cooperative Multi-Agent Deep Deterministic Policy Gradient (Co-MADDPG) algorithm.
It features a Leader-Follower (Stackelberg) update structure for semantic and traditional agents.
本文档实现了协作式多智能体深度确定性策略梯度 (Co-MADDPG) 算法。
该算法针对语义智能体和传统智能体采用了领导者-跟随者(Stackelberg)更新结构。
Key Components / 关键组件:
- Actor-Critic Architecture / Actor-Critic 架构
- Stackelberg Update / Stackelberg 更新 (Follower update first, then Leader uses Follower's best response)
- Dynamic Cooperation Weight / 动态协作权重: \u03bb(t) = sigmoid(\u03b2*(QoE_sys - Q_th))
- Mixed Reward / 混合奖励: r_i = \u03bb*r_coop + (1-\u03bb)*r_comp
- Soft Update / 软更新: \u03b8_target \u2190 \u03c4*\u03b8 + (1-\u03c4)*\u03b8_target
Reference / 参考文献: Section 3.2 Leader-Follower Game and Co-MADDPG in the project paper.
"""
import os
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from agents.actor import Actor
from agents.critic import Critic
from agents.replay_buffer import ReplayBuffer
from agents.noise import OUNoise
class CoMADDPG:
"""
Co-MADDPG Algorithm featuring Leader-Follower updating structure.
具有领导者-跟随者更新结构的 Co-MADDPG 算法。
Agent S: Semantic Agent (Leader) / 语义智能体(领导者)
Agent B: Traditional/Bit-stream Agent (Follower) / 传统/比特流智能体(跟随者)
Paper Ref / 论文参考: Section 3.2 - Co-MADDPG Implementation details.
"""
def __init__(self, config):
self.config = config
# Dimensions derived from config / 从配置中提取维度信息
# Dimensions derived from config
self.obs_dim = config['env']['num_subcarriers'] + 4
self.act_dim = 3
# The critic observes joint states and actions / Critic 观察联合状态和动作
# The critic observes joint states and actions
obs_dim_total = self.obs_dim * 2
act_dim_total = self.act_dim * 2
# Determine device implicitly / 自动检测设备 (CUDA 或 CPU)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Hyperparameters / 超参数设置
# Hyperparameters
train_cfg = config.get('training', {})
self.gamma = train_cfg.get('gamma', 0.95)
self.tau = train_cfg.get('tau', 0.01)
self.beta = train_cfg.get('beta', 5.0)
self.q_threshold = train_cfg.get('q_threshold', 0.6)
self.batch_size = train_cfg.get('batch_size', 256)
actor_lr = train_cfg.get('actor_lr', 1e-4)
critic_lr = train_cfg.get('critic_lr', 3e-4)
buffer_capacity = train_cfg.get('buffer_capacity', 100000)
# Network configurations / 网络配置项
# Network configurations
net_cfg = config.get('network', {})
actor_hidden = net_cfg.get('actor_hidden', [256, 256, 128])
critic_hidden = net_cfg.get('critic_hidden', [512, 512, 256])
# Create Actor Networks / 创建 Actor 网络
self.actor_s = Actor(self.obs_dim, self.act_dim, actor_hidden).to(self.device)
self.actor_b = Actor(self.obs_dim, self.act_dim, actor_hidden).to(self.device)
# Create Actor Target Networks / 创建 Actor 目标网络
self.actor_s_target = Actor(self.obs_dim, self.act_dim, actor_hidden).to(self.device)
self.actor_b_target = Actor(self.obs_dim, self.act_dim, actor_hidden).to(self.device)
self.actor_s_target.load_state_dict(self.actor_s.state_dict())
self.actor_b_target.load_state_dict(self.actor_b.state_dict())
# Create Critic Networks / 创建 Critic 网络
self.critic_s = Critic(obs_dim_total, act_dim_total, critic_hidden).to(self.device)
self.critic_b = Critic(obs_dim_total, act_dim_total, critic_hidden).to(self.device)
# Create Critic Target Networks / 创建 Critic 目标网络
self.critic_s_target = Critic(obs_dim_total, act_dim_total, critic_hidden).to(self.device)
self.critic_b_target = Critic(obs_dim_total, act_dim_total, critic_hidden).to(self.device)
self.critic_s_target.load_state_dict(self.critic_s.state_dict())
self.critic_b_target.load_state_dict(self.critic_b.state_dict())
# Optimizers / 优化器设置
self.actor_optimizer_s = optim.Adam(self.actor_s.parameters(), lr=actor_lr)
self.actor_optimizer_b = optim.Adam(self.actor_b.parameters(), lr=actor_lr)
self.critic_optimizer_s = optim.Adam(self.critic_s.parameters(), lr=critic_lr)
self.critic_optimizer_b = optim.Adam(self.critic_b.parameters(), lr=critic_lr)
# MSE Loss for critics / Critic 使用的均方误差损失函数
self.critic_loss_fn = nn.MSELoss()
# Replay Buffer / 经验回放池
self.replay_buffer = ReplayBuffer(buffer_capacity)
# Ornstein-Uhlenbeck noise / OU 探索噪声
ou_sigma = train_cfg.get('ou_sigma_init', 0.2)
ou_theta = train_cfg.get('ou_theta', 0.15)
self.noise_s = OUNoise(self.act_dim, theta=ou_theta, sigma_init=ou_sigma)
self.noise_b = OUNoise(self.act_dim, theta=ou_theta, sigma_init=ou_sigma)
def select_action(self, obs_s, obs_b, explore=True):
"""
Determines the actions using the actor networks, with optional OU exploration noise.
使用 Actor 网络确定动作,可选择性添加 OU 探索噪声。
Args / 参数:
obs_s, obs_b: Observations for agents S and B. / 智能体 S 和 B 的观测值。
explore (bool): Whether to add noise for exploration. / 是否添加探索噪声。
Returns / 返回:
tuple: (act_s, act_b) actions for each agent. / 每个智能体的动作 (act_s, act_b)。
"""
self.actor_s.eval()
self.actor_b.eval()
with torch.no_grad():
obs_s_t = torch.FloatTensor(obs_s).unsqueeze(0).to(self.device)
obs_b_t = torch.FloatTensor(obs_b).unsqueeze(0).to(self.device)
act_s = self.actor_s(obs_s_t).cpu().numpy().squeeze(0)
act_b = self.actor_b(obs_b_t).cpu().numpy().squeeze(0)
self.actor_s.train()
self.actor_b.train()
# Apply OU noise if exploration is enabled / 如果开启探索,则添加 OU 噪声
if explore:
act_s += self.noise_s.sample()
act_b += self.noise_b.sample()
# Formula / 公式: act \u2208 [0, 1]
# Clip mapping bounds as enforced by the (tanh + 1)/2 activation in Actor / 按照 Actor 中的激活函数限制动作范围到 [0, 1]
act_s = np.clip(act_s, 0.0, 1.0)
act_b = np.clip(act_b, 0.0, 1.0)
return act_s, act_b
def compute_lambda(self, qoe_sys):
"""
Compute dynamic cooperation weight \u03bb(t). / 计算动态协作权重 \u03bb(t)。
Formula / 公式: \u03bb(t) = sigmoid(\u03b2 * (QoE_sys - Q_th))
Args / 参数:
qoe_sys (float): Current system QoE. / 当前系统 QoE。
Returns / 返回:
float: Cooperation weight \u03bb(t) \u2208 [0, 1]. / 协作权重 \u03bb(t)。
"""
return 1.0 / (1.0 + np.exp(-self.beta * (qoe_sys - self.q_threshold)))
def compute_rewards(self, qoe_s, qoe_b, qoe_sys):
"""
Compute joint dynamically weighted rewards based on \u03bb cooperation factor.
基于 \u03bb 协作因子计算动态加权的联合奖励。
Formula / 公式: r_i = \u03bb * r_coop_i + (1 - \u03bb) * r_comp_i
Args / 参数:
qoe_s, qoe_b, qoe_sys: QoE values for semantic, traditional, and system levels. / 语义层、传统层和系统层的 QoE 值。
Returns / 返回:
tuple: (r_s, r_b, lambda_val) final mixed rewards and the cooperation weight. / 最终混合奖励与协作权重。
"""
lambda_val = self.compute_lambda(qoe_sys)
rew_cfg = self.config.get('reward', {})
coop_self = rew_cfg.get('coop_self', 0.5)
coop_other = rew_cfg.get('coop_other', 0.3)
coop_sys = rew_cfg.get('coop_sys', 0.2)
comp_self = rew_cfg.get('comp_self', 0.8)
comp_sys = rew_cfg.get('comp_sys', 0.2)
# Cooperative logic (shared benefit mindset) / 协作逻辑(共同利益导向)
# Formula / 公式: r_coop_i = 0.5*qoe_i + 0.3*qoe_j + 0.2*qoe_sys
r_coop_s = coop_self * qoe_s + coop_other * qoe_b + coop_sys * qoe_sys
r_coop_b = coop_self * qoe_b + coop_other * qoe_s + coop_sys * qoe_sys
# Competitive logic (individual maximization mindset) / 竞争逻辑(个体利益导向)
# Formula / 公式: r_comp_i = 0.8*qoe_i + 0.2*qoe_sys
r_comp_s = comp_self * qoe_s + comp_sys * qoe_sys
r_comp_b = comp_self * qoe_b + comp_sys * qoe_sys
# Dynamically balanced reward (mix based on System QoE state vs threshold) / 动态平衡奖励(基于系统 QoE 状态与阈值的混合)
r_s = lambda_val * r_coop_s + (1.0 - lambda_val) * r_comp_s
r_b = lambda_val * r_coop_b + (1.0 - lambda_val) * r_comp_b
return r_s, r_b, lambda_val
def update(self):
"""
Perform a gradient update loop containing Leader-Follower sequential methodology.
执行包含领导者-跟随者顺序方法的梯度更新循环。
Update Order / 更新顺序:
1. Update Follower (Agent B) Critic & Actor / 更新跟随者(智能体 B)的 Critic 和 Actor
2. Update Leader (Agent S) Critic & Actor / 更新领导者(智能体 S)的 Critic 和 Actor
Returns / 返回:
tuple: (critic_loss_s, critic_loss_b, actor_loss_s, actor_loss_b) or None if buffer not ready. / 各项损失值。
"""
if len(self.replay_buffer) < self.batch_size:
return None
# Sample batch from replay buffer / 从回放池中采样批次数据
batch = self.replay_buffer.sample(self.batch_size)
# Destructure standardized tuple. Assumes order:
# (obs_s, obs_b, act_s, act_b, rew_s, rew_b, next_obs_s, next_obs_b, dones)
obs_s, obs_b, act_s, act_b, rew_s, rew_b, next_obs_s, next_obs_b, dones = batch
obs_s = torch.FloatTensor(obs_s).to(self.device)
obs_b = torch.FloatTensor(obs_b).to(self.device)
act_s = torch.FloatTensor(act_s).to(self.device)
act_b = torch.FloatTensor(act_b).to(self.device)
rew_s = torch.FloatTensor(rew_s).unsqueeze(1).to(self.device)
rew_b = torch.FloatTensor(rew_b).unsqueeze(1).to(self.device)
next_obs_s = torch.FloatTensor(next_obs_s).to(self.device)
next_obs_b = torch.FloatTensor(next_obs_b).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(1).to(self.device)
# Construct joint states & actions for centralized critic / 构建用于集中式 Critic 的联合状态和动作空间
obs_all = torch.cat([obs_s, obs_b], dim=1)
next_obs_all = torch.cat([next_obs_s, next_obs_b], dim=1)
act_all = torch.cat([act_s, act_b], dim=1)
# Target actions for next state / 计算下一状态的目标动作值
with torch.no_grad():
next_act_s_target = self.actor_s_target(next_obs_s)
next_act_b_target = self.actor_b_target(next_obs_b)
next_act_all_target = torch.cat([next_act_s_target, next_act_b_target], dim=1)
# =====================================================================
# PHASE 1: Update Follower (Agent B) FIRST / 第一阶段:首先更新跟随者 (智能体 B)
# Stackelberg methodology / Stackelberg 方法论: Follower responds to Leader's action / 跟随者响应领导者的动作
# =====================================================================
# PHASE 1: Update Follower (Agent B) FIRST
# =====================================================================
# Update Critic B / 更新智能体 B 的 Critic
with torch.no_grad():
target_q_b_next = self.critic_b_target(next_obs_all, next_act_all_target)
target_q_b = rew_b + self.gamma * (1.0 - dones) * target_q_b_next
current_q_b = self.critic_b(obs_all, act_all)
critic_loss_b = self.critic_loss_fn(current_q_b, target_q_b)
self.critic_optimizer_b.zero_grad()
critic_loss_b.backward()
self.critic_optimizer_b.step()
# Update Actor B / 更新智能体 B 的 Actor
# Loss: -mean(critic_b(obs_all, [act_s_from_buffer, actor_b(obs_b)]))
# In Phase 1, the follower assumes leader's action from replay buffer / 在第一阶段,跟随者假定领导者的动作为回放池中的动作
# Loss: -mean(critic_b(obs_all, [act_s_from_buffer, actor_b(obs_b)]))
new_act_b = self.actor_b(obs_b)
act_all_for_b = torch.cat([act_s, new_act_b], dim=1)
actor_loss_b = -self.critic_b(obs_all, act_all_for_b).mean()
self.actor_optimizer_b.zero_grad()
actor_loss_b.backward()
self.actor_optimizer_b.step()
# =====================================================================
# PHASE 2: Update Leader (Agent S) with UPDATED Follower / 第二阶段:基于更新后的跟随者更新领导者 (智能体 S)
# Leader S uses Follower B's best response / 领导者 S 利用跟随者 B 的最佳响应函数
# =====================================================================
# PHASE 2: Update Leader (Agent S) with UPDATED Follower
# =====================================================================
# Update Critic S / 更新智能体 S 的 Critic
with torch.no_grad():
target_q_s_next = self.critic_s_target(next_obs_all, next_act_all_target)
target_q_s = rew_s + self.gamma * (1.0 - dones) * target_q_s_next
current_q_s = self.critic_s(obs_all, act_all)
critic_loss_s = self.critic_loss_fn(current_q_s, target_q_s)
self.critic_optimizer_s.zero_grad()
critic_loss_s.backward()
self.critic_optimizer_s.step()
# Update Actor S / 更新智能体 S 的 Actor
# KEY / 核心逻辑: Use newly updated actor_b(obs_b).detach() as follower's assumed action / 使用刚更新的 actor_b(obs_b).detach() 作为跟随者的预估动作
# This represents the Leader's knowledge of the Follower's best response / 这代表了领导者对跟随者最佳响应的认知
# KEY: Use newly updated actor_b(obs_b).detach() as follower's assumed action
new_act_s = self.actor_s(obs_s)
updated_act_b_detached = self.actor_b(obs_b).detach()
act_all_for_s = torch.cat([new_act_s, updated_act_b_detached], dim=1)
actor_loss_s = -self.critic_s(obs_all, act_all_for_s).mean()
self.actor_optimizer_s.zero_grad()
actor_loss_s.backward()
self.actor_optimizer_s.step()
# =====================================================================
# Target Networks Soft Update / 目标网络软更新
# Formula / 公式: \u03b8_target \u2190 \u03c4 * \u03b8 + (1 - \u03c4) * \u03b8_target
# =====================================================================
# Target Networks Soft Update
# =====================================================================
self.soft_update(self.actor_s_target, self.actor_s, self.tau)
self.soft_update(self.actor_b_target, self.actor_b, self.tau)
self.soft_update(self.critic_s_target, self.critic_s, self.tau)
self.soft_update(self.critic_b_target, self.critic_b, self.tau)
return critic_loss_s.item(), critic_loss_b.item(), actor_loss_s.item(), actor_loss_b.item()
def soft_update(self, target, source, tau):
"""
Polyak averaging for target network parameters. / 目标网络参数的 Polyak 平均(软更新)。
Args / 参数:
target: Target network. / 目标网络。
source: Source network. / 源网络。
tau (float): Soft update interpolation factor. / 软更新插值因子 \u03c4
"""
for target_param, source_param in zip(target.parameters(), source.parameters()):
target_param.data.copy_(tau * source_param.data + (1.0 - tau) * target_param.data)
def save(self, path):
"""
Saves all 4 network state_dicts and optimizers. / 保存所有 4 个网络的权重和优化器状态。
Args / 参数:
path (str): File path to save the checkpoint. / 保存检查点的文件路径。
"""
os.makedirs(os.path.dirname(path), exist_ok=True)
torch.save({
'actor_s': self.actor_s.state_dict(),
'actor_b': self.actor_b.state_dict(),
'critic_s': self.critic_s.state_dict(),
'critic_b': self.critic_b.state_dict(),
'actor_optimizer_s': self.actor_optimizer_s.state_dict(),
'actor_optimizer_b': self.actor_optimizer_b.state_dict(),
'critic_optimizer_s': self.critic_optimizer_s.state_dict(),
'critic_optimizer_b': self.critic_optimizer_b.state_dict(),
}, path)
def load(self, path):
"""
Loads all 4 networks and optimizer parameters from saved states. / 从保存的状态加载所有 4 个网络和优化器参数。
Args / 参数:
path (str): File path of the checkpoint to load. / 要加载的检查点文件路径。
"""
checkpoint = torch.load(path, map_location=self.device)
self.actor_s.load_state_dict(checkpoint['actor_s'])
self.actor_b.load_state_dict(checkpoint['actor_b'])
self.critic_s.load_state_dict(checkpoint['critic_s'])
self.critic_b.load_state_dict(checkpoint['critic_b'])
self.actor_optimizer_s.load_state_dict(checkpoint['actor_optimizer_s'])
self.actor_optimizer_b.load_state_dict(checkpoint['actor_optimizer_b'])
self.critic_optimizer_s.load_state_dict(checkpoint['critic_optimizer_s'])
self.critic_optimizer_b.load_state_dict(checkpoint['critic_optimizer_b'])
# Hard sync the target networks after loading
self.actor_s_target.load_state_dict(self.actor_s.state_dict())
self.actor_b_target.load_state_dict(self.actor_b.state_dict())
self.critic_s_target.load_state_dict(self.critic_s.state_dict())
self.critic_b_target.load_state_dict(self.critic_b.state_dict())
+63
View File
@@ -0,0 +1,63 @@
"""
Critic Network for Wireless Resource Allocation / 无线资源分配中的 Critic 网络
This file defines the Critic network architecture for the Co-MADDPG project.
The Critic estimates the joint Q-value based on the global observations and actions of all agents.
本文档定义了 Co-MADDPG 项目中的 Critic 网络架构。
Critic 网络基于所有智能体的全局观测和动作来估算联合 Q 值。
Network Architecture / 网络架构:
FC(obs_dim_total + act_dim_total \u2192 512 \u2192 512 \u2192 256 \u2192 1)
Input / 输入: Concatenated observations and actions / 拼接后的观测与动作
Reference / 参考文献: Section 3.2.1 Actor-Critic Structure in the project paper.
"""
import torch
import torch.nn as nn
class Critic(nn.Module):
"""
Critic network for assessing the value of joint actions given joint observations.
Critic 网络,用于在给定联合观测的情况下评估联合动作的价值。
Architecture / 架构: FC(obs_dim_total + act_dim_total \u2192 512 \u2192 512 \u2192 256 \u2192 1)
Paper Ref / 论文参考: Section 3.2.1 - Centralized Critic implementation.
Args / 参数:
obs_dim_total (int): Total dimension of concatenated observations. / 所有智能体拼接后的总观测维度。
act_dim_total (int): Total dimension of concatenated actions. / 所有智能体拼接后的总动作维度。
hidden_sizes (list): Sizes of the three hidden layers (default: [512, 512, 256]). / 三个隐藏层的维度(默认:[512, 512, 256])。
"""
def __init__(self, obs_dim_total, act_dim_total, hidden_sizes=[512, 512, 256]):
super(Critic, self).__init__()
# Ensure exactly 3 hidden layers as per model design / 确保按照模型设计包含恰好 3 个隐藏层
assert len(hidden_sizes) == 3, "Critic requires exactly 3 hidden layer sizes"
# Define the feedforward neural network / 定义前馈神经网络
# FC(obs_dim_total + act_dim_total \u2192 512 \u2192 512 \u2192 256 \u2192 1)
self.net = nn.Sequential(
nn.Linear(obs_dim_total + act_dim_total, hidden_sizes[0]),
nn.ReLU(),
nn.Linear(hidden_sizes[0], hidden_sizes[1]),
nn.ReLU(),
nn.Linear(hidden_sizes[1], hidden_sizes[2]),
nn.ReLU(),
nn.Linear(hidden_sizes[2], 1)
)
def forward(self, obs_all, act_all):
"""
Forward pass for the Critic network. / Critic 网络的前向传播。
Args / 参数:
obs_all (torch.Tensor): The concatenated joint observation tensor. / 拼接后的联合观测张量。
act_all (torch.Tensor): The concatenated joint action tensor. / 拼接后的联合动作张量。
Returns / 返回:
torch.Tensor: Scalar Q-value evaluation. / 标量 Q 值评估结果。
"""
# Formula / 公式: x = [obs_total, act_total]
# Concatenate joint states and actions together for input / 将联合状态和动作拼接作为输入
x = torch.cat([obs_all, act_all], dim=1)
# Pass the concatenated input through the network / 将拼接后的输入传入网络
return self.net(x)
+74
View File
@@ -0,0 +1,74 @@
"""
Ornstein-Uhlenbeck (OU) Exploration Noise / OU 探索噪声
This file implements the OU noise process for continuous action exploration.
The noise is temporally correlated and features linear sigma decay over training.
本文档实现了用于连续动作探索的 OU 噪声过程。
该噪声具有时间相关性,并在训练过程中具有线性标准差(sigma)衰减特性。
Formula / 公式: dx = \u03b8(\u03bc - x)dt + \u03c3dW
Decay / 衰减: Linear sigma decay over specified decay period. / 在指定的衰减周期内线性衰减 sigma。
Reference / 参考文献: Section 3.2.2 Exploration Mechanism in the project paper.
"""
import numpy as np
class OUNoise:
"""Ornstein-Uhlenbeck process for temporally correlated exploration noise.
用于生成具有时间相关性的探索噪声的 Ornstein-Uhlenbeck 过程。
Formula / 公式: dx = \u03b8(\u03bc - x)dt + \u03c3dW
Args / 参数:
action_dim (int): Dimensionality of the action space. / 动作空间的维度。
mu (float): Long-term mean of the process. / 过程的长期均值 \u03bc
theta (float): Mean-reversion rate. / 均值回归速率 \u03b8
sigma_init (float): Initial standard deviation. / 初始标准差 \u03c3
sigma_min (float): Minimum standard deviation after decay. / 衰减后的最小标准差。
decay_period (int): Number of episodes over which sigma decays linearly. / sigma 线性衰减的总回合数。
"""
def __init__(self, action_dim: int, mu: float = 0.0, theta: float = 0.15,
sigma_init: float = 0.2, sigma_min: float = 0.01,
decay_period: int = 5000):
# Initialize noise parameters and state / 初始化噪声参数和状态
self.action_dim = action_dim
self.mu = mu
self.theta = theta
self.sigma_init = sigma_init
self.sigma_min = sigma_min
self.sigma = sigma_init
self.decay_period = decay_period
self.state = np.full(action_dim, mu, dtype=np.float64)
def reset(self):
"""
Reset the internal state to the mean. / 将内部状态重置为均值 \u03bc
"""
self.state = np.full(self.action_dim, self.mu, dtype=np.float64)
def decay_sigma(self, episode: int):
"""Linearly decay sigma from sigma_init to sigma_min over decay_period.
在衰减周期内,将 sigma 从初始值线性衰减到最小值。
Args / 参数:
episode (int): Current episode number. / 当前回合数。
"""
# Calculate decay fraction / 计算衰减比例
frac = min(1.0, episode / max(1, self.decay_period))
# Linear decay formula / 线性衰减公式: \u03c3 = \u03c3_init + frac * (\u03c3_min - \u03c3_init)
self.sigma = self.sigma_init + frac * (self.sigma_min - self.sigma_init)
def sample(self) -> np.ndarray:
"""
Generate a noise sample via the OU process. / 通过 OU 过程生成噪声样本。
Returns / 返回:
np.ndarray: Noise vector of shape (action_dim,). / 形状为 (action_dim,) 的噪声向量。
"""
# OU Formula / OU 公式: dx = \u03b8 * (\u03bc - x) + \u03c3 * N(0,1)
dx = (self.theta * (self.mu - self.state)
+ self.sigma * np.random.randn(self.action_dim))
# Update state / 更新状态: x = x + dx
self.state = self.state + dx
return self.state.copy()
+92
View File
@@ -0,0 +1,92 @@
"""
Experience Replay Buffer for Multi-Agent RL / 多智能体强化学习的经验回放池
This file implements a fixed-size replay buffer to store and sample transitions.
Each transition contains observations, actions, and rewards for both semantic and traditional agents.
本文档实现了一个固定大小的回放池,用于存储和采样状态转移。
每个状态转移包含语义智能体和传统智能体的观测、动作及奖励。
Storage Format / 存储格式: 9-field transitions / 9 字段状态转移
(obs_s, obs_b, act_s, act_b, rew_s, rew_b, next_obs_s, next_obs_b, done)
Reference / 参考文献: Section 3.2.3 Experience Replay in the project paper.
"""
import random
from collections import deque
import numpy as np
class ReplayBuffer:
"""Fixed-size experience replay buffer for two-agent transitions.
用于双智能体状态转移的固定大小经验回放池。
Stores transitions of the form / 存储如下形式的状态转移:
(obs_s, obs_b, act_s, act_b, rew_s, rew_b, next_obs_s, next_obs_b, done)
Args / 参数:
capacity (int): Maximum number of transitions to store. / 存储转换的最大数量。
"""
def __init__(self, capacity: int):
# Initialize the buffer as a double-ended queue with a maximum length / 将回放池初始化为具有最大长度的双端队列
self.buffer = deque(maxlen=capacity)
def push(self, obs_s, obs_b, act_s, act_b, rew_s, rew_b,
next_obs_s, next_obs_b, done=False):
"""
Store a single transition into the buffer. / 将单次状态转移存入回放池。
Args / 参数:
obs_s, obs_b: Observations for Semantic and Traditional agents. / 语义智能体与传统智能体的观测。
act_s, act_b: Actions taken by each agent. / 各个智能体采取的动作。
rew_s, rew_b: Rewards received by each agent. / 各个智能体获得的奖励。
next_obs_s, next_obs_b: Next observations. / 下一个状态的观测。
done (bool): Whether the episode ended. / 回合是否结束。
"""
# Append the 9-field transition to the deque / 将 9 字段的状态转移添加到队列中
self.buffer.append((
np.asarray(obs_s, dtype=np.float32),
np.asarray(obs_b, dtype=np.float32),
np.asarray(act_s, dtype=np.float32),
np.asarray(act_b, dtype=np.float32),
float(rew_s),
float(rew_b),
np.asarray(next_obs_s, dtype=np.float32),
np.asarray(next_obs_b, dtype=np.float32),
float(done),
))
def sample(self, batch_size: int):
"""
Sample a random batch of transitions for training. / 随机采样一批状态转移用于训练。
Args / 参数:
batch_size (int): Number of transitions to sample. / 采样数量。
Returns / 返回:
tuple of np.ndarray: (obs_s, obs_b, act_s, act_b, rew_s, rew_b, next_obs_s, next_obs_b, dones)
Each array has shape (batch_size, ...). / 每个数组的形状均为 (batch_size, ...)。
"""
# Randomly select 'batch_size' samples from the buffer / 从回放池中随机选择 batch_size 个样本
batch = random.sample(self.buffer, batch_size)
# Unzip the batch into separate components / 将采样到的批次拆解为独立的组件
obs_s, obs_b, act_s, act_b, rew_s, rew_b, \
next_obs_s, next_obs_b, dones = zip(*batch)
# Convert each component to a numpy array / 将各组件转换为 numpy 数组
return (
np.array(obs_s),
np.array(obs_b),
np.array(act_s),
np.array(act_b),
np.array(rew_s),
np.array(rew_b),
np.array(next_obs_s),
np.array(next_obs_b),
np.array(dones),
)
def __len__(self) -> int:
"""
Return the current size of the buffer. / 返回回放池的当前大小。
"""
return len(self.buffer)