强化学习代码练习q-learning-迷宫

相比上一个demo,这个练习的环境更加复杂,但是就强化学习智能体而言,其整体是一样的

但是既然环境更加复杂,就需要把智能体和环境单独拉出来写,不能再放一个Python文件中

环境类
环境类总结起来就是定义了初始化的参数,构建迷宫,重置函数(每一次游戏结束后需重置到起始的环境),每一步怎么走的。
注意:这里的update和main函数知识对该环境的一个简单测试,不是智能体的行动,在使用强化学习训练的过程中,它们并不执行。

import numpy as np
import pandas as pd
import time
import tkinter as tk

UNIT=40 #像素
#画布
WIDTH=4
HIGTH=4

#新建环境类
class Maze(tk.Tk,object):
    def __init__(self):
        super(Maze,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(HIGTH*UNIT,WIDTH*UNIT))#建立画布
        self._build_maze()

    def _build_maze(self):
        self.canvas=tk.Canvas(self,bg='white',height=HIGTH*UNIT,width=WIDTH*UNIT)#新建画布
        #创建单元格
        for c in range(0,WIDTH*UNIT,UNIT):
            x0,y0,x1,y1=c,0,c,HIGTH*UNIT#获取两个坐标,然后绘制直线
            self.canvas.create_line(x0,y0,x1,y1)
        for r in range(0,HIGTH*UNIT,UNIT):
            x0,y0,x1,y1=0,r,WIDTH*UNIT,r
            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()
    #reset
    def reset(self):#说实话,这个是需要结束了后才调用
        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)#返回搜索者的起始坐标

    #智能体走的每一步的过程
    def step(self,action):
        s=self.canvas.coords(self.finder)#获取搜索者的位置
        base_action=np.array([0,0])
        if action==0:#数字从0-4代表上下左右
            if s[1]>UNIT:
                #此刻动作向上,如果搜索者不是在最上一层,则可以向上移动,否则不必执行操作
                base_action[1]-=UNIT
        elif action==1:
            if s[1]<(HIGTH-1)*UNIT:
                base_action[1]+=UNIT
        #左右
        elif action==2:
            if s[0]>UNIT:
                base_action[0]-=UNIT
        elif action==3:
            if s[0]<(WIDTH-1)*UNIT:
                base_action[0]+=UNIT

        #之所以用这种方式,是由于移动函数的参数需求所致
        self.canvas.move(self.finder,base_action[0],base_action[1])
        #移动完成后记录新的状态位置
        s_next=self.canvas.coords(self.finder)

        #奖励函数设计
        if s_next==self.canvas.coords(self.oval):
            reward=1
            done=True
            s_next='terminal'
        elif s_next in (self.canvas.coords(self.hell1),self.canvas.coords(self.hell2)):
            reward=-1
            done=True
            s_next='terminal'
        else:
            reward=0
            done=False
        return s_next,reward,done
    #渲染
    def render(self):
        time.sleep(0.1)
        self.update()#方法

#update,不停的在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)#100毫秒后更新,函数对象
    env.mainloop()#进入主循环

然后是智能体的构建
智能体的部分比较通用和固定,总结起来就是:参数的初始化阶段,动作选择函数,学习数据并更新

import pandas as pd
import numpy as np
#大脑写完了,总结来就是,初始化一些参数,动作选择策略,学习更新过程
class Q_table:
    #init,相当于构造方法
    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
        #生成q表
        #这个Q表的生成与之前的不同,这里只传递动作,而没有给出状态,状态在后面陆续自动添加
        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所对应的动作
            s_acitons=self.q_table.loc[s,:]
            #理论上直接选择最大值即可,但是考虑到相同值的情况,所以还是保险一点,这句代码太高度集成了
            action=np.random.choice(s_acitons[s_acitons==np.max(s_acitons)].index)
        else:
            action=np.random.choice(self.actions)#随机选择动作
        return action
    #学习,前面那个程序是因为直接就是主程序,所以无需传参
    def learn(self,s,a,r,s_):
        #这就是一组标准的学习数据,在treasure中的rl中
        self.check_state_exist(s_)#检查状态是否存在
        #从q表中获取预测值
        q_predict=self.q_table.loc[s,a]
        if s_!='terminal':
            #计算q目标值
            q_target=r+self.reward_decay*self.q_table.loc[s_,:].max()
        else:
            q_target=r
        #更新q表
        self.q_table.loc[s,a]+=self.learning_rate*(q_target-q_predict)
    #函数,检查状态是否存在
    def check_state_exist(self,state):
        if state not in self.q_table.index:#python的代码很多都是高度集成的
            self.q_table=self.q_table.append(
                pd.Series([0]*len(self.actions),index=self.q_table.columns,name=state)#添加
            )

好了,环境和智能体都已经写好了,就可以来玩走迷宫的游戏了
调用前面写好的两个类,并写好更新的过程即可运行,这里可以设定训练的次数,贪婪策略所用的系数以及其动态变化的控制

from environment import Maze
from agent import Q_table

#这里是更新交互过程的代码
def update():
    epison=0.9#贪婪策略
    for i in range(10):
        #迭代10次,到这里你才会明白前面写的那些类和函数怎么用
        observation=env.reset()#每进入新的一轮学习,就重置环境,这也是初始状态,这个observation其实就是智能体当前的坐标位置
        #进入该轮学习的探索步骤
        while True:
            #每一步都先渲染环境
            env.render()
            #选择动作
            action=RL.choose_action(str(observation),epison)#动作选择策略函数,这里传的两个参数你现在知道怎么用的了
            #执行新的动作,并返回下一个状态和奖励
            observation_,reward,done=env.step(action)
            #学习这一步的数据
            RL.learn(str(observation),action,reward,str(observation_))
            #进入新的状态
            observation=observation_

            #判断是否结束
            if done:
                break
            epison+=0.05#变化的贪心策略
    #for循环结束后,整个训练也就结束了
    print("game over")
    env.destroy()

if __name__ == '__main__':
    env=Maze()
    #所以在这里才只用传递动作
    RL=Q_table(actions=list(range(env.n_action)))#我最大的缺陷就是对知识的记忆不行,记忆力太差,这里不同的类,不同的方法中定义了很多的参数,然后又在其他不同的地方引用,导致我压根反应不过来
    env.after(10,update)#这里就是传参,update方法后面不加括号
    env.mainloop()

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值