小美新开了一家奶茶店,一口气研发了五款新配方。她心里清楚:每一款都有成为爆款的潜力,但谁也不知道顾客到底喜欢哪一款。每天第一位顾客进门,小美只有一次推荐机会——推对了,顾客满意还发朋友圈;推错了,顾客皱眉转身就走。她不敢只推荐销量最高的那款,因为样本太少、判断未必准;可如果每款都轮流硬推,又会把大量顾客挡在门外。这就像站在一排老虎机前:每台机器的中奖率都未知,手里的硬币却有限,要怎么玩才能赢回最多? 这个困境,就是多臂老虎机(Multi-Armed Bandit)问题。它的名字来自赌场:一排“独臂强盗”老虎机摆在那里,玩家每轮只能拉一台机器的杆,观察是否中奖。机器不会说话,只能靠一次次试错来逼近答案。Bandit 算法研究的正是“如何在探索与利用之间取得平衡”——探索是尝试新鲜选项,利用是坚持已知最优。接下来,我们会依次认识 epsilon-greedy、UCB、Thompson 采样、LinUCB 与 Contextual Bandit 这套从简单到聪明的算法家族,并学会如何在广告投放、推荐冷启动和实验分流中用好它们。

岗位关联

一句话价值:掌握 Bandit 算法,你就能在数据不足的冷启动阶段做出接近最优的在线决策,把流量花在刀刃上,直接提升点击率、转化率与实验效率——这是推荐、广告、增长方向工程师的必修课。

目录
  1. 可视化演示
  2. 问题定义:站在一排老虎机前
  3. ε-greedy:大部分时候精明,偶尔犯傻
  4. UCB算法:给未知一点乐观的加分
  5. 汤普森采样:让概率分布自己说话
  6. LinUCB:带上用户画像的决策
  7. 上下文老虎机:千人千面的在线学习
  8. 应用与实战:从试错到放量
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示多臂老虎机问题中三种经典策略(ε-greedy、UCB、Thompson采样)的探索与利用权衡过程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

ε-greedy:固定探索率策略 以ε概率随机探索,1-ε概率选当前最优臂 简单高效但探索率固定,可能次优收敛 arm_t = argmax Q(a) with prob 1-ε, else random 虚线为真实奖励率,实柱为估计值

问题定义:站在一排老虎机前

小美把五款配方编成 1 到 5 号,在吧台上一字排开。每来一位顾客,就相当于她拉起其中一台“老虎机”的摇杆:推荐 1 号,顾客满意记 1 分,不满意记 0 分。机器背后各有一个固定的“中奖率”(被顾客喜爱的概率),但小美看不到,只能从每一次好评差评的反馈里慢慢猜。

在数学上,五款配方被称作五只“臂”(arm),一次推荐就是一轮“拉杆”,顾客的好评就是“奖励”(reward)。问题来了:是继续推荐目前好评率最高的那款,还是把机会分给还没被充分验证的配方?前者叫“利用”(exploitation),后者叫“探索”(exploration),两者互相争夺有限的顾客流量,这就是 Bandit 的核心矛盾。

为了比较不同策略谁更聪明,学者定义了“遗憾”(Regret):假设存在一位“事后诸葛亮”,他永远知道每轮该推荐哪款才能拿到最优奖励,把他能拿到的总奖励减去我们实际策略拿到的总奖励,差值就是遗憾。遗憾越小,说明试错越高效。

想通这一点后,小美不再纠结“哪款一定最好”,而是把目标定为“让累计遗憾尽量小”:允许自己犯错,但错要错得值——每轮选择都要为未来收集信息和当下拿奖励做出最优权衡。这正是所有 Bandit 算法共同的设计哲学。

