当前位置: 首页 > news >正文

《Python实战进阶》No37: 强化学习入门:Q-Learning 与 DQN

No37: 强化学习入门:Q-Learning 与 DQN


摘要

强化学习是一种强大的机器学习范式,适用于解决决策和控制任务。本集将从基础概念开始,逐步深入讲解 Q-Learning 和深度 Q 网络(DQN)的原理,并通过两个实战案例(迷宫问题和 Atari 游戏)展示如何使用这些算法解决问题,本文章中代码都已经通过实际运行验证通过,具体代码环境和关键依赖版本也提供了清单。
**本文有两篇加餐,对Q-Learnin算法特别介绍了算法可视化和算法原理分析,详见:《Python实战进阶》No37: 强化学习入门:Q-Learning 与 DQN-加餐版1 Q-Learning算法可视化

Q-Learning算法训练可视化

在这里插入图片描述


核心概念和知识点
  1. 强化学习的基本概念:状态、动作、奖励

    • 状态(State, S): 当前环境的状态,表示智能体所处的情况。
    • 动作(Action, A): 智能体在当前状态下可以采取的行为。
    • 奖励(Reward, R): 环境对智能体行为的反馈,用于评估行为的好坏。
    • 价值函数(Value Function): 表示从某个状态出发,未来可能获得的累积奖励。
    • 策略(Policy): 智能体选择动作的规则或概率分布。
  2. Q-Learning 的算法原理

    • Q-Learning 是一种基于值迭代的无模型强化学习算法。
    • 核心思想是维护一个 Q 值表,记录每个状态-动作对的期望累积奖励。
    • 更新公式:
      [
      Q(s, a) \leftarrow Q(s, a) + \alpha \cdot (R + \gamma \cdot \max_{a’} Q(s’, a’) - Q(s, a))
      ]
      其中:
      • ( Q(s, a) ): 状态 ( s ) 下采取动作 ( a ) 的 Q 值。
      • ( \alpha ): 学习率,控制更新幅度。
      • ( R ): 当前奖励。
      • ( \gamma ): 折扣因子,决定对未来奖励的重视程度。
      • ( s’ ): 下一状态。
      • ( a’ ): 下一状态下的最优动作。
  3. 深度 Q 网络(DQN)的架构与实现

    • DQN 是一种结合深度学习的强化学习算法,用于处理高维输入(如图像)。
    • 核心思想是用神经网络近似 Q 值函数,而不是显式地存储 Q 表。
    • 主要组件:
      • 经验回放(Experience Replay): 随机采样历史数据进行训练,避免过拟合。
      • 目标网络(Target Network): 稳定目标 Q 值,减少训练过程中的不稳定性。
    • 损失函数:
      [
      L = \mathbb{E}{s, a, r, s’}\left[(r + \gamma \cdot \max{a’} Q(s’, a’; \theta^-) - Q(s, a; \theta))^2\right]
      ]
      其中:
      • ( \theta ): 主网络参数。
      • ( \theta^- ): 目标网络参数,定期从主网络同步。

实战案例

依赖文件 requirements.txt / Python虚拟环境版本:3.10.10

 numpy==2.1.2 
案例 1:使用 Q-Learning 解决迷宫问题

迷宫问题是强化学习的经典应用场景,目标是让智能体找到从起点到终点的最短路径。

步骤 1:定义环境

我们创建一个简单的二维迷宫环境,其中:

  • 起点为 S,终点为 G
  • 墙壁用 # 表示,空格用 . 表示。
  • 动作包括上、下、左、右四个方向。
import numpy as np

class MazeEnv:
    def __init__(self):
        self.maze = [
            ['.', '.', '.', '#', '.'],
            ['.', '#', '.', '.', '.'],
            ['.', '#', '.', '#', '.'],
            ['.', '.', '.', '#', '.'],
            ['.', '#', 'G', '#', '.']
        ]
        self.maze = np.array(self.maze)
        self.start = (0, 0)
        self.goal = (4, 2)
        self.current_state = self.start
        self.actions = [(0, 1), (0, -1), (1, 0), (-1, 0)]  # 右、左、下、上

    def reset(self):
        self.current_state = self.start
        return self.current_state

    def step(self, action):
        next_state = (self.current_state[0] + action[0], self.current_state[1] + action[1])
        if (
            next_state[0] < 0 or next_state[0] >= self.maze.shape[0] or
            next_state[1] < 0 or next_state[1] >= self.maze.shape[1] or
            self.maze[next_state] == '#'
        ):
            next_state = self.current_state  # 如果撞墙,保持原位置
        reward = -1  # 每步移动的默认奖励
        done = False
        if next_state == self.goal:
            reward = 10  # 到达终点的奖励
            done = True
        self.current_state = next_state
        return next_state, reward, done
