共计 3982 个字符,预计需要花费 10 分钟才能阅读完成。
本文将实现一个简单的 DQN(Deep Q-Network)Agent。DQN 是深度强化学习中的一个经典算法,用于学习从环境状态到动作的映射,使得 Agent 能够在环境中做出正确的决策以获得最大的奖励。
Q-Learning 简述
首先来对 QLearning 进行简单的解释。
Q-learning 是一种基于值迭代的强化学习算法,用于学习在给定环境中的最优策略。它的核心思想是通过更新 Q 值函数来学习如何在每个状态下选择最优动作,以最大化累积奖励。下面详细解释 Q -learning 算法及其数学公式:
- 算法描述:
假设我们有一个由状态空间 S 和动作空间 A 组成的 MDP(马尔可夫决策过程)。Q-learning 算法使用一个 Q 值函数 Q(s, a) 来表示在状态 s 下采取动作 a 所能获得的累积奖励。该算法的主要步骤如下:
- 初始化 Q 值函数 Q(s, a) 为任意初始值,对所有状态 - 动作对(s, a);
- 不断与环境交互,从当前状态 s 开始,采取动作 a,得到奖励 r 和下一个状态 s ’;
- 更新 Q 值函数:Q(s, a) = Q(s, a) + α [r + γ max(Q(s’, a’)) – Q(s, a)],其中 α 是学习率,γ 是折扣因子;
- 将当前状态更新为下一个状态:s = s’;
- 重复以上步骤直到收敛或达到预定迭代次数。
- 数学公式:
Q-learning 算法的核心更新规则由以下数学公式表示:
Q(s, a) = Q(s, a) + α [r + γ max(Q(s’, a’)) – Q(s, a)]
其中,
- Q(s, a) 表示在状态 s 下采取动作 a 所能获得的累积奖励(Q 值);
- α(0 ≤ α ≤ 1)是学习率,控制更新的步长,用于平衡新旧 Q 值的权重;
- r 是在当前状态 s 下采取动作 a 后获得的即时奖励;
- γ(0 ≤ γ ≤ 1)是折扣因子,用于权衡当前奖励和未来奖励的重要性;
- max(Q(s’, a’)) 是在下一个状态 s ’ 下所有可能动作 a ’ 的 Q 值的最大值。
更新公式表示了一个基于差分的更新过程,将当前的 Q 值逐步调整,使其逼近目标 Q 值,即即时奖励与下一状态的最大 Q 值的组合。
在 Q -learning 算法中,Agent 通过不断与环境交互和更新 Q 值函数来学习最优策略。在学习过程中,Agent 逐渐探索并学习到在每个状态下采取最优动作,从而使得累积奖励最大化。经过足够的迭代和训练,Q 值函数将收敛到最优值,从而找到最优的策略。
而 DQN 就是用神经网络来拟合 Q 函数。
DQN 实现
让我们逐步解释代码中的每个部分:
- 导入模块:
import time | |
import numpy as np | |
import tensorflow as tf | |
from collections import deque | |
import random | |
from tensorflow.keras.layers import Convolution2D, Flatten | |
from tensorflow.keras.layers import Dense |
在这里,代码导入了一些必要的库和模块,包括时间处理、数组操作、TensorFlow 框架、双端队列(deque)、随机数生成和 Keras 中的一些层。
- DQNAgent 类:
class DQNAgent: | |
def __init__(self, state_size, action_size): | |
# ... | |
# 初始化一些参数和变量 | |
# ... | |
self.model = self._build_model() # 创建 DQN 模型 | |
def _build_model(self): | |
# ... | |
# 创建 DQN 模型的结构并编译 | |
# ... | |
def remember(self, state, action, reward, next_state, done): | |
# ... | |
# 存储经验回放的方法 | |
# ... | |
def act(self, state, use_model=False): | |
# ... | |
# 根据当前状态选择动作的方法 | |
# ... | |
def replay(self, batch_size): | |
# ... | |
# 更新神经网络的方法 | |
# ... |
- 初始化方法:
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 = 0.95 # 探索因子 | |
self.epsilon_decay = 0.995 # 探索因子的衰减率 | |
self.epsilon_min = 0.01 # 探索因子的最小值 | |
self.learning_rate = 0.0001 # 学习率 | |
self.model = self._build_model() |
这是 DQNAgent 类的初始化方法。它设置了 Agent 的初始状态,包括状态空间大小(state_size)、动作空间大小(action_size)、经验回放存储器(memory)、折扣因子(gamma,用于考虑未来奖励的重要性)、探索因子(epsilon,用于控制随机探索的概率)、探索因子的衰减率(epsilon_decay,探索因子随时间逐渐减小)、探索因子的最小值(epsilon_min,最小探索概率)和学习率(learning_rate,用于更新神经网络的参数)。
- 构建 DQN 模型的方法:
def _build_model(self): | |
model = tf.keras.Sequential() | |
model.add(Convolution2D(32, 8, (4, 4), activation='relu', | |
input_shape=self.state_size)) | |
model.add(Convolution2D(64, 4, (2, 2), activation='relu')) | |
model.add(Convolution2D(64, 3, (1, 1), activation='relu')) | |
model.add(Flatten()) | |
model.add(Dense(512, activation='relu')) | |
model.add(Dense(self.action_size)) | |
model.compile(loss='mse', optimizer=tf.keras.optimizers.Adam(lr=self.learning_rate)) | |
return model |
这个方法定义了 DQN 模型的结构,使用了卷积层和全连接层构建神经网络。输入是环境状态(state),输出是动作空间的 Q 值。这个方法还编译了模型,指定了均方误差(MSE)作为损失函数,并使用 Adam 优化器进行参数更新。
- 存储经验回放的方法:
def remember(self, state, action, reward, next_state, done): | |
self.memory.append((state, action, reward, next_state, done)) |
这个方法用于将 Agent 在环境中的经验(状态、动作、奖励、下一个状态和完成标志)存储到经验回放存储器中。经验回放是 DQN 算法中的一项重要技术,用于从存储的经验中随机抽取一批数据来进行训练,以减少数据之间的相关性。
- 根据当前状态选择动作的方法:
def act(self, state, use_model=False): | |
if not use_model: | |
print("Using random action.") | |
if np.random.rand() <= self.epsilon: | |
time.sleep(0.1) | |
return random.randrange(self.action_size) | |
q_values = self.model.predict(state) | |
return np.argmax(q_values[0]) |
这个方法根据当前状态(state)选择一个动作(action)。如果 use_model 为 False,那么 Agent 会以一定的概率(由 epsilon 控制)随机选择动作,以进行探索。如果 use_model 为 True,那么 Agent 将根据当前模型预测的 Q 值选择最优的动作。
- 更新神经网络的方法:
def replay(self, batch_size): | |
minibatch = random.sample(self.memory, batch_size) | |
for state, action, reward, next_state, done in minibatch: | |
target = reward | |
if not done: | |
target = (reward + self.gamma * np.amax(self.model.predict(next_state)[0])) | |
target_f = self.model.predict(state) | |
target_f[0][action] = target | |
self.model.fit(state, target_f, epochs=1, verbose=0) | |
if self.epsilon > self.epsilon_min: | |
self.epsilon *= self.epsilon_decay |
读到这你可能有疑问,DQN 不是应该有两个网络吗?DQN 中的两个网络具有相同网络结构,只是参数不同。这里其实就是用上一次更新的模型来预测,然后进行一步更新。因此这里其实是其他 DQN 实现中把两个网络复制更新的步长设为 1。
这个方法用于更新神经网络的参数,以减小预测值与目标值之间的差距。首先,从经验回放存储器中随机采样一个批次的经验(minibatch)。
然后,对于每个经验,计算目标 Q 值。如果游戏没有结束(done 为 False),目标 Q 值是当前奖励加上折扣因子乘以下一个状态的最大 Q 值;如果游戏结束(done 为 True),目标 Q 值就是当前奖励。接着,根据目标 Q 值和当前状态,更新模型的参数。最后,如果当前探索因子大于最小探索因子,就按照衰减率逐渐减小探索因子。