强化学习之Dyna-Q算法——以悬崖漫步环境为例

0.介绍

在强化学习中模型通常指与智能体交互的环境模型,即对环境的状态转移概率和奖励函数进行建模,强化学习依据是否具有环境模型分为基于模型的强化学习(model-based reinforcement learning)以及无模型的强化学习(model-free reinforcement learning)两种。无模型强化学习根据智能体与环境交互采样到的数据直接进行策略提升或者价值估计。基于模型的强化学习中模型事前知道,也可以根据智能体与环境交互采样得到的数据学习得到,然后使用这个模型帮助我们进行策略提升或价值估计。Dyna-Q就是经典的基于模型的强化学习算法,它的环境模型是通过采样数据估计得到的。
强化学习有两个重要评价指标:一个是算法收敛后的策略在初始状态下的期望回报,另一个是样本复杂度,即算法达到收敛结果需要在真实环境中采样的样本数量。基于模型强化学习由于具有一个环境模型,智能体可以额外与环境进行交互,对真实环境中样本的需求量往往减少,因此通常比无模型强化学习算法具有更低的样本复杂度,但是由于环境模型可能不准确,无法完全代替真实环境,因此基于模型强化学习收敛后其策略的期望回报可能不如无模型强化学习。
Dyna-Q算法作为经典基于模型强化学习算法,使用一种称为Q-planning方法来基于模型生成模拟数据,然后用模拟数据联同真实数据进行策略改进。Q-planning每次选取一个曾经到访的状态s以及采取一个曾经在该状态s下执行过的动作a,通过模型得到转移后的状态s'以及奖励r,并根据这个模拟数据(s,a,r,s')采用Q-learning更新方法更新动作价值函数。
每次与环境进行交互执行一次Q-learning后Dyna-Q会执行n次Q-planning,其中Q-planning次数N是事前可以选择的超参数,当其为0时就是普通Q-learning。本Dyna-Q执行在离散并确定的环境中,故当看到一条经验数据(s,a,r,s')时可以直接对模型做出更新,即M(s,a)←r,s'。

1.导入相关库

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
import random
import time

2.悬崖漫步环境实现环节

python 复制代码
class cliffwalking():
    def __init__(self,colnum,rownum,initx,inity,stepr,cliffr):
        self.colnum=colnum
        self.rownum=rownum
        self.initx=initx
        self.inity=inity
        self.stepr=stepr
        self.cliffr=cliffr
        self.disaster=list(range((self.rownum-1)*self.colnum+1,self.rownum*self.colnum-1))
        self.end=[self.colnum*self.rownum-1]
    def step(self,action):
        change=[[0,-1],[0,1],[-1,0],[1,0]]
        self.x=min(self.colnum-1,max(0,self.x+change[action][0]))
        self.y=min(self.rownum-1,max(0,self.y+change[action][1]))
        nextstate=self.x+self.y*self.colnum
        reward=self.stepr
        done=False
        if nextstate in self.disaster:
            done=True
            reward=self.cliffr
        if nextstate in self.end:
            done=True
        return nextstate,reward,done
    def reset(self):
        self.x=self.initx
        self.y=self.inity
        return self.x+self.y*self.colnum

3.Dyna-Q算法实现

在Q-learning算法代码基础上进行修改实现Dyna-Q算法代码,修改内容有加入环境模型,环境模型采用字典表示,每次在真实环境中收集到新的数据,就把它加入字典,根据字典的性质,若该数据本身存在于字典中,就不会再一次进行添加,在Dyna-Q的更新中,执行完Q-learning后会立即执行Q-plaaning。

python 复制代码
class DynaQ():
    """ Dyna-Q算法"""
    def __init__(self,colnum,rownum,alpha,gamma,epsilon,n_planning,actionnum=4):
        self.colnum=colnum
        self.rownum=rownum
        self.actionnum=actionnum
        self.alpha=alpha
        self.gamma=gamma
        self.epsilon=epsilon
        self.n_planning=n_planning
        self.qtable=np.zeros([self.colnum*self.rownum,self.actionnum])
        self.model=dict()
    def takeaction(self,state):
        if np.random.random()<self.epsilon:
            action=np.random.randint(self.actionnum)
        else:
            action=np.argmax(self.qtable[state])
        return action
    def q_learning(self,s0,a0,r,s1):
        tderror=r+self.gamma*self.qtable[s1].max()-self.qtable[s0,a0]
        self.qtable[s0,a0]+=self.alpha*tderror
    def model_learning(self,s0,a0,r,s1):
        self.model[(s0,a0)]=r,s1
        for _ in range(self.n_planning):
            (s,a),(r0,s_)=random.choice(list(self.model.items()))
            self.q_learning(s,a,r0,s_)
    def update(self,s0,a0,r,s1):
        self.q_learning(s0,a0,r,s1)
        self.model_learning(s0,a0,r,s1)

4.Dyna-Q算法在悬崖漫步环境中的训练函数

python 复制代码
def DynaQ_cliffwalking(n_planning):
    colnum=12
    rownum=4
    initx=0
    inity=rownum-1
    stepr=-1
    cliffr=-100
    epsilon=0.1
    alpha=0.1
    gamma=0.9
    num_episodes=300
    pbarnum=10
    printreturnnum=10
    returnlist=[]
    env=cliffwalking(colnum=colnum,rownum=rownum,initx=initx,inity=inity,stepr=stepr,cliffr=cliffr)
    agent=DynaQ(colnum=colnum,rownum=rownum,alpha=alpha,gamma=gamma,epsilon=epsilon,n_planning=n_planning,actionnum=4)
    for i in range(pbarnum):
        with tqdm(total=int(num_episodes/pbarnum),desc='Iteration %d'% i) as pbar:
            for episode in range(int(num_episodes/pbarnum)):
                episodereturn=0
                state=env.reset()
                done=False
                while not done:
                    action=agent.takeaction(state)
                    nextstate,reward,done=env.step(action)
                    episodereturn+=reward
                    agent.update(state,action,reward,nextstate)
                    state=nextstate
                returnlist.append(episodereturn)
                if (episode+1)% printreturnnum==0:
                    pbar.set_postfix({'episode':'%d'%(num_episodes/pbarnum*i+episode+1),'return':'%.3f'%(np.mean(returnlist[-printreturnnum:]))})
                pbar.update(1)
    return returnlist

5.结果可视化代码

python 复制代码
np.random.seed(100)
random.seed(100)
n_planninglist=[0,2,20]
for n_planning in n_planninglist:
    print('Q-planning步数为:%d'% n_planning)
    time.sleep(0.5)
    returnlist=DynaQ_cliffwalking(n_planning)
    episodelist=list(range(len(returnlist)))
    plt.plot(episodelist,returnlist,label=str(n_planning)+'planning steps')
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Dyna-Q on {}'.format('Cliff Walking'))
plt.legend()
plt.show()

通过调整参数,可以观察Q-planning步数对结果的影响。若Q-planning步数为0,Dyna-Q算法退化为Q-learning算法。

6.结果展示


Q-planning步数为:0

Iteration 0: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 676.85it/s, episode=30, return=-151.900]

