作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助。这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学习的基础知识,后面也会更新强化学习的论文阅读专栏。本来是想每一篇多更新一点内容的,后面发现大家上CSDN主要是来提问的,就把很多拆分开来了(而且这样每天任务量也小一点哈哈哈哈偷懒大法)。但是我还是希望知识点能成系统,所以我在目录里面都好按章节系统地写的,而且在github上写成了书籍的形式,如果大家觉得有帮助,希望从头看的话欢迎关注我的github啊,谢谢大家!另外我还会分享深度学习-基础知识专栏以及深度学习-论文阅读专栏,很早以前就和小伙伴们花了很多精力写的,如果有对深度学习感兴趣的小伙伴也欢迎大家关注啊。大家一起互相学习啊!可能会有很多错漏,希望大家批评指正!不要高估一年的努力,也不要低估十年的积累,与君共勉!

K臂老虎机介绍及其Python实现

如果大家想对K臂老虎机做一个比较深入的了解的话,建议大家去阅读这篇博客,作者写的挺清楚的,而且还推荐了很多的其他材料,我这里主要是对K臂老虎机做一个简要的介绍。

定义

K臂老虎机(Multi-armed bandit,简称MAB)最早的场景是在赌场里面。赌场里面有K台老虎机,每次去摇老虎机都需要一个代币,且老虎机都会以一定概率吐出钱,你手上如果有T个代币,也就是你一共可以摇T次,你怎么才能使你的期望回报最大?当然我们要先假设每个老虎机吐钱的概率是不一样的,不然你怎么摇就都是一样的了。

我们一般也将所有的下面这种形式的问题成为K臂老虎机问题: 你可以反复面对 k 种不同的选择或行动。在每次选择之后,你会收到一个数值奖励,该奖励取决于你选择的行动的固定概率分布。 你的目标是在一段时间内最大化预期的总奖励。

如果我们是贝叶斯人,我们在实际对老虎机进行操作之前其实对老虎机吐钱的概率就已经有了一个先验的分布,然后我们不断地进行试验,根据试验的结果去调整我们前面的分布;而如果我们是频率学家,那我们一开始对这些机器吐钱的概率其实是没有先验的,我们会通过实验去预测出每台机器吐钱的概率,然后根据这个概率去不断优化我们的决策。

但不管从哪种角度出发,K臂老虎机的问题其实就是一个探索与利用的问题,就比如说我们先进行了m次实验(m<T),发现了第一个臂吐钱的频率更高,那接下来我们是一直去摇第一个臂(利用:exploitation)还是说我们还去试着摇一下其他的臂(探索:exploration),从短期来看利用是好的,但是从长期来看探索是好的。

基本概念

在我们的K臂老虎机中,只要选择了该动作, kkk 个动作的每一个都有预期的或平均的奖励, 让我们称之为该行动的价值。我们将在时间步 ttt 选择的动作表示为 At,A_{t},At​, 并将相应的奖励表示为 Rt∘R_{t_{\circ}}Rt∘​​ 然 后, 对于任意动作 aaa 的价值, 定义 q∗(a)q_{*}(a)q∗​(a) 是给定 aaa 选择的预期奖励:
q∗(a)≐E[Rt∣At=a]q_{*}(a) \doteq \mathbb{E}\left[R_{t} \mid A_{t}=a\right] q∗​(a)≐E[Rt​∣At​=a]
如果我们知道每个动作的价值, 那么解决 K臂老虎机将是轻而易举的:你总是选择具有最高价值 的动作。但是我们不知道实际动作价值, 尽管你可能有估计值。 我们将在时间步骤ttt 的动作 aaa 的估计值表示为 Qt(a)Q_{t}(a)Qt​(a) 。 我们希望 Qt(a)Q_{t}(a)Qt​(a) 接近 q∗(a)q_{*}(a)q∗​(a) 。

K臂老虎机的变种

我们在上面定义中介绍的K臂老虎机其实是最简单的一种场景,K臂老虎机还有很多其他的变形:

  • 如果那些臂的吐钱的概率分布在一开始就设定好了,而且之后不再改变,则称为oblivious adversary setting。
  • 如果那些臂吐钱的概率设定好了之后还会发生变化,那么称为adaptive adversary setting。
  • 如果把待推荐的商品作为MAB问题的arm,那么我们在推荐系统中我们就还需要考虑用户作为一个活生生的个体本身的兴趣点、偏好、购买力等因素都是不同的,也就是我们需要考虑同一臂在不同上下文中是不同的。
  • 如果每台老虎机每天摇的次数有上限,那我们就得到了一个Bandit with Knapsack问题。