RT=∑t=1T(rt∗−rt)\displaystyle R_T = \sum_{t=1}^{T} \left( r^*_t - r_t \right)
通俗说明:把“每一种可能的最优收益”与“实际策略的收益”逐轮做差并累加。遗憾值越小,说明我们在信息不足时做出的决策越接近“开挂选手”。
  1. 累计轮次 T$T$ 表示从开业到现在一共接待的顾客总数,也就是决策的总次数。
    TT
  2. 单轮最优奖励$r^*_t$ 表示第 $t$ 轮如果推荐了真正最受欢迎的配方,能拿到的最高满意度分数。
    rt∗r^*_t
  3. 实际奖励$r_t$ 表示第 $t$ 轮我们实际推荐的配方拿到的满意度分数(0 或 1)。
    rtr_t
  4. 单轮遗憾$r^*_t - r_t$ 是这一轮“错过最优”造成的遗憾,差值越大说明这轮决策越吃亏。
    rt∗−rtr^*_t - r_t
  5. 求和累加$\sum_{t=1}^{T}$ 把从第 1 轮到第 $T$ 轮的遗憾全部加在一起,得到策略的总遗憾值。
    ∑t=1T(rt∗−rt)\sum_{t=1}^{T} ( r^*_t - r_t )

ε-greedy:大部分时候精明,偶尔犯傻

小美决定先试最简单的策略:定一个很小的概率 ε(比如 0.1)。营业时,她拿一枚“偏心骰子”做决定——骰子落在 10% 的区间,就随机推荐一款配方尝尝鲜;落在 90% 的区间,就推荐目前好评率最高的那款。这个方法叫“贪婪加强随机”,英文名 epsilon-greedy。

“greedy“(贪婪)指的是每次尽量选当前估计最佳的手臂,把流量集中给”赢家”;“epsilon“(ε)则给探索留了一扇小门,让没被充分验证的配方也有机会被选到。这个策略虽然简单,却精准地刻画了”大部分时候精明、偶尔犯傻”的决策风格。

从概率角度看,任意一轮中,当前最优臂 $\hat{a}$ 被选中的概率接近 $1-\epsilon$,其余每只臂也都有 $\tfrac{\epsilon}{K}$ 的机会被随机选中,其中 $K$ 是臂的总数。写成公式:

ε 的取值很有讲究:太小,冷门配方永远没机会证明自己;太大,流量大量浪费在明显差劲的配方上。小美发现 ε=0.1 是个不错的起点,而且实现只要十行代码——这也解释了为什么 epsilon-greedy 至今仍是工业界的标准“起跑线”基线。