Iteration 1: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 699.35it/s, episode=60, return=-103.200]

Iteration 2: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1203.27it/s, episode=90, return=-73.000]

Iteration 3: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1305.50it/s, episode=120, return=-53.700]

Iteration 4: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1583.87it/s, episode=150, return=-50.500]

Iteration 5: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1812.24it/s, episode=180, return=-56.600]

Iteration 6: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 2032.75it/s, episode=210, return=-65.200]

Iteration 7: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 2313.12it/s, episode=240, return=-73.600]

Iteration 8: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 2148.06it/s, episode=270, return=-31.300]

Iteration 9: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 2725.88it/s, episode=300, return=-30.800]
Q-planning步数为:2

Iteration 0: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 375.86it/s, episode=30, return=-85.800]

Iteration 1: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 653.92it/s, episode=60, return=-42.900]

Iteration 2: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 790.74it/s, episode=90, return=-42.600]

Iteration 3: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1036.12it/s, episode=120, return=-30.500]

Iteration 4: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1220.46it/s, episode=150, return=-16.100]

Iteration 5: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1367.80it/s, episode=180, return=-25.500]

Iteration 6: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1582.64it/s, episode=210, return=-33.500]

Iteration 7: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1507.98it/s, episode=240, return=-42.600]

Iteration 8: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1880.04it/s, episode=270, return=-50.000]

