65、强化学习:原理、方法与实践

强化学习:原理、方法与实践

1. 强化学习概述

强化学习(Reinforcement Learning,RL)是当今机器学习领域中最令人兴奋且历史悠久的分支之一,其起源可追溯到 20 世纪 50 年代。多年来,强化学习在诸多领域产生了有趣的应用,尤其在游戏(如西洋双陆棋程序 TD - Gammon)和机器控制方面。

2013 年,英国初创公司 DeepMind 带来了一场革命。他们展示了一个系统,该系统仅使用原始像素作为输入,无需任何游戏规则的先验知识,就能从零开始学习玩几乎任何 Atari 游戏,并最终在大多数游戏中超越人类。此后,该系统不断取得惊人成就,如 2016 年 3 月 AlphaGo 战胜传奇职业围棋选手李世石,2017 年 5 月战胜世界冠军柯洁。如今,强化学习领域充满了新思想和广泛的应用。

DeepMind 取得这些成就的关键在于将深度学习的强大能力应用于强化学习领域。接下来,我们将深入了解强化学习的原理、重要技术以及如何在实际中应用。

2. 学习优化奖励

在强化学习中,软件智能体(agent)在环境中进行观察并采取行动,作为回报,它会从环境中获得奖励。其目标是学会以一种能在长时间内最大化预期奖励的方式行动。可以将正奖励视为愉悦,负奖励视为痛苦,智能体通过试错学习在环境中行动,以最大化愉悦并最小化痛苦。

强化学习的应用场景广泛,以下是一些示例:
- 机器人控制 :智能体是控制机器人的程序,环境是现实世界。机器人通过相机和触摸传感器等一组传感器观察环境,其行动是向电机发送信号以激活它们。当机器人接近目标目的地时可获得正奖励,浪费时间或走错方向则获得负奖励。
- 游戏控制 :以控制吃豆人游戏为例,环境是 Atari 游戏的模拟,行动是九种可能的操纵杆位置(左上、下、中心等),观察是游戏截图,奖励就是游戏得分。
- 棋盘游戏 :如围棋程序,只有获胜时才能获得奖励。
- 智能设备 :例如智能恒温器,当接近目标温度并节省能源时获得正奖励,当人类需要调整温度时获得负奖励,因此它必须学会预测人类需求。
- 金融交易 :智能体观察股票市场价格,并决定每秒买卖的数量,奖励显然是货币收益和损失。

此外,有些任务可能没有正奖励,如在迷宫中移动的智能体,每一步都会获得负奖励,因此它需要尽快找到出口。强化学习还适用于许多其他任务,如自动驾驶汽车、推荐系统、网页广告投放或控制图像分类系统的注意力焦点。

3. 策略搜索

软件智能体用于确定其行动的算法称为策略。策略可以是任何算法,不一定是确定性的,甚至在某些情况下不需要观察环境。

以机器人真空吸尘器为例,其奖励是 30 分钟内吸起的灰尘量。它的策略可以是每秒以概率 p 向前移动,或以概率 1 - p 随机向左或向右旋转,旋转角度是 - r 到 + r 之间的随机角度。这种包含随机性的策略称为随机策略,能保证机器人最终到达它能到达的任何地方并吸起所有灰尘。

训练这样的机器人可以采用以下几种策略搜索方法:
- 暴力搜索 :调整两个策略参数,即概率 p 和角度范围 r。尝试许多不同的参数值,选择表现最佳的组合。但当策略空间过大时,这种方法就像在巨大的干草堆中寻找针一样困难。
- 遗传算法 :随机创建第一代 100 个策略并进行测试,淘汰 80 个表现最差的策略,让 20 个幸存者每个产生 4 个后代。后代是其父母的副本加上一些随机变化,幸存者和它们的后代构成第二代。持续迭代直到找到好的策略。
- 优化技术 :通过评估奖励相对于策略参数的梯度,然后沿着梯度方向调整这些参数以获得更高的奖励,这种方法称为策略梯度(Policy Gradients,PG)。例如,对于真空吸尘器机器人,可以稍微增加 p 并评估是否能增加 30 分钟内吸起的灰尘量,如果可以,则进一步增加 p,否则减少 p。