步骤 2:实现 Q-Learning

我们使用 Q-Learning 算法训练智能体在迷宫中找到最优路径。

import random

class QLearningAgent:
    def __init__(self, env, learning_rate=0.1, discount_factor=0.9, epsilon=0.1):
        self.env = env
        self.q_table = {}
        self.learning_rate = learning_rate
        self.discount_factor = discount_factor
        self.epsilon = epsilon

    def get_action(self, state):
        if random.uniform(0, 1) < self.epsilon:
            return random.choice(self.env.actions)  # 探索
        else:
            q_values = [self.get_q_value(state, action) for action in self.env.actions]
            return self.env.actions[np.argmax(q_values)]  # 贪婪策略

    def get_q_value(self, state, action):
        key = (state, action)
        return self.q_table.get(key, 0.0)

    def update_q_table(self, state, action, reward, next_state):
        old_q = self.get_q_value(state, action)
        max_next_q = max([self.get_q_value(next_state, a) for a in self.env.actions])
        new_q = old_q + self.learning_rate * (reward + self.discount_factor * max_next_q - old_q)
        self.q_table[(state, action)] = new_q

    def train(self, num_episodes=1000):
        for episode in range(num_episodes):
            state = self.env.reset()
            done = False
            while not done:
                action = self.get_action(state)
                next_state, reward, done = self.env.step(action)
                self.update_q_table(state, action, reward, next_state)
                state = next_state
            if episode % 100 == 0:
                print(f"Episode {episode}: Training...")

    def test(self):
        state = self.env.reset()
        path = [state]
        done = False
        while not done:
            action = self.get_action(state)
            state, _, done = self.env.step(action)
            path.append(state)
        print("Path:", path)

# 创建环境和智能体
env = MazeEnv()
agent = QLearningAgent(env)
agent.train()
agent.test()

输出:

Episode 0: Training...
Episode 100: Training...
Episode 200: Training...
...
Path: [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2)]
案例 2:使用 DQN 玩 Atari 游戏

Atari 游戏是强化学习的经典应用之一,我们将使用 DQN 算法训练智能体玩一款简单的 Atari 游戏(如 CartPole)。

步骤 1:安装依赖

requirements.txt / Python虚拟环境版本:3.10.10

cloudpickle==3.1.1
filelock==3.18.0
fsspec==2025.3.0
gym==0.26.2
gym-notices==0.0.8
Jinja2==3.1.6
MarkupSafe==3.0.2
mpmath==1.3.0
networkx==3.4.2
numpy==1.24.3
sympy==1.13.1
torch==2.6.0
typing_extensions==4.13.0
pip install -r requirements.txt
步骤 2:加载环境