Iteration 9: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 1833.01it/s, episode=300, return=-69.000]
Q-planning步数为:20

Iteration 0: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 153.51it/s, episode=30, return=-45.000]

Iteration 1: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 313.20it/s, episode=60, return=-62.100]

Iteration 2: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 322.71it/s, episode=90, return=-32.700]

Iteration 3: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 337.84it/s, episode=120, return=-31.900]

Iteration 4: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 338.02it/s, episode=150, return=-42.000]

Iteration 5: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 356.60it/s, episode=180, return=-50.700]

Iteration 6: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 337.57it/s, episode=210, return=-22.600]

Iteration 7: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 366.66it/s, episode=240, return=-60.400]

Iteration 8: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 323.30it/s, episode=270, return=-23.200]

Iteration 9: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 [00:00<00:00, 346.09it/s, episode=300, return=-33.300]

7.总结

从上述结果中看出:随着Q-planning步数增长,Dyna-Q算法收敛速度也越快。当然并非所有环境中都是满足这个规律的,这主要取决于环境是否是确定性的和环境模型的精度。在上述悬崖漫步环境中,状态转移是完全确定的,构建的环境模型精度也是最高的,所以可以增加Q-planning步数来直接降低算法的样本复杂度。
本次内容实现了经典的基于模型的强化学习算法Dyna-Q算法在悬崖漫步环境中的学习过程,并通过调整Q-planning步数直观展示Q-planning步数对算法收敛速度的影响。基于模型的强化学习Dyna-Q算法取得很好的效果。但是这些环境比较简单,模型可以直接通过经验数据学习,若环境比较复杂,状态连续或者状态转移随机而非决定性的,学习一个比较准确的模型就变得具有挑战,直接影响基于模型强化学习算法是否能应用于此等环境并获得比无模型强化学习更好效果。

相关推荐
robot_大菜鸟14 分钟前
python_openCV_计算图片中的区域的黑色比例
开发语言·python·opencv
6230_15 分钟前
关于HTTP通讯流程知识点补充—常见状态码及常见请求方式
前端·javascript·网络·网络协议·学习·http·html
AI让世界更懂你37 分钟前
漫谈设计模式 [18]:策略模式
python·设计模式·策略模式
Pandaconda41 分钟前
【C++ 面试 - 新特性】每日 3 题(六)
开发语言·c++·经验分享·笔记·后端·面试·职场和发展
这不巧了41 分钟前
Faker在pytest中的应用
python·自动化·pytest
oennn欧冷1 小时前
中文关键字检索分析-导出到csv或者excel-多文件或文件夹-使用python和asyncio和pandas的dataframe
python·pandas·vba·asyncio·dataframe·completablefuture
小言从不摸鱼1 小时前
【NLP自然语言处理】文本处理的基本方法
人工智能·python·自然语言处理
hummhumm1 小时前
数据库系统 第46节 数据库版本控制
java·javascript·数据库·python·sql·json·database
手打猪大屁1 小时前
STM32——串口通信(发送/接收数据与中断函数应用)
经验分享·笔记·stm32·单片机·嵌入式硬件
阿拉伯的劳伦斯2921 小时前
LeetCode第一题(梦开始的地方)
数据结构·算法·leetcode