ToB企服应用市场:ToB评测及商务社交产业平台

标题: 深度学习学习履历——强化学习(rl) [打印本页]

作者: 干翻全岛蛙蛙    时间: 2024-8-29 17:58
标题: 深度学习学习履历——强化学习(rl)
强化学习

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

实际应用

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

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

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

第二步:环境搭建

首先,我们需要创建一个简朴的贪吃蛇游戏环境。这个环境将包罗蛇的状态(位置、方向等)以及食品的位置。
  1. import numpy as np
  2. import random
  3. import torch
  4. class SnakeGame:
  5.     def __init__(self, grid_size=10):
  6.         self.grid_size = grid_size
  7.         self.reset()
  8.     def reset(self):
  9.         self.snake = [(self.grid_size // 2, self.grid_size // 2)]
  10.         self.direction = (0, 1)  # Start by moving right
  11.         self.food = self._place_food()
  12.         self.done = False
  13.         return self._get_observation()
  14.     def _place_food(self):
  15.         while True:
  16.             food = (random.randint(0, self.grid_size - 1), random.randint(0, self.grid_size - 1))
  17.             if food not in self.snake:
  18.                 return food
  19.     def _get_observation(self):
  20.         obs = np.zeros((self.grid_size, self.grid_size))
  21.         for x, y in self.snake:
  22.             obs[x, y] = 1
  23.         obs[self.food[0], self.food[1]] = 2
  24.         return obs
  25.     def step(self, action):
  26.         if action == 0:  # Up
  27.             self.direction = (-1, 0)
  28.         elif action == 1:  # Down
  29.             self.direction = (1, 0)
  30.         elif action == 2:  # Left
  31.             self.direction = (0, -1)
  32.         elif action == 3:  # Right
  33.             self.direction = (0, 1)
  34.         head = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
  35.         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):
  36.             self.done = True
  37.             return self._get_observation(), -10, self.done  # Collision: negative reward
  38.         self.snake = [head] + self.snake[:-1]
  39.         if head == self.food:
  40.             self.snake.append(self.snake[-1])  # Grow
  41.             self.food = self._place_food()
  42.             return self._get_observation(), 10, self.done  # Food eaten: positive reward
  43.         return self._get_observation(), 0, self.done  # Neutral step
复制代码
第三步:界说强化学习的智能体

我们将利用一个简朴的神经网络作为我们的智能体,它将输入贪吃蛇游戏的状态,输出每个大概的动作的“价值”(即采取该办法的预期回报)。在强化学习中,我们通常利用 Q-learning 算法来练习这个网络。
  1. import torch.nn as nn
  2. import torch.optim as optim
  3. class DQN(nn.Module):
  4.     def __init__(self, grid_size, num_actions):
  5.         super(DQN, self).__init__()
  6.         self.fc1 = nn.Linear(grid_size * grid_size, 128)  # 第一层全连接层,输入大小为网格大小平方
  7.         self.fc2 = nn.Linear(128, 64)  # 第二层全连接层
  8.         self.fc3 = nn.Linear(64, num_actions)  # 输出层,输出动作的Q值
  9.     def forward(self, x):
  10.         batch_size = x.size(0)  # 获取批次大小
  11.         x = x.view(batch_size, -1)  # 将输入展平为 (batch_size, grid_size * grid_size)
  12.         x = torch.relu(self.fc1(x))  # 经过第一层全连接并ReLU激活
  13.         x = torch.relu(self.fc2(x))  # 经过第二层全连接并ReLU激活
  14.         return self.fc3(x)  # 输出动作的Q值
  15.     # 计算输入张量的展平后的特征数目
  16.     def num_flat_features(self, x):
  17.         size = x.size()[1:]  # 除去批次维度,计算特征维度的大小
  18.         num_features = 1
  19.         for s in size:
  20.             num_features *= s
  21.         return num_features
  22. # Initialize DQN
  23. grid_size = 10
  24. num_actions = 4  # Up, Down, Left, Right
  25. dqn = DQN(grid_size, num_actions)
  26. optimizer = optim.Adam(dqn.parameters(), lr=0.001)
  27. loss_fn = nn.MSELoss()
复制代码
第四步:练习智能体

在这个步骤中,我们将利用贪吃蛇的游戏状态作为输入,通过神经网络推测各个办法的价值,并选择价值最高的办法。然后,我们利用Q-learning来更新神经网络的参数。
  1. def train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.9):
  2.     for episode in range(episodes):
  3.         state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)  # 添加批次维度
  4.         total_reward = 0
  5.         while True:
  6.             q_values = dqn(state)  # 预测当前状态的Q值
  7.             action = torch.argmax(q_values).item()  # 选择Q值最大的动作
  8.             next_state, reward, done = game.step(action)  # 执行动作并获得下一状态和奖励
  9.             next_state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)  # 将下一状态转换为张量并添加批次维度
  10.             with torch.no_grad():
  11.                 max_next_q_values = torch.max(dqn(next_state))  # 计算下一状态的最大Q值
  12.                 target = reward + gamma * max_next_q_values * (1 - int(done))  # 计算目标Q值
  13.             loss = loss_fn(q_values[0, action], target)  # 计算损失
  14.             optimizer.zero_grad()  # 梯度清零
  15.             loss.backward()  # 反向传播
  16.             optimizer.step()  # 更新网络参数
  17.             state = next_state  # 更新当前状态
  18.             total_reward += reward
  19.             if done:  # 如果游戏结束,退出循环
  20.                 break
  21.         print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward}")  # 输出当前回合的总奖励
  22. # Train the agent
  23. game = SnakeGame(grid_size)
  24. train(game, dqn, optimizer, loss_fn, episodes=1000)
