【人工智能II】实验2 强化学习Q-Learning算法


搞不懂我一个本科生为啥要学这种必修课,无能哐哐捶地
:(
实验是在百度的Ai Studio上学习操作的

核心思想

智能体agent在环境environment中学习,根据环境的状态state(或观测到的observation),执行动作action,并根据环境的反馈reward(奖励)来指导更好的动作。强化学习通过不断的试错探索,吸取经验和教训,持续不断地优化策略,以从环境中拿到更好的反馈。

实验原理

  • Q-learning学习特定的state下,特定action的价值Q,采用Q表格的方式存储Q值(状态动作价值)。在训练中为了更好的探索环境,采用ε-greedy方式来训练,有一定概率随机选择动作输出。
  • Q-learning是离线(off-policy)的更新方式,更新learn()时无需获取下一步实际做出的动作next_action,并假设下一步动作是取最大Q值的动作。其具体更新公式如下:
    在这里插入图片描述
  • 1、建立Q表,行是每种状态,列是每种状态的行为,值是某状态下某行为估计获得的奖励
    2.每次进行状态转移时有e_greedy概率选当前状态最优方法,有 1- e_greedy选随机方法
    3.选完之后就更新当前状态下对应所选行为的Q值(估计值)
    4.更新方法:其自身= 其自身+学习率*(估计-现实) —> (实际更新 = 实际未更新+学习率*(预测-实际发生))

实验流程图

在这里插入图片描述

实验分析

理解Q-Learning算法

GYM库

  • GYM是强化学习中经典的环境库,它将环境交互接口规范化为:
    重置环境reset()、交互step()、渲染render()。
  • gym.make()生成环境。在示例中,CliffWalking-v0生成悬崖环境。o代表正常可通行路径,x代表受控制的人物,C代表悬崖,T代表目标。
    reset()重置(初始化)环境。
  • step()控制进行一次动作。在示例中,传入参数:0代表上,1代表右,2代表下,3代表左;返回结果:obs观测结果,此处为所处方格编号;reward环境反馈,此处为了最优化路径,每走一步反馈-1,跌落峡谷反馈-100;down是否结束,此处为是否走到终点,布尔变量;info额外信息。
  • render()绘制当前环境
  • predict()方法:输入观察值observation(或者说状态state),输出动作值
  • sample()方法:在predict()方法基础上使用ε-greedy增加探索
  • learn()方法:输入训练数据,完成一轮Q表格的更新
  • run_episode():agent在一个episode中训练的过程,使用agent.sample()与环境交互,使用agent.learn()训练Q表格。
  • test_episode():agent在一个episode中测试效果,评估目前的agent能在一个episode中拿到多少总reward。
  • 在悬崖环境中,目的是寻找最快路径从左下角起点到右下角终点,跌落悬崖会返回起点。Agent是和环境environment交互的主体。

更换实验环境

  • 将CliffWalking悬崖环境更换为FrozenLake-v0冰面行走
  • 使用gym的FrozenLake-V0环境进行训练,F为frozen lake,H为hole,S为起点,G为终点,掉到hole里就游戏结束,可以有上每一步可以有上下左右四个方向的走法,只有走到终点G才能得1分。

实验代码

Q-Learning:

import gym
import numpy as np
class QLearningAgent(object):
    def __init__(self, obs_n, act_n, learning_rate=0.01, gamma=0.9, e_greed=0.1):
        self.act_n = act_n      # 动作维度,有几个动作可选
        self.lr = learning_rate # 学习率
        self.gamma = gamma      # reward的衰减率
        self.epsilon = e_greed  # 按一定概率随机选动作
        self.Q = np.zeros((obs_n, act_n))

    # 根据输入观察值,采样输出的动作值,带探索
    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) #有一定概率随机探索选取一个动作
        return action

    # 根据输入观察值,预测输出的动作值
    def predict(self, obs):
        Q_list = self.Q[obs, :]
        maxQ = np.max(Q_list)
        action_list = np.where(Q_list == maxQ)[0]  # maxQ可能对应多个action
        action = np.random.choice(action_list)
        return action

    # 学习方法,也就是更新Q-table的方法
    def learn(self, obs, action, reward, next_obs, done):
        """ off-policy
            obs: 交互前的obs, s_t
            action: 本次交互选择的action, a_t
            reward: 本次动作获得的奖励r
            next_obs: 本次交互后的obs, s_t+1
            done: episode是否结束
        """
        predict_Q = self.Q[obs, action]
        if done:
            target_Q = reward # 没有下一个状态了
        else:
            target_Q = reward + self.gamma * np.max(self.Q[next_obs, :]) # Q-learning
        self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 修正q

    # 把 Q表格 的数据保存到文件中
    def save(self):
        npy_file = './q_table.npy'
        np.save(npy_file, self.Q)
        print(npy_file + ' saved.')
    
    # 从文件中读取数据到 Q表格
    def restore(self, npy_file='./q_table.npy'):
        self.Q = np.load(npy_file)
        print(npy_file + ' loaded.')
def run_episode(env, agent, render=False):
    total_steps = 0 # 记录每个episode走了多少step
    total_reward = 0

    obs = env.reset() # 重置环境, 重新开一局(即开始新的一个episode)

    while True:
        action = agent.sample(obs) # 根据算法选择一个动作
        next_obs, reward, done, _ = env.step(action) # 与环境进行一个交互
        # 训练 Q-learning算法
        agent.learn(obs, action, reward, next_obs, done)

        obs = next_obs  # 存储上一个观察值
        total_reward += reward
        total_steps += 1 # 计算step数
        if render:
            env.render() #渲染新的一帧图形
        if done:
            break
    return total_reward, total_steps

