强化学习之Sarsa算法最简单的实现代码-(环境:“CliffWalking-v0“悬崖问题)

3 篇文章 0 订阅
2 篇文章 1 订阅

1、算法简介

直接上伪代码:

伪代码解释:

第一行:①设置动作空间A和状态空间S,以后你agent只能执行这A中有的动作,你环境的状态也就S中这么些; 

              ②初始化Q表格,也就是表格的横坐标为动作,纵坐标为状态,每个格子里面的值表示:纵坐标对应的状态s下,执行横坐标对应的动作a,后环境反馈回来的奖励值r(注意啊,这个奖励值先开是都初始化为0啥的,然后不断的episode,这整个表不断的更新,不断的确定哪个状态执行哪个动作奖励是多少,宏观上就是agent不断的确定,不断的强化,比如在这个状态,我该执行哪个动作,强化到最后,我就知道我不管在哪个状态,我都知道,我在这个当前状态我该干啥,强化,强化,强化重要的事说。。。直接点题强化学习,哈哈。。);

              ③如果当前处在终止状态terminal-state下,(你这个都终止状态了,达到目标了,或者坏了,跑出范围了,摔倒了,也不可能给终止状态上施加啥动作,所以不能有动作啊),此时奖励值r都设置为0;

                                                         

第二行:不断的循环重复回合episode,不断的训练,一次回合就是环境重置,导演说各就各位,场景开始什么样还原成开始的样子,agent刚开始在哪啥状态姿势,回到刚开始那样,然后就是开始这个回合,agent开始和环境交互搞啊搞,直到到达终止状态;  

第三行:它这个初始化状态S不是让你把状态空间S重新初始化一个新的,而是每次回合你都把环境搞乱了,现在要开始新的一个回合,你得把前一个回合搞乱的环境重置一下啊,重新各就各位一下,你这不让它各就各位,那agent每次都在学一个新的环境,回合起始学习的环境是上个环境遗留下来的终止环境,这学个毛线啊吗。说白了,就是 env.reset()。

第四行:在状态为s情况下,我要从Q表中选一个动作a执行到环境中,我采用的是(ε-greedy) ε贪婪策略,这个策略设置一个概率比如0.1。我现在采用这个策略的话,我有0.9的概率是直接从Q表中选取状态s对应的这一行,执行哪个动作给的奖励最高,我就选哪个动作(如果有多个动作给的奖励都是最高,那么我随机选这里面的一个动作a就成);有0.1的概率是随机从动作空间A中挑选动作a,用得到的动作a施加到环境中,那么施加完,环境就会从现在的状态s,转换到状态s'。

第五行:这个episode前面两行该准备的准备完了,现在执行这个episode,直到环境反馈回来的状态为终止状态terminal-state,就终止这个回合episode,否则一直循环,也就是agent不断的和环境进行交互,不断的学习。

第六行:在环境状态为s时,给环境施加一个动作a,得到环境的反馈,下一个状态s'和奖励r(这行代码体现的就是智能体与环境之间的交互)。

第七行:根据第六行,环境返回回来的下个状态s',我们再次使用(ε-greedy) ε贪婪策略,获得环境在下个状态s',要执行的s'上的动作a'(也就下一个要执行动作)。

第八行:这个就是数学公式的伪代码,到过程中就是,你现在要更新的Q表了,要更新Q表中的Q(s, a)这个格子中的奖励值,你首先先取出这个格子中老的Q(s, a)值,然后取出Q(s', a')这个格子中的奖励值,然后根据公式,直接算出新的Q(s, a)这个格子奖励值该是啥。这个α(alpha)就是用于软更新soft update的,类似机器学习中的学习率;这个γ(gamma)就是折扣因子。

第九行:都往前走一步,状态变为下一个装填s',动作变为下一个动作a'

第十行:当此时这个新的状态是终止状态时,我直接停止这轮回合。

代码:使用悬崖问题中最简单的模型"CliffWalking-v0"作为环境,其中o表示能走的路地;C表示悬崖;T表示终点;智能体agent用x表示,x从左下角起始位置,要走到终点T,进入悬崖、出界、终点都是终止状态。

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import time

import numpy as np
import gym


