强化学习(一) K臂老虎机python实现
前言: 在本科毕业设计中使用Sarsa算法做了机械臂的运动控制,受限于时间,没有深入研究。现在研究生开学已经一月有余,利用课余时间系统的学习Sutton编写的《强化学习》第二版。课余时间真的太少(如果有中科大的老师逛到这,少布置点作业吧,球球了)。学习全程单打独斗,如有错误,恳请指正。
1. 贪心算法+乐观初始值10臂赌博机
下面这个程序中,10个动作的收益符合正态分布,均值依次从-10到9,价值初始值均为0。这也算是一种乐观初始值。后续的实验表面,乐观初始值对这个初级模型的影响非常大,如果初始值不够乐观甚至有些“悲观”,往往不能训练出正确结果。
# 10臂赌博机,采样平均策略,贪心算法
import numpy as np
import matplotlib.pyplot as plt
import random
def choose(a): # 动作选择函数,返回动作编号
max_act = np.max(a)
max_array = np.where(a == max_act)[0]
# 如果有多个最大价值,随机选
if max_array.size > 1:
return np.random.choice(max_array)
else:
return max_array[0]
def r_func(x): # 奖励函数
# 返回相应动作的奖励,所有动作奖励符合正态分布
# 均值=相应动作的编号-10,标准差(方差的算数平方根)均为1
a = int(np.random.normal(x - 10, 1, 1))
return a
# 初始化价值Q和迭代轮数N
Q = np.zeros(10)
N = 0
# 总收益
r_all = [0]
for i in range(10000):
act = choose(Q)
r = r_func(act)
N = N + 1
# 计算平均收益序列
k = r_all[i] + r
k = k / N
r_all.append(k)
Q[act] = Q[act] + (r - Q[act]) / N
# 输出最大价值动作
print(Q)
e = np.max(Q)
print(np.where(Q == e)[0])
plt.plot(range(10000), r_all[1:100001])
plt.show()
输出如下:
[-1.8 -2. -1.28571429 -3. -8. -0.75242984
-0.73172197 -0.7350572 -1. -0.66035183]
[9]
2. e-贪心算法
将动作选择函数改为如下所示:
def choose_e(a): # e-贪心算法动作选择函数,返回动作编号
ee = 0.2
rand_e = random.random()
if rand_e < ee: # 随机选择
return random.randint(0, 9)
else: # 贪心选择
max_act = np.max(a)
# 注意where函数的用法,括号内是条件式,
# 返回一个元胞,一个是aaray,另一个是数据格式
max_array = np.where(a == max_act)[0]
# 如果有多个最大价值,随机选
if max_array.size > 1:
return np.random.choice(max_array)
else:
return max_array[0]
两者平均收益对比如图所示,其中黄色为贪心算法,红色为e-贪心算法
3. softmax算法
需新增四个全局变量:
# softmax,初始化概率H矩阵
H = np.ones(10)
# 指数HH矩阵
HH = np.ones(10)
# 累积收益标量
r_all2 = 0
# softmax步长
n = 0.1
动作选择函数设计:十个动作被选择的概率归一化之后,[0,1]刚好按十个动作的概率叠加和被划分为10个区间。随机产生[0,1]之间的浮点数,按顺序插入这个区间,浮点数区间中的索引值即为动作编号。
程序如下:
def choose_softmax(H):
# 归一化
h = sum(H)
HP = H / h
print(h)
# 将[0,1]这一区间划分为各个动作的概率之比
H2 = np.zeros(11)
for t in range(10):
H2[t + 1] = H2[t] + HP[t]
H2 = H2[1:11]
# 生成随机数并查看它在数组中的位置
e = random.random()
# 这里要用赋值覆盖原array,不然值不变
H2 = np.append(H2, e)
H2.sort()
# 本次选择的动作编号 = 随机值在H2矩阵的坐标
return np.where(H2 == e)[0]
在奖励函数中得到奖励,并根据公式更新指数HH数组。
def r_func_softmax(x): # 奖励函数
a = np.random.normal(x, 1, 1)
global N, r_all2, H
for i in range(10):
if i == x:
HH[i] = HH[i] + n * (a - r_all2 / N) * (1 - H[i])
else:
HH[i] = HH[i] - n * (a - r_all2 / N) * H[i]
H[i] = pow(math.e, HH[i])
r_all2 += a
return r_all2
在softmax的训练中没有再使用乐观初始值,即价值初值为0,10个动作对应奖励的正态分布的均值=动作自身的编号。训练结果如下: