933 字
5 分钟
强化学习环境/多臂老虎机
前言
本博客参考学习张伟楠老师的动手学强化学习 代码来自相关教程,本人仅做一些注释,部分由于解释器抽风导致的警告修改,还有一些年久失修的库的替换。
多臂老虎机
2.2.4 估计期望奖励
import gymnasium as gymimport numpy as np
# 初始化环境env = gym.make("CartPole-v1")state, _ = env.reset()total_reward = 0
# 智能体与环境交互for _ in range(100): action = env.action_space.sample() # 随机动作 next_state, reward, done, truncated, info = env.step(action) total_reward += float(reward) state = next_state if done or truncated: break
print(f"总奖励:{total_reward}")env.close()# 导入需要使用的库,其中numpy是支持数组和矩阵运算的科学计算库,而matplotlib是绘图库import numpy as npimport matplotlib.pyplot as plt
class BernoulliBandit: """ 伯努利多臂老虎机,输入K表示拉杆个数 """ def __init__(self, K:int): self.probs = np.random.uniform(size=K) # 随机生成K个0~1的数,作为拉动每根拉杆的获奖概率 # 概率 self.best_idx = np.argmax(self.probs) # 获奖概率最大的拉杆 self.best_prob = self.probs[self.best_idx] # 最大的获奖概率 self.K = K
def step(self, k): # 当玩家选择了k号拉杆后,根据拉动该老虎机的k号拉杆获得奖励的概率返回1(获奖)或0(未 # 获奖) if np.random.rand() < self.probs[k]: return 1 else: return 0
np.random.seed(1) # 设定随机种子,使实验具有可重复性K = 10bandit_10_arm = BernoulliBandit(K)print("随机生成了一个%d臂伯努利老虎机" % K)print("获奖概率最大的拉杆为%d号,其获奖概率为%.4f" % (bandit_10_arm.best_idx, bandit_10_arm.best_prob))class Solver: """ 多臂老虎机算法基本框架 """ def __init__(self, bandit): #bandit:BernoulliBandit 的实例 self.bandit = bandit self.counts = np.zeros(self.bandit.K) # 每根拉杆的尝试次数 self.regret = 0. # 当前步的累积懊悔 self.actions = [] # 维护一个列表,记录每一步的动作 self.regrets = [] # 维护一个列表,记录每一步的累积懊悔
def update_regret(self, k): # 计算累积懊悔并保存,k为本次动作选择的拉杆的编号 self.regret += self.bandit.best_prob - self.bandit.probs[k] self.regrets.append(self.regret)
def run_one_step(self): # 返回当前动作选择哪一根拉杆,由每个具体的策略实现 raise NotImplementedError
def run(self, num_steps): # 运行一定次数,num_steps为总运行次数 for _ in range(num_steps): k = self.run_one_step() self.counts[k] += 1 self.actions.append(k) self.update_regret(k)2.4 ϵ-贪心算法
class EpsilonGreedy(Solver): """ epsilon贪婪算法,继承Solver类 """ def __init__(self, bandit, epsilon=0.01, init_prob=1.0): super(EpsilonGreedy, self).__init__(bandit) self.epsilon = epsilon #初始化拉动所有拉杆的期望奖励估值 self.estimates = np.array([init_prob] * self.bandit.K)
def run_one_step(self): if np.random.random() < self.epsilon: k = np.random.randint(0, self.bandit.K) # 随机选择一根拉杆 随机抽取[0,K]的一个整数 else: k = np.argmax(self.estimates) # 选择期望奖励估值最大的拉杆 r = self.bandit.step(k) # 得到本次动作的奖励 ''' Q_old:self.estimates[k] N:self.counts[k] + 1(本次算进去后的第 N 次) r:这次奖励 r Q_new=Q_old+1/N(r-Q_old) ''' self.estimates[k] += 1. / (self.counts[k] + 1) * (r - self.estimates[k]) return kdef plot_results(solvers, solver_names): """生成累积懊悔随时间变化的图像。输入solvers是一个列表,列表中的每个元素是一种特定的策略。 而solver_names也是一个列表,存储每个策略的名称""" for idx, solver in enumerate(solvers): time_list = range(len(solver.regrets)) plt.plot(time_list, solver.regrets, label=solver_names[idx]) plt.xlabel('Time steps') plt.ylabel('Cumulative regrets') plt.title('%d-armed bandit' % solvers[0].bandit.K) plt.legend() plt.show()
np.random.seed(1)epsilon_greedy_solver = EpsilonGreedy(bandit_10_arm, epsilon=0.01)epsilon_greedy_solver.run(5000)print('epsilon-贪婪算法的累积懊悔为:', epsilon_greedy_solver.regret)plot_results([epsilon_greedy_solver], ["EpsilonGreedy"])np.random.seed(0)epsilons = [1e-4, 0.01, 0.1, 0.25, 0.5]epsilon_greedy_solver_list = [ EpsilonGreedy(bandit_10_arm, epsilon=e) for e in epsilons]epsilon_greedy_solver_names = ["epsilon={}".format(e) for e in epsilons]for solver in epsilon_greedy_solver_list: solver.run(5000)
plot_results(epsilon_greedy_solver_list, epsilon_greedy_solver_names)碎碎念:这本书是真不错,虽然我们没买实体书,但pdf,电子书都蛮好找的,而且学习官网竟然贴着现成的代码,实验课难度大大降低,而且大部分都不用改动(毕竟强化学习也比较新吧)。虽然但是吧,本人数学一坨,与数学有关的天书部分是真难搞。
内容
评论区
期待你的第一条评论