1. PyTorch与Gym环境搭建
第一次接触强化学习的朋友们可能会觉得环境配置很头疼,但别担心,跟着我的步骤来,保证你能在10分钟内跑通第一个Demo。我去年在给团队做技术培训时,这套配置方法已经帮助50+新人快速上手了。
1.1 PyTorch安装避坑指南
PyTorch的安装命令看似简单,但新手常在这里翻车。先检查你的Python版本(建议3.8+),然后打开PyTorch官网选择对应配置。以Linux系统+CUDA 11.3为例,实际安装时要注意:
# 关键!先升级pip避免版本冲突 python -m pip install --upgrade pip # 安装带CUDA支持的PyTorch(根据显卡驱动选择CUDA版本) pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113验证安装时别只用简单的import torch,试试这个完整测试:
import torch print(torch.__version__) # 应显示1.12.1 print(torch.cuda.is_available()) # 必须返回True test_tensor = torch.randn(2,3).cuda() # 创建GPU张量 print(test_tensor.device) # 应显示cuda:0如果CUDA不可用,八成是显卡驱动版本不匹配。我遇到过最坑的情况是RTX 30系显卡必须装CUDA 11+,而默认安装的10.2会报错。
1.2 Gym全家桶安装技巧
OpenAI Gym现在有多个变体版本,推荐直接安装最新版Gymnasium(原Gym的升级版):
# 基础包(包含经典控制环境) pip install gymnasium # 完整版(包含Box2D、Atari等) pip install "gymnasium[all]"验证安装时特别要注意渲染依赖。比如在无GUI的服务器上,得先装这些:
# Ubuntu系统需要先装这些 sudo apt-get install -y python-opengl xvfb ffmpeg测试环境时别急着跑复杂任务,先用这个最小示例:
import gymnasium as gym env = gym.make('CartPole-v1', render_mode='human') obs, _ = env.reset() for _ in range(100): action = env.action_space.sample() # 随机动作 obs, reward, done, _, _ = env.step(action) if done: break env.close()2. CartPole环境深度解析
2.1 状态空间与动作空间
CartPole看似简单,但它的状态空间设计非常经典。通过env.observation_space可以看到4个关键参数:
print(env.observation_space) # Box([-4.8, -3.4e38, -0.42, -3.4e38], [4.8, 3.4e38, 0.42, 3.4e38], (4,), float32)这4个值分别代表:
- 小车位置(范围±2.4)
- 小车速度(理论上无界)
- 杆子角度(±12度)
- 杆子角速度
动作空间只有两个离散值:
print(env.action_space) # Discrete(2)0表示向左推,1表示向右推。我在第一次实现时犯了个低级错误——以为动作是连续力值,结果代码怎么调都不work。
2.2 奖励机制设计奥秘
CartPole的奖励设计是典型的稀疏奖励:
- 每步存活奖励+1
- 失败条件:
- 杆子倾斜超过12度
- 小车超出±2.4单位范围
- 步数超过500(v1版本)
用以下代码可以可视化环境状态:
def print_state(obs): print(f"位置:{obs[0]:+.2f} 速度:{obs[1]:+.2f} 角度:{math.degrees(obs[2]):+.1f}° 角速度:{obs[3]:+.2f}") env = gym.make('CartPole-v1') obs, _ = env.reset() print_state(obs) # 初始状态示例3. DQN算法原理剖析
3.1 Q-Learning核心思想
DQN的本质是用神经网络近似Q函数。传统Q-Learning更新公式:
Q(s,a) ← Q(s,a) + α[r + γ·max(Q(s',a')) - Q(s,a)]在深度版本中,我们用神经网络参数θ来近似Q值:
Loss = [r + γ·max(Q(s',a'; θ')) - Q(s,a; θ)]²这里有个关键技巧——使用目标网络(Target Network)来稳定训练。我最初实现时没注意这点,结果模型根本学不会。
3.2 经验回放实现
直接上代码更直观。首先定义回放缓冲区:
from collections import deque import random class ReplayBuffer: def __init__(self, capacity): self.buffer = deque(maxlen=capacity) def push(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): return random.sample(self.buffer, batch_size) def __len__(self): return len(self.buffer)使用时要注意:
- 缓冲区大小建议1e5~1e6
- 采样batch size通常取128或256
- 在收集足够样本(如1k)后再开始训练
4. PyTorch实现完整DQN
4.1 网络架构设计
这里给出一个比官方教程更实用的网络结构,加入了层标准化(LayerNorm):
import torch.nn as nn import torch.nn.functional as F class DQN(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.fc1 = nn.Linear(state_dim, 64) self.ln1 = nn.LayerNorm(64) self.fc2 = nn.Linear(64, 64) self.ln2 = nn.LayerNorm(64) self.out = nn.Linear(64, action_dim) def forward(self, x): x = F.relu(self.ln1(self.fc1(x))) x = F.relu(self.ln2(self.fc2(x))) return self.out(x)4.2 训练流程关键代码
完整训练循环包含这些核心部分:
# 初始化 policy_net = DQN(4, 2).to(device) target_net = DQN(4, 2).to(device) target_net.load_state_dict(policy_net.state_dict()) optimizer = torch.optim.Adam(policy_net.parameters(), lr=1e-3) memory = ReplayBuffer(10000) # 训练循环 for episode in range(1000): state, _ = env.reset() total_reward = 0 for t in count(): # ε-greedy动作选择 if random.random() < epsilon: action = env.action_space.sample() else: with torch.no_grad(): action = policy_net(torch.FloatTensor(state).to(device)).argmax().item() # 环境交互 next_state, reward, done, _, _ = env.step(action) memory.push(state, action, reward, next_state, done) state = next_state total_reward += reward # 网络更新 if len(memory) > 1000: batch = memory.sample(128) # ...省略计算loss和反向传播... if done: break # 更新目标网络 if episode % 10 == 0: target_net.load_state_dict(policy_net.state_dict())5. 实战调试技巧
5.1 超参数设置参考
根据我的调参经验,CartPole任务的推荐参数:
| 参数 | 推荐值 | 作用 |
|---|---|---|
| γ | 0.99 | 折扣因子 |
| ε初始值 | 0.9 | 探索率 |
| ε衰减 | 2000步 | 线性衰减到0.05 |
| 学习率 | 1e-3 | Adam优化器 |
| 批大小 | 128 | 每次更新样本数 |
| 目标网络更新 | 每10episode | 软更新效果更好 |
5.2 常见问题排查
奖励不增长:
- 检查ε值是否过大导致纯随机
- 确认网络梯度在更新(打印loss变化)
训练不稳定:
- 尝试减小学习率
- 增加目标网络更新频率
- 添加梯度裁剪(
nn.utils.clip_grad_norm_)
GPU内存溢出:
- 减小批大小
- 检查是否有张量未释放(用
torch.cuda.empty_cache())
最后分享一个实用技巧:用wandb或TensorBoard记录训练曲线,能大幅提升调试效率。我在实际项目中发现,当平均奖励超过300时,说明模型已经学到有效策略了。