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

深度学习学习经验——强化学习(rl)

强化学习

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

强化学习的主要特点:

  1. 基于试错学习:强化学习中的智能体通过与环境的互动,不断尝试不同的行动,并根据行动结果(即从环境中获得的回报)来调整未来的决策。这个过程通常被称为“试错”学习。

  2. 累积回报:强化学习的目标是找到一种策略,使得智能体在长时间内能够获得最大的累积回报。智能体不仅要关注即时的回报,还需要考虑未来的回报,如何权衡当前的行动与未来的回报是强化学习中的一个关键问题。

  3. 反馈信号(奖励):智能体从环境中接收到的反馈通常是一个奖励信号,用于指引它的行为。奖励信号可以是正的,也可以是负的,正如一个人可以因做对了事而获得奖励,也可以因犯错而受到惩罚。

  4. 探索与利用的平衡:在强化学习中,智能体需要在探索新的行动(探索)与利用已知信息(利用)之间找到平衡。过多的探索可能导致时间浪费,而过多的利用可能导致智能体陷入局部最优解。

  5. 状态与行动空间:强化学习通常在状态空间(state space)和行动空间(action space)中进行。状态空间表示环境的所有可能状态,行动空间表示智能体在每个状态下可以采取的行动。如何有效地在这些空间中找到最优策略是强化学习的核心挑战之一。

  6. 策略与价值函数:强化学习中的策略(policy)是指智能体在每个状态下选择行动的规则,而价值函数(value function)则评估了在某一状态下采取某一行动所期望获得的累积回报。

实际应用

强化学习在机器人控制、游戏AI、自动驾驶、智能交易系统等领域有广泛的应用。例如,AlphaGo使用了强化学习来学习如何下围棋,并成功战胜了人类冠军。

强化学习实践案例——贪吃蛇游戏

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

第一步:理解强化学习的基本概念

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

在贪吃蛇游戏中:

  • 环境:游戏的棋盘和蛇的状态(位置、方向等)。
  • 智能体:控制蛇移动的程序。
  • 行动:上、下、左、右移动。
  • 奖励:吃到食物时获得正奖励;撞墙或撞到自己时获得负奖励。

第二步:环境搭建

首先,我们需要创建一个简单的贪吃蛇游戏环境。这个环境将包含蛇的状态(位置、方向等)以及食物的位置。

import numpy as np
import random
import torchclass SnakeGame:def __init__(self, grid_size=10):self.grid_size = grid_sizeself.reset()def reset(self):self.snake = [(self.grid_size // 2, self.grid_size // 2)]self.direction = (0, 1)  # Start by moving rightself.food = self._place_food()self.done = Falsereturn 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 fooddef _get_observation(self):obs = np.zeros((self.grid_size, self.grid_size))for x, y in self.snake:obs[x, y] = 1obs[self.food[0], self.food[1]] = 2return obsdef step(self, action):if action == 0:  # Upself.direction = (-1, 0)elif action == 1:  # Downself.direction = (1, 0)elif action == 2:  # Leftself.direction = (0, -1)elif action == 3:  # Rightself.direction = (0, 1)head = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])if (head in self.snake) or (head[0] < 0 or head[0] >= self.grid_size) or (head[1] < 0 or head[1] >= self.grid_size):self.done = Truereturn self._get_observation(), -10, self.done  # Collision: negative rewardself.snake = [head] + self.snake[:-1]if head == self.food:self.snake.append(self.snake[-1])  # Growself.food = self._place_food()return self._get_observation(), 10, self.done  # Food eaten: positive rewardreturn self._get_observation(), 0, self.done  # Neutral step

第三步:定义强化学习的智能体

我们将使用一个简单的神经网络作为我们的智能体,它将输入贪吃蛇游戏的状态,输出每个可能的动作的“价值”(即采取该行动的预期回报)。在强化学习中,我们通常使用 Q-learning 算法来训练这个网络。

import torch.nn as nn
import torch.optim as optimclass 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()[1:]  # 除去批次维度,计算特征维度的大小num_features = 1for s in size:num_features *= sreturn 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 = 0while 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[0, action], target)  # 计算损失optimizer.zero_grad()  # 梯度清零loss.backward()  # 反向传播optimizer.step()  # 更新网络参数state = next_state  # 更新当前状态total_reward += rewardif done:  # 如果游戏结束,退出循环breakprint(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 pltdef evaluate(game, dqn, episodes=10):for episode in range(episodes):state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)  # 添加批次维度total_reward = 0steps = 0plt.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 += rewardsteps += 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}")breakplt.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_sizeself.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 = Falsereturn 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[x, y] = 1  # 用1表示蛇的身体obs[self.food[0], self.food[1]] = 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[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])# 如果蛇撞到自己或墙壁,游戏结束if (head in self.snake) or (head[0] < 0 or head[0] >= self.grid_size) or (head[1] < 0 or head[1] >= self.grid_size):self.done = Truereturn self._get_observation(), -10, self.done  # 碰撞:给予负奖励# 更新蛇的身体self.snake = [head] + 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()[1:]  # 除去批次维度,计算特征维度的大小num_features = 1for s in size:num_features *= sreturn 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 = 0while 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[0, action], target)  # 计算损失optimizer.zero_grad()  # 梯度清零loss.backward()  # 反向传播optimizer.step()  # 更新网络参数state = next_state  # 更新当前状态total_reward += rewardif done:  # 如果游戏结束,退出循环breakprint(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 = 0steps = 0plt.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 += rewardsteps += 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}")breakplt.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 dequereplay_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 = 0while 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[a], target)optimizer.zero_grad()loss.backward()optimizer.step()state = next_statetotal_reward += rewardif done:breakprint(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:  # Upself.direction = (-1, 0)elif action == 1:  # Downself.direction = (1, 0)elif action == 2:  # Leftself.direction = (0, -1)elif action == 3:  # Rightself.direction = (0, 1)head = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])if (head in self.snake) or (head[0] < 0 or head[0] >= self.grid_size) or (head[1] < 0 or head[1] >= self.grid_size):self.done = Truereturn self._get_observation(), -10, self.done  # Collision: negative rewardself.snake = [head] + self.snake[:-1]if head == self.food:self.snake.append(self.snake[-1])  # Growself.food = self._place_food()return self._get_observation(), 10, self.done  # Food eaten: positive reward# 距离食物近一步,给出微小奖励dist = abs(head[0] - self.food[0]) + abs(head[1] - self.food[1])reward = 1.0 / (dist + 1)  # 距离越近,奖励越大return self._get_observation(), reward, self.done  # Neutral step

通过这些参数的调整,你可以提高训练的效果和智能体的表现。每个参数的调整对训练结果都有可能产生显著的影响,因此建议在调整时逐步进行,并仔细观察训练的变化情况。


http://www.mrgr.cn/news/9342.html

相关文章:

  • react笔记(React18)
  • Openssl Infinite Loop 漏洞(CVE-2022-0778)
  • git使用
  • 【Kaggle】练习赛《有毒蘑菇的二分类预测》(上)
  • 基于无人机边沿相关 ------- IBUS、SBUS协议和PPM信号
  • 31套科技风PPT模版免费下载
  • 什么是Redis集群的脑裂问题?
  • 显示“ ‘cmd‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。”的解决方法
  • Nginx入门
  • 百度网盘电脑版播放视频没有声音 - 解决方案
  • E-Prime2中同时识别大小写字母与中文支持设置
  • Vue.js与UI构建
  • 【Linux系列】AWK命令使用
  • AJAX(5)——Promise
  • 集团数字化转型方案(十三)
  • UE管理内容 —— FBX Animation Pipeline
  • 如何监控Eureka集群:Prometheus与Grafana的监控集成
  • FTP协议-匿名用户登录 从0到1
  • 机器人笛卡尔空间轨迹规划-直线差补和圆弧差补
  • [数据集][目标检测]agvs仓储机器人检测数据集VOC+YOLO格式967张3类别