目录
多臂老虎机(Multi-Armed Bandit,MAB)问题是强化学习的经典入门例子,也是理解探索与利用(exploration-exploitation)平衡的重要案例。
什么是多臂老虎机?
设想你在一个赌场里,面前有一排老虎机,每台老虎机都有一个不同的中奖概率。你可以选择拉任意一个老虎机的“手臂”,但你不知道每台机器的中奖概率是多少。你的目标是在有限的操作次数内,最大化你的收益。
每台老虎机(也称“臂”)对应一种概率分布,投币后会产生不同的奖励(例如赢得不同数量的筹码),但每台机器的奖励分布是固定的。问题的核心是如何选择拉哪台老虎机的手臂,以便在不确定每台机器的中奖概率的情况下获得最高收益。
强化学习中的多臂老虎机问题
多臂老虎机问题在强化学习中是“探索与利用”平衡的基础案例。你面临着两个选择:
- 探索:选择不熟悉的手臂,以获取更多的信息。
- 利用:选择已知奖励较高的手臂,以获取更大的即时收益。
探索过多会浪费资源,而利用过多则可能错过更优的选项。多臂老虎机问题提供了一个简单的框架来研究和测试不同的探索与利用策略。
解决多臂老虎机问题的常见方法
以下是几种常见的解决策略:
-
贪心策略(Greedy)
只选择当前预期奖励最高的臂。缺点是完全不探索,可能会错过最优臂。 -
ε-贪心策略(ε-Greedy)
在大部分时间(概率为 1−ϵ )选择当前最好的臂,但偶尔(概率为 ϵ )随机探索其他臂。通常,ϵ 会设为一个小值,比如 0.1,以平衡探索与利用。 -
上置信界(UCB,Upper Confidence Bound)
基于统计学的原理,每个臂的奖励期望值上加上一个置信区间。随着臂的拉动次数增加,置信区间逐渐减小,最终可以更可靠地选择最优臂。UCB能在拉得较少的臂上探索更多次,从而加快找到最优臂的速度。 -
汤普森采样(Thompson Sampling)
使用贝叶斯方法来选择臂。每次尝试时,依据历史奖励数据生成不同臂的奖励概率分布,然后从中采样选择最优臂。汤普森采样在很多情况下表现优于 ε-贪心和 UCB,尤其是分布已知的情况。
强化学习中的重要性
多臂老虎机问题帮助我们了解如何在有不确定性和有限资源的情况下选择行动,这种探索与利用的平衡思想在更复杂的强化学习任务中非常重要。例如,在训练深度 Q 网络(DQN)或策略梯度(Policy Gradient)算法时,探索与利用的权衡直接影响收敛速度和策略的优劣。
通过多臂老虎机问题,初学者可以更直观地理解强化学习中的策略选择与优化方式,为进一步学习复杂的环境奠定基础。
代码实现
以下是这些策略的Python实现。首先,我们定义多臂老虎机环境,然后实现不同策略的代理。
import numpy as np
class MultiArmedBandit:
def __init__(self, num_arms):
# 每个老虎机臂的奖励分布是正态分布,均值在0到1之间,标准差为1
self.probabilities = np.random.rand(num_arms)
self.num_arms = num_arms
def pull_arm(self, arm):
# 模拟拉动臂的过程,返回一个奖励
return np.random.normal(self.probabilities[arm], 1)
1. 贪心策略
class GreedyAgent:
def __init__(self, num_arms):
self.num_arms = num_arms
self.arm_counts = np.zeros(num_arms) # 每个臂的拉动次数
self.arm_rewards = np.zeros(num_arms) # 每个臂的累计奖励
def select_arm(self):
# 选择当前期望奖励最高的臂
return np.argmax(self.arm_rewards / (self.arm_counts + 1e-5))
def update(self, arm, reward):
# 更新该臂的奖励和次数
self.arm_counts[arm] += 1
self.arm_rewards[arm] += reward
2. ε-贪心策略
class EpsilonGreedyAgent:
def __init__(self, num_arms, epsilon=0.1):
self.num_arms = num_arms
self.epsilon = epsilon
self.arm_counts = np.zeros(num_arms)
self.arm_rewards = np.zeros(num_arms)
def select_arm(self):
if np.random.rand() < self.epsilon:
# 以 ε 概率随机选择一个臂
return np.random.randint(self.num_arms)
else:
# 否则选择期望奖励最高的臂
return np.argmax(self.arm_rewards / (self.arm_counts + 1e-5))
def update(self, arm, reward):
self.arm_counts[arm] += 1
self.arm_rewards[arm] += reward
3. 上置信界(UCB)
class UCBAgent:
def __init__(self, num_arms, c=2):
self.num_arms = num_arms
self.arm_counts = np.zeros(num_arms)
self.arm_rewards = np.zeros(num_arms)
self.c = c # 控制置信区间大小的参数
def select_arm(self):
total_counts = np.sum(self.arm_counts)
if total_counts == 0:
return np.random.randint(self.num_arms)
ucb_values = self.arm_rewards / (self.arm_counts + 1e-5) + \
self.c * np.sqrt(np.log(total_counts + 1) / (self.arm_counts + 1e-5))
return np.argmax(ucb_values)
def update(self, arm, reward):
self.arm_counts[arm] += 1
self.arm_rewards[arm] += reward
4. 汤普森采样(Thompson Sampling)
class ThompsonSamplingAgent:
def __init__(self, num_arms):
self.num_arms = num_arms
self.successes = np.zeros(num_arms) # 记录每个臂成功的次数
self.failures = np.zeros(num_arms) # 记录每个臂失败的次数
def select_arm(self):
# 对每个臂从 beta 分布中采样
samples = [np.random.beta(1 + self.successes[arm], 1 + self.failures[arm]) for arm in range(self.num_arms)]
return np.argmax(samples)
def update(self, arm, reward):
# 更新成功或失败的次数
if reward > 0:
self.successes[arm] += 1
else:
self.failures[arm] += 1
运行模拟实验
# 定义老虎机数量和步骤
num_arms = 10
num_steps = 1000
bandit = MultiArmedBandit(num_arms)
# 选择一个策略,初始化相应的Agent
agent = EpsilonGreedyAgent(num_arms, epsilon=0.1) # 可以换成其他策略
# 记录奖励
rewards = []
for step in range(num_steps):
arm = agent.select_arm()
reward = bandit.pull_arm(arm)
agent.update(arm, reward)
rewards.append(reward)
# 计算平均奖励
average_reward = np.mean(rewards)
print(f"平均奖励: {average_reward:.2f}")
结果与分析
不同策略的平均奖励可能不同。一般来说:
- ε-贪心策略在长时间内能获得较高奖励。
- UCB 和汤普森采样往往能更快找到最优臂,尤其在前期有较明显优势。
实验可以帮助直观地理解这些策略的特点,以及如何在不确定环境中平衡探索与利用。
标签:入门,self,arms,多臂,num,np,arm,老虎机 From: https://blog.csdn.net/qq_56683019/article/details/143466997