class SarsaAgent(object):
    def __init__(self, obs_n, act_n, learning_rate=0.01, gamma=0.9, e_greedy=0.1):
        self.act_n = act_n       # 动作的维度, 有几个动作可选
        self.lr = learning_rate    # 学习率
        self.gamma = gamma   # 折扣因子,reward的衰减率
        self.epsilon = e_greedy   # 按一定的概率随机选动作
        self.Q = np.zeros((obs_n, act_n))   # 创建一个Q表格


    # 根据输入观察值(这个代码不区分state和observation),采样输出的动作值
    def sample(self, obs):
        if np.random.uniform(0, 1) < (1.0 - self.epsilon):    # 根据table的Q值选动作
            action = self.predict(obs)    # 调用函数获得要在该观察值(或状态)条件下要执行的动作
        else:
            action = np.random.choice(self.act_n)   # e_greedy概率直接从动作空间中随机选取一个动作
        return action


    # 根据输入的观察值,预测输出的动作值
    def predict(self, obs):
        Q_list = self.Q[obs, :]    # 从Q表中选取状态(或观察值)对应的那一行
        maxQ = np.max(Q_list)    # 获取这一行最大的Q值,可能出现多个相同的最大值

        action_list = np.where(Q_list == maxQ)[0]    # np.where(条件)功能是筛选出满足条件的元素的坐标
        action = np.random.choice(action_list)      # 这里尤其如果最大值出现了多次,随机取一个最大值对应的动作就成
        return action

    # 给环境作用一个动作后,对环境的所有反馈进行学习,也就是用环境反馈的结果来更新Q-table
    def learn(self, obs, action, reward, next_obs, next_action, done):
        """
            on-policy
            obs:交互前的obs, 这里observation和state通用,也就是公式或者伪代码码中的s_t
            action: 本次交互选择的动作, 也就是公式或者伪代码中的a_t
            reward: 本次与环境交互后的奖励,  也就是公式或者伪代码中的r
            next_obs: 本次交互环境返回的下一个状态,也就是s_t+1
            next_action: 根据当前的Q表,针对next_obs会选择的动作,a_t+1
            done: 回合episode是否结束
        """
        predict_Q = self.Q[obs, action]
        if done:
            target_Q = reward     # 如果到达终止状态, 没有下一个状态了,直接把奖励赋值给target_Q
        else:
            target_Q = reward + self.gamma * self.Q[next_obs, next_action]      # 这两行代码直接看伪代码或者公式
        self.Q[obs, action] = predict_Q + self.lr * (target_Q - predict_Q)      # 修正q



def run_episode(env, agent, render=False):
    total_steps = 0    # 记录每一个回合episode走了多少step
    total_reward = 0    # 记录一个episode获得总奖励

    obs = env.reset()   # 重置环境,重新开始新的一轮(episode)
    action = agent.sample(obs)    # 根据算法选择一个动作,采用ε-贪婪算法选取动作

    while True:
        next_obs, reward, done, info = env.step(action)    # 与环境进行一次交互,即把动作action作用到环境,并得到环境的反馈
        next_action = agent.sample(next_obs)   # 根据获得的下一个状态,执行ε-贪婪算法后,获得下一个动作

        # 训练Sarsa算法, 更新Q表格
        agent.learn(obs, action, reward, next_obs, next_action, done)

        action = next_action
        obs = next_obs   # 存储上一个观测值(这里状态和观测不区分,正常observation是state的一部分)

        total_reward += reward
        total_steps += 1

        if render:
            env.render()     # 重新画一份效果图
        if done:      # 如果达到了终止状态,则回合结束,跳出该轮循环
            break
    return total_reward, total_steps



def test_episode(env, agent):
    total_reward = 0
    obs = env.reset()

    while True:
        action = agent.predict(obs)  # greedy
        next_obs, reward, done, info = env.step(action)  # 执行一步,这是强化学习最关键的一行代码,通俗讲就是智能体采取这个动作action,环境就相应的发生了变化,得到下一个状态next_obs, 奖励reward, 是否回合结束done(True or False),其它信息info
        total_reward += reward
        obs = next_obs
        time.sleep(0.5)
        env.render()
        if done:
            print('test reward = %.lf' % (total_reward))

def main():
    env = gym.make("CliffWalking-v0")   # 悬崖边行走游戏,动作空间及其表示为:0 up , 1 right, 2 down, 3 left

    agent = SarsaAgent(
        obs_n=env.observation_space.n,
        act_n=env.action_space.n,
        learning_rate=0.1,
        gamma=0.9,
        e_greedy=0.1)

    is_render = False

    for episode in range(500):
        ep_reward, ep_steps = run_episode(env, agent, is_render)
        print('Episode %s: steps = %s, reward = %.lf' % (episode, ep_steps, ep_reward))


        # 每隔20个episode渲染一下看看效果
        if episode % 20 == 0:
            is_render = True
        else:
            is_render = False


    # 训练结束,查看算法效果
    test_episode(env, agent)



if __name__ == '__main__':
    main()

,

            

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值