干翻全岛蛙蛙 发表于 2024-8-29 17:58:47

深度学习学习履历——强化学习(rl)

强化学习

强化学习(Reinforcement Learning, RL)是一种机器学习方法,重要用于让智能体(agent)通过与环境的互动,逐步学习如何在不恻隐况下采取最佳办法,以最大化其获得的累积回报。与监督学习和无监督学习不同,强化学习并不依靠于已标注的数据集,而是通过智能体在环境中的探索和试错来学习最优策略。
强化学习的重要特点:


[*] 基于试错学习:强化学习中的智能体通过与环境的互动,不断实行不同的办法,并根据办法结果(即从环境中获得的回报)来调整未来的决议。这个过程通常被称为“试错”学习。
[*] 累积回报:强化学习的目标是找到一种策略,使得智能体在长时间内可以或许获得最大的累积回报。智能体不但要关注即时的回报,还需要考虑未来的回报,如何权衡当前的办法与未来的回报是强化学习中的一个关键题目。
[*] 反馈信号(奖励):智能体从环境中吸收到的反馈通常是一个奖励信号,用于指引它的举动。奖励信号可以是正的,也可以是负的,正如一个人可以因做对了事而获得奖励,也可以因犯错而受到惩罚。
[*] 探索与利用的平衡:在强化学习中,智能体需要在探索新的办法(探索)与利用已知信息(利用)之间找到平衡。过多的探索大概导致时间浪费,而过多的利用大概导致智能体陷入局部最优解。
[*] 状态与办法空间:强化学习通常在状态空间(state space)和办法空间(action space)中举行。状态空间表示环境的全部大概状态,办法空间表示智能体在每个状态下可以采取的办法。如何有效地在这些空间中找到最优策略是强化学习的核心挑战之一。
[*] 策略与价值函数:强化学习中的策略(policy)是指智能体在每个状态下选择办法的规则,而价值函数(value function)则评估了在某一状态下采取某一办法所期望获得的累积回报。
实际应用

强化学习在机器人控制、游戏AI、自动驾驶、智能交易系统等领域有广泛的应用。比方,AlphaGo利用了强化学习来学习如何下围棋,并成功降服了人类冠军。
强化学习实践案例——贪吃蛇游戏

让我们从零开始,通过一个简朴而经典的游戏——贪吃蛇(Snake Game),来逐步了解强化学习的基本概念和如何利用PyTorch实现它。
第一步:明白强化学习的基本概念

在强化学习中,智能体(agent) 在一个 环境(environment) 中不断地做出决议,这些决议被称为 办法(actions)。环境会根据智能体的办法给予反馈,这个反馈通常是一个 奖励(reward)。智能体的目标是通过选择适当的办法来最大化其获得的累积奖励。
在贪吃蛇游戏中:


[*]环境:游戏的棋盘和蛇的状态(位置、方向等)。
[*]智能体:控制蛇移动的程序。
[*]办法:上、下、左、右移动。
[*]奖励:吃到食品时获得正奖励;撞墙或撞到自己时获得负奖励。
第二步:环境搭建

首先,我们需要创建一个简朴的贪吃蛇游戏环境。这个环境将包罗蛇的状态(位置、方向等)以及食品的位置。
import numpy as np
import random
import torch

