从 Q-Learning 到 DQN

329 阅读3分钟

从 Q-Learning 到 DQN:强化学习经典算法详解与 PyTorch 实现

强化学习是人工智能领域的一个重要分支,而 Q-Learning 和 Deep Q-Networks (DQN) 是其中两个经典的算法。本文将详细介绍它们的原理、区别,并通过 PyTorch 实现一个简单的 DQN 模型来解决 OpenAI Gym 中的 CartPole 问题。


1. Q-Learning:表格型强化学习

基本思想

Q-Learning 是一种基于值函数的强化学习算法,它通过学习一个 Q 值表(Q-Table)来指导智能体的行为。Q 值表记录了在每个状态(State)下,采取每个动作(Action)所能获得的预期累积奖励

核心公式

Q-Learning 的更新公式为:

Q(st,at)Q(st,at)+α[rt+1+γmaxaQ(st+1,a)Q(st,at)]Q(st,at)Q(st,at)+α[rt+1+γamaxQ(st+1,a)Q(st,at)]Q(st,at)←Q(st,at)+α[r_{t+1}+γmax⁡_aQ(s_{t+1},a)−Q(st,at)]Q(st​,at​)←Q(st​,at​)+α[rt+1​+γamax​Q(st+1​,a)−Q(st​,at​)]

其中:

  • Q(st,at)Q(st​,at​):当前状态 stst​ 和动作 atat​ 的 Q 值。
  • αα:学习率。
  • γγ:折扣因子。
  • rt+1rt+1​:即时奖励。
  • max⁡aQ(st+1,a)maxa​Q(st+1​,a):下一状态的最大 Q 值。

优点

  • 简单直观,易于实现。
  • 适用于小规模离散状态和动作空间。

缺点

  • 无法处理高维状态空间。
  • Q 值表随状态和动作空间增长而膨胀。

2. Deep Q-Networks (DQN):深度强化学习

基本思想

DQN 是 Q-Learning 的扩展,使用深度神经网络近似 Q 值函数,从而解决了高维状态空间的问题。DQN 通过经验回放和目标网络提高了训练的稳定性。

核心改进

  1. 神经网络近似 Q 值函数

    Q(s,a;θ)≈Q(s,a)Q(s,a;θ)≈Q(s,a)

  2. 经验回放:存储经验并随机采样,打破数据相关性。

  3. 目标网络:使用独立的网络计算目标 Q 值,减少波动。

核心公式

DQN 的损失函数为:

L(θ)=E[(r+γmax⁡a′Q(s′,a′;θ−)−Q(s,a;θ))2]L(θ)=E[(r+γa′max​Q(s′,a′;θ−)−Q(s,a;θ))2]

优点

  • 能够处理高维状态空间。
  • 通过经验回放和目标网络提高稳定性。

缺点

  • 训练过程需要大量计算资源。
  • 对超参数敏感。

3. Q-Learning 和 DQN 的区别

特性Q-LearningDQN
Q 值表示Q 值表神经网络
状态空间离散高维/连续
动作空间离散离散(可扩展至连续)
存储需求
训练稳定性较稳定更稳定
计算复杂度
适用场景小规模问题大规模复杂问题

4. PyTorch 实现 DQN

以下是一个使用 PyTorch 实现 DQN 解决 CartPole 问题的示例代码:

python

复制

import gym
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import deque
import random

# 定义神经网络
class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 定义 DQN 智能体
class DQNAgent:
    def __init__(self, state_size, action_size):
        self.state_size = state_size
        self.action_size = action_size
        self.memory = deque(maxlen=2000)
        self.gamma = 0.95  # 折扣因子
        self.epsilon = 1.0  # 探索率
        self.epsilon_min = 0.01
        self.epsilon_decay = 0.995
        self.learning_rate = 0.001
        self.model = DQN(state_size, action_size)
        self.optimizer = optim.Adam(self.model.parameters(), lr=self.learning_rate)

    def remember(self, state, action, reward, next_state, done):
        self.memory.append((state, action, reward, next_state, done))

    def act(self, state):
        if np.random.rand() <= self.epsilon:
            return random.randrange(self.action_size)
        state = torch.FloatTensor(state)
        act_values = self.model(state)
        return torch.argmax(act_values).item()

    def replay(self, batch_size):
        if len(self.memory) < batch_size:
            return
        minibatch = random.sample(self.memory, batch_size)
        states = torch.FloatTensor([t[0] for t in minibatch])
        actions = torch.LongTensor([t[1] for t in minibatch])
        rewards = torch.FloatTensor([t[2] for t in minibatch])
        next_states = torch.FloatTensor([t[3] for t in minibatch])
        dones = torch.FloatTensor([t[4] for t in minibatch])

        current_q = self.model(states).gather(1, actions.unsqueeze(1))
        next_q = self.model(next_states).detach().max(1)[0]
        target_q = rewards + (1 - dones) * self.gamma * next_q

        loss = nn.MSELoss()(current_q.squeeze(), target_q)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

        if self.epsilon > self.epsilon_min:
            self.epsilon *= self.epsilon_decay

# 训练 DQN 智能体
env = gym.make('CartPole-v1')
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
agent = DQNAgent(state_size, action_size)
batch_size = 32
episodes = 500

for e in range(episodes):
    state = env.reset()
    state = np.reshape(state, [1, state_size])
    total_reward = 0

    for time in range(500):
        action = agent.act(state)
        next_state, reward, done, _ = env.step(action)
        next_state = np.reshape(next_state, [1, state_size])
        agent.remember(state, action, reward, next_state, done)
        state = next_state
        total_reward += reward

        if done:
            print(f"Episode: {e}/{episodes}, Score: {total_reward}, Epsilon: {agent.epsilon:.2f}")
            break

    if len(agent.memory) > batch_size:
        agent.replay(batch_size)

5. 总结

  • Q-Learning 是一种简单而有效的表格型方法,适用于小规模离散问题。
  • DQN 通过深度神经网络解决了高维状态空间的问题,适用于更复杂的任务。
  • 本文提供了 DQN 的 PyTorch 实现代码,帮助你快速上手强化学习实践。