Sarsa和Q-learning是两种经典的强化学习算法,它们在更新动作值函数时有一些重要的区别。
首先,Sarsa算法使用的是on-policy学习方法,即它在学习时使用当前策略选择的动作更新动作值函数。因此,Sarsa算法对当前策略的评估更加准确,但也可能导致过度探索。
相比之下,Q-learning使用的是off-policy学习方法,即它在学习时使用最优策略选择的动作更新动作值函数。因此,Q-learning算法的学习过程更加高效,但它可能会低估当前策略的价值。
另一个区别是,Sarsa算法在每次更新动作值函数时都需要选择一个新的动作,而Q-learning算法则会选择当前状态下的最优动作来更新动作值函数。这意味着Sarsa算法会更加注重探索,而Q-learning算法则更加注重利用。
综上所述,Sarsa算法更适合探索性任务,而Q-learning算法更适合利用性任务。同时,它们的选择也取决于具体的问题和环境。
话不多说,直接看代码
其实Sarsa和q-learning代码的差异比较小,对比之前写的q-learning就可以发现
首先,环境一样的
import numpy as np
import time
import tkinter as tk
#定义一些常量
UNIT=40
WIDTH=4
HIGHT=4
#环境也写在一个类中
class Maze0506(tk.Tk,object):
def __init__(self):
super(Maze0506, self).__init__()
#动作空间
self.action_space=['u','d','l','r']
self.n_action=len(self.action_space)
self.title('maze')
#建立画布
self.geometry('{0}x{1}'.format(HIGHT*UNIT,WIDTH*UNIT))
self.build_maze()
def build_maze(self):
self.canvas=tk.Canvas(self,bg='white',height=HIGHT*UNIT,width=WIDTH*UNIT)
#绘制线框
for i in range(0,WIDTH*UNIT,UNIT):
x0,y0,x1,y1=i,0,i,WIDTH*UNIT
self.canvas.create_line(x0,y0,x1,y1)
for j in range(0,HIGHT*UNIT,UNIT):
x0,y0,x1,y1=0,j,HIGHT*UNIT,j
self.canvas.create_line(x0,y0,x1,y1)
#创建迷宫中的地狱
hell_center1=np.array([100,20])
self.hell1=self.canvas.create_rectangle(hell_center1[0]-15,hell_center1[1]-15,hell_center1[0]+15,hell_center1[1]+15,fill='black')
hell_center2=np.array([20,100])
self.hell2=self.canvas.create_rectangle(hell_center2[0]-15,hell_center2[1]-15,hell_center2[0]+15,hell_center2[1]+15,fill='green')
#创建出口
out_center=np.array([100,100])
self.oval=self.canvas.create_oval(out_center[0]-15,out_center[1]-15,out_center[0]+15,out_center[1]+15,fill='yellow')
#智能体
origin=np.array([20,20])
self.finder=self.canvas.create_rectangle(origin[0]-15,origin[1]-15,origin[0]+15,origin[1]+15,fill='red')
self.canvas.pack()#一定不要忘记加括号
#智能体探索步
def step(self,action):
s=self.canvas.coords(self.finder)#获取智能体当前的位置
#由于移动的函数需要传递移动大小的参数,所以这里需要定义一个移动的基准距离
base_action=np.array([0,0])
#根据action来确定移动方向
if action==0:
if s[1]>UNIT:
base_action[1]-=UNIT
elif action==1:
if s[1]<HIGHT*UNIT:
base_action[1]+=UNIT
elif action==2:
if s[0]>UNIT:
base_action[0]-=UNIT
elif action==3:
if s[0]<WIDTH*UNIT:
base_action[0]+=UNIT
#移动
self.canvas.move(self.finder,base_action[0],base_action[1])
#移动后记录新位置指标
s_=self.canvas.coords(self.finder)
#反馈奖励,terminal不是自己赋予的,而是判断出来的
if s_==self.canvas.coords(self.oval):
reward=1
done=True
s_='terminal'#结束了
elif s_ in (self.canvas.coords(self.hell2),self.canvas.coords(self.hell1)):
reward=-1
done=True
s_='terminal'
else:
reward=0
done=False
#这个学习函数不但传入的参数多,返回的结果也多
return s_,reward,done
#渲染函数
def render(self):
time.sleep(0.1)
self.update()#这里的update应该是画布里的自动更新函数
#重置函数,当一轮走完后,需要重置画布到最初状态
def resets(self):
#其实,就是删掉原先的Finder,再重新定义新的Finder
self.update()
time.sleep(0.5)
#删掉搜索这
self.canvas.delete(self.finder)
#删掉后,再重新定义最初位置
origin=np.array([20,20])
self.finder=self.canvas.create_rectangle(origin[0]-15,origin[1]-15,origin[0]+15,origin[1]+15,fill='red')
return self.canvas.coords(self.finder)
接下来是智能体,这里是有一点区别,主要在体现在learn中,learn的参数多了一个a_,即s_状态下经过策略选择的动作。然后直接使用Q(s_,a_)来更新Q表
import numpy as np
import pandas as pd
class Q_table:
def __init__(self,actions,learning_rate=0.01,reward_decay=0.9,e_greedy=0.9):#除了更新方式学习不一样,其他都是一样的
self.actions=actions
self.learning_rate=learning_rate
self.reward_decay=reward_decay
self.e_greedy=e_greedy
self.q_table=pd.DataFrame(columns=actions,dtype=np.float64)
#动作选择
def choose_action(self,s,e):
self.check_state_exist(s)
#然后用贪婪即可
if np.random.uniform()<e:
s_actions=self.q_table.loc[s,:]
action=np.random.choice(s_actions[s_actions==np.max(s_actions)].index)
else:
action=np.random.choice(self.actions)
return action
#学习,注意,sarsa的学习和q-learning不一样,其实是它多了一个参数
def learn(self,s,a,r,s_,a_):
self.check_state_exist(s_)
q_predict=self.q_table.loc[s,a]
if s_!='terminal':
q_target=r+self.reward_decay*self.q_table.loc[s_,a_]#这里就选择确定的值
else:
q_target=r
#更新
self.q_table.loc[s,a]+=self.learning_rate*(q_target-q_predict)
return self.q_table
def check_state_exist(self,state):
if state not in self.q_table.index:
self.q_table=self.q_table.append(pd.Series([0]*len(self.actions),index=self.q_table.columns,name=state))
然后就是运行智能体玩游戏,update函数也有一点变化
from environment0506 import Maze0506
from agent0506 import Q_table
def update():
epison=0.9#之所以额外定义这个参数目的就是通过传参来动态改变它的值
for i in range(10):
s=env.resets()
a=RL.choose_action(str(s),epison)
while True:
env.render()
s_,r,done=env.step(a)
#再根据新状态选新动作,
a_=RL.choose_action(str(s_),epison)
#然后一起传入学习函数
q_table=RL.learn(str(s),a,r,str(s_),a_)
s=s_
a=a_
if done:
break
epison+=0.05
print("game over")
q_table.to_csv('output.csv')
env.destroy()
if __name__ == '__main__':
env=Maze0506()
RL=Q_table(actions=list(range(env.n_action)))#动态赋予动作
env.after(10,update)
env.mainloop()
OK了大家可以自行运行看一看性能表现,不过呢,游戏环境设置的并不复杂,无法体现出明显的区别,重在学习