class SnakeGame:
    def __init__(self, grid_size=10):
      self.grid_size = grid_size
      self.reset()

    def reset(self):
      self.snake = [(self.grid_size // 2, self.grid_size // 2)]
      self.direction = (0, 1)# Start by moving right
      self.food = self._place_food()
      self.done = False
      return self._get_observation()

    def _place_food(self):
      while True:
            food = (random.randint(0, self.grid_size - 1), random.randint(0, self.grid_size - 1))
            if food not in self.snake:
                return food

    def _get_observation(self):
      obs = np.zeros((self.grid_size, self.grid_size))
      for x, y in self.snake:
            obs = 1
      obs, self.food] = 2
      return obs

    def step(self, action):
      if action == 0:# Up
            self.direction = (-1, 0)
      elif action == 1:# Down
            self.direction = (1, 0)
      elif action == 2:# Left
            self.direction = (0, -1)
      elif action == 3:# Right
            self.direction = (0, 1)

      head = (self.snake + self.direction, self.snake + self.direction)
      if (head in self.snake) or (head < 0 or head >= self.grid_size) or (head < 0 or head >= self.grid_size):
            self.done = True
            return self._get_observation(), -10, self.done# Collision: negative reward

      self.snake = + self.snake[:-1]

      if head == self.food:
            self.snake.append(self.snake[-1])# Grow
            self.food = self._place_food()
            return self._get_observation(), 10, self.done# Food eaten: positive reward

      return self._get_observation(), 0, self.done# Neutral step
第三步:界说强化学习的智能体

我们将利用一个简朴的神经网络作为我们的智能体,它将输入贪吃蛇游戏的状态,输出每个大概的动作的“价值”(即采取该办法的预期回报)。在强化学习中,我们通常利用 Q-learning 算法来练习这个网络。
import torch.nn as nn
import torch.optim as optim

class DQN(nn.Module):
    def __init__(self, grid_size, num_actions):
      super(DQN, self).__init__()
      self.fc1 = nn.Linear(grid_size * grid_size, 128)# 第一层全连接层,输入大小为网格大小平方
      self.fc2 = nn.Linear(128, 64)# 第二层全连接层
      self.fc3 = nn.Linear(64, num_actions)# 输出层,输出动作的Q值

    def forward(self, x):
      batch_size = x.size(0)# 获取批次大小
      x = x.view(batch_size, -1)# 将输入展平为 (batch_size, grid_size * grid_size)
      x = torch.relu(self.fc1(x))# 经过第一层全连接并ReLU激活
      x = torch.relu(self.fc2(x))# 经过第二层全连接并ReLU激活
      return self.fc3(x)# 输出动作的Q值


    # 计算输入张量的展平后的特征数目
    def num_flat_features(self, x):
      size = x.size()# 除去批次维度,计算特征维度的大小
      num_features = 1
      for s in size:
            num_features *= s
      return num_features

# Initialize DQN
grid_size = 10
num_actions = 4# Up, Down, Left, Right
dqn = DQN(grid_size, num_actions)
optimizer = optim.Adam(dqn.parameters(), lr=0.001)
loss_fn = nn.MSELoss()
第四步:练习智能体

在这个步骤中,我们将利用贪吃蛇的游戏状态作为输入,通过神经网络推测各个办法的价值,并选择价值最高的办法。然后,我们利用Q-learning来更新神经网络的参数。
def train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.9):
    for episode in range(episodes):
      state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)# 添加批次维度
      total_reward = 0
      while True:
            q_values = dqn(state)# 预测当前状态的Q值
            action = torch.argmax(q_values).item()# 选择Q值最大的动作

            next_state, reward, done = game.step(action)# 执行动作并获得下一状态和奖励
            next_state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)# 将下一状态转换为张量并添加批次维度

            with torch.no_grad():
                max_next_q_values = torch.max(dqn(next_state))# 计算下一状态的最大Q值
                target = reward + gamma * max_next_q_values * (1 - int(done))# 计算目标Q值

            loss = loss_fn(q_values, target)# 计算损失
            optimizer.zero_grad()# 梯度清零
            loss.backward()# 反向传播
            optimizer.step()# 更新网络参数

            state = next_state# 更新当前状态
            total_reward += reward

            if done:# 如果游戏结束,退出循环
                break

      print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward}")# 输出当前回合的总奖励

# Train the agent
game = SnakeGame(grid_size)
train(game, dqn, optimizer, loss_fn, episodes=1000)
第五步:评估和可视化

练习完智能体后,我们可以让它在游戏中实行跑几局,并观察它的表现。以下代码展示了如何评估智能体并可视化它的表现。
import matplotlib.pyplot as plt