4. OpenAI Gym 简介

强化学习的一个挑战是训练智能体需要一个有效的环境。在现实世界中训练存在诸多限制,如无法撤销错误操作、无法加速时间以及成本高昂等,因此通常需要一个模拟环境进行初始训练。

OpenAI Gym 是一个工具包,提供了各种各样的模拟环境,如 Atari 游戏、棋盘游戏、2D 和 3D 物理模拟等,可用于训练智能体、比较它们或开发新的强化学习算法。

如果在 Colab 上使用,需要更新 OpenAI Gym 到最新版本并安装一些依赖项:

# Only run these commands on Colab or Kaggle!
%pip install -q -U gym
%pip install -q -U gym[classic_control,box2d,atari,accept-rom-license]

第一个 %pip 命令将 Gym 升级到最新版本, -q 选项使输出不那么冗长, -U 选项表示升级。第二个 %pip 命令安装运行各种环境所需的库,包括控制理论中的经典环境(如在小车上平衡杆子)、基于 Box2D 库的 2D 物理环境以及基于 Arcade Learning Environment(ALE)的 Atari 2600 游戏环境。运行此代码会自动下载几个 Atari 游戏 ROM,并同意 Atari 的 ROM 许可证。

安装完成后,我们可以导入并创建一个环境:

import gym
env = gym.make("CartPole-v1", render_mode="rgb_array")

这里创建了一个 CartPole 环境,这是一个 2D 模拟环境,其中小车可以向左或向右加速以平衡放在其上的杆子,这是一个经典的控制任务。

创建环境后,需要使用 reset() 方法初始化它,并可选择指定随机种子,该方法返回第一个观察值和一个可能包含额外环境特定信息的字典。对于 CartPole 环境,每个观察值是一个包含四个浮点数的 1D NumPy 数组,分别表示小车的水平位置(0.0 表示中心)、速度(正值表示向右)、杆子的角度(0.0 表示垂直)和角速度(正值表示顺时针)。

obs, info = env.reset(seed=42)
print(obs)
print(info)

可以使用 render() 方法将环境渲染为图像,由于创建环境时设置了 render_mode="rgb_array" ,图像将作为 NumPy 数组返回:

img = env.render()
print(img.shape)  # height, width, channels (3 = Red, Green, Blue)

可以使用 Matplotlib 的 imshow() 函数显示此图像。

接下来,我们可以询问环境可能的行动:

print(env.action_space)

Discrete(2) 表示可能的行动是整数 0 和 1,分别代表向左或向右加速。由于杆子向右倾斜( obs[2] > 0 ),我们可以让小车向右加速:

action = 1  # accelerate right
obs, reward, done, truncated, info = env.step(action)
print(obs)
print(reward)
print(done)
print(truncated)
print(info)

step() 方法执行所需的行动并返回五个值:
- obs :新的观察值。
- reward :在这个环境中,无论做什么,每一步都会获得 1.0 的奖励,因此目标是尽可能长时间地保持回合进行。
- done :当回合结束时为 True,例如杆子倾斜过多、移出屏幕或达到 200 步(在最后一种情况下表示获胜)。之后,需要重置环境才能再次使用。
- truncated :当回合提前中断时为 True,例如环境包装器对每回合的最大步数进行限制。在本章中,我们将截断的回合和正常结束的回合视为相同。
- info :这个环境特定的字典可能提供额外信息,与 reset() 方法返回的字典类似。

使用完环境后,应调用 close() 方法释放资源。

为了测试一个简单的策略,我们可以硬编码一个策略,当杆子向左倾斜时向左加速,向右倾斜时向右加速,并运行 500 个回合来查看平均奖励:

def basic_policy(obs):
    angle = obs[2]
    return 0 if angle < 0 else 1

