K-摇臂***算法与实现
问题描述
有 个***,每个***有一定概率
吐出硬币,但是我们不知道这个概率是多少,每个***吐出的硬币价值
也是不一样的,现在有
次机会选择***,怎么选才能使得到的硬币总价值最大?
在下面的不同算法实现中,统一设定
可以计算出,这种情况下:
- 如果每次都选期望价值最高的4号***,可以获得的最高总价值为2800000。
- 如果每次都选期望价值最低的2号***,可以获得的最低总价值为300000。
- 如果随机选取***,可以获得的期望总价值为1540000。
探索与利用算法
原理
“仅探索”(exploration-only)算法就是将机会平均分配给每一个***,随机挑选***。
“仅利用”(exploitation-only)算法就是选取当前平均价值最高的那台***。
但是由于尝试的次数有限,所以要在探索与利用之间进行权衡,这也是强化学习面临的一大问题:探索-利用窘境(Exploration-Exploitation dilemma)。
实现代码
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import random
import numpy as np
def R(k, P, V):
if random.random() < P[k]:
return V[k]
else:
return 0
def exploration_bandit(K, P, V, R, T):
r = 0
for t in range(T):
k = random.randint(0, K - 1)
v = R(k, P, V)
r += v
return r
def main():
K = 5
P = np.array([0.1, 0.9, 0.3, 0.2, 0.7])
V = np.array([5, 3, 1, 7, 4])
T = 1000000
print exploration_bandit(K, P, V, R, T)
if __name__ == '__main__':
main()
代码运行结果为:获得总价值1538893。这里只实现了仅探索方法,结果和预估的情况3还是比较接近的。
贪心算法
原理
这个方法原理就是每次以 的概率来探索,即以均匀概率随机挑选一个摇臂。以
的概率利用,即挑选当前平均价值最高的那个摇臂。
剩下的问题就是如何维护平均价值最高的摇臂了,对于做过算法竞赛的人来说,这都是小菜一碟。
用 记录第
个摇臂在第
次尝试之后的平均价值。那么最高效的算法就是根据
来直接推导出
,如果第
次尝试获得的价值为
,那么
可以更新为:
实现代码
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import random
import numpy as np
def R(k, P, V):
if random.random() < P[k]:
return V[k]
else:
return 0
def eplison_bandit(K, P, V, R, T):
r = 0
Q = np.zeros(K)
count = np.zeros(K)
for t in range(T):
eplison = 1. / np.sqrt(t + 1)
if random.random() < eplison:
k = random.randint(0, K - 1)
else:
k = np.argmax(Q)
v = R(k, P, V)
r += v
Q[k] += (v - Q[k]) / (count[k] + 1)
count[k] += 1
return r
def main():
K = 5
P = np.array([0.1, 0.9, 0.3, 0.2, 0.7])
V = np.array([5, 3, 1, 7, 4])
T = 1000000
print eplison_bandit(K, P, V, R, T)
if __name__ == '__main__':
main()
一般取值为较小值0.1或者0.01,当然也可以随着尝试次数增加而减小,如果尝试次数为
,那么设为
即可。
代码运行结果为:获得总价值2795546。结果和情况1还是很接近的,说明这种方法基本能达到最优。
Softmax算法
原理
上面的方法是以 的概率来进行探索利用抉择,而Softmax方法则是根据Boltzmann分布
来进行抉择。其中 被称作“温度”,
越小的话平均价值越高的摇臂被选取的概率越高,
趋向于无穷大的话选取概率就很均匀了,这时候就变成了仅探索。
实现代码
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import random
import numpy as np
def softmax(x):
return np.exp(x) / np.sum(np.exp(x))
def R(k, P, V):
if random.random() < P[k]:
return V[k]
else:
return 0
def eplison_bandit(K, P, V, R, T, tau=0.1):
r = 0
Q = np.zeros(K)
count = np.zeros(K)
for t in range(T):
p = softmax(Q / tau)
rand = random.random()
total = 0.0
for i in range(K):
total += p[i]
if total >= rand:
k = i
break
v = R(k, P, V)
r += v
Q[k] += (v - Q[k]) / (count[k] + 1)
count[k] += 1
return r
def main():
K = 5
P = np.array([0.1, 0.9, 0.3, 0.2, 0.7])
V = np.array([5, 3, 1, 7, 4])
T = 1000000
tau = 0.1
print eplison_bandit(K, P, V, R, T, tau)
if __name__ == '__main__':
main()
代码运行结果为: 时,获得总价值1397795。
时,获得总价值2798372。当然随机性很大,每次运行结果都会不同。
而 贪心算法和Softmax算法谁更好,取决于具体应用,这里也没有定论。
公众号「算法码上来」。godweiyang带你学习算法,不管是编程算法,还是深度学习、自然语言处理算法都一网打尽,更有各种计算机新鲜知识和你分享。别急,算法码上来。