想象你是一家新开张的面馆老板,店里同时推出了四种新口味的汤底:番茄、菌菇、麻辣和骨汤。你只有足够接待一百位顾客的食材,而你不知道哪种口味最受欢迎。你会怎么做?是平均分配给每位顾客试吃,还是先让一小批人试吃,然后迅速把食材集中到卖得最好的口味上?这个问题在数学家和工程师眼里,就是一个经典的“多臂老虎机”问题。 多臂老虎机这个名字来自赌场里的老虎机——如果你面前有一排老虎机(“多臂”),每台的中奖率不同,而你只有有限数量的硬币,你该如何分配你的下注,才能在收益最大化的同时,还能搞清楚哪台机器最值得玩?在真实的推荐系统里,“老虎机”变成了不同的算法模型,“硬币”成了涌入的用户流量,“中奖”则意味着用户点击了推荐的商品。这就是A/B测试背后那个聪明的大脑。 传统A/B测试像一位固执的农夫,把种子固定分成几块田,等收成后再判断哪块地最好——这需要等待,且浪费了原本可以多种好田的机会。而多臂老虎机算法则像一位精明的赌徒,边玩边学,越玩越精明。它能动态调整流量分配,把更多用户引导到表现更好的模型上,大大缩短了找到“最优口味”的时间,同时也减少了“机会成本”——那些本可以体验更好模型而流失的用户。 今天,我们就走进这个赌场,看看这位聪明的“赌徒”是如何思考、如何决策的。你会发现,它不仅是数学公式,更是一种生活和工程中无处不在的智慧。

岗位关联

多臂老虎机算法是现代推荐系统和个性化策略的核心引擎。掌握它,意味着你不再是机械地做实验,而是能动态优化每一次用户交互,在节省成本的同时提升模型收益——这是每位AI工程师和MLOps从业者都应具备的进阶思维。

目录
  1. 可视化演示
  2. 决策困境起源
  3. 数学建模框架
  4. ε-贪心策略
  5. UCB算法
  6. 汤普森采样
  7. 工程落地实践
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示多臂老虎机探索与利用平衡、UCB置信上界选择策略、MAB与传统A/B测试流量分配对比。切换模式查看各算法维度,拖动滑块调节探索率。

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

多臂老虎机探索与利用 多臂老虎机(MAB)面对K台未知回报的老虎机,在有限次数内最大化总奖励,核心矛盾是探索(尝试不确定的臂)与利用(选已知好的臂)的平衡 ε-greedy以ε概率随机探索、1-ε概率利用当前最优;自适应策略如UCB和Thompson Sampling动态调整探索强度,实现更优的遗憾界 regret = T·μ* - Σ μ(at) 遗憾是衡量MAB策略的核心指标

决策困境起源

回到我们开篇的面馆。假设你请来一百位顾客品尝四种汤底,然后把票数最高的汤底定为招牌。这个方法看似公平,但存在一个致命问题:如果前三十位顾客里恰好多数人偏好麻辣,而后面来的七十位顾客可能更喜欢骨汤——但你已经没机会证明了,因为食材用完了。这就是最朴素的多臂老虎机困境:你在信息不足时做出的选择,可能让后来的收益落空。

在数学和工程的世界里,这个困境被抽象成了赌场里的“多臂老虎机”(Multi-Armed Bandit, MAB)问题。假设你走进赌场,面前有 K 台老虎机,每台机器的中奖概率不同且未知。你手里只有 N 枚硬币,每次只能拉下其中一台机器的摇杆。你的目标是在 N 次操作后,让总收益最大化。你面临的核心矛盾就是探索(探索那些尚未确定好坏的新机器)与利用(把硬币都投给目前看来最好的那台机器)之间的权衡。

如果我们用工程语言来翻译:老虎机的每一条“臂”就是一个待测的方案——可以是一个推荐模型、一个界面设计、甚至一条文案。拉一次摇杆就是向用户展示一次该方案,并获得一个反馈信号,比如“是否点击”或“是否购买”。所谓“收益最大化”,就是让尽可能多的用户得到他们满意的体验,同时让业务方赚到更多钱。而“机会成本”正是那些因为流量分配给了差方案而错失的潜在转化。

回到面馆,那位精明的老板并不会傻傻地平均分配食材。他会先让每位顾客试一小口(低成本探索),然后迅速加大卖得好的口味的生产量(利用)。但这一“小口”到底该多大?该试多少次才切换策略?这正是接下来我们要用数学回答的问题。