def evaluate(game, dqn, episodes=10):
    for episode in range(episodes):
      state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)# 添加批次维度
      total_reward = 0
      steps = 0

      plt.figure()# 创建新的图像窗口

      while True:
            q_values = dqn(state)# 预测当前状态的Q值
            action = torch.argmax(q_values).item()# 选择Q值最大的动作

            next_state, reward, done = game.step(action)# 执行动作并获得下一状态和奖励
            state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)# 将下一状态转换为张量并添加批次维度
            total_reward += reward
            steps += 1

            # Visualization (Optional)
            plt.imshow(state.squeeze(0).numpy())# 去掉批次维度进行显示
            plt.axis('off')# 隐藏坐标轴
            plt.pause(0.1)# 短暂停留以更新视图

            if done:
                print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward} - Steps: {steps}")
                break
      
      plt.close()# 关闭图像窗口
最终代码汇总

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

# 定义贪吃蛇游戏环境
class SnakeGame:
    def __init__(self, grid_size=10):
      self.grid_size = grid_size
      self.reset()

    # 重置游戏状态,初始化蛇的位置和食物的位置
    def reset(self):
      self.snake = [(self.grid_size // 2, self.grid_size // 2)]# 蛇的初始位置在网格的中央
      self.direction = (0, 1)# 蛇的初始移动方向为向右
      self.food = self._place_food()# 随机放置食物
      self.done = False
      return self._get_observation()

    # 随机生成一个食物位置,确保不与蛇的身体重叠
    def _place_food(self):
      while True:
            food = (random.randint(0, self.grid_size - 1), random.randint(0, self.grid_size - 1))
            if food not in self.snake:
                return food

    # 获取当前游戏状态,返回一个网格表示的观察值
    def _get_observation(self):
      obs = np.zeros((self.grid_size, self.grid_size))# 初始化一个全0的网格
      for x, y in self.snake:
            obs = 1# 用1表示蛇的身体
      obs, self.food] = 2# 用2表示食物的位置
      return obs

    # 游戏的主要逻辑:根据动作更新蛇的状态
    def step(self, action):
      if action == 0:# 上
            self.direction = (-1, 0)
      elif action == 1:# 下
            self.direction = (1, 0)
      elif action == 2:# 左
            self.direction = (0, -1)
      elif action == 3:# 右
            self.direction = (0, 1)

      # 更新蛇头的位置
      head = (self.snake + self.direction, self.snake + self.direction)
      
      # 如果蛇撞到自己或墙壁,游戏结束
      if (head in self.snake) or (head < 0 or head >= self.grid_size) or (head < 0 or head >= self.grid_size):
            self.done = True
            return self._get_observation(), -10, self.done# 碰撞:给予负奖励

      # 更新蛇的身体
      self.snake = + self.snake[:-1]

      # 如果蛇吃到食物
      if head == self.food:
            self.snake.append(self.snake[-1])# 蛇的身体增长
            self.food = self._place_food()# 重新放置食物
            return self._get_observation(), 10, self.done# 吃到食物:给予正奖励

      return self._get_observation(), 0, self.done# 普通移动:奖励为0

# 定义DQN(深度Q网络)模型
class DQN(nn.Module):
    def __init__(self, grid_size, num_actions):
      super(DQN, self).__init__()
      self.fc1 = nn.Linear(grid_size * grid_size, 128)# 第一层全连接层,输入大小为网格大小平方
      self.fc2 = nn.Linear(128, 64)# 第二层全连接层
      self.fc3 = nn.Linear(64, num_actions)# 输出层,输出动作的Q值

    def forward(self, x):
      batch_size = x.size(0)# 获取批次大小
      x = x.view(batch_size, -1)# 将输入展平为 (batch_size, grid_size * grid_size)
      x = torch.relu(self.fc1(x))# 经过第一层全连接并ReLU激活
      x = torch.relu(self.fc2(x))# 经过第二层全连接并ReLU激活
      return self.fc3(x)# 输出动作的Q值


    # 计算输入张量的展平后的特征数目
    def num_flat_features(self, x):
      size = x.size()# 除去批次维度,计算特征维度的大小
      num_features = 1
      for s in size:
            num_features *= s
      return num_features

# 初始化DQN
grid_size = 10
num_actions = 4# 上、下、左、右四个动作
dqn = DQN(grid_size, num_actions)
optimizer = optim.Adam(dqn.parameters(), lr=0.00001)# 使用Adam优化器
loss_fn = nn.MSELoss()# 使用均方误差损失函数

# 训练智能体
def train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.9):
    for episode in range(episodes):
      state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)# 添加批次维度
      total_reward = 0
      while True:
            q_values = dqn(state)# 预测当前状态的Q值
            action = torch.argmax(q_values).item()# 选择Q值最大的动作

            next_state, reward, done = game.step(action)# 执行动作并获得下一状态和奖励
            next_state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)# 将下一状态转换为张量并添加批次维度

            with torch.no_grad():
                max_next_q_values = torch.max(dqn(next_state))# 计算下一状态的最大Q值
                target = reward + gamma * max_next_q_values * (1 - int(done))# 计算目标Q值

            loss = loss_fn(q_values, target)# 计算损失
            optimizer.zero_grad()# 梯度清零
            loss.backward()# 反向传播
            optimizer.step()# 更新网络参数

            state = next_state# 更新当前状态
            total_reward += reward

            if done:# 如果游戏结束,退出循环
                break

      print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward}")# 输出当前回合的总奖励

