A2C算法

1.算法描述

A2C是一种同步式策略梯度算法 ,核心是通过优势函数 (Â(s,a) = Q(s,a) - V(s))替代传统回报值,显著降低策略梯度的方差

2.代码实现

python 复制代码
# -*- coding: utf-8 -*-

import random
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

class GridWorld:
    def __init__(self, size=6):
        self.size = size
        self.action_space = 4
        self.state_dim = size * size

        self.goal = (size - 1, size - 1)
        self.obstacles = {(2, 2), (3, 2), (1, 4)}

        self.reset()

    def reset(self):
        while True:
            self.agent = (
                np.random.randint(self.size),
                np.random.randint(self.size)
            )
            if self.agent != self.goal and self.agent not in self.obstacles:
                break
        return self._encode_state()

    def step(self, action):
        row, col = self.agent
        nrow, ncol = row, col

        if action == 0:      # up
            nrow -= 1
        elif action == 1:    # down
            nrow += 1
        elif action == 2:    # left
            ncol -= 1
        elif action == 3:    # right
            ncol += 1

        # 边界检查
        if not (0 <= nrow < self.size and 0 <= ncol < self.size):
            nrow, ncol = row, col

        # 障碍物检查
        if (nrow, ncol) in self.obstacles:
            nrow, ncol = row, col

        self.agent = (nrow, ncol)

        reward = -0.05
        done = False

        if self.agent == self.goal:
            reward = 1.0
            done = True

        return self._encode_state(), reward, done

    def _encode_state(self):
        s = np.zeros(self.size * self.size, dtype=np.float32)
        idx = self.agent[0] * self.size + self.agent[1]
        s[idx] = 1.0
        return s