Rtotal=∑t=1Trt\text{R}_{total} = \sum_{t=1}^{T} r_t
累计总收益等于每一步获得的奖励之和。如果我们能全程使用最优方案,总收益会更高;二者的差距就是“后悔度”。
  1. 总收益符号\\( \text{R}_{total} \\) 表示整个实验期间获得的累计奖励,在推荐系统中可以理解为总点击量或总转化额。
    Rtotal=∑t=1Trt\displaystyle \text{R}_{total} = \sum_{t=1}^{T} r_t
  2. 累计求和\\( \sum_{t=1}^{T} \\) 表示从第 1 轮到第 T 轮把所有奖励加起来。每一次拉杆获得 \\( r_t \\),它只有 1(中奖)或 0(没中)两种取值。
    rt∈{0,1}\displaystyle r_t \in \{0, 1\}
  3. 遗憾的来源如果我们提前知道最优臂,理论上可获得 \\( T \times \mu^* \\) 的收益。之所以实际收益更少,是因为我们花了部分轮次去探索其他臂。
    Regret=T⋅μ∗−E[∑t=1Trt]\displaystyle \text{Regret} = T \cdot \mu^* - \mathbb{E}\left[\sum_{t=1}^{T} r_t\right]

数学建模框架

要把面馆老板的直觉变成可计算的算法,我们得先做一次“物理抽象”。面馆里的每种汤底,抽象成老虎机的一条“臂”;每位顾客的品尝反馈(好吃/不好吃),抽象成一次伯努利试验——结果只有成功(1)或失败(0)。每种汤底背后藏着一个我们不知道的“真实好评率” \\( \mu_i \\),我们只能通过一次次试验去估计它。

现在,我们再引入一个关键概念:后悔度(Regret)。后悔度衡量的是“如果我一直用最优策略,本来能赚多少”与“我实际赚了多少”之间的差距。它像一个计分板,直观地告诉我们:某个策略在探索和利用之间平衡得有多好。理想情况下,随着试验次数增加,平均每次操作的后悔应该趋近于零——这意味着我们已经找到了最优臂并充分加以利用。

为了刻画每个臂估计值的不确定性,我们还需要描述“置信区间”。简单说,置信区间就是告诉我们“真实好评率大概落在哪个范围内”。一开始,我们对所有臂一无所知,所以区间都很宽;随着试验增多,被试验得多的臂区间逐渐收窄。多臂老虎机算法们(UCB、汤普森采样)的精髓,就是在“区间宽度”上做文章。

这个公式像一张成绩单,告诉我们所有花在非最优臂上的试验都是一种“学费”。我们的目标就是想办法少交学费、多拿收益。接下来出场的三位选手——ε-贪心、UCB 和汤普森采样——正是三种不同的“交学费策略”。

RegretT=Tμ∗−∑i=1KμiE[Ni(T)]\text{Regret}_T = T \mu^* - \sum_{i=1}^{K} \mu_i \mathbb{E}[N_i(T)]
后悔度 = 最优臂的累计期望收益 - 各臂实际累计收益。\\( N_i(T) \\) 表示前 T 轮中第 i 个臂被选中的次数。
  1. 最优臂收益\\( \mu^ \\) 是所有臂中真实奖励率最高的那个值。如果我们每次选它,T 轮的期望收益就是 \\( T \mu^ \\)。
    μ∗=max⁡1≤i≤Kμi\displaystyle \mu^* = \max_{1 \le i \le K} \mu_i
  2. 实际收益每个臂被选了 \\( N_i(T) \\) 次,每次以概率 \\( \mu_i \\) 带来奖励,所以它对总收益的期望贡献是 \\( \mu_i \mathbb{E}[N_i(T)] \\)。
    E[∑t=1Trt]=∑i=1KμiE[Ni(T)]\displaystyle \mathbb{E}\left[\sum_{t=1}^{T} r_t\right] = \sum_{i=1}^{K} \mu_i \mathbb{E}[N_i(T)]
  3. 差距即遗憾把前两项相减,得到后悔度。如果算法擅长找到最优臂并集中选择它,那么 \\( N_{\text{最优}}(T) \\) 会接近 T,后悔度增长速度缓慢。
    RegretT=Tμ∗−∑i=1KμiE[Ni(T)]\displaystyle \text{Regret}_T = T \mu^* - \sum_{i=1}^{K} \mu_i \mathbb{E}[N_i(T)]

ε-贪心策略

如果说多臂老虎机是一场考试,那么 ε-贪心(Epsilon-Greedy)就是那位“大部分时间刷真题,偶尔翻翻新题集”的考生。他给自己定下规矩:在 ε 比例的时间里,放弃当前最优选择,随机尝试一个未知的方案;在剩下的 1-ε 时间里,坚定不移地选择目前平均收益最高的方案。