greedy和ϵ−greedy\epsilon-greedyϵ−greedy

greedy(贪婪)的算法也就是选择具有最高估计值的动作之一:At=argmax⁡aQt(a)A_{t}=\underset{a}{\operatorname{argmax}} Q_{t}(a)At​=aargmax​Qt​(a),也就是相当于我们只做exploitation ; 而ϵ−greedy\epsilon-greedyϵ−greedy以较小的概率 ϵ\epsilonϵ地从具有相同概率的所有动作中随机选择, 相当于我们在做exploitation的同时也做一定程度的exploration。greedy的算法很容易陷入执行次优动作的怪圈,当reward的方差更大时,我们为了做更多的探索应该选择探索度更大的ϵ−greedy\epsilon-greedyϵ−greedy,但是当reward的方差很小时,我们可以选择更greedy的方法,在实际当中我们很多时候都会让ϵ\epsilonϵ 从一个较大的值降低到一个较小的值,比如说从1降低到0.1,相当于我们在前期基本上只做探索,后期只做利用。

  • Trade-off between exploration and exploitation

  • ϵ\epsilonϵ -Greedy Exploration: Ensuring continual exploration

    • Q All actions are tried with non-zero probability
    • With probability 1−ϵ1-\epsilon1−ϵ choose the greedy action
    • B With probability ϵ\epsilonϵ choose an action at random

