强化学习之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算法取得很好的效果。但是这些环境比较简单,模型可以直接通过经验数据学习,若环境比较复杂,状态连续或者状态转移随机而非决定性的,学习一个比较准确的模型就变得具有挑战,直接影响基于模型强化学习算法是否能应用于此等环境并获得比无模型强化学习更好效果。

相关推荐
凭君语未可1 分钟前
豆包MarsCode:小C点菜问题
算法
cdut_suye11 分钟前
踏浪而行,2024年技术创作的星光轨迹
经验分享·笔记·学习方法·热榜·博客之星·学习历程·回顾2024
深蓝海拓14 分钟前
Pyside6(PyQT5)中的QTableView与QSqlQueryModel、QSqlTableModel的联合使用
数据库·python·qt·pyqt
C语言魔术师21 分钟前
【小游戏篇】三子棋游戏
前端·算法·游戏
自由自在的小Bird21 分钟前
简单排序算法
数据结构·算法·排序算法
无须logic ᭄22 分钟前
CrypTen项目实践
python·机器学习·密码学·同态加密
百流34 分钟前
scala文件编译相关理解
开发语言·学习·scala
Channing Lewis35 分钟前
flask常见问答题
后端·python·flask
Channing Lewis36 分钟前
如何保护 Flask API 的安全性?
后端·python·flask