强化学习中的两种探索-平衡策略

强化学习中的两种探索-平衡策略

  • ε-greedy方法
  • UCB(Upper Confidence Bound)方法

为了解决强化学习中的一个经典问题:exploration and exploitation tradeoff 即:到底我们应该花精力去探索从而对收益有更精确的估计,还是应该按照目前拥有的信息,选择最大收益期望的行为?

这样看上去可能不好理解,一个小例子帮助理解:

假如你想在淘宝上买一本书,你一输入书的名字就看到,第一个链接的价格为10元,第二个链接为9.9元,第三个为11元,此时你有两个选择,直接买9.9元的书,因为这是你目前看到最便宜的价格。这就是exploitation。但是现实中你并不会这么做,至少大部分人不会这么做,大家应该都会继续把列表往下翻,你有可能还会找到8元的价格,这个价格显然更加划算,但你付出了更多的时间精力。这就是exploration。 更多的exploration可能会得到更多的收益,但会花费更大的精力,直接exploitation虽然节省精力,但不一定得到更多的收益,这样就会存在exploration 和 exploitation 的平衡问题。
ε-greedy和UCB就是exploration and exploitation tradeoff中两种常用的策略。

1.ε-greedy方法

a = { a r g m a x q ( a ) , 1 − ε 随 机 , ε a=\begin{cases} argmaxq(a) ,& 1-ε \\ 随机, & ε \end{cases} a={argmaxq(a)1εε

以1-ε 的概率选择q值大的动作,以ε的概率选择随机动作


例子:


a1a2a3
当前时刻q(a1)=0.2q(a2)=0.3q(a3)=0.6
下一时刻q(a1)=0.2q(a2)=1.5q(a3)=0.3

如果我们根据贪婪策略的话,会选择q值大的那个动作,即a3。但下一时刻q值就会变化,因此根据贪婪策略我们的最终收益只会是0.6+0.3=0.9。

但根据ε-greedy方法,当前时刻也有ε的概率选择到动作a2,最终的收益为0.3+1.5=1.8,显然最终的收益比贪婪策略得到的要高。

代码实现:

    def choose_action(self, policy, **kwargs):
        if np.random.random() < kwargs['epsilon']:
            action = np.random.randint(1, 4)
        else:
            action = np.argmax(self.q) + 1
        return action

2.UCB方法

在ε-greedy方法收敛后,仍然会以一个ε的概率去选择不是最优的动作,容易造成精力的白白浪费。

在这里插入图片描述

该公式分为两部分,前半部分是正在的q值,可以理解为exploitation。后半部分可视为影响因子,n为总的动作数,该部分随着动作a的选择次数增加而减小,从而更偏向于探索执行次数少的动作,可理解为exploration。

ε-greedy方法也可以设置衰减因子,让ε随着循环次数的增加而衰减,最终衰减到很小的值,此时ε-greedy方法也就变成了贪婪算法。此方法也能让ε-greedy方法达到和UCB差不多的效率。

代码实现:

 def choose_action(self, policy, **kwargs):
    c_ratio = kwargs['c_ratio']
    if 0 in self.action_counts:
        action = np.where(self.action_counts==0)[0][0]+1
    else:
        value = self.q + c_ratio*np.sqrt(np.log(self.counts) / self.action_counts)
        action = np.argmax(value)+1
    return action

3.总结

为了比较两种方法的优劣,将两种方法放在一个多臂赌博机的例子中进行对比,代码如下:

import numpy as np
import matplotlib.pyplot as plt

class KB_Game:
    def __init__(self, *args, **kwargs):
        self.q = np.array([0.0, 0.0, 0.0])
        self.action_counts = np.array([0,0,0])
        self.current_cumulative_rewards = 0.0
        self.actions = [1, 2, 3]
        self.counts = 0
        self.counts_history = []
        self.cumulative_rewards_history=[]
        self.a = 1
        self.reward = 0
    def step(self, a):
        r = 0
        if a == 1:
            r = np.random.normal(1,1)
        if a == 2:
            r = np.random.normal(2,1)
        if a == 3:
            r = np.random.normal(1.5,1)
        return r

    def choose_action(self, policy, **kwargs):
        action = 0
        if policy == 'e_greedy':
            if np.random.random()<kwargs['epsilon']:
                action = np.random.randint(1,4)
            else:
                action = np.argmax(self.q)+1
        if policy == 'ucb':
            c_ratio = kwargs['c_ratio']
            if 0 in self.action_counts:
                action = np.where(self.action_counts==0)[0][0]+1
            else:
                value = self.q + c_ratio*np.sqrt(np.log(self.counts) / self.action_counts)
                action = np.argmax(value)+1
        if policy == 'boltzmann':
            tau = kwargs['temperature']
            p = np.exp(self.q/tau)/(np.sum(np.exp(self.q/tau)))
            action = np.random.choice([1,2,3], p = p.ravel())
        return action
    def train(self, play_total, policy, **kwargs):
        reward_1 = []
        reward_2 = []
        reward_3 = []
        for i in range(play_total):
            action = 0
            if policy == 'e_greedy':
                action = self.choose_action(policy,epsilon=kwargs['epsilon'] )
            if policy == 'ucb':
                action = self.choose_action(policy, c_ratio=kwargs['c_ratio'])
            if policy == 'boltzmann':
                action = self.choose_action(policy, temperature=kwargs['temperature'])
            self.a = action
            # print(self.a)
            #与环境交互一次
            self.r = self.step(self.a)
            self.counts += 1
            #更新值函数
            self.q[self.a-1] = (self.q[self.a-1]*self.action_counts[self.a-1]+self.r)/(self.action_counts[self.a-1]+1)
            self.action_counts[self.a-1] +=1
            reward_1.append([self.q[0]])
            reward_2.append([self.q[1]])
            reward_3.append([self.q[2]])
            self.current_cumulative_rewards += self.r
            # self.cumulative_rewards_history.append(self.current_cumulative_rewards)
            self.cumulative_rewards_history.append(self.current_cumulative_rewards / self.counts)  # 平均奖励
            self.counts_history.append(i)
            # self.action_history.append(self.a)
        # plt.figure(1)
        # plt.plot(self.counts_history, reward_1,'r')
        # plt.plot(self.counts_history, reward_2,'g')
        # plt.plot(self.counts_history, reward_3,'b')
        # plt.draw()
        # plt.figure(2)
        # plt.plot(self.counts_history, self.cumulative_rewards_history,'k')
        # plt.draw()
        # plt.show()

    def reset(self):
        self.q = np.array([0.0, 0.0, 0.0])
        self.action_counts = np.array([0, 0, 0])
        self.current_cumulative_rewards = 0.0
        self.counts = 0
        self.counts_history = []
        self.cumulative_rewards_history = []
        self.a = 1
        self.reward = 0
    def plot(self, colors, policy,style):
        plt.figure(1)
        plt.plot(self.counts_history,self.cumulative_rewards_history,colors,label=policy,linestyle=style)
        plt.legend()
        plt.xlabel('n',fontsize=18)
        plt.ylabel('total rewards',fontsize=18)
        # plt.figure(2)
        # plt.plot(self.counts_history, self.action_history, colors, label=policy)
        # plt.legend()
        # plt.xlabel('n', fontsize=18)
        # plt.ylabel('action', fontsize=18)


if __name__ == '__main__':
    np.random.seed(0)
    k_gamble = KB_Game()
    total = 2000
    k_gamble.train(play_total=total, policy='e_greedy', epsilon=0.05)
    k_gamble.plot(colors='r',policy='e_greedy',style='-.')
    k_gamble.reset()
    # k_gamble.train(play_total=total, policy='boltzmann',temperature=1)
    # k_gamble.plot(colors='b', policy='boltzmann',style='--')
    # k_gamble.reset()
    k_gamble.train(play_total=total, policy='ucb', c_ratio=0.5)
    k_gamble.plot(colors='g', policy='ucb',style='-')
    plt.show()

    # k_gamble.plot(colors='r', strategy='e_greedy')
    # k_gamble.reset()
    # k_gamble.train(steps=200, strategy='ucb', c_ratio=0.5)
    # k_gamble.plot(colors='g', strategy='ucb')
    # k_gamble.reset()
    # k_gamble.train(steps=200, strategy='boltzmann', a_ratio=0.1)
    # k_gamble.plot(colors='b', strategy='boltzmann')
    # plt.show()

代码运行结果如下:
在这里插入图片描述

从图中可以看出,UCB算法能更快达到最大收益,且更加稳定。UCB的累计收益也要高于ε-greedy。

  • 22
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值