这个策略简单得令人发指,但它的弱点也显而易见:探索的比例是固定的。哪怕我们已经做了一万次试验,完全确定第 2 个臂是最优的,ε-贪心仍然会以 ε 的概率去随机拉其他臂,造成不必要的浪费。这意味着它的后悔度会随着时间线性增长——即“线性遗憾”。

从面馆的角度看,这就像是老板永远固执地保留 20% 的食材去做新口味试验,哪怕其中三种口味已经被证明没有竞争力,也不肯把这点食材省下来给畅销口味。当然,工程师们想出了改进方案:让 ε 随时间衰减,比如 \\( \varepsilon_t = \frac{1}{t} \\)。这样算法前期多探索,后期少探索,理论上能把线性遗憾压到对数遗憾。

回到考场比喻,ε-贪心考生的问题在于他“翻新题”的频率不会随着考试进度而降低。一个更聪明的考生应该在考试前期多翻新题(因为信息不足),后期少翻甚至不翻(因为已经胸有成竹)。这种“动态调整探索意愿”的思想,正是 UCB 算法的起点。

μ^i=∑t=1TI(at=i)⋅rtNi(T)\hat{\mu}_i = \frac{\sum_{t=1}^{T} \mathbb{I}(a_t = i) \cdot r_t}{N_i(T)}
\\( \hat{\mu}_i \\) 是第 i 个臂的经验平均奖励,即“迄今赚到的总奖励除以拉它的次数”。ε-贪心就是根据这些平均值来决定是否“贪心”。
  1. 经验均值\\( N_i(T) \\) 是前 T 轮中臂 i 被选中的次数,分子是所有选中臂 i 时的奖励之和,比值就是该臂目前的平均成绩。
    μ^i=∑t=1TI(at=i)⋅rtNi(T)\displaystyle \hat{\mu}_i = \frac{\sum_{t=1}^{T} \mathbb{I}(a_t = i) \cdot r_t}{N_i(T)}
  2. 指示函数\\( \mathbb{I}(a_t = i) \\) 是“第 t 轮动作是否为臂 i”的开关,为真取 1,为假取 0。它保证我们只累加属于臂 i 的奖励。
    I(at=i)={1,at=i0,at≠i\displaystyle \mathbb{I}(a_t=i) = \begin{cases} 1, & a_t = i \\ 0, & a_t \ne i \end{cases}
  3. 决策规则以 \\( 1-\varepsilon \\) 的概率选择 \\( \arg \max_i \hat{\mu}_i \\),以 \\( \varepsilon \\) 的概率均匀随机选择一个臂。
    at={arg⁡max⁡iμ^i,以概率 1−ε均匀随机,以概率 ε\displaystyle a_t = \begin{cases} \arg\max_i \hat{\mu}_i, & \text{以概率 } 1-\varepsilon \\ \text{均匀随机}, & \text{以概率 } \varepsilon \end{cases}

UCB算法

UCB(Upper Confidence Bound,置信上界)算法的思路,可以理解为一位“乐观主义者”的决策方式:每个方案的真实质量未知,但我根据已有数据可以算出它最乐观的上界。然后我每次都选择“最乐观上界最高的那个方案”。为什么不选平均收益最高的方案?因为平均收益高的方案可能只是走了好运,而一个试验次数少的方案虽然平均值低,却可能潜力巨大——它的置信上界可能远高于前者。

这就是 UCB 的核心洞察:用不确定性驱动探索。试得越少的臂,其置信区间越宽,上界越高,就越容易被选中;试得越多的臂,置信区间收窄,上界回归平均值附近。这样,探索和利用的权衡不是人为设定的固定比例,而是由统计规律自然涌现。UCB 的遗憾增长是对数级的——这意味着它的“学费”远低于 ε-贪心。

从数学上看,UCB 选择第 t 轮动作的规则是:均值 + 探索奖励项。奖励项与“该臂已经探索的次数的对数”成正比,与该臂自身被拉的次数成反比。这使得一个臂被忽略得越久,它的“乐观上界”就会涨得越高,最终被重新拾起。这就是 UCB 能对上界进行“重新审视”的机制。

UCB 就像一位既乐观又谨慎的品鉴师:他会持续关注那道评分最高的菜,但也会因为太久没让某道冷门菜上桌而主动给它一次机会。正是这种“不甘心错过”的机制,让 UCB 在数学上被证明能实现对数遗憾。不过,UCB 需要对每个臂维护独立的参数,且公式里的常数 \\( \sqrt{2} \\) 在实际工程中需要调优。接下来出场的汤普森采样,则提供了一种完全不同的贝叶斯视角。

at=arg⁡max⁡i[μ^i+2ln⁡tNi(t)]a_t = \arg\max_{i} \left[ \hat{\mu}_i + \sqrt{\frac{2 \ln t}{N_i(t)}} \right]
UCB 的决策 = 平均收益 + 不确定性奖励。被探索得越少的臂,第二项越大,越容易被“捞起来”再给一次机会。
  1. 均值项\\( \hat{\mu}_i \\) 是臂 i 当前的平均收益,相当于“我觉得它有多好”。这是一切的基准。
    μ^i=1Ni(t)∑s=1trsI(as=i)\displaystyle \hat{\mu}_i = \frac{1}{N_i(t)} \sum_{s=1}^{t} r_s \mathbb{I}(a_s=i)
  2. 不确定性项\\( \sqrt{ \frac{2 \ln t}{N_i(t)} } \\) 是置信区间宽度的一半。\\( N_i(t) \\) 越小,这一项越大;\\( \ln t \\) 随时间增长,所以长期被冷落的臂会被重新“点亮”。
    UCBi=μ^i+2ln⁡tNi(t)\displaystyle \text{UCB}_i = \hat{\mu}_i + \sqrt{\frac{2 \ln t}{N_i(t)}}
  3. 选择规则取 UCB 值最大的臂。这是一个理性乐观主义者的选择——它不赌某个臂真的最好,而是选择“最值得再玩一次”的臂。
    at=arg⁡max⁡1≤i≤KUCBi\displaystyle a_t = \arg\max_{1 \le i \le K} \text{UCB}_i

汤普森采样

如果说 UCB 是乐观主义者的算法,那么汤普森采样(Thompson Sampling)就是贝叶斯学派的“直觉派”算法。它的核心思想是:我们不需要刻意设计探索机制,只需要给每个臂的真实收益概率估计一个“信仰分布”(后验分布),然后从这些分布中随机抽样,选择抽样值最大的臂。

听起来绕,但面馆场景一下子就能讲清楚。假设老板对四种汤底的好评率心里各有一个“估计曲线”(概率分布)。骨汤好评率分布集中在那 70% 附近,而菌菇汤的分布可能还扁扁地横跨 20%~60%——因为试过的人太少,老板很不确定。每次决定推哪种口味时,老板不直接用平均值,而是分别从四条曲线中随机抽一个值,选抽到最高的那个。这样,大概率会抽中骨汤的高值,但也偶尔抽中菌菇汤的高值——于是菌菇汤获得一次试水机会。

这在数学上是通过 Beta 分布实现的。Beta 分布有两个参数 \\( \alpha \\) 和 \\( \beta \\),可以优雅地表示“至今中奖次数 + 1”和“失败次数 + 1”。每得到一个新反馈,就把对应臂的 \\( \alpha \\) 或 \\( \beta \\) 加一,分布就更新一次。这种“每来一个数据就更新一次信仰”的方式,让汤普森采样在工程实现上非常简洁,且有理论证明其遗憾也能达到对数级(甚至渐近最优)。

汤普森采样的美妙之处在于它完全不需要“显式探索”。探索是自然而然发生的——当某个臂的不确定性很大(分布很宽),它就有很大机会被抽出一个偏高的值从而被选中。试想面馆老板每天根据当天的心情(采样值)决定主推口味,凡是还有悬念的口味都会时不时被端上桌来。这种“随缘又不失明智”的策略,难怪在大规模工业推荐系统中备受青睐。

θi∼Beta(αi,βi)\theta_i \sim \text{Beta}(\alpha_i, \beta_i)
汤普森采样的核心:每个臂的“真实奖励率”用一个 Beta 分布来描述,我们通过采样来决策。抽出来的 \\( \theta_i \\) 是“根据现有信息对这个臂质量的猜测”。
  1. Beta分布\\( \theta_i \\) 表示第 i 个臂的真实奖励率。我们用 Beta 分布描述它的不确定性,其中 \\( \alpha_i = \text{成功次数}+1 \\),\\( \beta_i = \text{失败次数}+1 \\)。
    θi∼Beta(αi,βi)\displaystyle \theta_i \sim \text{Beta}(\alpha_i, \beta_i)
  2. 后验更新每次试验后,如果臂 i 成功则 \\( \alpha_i \\) 加 1,失败则 \\( \beta_i \\) 加 1。这条规则让分布随数据不断“瘦身”,越来越接近真实值。
    (αi,βi)←(αi+rt,βi+1−rt)\displaystyle (\alpha_i, \beta_i) \leftarrow (\alpha_i + r_t, \beta_i + 1 - r_t)
  3. 采样决策第 t 轮时,从每个臂的 \\( \text{Beta}(\alpha_i, \beta_i) \\) 中各抽取一个 \\( \theta_i \\),选择 \\( \arg\max_i \theta_i \\) 对应的臂。
    at=arg⁡max⁡1≤i≤Kθi,θi∼Beta(αi,βi)\displaystyle a_t = \arg\max_{1 \le i \le K} \theta_i, \quad \theta_i \sim \text{Beta}(\alpha_i, \beta_i)

工程落地实践

理论说了一堆,面馆老板终究得回到灶台前。真实工程中的问题往往比数学假设要“邋遢”得多:流量不是均匀到达的,用户不是同质的,模型可能随时掉点,甚至我们今天推出的“最优口味”下周就过季了。因此,把多臂老虎机算法落地到 A/B 测试中,需要一套完整的工程流程。

第一步永远是小流量验证。即使汤普森采样理论上再完美,也必须在 1%~5% 的流量上先跑一段时间,观察数据是否符合预期:奖励信号是否有噪声?参数服务器是否扛得住?延迟是否超标?这就像面馆先请十位熟客试吃,而不是直接上架全城推广。第二步是设计动态路由:根据在线反馈实时调整流量比例,需要保证同一个用户尽量被路由到同一个臂(避免“分裂实验”造成前后体验不一致)。第三步是监控预警:多臂老虎机算法会自主地倾斜流量,我们需要监控每个臂的曝光量、奖励率、以及延迟,一旦出现数据异常(比如某臂奖励率骤降到 0.1 以下),立即触发熔断。

最后,还有一个容易被忽略的问题:冷启动策略。新模型上线时没有任何历史数据,Beta 分布参数全为 (1,1),U CB 的不确定性项接近无穷大。如果直接上汤普森采样,新模型可能会获得过多流量。一个常见的做法是先按固定比例(比如 50/50)给新旧模型各分配一部分流量做预热,等新模型积累了足够的样本量后再启动多臂老虎机算法接管。

从面馆到推荐系统,这套工程方法论的灵魂是一致的:小步快跑、实时反馈、快速修正。面馆老板今天主推骨汤,明天发现顾客对番茄汤的复购率在上升,就立刻增加番茄汤的出货量——这正是多臂老虎机在 MLOps 体系中最具价值的应用场景。

traffici(t)=scoresi(t)∑j=1Kscoresj(t)\text{traffic}_i(t) = \frac{\text{scores}_i(t)}{\sum_{j=1}^{K} \text{scores}_j(t)}
流量分配公式:将每个臂的“得分”(可以是 UCB 值、采样值或经过平滑的经验均值)归一化,得到每个臂的流量占比。这样就把算法决策与真实路由器对接起来。
  1. 得分函数每个臂有一个反映其优劣的得分。汤普森采样用 \\( \theta_i \\)(Beta 分布采样值),UCB 用 \\( \hat{\mu}_i + \text{bonus}_i \\)。
    scoresi(t)={θi,汤普森μ^i+2ln⁡tNi(t),UCB\displaystyle \text{scores}_i(t) = \begin{cases} \theta_i, & \text{汤普森} \\ \hat{\mu}_i + \sqrt{\frac{2\ln t}{N_i(t)}}, & \text{UCB} \end{cases}
  2. 归一化比例把各臂得分除以总和,得到 0~1 之间的流量占比。得分越高的臂获得越多流量。
    traffici(t)=scoresi(t)∑j=1Kscoresj(t)\displaystyle \text{traffic}_i(t) = \frac{\text{scores}_i(t)}{\sum_{j=1}^{K} \text{scores}_j(t)}
  3. 平滑处理为了避免流量分配剧烈抖动,工程师常对得分做指数滑动平均(EMA)或加一个最小流量下限 \\( \delta \\),确保每个臂始终保留少量探索流量。
    traffici′(t)=max⁡(traffici(t),δ)\displaystyle \text{traffic}_i'(t) = \max\left( \text{traffic}_i(t), \delta \right)

AI 应用场景

场景一:推荐系统商品流动态分配

阶段:在线模型评估与流量调度(MLOps 中的模型上线与监控阶段)

解决的问题:传统 A/B 测试用固定比例(如 50/50)分流,需要积累数周数据才能得出显著结论,期间有大量用户被分配到劣质模型中,造成点击率损失。多臂老虎机算法(如汤普森采样)让模型在测试期间就动态获得更多流量,点击率高的一方快速胜出,同时避免探索不足导致的误判。

具体案例:某电商 App 首页“猜你喜欢”模块有两个候选模型:模型 A 是优化后的图神经网络模型,模型 B 是当前线上版本。平台先以 10% 流量对模型 A 做预热,积累 10 万样本后用汤普森采样动态分配。算法迅速识别出模型 A 在不同品类上的条件优势,为它分配了 70% 的流量,最终整体点击率比固定 50/50 分流提升了 8%。

场景二:活动页 UI 版本动态优选

阶段:前端实验平台(A/B 实验管理与策略下发)

解决的问题:营销活动页的 UI 设计往往有多个备选方案(不同配色、不同文案位置),传统方法是等分流量然后等整个活动结束再分析。活动周期短(往往只有 3~7 天),等数据攒够活动已经结束了。多臂老虎机算法可以在活动开始后的 24 小时内快速收敛,把大部分流量导向最优版本。

具体案例:某旅行 App 的“双十一”大促专题页有 3 套 UI 方案。界面团队借用 UCB 算法进行动态分配:第一小时三个方案各获 33% 流量,之后每小时更新一次置信上界,第 6 小时方案 C 的优势已经显著,系统将其流量自动提升到 80%。活动结束时,相比固定等分方案,整体转化率提升了 15%。

Python 演示

python92 行
import randomimport math  class BernoulliBandit:    """每一台老虎机: 真实中奖率为 p, 返回0或1"""     def __init__(self, p):        self.p = p        self.best = 0.0     def pull(self):        return 1 if random.random() < self.p else 0  def run_ep_greedy(bandits, steps, eps=0.1):    """epsilon-greedy 策略"""    counts = [0] * len(bandits)    rewards = [0.0] * len(bandits)    total = 0.0    history = []    for t in range(1, steps + 1):        if random.random() < eps:            i = random.randrange(len(bandits))        else:            avg = [r / c if c > 0 else 0.0 for r, c in zip(rewards, counts)]            i = max(range(len(bandits)), key=lambda x: avg[x])        r = bandits[i].pull()        counts[i] += 1        rewards[i] += r        total += r        history.append(total)    return total, history  def run_ucb(bandits, steps):    """UCB1 策略"""    counts = [0] * len(bandits)    rewards = [0.0] * len(bandits)    total = 0.0    history = []    for t in range(1, steps + 1):        ucb = []        for i in range(len(bandits)):            if counts[i] == 0:                ucb.append(float("inf"))            else:                avg = rewards[i] / counts[i]                bonus = math.sqrt(2.0 * math.log(t) / counts[i])                ucb.append(avg + bonus)        i = max(range(len(bandits)), key=lambda x: ucb[x])        r = bandits[i].pull()        counts[i] += 1        rewards[i] += r        total += r        history.append(total)    return total, history  def run_thompson(bandits, steps):    """Thompson Sampling 策略 (Beta分布采样)"""    alpha = [1] * len(bandits)  # 成功次数 + 1    beta = [1] * len(bandits)   # 失败次数 + 1    total = 0.0    history = []    for _ in range(steps):        samples = [random.betavariate(a, b) for a, b in zip(alpha, beta)]        i = max(range(len(bandits)), key=lambda x: samples[x])        r = bandits[i].pull()        alpha[i] += r        beta[i] += 1 - r        total += r        history.append(total)    return total, history  def main():    random.seed(42)    bandits = [BernoulliBandit(0.2), BernoulliBandit(0.5),               BernoulliBandit(0.7), BernoulliBandit(0.4)]    steps = 2000    for name, func in [("eps-greedy", run_ep_greedy),                       ("UCB1", run_ucb),                       ("Thompson", run_thompson)]:        total, hist = func(bandits, steps)        print(f"{name:>10}: total reward = {total:.0f}")    best = max(b.p for b in bandits)    print(f"最优臂真实概率 = {best}, 理论上限 = {best * steps:.0f}")  if __name__ == "__main__":    main()
目的:在四台“中奖率不同”的伯努利老虎机上对比 ε-贪心、UCB1 和汤普森采样三种策略的累计收益,验证多臂老虎机算法的收敛能力。
  1. random.betavariate(a, b)从 Beta 分布中采样,是汤普森采样最核心的一行操作,等价于手写 random.gammavariate 比值采样。
  2. avg + math.sqrt(2 * math.log(t) / counts[i])UCB 的“乐观奖励”:被探索次数越少的臂,这个加成越大。
  3. max(range(len(bandits)), key=lambda x: samples[x])选出采样值最大的臂,让贝叶斯“信仰”直接指挥动作。
  4. alpha[i] += r; beta[i] += 1 - r用当前轮反馈原位更新后验分布参数,实现“在线学习”。
  5. history.append(total)记录累计奖励曲线,方便事后可视化对比策略优劣。

常见误区

误区“A/B 测试不就是把用户分成几组,各给 50% 流量,然后看哪个方案数据好吗?多臂老虎机应该也类似,只是多分几组而已。”

正解多臂老虎机与固定比例分流的本质区别在于“动态性”。固定比例分流全程不改变流量分配,多臂老虎机则会根据实时反馈不断将流量向表现更好的方案倾斜。它不需要等到实验结束再做统计检验,而是在过程中就持续做决策——本质上是一个在线学习算法,而不是一个离线统计工具。用面馆比喻来说:固定分流是“每道菜各做 25 份等顾客评价”,多臂老虎机是“哪道菜卖得快就多做一些,同时保留少量给冷门菜试卖”。

为何常见:很多初学者先接触传统 A/B 测试的假设检验框架,自然把多臂老虎机理解成“更细粒度的分组测试”,而没有意识到它在决策模式上已经从“先实验后决策”变成了“边实验边决策”。

误区“既然探索会浪费流量,那把 ε 设成 0.01 甚至 0,把所有流量都给目前最好的方案,收益一定最大。”

正解如果 ε = 0,算法变成纯粹的“贪心选取当前最优”,它将永远无法发现新方案可能更好。尤其在非平稳环境下(用户偏好会变化),不探索会让你困在局部最优里。更严重的是,由于每个臂的均值估计有噪声,当前“看起来最好”的臂可能只是运气好。合理的 ε 应该在 0.05~0.2 之间,或者随时间衰减。数学上已经证明,固定 ε 的算法遗憾线性增长,而衰减 ε 才能达到对数遗憾——也就是说,适当探索不仅不会损失太多收益,反而是长期收益最大的必要前提。

为何常见:人们容易把“利用”理解为“收益”,把“探索”理解为“浪费”,忽略了探索对于信息收集的长期价值。

误区“论文上说了 UCB 和汤普森采样是对数遗憾,ε-贪心是线性遗憾,那工程项目直接用 UCB 或汤普森采样就完事了。”

正解理论上的遗憾界是渐进性质,在小样本场景下不一定更优。而且 UCB 假设奖励分布是次高斯分布,汤普森采样的 Beta 分布先验也可能与真实数据不符。在工程实际中,算法的效果受数据噪声、流量波动、业务约束(如某些臂必须保证最低流量)影响很大。一个经过调参的衰减 ε-贪心,在几千样本的小实验里可能表现得比理论最优算法更稳定。正确做法是先在离线数据上回测,再小流量在线验证,而不是一上来就上最“高级”的算法。

为何常见:初学者容易被理论最优性吸引,忽略工程环境与理论假设之间的差距。

误区“既然多臂老虎机能在更短时间内找到最优方案,那就把所有 A/B 测试都换成多臂老虎机吧,省时省力。”

正解多臂老虎机擅长的是“在多个已知候选中快速找到并倾向最优者”,适合流量大、反馈快、需要实时决策的场景。但当你需要对某个改进方案做严格的统计显著性验证(比如要向管理层证明新模型比旧模型提升 2%),传统 A/B 测试的固定流量、预设样本量、置信区间报告仍然是不可替代的。此外,多臂老虎机的“后悔度”优化目标并不保证实验结束时你能得到一个可信的 p 值,它追求的是“过程收益最大”,而非“结论可发表”。工程上常见的做法是:先用传统 A/B 测试验证方向正确,再用多臂老虎机做流量优化和持续调参。

为何常见:新兴技术容易被捧为万能钥匙,忽略了统计推断和在线决策在目标上的本质差异。

面试题

Q1. 多臂老虎机问题中,“臂”(Arm)所代表的含义最准确的是?

单选题 · easy

  • A.老虎机的摇杆
  • B.一个待试验的可选方案← 正确答案
  • C.一个用户流量
  • D.一个奖励信号

答案:B。在多臂老虎机中,“臂”抽象为每个可选的行动方案,比如不同的推荐模型或 UI 设计。A 是字面义,C 是流量本身不是臂,D 是反馈信号,都不是臂的含义。

Q2. 探索与利用的权衡,从面馆比喻来看最贴切的描述是?

单选题 · easy

  • A.多进货几种食材以防缺货
  • B.既要多卖畅销菜,又要留点机会试新菜← 正确答案
  • C.只做一道最受欢迎的菜
  • D.每天换一道新菜

答案:B。多臂老虎机需要在利用(集中资源给目前最优臂)和探索(试验其他潜在好臂)之间平衡。B 准确描述了这种动态权衡;C 只利用不探索,D 只探索不利用,A 是供应链问题。

Q3. 在 UCB1 算法中,下列哪个臂最有可能被选中?

单选题 · mid

  • A.平均奖励最高,试验次数也最多的臂
  • B.平均奖励中等,试验次数极少的臂← 正确答案
  • C.平均奖励最低,试验次数最少的臂
  • D.平均奖励最低,试验次数最多的臂

答案:B。UCB = 均值 + 探索项。B 的均值虽然不高,但试验次数极少,探索项 \\( \sqrt{2\ln t / N_i} \\) 非常大,二者相加可能超过 A 的高均值+小探索项。C 均值太差即使有高探索项也难超越;D 探索项小且均值低,最不可能。

Q4. 下列哪些策略属于“自适应”的流量分配算法?

多选题 · mid

  • A.以固定 50/50 比例分流
  • B.ε-贪心← 正确答案
  • C.UCB1← 正确答案
  • D.汤普森采样← 正确答案

答案:BCD。自适应指的是随反馈动态调整分配。B、C、D 都会根据历史奖励数据实时调整选择概率。A 是传统的固定比例分流,全程不变化,故不选。

Q5. 如果某臂的 Beta 分布参数为 Beta(1,1),它的含义是?

单选题 · mid

  • A.该臂已经成功 1 次失败 1 次
  • B.该臂完全没有任何观测数据(先验分布)← 正确答案
  • C.该臂真实中奖率为 0.5
  • D.该臂已被拉过 2 次且都失败

答案:B。Beta(1,1) 是均匀分布,代表“没有成功也没有失败”的先验状态,即冷启动时的默认状态。A 有观测不对;C 真实概率未知不是因为先验是 0.5;D 观测 2 次失败对应 Beta(1,3)。

Q6. 关于 ε-贪心的线性遗憾,下列说法正确的是?

单选题 · hard

  • A.因为 ε 太大导致每次选择都不是最优
  • B.因为无论试验多少次,探索概率恒定,浪费的尝试次数随时间线性累积← 正确答案
  • C.因为贪心导致算法陷入局部最优
  • D.因为算法不具备任何探索能力

答案:B。固定 ε 的 ε-贪心每轮都有 ε 的概率选非最优臂,累计浪费的期望次数是 εT,与 T 线性相关,所以后悔度线性增长。A 是原因但不是线性遗憾的关键;C 是贪心不探索会线性增长但不是“固定 ε”导致的;D 明显错误。

Q7. 在多臂老虎机的工程落地中,小流量验证的目的包括?

多选题 · hard

  • A.验证算法代码正确性← 正确答案
  • B.避免劣质方案影响过大← 正确答案
  • C.收集先验数据供冷启动← 正确答案
  • D.保证 p 值显著

答案:ABC。小流量验证可以检查代码 bug、评估算法在真实流量上的表现、积累初始数据。D 是传统假设检验的目标,多臂老虎机以优化过程收益为目标,并不追求可检验的 p 值。

Q8. 后悔度(Regret)衡量的是什么?

单选题 · easy

  • A.一直选最优臂与实际策略的累计收益差距← 正确答案
  • B.用户点了不喜欢的内容后后悔的次数
  • C.算法运行消耗的内存大小
  • D.流量分配不均导致的方差

答案:A。后悔度定义为最优策略的期望累计收益与实际策略期望收益之差。它直接量化了因“探索和利用没做到最优”产生的损失。B、C、D 与此无关。

Q9. 汤普森采样与 UCB 的最大理论差异在于?

单选题 · mid

  • A.汤普森采样基于贝叶斯后验采样,UCB 基于置信区间上界← 正确答案
  • B.汤普森采样只能做伯努利奖励,UCB 只支持高斯奖励
  • C.汤普森采样没有探索能力
  • D.UCB 一定比汤普森采样收敛更快

答案:A。两者的核心区别是决策依据:汤普森采样从后验分布采样,用随机样本做决策;UCB 直接计算置信上界取最大。B 两者都可以扩展到多种分布;C 错误,探索由采样的随机性天然产生;D 没有绝对优势。

Q10. 关于多臂老虎机与传统 A/B 测试的关系,正确的说法是( )。

多选题 · hard

  • A.多臂老虎机可用于 A/B 测试的“快速探索”阶段← 正确答案
  • B.传统 A/B 测试提供严格的显著性结论← 正确答案
  • C.两者可组合使用:先用 A/B 验证假设再用老虎机调优← 正确答案
  • D.多臂老虎机完全可以取代传统 A/B 测试

答案:ABC。多臂老虎机适合在候选方案中快速分流,传统 A/B 测试适合严格的统计推断,两者在流程上互补而非替代。D 夸大了老虎机的能力。