复制代码
第五步:评估和可视化

练习完智能体后,我们可以让它在游戏中实行跑几局,并观察它的表现。以下代码展示了如何评估智能体并可视化它的表现。
  1. import matplotlib.pyplot as plt
  2. def evaluate(game, dqn, episodes=10):
  3.     for episode in range(episodes):
  4.         state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)  # 添加批次维度
  5.         total_reward = 0
  6.         steps = 0
  7.         plt.figure()  # 创建新的图像窗口
  8.         while True:
  9.             q_values = dqn(state)  # 预测当前状态的Q值
  10.             action = torch.argmax(q_values).item()  # 选择Q值最大的动作
  11.             next_state, reward, done = game.step(action)  # 执行动作并获得下一状态和奖励
  12.             state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)  # 将下一状态转换为张量并添加批次维度
  13.             total_reward += reward
  14.             steps += 1
  15.             # Visualization (Optional)
  16.             plt.imshow(state.squeeze(0).numpy())  # 去掉批次维度进行显示
  17.             plt.axis('off')  # 隐藏坐标轴
  18.             plt.pause(0.1)  # 短暂停留以更新视图
  19.             if done:
  20.                 print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward} - Steps: {steps}")
  21.                 break
  22.         
  23.         plt.close()  # 关闭图像窗口