class ActorCritic(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.fc1 = nn.Linear(state_dim, 128)
        self.fc2 = nn.Linear(128, 128)
        self.policy_head = nn.Linear(128, action_dim)
        self.value_head = nn.Linear(128, 1)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return self.policy_head(x), self.value_head(x)

class A2CAgent:
    def __init__(self, state_dim, action_dim, lr=3e-4, gamma=0.99):
        self.gamma = gamma
        self.model = ActorCritic(state_dim, action_dim).to(device)
        self.optimizer = optim.Adam(self.model.parameters(), lr=lr)

    def select_action(self, state):
        state_t = torch.tensor(state[None, :], dtype=torch.float32, device=device)
        with torch.no_grad():
            logits, value = self.model(state_t)
            probs = F.softmax(logits, dim=-1)
        
        action = np.random.choice(len(probs[0]), p=probs.cpu().numpy()[0])
        return action, value.item()

    def update(self, states, actions, rewards, values, next_value, done):
        returns = []
        R = 0.0 if done else next_value

        for r in reversed(rewards):
            R = r + self.gamma * R
            returns.insert(0, R)

        states_t = torch.tensor(np.array(states), dtype=torch.float32, device=device)
        actions_t = torch.tensor(actions, dtype=torch.long, device=device)
        returns_t = torch.tensor(returns, dtype=torch.float32, device=device)
        values_t = torch.tensor(values, dtype=torch.float32, device=device)

        advantages = (returns_t - values_t).detach()

        logits, value_preds = self.model(states_t)
        value_preds = value_preds.squeeze()

        policy_loss = F.cross_entropy(logits, actions_t, reduction='none')
        policy_loss = torch.mean(policy_loss * advantages)

        # Value Loss
        value_loss = torch.mean(torch.square(returns_t - value_preds))

        # Entropy Loss
        probs = F.softmax(logits, dim=-1)
        log_probs = F.log_softmax(logits, dim=-1)
        entropy = -torch.mean(torch.sum(probs * log_probs, dim=1))

        # 总 Loss
        loss = policy_loss + 0.5 * value_loss - 0.01 * entropy

        # 反向传播更新
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

env = GridWorld(size=6)
agent = A2CAgent(env.state_dim, env.action_space)

EPISODES = 600
MAX_STEPS = 80

for ep in range(EPISODES):
    state = env.reset()
    states, actions, rewards, values = [], [], [], []

    for _ in range(MAX_STEPS):
        action, value = agent.select_action(state)
        next_state, reward, done = env.step(action)

        states.append(state)
        actions.append(action)
        rewards.append(reward)
        values.append(value)

        state = next_state
        if done:
            break

    state_t = torch.tensor(state[None, :], dtype=torch.float32, device=device)
    with torch.no_grad():
        _, next_value = agent.model(state_t)
    
    agent.update(states, actions, rewards, values, next_value.item(), done)

    if ep % 50 == 0:
        print(f"Episode {ep:3d} | steps: {len(rewards)}")

def visualize_policy(env, agent):
    arrow = {0: "↑", 1: "↓", 2: "←", 3: "→"}
    grid = [["" for _ in range(env.size)] for _ in range(env.size)]

    for r in range(env.size):
        for c in range(env.size):
            if (r, c) == env.goal:
                grid[r][c] = "G"
            elif (r, c) in env.obstacles:
                grid[r][c] = "X"
            else:
                s = np.zeros(env.size * env.size, dtype=np.float32)
                s[r * env.size + c] = 1.0
                s_t = torch.tensor(s[None, :], dtype=torch.float32, device=device)
                with torch.no_grad():
                    logits, _ = agent.model(s_t)
                a = torch.argmax(logits, dim=1).item()
                grid[r][c] = arrow[a]

    fig, ax = plt.subplots(figsize=(5, 5))
    ax.set_xlim(0, env.size)
    ax.set_ylim(0, env.size)
    ax.set_xticks(np.arange(env.size + 1))
    ax.set_yticks(np.arange(env.size + 1))
    ax.grid(True)

    for r in range(env.size):
        for c in range(env.size):
            ax.text(
                c + 0.5,
                env.size - r - 0.5,
                grid[r][c],
                ha="center",
                va="center",
                fontsize=16
            )

    ax.set_title("A2C 学到的策略(箭头方向)")
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    plt.show()

if __name__ == "__main__":
    visualize_policy(env, agent)

3.结果展示

bash 复制代码
Using device: cpu
Episode   0 | steps: 80
Episode  50 | steps: 3
Episode 100 | steps: 38
Episode 150 | steps: 4
Episode 200 | steps: 31
Episode 250 | steps: 11
Episode 300 | steps: 7
Episode 350 | steps: 15
Episode 400 | steps: 9
Episode 450 | steps: 3
Episode 500 | steps: 15
Episode 550 | steps: 3
相关推荐
东坡肘子8 小时前
不是模型变慢了,是任务变大了 -- 肘子的 Swift 周报 #146
人工智能·swiftui·swift
xexpertS9 小时前
不稳定测试治理:如何通过自动检测与自动抑制提升 CI 稳定性
人工智能·自动化
小程故事多_809 小时前
Graph Engineering,重构AI智能体协作的底层逻辑,让复杂任务高效落地
人工智能·重构
宸津-代码粉碎机9 小时前
Jar热部署进阶实战|修复原生方案OOM与类冲突问题,生产级无BUG优化方案
java·大数据·服务器·开发语言·前端·人工智能·python
咖啡星人k9 小时前
AI 编程工具集体变脸:Cursor 漏洞、TRAE 限额、Claude Code 数据收集风波
人工智能·安全·microsoft·cursor·trae·ai编程工具
凡人AI录9 小时前
GPT-5.6 正式发布:Codex 并入 ChatGPT,额度与模型体系全面调整
人工智能·gpt·chatgpt
墨舟的AI笔记9 小时前
组件级响应式的破局:CSS 容器查询的工程落地与取舍
人工智能
Molesidy10 小时前
【AI】【ChatGPT】【Codex】codex桌面版的插件(MCP)和技能(skills)的安装和应用
人工智能·codex·ai agent
工业设备方案笔记10 小时前
AI大模型为什么开始跑到边缘设备上?——本地大模型部署正在成为新的行业趋势
人工智能
未知违规用户11 小时前
大模型项目:RAG项目实战与FlagEmbedding模型
人工智能·windows·python·深度学习