# 训练智能体
game = SnakeGame(grid_size)
train(game, dqn, optimizer, loss_fn, episodes=10000)# 开始训练


# Step 5: Evaluate the Agent
def evaluate(game, dqn, episodes=10):
    for episode in range(episodes):
      state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)# 添加批次维度
      total_reward = 0
      steps = 0

      plt.figure()# 创建新的图像窗口

      while True:
            q_values = dqn(state)# 预测当前状态的Q值
            action = torch.argmax(q_values).item()# 选择Q值最大的动作

            next_state, reward, done = game.step(action)# 执行动作并获得下一状态和奖励
            state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)# 将下一状态转换为张量并添加批次维度
            total_reward += reward
            steps += 1

            # Visualization (Optional)
            plt.imshow(state.squeeze(0).numpy())# 去掉批次维度进行显示
            plt.axis('off')# 隐藏坐标轴
            plt.pause(0.1)# 短暂停留以更新视图

            if done:
                print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward} - Steps: {steps}")
                break
      
      plt.close()# 关闭图像窗口



# Evaluate the trained agent
evaluate(game, dqn)
通过这个贪吃蛇的例子,我们逐步了解了强化学习的概念,并通过利用PyTorch实现了一个简朴的强化学习智能体。这种方法不但实用于贪吃蛇游戏,也可以扩展到更复杂的环境和任务中。
如何进步练习效果(调整参数)

在强化学习过程中,有多个参数可以举行调整,以进步练习效果和智能体的表现。以下是一些关键参数和调整建议:
1. 学习率(Learning Rate)



[*]界说:学习率决定了模型在每次参数更新时的步长。太高的学习率大概导致模型不收敛,而太低的学习率大概使练习过程非常缓慢。
[*]调整建议:通常在0.001到0.00001之间举行调整。可以通过逐步减小学习率(比方利用学习率衰减策略)来进步练习的稳定性。
optimizer = optim.Adam(dqn.parameters(), lr=0.0005)# 例如,减小学习率
2. 折扣因子(Discount Factor, γ)



[*]界说:折扣因子决定了智能体在优化过程中对未来奖励的器重程度。γ 值靠近1时,智能体会更加注重长远的回报;γ 值靠近0时,智能体则更关注短期的回报。
[*]调整建议:γ 通常设置在0.9到0.99之间。对于较短的游戏或回报立即显现的任务,可以选择较低的 γ 值。
train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.95)# 例如,稍微降低 γ
3. 探索与利用的平衡(Exploration vs Exploitation)