totals = []
for episode in range(500):
    episode_rewards = 0
    obs, info = env.reset(seed=episode)
    for step in range(200):
        action = basic_policy(obs)
        obs, reward, done, truncated, info = env.step(action)
        episode_rewards += reward
        if done or truncated:
            break
    totals.append(episode_rewards)

import numpy as np
print(np.mean(totals), np.std(totals), min(totals), max(totals))

结果显示,即使尝试了 500 次,这个策略也从未使杆子连续直立超过 63 步。接下来,我们将看看神经网络是否能提出更好的策略。

5. 神经网络策略

我们可以创建一个神经网络策略,该神经网络将观察值作为输入,并输出要执行的行动,更准确地说,它会为每个行动估计一个概率,然后根据估计的概率随机选择一个行动。

对于 CartPole 环境,只有两种可能的行动(左或右),因此只需要一个输出神经元。它将输出行动 0(左)的概率 p,行动 1(右)的概率为 1 - p。例如,如果输出 0.7,则以 70% 的概率选择行动 0,以 30% 的概率选择行动 1。

选择基于概率随机选择行动的方法,是为了让智能体在探索新行动和利用已知有效的行动之间找到正确的平衡。这类似于去一家新餐厅,所有菜肴看起来都同样吸引人,随机选择一道菜。如果这道菜好吃,可以增加下次点它的概率,但不应将该概率提高到 100%,否则将永远不会尝试其他可能更好的菜肴。这种探索/利用困境是强化学习的核心问题。

在 CartPole 环境中,由于每个观察值包含环境的完整状态,因此可以安全地忽略过去的行动和观察值。但如果存在隐藏状态,可能需要考虑过去的行动和观察值。例如,如果环境只揭示了小车的位置而不揭示其速度,则需要同时考虑当前观察值和上一个观察值来估计当前速度;或者当观察值有噪声时,通常需要使用过去的几个观察值来估计最可能的当前状态。

综上所述,神经网络策略为强化学习提供了一种更灵活和强大的方式来解决复杂问题,我们将在后续进一步探讨如何训练和优化这种策略。

以下是一个简单的流程图,展示了强化学习的基本流程:

graph LR
    A[智能体] --> B[观察环境]
    B --> C[选择行动]
    C --> D[执行行动]
    D --> E[环境反馈奖励和新状态]
    E --> A

同时,我们可以用表格总结 CartPole 环境的相关信息:
| 项目 | 描述 |
| ---- | ---- |
| 观察值 | 包含四个浮点数的 1D NumPy 数组,分别表示小车的水平位置、速度、杆子的角度和角速度 |
| 行动 | Discrete(2) ,整数 0 和 1 分别代表向左或向右加速 |
| 奖励 | 每一步固定为 1.0,目标是尽可能长时间保持回合进行 |
| 回合结束条件 | 杆子倾斜过多、移出屏幕或达到 200 步 |

强化学习:原理、方法与实践

6. 策略梯度(Policy Gradients)

策略梯度(PG)是一种通过优化策略参数来最大化累积奖励的方法。与之前提到的策略搜索方法不同,策略梯度利用梯度信息来引导参数的更新,使得策略能够朝着获得更高奖励的方向改进。

具体来说,策略梯度的核心思想是通过计算奖励相对于策略参数的梯度,然后沿着梯度的方向调整参数。在强化学习中,我们的目标是最大化智能体在环境中获得的累积奖励,而策略梯度方法通过不断地更新策略参数,使得策略能够更好地适应环境,从而获得更多的奖励。

以之前的 CartPole 环境为例,我们可以使用 TensorFlow 来实现一个简单的策略梯度算法。以下是一个基本的实现步骤:

import tensorflow as tf
import numpy as np
import gym

# 创建 CartPole 环境
env = gym.make("CartPole-v1")

