首先从最简单的开始学习,可以说最新的强化学习的各种新算法都是基于这个最简单的模式改进出来的。
介绍一个最简单的问题
如下图所示,中间的o就是我们现在的位置,目标是到达最右边的T位置,o可以左右移动。
–o--T
具体的移动过程如下
–o--T
-o—T
–o--T
—o-T
----oT
—o-T
离散情况的设计思路
最简单的设计思路是这样的,我们首先构造一个表,如下所示:
0 left right
0 0.0 0.0
1 0.0 0.0
2 0.0 0.0
3 0.0 0.0
4 0.0 0.0
5 0.0 0.0
最简单的强化学习思路就是更新上面这个表,行是状态,列是行为,而值是表示各不同状态下不同的动作的分数,分数越大表示这个状态下这个动作是更优的。
实现方法
定义(所有变量与公式中的对应):
r:下一个位置的已知条件的奖励分数(已知条件就是百分之百确定的条件,就是我们代码写上去的加分规则,从始至终不做任何改变),因此:终点是1,其他地方都是0
x:当前位置
a:action ,动作
通过迭代来更新表的值,直到收敛。
(1)一开始随机的让小球移动(若此点所有动作的奖励都是0,那么就随机。否则,百分之90选奖励大的,百分之10选奖励小的),只有T那个位置的奖励分数为1,其余都是0
(2)一开始由于每个位置都是0,因此表没有做任何的更新,而直到有一次巧合,正好小球移动到T了,然后就可以利用公式来更新一次表格中的值。
(3)经过(2)后,最靠近T的点也有值了,那么接下来每次走到点5时也会对点4的值进行更新。具体流程可以运行代码中的rl()函数来观看过程。
公式解释:Q是指的这个位置x,和执行动作a情况下能够得到多少分,分越高越好。
更新表格公式:用式3.5得到q_target,然后减去q_predict(是表格里的值)。更新表格。
如此循环。
引自Reinforcement learning for robots using neural networks
其中r右边的max(…)表示新状态的分数,根据代码中显示,这个分数是取这个状态两个值的最大值。比如在状态5时往左和往右分别是0.1和0.2,那么r就是0.2。r只有在T点是1。
总结
自然语言处理的早期也是用一个表来记录词之间的关系,比如b字在a字后面的概率是多少是一个表,而这个概率是通过对大量的已知数据统计得到的。但是存在的问题是其泛化能力为0,对没见过的情况无法处理。
而改进后则是先将词映射到一个特征空间,将一个离散的表格变成了一个连续的函数。
强化学习也是类似的,表格是离散的且有限的,需要用一个连续的函数构造一个映射,这个映射可以代替这个表格。其需要有以下几个功能:
(1)给定状态计算出这个状态的优秀程度,或计算出这个状态下各动作的分数。
代码
import numpy as np
import pandas as pd
import time
np.random.seed(2) # reproducible
N_STATES = 6 # the length of the 1 dimensional world
ACTIONS = ['left', 'right'] # available actions
EPSILON = 0.9 # greedy police
ALPHA = 0.1 # learning rate
GAMMA = 0.9 # discount factor
MAX_EPISODES = 3 # maximum episodes
FRESH_TIME = 0.3 # fresh time for one move
def build_q_table(n_states, actions):
table = pd.DataFrame(
np.zeros((n_states, len(actions))), # q_table initial values
columns=actions, # actions's name
)
# print(table) # show table
return table
#输入当前的状态state,当前的状态表q_table,0.1的概率随机走一步或0.9的概率选择收益
#最大的动作行动。输出的是动作的名字
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 == 0).all()): # act non-greedy or state-action have no value
action_name = np.random.choice(ACTIONS)
else: # act greedy
action_name = state_actions.idxmax() # replace argmax to idxmax as argmax means a different function in newer version of pandas
return action_name
#输入状态S,动作的名字A。
#返回下一个状态S,和这一步的奖励(到达终点奖励是1,否则是0)
def get_env_feedback(S, A):
# This is how agent will interact with the environment
if A == 'right': # move right
if S == N_STATES - 2: # terminate
S_ = 'terminal'
R = 1
else:
S_ = S + 1
R = 0
else: # move left
R = 0
if S == 0:
S_ = S # reach the wall
else:
S_ = S - 1
return S_, R
#输入小球的坐标S,第几个循环episode,第几步step_counter。
#将当前的游戏状态用字符串的形式展现出来
def update_env(S, episode, step_counter):
# This is how environment be updated
env_list = ['-'] * (N_STATES - 1) + ['T'] # '---------T' our environment
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():
# main part of RL loop
q_table = build_q_table(N_STATES, 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) # take action & get next state and reward
#q_predict为当前表格中当前状态的行动在表格中的值。
q_predict = q_table.loc[S, A]
if S_ != 'terminal':
q_target = R + GAMMA * q_table.iloc[S_, :].max() # next state is not terminal
else:
q_target = R # next state is terminal
is_terminated = True # terminate this episode
q_table.loc[S, A] += ALPHA * (q_target - q_predict) # update
print(q_table)
S = S_ # move to next state
update_env(S, episode, step_counter + 1)
step_counter += 1
return q_table