今天给大家分享如何用Q_learning算法来实现走迷宫,我们的红色方块会一次次的尝试不同的格子,直到落入黑格子,获得惩罚*1;或者走进黄格子,获得奖励*1为止。每一次游戏都会更新Q_table的权重,以实现红色方块下一次能够更加快速的找到黄格子。
本文参照了莫烦python的博客:https://morvanzhou.github.io/tutorials/machine-learning/reinforcement-learning/2-2-tabular-q1/
Q_learning的整个算法过程如下图,具体含义:
首先:初始化一个Q_table;
循环一定次数;
在循环中根据奖励和action更新Q_table;
,这些在本文中都会以代码例子的形式表述。
本文主要分为以下几大块:
一.主循环搭建
二.RL_brain选择动作&更新Q_table的实现
三.环境编写(代码)
一:主循环搭建
我们创建100次循环用来训练更新Q_table。
首先,初始化环境,获取红色方块的开始位置;核心代码:position=env.reset()
其次,我们根据Q_table中的在position位置走不同方向(即动作)reward,来选择reward最高的action;核心代码:action=RL.choose_action(str(position))
第三,我们让环境执行该action,获得环境的反馈:走进黑色各自-1,走进黄色格子+1,其他+0,更新我们的Q_table;核心代码:position_next,reward,done=env.step(action);RL.learn(str(position),action,reward,str(position_next))
最后,我们由于已经执行了action,即我们在当前position向某个方向走了一步,所以我们更新position=position_next
以下为主循环所有代码:
from maze_env import Maze
from RL_brain import QLearningTable
"""
循环更新Q_table
"""
def update():
for episode in range(100):
position=env.reset()
while True:
env.render()
#选择执行的动作,条件为在q_table中奖励最大
action=RL.choose_action(str(position))
position_next,reward,done=env.step(action)
#根据环境反馈的奖励reward更新q_table
RL.learn(str(position),action,reward,str(position_next))
position=position_next
if done:
print('--------episode:{0}-------'.format(episode))
print(RL.q_table)
break;
print('game over')
env.destroy()
if __name__=='__main__':
env=Maze()
RL=QLearningTable(actions=list(range(env.n_actions)))
env.after(100,update)
env.mainloop()
二.RL_brain选择动作&更新Q_table的实现
在RL_brain中:
首先,我们先创建一个空的Q_table;核心代码:self.q_table=pd.DataFrame(columns=self.actions,dtype=np.float64)
然后,我们实现主循环第二步中用到的,根据position选择奖励最大的action的方法:choose_action(self,positon),90%的概率我们按照Q_table的奖励来随机选择奖励最大的action;10%的概率我们随机选择一个action,这样可以是我们的红色方块探索更多的区域,以找到更短的路径找到黄色的方块
第三,我们实现更新Q_table的方法。根据Q_learning的整个算法过程,
Q_tabel.loc[positon,action]位置的奖励需要被更新为:Q_tabel.loc[positon,action]+=动作奖励+α*(γ理想奖励-实际奖励);
动作奖励 我们在主循环第三步中得到;
理想奖励 则=γ*执行该动作后获得的最大奖励=γ*q_table.loc[position_next,:].max()
实际奖励 则=红色方块执行action获得的Q_table中的奖励=q_table.loc[position,action]
第四,实现检查position在Q_table中是否存在的方法,不存在则append到q_table中,并用0填充各个action的reward;新增position如图所示:
经过一次次的训练,我们的Q_table也会慢慢趋于稳定,此时红色方块就能很快的找到黄色方块了
训练一次:
训练48次:
训练100次:
以下是QL_brain的全部代码:
"""
用于完成
1.创建Q_table
2.根据Q_table,选择动作
3.根据环境反馈reward,action更新Q_table
"""
import numpy as np;
import pandas as pd;
class QLearningTable:
def __init__(self,actions,learning_rate=0.01,reward_decay=0.9,e_greedy=0.9):
self.actions=actions
self.q_table=pd.DataFrame(columns=self.actions,dtype=np.float64)
self.rl=learning_rate
self.gamma=reward_decay
self.epsilon=e_greedy
"""
根据当前位置&状态表选择动作
"""
def choose_action(self,position):
self.check_state_exist(position)
#90%的概率按q_table奖励选择
#10%的概率随机选择
if np.random.rand()
三.环境编写
环境编写这部分不想怎么讲,它是针对每个实际的事件个性化编写的,有兴趣可以看我代码的注释.
以下是环境全部代码:
import numpy as np
import time
import sys
if sys.version_info.major == 2:
import Tkinter as tk
else:
import tkinter as tk
UNIT = 40 # pixels
MAZE_H = 4 # grid height
MAZE_W = 4 # grid width
class Maze(tk.Tk, object):
def __init__(self):
super(Maze, self).__init__()
self.action_space = ['u', 'd', 'l', 'r']
self.n_actions = len(self.action_space)
self.title('maze')
self.geometry('{0}x{1}'.format(MAZE_H * UNIT, MAZE_H * UNIT))
self._build_maze()
def _build_maze(self):
self.canvas = tk.Canvas(self, bg='white',
height=MAZE_H * UNIT,
width=MAZE_W * UNIT)
# create grids
for c in range(0, MAZE_W * UNIT, UNIT):
x0, y0, x1, y1 = c, 0, c, MAZE_H * UNIT
self.canvas.create_line(x0, y0, x1, y1)
for r in range(0, MAZE_H * UNIT, UNIT):
x0, y0, x1, y1 = 0, r, MAZE_H * UNIT, r
self.canvas.create_line(x0, y0, x1, y1)
# create origin
origin = np.array([20, 20])
#创建陷阱:黑色方块
# hell
hell1_center = origin + np.array([UNIT * 2, UNIT])
self.hell1 = self.canvas.create_rectangle(
hell1_center[0] - 15, hell1_center[1] - 15,
hell1_center[0] + 15, hell1_center[1] + 15,
fill='black')
# hell
hell2_center = origin + np.array([UNIT, UNIT * 2])
self.hell2 = self.canvas.create_rectangle(
hell2_center[0] - 15, hell2_center[1] - 15,
hell2_center[0] + 15, hell2_center[1] + 15,
fill='black')
#创建目标,黄色方块
# create oval
oval_center = origin + UNIT * 2
self.oval = self.canvas.create_oval(
oval_center[0] - 15, oval_center[1] - 15,
oval_center[0] + 15, oval_center[1] + 15,
fill='yellow')
#创建红色方块初始位置
# create red rect
self.rect = self.canvas.create_rectangle(
origin[0] - 15, origin[1] - 15,
origin[0] + 15, origin[1] + 15,
fill='red')
# pack all
self.canvas.pack()
def reset(self):
self.update()
time.sleep(0.5)
self.canvas.delete(self.rect)
origin = np.array([20, 20])
self.rect = self.canvas.create_rectangle(
origin[0] - 15, origin[1] - 15,
origin[0] + 15, origin[1] + 15,
fill='red')
# return observation
return self.canvas.coords(self.rect)
"""
每次移动根据移动方向计算红色方块位置
"""
def step(self, action):
s = self.canvas.coords(self.rect)
base_action = np.array([0, 0])
if action == 0: # up
if s[1] > UNIT:
base_action[1] -= UNIT
elif action == 1: # down
if s[1] < (MAZE_H - 1) * UNIT:
base_action[1] += UNIT
elif action == 2: # right
if s[0] < (MAZE_W - 1) * UNIT:
base_action[0] += UNIT
elif action == 3: # left
if s[0] > UNIT:
base_action[0] -= UNIT
self.canvas.move(self.rect, base_action[0], base_action[1]) # move agent
s_ = self.canvas.coords(self.rect) # next state
#设定奖励;
#黑色陷阱 -1
#黄色目标 +1
#其他位置 +0
# reward function
if s_ == self.canvas.coords(self.oval):
reward = 1
done = True
s_ = 'terminal'
elif s_ in [self.canvas.coords(self.hell1), self.canvas.coords(self.hell2)]:
reward = -1
done = True
s_ = 'terminal'
else:
reward = 0
done = False
return s_, reward, done
def render(self):
time.sleep(0.1)
self.update()
def update():
for t in range(10):
s = env.reset()
while True:
env.render()
a = 1
s, r, done = env.step(a)
if done:
break
if __name__ == '__main__':
env = Maze()
env.after(100, update)
env.mainloop()