写在前面的话: 留给以后的自己,先通俗的弄懂这个算法具体的效果,每一步怎么实现的,每一步的含义,然后实现这个算法,也可以仿写别人的例子,但是一定得写,这样我们就能从中得到一个正向反馈,才会更有动力,最后再去看算法对应的公式,也许就能看懂了呢~
1 Q-learning算法是智能体(agent)通过与环境进行不停的交互,并从环境中得到行为反馈,最终生成一个状态-行为的奖励表(Q table)。而我们的智能体(agent),根据这个q table来进行决策,完成人类交给她的各种任务的。
例如:一个agent为O,在一个二维的直线上,从左边移动到右边的目标点T处,即完成任务。
O_________________T
对于以上的例子,O有从起始点到终点T这条线段长度状态(State s),并且每个状态有左(left)、右(right)两种行为(Action a),当O移动到T时,训练结束给予1的反馈(Reward R),否则认为这次行为不是有益的,给予R 为 0 。
根据上述例子描述,可以初始化一个Q表:(假设起始位置到T的长度仅为6)
q table:
S\A(Q(s,a)) | L | R |
---|---|---|
0 | 0 | 0 |
1 | 0 | 0 |
2 | 0 | 0 |
3 | 0 | 0 |
4 | 0 | 0 |
5 | 0 | 0 |
6 | 0 | 0 |
表格中列表示为状态State ,行表示行为action,他们之间对应的值Q(s,a)得含义为:在状态s下,采取行为a,环境给出的反馈值。如当前表,Q(s|1, a|L) = 0表示目前环境给出的反馈值为0,当然 ,这是因为在初始化的时候我们的“O”还没有和环境进行交互,所以还没有从环境中学习到任何宝贵的经验。
下面我们通过下面的算法来更新我们的Q表中的每一个值:
Q Table 更新清单:
- 首先我们有epsilon的概率(贪婪度greedy)去选择最大Q值的action,否则随机选择一个action;
- 我们执行这个a,通过和环境进行交互,它会给我们一个反馈r 和通过这个a智能体到达的下一个状态s_;
- 计算出Q(s,a)的估计值(也有说法叫做旧值),这个估计值是我们通过s和a在Q 表中查到的值;
- 计算出Q(s,a)的现实值(新值),这个现实值是指,环境给我们的反馈r,以及加上在新的状态s_下,我们采取最优的行为所得到的最大反馈乘以一个折扣因子。即,将下一步的衰减最大估计和当前的奖励当做当前步的现实奖励。个人理解为,当前状态s下采取的行为a对下一个状态s_的行为有影响,但是又不能将下一步行为得到的奖励全部归功于上一个状态s下选择的行为,所以会乘上一个衰减因子(折扣因子,一个意思)gamma,将二者之和作为当前状态s选择行为a的现实奖励;
- Q现实和Q估计(或者说是Q新值和Q旧值)做差,即得到一个误差。误差乘上学习因子(学习效率)alpha,再加上q表中原来s、a对应的值(Q估计/Q旧值),最后将计算得到的新的奖励值更新到Q表中;
- 将当前状态更新为s_,因为我们已经执行过a,到达s_的状态了。
根据上面的更新步骤,我们可以进行Q Table的更新,但是更新的步骤只进行一次(“O”进行一次探索到达“T”)可能无法完全的更新所有的Q值,所以我们会需要进行多次的迭代,来尽可能多的更新Q table中的值。
一次完整的算法清单:
开始迭代,迭代次数为完成目标次数
1 初始化环境env
2 从环境中获取初始状态
3 开始执行Q Table更新清单步骤:行为选择、行为实施、更新Q值、转移当前状态为最新状态
4 如果当前状态为完成目标的状态,即完成此次迭代
代码示例:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@Author :
@Contact : 907177955@qq.com
@Time : 2020/3/4 23:36
@Desc :
'''
import numpy as np
import pandas as pd
import time
np.random.seed(2)
N_STATE = 6 # the length of the 1 dimensional world
ACTIONS = ['left', 'right'] # available cations
EPSILON = 0.9 # greedy policies
ALPHA = 0.1 # learning rate
LAMBDA = 0.9 # discount factor
MAX_EPISODES = 13 # maximum episodes => training steps
FRESH_TIME = 0.3 # fresh time for one move, the cost of time to move for each step
def build_q_table(nstates, actions):
table = pd.DataFrame(
np.zeros((nstates, len(actions))), # q_table initial values
columns=actions # action's name
)
print(table) # show table
return table
def choose_action(state, q_table):
# This is how to choose an action
state_actions = q_table.iloc[state, :]
if np.random.uniform() > EPSILON or (state_actions.all() == 0):
#
action_name = np.random.choice(ACTIONS)
else: # act greedy(贪婪的,贪心的)
action_name = state_actions.idxmax()
return action_name
def get_env_feedback(S, A):
# this is how agent will interact with the environment
if A == 'right': # move right
if S == N_STATE - 2: # reach to the terminal
S_ = 'terminal'
R = 1
else:
S_ = S + 1
R = 0
else: # move left
R = 0
if S == 0: # reach the wall
S_ = S
else:
S_ = S - 1
return S_, R
def update_env(S, episode, step_counter):
env_list = ['-'] * (N_STATE - 1) + ['T']
if S == 'terminal':
interaction = 'Episode %s: total steps = %s' % (episode+1, step_counter)
print('\r{}'.format(interaction), end='')
time.sleep(2)
print('\r ', end='')
else:
env_list[S] = 'o'
interaction = ''.join(env_list)
print('\r{}'.format(interaction), end='')
time.sleep(FRESH_TIME)
def rl():
q_table = build_q_table(N_STATE, ACTIONS)
for episode in range(MAX_EPISODES):
step_counter = 0
S = 0
is_terminated = False
update_env(S, episode, step_counter)
while not is_terminated:
# 真实的行为,有一定概率随机选择行为,否则选择反馈最大的行为
A = choose_action(S, q_table)
S_, R = get_env_feedback(S, A)
# q 估计,指只选择q表中的反馈最大的action
q_predict = q_table.ix[S, A] # ix 既可以通过标签进行索引,也可以通过整数进行索引
if S_ != 'terminal':
# q 现实
q_target = R + LAMBDA * q_table.iloc[S_, :].max()
else:
q_target = R
is_terminated = True
# 更新q表
q_table.ix[S, A] += ALPHA * (q_target - q_predict)
S = S_
update_env(S, episode, step_counter+1)
step_counter += 1
return q_table
if __name__ == '__main__':
q_table = rl()
print(q_table)
运行结果:
参考
其他博客:
https://www.cnblogs.com/yifdu25/p/8169226.html
https://zhuanlan.zhihu.com/p/29213893
推荐一篇写的很好的文章:
https://blog.csdn.net/songrotek/article/details/50580904