# encoding:utf-8
import numpy as np
import matplotlib.pylab as plt '''
随机行走问题
0 - 1 - 2 - 3 - 4 - 5 - 6
e s e
0终点r为0. 6终点r为1
中间每个选择r为0 策略 [-1, 1] 每种选择0.5, -1向左,1向右
这个策略下,理论上数字越大回报越高
''' stats = range(7)
start = 3
end = [0, 6]
actions = [-1, 1] r = 1 # 衰减因子
alpha = 0.5 # 学习率
echos = [5, 10, 50, 100, 500, 1000, 10000] def choose_act(stat):
# 策略
if np.random.rand() > 0.5:
return 1
else:
return -1 v = np.zeros([len(stats)]) for i in echos:
for j in range(i):
act = choose_act(start)
stat_ = start + act if stat_ in end:
if stat_ == 6:
v[start] += alpha * (1 + v[stat_] - v[start])
else:
v[start] += alpha * (v[stat_] - v[start])
start = np.random.randint(1,6)
else:
v[start] += alpha * (v[stat_] - v[start])
start = np.random.randint(1,6) plt.plot(v[1:-1])
plt.text(stats[-4], v[-3], j+1) plt.xlabel('state')
plt.ylabel('v')
plt.text(1, 0.8, 'alpha = %s'%alpha)
plt.show()
可以看到 随着学习率的增大,效果越来越好,当学习率为0.5时,已经明显过拟合了
这个是单步的,书上是单回合的,所以不同,后续有空会更新代码
# encoding:utf-8
from __future__ import division
import numpy as np
import matplotlib.pylab as plt stats = range(7)
end = [0, 6]
actions = [-1, 1]
r = 1 # 衰减因子 def choose_act(stat):
# 策略
if np.random.rand() > 0.5:
return 1
else:
return -1 v_t = [0, 1/6, 1/3, 1/2, 2/3, 5/6, 0]
alpha_td = [0.1, 0.15, 0.2] # 学习率
alpha_mc = [0.01, 0.02, 0.04]
for c in range(3):
# TD
alpha = alpha_td[c]
# v = np.random.rand(len(stats))
# v = np.zeros(len(stats))
v = [0.2] * len(stats)
errors = []
start = 3 for j in range(100):
act = choose_act(start)
stat_ = start + act if stat_ in end:
if stat_ == 6:
v[start] += alpha * (1 + v[stat_] - v[start])
else:
v[start] += alpha * (v[stat_] - v[start])
start = np.random.randint(1,6)
else:
v[start] += alpha * (v[stat_] - v[start])
start = stat_ # np.random.randint(1,6) error = np.sqrt(sum([pow(value - v_t[index], 2) for index, value in enumerate(v)]))
errors.append(error) plt.plot(range(100), errors)
index = np.random.randint(40,100)
plt.text(index-3, errors[index], 'alpha_td = %s'%alpha) # MC
alpha = alpha_mc[c]
# v_mc = np.random.rand(len(stats))
# v_mc = np.zeros(len(stats))
v_mc = [0.2] * len(stats)
count_mc = np.zeros(len(stats))
errors = []
for j in range(100):
process = []
start = 3 # np.random.randint(1, 6)
while True:
if start in end:
process.append([start])
break
act = choose_act(start)
if start == 5 and act == 1:
r = 1
else:
r = 0
process.append([start, act, r])
start = start + act T = len(process[:-1])
s_all = [i[0] for i in process[:-1]]
s_dealed = []
for k in range(T):
sar = process[k]
s = sar[0]
if s in s_dealed:continue # first visit
t = s_all.index(s) # 该s 首次出现的位置
num = s_all.count(s) # 该s 总共出现的次数
r_all = sum([i[2] for i in process[t:-1]]) / num
v_mc[s] += alpha * (r_all - v_mc[s])
# v_mc[s] = (v_mc[s] * count_mc[s] + r_all) / (count_mc[s] + 1)
# count_mc[s] += 1 s_dealed.append(s)
error = np.sqrt(sum([pow(value - v_t[index], 2) for index, value in enumerate(v_mc)]))
errors.append(error)
plt.plot(range(100), errors, '.')
index = np.random.randint(40,100)
plt.text(index-3, errors[index], 'alpha_mc = %s'%alpha) plt.xlabel('echo')
plt.ylabel('mse')
plt.show()
随机行走有个特殊性:两个终点,有一个终点奖励为0,也就是说在前几个回合中,单步更新的TD如果一开始向左走,需要好多步才能到达右边终点,而MC由于是整个回合,要么左,要么右,先到右边终点的概率要大得多,所以,前几步MC收敛明显比TD快
但是从总体来看,TD收敛比MC要快,而且收敛值要小,故TD效率更高
上述代码的问题
1.TD 是单步计算MSE,而MC是单回合计算MSE,比较的前提不同
2.在计算MSE时,只是计算了一次评估的误差,并不是平均误差
更新代码