复制代码
最终代码汇总

  1. import numpy as np
  2. import random
  3. import torch
  4. import torch.nn as nn
  5. import torch.optim as optim
  6. import matplotlib.pyplot as plt
  7. # 定义贪吃蛇游戏环境
  8. class SnakeGame:
  9.     def __init__(self, grid_size=10):
  10.         self.grid_size = grid_size
  11.         self.reset()
  12.     # 重置游戏状态,初始化蛇的位置和食物的位置
  13.     def reset(self):
  14.         self.snake = [(self.grid_size // 2, self.grid_size // 2)]  # 蛇的初始位置在网格的中央
  15.         self.direction = (0, 1)  # 蛇的初始移动方向为向右
  16.         self.food = self._place_food()  # 随机放置食物
  17.         self.done = False
  18.         return self._get_observation()
  19.     # 随机生成一个食物位置,确保不与蛇的身体重叠
  20.     def _place_food(self):
  21.         while True:
  22.             food = (random.randint(0, self.grid_size - 1), random.randint(0, self.grid_size - 1))
  23.             if food not in self.snake:
  24.                 return food
  25.     # 获取当前游戏状态,返回一个网格表示的观察值
  26.     def _get_observation(self):
  27.         obs = np.zeros((self.grid_size, self.grid_size))  # 初始化一个全0的网格
  28.         for x, y in self.snake:
  29.             obs[x, y] = 1  # 用1表示蛇的身体
  30.         obs[self.food[0], self.food[1]] = 2  # 用2表示食物的位置
  31.         return obs
  32.     # 游戏的主要逻辑:根据动作更新蛇的状态
  33.     def step(self, action):
  34.         if action == 0:  # 上
  35.             self.direction = (-1, 0)
  36.         elif action == 1:  # 下
  37.             self.direction = (1, 0)
  38.         elif action == 2:  # 左
  39.             self.direction = (0, -1)
  40.         elif action == 3:  # 右
  41.             self.direction = (0, 1)
  42.         # 更新蛇头的位置
  43.         head = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
  44.         
  45.         # 如果蛇撞到自己或墙壁,游戏结束
  46.         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):
  47.             self.done = True
  48.             return self._get_observation(), -10, self.done  # 碰撞:给予负奖励
  49.         # 更新蛇的身体
  50.         self.snake = [head] + self.snake[:-1]
  51.         # 如果蛇吃到食物
  52.         if head == self.food:
  53.             self.snake.append(self.snake[-1])  # 蛇的身体增长
  54.             self.food = self._place_food()  # 重新放置食物
  55.             return self._get_observation(), 10, self.done  # 吃到食物:给予正奖励
  56.         return self._get_observation(), 0, self.done  # 普通移动:奖励为0
  57. # 定义DQN(深度Q网络)模型
  58. class DQN(nn.Module):
  59.     def __init__(self, grid_size, num_actions):
  60.         super(DQN, self).__init__()
  61.         self.fc1 = nn.Linear(grid_size * grid_size, 128)  # 第一层全连接层,输入大小为网格大小平方
  62.         self.fc2 = nn.Linear(128, 64)  # 第二层全连接层
  63.         self.fc3 = nn.Linear(64, num_actions)  # 输出层,输出动作的Q值
  64.     def forward(self, x):
  65.         batch_size = x.size(0)  # 获取批次大小
  66.         x = x.view(batch_size, -1)  # 将输入展平为 (batch_size, grid_size * grid_size)
  67.         x = torch.relu(self.fc1(x))  # 经过第一层全连接并ReLU激活
  68.         x = torch.relu(self.fc2(x))  # 经过第二层全连接并ReLU激活
  69.         return self.fc3(x)  # 输出动作的Q值
  70.     # 计算输入张量的展平后的特征数目
  71.     def num_flat_features(self, x):
  72.         size = x.size()[1:]  # 除去批次维度,计算特征维度的大小
  73.         num_features = 1
  74.         for s in size:
  75.             num_features *= s
  76.         return num_features
  77. # 初始化DQN
  78. grid_size = 10
  79. num_actions = 4  # 上、下、左、右四个动作
  80. dqn = DQN(grid_size, num_actions)
  81. optimizer = optim.Adam(dqn.parameters(), lr=0.00001)  # 使用Adam优化器
  82. loss_fn = nn.MSELoss()  # 使用均方误差损失函数
  83. # 训练智能体
  84. def train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.9):
  85.     for episode in range(episodes):
  86.         state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)  # 添加批次维度
  87.         total_reward = 0
  88.         while True:
  89.             q_values = dqn(state)  # 预测当前状态的Q值
  90.             action = torch.argmax(q_values).item()  # 选择Q值最大的动作
  91.             next_state, reward, done = game.step(action)  # 执行动作并获得下一状态和奖励
  92.             next_state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)  # 将下一状态转换为张量并添加批次维度
  93.             with torch.no_grad():
  94.                 max_next_q_values = torch.max(dqn(next_state))  # 计算下一状态的最大Q值
  95.                 target = reward + gamma * max_next_q_values * (1 - int(done))  # 计算目标Q值
  96.             loss = loss_fn(q_values[0, action], target)  # 计算损失
  97.             optimizer.zero_grad()  # 梯度清零
  98.             loss.backward()  # 反向传播
  99.             optimizer.step()  # 更新网络参数
  100.             state = next_state  # 更新当前状态
  101.             total_reward += reward
  102.             if done:  # 如果游戏结束,退出循环
  103.                 break
  104.         print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward}")  # 输出当前回合的总奖励
  105. # 训练智能体
  106. game = SnakeGame(grid_size)
  107. train(game, dqn, optimizer, loss_fn, episodes=10000)  # 开始训练
  108. # Step 5: Evaluate the Agent
  109. def evaluate(game, dqn, episodes=10):
  110.     for episode in range(episodes):
  111.         state = torch.tensor(game.reset(), dtype=torch.float32).unsqueeze(0)  # 添加批次维度
  112.         total_reward = 0
  113.         steps = 0
  114.         plt.figure()  # 创建新的图像窗口
  115.         while True:
  116.             q_values = dqn(state)  # 预测当前状态的Q值
  117.             action = torch.argmax(q_values).item()  # 选择Q值最大的动作
  118.             next_state, reward, done = game.step(action)  # 执行动作并获得下一状态和奖励
  119.             state = torch.tensor(next_state, dtype=torch.float32).unsqueeze(0)  # 将下一状态转换为张量并添加批次维度
  120.             total_reward += reward
  121.             steps += 1
  122.             # Visualization (Optional)
  123.             plt.imshow(state.squeeze(0).numpy())  # 去掉批次维度进行显示
  124.             plt.axis('off')  # 隐藏坐标轴
  125.             plt.pause(0.1)  # 短暂停留以更新视图
  126.             if done:
  127.                 print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward} - Steps: {steps}")
  128.                 break
  129.         
  130.         plt.close()  # 关闭图像窗口
  131. # Evaluate the trained agent
  132. evaluate(game, dqn)
