说明
找到了一个合适的例子,然后我对其中的内容进行了拆解分析。我觉得代码表达的内容比伪代码清晰多了。
这次算是补砖了(监督+无监督+强化),过去实际上接触过很多强化体系内的基本工具,但一直没有开始做,部分原因是没时间,但更多的还是因为技术洁癖吧(没ready就不想搞)。
所以我觉得应该可以不考虑姿势,先进行破冰,但同时也不能分散注意力。所以之前我给自己定义的界限是:探索新领域可以,但不能写代码。我觉得这次算是一个微微踩线的动作:没自己写代码,但是稍微改了改代码,总体不到1小时完成整个Case,还行。
内容
这个例子的源是这篇文章, 我从我觉得比较方便的地方把代码拆开来做了分析。
图也来自上文哈。
原文也挺有趣的,用DataFrame来表达上面的结构:
- 1 索引是状态
- 2 列名是可采取的行动(Left, Right)
- 3 值是对应的回报(Return)
目标问题是:在一维平面上,有6个点,其中一个是目标点。通过QLearning来找到最优的路径。
这算是无模型的方法,一开始并没有任何的提示,只是在目标点给了1的奖励,其他位置为0。所以算法需要经过一次次的探索来丰富自己的路径数据。
每一次的探索从一个随机点(本例固定为最左边的点)开始游走,到目标点就停下,探索结束。一次也就是一个episode。我想,如果空间大的话,是可以采取有限步达到目标的方式,随机很多点去探索。
以下是我去分解验证的过程:
函数可参考原文,没有改
每次episode从重置状态开始
#设置Q估计表内的状态位置为0
current_state = 0
#与绑定环境相关
updata_environment(current_state)
total_steps = 0
如果不是在目标状态,则执行一次探索
if current_state != states[-1]:
cur_rand = random.uniform(0,1)
#random.uniform(0,1)表示在0-1内随机生成一个随机数
# loc通过行/列标签索引到状态矩阵,(iloc通过行/列号索引矩阵这里没用到),all()表示索引到的状态矩阵与0的比对结果再作一次与运算
is_random_open = cur_rand > want
print('【1】Is random Open',is_random_open)
is_current_state_all_Q_0 = (Q_table.loc[current_state] == 0).all()
print('【2】Is Current State all 0',is_current_state_all_Q_0)
# 是否要随机采取行动(改变位置)
is_random_need_to_change = is_random_open or is_current_state_all_Q_0
print('【3】Is Need To Random Change Action', is_random_need_to_change)
if is_random_need_to_change:
if is_random_open:
print('【4】Change Action Due To Random')
if is_current_state_all_Q_0:
print('【4】Change Action Due To Current State Return 0')
# 基于当前状态选择动作
current_action = random.choice(get_vaild_actions(current_state))
else:
# 根据已知的回报选择动作
print('【4】Choose Known Best Action')
current_action = Q_table.loc[current_state].idxmax()
print('【5】Current Action:', current_action)
# 获取根据当前状态+行动后的新状态
next_state = get_next_state(current_state,current_action)
print('【6】Moving From State %s To State %s by Action %s' %(current_state, next_state, current_action))
# 读取下一个状态下的【行为-奖励】数据
next_state_Q_values = Q_table.loc[next_state,get_vaild_actions(next_state)]
print('【7】Next State Action Returns', next_state_Q_values)
# 从下一步值从获得的影响
part1_next_reward = rewards[next_state]
print('【8.1】part1_next reward', part1_next_reward)
part2_decrease_max = reward_decrease*next_state_Q_values.max()
print('【8.2】part2_decrease_max reward', part2_decrease_max)
part3_current_reward = Q_table.loc[current_state,current_action]
print('【8.3】part3_current reward', part3_current_reward)
# 本状态、本动作的奖励增益
delta_reward = efficiency *(part1_next_reward + part2_decrease_max - part3_current_reward)
print('【9】Current State & Action 【Delta】 Reward' ,delta_reward)
print('【10】Current State & Action Reward ' , Q_table.loc[current_state, current_action])
Q_table.loc[current_state, current_action]+=delta_reward
print('【11】Current State & Action Reward Changed By 【Delta Reward】 ' , Q_table.loc[current_state, current_action])
print('【12】Moving To New State & Update')
current_state = next_state
updata_environment(current_state) #更新环境
print('【13】QTable',Q_table)
else:
print('【14】找到目标')
一开始执行时,会看到探索的位置左右移动,一直到找到目标之后。可能会经过十几轮才会找到目标。
那个u会一直挪来挪去,最终会找到出口。
到达目标后,第一次的episode结束,然后进入第二波。此时的QTable不再是全为0,游走的效率会提升(达到状态4之后就会必然走向5,而不会退回3)
然后再过一轮,这时候状态3的Right也有了大于0的回报,这样随机游走会进一步减少。
之后的每一次游走,会不断丰富在各个状态下的回报值。这个过程中,随机游走已经确定了,每次都直接往右走。
进入批次运行的状态,看u总是直接从左到右。
大概几十次之后
总结
整体上,我觉得强化学习很好玩,和打游戏简直一毛一样。
这次是一个快速探索的实验,到这里就结束了,原理性验证通过,还是挺满意的。