def test_episode(env, agent, render=False):
    total_reward = 0
    obs = env.reset()
    while True:
        action = agent.predict(obs) # greedy
        next_obs, reward, done, _ = env.step(action)
        total_reward += reward
        obs = next_obs
        if render:
            env.render()
        if done:
            break
return total_reward
# 使用gym创建悬崖环境
env = gym.make("CliffWalking-v0")  # 0 up, 1 right, 2 down, 3 left

# 创建一个agent实例,输入超参数
agent = QLearningAgent(
    obs_n=env.observation_space.n,
    act_n=env.action_space.n,
    learning_rate=0.1,
    gamma=0.9,
    e_greed=0.1)

# 训练500个episode
print('Training...')
for episode in range(500):
    ep_reward, ep_steps = run_episode(env, agent, False)
    print('Episode %s: steps = %s , reward = %.1f' % (episode, ep_steps, ep_reward)) # 取消注释以展示训练过程
print('Done.')
# 查看算法效果,输出行走路径
test_reward = test_episode(env, agent, True)
print('test reward = %.1f' % (test_reward))

Sarsa代码

import gym
import numpy as np 
import time
# agent.py 
class SarsaAgent(object):
    def __init__(self, obs_n, act_n, learning_rate=0.01, gamma=0.9, e_greed=0.1):
        self.act_n = act_n      # 动作维度,有几个动作可选
        self.lr = learning_rate # 学习率
        self.gamma = gamma      # reward的衰减率
        self.epsilon = e_greed  # 按一定概率随机选动作
        self.Q = np.zeros((obs_n, act_n))
        #生成二维得Q表格,状态为行,动作为列,所谓状态即obs观测者,在Q表格中表示为环境格子得总数
        #Q表格的行是环境格子的总数,比如CliffWalking的Q表格有4*12=48行,FrozenLake的Q表格有4*4=16行。
        #Q结构是“环境格子数”*“动作数”。Q值即对应的每个格子采用某个动作所获得的终极回报
 
    # 根据输入观察值,采样输出的动作值,带探索
    def sample(self, obs):
        #if条件是根据状态选择能输出最大Q值的动作,有90%的机会执行exploitation
        if np.random.uniform(0, 1) < (1.0 - self.epsilon): #根据table的Q值选动作
            action = self.predict(obs)
        else:#有10%的机会执行exploration探索
            action = np.random.choice(self.act_n) #有一定概率随机探索选取一个动作
        return action
 
    # 根据输入观察值,预测输出的动作值,输出能产生最大Q值得action
    def predict(self, obs):
        Q_list = self.Q[obs, :]
        maxQ = np.max(Q_list)
        action_list = np.where(Q_list == maxQ)[0]  # maxQ可能对应多个action
        action = np.random.choice(action_list)#多个action则随机选一个
        return action
 
    # 学习方法,也就是更新Q-table的方法
    def learn(self, obs, action, reward, next_obs, next_action, done):
        """ on-policy 即求target_Q时使用的self.Q[next_obs, next_action]是下一个obs-状态实际sample
                      到的action所对应的Q。即下一个obs和实际动作对应的Q
            obs: 交互前的obs, s_t
            action: 本次交互选择的action, a_t
            reward: 本次动作获得的奖励r
            next_obs: 本次交互后的obs, s_t+1
            next_action: 根据当前Q表格, 针对next_obs会选择的动作, a_t+1
            done: episode是否结束
        """
        predict_Q = self.Q[obs, action]#获取对应单元格在动作action下的Q值
        if done:
            target_Q = reward # 没有下一个状态了
        else:
            target_Q = reward + self.gamma * self.Q[next_obs, next_action] # Sarsa
        self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 修正q
 
    # 保存Q表格数据到文件
    def save(self):
        npy_file = './q_table.npy'
        np.save(npy_file, self.Q)
        print(npy_file + ' saved.')
    
    # 从文件中读取Q值到Q表格中
    def restore(self, npy_file='./q_table.npy'):
        self.Q = np.load(npy_file)
        print(npy_file + ' loaded.')
def run_episode(env, agent, render=False):
    total_steps = 0 # 记录每个episode走了多少step
    total_reward = 0
 
    obs = env.reset() # 重置环境, 重新开一局(即开始新的一个episode)
    action = agent.sample(obs) # 根据算法选择一个动作
 
    while True:
        next_obs, reward, done, _ = env.step(action) # 与环境进行一个交互,由action->state与策略无关,
        #由环境决定,由环境的状态转移概率决定。
        next_action = agent.sample(next_obs) # 根据算法选择一个动作,
        #由策略决定,一般平衡exploration和exploitation
        # 训练 Sarsa 算法,更新Q值表
        agent.learn(obs, action, reward, next_obs, next_action, done)
 
        action = next_action  #该动作是下次实际采用动作
        obs = next_obs  # 存储上一个观察值
        total_reward += reward
        total_steps += 1 # 计算step数
        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, _ = env.step(action)
        total_reward += reward
        obs = next_obs
        time.sleep(0.5)
        env.render()
        if done:
            break
    return total_reward
# 使用gym创建悬崖环境
env = gym.make("CliffWalking-v0")  # 0 up, 1 right, 2 down, 3 left
 
# 创建一个agent实例,输入超参数
agent = SarsaAgent(
        obs_n=env.observation_space.n,
        act_n=env.action_space.n,
        learning_rate=0.1,
        gamma=0.9,
        e_greed=0.1)
 
 
# 训练500个episode,打印每个episode的分数
for episode in range(500):
    ep_reward, ep_steps = run_episode(env, agent, False)
    print('Episode %s: steps = %s , reward = %.1f' % (episode, ep_steps, ep_reward))
# 全部训练结束,查看算法效果
test_reward = test_episode(env, agent)
print('test reward = %.1f' % (test_reward))




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值