复制代码
通过这个贪吃蛇的例子,我们逐步了解了强化学习的概念,并通过利用PyTorch实现了一个简朴的强化学习智能体。这种方法不但实用于贪吃蛇游戏,也可以扩展到更复杂的环境和任务中。
如何进步练习效果(调整参数)

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


  1. optimizer = optim.Adam(dqn.parameters(), lr=0.0005)  # 例如,减小学习率
复制代码
2. 折扣因子(Discount Factor, γ)


  1. train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.95)  # 例如,稍微降低 γ
复制代码
3. 探索与利用的平衡(Exploration vs Exploitation)


  1. epsilon = 0.9  # 初始探索率
  2. decay_rate = 0.995  # 衰减率
  3. for episode in range(episodes):
  4.     epsilon = max(epsilon * decay_rate, 0.01)  # 确保 epsilon 最小为 0.01
  5.     # 其余代码保持不变
复制代码
4. 履历回放(Experience Replay)


  1. from collections import deque
  2. replay_memory = deque(maxlen=10000)  # 扩展经验回放池的容量
  3. def train(game, dqn, optimizer, loss_fn, episodes=1000, gamma=0.99, batch_size=64):
  4.     for episode in range(episodes):
  5.         state = torch.tensor(game.reset(), dtype=torch.float32)
  6.         total_reward = 0
  7.         while True:
  8.             if random.random() < epsilon:
  9.                 action = random.randint(0, num_actions - 1)
  10.             else:
  11.                 q_values = dqn(state)
  12.                 action = torch.argmax(q_values).item()
  13.             next_state, reward, done = game.step(action)
  14.             next_state = torch.tensor(next_state, dtype=torch.float32)
  15.             replay_memory.append((state, action, reward, next_state, done))
  16.             # 开始经验回放训练
  17.             if len(replay_memory) > batch_size:
  18.                 minibatch = random.sample(replay_memory, batch_size)
  19.                 for s, a, r, ns, d in minibatch:
  20.                     q_values = dqn(s)
  21.                     with torch.no_grad():
  22.                         max_next_q_values = torch.max(dqn(ns))
  23.                         target = r + gamma * max_next_q_values * (1 - int(d))
  24.                     loss = loss_fn(q_values[a], target)
  25.                     optimizer.zero_grad()
  26.                     loss.backward()
  27.                     optimizer.step()
  28.             state = next_state
  29.             total_reward += reward
  30.             if done:
  31.                 break
  32.         print(f"Episode {episode + 1}/{episodes} - Total Reward: {total_reward}")
复制代码
5. 练习轮数(Number of Episodes)


  1. train(game, dqn, optimizer, loss_fn, episodes=5000)  # 增加训练轮数
复制代码
6. 奖励函数(Reward Function)


  1. def step(self, action):
  2.     if action == 0:  # Up
  3.         self.direction = (-1, 0)
  4.     elif action == 1:  # Down
  5.         self.direction = (1, 0)
  6.     elif action == 2:  # Left
  7.         self.direction = (0, -1)
  8.     elif action == 3:  # Right
  9.         self.direction = (0, 1)
  10.     head = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
  11.     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):
  12.         self.done = True
  13.         return self._get_observation(), -10, self.done  # Collision: negative reward
  14.     self.snake = [head] + self.snake[:-1]
  15.     if head == self.food:
  16.         self.snake.append(self.snake[-1])  # Grow
  17.         self.food = self._place_food()
  18.         return self._get_observation(), 10, self.done  # Food eaten: positive reward
  19.     # 距离食物近一步,给出微小奖励
  20.     dist = abs(head[0] - self.food[0]) + abs(head[1] - self.food[1])
  21.     reward = 1.0 / (dist + 1)  # 距离越近,奖励越大
  22.     return self._get_observation(), reward, self.done  # Neutral step
复制代码
通过这些参数的调整,你可以进步练习的效果和智能体的表现。每个参数的调整对练习结果都有大概产生明显的影响,因此建议在调整时逐步举行,并仔细观察练习的变化环境。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4