一、代码概述
本代码实现了一个基于 ε-Greedy 算法的多臂老虎机实验。通过模拟多个具有不同真实收益均值的老虎机,在一定次数的尝试中,使用 ε-Greedy 策略来决定选择哪个老虎机进行拉动,以平衡探索(尝试不同的老虎机以发现更好的选择)和利用(选择当前认为最好的老虎机以获取最大收益)。最终,通过可视化展示累积平均收益随尝试次数的变化,并输出每个老虎机的估计均值。
二、依赖库
numpy
:用于数值计算,包括生成随机数、数组操作和数学运算等。matplotlib.pyplot
:用于数据可视化,绘制累积平均收益曲线和图表。
三、代码详解
1. 定义老虎机类 Bandit
python
class Bandit:
def __init__(self, true_mean):
"""
初始化老虎机。
:param true_mean: 老虎机的真实均值(收益的期望值)
"""
self.true_mean = true_mean # 老虎机的真实均值
self.estimated_mean = 0 # 对老虎机均值的估计值,初始为0
self.N = 0 # 老虎机被尝试的次数,初始为0
def pull(self):
"""
模拟拉老虎机的过程,返回一个随机收益。
:return: 返回一个服从正态分布的随机值,均值为true_mean,标准差为1
"""
return np.random.randn() + self.true_mean
def update(self, x):
"""
更新对老虎机均值的估计值。
:param x: 本次拉老虎机得到的收益
"""
self.N += 1 # 尝试次数加1
# 更新估计均值:使用增量式更新公式
self.estimated_mean = (1 - 1.0 / self.N) * self.estimated_mean + 1.0 / self.N * x
__init__
方法:初始化老虎机对象,接受一个参数true_mean
表示老虎机的真实收益均值。同时初始化estimated_mean
(估计均值)为 0,N
(尝试次数)为 0。pull
方法:模拟拉老虎机的操作,返回一个随机收益。收益是一个服从正态分布的随机值,均值为老虎机的真实均值true_mean
,标准差为 1(通过np.random.randn()
生成标准正态分布随机数再加上true_mean
实现)。update
方法:根据本次拉老虎机得到的收益x
,更新对老虎机均值的估计值。使用增量式更新公式,随着尝试次数N
的增加,新的收益对估计均值的影响逐渐减小。
2. 运行实验的函数 run_experiment
python
def run_experiment(true_means, N, eps):
"""
运行ε-Greedy算法的实验。
:param true_means: 每个老虎机的真实均值列表
:param N: 总尝试次数
:param eps: ε值,控制探索与利用的比例
:return: 累积平均收益列表和最终的老虎机列表
"""
# 初始化老虎机列表,每个老虎机的真实均值由true_means提供
bandits = [Bandit(m) for m in true_means]
data = np.empty(N) # 用于存储每次尝试的收益
for i in range(N):
# ε-Greedy策略:以概率ε随机选择一个老虎机(探索),否则选择当前估计均值最大的老虎机(利用)
if np.random.random() < eps:
j = np.random.choice(len(bandits)) # 随机选择一个老虎机
else:
j = np.argmax([b.estimated_mean for b in bandits]) # 选择估计均值最大的老虎机
# 拉选中的老虎机,获得收益
x = bandits[j].pull()
# 更新该老虎机的估计均值
bandits[j].update(x)
# 记录本次收益
data[i] = x
# 计算累积平均收益:累积收益除以尝试次数
cumulative_average = np.cumsum(data) / (np.arange(N) + 1)
return cumulative_average, bandits
- 参数说明:
true_means
:一个列表,包含每个老虎机的真实收益均值。N
:总尝试次数,即实验中拉动老虎机的总次数。eps
:ε 值,一个介于 0 和 1 之间的概率值,用于控制探索(随机选择老虎机)和利用(选择当前估计均值最大的老虎机)的比例。
- 函数逻辑:
- 初始化一个老虎机列表
bandits
,每个老虎机的真实均值由true_means
提供。 - 创建一个长度为
N
的空数组data
,用于存储每次尝试的收益。 - 在
N
次尝试的循环中:- 根据 ε-Greedy 策略决定选择哪个老虎机。如果随机生成的一个 0 到 1 之间的数小于
eps
,则随机选择一个老虎机;否则选择当前估计均值最大的老虎机。 - 拉选中的老虎机,获得收益
x
,更新该老虎机的估计均值,并将本次收益记录到data
数组中。
- 根据 ε-Greedy 策略决定选择哪个老虎机。如果随机生成的一个 0 到 1 之间的数小于
- 计算累积平均收益
cumulative_average
,通过对data
数组的累积求和并除以尝试次数(np.arange(N) + 1
)得到。 - 最后返回累积平均收益列表和最终的老虎机列表。
- 初始化一个老虎机列表
3. 参数设置与实验运行
python
# 参数设置
true_means = [1.0, 2.0, 3.0] # 每个老虎机的真实均值
N = 1000 # 总尝试次数
eps = 0.1 # ε值,控制探索与利用的比例
# 运行实验
cumulative_average, bandits = run_experiment(true_means, N, eps)
定义了实验的参数:true_means
为每个老虎机的真实均值列表,N
为总尝试次数,eps
为 ε 值。然后调用run_experiment
函数运行实验,得到累积平均收益列表cumulative_average
和最终的老虎机列表bandits
。
4. 可视化结果
python
# 可视化结果
plt.plot(cumulative_average, label='Cumulative Average Reward') # 绘制累积平均收益曲线
# 绘制最优收益水平线(即真实均值最大的老虎机的收益)
plt.plot(np.ones(N) * true_means[np.argmax(true_means)], label='Optimal Reward')
plt.legend() # 显示图例
plt.xlabel('Number of Trials') # x轴标签
plt.ylabel('Average Reward') # y轴标签
plt.title('ε-Greedy Bandit Algorithm') # 图表标题
plt.show() # 显示图表
使用matplotlib.pyplot
绘制累积平均收益曲线,并绘制一条表示最优收益(真实均值最大的老虎机的收益)的水平线。添加图例、轴标签和图表标题,最后显示图表。
5. 打印每个老虎机的估计均值
python
# 打印每个老虎机的估计均值
for i, bandit in enumerate(bandits):
print(f"Bandit {i+1}: Estimated Mean = {bandit.estimated_mean}")
遍历最终的老虎机列表bandits
,打印每个老虎机的估计均值。
四、注意事项
- 代码中的 ε-Greedy 策略是一种简单的平衡探索与利用的方法,但可能不是最优的。在实际应用中,可以尝试不同的 ε 值或其他更复杂的算法来优化结果。
- 实验结果可能会受到随机因素的影响,每次运行代码得到的具体数值和图表可能会有所不同。
- 代码中假设老虎机的收益服从正态分布且标准差为 1,这是一个简化的模型。在实际场景中,收益分布可能更加复杂,需要根据具体情况进行调整。
完整代码
import numpy as np
import matplotlib.pyplot as plt
# 定义老虎机类
class Bandit:
def __init__(self, true_mean):
"""
初始化老虎机。
:param true_mean: 老虎机的真实均值(收益的期望值)
"""
self.true_mean = true_mean # 老虎机的真实均值
self.estimated_mean = 0 # 对老虎机均值的估计值,初始为0
self.N = 0 # 老虎机被尝试的次数,初始为0
def pull(self):
"""
模拟拉老虎机的过程,返回一个随机收益。
:return: 返回一个服从正态分布的随机值,均值为true_mean,标准差为1
"""
return np.random.randn() + self.true_mean
def update(self, x):
"""
更新对老虎机均值的估计值。
:param x: 本次拉老虎机得到的收益
"""
self.N += 1 # 尝试次数加1
# 更新估计均值:使用增量式更新公式
self.estimated_mean = (1 - 1.0 / self.N) * self.estimated_mean + 1.0 / self.N * x
def run_experiment(true_means, N, eps):
"""
运行ε-Greedy算法的实验。
:param true_means: 每个老虎机的真实均值列表
:param N: 总尝试次数
:param eps: ε值,控制探索与利用的比例
:return: 累积平均收益列表和最终的老虎机列表
"""
# 初始化老虎机列表,每个老虎机的真实均值由true_means提供
bandits = [Bandit(m) for m in true_means]
data = np.empty(N) # 用于存储每次尝试的收益
for i in range(N):
# ε-Greedy策略:以概率ε随机选择一个老虎机(探索),否则选择当前估计均值最大的老虎机(利用)
if np.random.random() < eps:
j = np.random.choice(len(bandits)) # 随机选择一个老虎机
else:
j = np.argmax([b.estimated_mean for b in bandits]) # 选择估计均值最大的老虎机
# 拉选中的老虎机,获得收益
x = bandits[j].pull()
# 更新该老虎机的估计均值
bandits[j].update(x)
# 记录本次收益
data[i] = x
# 计算累积平均收益:累积收益除以尝试次数
cumulative_average = np.cumsum(data) / (np.arange(N) + 1)
return cumulative_average, bandits
# 参数设置
true_means = [1.0, 2.0, 3.0] # 每个老虎机的真实均值
N = 1000 # 总尝试次数
eps = 0.1 # ε值,控制探索与利用的比例
# 运行实验
cumulative_average, bandits = run_experiment(true_means, N, eps)
# 可视化结果
plt.plot(cumulative_average, label='Cumulative Average Reward') # 绘制累积平均收益曲线
# 绘制最优收益水平线(即真实均值最大的老虎机的收益)
plt.plot(np.ones(N) * true_means[np.argmax(true_means)], label='Optimal Reward')
plt.legend() # 显示图例
plt.xlabel('Number of Trials') # x轴标签
plt.ylabel('Average Reward') # y轴标签
plt.title('ε-Greedy Bandit Algorithm') # 图表标题
plt.show() # 显示图表
# 打印每个老虎机的估计均值
for i, bandit in enumerate(bandits):
print(f"Bandit {i+1}: Estimated Mean = {bandit.estimated_mean}")