深度学习(1)--多臂老虎Ji模型

探索与利用的平衡

\(\epsilon\) 贪心算法

在贪心采取每一时刻奖励估值最大的动作的基础上,加入了一个随机参数 \(\epsilon\)

每次以概率 $1-\epsilon $ 选择期望奖励最大的那个动作(利用),以概率 $\epsilon $ 来随机选取一根拉杆(探索)。

如果 $\epsilon $ 一直不变的话,最后的收益会一直存在一个无法收敛的累积懊悔(距离实际最优的差值)。所以让 \(\epsilon\) 按照一个速率来衰减,可以是反比例,也可以是 \(e^{-\lambda}\)

上置信界优化(Upper confidence bound)

基于不确定性的策略算法。前期的选择中,由于样本量太小使得统计的不确定性太大了,贪心选择的话会造成很大的偏差。

引入一个不确定性度量 \(U(a)\) ,它随着一个动作被尝试的次数增加而减小。使用这个不确定性度量和期望奖励来共同决定策略的选择。

霍夫丁不等式: \(X_1,X_2,\dots,X_n\)\(n\) 个独立同分布的随机变量,取值范围为 \([0,1]\),其经验期望为 \(\bar{x} = \frac{1}{n} \sum_{j=1}^{n} X_j\), 则有 \(\mathbb{P}\{ \mathbb{E}[x] \ge \bar{x}_x + u \} \le e^{=2nu^2}\)

将霍夫丁不等式运用于多臂老虎Ji问题中。总操作 \(t\) 次之后,将操作 \(a\) 的期望奖励 \(\hat{Q}_t(a)\) 带入 \(\bar{x}_t\)

根据上述不等式,$ Q_t < \hat{Q}(a) + \hat{U}_t(a)$ 至少以概率 \(1-p =1- e^{-2N_t(a)U(a)^2}\) 成立。

\(p\) 很小的时候,我们认为 $ Q_t < \hat{Q}(a) + \hat{U}_t(a)$ 以很大的概率成立,视 $ Q_t < \hat{Q}(a) + \hat{U}_t(a)$ 为期望奖励上界。实际操作的时候会给 \(\hat{U}_t(a)\) 乘上一个不确定度调节系数 \(c\)

UCB实际操作:

  1. 设置 \(p = \frac{1}{t}\),此时 \(\hat{U}_t(a) = \sqrt{\frac{ln(t)}{2(N_t(a)+1)}}\) ,此处的 \(N_t(a)+1\) 是为了防止分母为0的情形。
  2. 找出加权后的 \(\hat{Q}(a) + c \times \hat{U}(a)\) 最大的操作 \(a\)
  3. 执行操作 \(a\) 并更新 \(\hat{Q}_a\)
class UCB(Solver):
    """ UCB算法,继承Solver类 """
    def __init__(self, bandit, coef, init_prob=1.0):
        super(UCB, self).__init__(bandit)
        self.total_count = 0
        self.estimates = np.array([init_prob] * self.bandit.K)
        self.coef = coef

    def run_one_step(self):
        self.total_count += 1
        ucb = self.estimates + self.coef * np.sqrt(
            np.log(self.total_count) / (2 * (self.counts + 1)))  # 计算上置信界
        k = np.argmax(ucb)  # 选出上置信界最大的拉杆
        r = self.bandit.step(k)
        self.estimates[k] += 1. / (self.counts[k] + 1) * (r - self.estimates[k])
        return k


np.random.seed(1)
coef = 1  # 控制不确定性比重的系数
UCB_solver = UCB(bandit_10_arm, coef)
UCB_solver.run(5000)
print('上置信界算法的累积懊悔为:', UCB_solver.regret)
plot_results([UCB_solver], ["UCB"])

汤普森采样算法

使用采样的方式对每个动作 \(a\) 的奖励概率分布进行一次采样,得到的各个拉杆的奖励样本,再选择样本中奖励最大的动作。

实际情况中,我们使用 Beta 分布来对当前每个动作的奖励进行建模。每次拉杆成功和不成功视作一次 0-1 伯努利试验。

定义 Beta 函数为 \(B(x,y) = \int_0^1 t^{x-1}(1-t)^{y-1}dt\)

我们使用 Gemma 函数来计算 Beta函数 \(B(x,y) = \frac{\Gamma(x)\Gamma(y)}{\Gamma(x+y)}\) ,其中 Gamma 函数有很好的递推性质。

Beta 分布的概率密度函数 \(f(p;\alpha,\beta) = \frac{1}{B(\alpha, \beta) } p^{\alpha -1} (1-p)^{\beta -1}\), 其中的 \(p\) 为对真实成功率的一种预测,而 \(f\) 则是真实成功率为 \(p\) 的概率。

具体来说,如果某个拉杆被选择了 \(m_1+m_2\) 次 其中 \(m_1\) 次奖励为 \(1\)\(m_2\) 次奖励为 \(0\) 。这视作这个拉杆服从参数为 \((m_1+1,m_2+1)\) 的 Beta 分布。

image

初始分布为Beta(1,1),均匀分布;刚开始的时候每个操作的分布在高概率处都比较多,所以有比较高的概率被采样到(初期:探索的权重大);随着每个操作的采样数的上涨,分布会慢慢收拢,那些实际期望奖励小的操作的分布靠近 \(0\),实际期望奖励大的操作的分布靠近 \(1\)。所以实际期望奖励大的点在后期更有可能被采样到(后期:利用的权重大)。

这个方法比较优雅的实现了探索和利用的权衡。

class ThompsonSampling(Solver):
    """ 汤普森采样算法,继承Solver类 """
    def __init__(self, bandit):
        super(ThompsonSampling, self).__init__(bandit)
        self._a = np.ones(self.bandit.K)  # 列表,表示每根拉杆奖励为1的次数
        self._b = np.ones(self.bandit.K)  # 列表,表示每根拉杆奖励为0的次数

    def run_one_step(self):
        samples = np.random.beta(self._a, self._b)  # 按照Beta分布采样一组奖励样本
        k = np.argmax(samples)  # 选出采样奖励最大的拉杆
        r = self.bandit.step(k)

        self._a[k] += r  # 更新Beta分布的第一个参数
        self._b[k] += (1 - r)  # 更新Beta分布的第二个参数
        return k


np.random.seed(1)
thompson_sampling_solver = ThompsonSampling(bandit_10_arm)
thompson_sampling_solver.run(5000)
print('汤普森采样算法的累积懊悔为:', thompson_sampling_solver.regret)
plot_results([thompson_sampling_solver], ["ThompsonSampling"])
posted @ 2026-07-02 13:03  BorisDimitri  阅读(2)  评论(0)    收藏  举报