# 定义神经网络策略
model = tf.keras.Sequential([
    tf.keras.layers.Dense(32, activation='relu', input_shape=env.observation_space.shape),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

# 定义优化器
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)

# 定义训练函数
def train_step(observations, actions, rewards):
    with tf.GradientTape() as tape:
        logits = model(observations)
        action_probs = tf.concat([logits, 1 - logits], axis=1)
        action_masks = tf.one_hot(actions, 2)
        log_probs = tf.nn.log_softmax(action_probs)
        selected_log_probs = tf.reduce_sum(action_masks * log_probs, axis=1)
        loss = -tf.reduce_mean(selected_log_probs * rewards)

    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# 训练循环
num_episodes = 500
for episode in range(num_episodes):
    observations = []
    actions = []
    rewards = []

    obs = env.reset()
    done = False
    while not done:
        observations.append(obs)
        logits = model(np.array([obs]))
        action = np.random.choice([0, 1], p=[logits.numpy()[0][0], 1 - logits.numpy()[0][0]])
        actions.append(action)
        obs, reward, done, _ = env.step(action)
        rewards.append(reward)

    observations = np.array(observations)
    actions = np.array(actions)
    rewards = np.array(rewards)

    # 计算折扣奖励
    discounted_rewards = []
    cumulative_reward = 0
    for r in reversed(rewards):
        cumulative_reward = r + 0.99 * cumulative_reward
        discounted_rewards.insert(0, cumulative_reward)
    discounted_rewards = np.array(discounted_rewards)
    discounted_rewards = (discounted_rewards - np.mean(discounted_rewards)) / (np.std(discounted_rewards) + 1e-10)

    train_step(observations, actions, discounted_rewards)

    if episode % 10 == 0:
        print(f"Episode {episode}: Total reward = {np.sum(rewards)}")

env.close()

在上述代码中,我们首先定义了一个简单的神经网络策略,该网络接受环境的观察值作为输入,并输出一个表示向左行动概率的标量。然后,我们使用 Adam 优化器来更新网络的参数。

在训练过程中,我们通过与环境进行交互,收集每一轮的观察值、行动和奖励。在每一轮结束后,我们计算折扣奖励,以考虑未来奖励的影响,并将其作为训练的目标。最后,我们使用策略梯度算法更新网络的参数,使得策略能够朝着获得更高奖励的方向改进。

以下是一个流程图,展示了策略梯度算法的训练过程:

graph LR
    A[初始化策略网络] --> B[与环境交互收集数据]
    B --> C[计算折扣奖励]
    C --> D[计算策略梯度]
    D --> E[更新策略参数]
    E --> B
7. 深度 Q - 网络(Deep Q - Networks)

深度 Q - 网络(DQN)是另一种重要的深度强化学习技术,它结合了深度学习和 Q - 学习的思想。Q - 学习是一种基于值函数的强化学习方法,通过估计每个状态 - 行动对的 Q 值(即预期的累积奖励)来选择最优行动。

DQN 的核心思想是使用一个深度神经网络来近似 Q 值函数。具体来说,DQN 网络接受环境的观察值作为输入,并输出每个可能行动的 Q 值。在选择行动时,智能体通常会选择具有最高 Q 值的行动。

为了训练 DQN 网络,我们使用经验回放(Experience Replay)和目标网络(Target Network)这两个重要的技巧。经验回放通过将智能体的经验(即状态、行动、奖励和下一个状态)存储在一个经验池中,并随机从中采样进行训练,从而打破数据之间的相关性,提高训练的稳定性。目标网络则是一个与主网络结构相同但参数更新较慢的网络,用于计算目标 Q 值,减少训练过程中的波动。

以下是一个简单的 DQN 实现示例:

import tensorflow as tf
import numpy as np
import gym
from collections import deque

# 创建 CartPole 环境
env = gym.make("CartPole-v1")

# 定义 DQN 网络
model = tf.keras.Sequential([
    tf.keras.layers.Dense(32, activation='relu', input_shape=env.observation_space.shape),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(env.action_space.n, activation='linear')
])

# 定义目标网络
target_model = tf.keras.Sequential([
    tf.keras.layers.Dense(32, activation='relu', input_shape=env.observation_space.shape),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(env.action_space.n, activation='linear')
])
target_model.set_weights(model.get_weights())

# 定义优化器和损失函数
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
loss_fn = tf.keras.losses.MeanSquaredError()

# 经验回放池
replay_buffer = deque(maxlen=10000)

# 超参数
gamma = 0.99
epsilon = 1.0
epsilon_min = 0.01
epsilon_decay = 0.995
batch_size = 32

# 训练函数
def train_dqn():
    if len(replay_buffer) < batch_size:
        return

    # 从经验回放池中随机采样
    minibatch = np.random.choice(len(replay_buffer), batch_size, replace=False)
    states, actions, rewards, next_states, dones = zip(*[replay_buffer[i] for i in minibatch])

    states = np.array(states)
    actions = np.array(actions)
    rewards = np.array(rewards)
    next_states = np.array(next_states)
    dones = np.array(dones)

    # 计算目标 Q 值
    target_q_values = target_model.predict(next_states)
    max_target_q_values = np.max(target_q_values, axis=1)
    target_q = rewards + (1 - dones) * gamma * max_target_q_values

    with tf.GradientTape() as tape:
        q_values = model(states)
        one_hot_actions = tf.one_hot(actions, env.action_space.n)
        selected_q_values = tf.reduce_sum(one_hot_actions * q_values, axis=1)
        loss = loss_fn(target_q, selected_q_values)

    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# 训练循环
num_episodes = 500
for episode in range(num_episodes):
    state = env.reset()
    done = False
    total_reward = 0

    while not done:
        if np.random.rand() <= epsilon:
            action = env.action_space.sample()
        else:
            q_values = model.predict(np.array([state]))
            action = np.argmax(q_values)

        next_state, reward, done, _ = env.step(action)
        replay_buffer.append((state, action, reward, next_state, done))

        train_dqn()

        state = next_state
        total_reward += reward

    if epsilon > epsilon_min:
        epsilon *= epsilon_decay

    if episode % 10 == 0:
        target_model.set_weights(model.get_weights())
        print(f"Episode {episode}: Total reward = {total_reward}")

env.close()

在上述代码中,我们首先定义了一个 DQN 网络和一个目标网络。然后,我们使用经验回放池来存储智能体的经验,并在训练时随机从中采样。在每一轮训练中,我们计算目标 Q 值,并使用均方误差损失函数来更新 DQN 网络的参数。

以下是一个表格,总结了 DQN 与策略梯度方法的主要区别:
| 方法 | 核心思想 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| 策略梯度 | 通过优化策略参数来最大化累积奖励 | 可以处理连续动作空间,直接优化策略 | 训练不稳定,收敛速度慢 |
| 深度 Q - 网络 | 使用深度神经网络近似 Q 值函数 | 训练相对稳定,适用于离散动作空间 | 难以处理连续动作空间 |

8. 总结与展望

强化学习作为机器学习领域的一个重要分支,在近年来取得了显著的进展。策略梯度和深度 Q - 网络等深度强化学习技术为解决复杂的决策问题提供了强大的工具。

通过策略梯度方法,我们可以直接优化策略参数,使得智能体能够在环境中获得更高的累积奖励。而深度 Q - 网络则通过近似 Q 值函数,为智能体提供了一种基于值的决策方法。

然而,强化学习仍然面临着许多挑战,如训练的不稳定性、样本效率低、可解释性差等问题。未来的研究方向包括开发更高效的算法、提高样本利用率、增强模型的可解释性以及探索强化学习在更多领域的应用等。

总之,强化学习具有广阔的应用前景和研究价值,随着技术的不断发展,我们有望看到更多令人兴奋的成果。

希望通过本文的介绍,你对强化学习的原理、方法和实践有了更深入的了解。如果你对强化学习感兴趣,可以进一步探索相关的文献和开源代码,进行更深入的学习和实践。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值