P(a)={1−ϵ+ϵK,a=a^ϵK,a≠a^\displaystyle P(a) = \begin{cases} 1 - \epsilon + \dfrac{\epsilon}{K}, & a = \hat{a} \\ \dfrac{\epsilon}{K}, & a \neq \hat{a} \end{cases}
通俗说明:这个公式只是在说一件事——绝大多数时候按当前最佳来,但每个配方(包括最佳款)都保留一小部分被尝试的机会,避免“一锤定音”过早。
  1. 当前最优臂$\hat{a}$ 是根据已有数据估计出的“当前好评率最高”的配方。
    a^\hat{a}
  2. 探索概率$\epsilon$ 是人为设定的犯傻概率,比如 0.1,决定整体上有多大概率不按最优来。
    ϵ\epsilon
  3. 利用概率$1 - \epsilon + \dfrac{\epsilon}{K}$:当 $a = \hat{a}$ 时,选到它的概率,包含“直接利用”的部分和随机落回它身上的部分。
    1−ϵ+ϵK1 - \epsilon + \dfrac{\epsilon}{K}
  4. 等概率分配$\dfrac{\epsilon}{K}$:把探索概率平分给 $K$ 只臂,每只非最优臂都有同等机会被尝鲜。
    ϵK\dfrac{\epsilon}{K}

UCB算法:给未知一点乐观的加分

用小美的话说,epsilon-greedy 的探索有点“闭着眼睛乱逛”:它可能反复去尝一款明显不好喝的配方,却对另一款“只试过两次、两次都好喝”的配方不够上心。问题出在它没有区分“信息量”——被尝试次数越少的配方,好评率估计越不可靠,越值得优先再试。

于是有了“置信上界”(Upper Confidence Bound, UCB)的思路:给每款配方算一个综合得分——目前平均好评率加上一个“未知奖励项”。这个奖励项跟配方被尝试的次数 $n_a$ 成反比:试得越少,加分越多。这样一来,没被充分验证的配方会持续获得额外加分,直到数据足够,加分数值自然变小,让真实水平“原形毕露”。

UCB 每轮选择得分最高的配方,公式如下:

这一招叫“乐观地看待未知”:假设还没被验证的配方可能比现在看起来更好。小美惊喜地发现,UCB 不再盲目随机探索,而是自动把试验机会优先分给“最值得被了解”的配方,累计遗憾的增长速度接近理论最优点 $\mathcal{O}(\ln T)$——比 epsilon-greedy 聪明得多。

at=arg⁡max⁡a(μ^a+2ln⁡tna)\displaystyle a_t = \arg\max_{a} \left( \hat{\mu}_a + \sqrt{\frac{2 \ln t}{n_a}} \right)
通俗说明:每款配方的得分 = 目前平均好评率 + 一个“未知加成分”。试得越少、总轮次越多,加成分越大,于是那些还没被充分验证的配方总有机会补试。
  1. 选择动作$a_t$ 表示第 $t$ 轮我们要推荐哪款配方,目标是让括号里的值最大。
    ata_t
  2. 平均好评率$\hat{\mu}_a$ 是配方 $a$ 到目前为止的平均奖励,代表它当前的“口碑基线”。
    μ^a\hat{\mu}_a
  3. 总轮次对数$\ln t$ 是累计决策次数取自然对数,随时间缓慢增长,代表“我们知道的越来越多”。
    ln⁡t\ln t
  4. 尝试次数$n_a$ 是配方 $a$ 被尝试过的次数,它在分母上:试得越少,整个根号项越大。
    nan_a
  5. 探索加分项$\sqrt{\dfrac{2 \ln t}{n_a}}$:给样本不足的配方额外加分,鼓励优先补试不确定的选项。
    2ln⁡tna\sqrt{\frac{2 \ln t}{n_a}}
  6. 取最大$\arg\max_a$ 在所有配方中选出“口碑基线 + 未知加分”总和最高的那个。
    arg⁡max⁡a\arg\max_a

汤普森采样:让概率分布自己说话

UCB 靠手动设计“加分公式”引导探索,Thompson 采样则换了一个思路:不为每款配方算一个分数,而是为它的“真实好评率”建立一个完整的概率分布,每轮从这个分布里随机抽一个数,谁抽到的数大就推荐谁。小美觉得这像在玩“抽签决定推荐”的游戏,但签筒的形状会随数据不断变化。

这个分布通常用 Beta 分布刻画。Beta 分布像一本带先验的小账本:$\alpha$ 记好评次数,$\beta$ 记差评次数(初始各加 1,表示“还没试过之前我们保持中立”)。好评越多,分布整体右移,抽到高值的概率变大;差评越多,分布左移;样本越多,分布越“尖”——说明我们对这款配方的真实评价越来越有把握。

每轮按如下方式决策:

Thompson 采样的妙处在于自动平衡探索与利用:当一款配方数据不足、分布宽而扁时,它抽到高值的概率很大,自然频繁被选中;当它被验证、分布变尖后,抽到低值的概率越来越多,被选中的机会也随之下降。小美发现它连 ε 都不用调,被业界称为“贝叶斯式的直觉决策”。

θa∼Beta⁡(αa+1,βa+1),at=arg⁡max⁡aθa\displaystyle \theta_a \sim \operatorname{Beta}(\alpha_a + 1, \beta_a + 1), \quad a_t = \arg\max_a \theta_a
通俗说明:先把每款配方的“好评差评账本”填进 Beta 分布,再各抽一个随机样本,样本值最大的配方获得推荐权。分布越不确定,“爆冷”抽高值的概率越大,探索就自然地发生了。
  1. 好评计数$\alpha_a$ 表示配方 $a$ 历史上收到的好评次数。
    αa\alpha_a
  2. 差评计数$\beta_a$ 表示配方 $a$ 历史上收到的差评次数。
    βa\beta_a
  3. 先验平滑$\alpha_a + 1, \beta_a + 1$:在真实计数基础上各加 1,避免刚开始时出现零概率。
    αa+1,βa+1\alpha_a + 1, \beta_a + 1
  4. Beta 分布采样$\theta_a \sim \operatorname{Beta}(\alpha_a + 1, \beta_a + 1)$:从该分布中随机抽取一个可能的“真实好评率”。
    θa∼Beta⁡(αa+1,βa+1)\theta_a \sim \operatorname{Beta}(\alpha_a + 1, \beta_a + 1)
  5. 选最大样本$a_t = \arg\max_a \theta_a$:比较所有抽到的样本,推荐样本值最大的配方。
    at=arg⁡max⁡aθaa_t = \arg\max_a \theta_a

LinUCB:带上用户画像的决策

试了一段时间,小美隐约觉得不对劲:前面的算法都默认“同一款配方对所有顾客吸引力相同”,可现实是白领偏爱清爽果茶,学生偏爱浓郁奶茶。两款配方总体好评率接近,但各自的“粉丝”完全不同——把所有顾客的数据混在一起算平分,太粗糙了。

LinUCB 的解决方法是给决策引入“上下文”(context):每位顾客的画像(年龄段、到店时段)和每款配方的类型组成一个特征向量 $x$,为每款配方单独拟合一个线性模型,预测“这类顾客对这款配方的满意概率”。预测不再是一个全局常数,而是随顾客特征变化。

选择配方时,LinUCB 沿用 UCB 的“均值 + 不确定项”结构:

LinUCB 让决策从“千人一面的平均最优”升级为“千人千面的个性化最优”。新顾客刚进店,没有历史反馈,但只要知道她的画像,模型就能借助相似顾客的经验给出不错的首推。小美的吧台上多了一本“顾客画像登记册”,推荐命中率肉眼可见地提升了。

at=arg⁡max⁡a(θa⊤xt,a+αxt,a⊤Aa−1xt,a)\displaystyle a_t = \arg\max_{a} \left( \theta_a^\top x_{t,a} + \alpha \sqrt{x_{t,a}^\top A_a^{-1} x_{t,a}} \right)
通俗说明:左边是“预测好评率”,右边是“对这个预测有多大把握”。预测高且把握大的配方优先推荐;还没多少数据、预测不稳的配方,靠右边这项获得尝试机会。
  1. 上下文特征向量$x_{t,a}$ 是第 $t$ 轮把顾客特征与配方 $a$ 组合成的特征向量(如晴天 + 女性 + 果茶)。
    xt,ax_{t,a}
  2. 线性权重$\theta_a$ 是配方 $a$ 专属的权重向量,表示哪些特征对该配方的好评贡献大。
    θa\theta_a
  3. 预测均值$\theta_a^\top x_{t,a}$:特征与权重做内积,得到该顾客对配方 $a$ 的预测好评率。
    θa⊤xt,a\theta_a^\top x_{t,a}
  4. 累积信息矩阵$A_a$ 是配方 $a$ 历史特征的外积累积矩阵,刻画已有数据的信息量。
    AaA_a
  5. 置信参数$\alpha$ 是人为设定的常数,控制探索强度,通常取 0.5 到 1.0。
    α\alpha
  6. 不确定性项$\sqrt{x_{t,a}^\top A_a^{-1} x_{t,a}}$:衡量对当前预测的把握程度,数据越少、此项越大。
    xt,a⊤Aa−1xt,a\sqrt{x_{t,a}^\top A_a^{-1} x_{t,a}}

上下文老虎机:千人千面的在线学习

把 LinUCB 放到更大的框架里看,它属于一个叫 Contextual Bandit(上下文老虎机)的算法家族。普通多臂老虎机只回答“哪只臂整体最好”,Contextual Bandit 则回答“在什么情境下哪只臂最好”。它的名字直白:每一轮都要先观察上下文 $x_t$(顾客是谁、什么场景),再决定推哪只臂,最后获得对应的奖励。

小美发现自己的经验总结方式也变了:以前她会说“3 号最好喝”,现在她会总结“晴天傍晚、结伴女性顾客,3 号好评率显著更高;雨天单人顾客,5 号表现更好”。这些“情境-动作-奖励”的对应关系,正是 Contextual Bandit 想在线学到的策略。

LinUCB 的参数更新也是典型的在线增量式写法:

对推荐冷启动来说,Contextual Bandit 尤其重要:新用户没有行为历史,协同过滤无从谈起,但只要存在画像特征,就能结合相似人群的经验给出个性化推荐,并随着用户行为不断修正。在线学习让模型“边接客、边学习”,永远跟得上变化。

Aa←Aa+xt,axt,a⊤,ba←ba+rtxt,a,θa=Aa−1ba\displaystyle A_a \leftarrow A_a + x_{t,a} x_{t,a}^\top, \qquad b_a \leftarrow b_a + r_t x_{t,a}, \qquad \theta_a = A_a^{-1} b_a
通俗说明:每产生一次推荐反馈,就把新的“特征×特征”和“奖励×特征”累积进矩阵,再用最小二乘公式重新算一遍各配方的权重。数据流动进来,模型跟着更新,无需离线重训。
  1. 累积特征外积$A_a \leftarrow A_a + x_{t,a} x_{t,a}^\top$:每轮把当前特征外积加入协方差矩阵,信息越积越多。
    Aa←Aa+xt,axt,a⊤A_a \leftarrow A_a + x_{t,a} x_{t,a}^\top
  2. 累积奖励加权特征$b_a \leftarrow b_a + r_t x_{t,a}$:用实际奖励 $r_t$ 加权特征并累加,记录“什么样的特征带来好评”。
    ba←ba+rtxt,ab_a \leftarrow b_a + r_t x_{t,a}
  3. 闭式解$\theta_a = A_a^{-1} b_a$:岭回归与最小二乘的标准解,一步算出当前最优权重。
    θa=Aa−1ba\theta_a = A_a^{-1} b_a
  4. 求逆更新$A_a^{-1}$:矩阵求逆运算,工程实现时通常用增量更新避免每轮重新求逆。
    Aa−1A_a^{-1}

应用与实战:从试错到放量

小美的奶茶店只是一个缩影。把“顾客”换成“广告曝光”、“配方”换成“素材”,就是广告系统的日常:新素材没有任何点击数据,要不要给它流量?给多少?如果把流量均匀分给所有素材,等于把一半以上的预算浪费在明显跑不动的创意上。

Bandit 在工业界的三大典型战场:广告投放中用 UCB/LinUCB 动态分配合适曝光给新旧素材;推荐冷启动中用 Thompson 采样快速建立新用户兴趣画像;实验分流中用 Bandit 替代固定的五五开 A/B test,让流量实时流向表现更好的版本。三者共享同一句口诀——探索要趁早,利用要跟上。

实战中一个重要调参是让探索率随时间衰减。早期对一切好奇,多尝试;后期资源有限,偏向利用已知最优。常见做法:

上线前还要做离线评估:用历史日志“重放”(replay)各策略,看看如果当初用了 Thompson 或 LinUCB,累计收益会不会更高。小美的奶茶店也遵循同样的流程——先小范围试跑、看真实反馈、再决定全量推广。试错不是乱试,而是每步都有衡量、有边界、有节奏。

ϵt=ϵ0t\displaystyle \epsilon_t = \frac{\epsilon_0}{\sqrt{t}}
通俗说明:探索率不再固定,而是随着决策次数增加逐渐变小。开局多方试错,后期锁定赢家,符合“先广撒网、再重点培养”的朴素规律。
  1. 初始探索率$\epsilon_0$ 是开局设定的探索概率,比如 0.3,决定最初有多少流量用于尝鲜。
    ϵ0\epsilon_0
  2. 当前轮次$t$ 是到目前为止的决策总次数,每来一位顾客就加 1。
    tt
  3. 平方根衰减$\dfrac{\epsilon_0}{\sqrt{t}}$:探索率随轮次以平方根速度下降,前期大、后期小。
    ϵ0t\frac{\epsilon_0}{\sqrt{t}}
  4. 早期探索前期 $t$ 小,$\sqrt{t}$ 也小,探索率接近 $\epsilon_0$,数据不足时保持好奇。
    t 小\sqrt{t} \text{ 小}
  5. 后期利用后期 $t$ 很大,探索率趋近 0,流量几乎全部给到已验证的最优配方。
    ϵt→0\epsilon_t \to 0

AI 应用场景

场景一:广告素材冷启动

阶段:在线决策 / 流量分配

解决的问题:传统 A/B test 把新素材与老素材固定五五分流,至少一半流量被浪费在表现明显较差的新素材上。AI 用 Bandit 动态分配:新素材先拿到少量探索流量,一旦数据表明实际表现差,系统自动减少曝光;表现好的素材迅速获得更多流量。

具体案例:某信息流广告平台每天新增上千条素材,使用 LinUCB 结合用户兴趣标签决定素材曝光分配,将新素材冷启动期的点击率提升约 15%,同时把无效探索的流量消耗降低三成。

场景二:新用户推荐冷启动

阶段:在线学习 / 个性化推荐

解决的问题:新用户没有任何行为历史,协同过滤和深度 CTR 模型在冷启动时几乎失效。AI 用 Contextual Bandit 将注册画像(性别、年龄、地域、设备型号)作为上下文,边推荐边学习,用最小的试错代价快速建立用户兴趣模型。

具体案例:某短视频 App 对注册 24 小时内的新用户使用 Thompson 采样 + 内容类目上下文,前 20 次推荐内即可将人均观看时长提升 12%,显著缩短“兴趣冷启动期”。

场景三:实验流量动态分配

阶段:增长实验 / 在线分流

解决的问题:传统 A/B test 在实验结束前保持固定分流比,淘汰劣质版本要等满整个周期。AI 用 Bandit 做“在线实验”:多个候选版本同时上线,系统实时监控指标,自动把流量向领先版本倾斜,加快收敛、降低成本。

具体案例:某电商平台首页改版时对三个版本用 UCB 动态分流,三天内自动将 90% 流量分配给点击率领先的版本,相比固定分流提前一周完成实验决策。

Python 演示

python71 行
import mathimport random  class BanditSim:    """多臂老虎机模拟器: 维护每只手臂的尝试次数与累计奖励"""     def __init__(self, true_probs):        self.true_probs = true_probs        self.count_list = [0] * len(true_probs)        self.reward_sum = [0.0] * len(true_probs)     def pull(self, arm):        return 1.0 if random.random() < self.true_probs[arm] else 0.0     def play_epsilon_greedy(self, rounds, epsilon):        total = 0.0        for _ in range(rounds):            if random.random() < epsilon:                arm = random.randint(0, len(self.true_probs) - 1)            else:                arm = max(                    range(len(self.true_probs)),                    key=lambda i: self.reward_sum[i] / max(self.count_list[i], 1)                )            reward = self.pull(arm)            self.count_list[arm] += 1            self.reward_sum[arm] += reward            total += reward        return total / rounds     def play_ucb(self, rounds):        total = 0.0        for t in range(1, rounds + 1):            best_val = -1.0            arm = 0            for i in range(len(self.true_probs)):                if self.count_list[i] == 0:                    ucb = float("inf")                else:                    avg = self.reward_sum[i] / self.count_list[i]                    ucb = avg + math.sqrt(2.0 * math.log(t) / self.count_list[i])                if ucb > best_val:                    best_val = ucb                    arm = i            reward = self.pull(arm)            self.count_list[arm] += 1            self.reward_sum[arm] += reward            total += reward        return total / rounds  def main():    random.seed(42)    true_probs = [0.30, 0.50, 0.70, 0.55, 0.42]     eg_sim = BanditSim(true_probs)    eg_avg = eg_sim.play_epsilon_greedy(3000, 0.1)     ucb_sim = BanditSim(true_probs)    ucb_avg = ucb_sim.play_ucb(3000)     print("epsilon-greedy 平均奖励: %.4f" % eg_avg)    print("UCB          平均奖励: %.4f" % ucb_avg)    print("最优手臂真实概率  : %.2f" % max(true_probs))    print("UCB 相比 epsilon-greedy 提升: %.2f%%"          % ((ucb_avg - eg_avg) / eg_avg * 100))  if __name__ == "__main__":    main()
目的:在同一组“真实好评率”上模拟 epsilon-greedy 与 UCB 的 3000 轮在线决策,对比两者的平均奖励,直观感受“更聪明的探索”带来的收益。
  1. self.count_list = [0] * len(self.true_probs)初始化每只手臂的尝试次数,供后面算平均和探索加分。
  2. if random.random() < epsilon:以 ε=0.1 的概率随机探索一只臂。
  3. key=lambda i: self.reward_sum[i] / max(self.count_list[i], 1)否则选出平均奖励最高的臂,max 防止除以 0。
  4. reward = self.pull(arm)模拟拉杆:按真实概率返回 1(好评)或 0(差评)。
  5. if self.count_list[i] == 0: ucb = float("inf")没试过的臂给无穷大上界,保证每只臂至少先体验一次。
  6. ucb = avg + math.sqrt(2.0 * math.log(t) / self.count_list[i])实现 UCB 的“均值 + 探索加分”公式。
  7. random.seed(42)固定随机种子,让两次实验在完全相同的随机序列下对比,结论更公平。

常见误区

误区“多探索总能发现更多好配方,那把 ε 设成 0.5 甚至 0.8,不是更聪明吗?”

正解探索是有成本的——每一次随机尝试都可能把一个宝贵的流量给到明显较差的臂。ε 越大,浪费的“利用”越多,累计遗憾越大。经验做法是 ε 取 0.05 到 0.2,并且随轮次衰减;想要更高效率的探索,应该换用 UCB 或 Thompson 采样这类“价值导向”的探索,而不是一味加大随机概率。

为何常见:容易把“探索”本身当成目标,忽略了探索的目的是用最小代价换取最有价值的信息。

误区“既然要试错,那跟随便给顾客推荐有什么差别?反正都是碰运气。”

正解Bandit 的每一步选择都在最大化“信息收益 + 奖励收益”的加权和。epsilon-greedy 也有 $(1-\epsilon)$ 概率集中利用当前最优;UCB 用数学推导判断哪些臂的信息价值更高;Thompson 依据贝叶斯后验自动权衡。它们都是结构化的统计决策,不是盲猜。

为何常见:只看到“探索”二字,忽略了背后的“利用”和“统计推断”机制。

误区“离线训练一个超级强大的模型,把所有用户行为学透,线上直接预测不就行了?”

正解离线模型解决的是“已知问题的预测”,Bandit 解决的是“未知选项的在线探索”。新商品、新用户、新素材每天都在产生,离线模型对它们没有任何经验;且用户兴趣会漂移,昨天的分布不能完全代表今天。Bandit 可以无缝接在离线模型之后做“最后一公里的探索决策”。

为何常见:混淆了“利用历史”与“探索未知”两类完全不同的任务。

误区“只要策略够聪明,最终一定能锁定真正最好的那款配方,遗憾趋近于零。”

正解经典 Bandit 算法保证的是“累计遗憾增长最慢”(理论最优级别 $\mathcal{O}(\ln T)$),而不是“一定收敛到全局最优”。在非稳态环境(比如食谱热度随季节变化)中,旧数据甚至会误导决策,需要通过滑动窗口或指数衰减来“遗忘”过期信息。

为何常见:把“遗憾最小化”误当成“收敛保证”,忽略了算法目标本身的设计边界。

面试题

Q1. 多臂老虎机问题的核心挑战是什么?

单选题 · easy

  • A.如何让模型训练得更快
  • B.平衡探索与利用的选择← 正确答案
  • C.如何压缩日志存储成本
  • D.如何提升线上推理速度

答案:B。Bandit 问题的本质是每次决策都要在“探索未知臂”与“利用已知最优臂”之间做权衡,从而最大化累计奖励。其余选项属于工程优化问题,不是 Bandit 的数学本质,故不选。

Q2. 关于 epsilon-greedy 策略,下列说法正确的是?

单选题 · easy

  • A.ε 越大,利用流量占比越高
  • B.ε=0 时变成完全随机探索
  • C.非最优臂各有 ε/K 概率被选← 正确答案
  • D.固定 ε 不会造成任何流量浪费

答案:C。ε-greedy 以概率 ε 均匀随机选择一只臂,因此每只非最优臂有 ε/K 的被选概率。ε 越大探索占比越高,利用越少;ε=0 时是纯贪心而非随机;固定 ε 会持续把流量浪费在差臂上,故选 C。

Q3. UCB 公式中探索项 √(2ln t / n_a) 的设计意图是什么?

单选题 · mid

  • A.优先尝试平均奖励最高的臂
  • B.优先尝试被验证次数少的臂← 正确答案
  • C.优先尝试奖励方差最大的臂
  • D.优先尝试总轮次最少的臂

答案:B。探索项的分母是尝试次数 n_a,试得越少值越大;分子 ln t 随时间增长,保证每只臂最终都能被充分尝试。A 是“利用”而非探索;C 混淆了不确定性的来源;D 说的是轮次而非臂被试验的次数,均不正确。

Q4. Thompson 采样中 Beta(α+1, β+1) 的参数含义是什么?

单选题 · mid

  • A.α 是好评次数,β 是差评次数← 正确答案
  • B.α 是探索次数,β 是利用次数
  • C.α 是均值,β 是方差
  • D.α 是学习率,β 是衰减率

答案:A。Beta 分布的两个参数分别对应成功次数(好评)与失败次数(差评),各加 1 是先验平滑。B 混淆了采样过程与探索利用;C 是正态分布的参数;D 是优化器概念,均不符合 Thompson 采样的设定。

Q5. LinUCB 相比经典 UCB 的核心改进是什么?

单选题 · mid

  • A.用深度神经网络替代线性模型
  • B.引入上下文特征实现个性化← 正确答案
  • C.取消探索项只保留均值预测
  • D.把在线学习改成离线训练

答案:B。LinUCB 把用户与物品的上下文特征向量引入 UCB 框架,用线性模型拟合“特征到奖励”的映射,从而做到千人千面的个性化决策。A 不是 LinUCB 的默认设定;C、D 与 LinUCB 的设计背道而驰。

Q6. 下列哪些场景适合用 Contextual Bandit 解决?

多选题 · mid

  • A.新注册用户的兴趣冷启动← 正确答案
  • B.新广告素材的动态流量分配← 正确答案
  • C.存量用户全量画像离线分析
  • D.在线实验版本的动态分流← 正确答案

答案:ABD。Contextual Bandit 适合“有上下文、需在线决策、数据稀疏”的场景,冷启动、素材分流与在线实验正是典型。C 是全量离线分析,没有在线探索动作与即时奖励反馈,不属于 Bandit 的适用范围。

Q7. 关于遗憾值 Regret 的理解,正确的是?

单选题 · hard

  • A.遗憾值越小说明模型推理越快
  • B.遗憾值是实际与最优收益的累计差← 正确答案
  • C.遗憾值等于累计奖励的最大值
  • D.遗憾值越大说明探索越充分

答案:B。遗憾值是“每轮最优奖励 − 实际奖励”的逐轮累加,衡量一个策略相比“事后再看的最优策略”少赚了多少。它与推理速度无关;它是差距而非奖励本身;遗憾大只说明决策差,不代表探索有效。

Q8. epsilon-greedy 的主要局限有哪些?

多选题 · hard

  • A.随机探索不区分臂的不确定性← 正确答案
  • B.固定 ε 会浪费大量探索流量← 正确答案
  • C.无法利用用户上下文特征← 正确答案
  • D.完全用不了任何历史数据

答案:ABC。ε-greedy 的随机探索对每只臂一视同仁,不考虑哪只臂更有信息价值;固定 ε 让差臂持续拿到流量;算法层面没有特征向量,因此无法个性化。但它会利用历史数据选出当前最优臂,故 D 错误。

Q9. 在非稳态场景(配方热度随季节变化)下,应如何处理?

单选题 · hard

  • A.固定 ε 且永远不再调整
  • B.删除所有历史数据重新开始
  • C.使用滑动窗口或衰减权重遗忘旧数据← 正确答案
  • D.停止探索只保留最优臂

答案:C。非稳态环境下,旧数据反映的是过去的分布,继续全量使用会误导决策。滑动窗口只保留近期样本,指数衰减则给旧样本降低权重,让模型跟上变化。A、D 会僵化,B 过于激进损失信息。

Q10. 关于 Bandit 与 A/B test 的关系,正确的说法是?

多选题 · hard

  • A.二者完全等价可以互相替换
  • B.Bandit 可动态调整流量分配← 正确答案
  • C.Bandit 适合快速低成本取舍← 正确答案
  • D.Bandit 无法用于线上实验

答案:BC。Bandit 会随反馈动态调流量,向领先版本倾斜,适合快速、低成本的候选方案取舍;A/B test 通常固定分流比直到实验期满。两者并非等价,Bandit 恰恰常用于线上实验,因此 A、D 错误。