[*]界说:在强化学习中,智能体需要在探索新策略和利用已知策略之间找到平衡。常用的探索策略有 ε-greedy,其中 ε 是智能体随机选择办法的概率。
[*]调整建议:可以设置一个较高的初始 ε 值(比方0.9),然后逐步减小,淘汰随机探索的次数。也可以引入更多先辈的策略,如 UCB 或 Boltzmann 探索。
epsilon = 0.9# 初始探索率
decay_rate = 0.995# 衰减率

for episode in range(episodes):
    epsilon = max(epsilon * decay_rate, 0.01)# 确保 epsilon 最小为 0.01
    # 其余代码保持不变
4. 履历回放(Experience Replay)



[*]界说:履历回放存储智能体在环境中经历的状态、办法、奖励和下一状态。练习时从这些存储的履历中随机抽取一个批次举行学习,从而进步练习的效率和稳定性。
[*]调整建议:增加履历回放的批次巨细或扩展履历回放池的容量,可以帮助智能体更有效地学习。
from collections import deque

replay_memory = deque(maxlen=10000)# 扩展经验回放池的容量

def train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.99, batch_size=64):
    for episode in range(episodes):
      state = torch.tensor(game.reset(), dtype=torch.float32)
      total_reward = 0
      while True:
            if random.random() < epsilon:
                action = random.randint(0, num_actions - 1)
            else:
                q_values = dqn(state)
                action = torch.argmax(q_values).item()

            next_state, reward, done = game.step(action)
            next_state = torch.tensor(next_state, dtype=torch.float32)
            replay_memory.append((state, action, reward, next_state, done))

            # 开始经验回放训练
            if len(replay_memory) > batch_size:
                minibatch = random.sample(replay_memory, batch_size)
                for s, a, r, ns, d in minibatch:
                  q_values = dqn(s)
                  with torch.no_grad():
                        max_next_q_values = torch.max(dqn(ns))
                        target = r + gamma * max_next_q_values * (1 - int(d))
                  loss = loss_fn(q_values, target)
                  optimizer.zero_grad()
                  loss.backward()
                  optimizer.step()

            state = next_state
            total_reward += reward

            if done:
                break
      print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward}")
5. 练习轮数(Number of Episodes)



[*]界说:练习轮数决定了智能体在环境中练习的总次数。
[*]调整建议:增加练习轮数通常会进步模型的表现,特别是在练习的早期阶段。如果练习过程过长,大概会导致过拟合题目,因此要留意在增加练习轮数的同时监控模型性能。
train(game, dqn, optimizer, loss_fn, episodes=5000)# 增加训练轮数
6. 奖励函数(Reward Function)



[*]界说:奖励函数是强化学习中非常关键的一部分,它直接影响到智能体的举动。不同的奖励设计会引导智能体做出不同的决议。
[*]调整建议:可以通过修改奖励函数来更加正确地引导智能体的举动。比方,给予更多的奖励当蛇靠近食品,大概淘汰每一步办法的惩罚,从而激励智能体更积极地去靠近食品。
def step(self, action):
    if action == 0:# Up
      self.direction = (-1, 0)
    elif action == 1:# Down
      self.direction = (1, 0)
    elif action == 2:# Left
      self.direction = (0, -1)
    elif action == 3:# Right
      self.direction = (0, 1)

    head = (self.snake + self.direction, self.snake + self.direction)
    if (head in self.snake) or (head < 0 or head >= self.grid_size) or (head < 0 or head >= self.grid_size):
      self.done = True
      return self._get_observation(), -10, self.done# Collision: negative reward

    self.snake = + self.snake[:-1]

    if head == self.food:
      self.snake.append(self.snake[-1])# Grow
      self.food = self._place_food()
      return self._get_observation(), 10, self.done# Food eaten: positive reward

    # 距离食物近一步,给出微小奖励
    dist = abs(head - self.food) + abs(head - self.food)
    reward = 1.0 / (dist + 1)# 距离越近,奖励越大

    return self._get_observation(), reward, self.done# Neutral step
通过这些参数的调整,你可以进步练习的效果和智能体的表现。每个参数的调整对练习结果都有大概产生明显的影响,因此建议在调整时逐步举行,并仔细观察练习的变化环境。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 深度学习学习履历——强化学习(rl)