强化学习入门
什么是强化学习?
强化学习(Reinforcement Learning, RL)是一种基于反馈的机器学习技术,适用于序列决策问题。在强化学习中,智能体(Agent)通过与环境(Environment)不断交互,根据环境的反馈调整自身行为,以最大化累积奖励(Reward)。强化学习问题可以描述为一个智能体从与环境的交互中不断学习以完成特定目标(比如取得最大奖励值)。强化学习通常基于马尔可夫决策过程(Markov Decision Process, MDP),其中智能体观测到环境状态(State),根据某种策略(Policy)选出一个对应的动作(Action),环境执行这个动作后状态发生变化,并得到一个奖励作为反馈。智能体依据得到的奖励进行策略调整,目的是获得最大的累积奖励,从而学会在特定环境下做出最佳决策。
Q-Learning算法
Q-Learning是强化学习算法家族中最具代表性的基础算法之一。经典的Q-Learning算法相对简单、学习快速,具有以下特点:
- Model-free:不需要理解环境,简单方便。
- Value-based:直接给出当前环境中每一个行动的价值,但只支持离散行动场景。
- 单步更新:行动一次就更新Q表(环境~动作价值表),效率高,但缺乏长期记忆。
- Off-policy:可以自己边玩边学,也可以看别人玩并从中学到经验。
表格
Q-Learning算法的核心就是,利用一个表格去储存映射关系, Q ( s , a ) Q(s,a) Q(s,a)它在QL算法中不是一个函数,而是一个表格。
Q值
Q值,即Q(s,a),表示在状态s下采取动作a所能获得的期望总回报。这个总回报是考虑了即时奖励和未来奖励(通过折扣因子γ来衰减)的累加。
类比在游戏中,我们可以理解为,当前游戏状态s下,我们采取动作a,根据我们当前经验所能得到的奖励。
估计值(难理解)
在Q-Learning的更新公式中,“估计值”实际上是指在当前状态s下采取动作a后,进入新状态s 所能获得的最大Q值,再乘以折扣因子γ并加上即时奖励r。这个估计值代表了从当前状态-动作对出发,未来可能获得的最大回报的估计。
类比在游戏中,我们可以理解为,当前游戏状态s下,我们采取动作a后,进入了新状态s‘,此时在新状态我们可以采取n种状态,其中总有一个a’ 的动作依据你的经验得到的奖励最多即
Q
(
s
′
,
a
′
)
Q(s',a')
Q(s′,a′),
r
r
r是指的是获得的奖励,
γ
\gamma
γ是一个权值,可以理解为学习率。
估
计
值
:
r
+
γ
max
a
′
Q
(
s
′
,
a
′
)
估计值: r + \gamma\max_{a'}Q(s',a')
估计值:r+γmaxa′Q(s′,a′)
Q-Learning算法步骤
- 随机初始化Q(s,a),其中s表示环境状态,a表示动作, Q ( s , a ) Q(s,a) Q(s,a)是指在当前环境状态,选取a动作的时候,智能体的当前时刻和未来的总回报值。。
- 对于每一个回合(episode),重复以下步骤:
- 初始化s。
- 对于此回合中的每一步(step),重复以下步骤:
- 根据当前的s,按照一定的规则(例如ε-greedy),从Q表中选择a。
- 执行a,观察s′(新的环境)和r(激励)。
- 更新Q表: Q ( s , a ) ← Q ( s , a ) + α [ r + γ max a ′ Q ( s ′ , a ′ ) − Q ( s , a ) ] Q(s,a) \leftarrow Q(s,a) + \alpha[r + \gamma\max_{a'}Q(s',a') - Q(s,a)] Q(s,a)←Q(s,a)+α[r+γmaxa′Q(s′,a′)−Q(s,a)]。
- s←s′。
- 直到s到达终止状态。
解释:
新 Q ( s , a ) = 老 Q ( s , a ) + α ∗ ( 估 计 值 − 老 Q ( s , a ) ) 新Q(s,a)=老Q(s,a)+\alpha*(估计值-老Q(s,a)) 新Q(s,a)=老Q(s,a)+α∗(估计值−老Q(s,a))
估 计 值 : r + γ max a ′ Q ( s ′ , a ′ ) 估计值: r + \gamma\max_{a'}Q(s',a') 估计值:r+γmaxa′Q(s′,a′)
估计值是一个数列求极限所得,这部分数学原理不需要理解。
import numpy as np
import gymnasium as gym
# 定义一个QL类
class QLearningAgent:
def __init__(self, state_space, action_space, learning_rate=0.1, gamma=0.95):
self.state_space = state_space
self.action_space = action_space
self.learning_rate = learning_rate
self.gamma = gamma
self.q_table = np.zeros((len(state_space), len(action_space)))
def choose_action(self, state, epsilon):
if np.random.uniform() < epsilon:
return np.random.choice(self.action_space)
else:
action = np.argmax(self.q_table[state])
return action
def learn(self, state, action, reward, next_state):
target = reward + self.gamma * np.max(self.q_table[next_state])
prediction = self.q_table[state, action]
self.q_table[state, action] += self.learning_rate * (target - prediction)
return self.q_table
# 创建 FrozenLake 环境
env = gym.make('FrozenLake-v1', is_slippery=False)
# 定义一个QL智能体
agent = QLearningAgent(state_space=range(env.observation_space.n), action_space=range(env.action_space.n))
# 开始训练
epochs = 1000
epsilon = 1.0
epsilon_min = 0.01
epsilon_decay = 0.995
for epoch in range(epochs):
state, _ = env.reset()
done = False
while not done:
action = agent.choose_action(state, epsilon)
result = env.step(action)
next_state, reward, done, _, _ = result
agent.learn(state, action, reward, next_state)
state = next_state
# 逐渐减少 epsilon
epsilon = max(epsilon_min, epsilon * epsilon_decay)
print("Training finished")
# 创建 FrozenLake 测试环境
env_player = gym.make('FrozenLake-v1', render_mode="human", is_slippery=False)
# 训练结束后进行渲染
state, _ = env_player.reset()
done = False
while not done:
action = agent.choose_action(state, epsilon=0.0) # 选择最优动作
print("Action:", action)
next_state, reward, done, _, _ = env_player.step(action)
state = next_state
Deep Q-Network (DQN)
DQN是一种深度增强学习算法,它将Q-Learning算法与深度神经网络相结合。相比传统的Q-Learning算法,DQN通过引入深度神经网络可以处理高维输入状态空间,使得算法具备更强的表达能力和泛化能力。
DQN是将Q从表格映射转为用一个神经网络运算。
DQN的改进
- 引入深度学习中的神经网络:利用神经网络去拟合Q-Learning中的Q表,解决了Q-Learning中当状态维数过高时产生的“维数灾难”问题。
- 固定Q目标网络:利用延后更新的目标网络计算目标Q值,极大地提高了网络训练的稳定性和收敛性。
- 引入经验回放机制:使得在进行网络更新时输入的数据符合独立同分布,打破了数据间的相关性。
DQN算法流程
- 初始化经验池,随机初始化Q网络,初始化targetQ网络,其参数与Q网络参数相同。
- 重置环境,获得第一个状态。
- 重复:
- 用ε-greedy策略生成一个action:其中有ε的概率会随机选择一个action,其他情况下,选择在s状态下使得Q最大的action。
- 根据动作与环境的交互,获得反馈的reward、下一个状态 s t + 1 s_{t+1} st+1和是否触发终止条件done。
- 将经验 s t , a t , r t , s t + 1 s_t, a_t, r_t, s_{t+1} st,at,rt,st+1存入经验池。
- 从经验池中随机获取一个minibatch的经验。
- 计算目标Q值,并根据 Q p r e d Q_{pred} Qpred和 Q t a r g e t Q_{target} Qtarget求 l o s s loss loss,梯度下降法更新Q网络。
- 每隔固定个training step,更新targetQ网络,使其参数与Q网络相同。
- 直到Q(s,a)收敛。
利用PaddlePaddle实现DQN示例代码
以下是一个简单的DQN算法的实现示例。在实际应用中,还需要添加训练和测试的逻辑,以及与环境的交互等部分。
import random
import gym
import numpy as np
import paddle
from paddle import nn
from paddle.optimizer import Adam
# 创建一个Q网络
class QNetwork(nn.Layer):
def __init__(self, state_size, action_size):
super(QNetwork, self).__init__()
self.fc1 = nn.Linear(state_size, 24)
self.fc2 = nn.Linear(24, 24)
self.fc3 = nn.Linear(24, action_size)
def forward(self, state):
x = self.fc1(state)
x = paddle.nn.functional.relu(x)
x = self.fc2(x)
x = paddle.nn.functional.relu(x)
x = self.fc3(x)
return x
# 经验池类
class ReplayBuffer:
def __init__(self, buffer_size, batch_size):
self.memory = []
self.buffer_size = buffer_size
self.batch_size = batch_size
def add(self, experience):
if len(self.memory) > self.buffer_size:
self.memory.pop(0)
self.memory.append(experience)
def sample(self):
experiences = random.choices(self.memory, k=self.batch_size)
states, actions, rewards, next_states, dones = zip(*experiences)
return np.array(states), np.array(actions), np.array(rewards), np.array(next_states), np.array(dones)
def __len__(self):
return len(self.memory)
# 创建一个DQN
class DQNAgent:
def __init__(self, state_size, action_size):
self.state_size = state_size
self.action_size = action_size
self.gamma = 0.99
self.epsilon = 1.0
self.epsilon_decay = 0.995
self.epsilon_min = 0.1
self.learning_rate = 0.001
self.qnetwork_local = QNetwork(state_size, action_size)
self.qnetwork_target = QNetwork(state_size, action_size)
self.optimizer = Adam(parameters=self.qnetwork_local.parameters(), learning_rate=self.learning_rate)
self.loss = paddle.nn.MSELoss()
def act(self, state):
if np.random.rand() < self.epsilon:
return random.randrange(self.action_size)
else:
state = paddle.to_tensor(state)
return self.qnetwork_local(state).argmax().item()
def learn(self, experiences):
states, actions, rewards, next_states, dones = experiences
# 转换为Tensor
states = paddle.to_tensor(states, dtype='float32')
actions = paddle.to_tensor(actions, dtype='int64')
rewards = paddle.to_tensor(rewards, dtype='float32')
next_states = paddle.to_tensor(next_states, dtype='float32')
dones = paddle.to_tensor(dones, dtype='float32')
# 计算目标Q值
target_q_values = self.qnetwork_target(next_states).max(axis=1, keepdim=True).squeeze(1)
target_q_values = rewards + self.gamma * target_q_values * (1 - dones)
# 计算当前Q值
q_values = self.qnetwork_local(states)
# 从 input_tensor 中根据 index_tensor 收集元素
# 创建一个范围张量,用于与actions组合以正确地索引q_values
batch_indices = paddle.arange(q_values.shape[0])
# 使用两个张量作为索引来获取所需元素
current_q_values = q_values[batch_indices, actions]
# 计算损失函数
loss = self.loss(current_q_values, target_q_values)
# 更新参数
self.optimizer.clear_grad()
loss.backward()
self.optimizer.step()
return loss.item()
def update_target_network(self):
# 将局部网络的参数复制到目标网络
for target_param, local_param in zip(self.qnetwork_target.parameters(), self.qnetwork_local.parameters()):
target_param.set_value(local_param)
if __name__ == "__main__":
env = gym.make('CartPole-v1', render_mode='human')
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
agent = DQNAgent(state_size, action_size)
epochs = 1000
buffer_size = 512
batch_size = 64
replay_buffer = ReplayBuffer(buffer_size, batch_size)
# 初始化epsilon
agent.epsilon = 1.0
for epoch in range(epochs):
state, _ = env.reset()
done = False
loss = 0
while not done:
action = agent.act(state)
next_state, reward, done, info, _ = env.step(action)
# 先存储经验,再更新状态
replay_buffer.add((state, action, reward, next_state, done))
state = next_state
if len(replay_buffer) > batch_size:
experiences = replay_buffer.sample()
loss = agent.learn(experiences)
if agent.epsilon > agent.epsilon_min:
agent.epsilon *= agent.epsilon_decay
# 每一定步数更新目标网络
if epoch % 10 == 0: # 假设每10个episode更新一次
agent.update_target_network()
if epoch % 100 == 0:
print(f"Epoch:{epoch}, Loss:{loss}")
print("Training finished")
env.close()
# 训练结束后进行渲染
env_player = gym.make('CartPole-v1', render_mode='human')
state, _ = env_player.reset()
done = False
while not done:
agent.epsilon = 0
action = agent.act(state) # 选择最优动作
print("Action:", action)
next_state, reward, done, _, _ = env_player.step(action)
state = next_state
env_player.render()