π(a∣s)={ϵ/∣A∣+1−ϵif a∗=arg⁡max⁡a∈AQ(s,a)ϵ/∣A∣otherwise \pi(a \mid s)=\left\{\begin{array}{ll} \epsilon /|\mathcal{A}|+1-\epsilon & \text { if } a^{*}=\arg \max _{a \in \mathcal{A}} Q(s, a) \\ \epsilon /|\mathcal{A}| & \text { otherwise } \end{array}\right. π(a∣s)={ϵ/∣A∣+1−ϵϵ/∣A∣​ if a∗=argmaxa∈A​Q(s,a) otherwise ​

Policy improvement theorem: For any ϵ\epsilonϵ -greedy policy π,\pi,π, the ϵ\epsilonϵ -greedy policy π′\pi^{\prime}π′ with respect to qπq_{\pi}qπ​ is an improvement, vπ′(s)≥vπ(s)v_{\pi^{\prime}}(s) \geq v_{\pi}(s)vπ′​(s)≥vπ​(s)
qπ(s,π′(s))=∑a∈Aπ′(a∣s)qπ(s,a)=ϵ∣A∣∑a∈Aqπ(s,a)+(1−ϵ)max⁡aqπ(s,a)≥ϵ∣A∣∑a∈Aqπ(s,a)+(1−ϵ)∑a∈Aπ(a∣s)−ϵ∣A∣1−ϵqπ(s,a)=∑a∈Aπ(a∣s)qπ(s,a)=vπ(s)\begin{aligned} q_{\pi}\left(s, \pi^{\prime}(s)\right) &=\sum_{a \in \mathcal{A}} \pi^{\prime}(a \mid s) q_{\pi}(s, a) \\ &=\frac{\epsilon}{|\mathcal{A}|} \sum_{a \in \mathcal{A}} q_{\pi}(s, a)+(1-\epsilon) \max _{a} q_{\pi}(s, a) \\ & \geq \frac{\epsilon}{|\mathcal{A}|} \sum_{a \in \mathcal{A}} q_{\pi}(s, a)+(1-\epsilon) \sum_{a \in \mathcal{A}} \frac{\pi(a \mid s)-\frac{\epsilon}{|\mathcal{A}|}}{1-\epsilon} q_{\pi}(s, a) \\ &=\sum_{a \in \mathcal{A}} \pi(a \mid s) q_{\pi}(s, a)=v_{\pi}(s) \end{aligned} qπ​(s,π′(s))​=a∈A∑​π′(a∣s)qπ​(s,a)=∣A∣ϵ​a∈A∑​qπ​(s,a)+(1−ϵ)amax​qπ​(s,a)≥∣A∣ϵ​a∈A∑​qπ​(s,a)+(1−ϵ)a∈A∑​1−ϵπ(a∣s)−∣A∣ϵ​​qπ​(s,a)=a∈A∑​π(a∣s)qπ​(s,a)=vπ​(s)​
Therefore, vπ′(s)≥vπ(s)v_{\pi^{\prime}}(s) \geq v_{\pi}(s)vπ′​(s)≥vπ​(s) from the policy improvement theorem

softmax 方法

softamx是另一种兼顾探索与利用的方法,它既不像greedy算法那样贪婪,也没有像ϵ−\epsilon-ϵ− greedy那样在探索阶段做随机动作而是使用 softmax函数计算每一个arm被选中的概率,以更高的概率去摇下平均收益高的臂,以更地的概率去摇下平均收益低的臂。 armia r m_{i}armi​ 表示第i 个手柄, Ui\quad U_{i}Ui​ 表示手柄的平均收益, k是手柄总数。
p(armi)=eui∑jkeuip\left(a r m_{i}\right)=\frac{e^{u_{i}}}{\sum_{j}^{k} e^{u_{i}}} p(armi​)=∑jk​eui​eui​​
当然这里有一个问题是为什么要用softmax,我们直接用某一个臂得到的平均收益除以总的平均收益不行吗?我理解上感觉softmax方法是在agrmax方法和直接除这种方法之间的方法,因为softmax加上e之后其实会让平均收益低的臂和平均收益高的臂走向极端,也就是让策略越来越激进,甚至到最终收敛成argmax?而且我感觉图像分类里面经常用softmax一方面是因为求梯度比较好计算,另一方面是因为有时候softmax之前得到的分数可能有负数,那我们这里的好处是我们可以在刚开始某个壁的奖励是0的时候,我们依旧会有一定概率选它而不会像下面公式里面的这种一样不选它。
p(armi)=ui∑jkuip\left(a r m_{i}\right)=\frac{{u_{i}}}{\sum_{j}^{k} {u_{i}}} p(armi​)=∑jk​ui​ui​​
所以总的来说softmax有三个好处:

  • 便于求梯度
  • 在刚开始某一个臂收益为0的时候这个臂依旧有被选上的可能
  • softmax算法让平均收益低的臂和平均收益高的臂走向极端,也就是让策略越来越激进,甚至到最终收敛成argmax,就有点像ϵ−greedy\epsilon-greedyϵ−greedy中ϵ\epsilonϵ不断下降。

一个简单的赌博机算法

循环的最后一步其实用到了

​ Qn+1=1n∑i=1nRi=1n(Rn+∑i=1n−1Ri)=1n(Rn+(n−1)1n−1∑i=1n−1Ri)=1n(Rn+(n−1)Qn)=1n(Rn+nQn−Qn)=Qn+1n(Rn−Qn)\begin{aligned} Q_{n+1} &=\frac{1}{n} \sum_{i=1}^{n} R_{i} \\ &=\frac{1}{n}\left(R_{n}+\sum_{i=1}^{n-1} R_{i}\right) \\ &=\frac{1}{n}\left(R_{n}+(n-1) \frac{1}{n-1} \sum_{i=1}^{n-1} R_{i}\right) \\ &=\frac{1}{n}\left(R_{n}+(n-1) Q_{n}\right) \\ &=\frac{1}{n}\left(R_{n}+n Q_{n}-Q_{n}\right) \\ &=Q_{n}+\frac{1}{n}\left(R_{n}-Q_{n}\right) \end{aligned}Qn+1​​=n1​i=1∑n​Ri​=n1​(Rn​+i=1∑n−1​Ri​)=n1​(Rn​+(n−1)n−11​i=1∑n−1​Ri​)=n1​(Rn​+(n−1)Qn​)=n1​(Rn​+nQn​−Qn​)=Qn​+n1​(Rn​−Qn​)​

也就是:新估计←旧估计+步长[目标−旧估计]

Python 代码实现

在代码里面实现了ϵ−greedy\epsilon-greedyϵ−greedy、softmax,以及直接根据当前各个臂的平均收益去决策三种方法,完整的代码放在github上了,写的比较匆忙,后面会再更新一下放到github的仓库之中

# 作者:Yunhui
# 创建时间:2021/1/13 23:54
# IDE:PyCharm
# encoding: utf-8import random
import math
import numpy as np
import matplotlib.pyplot as pltARM_NUM = 5
E_GREEDY_FACTOR = 0.9
SEED = random.randint(1, 10000)
TEST_STEPS = 1000class MAB:def __init__(self, arm_num: int) -> None:""":param arm_num:  the number of arms  臂的数量"""self.arm_num = arm_num    # 设置臂的数量self.probability = dict({})  # 设置每个臂能摇出一块钱的概率self.try_time = dict({})  # 每个臂已经摇过的次数self.reward = dict({})  # 每个臂已经获得的钱self.reward_all = 0  # 所有臂获得的收益之和self.try_time_all = 0  # 总的尝试的次数def reset(self, seed: int) -> None:"""Each arm is initialized, and each arm is set the same when passing in the same random seed对每一个臂进行初始化,当传入的随机种子一样时,每个臂的设置相同:param seed: random seed  传入的随机种子"""print("We have %d arms" % self.arm_num)for num in range(self.arm_num):random.seed(num+seed)self.probability[str(num + 1)] = random.random()self.try_time[str(num + 1)] = 0self.reward[str(num + 1)] = 0def step(self, arm_id: str):"""Change the arm according to the arm_id当传入每次要摇下的臂的编号后,老虎机的状态发生变化:param arm_id: the id of the arm in this step 这一步控制摇下杆的id"""self.try_time[arm_id] += 1self.try_time_all += 1if random.random() < self.probability[arm_id]:self.reward[arm_id] += 1self.reward_all += 1def render(self):"""draw the multi-armed bandit,including tried times and rewardfor each arm, and total tried times and rewards."""if self.arm_num <= 10:print('*' * 8 * (self.arm_num + 1) + '**')title = str(self.arm_num) + " arm bandit"title_format = '{:^' + str(8 * (self.arm_num + 1)) + 's}'print('*' + title_format.format(title) + '*')print('*' + ' ' * 8 * (self.arm_num + 1) + '*')print('*{:^8s}'.format('arm'), end='')for arm in range(self.arm_num):print('{:^8d}'.format(arm + 1), end='')print('*\n')print('*{:^8s}'.format('tried'), end='')for arm in range(self.arm_num):print('{:^8d}'.format(self.try_time[str(arm + 1)]), end='')print('*\n')print('*{:^8s}'.format('reward'), end='')for arm in range(self.arm_num):print('{:^8d}'.format(self.reward[str(arm + 1)]), end='')print('*\n')print('*' + title_format.format("total tried:" + str(self.try_time_all)) + '*')print('*' + title_format.format("total rewards:" + str(self.reward_all)) + '*')print('*' + ' ' * 8 * (self.arm_num + 1) + '*')print('*' * 8 * (self.arm_num + 1) + '**')def e_greedy_method(mab):"""e greedy method: define a e_greedy_factor and create a random number,when the random number is less then e_greedy_factor, then pick a armrandomly, else pick the arm with argmax q_table.:param mab: the class MBA:return: selected arm_id"""q_table = []for arm_num in range(mab.arm_num):if mab.try_time[str(arm_num+1)] != 0:q_table.append(mab.reward[str(arm_num+1)]/mab.try_time[str(arm_num+1)])else:q_table.append(0)if random.random() < E_GREEDY_FACTOR:arm_id = random.randint(1, mab.arm_num)else:arm_id = np.argmax(q_table) + 1return arm_iddef sofmax_method(mab):"""softmax method: calculate the softmax value of each arm's avarage reward,and pick the arm with greatest softmax value.:param mab: the class MBA:return: selected arm_id"""exp_sum = 0softmax_list = []for arm_num in range(mab.arm_num):if mab.try_time[str(arm_num+1)] > 0:exp_sum += math.exp(mab.reward[str(arm_num+1)] / mab.try_time[str(arm_num+1)])else:exp_sum += math.exp(0)assert exp_sum > 0for arm_num in range(mab.arm_num):if mab.try_time[str(arm_num+1)] == 0:avg_reward_temp = 0else:avg_reward_temp = mab.reward[str(arm_num+1)] / mab.try_time[str(arm_num+1)]softmax_list.append(math.exp(avg_reward_temp) / exp_sum)arm_id = np.random.choice(mab.arm_num, 1, p=softmax_list)[0]print("The softmax list is", softmax_list)print("The id of returned arm is ", arm_id+1)return arm_id + 1def average_method(mab):"""decide the arm_id according to the average return of each arm but don't do the math.exp() operation like softmax:param mab: the class MBA:return: selected arm_id"""sum_average = 0softmax_list = []for arm_num in range(mab.arm_num):if mab.try_time[str(arm_num + 1)] > 0:sum_average += (mab.reward[str(arm_num + 1)] / mab.try_time[str(arm_num + 1)])else:sum_average += 0if sum_average == 0:arm_id = np.random.choice(mab.arm_num) + 1else:for arm_num in range(mab.arm_num):if mab.try_time[str(arm_num + 1)] == 0:avg_reward_temp = 0else:avg_reward_temp = mab.reward[str(arm_num + 1)] / mab.try_time[str(arm_num + 1)]softmax_list.append(avg_reward_temp / sum_average)arm_id = np.random.choice(mab.arm_num, 1, p=softmax_list)[0]print("The softmax list is", softmax_list)print("The id of returned arm is ", arm_id + 1)return arm_id + 1if __name__ == '__main__':reward_list = []mab_test = MAB(ARM_NUM)print("****Multi-armed Bandit***")mab_test.reset(SEED)mab_test.render()for i in range(TEST_STEPS):mab_test.step(str(e_greedy_method(mab_test)))reward_list.append(mab_test.reward_all/mab_test.try_time_all)if (i+1) % 20 == 0:print("We have test for %i times" % (i+1))mab_test.render()plt.plot(reward_list)plt.show()

上一篇:强化学习的学习之路(九)_2021-01-09:强化学习中的MDP

下一篇:强化学习的学习之路(十一)_2021-01-11 :贝尔曼方程

强化学习的学习之路(十)_2021-01-10:K臂老虎机介绍及其Python实现相关推荐

  1. 强化学习笔记:多臂老虎机问题(2)--Python仿真

    目录 0. 前言 1. k_armed_bandit function 2. The first trial 2.1 Optimal selection ratio along the time 2. ...

  2. 线性代数学习笔记——行列式的性质及拉普拉斯定理——10. k阶子式、余子式、代数余子式

    1. k阶子式.余子式.代数余子式的定义 2. k阶子式.余子式.代数余子式的示例

  3. 强化学习笔记:多臂老虎机问题(1)

    目录 1. 前言 2. 多臂老虎机问题描述 3. Conlict between exploitation and exploring 4. Action-Value method, 行动-价值方法 ...

  4. 从多臂老虎机开始学习强化学习中的探索与利用

    从多臂老虎机开始学习强化学习中的探索与利用 \quad 目录 从多臂老虎机开始学习强化学习中的探索与利用 多臂老虎机问题 形式化描述 估计期望奖励 代码实现 策略中的探索与利用 ϵ\epsilonϵ- ...

  5. 强化学习笔记:多臂老虎机问题(4)--跟踪非平稳环境

    目录 0. 前言 1. 问题描述 2. 练习1 3. 练习2 3.1 k_armed_bandit_one_run()接口扩张 3.2 Comparison in stationary environ ...

  6. 强化学习的学习之路(四十四)2021-02-13 Monotonic Improvement with KL Divergence

    作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助.这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学 ...

  7. 强化学习的学习之路(四十六)2021-02-15自然梯度法实现策略上的单调提升(Monotonic Improvement with Natural gradient descent)

    作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助.这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学 ...

  8. 强化学习的学习之路(五十一)2021-02-20 Retrace

    作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助.这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学 ...

  9. 强化学习入坑之路04

    强化学习入坑之路04 今天继续强化学习的学习,这些内容的学习主要参考了李宏毅的强化学习教程,在此基础上加入了个人的理解和总结.好,废话少说下面开始进入正题. 1.Q-learning(DQN) Sta ...

最新文章

  1. 跨平台PHP调试器设计及使用方法——界面设计和实现
  2. Expandable Table的Demo
  3. [转载]关于request和session详解
  4. MPDU 和 MSDU 的区别及关系
  5. VTK:InfoVis之TreeMapView
  6. python创建和控制的实体称为_Python语法基础
  7. MySQL笔记-死锁原理与分析及InnoDB中如何减少死锁
  8. python等待用户输入指定秒_如何在10秒后强制用户输入
  9. WebSocket 协议
  10. C#l操作Exce知识点
  11. 《Windows程序设计》复习题
  12. Adobe Flash CS6 下载与安装教程
  13. java max 函数_Java Math max()用法及代码示例
  14. excel linux时间戳转换成日期,Excel将Unix时间戳转换为日期
  15. 从已知身份证号码中提取生日和性别
  16. Hive中变量的使用
  17. tableau 常用函数整理
  18. 王垠 java,王垠,40行代码,JAVA吧的大神怎么评论?
  19. html中input type什么意思,HTML中type是什么意思
  20. 在html中加动画效果,教你如何在网页上用H5实现动画效果

热门文章

  1. Ubuntu 14.04安装配置Calamari
  2. mysql nlssort_nlssort排序
  3. 中国程序员开发的远程桌面火了!Mac可用,只有9MB,支持自建中继器
  4. element ui table组件筛选数据
  5. dell加装固态硬盘_[图解]戴尔灵越15R 5537怎么更换加装固态硬盘?
  6. linux基础命令之一
  7. No1.初来乍到,请多指教
  8. 2019年1月1日之后 你能少缴纳多少个税
  9. 高速ADC的关键指标:量化误差、offset/gain error、DNL、INL、ENOB、分辨率、RMS、SFDR、THD、SINAD、dBFS、TWO-TONE IMD
  10. Lambda 表达式