我们使用 OpenAI Gym 提供的 `CartPole-v1 环境:

import gym
import numpy as np

env = gym.make('CartPole-v1')
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
print(f"State size: {state_size}, Action size: {action_size}")

输出:

State size: 4, Action size: 2
步骤 3:实现 DQN

我们使用 PyTorch 实现 DQN 算法。

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

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 = self._build_model()

    def _build_model(self):
        model = nn.Sequential(
            nn.Linear(self.state_size, 24),
            nn.ReLU(),
            nn.Linear(24, 24),
            nn.ReLU(),
            nn.Linear(24, self.action_size)
        )
        return model

    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.tensor(state, dtype=torch.float32).unsqueeze(0)
        with torch.no_grad():
            q_values = self.model(state)
        return torch.argmax(q_values).item()

    def replay(self, batch_size):
        if len(self.memory) < batch_size:
            return
        minibatch = random.sample(self.memory, batch_size)
        states, actions, rewards, next_states, dones = zip(*minibatch)

        states = torch.tensor(np.array(states), dtype=torch.float32)
        actions = torch.tensor(actions, dtype=torch.int64).unsqueeze(-1)
        rewards = torch.tensor(rewards, dtype=torch.float32).unsqueeze(-1)
        next_states = torch.tensor(np.array(next_states), dtype=torch.float32)
        dones = torch.tensor(dones, dtype=torch.float32).unsqueeze(-1)

        q_values = self.model(states).gather(1, actions)
        next_q_values = self.model(next_states).max(1)[0].unsqueeze(-1)
        target_q_values = rewards + (self.gamma * next_q_values * (1 - dones))

        loss = nn.MSELoss()(q_values, target_q_values)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

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

    def train(self, episodes=1000, batch_size=32):
        self.optimizer = optim.Adam(self.model.parameters(), lr=self.learning_rate)
        for e in range(episodes):
            state = env.reset()[0]  # 注意:reset() 返回值也发生了变化
            total_reward = 0
            done = False
            while not done:
                action = self.act(state)
                next_state, reward, terminated, truncated, _ = env.step(action)
                done = terminated or truncated  # 合并 terminated 和 truncated
                self.remember(state, action, reward, next_state, done)
                state = next_state
                total_reward += reward
            self.replay(batch_size)
            print(f"Episode {e+1}/{episodes}, Total Reward: {total_reward}")

# 创建环境和智能体
env = gym.make('CartPole-v1')
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
print(f"State size: {state_size}, Action size: {action_size}")

agent = DQNAgent(state_size, action_size)
agent.train()

输出:

Episode 1/1000, Total Reward: 10.0
Episode 2/1000, Total Reward: 15.0
...
Episode 1000/1000, Total Reward: 352.0

总结

强化学习是一种强大的学习范式,适用于解决决策和控制任务。Q-Learning 是一种经典的强化学习算法,适用于离散状态和动作空间;而 DQN 则通过深度学习扩展了其能力,能够处理高维输入(如图像)。通过迷宫问题和 Atari 游戏的实战案例,我们展示了如何使用这些算法解决实际问题。


扩展思考
  1. 探讨强化学习在机器人领域的应用

    • 强化学习在机器人领域有广泛的应用,例如自主导航、抓取物体、人机协作等。
    • 通过模拟器(如 MuJoCo 或 Isaac Sim)生成大量训练数据,强化学习可以显著提升机器人的决策能力。
  2. 如何结合多智能体强化学习解决复杂问题?

    • 多智能体强化学习(MARL)适用于需要多个智能体协同工作的场景,如交通管理、多人游戏等。
    • 关键挑战包括通信机制、竞争与合作的平衡以及可扩展性。

代码总结

本集通过两个实战案例展示了强化学习的基础知识和实现方法。迷宫问题中,我们使用 Q-Learning 训练智能体找到最优路径;Atari 游戏中,我们使用 DQN 训练智能体完成控制任务。这些案例不仅涵盖了强化学习的核心概念,还突出了其实战性和实用性。

http://www.dtcms.com/a/95353.html

相关文章:

  • 2025年人工智能产业TOP10有哪些省份?人工智能产业发展前景如何?
  • HarmonyOS NEXT 鸿蒙中手写和使用第三方仓库封装Logger打印工具
  • 04 单目标定实战示例
  • MySQL 用户权限与安全管理
  • 5G网络中CPE和ACS
  • 优雅的开始一个Python项目
  • Windows 我的世界 Minecraft 服务器搭建,Fabric 模组搭建教程(内网穿透)
  • 2025年渗透测试面试题总结-某快手-安全工程师(题目+回答)
  • 【Git 暂存操作与升级应用指南】
  • 华为GaussDB数据库的手动备份与还原操作介绍
  • 将 Markdown 表格结构转换为Excel 文件
  • ETCD --- ​租约(Lease)​详解
  • ICRA-2025 | 从人类视角到机器人视角的具身导航!连续环境中基于地面视角的视觉语言导航
  • 基于核选择融合注意力机制TCN-MTLATTENTION-MAMBA模型(Python\matlab代码)
  • Spring Framework启动机制深度解析
  • 大模型重点5【Agent构建】
  • 华为hcia——Datacom实验指南——配置IPv4静态路由,默认路由和浮动静态路由
  • java开发环境本地全套
  • 从vue2过渡到vue3
  • 2025年成都市双流区农业科技试验示范基地建设方案申报条件材料和补贴程序、时间安排
  • CTF类题目复现总结-[MRCTF2020]不眠之夜 1
  • CMake 构建的Qt 项目中的构建套件的配置
  • OpenCV图像拼接(4)构建图像的拉普拉斯金字塔 (Laplacian Pyramid)
  • 【蓝桥杯】单片机设计与开发,中断系统,外部中断(下)
  • 【Linux加餐-验证UDP:TCP】-windows作为client访问Linux
  • UDP视频传输中的丢包和播放花屏处理方法
  • 11:00开始面试,11:08就出来了,问的问题有点变态。。。
  • SpringBoot集成腾讯云OCR实现身份证识别
  • 企业网站源码HTML成品网站与网页代码模板指南
  • SpringBoot报错解决方案