1. 程式人生 > >推薦系統遇上深度學習(十二)--推薦系統中的EE問題及基本Bandit演算法

推薦系統遇上深度學習(十二)--推薦系統中的EE問題及基本Bandit演算法

1、推薦系統中的EE問題

Exploration and Exploitation(EE問題,探索與開發)是計算廣告和推薦系統裡常見的一個問題,為什麼會有EE問題?簡單來說,是為了平衡推薦系統的準確性和多樣性。

EE問題中的Exploitation就是:對使用者比較確定的興趣,當然要利用開採迎合,好比說已經掙到的錢,當然要花;而exploration就是:光對著使用者已知的興趣使用,使用者很快會膩,所以要不斷探索使用者新的興趣才行,這就好比雖然有一點錢可以花了,但是還得繼續搬磚掙錢,不然花完了就得喝西北風。

2、Bandit演算法

Bandit演算法是解決EE問題的一種有效演算法,我們先來了解一下Bandit演算法的起源。
Bandit演算法來源於歷史悠久的賭博學,它要解決的問題是這樣的:

一個賭徒,要去搖老虎機,走進賭場一看,一排老虎機,外表一模一樣,但是每個老虎機吐錢的概率可不一樣,他不知道每個老虎機吐錢的概率分佈是什麼,那麼每次該選擇哪個老虎機可以做到最大化收益呢?這就是多臂賭博機問題(Multi-armed bandit problem, K-armed bandit problem, MAB)。

怎麼解決這個問題呢?最好的辦法是去試一試,不是盲目地試,而是有策略地快速試一試,這些策略就是Bandit演算法。

Bandit演算法如何同推薦系統中的EE問題聯絡起來呢?假設我們已經經過一些試驗,得到了當前每個老虎機的吐錢的概率,如果想要獲得最大的收益,我們會一直搖哪個吐錢概率最高的老虎機,這就是Exploitation。但是,當前獲得的資訊並不是老虎機吐錢的真實概率,可能還有更好的老虎機吐錢概率更高,因此還需要進一步探索,這就是Exploration問題。

下面,我們就來看一下一些經典的Bandit演算法實現吧,不過我們還需要補充一些基礎知識。

3、基礎知識

3.1 累積遺憾

Bandit演算法需要量化一個核心問題:錯誤的選擇到底有多大的遺憾?能不能遺憾少一些?所以我們便有了衡量Bandit演算法的一個指標:累積遺憾:

這裡 t 表示輪數, r表示回報。公式右邊的第一項表示第t輪的期望最大收益,而右邊的第二項表示當前選擇的arm獲取的收益,把每次差距累加起來就是總的遺憾。

對應同樣的問題,採用不同bandit演算法來進行實驗相同的次數,那麼看哪個演算法的總regret增長最慢,那麼哪個演算法的效果就是比較好的。

3.2 Beta分佈

有關Beta分佈,可以參考帖子:

https://www.zhihu.com/question/30269898。這裡只做一個簡單的介紹。
beta分佈可以看作一個概率的概率分佈。它是對二項分佈中成功概率p的概率分佈的描述。它的形式如下:

其中,a和b分別代表在a+b次伯努利試驗中成功和失敗的次數。我們用下面的圖來說明一下Beta分佈的含義:

上圖中一共有三條線,我們忽略中間的一條線,第一條線中a=81,b=219。也就是說在我們進行了300次伯努利試驗中,成功81次,失敗219次的情況下,成功概率p的一個分佈,可以看到,p的概率在0.27左右概率最大,但我們不能說成功的概率就是0.27,這也就是頻率派和貝葉斯派的區別,哈哈。此時,我們又做了300次試驗,此時在總共600次伯努利試驗中,成功了181次,失敗了419次,此時成功概率p的概率分佈變為了藍色的線,在0.3左右概率最大。

4、經典Bandit演算法原理及實現

下文中的收益可以理解為老虎機吐錢的觀測概率。

4.1 樸素Bandit演算法

先隨機試若干次,計算每個臂的平均收益,一直選均值最大那個臂。

4.2 Epsilon-Greedy演算法

選一個(0,1)之間較小的數epsilon,每次以epsilon的概率在所有臂中隨機選一個。以1-epsilon的概率選擇截止當前,平均收益最大的那個臂。根據選擇臂的回報值來對回報期望進行更新。

這裡epsilon的值可以控制對exploit和explore的偏好程度,每次決策以概率ε去勘探Exploration,1-ε的概率來開發Exploitation,基於選擇的item及回報,更新item的回報期望。

對於Epsilon-Greedy演算法來首,能夠應對變化,即如果item的回報發生變化,能及時改變策略,避免卡在次優狀態。同時Epsilon的值可以控制對Exploit和Explore的偏好程度。越接近0,越保守,只想花錢不想掙錢。但是策略執行一段時間後,我們已經對各item有了一定程度瞭解,但沒用利用這些資訊,仍然不做任何區分地隨機Exploration,這是Epsilon-Greedy演算法的缺點。

4.3 Thompson sampling演算法

Thompson sampling演算法用到了Beta分佈,該方法假設每個老虎機都有一個吐錢的概率p,同時該概率p的概率分佈符合beta(wins, lose)分佈,每個臂都維護一個beta分佈的引數,即wins, lose。每次試驗後,選中一個臂,搖一下,有收益則該臂的wins增加1,否則該臂的lose增加1。

每次選擇臂的方式是:用每個臂現有的beta分佈產生一個隨機數b,選擇所有臂產生的隨機數中最大的那個臂去搖。

4.4 UCB演算法

前面提到了,Epsilon-Greedy演算法在探索的時候,所有的老虎機都有同樣的概率被選中,這其實沒有充分利用歷史資訊,比如每個老虎機之前探索的次數,每個老虎機之前的探索中吐錢的頻率。

那我們怎麼能夠充分利用歷史資訊呢?首先,根據當前老虎機已經探索的次數,以及吐錢的次數,我們可以計算出當前每個老虎機吐錢的觀測概率p'。同時,由於觀測次數有限,因此觀測概率和真實概率p之間總會有一定的差值 ∆ ,即p' - ∆ <= p <= p' + ∆。

基於上面的討論,我們得到了另一種常用的Bandit演算法:UCB(Upper Confidence Bound)演算法。該演算法在每次推薦時,總是樂觀的認為每個老虎機能夠得到的收益是p' + ∆。

好了,接下來的問題就是觀測概率和真實概率之間的差值∆如何計算了,我們首先有兩個直觀的理解:
1)對於選中的老虎機,多獲得一次反饋會使∆變小,當反饋無窮多時,∆趨近於0,最終會小於其他沒有被選中的老虎機的∆。
2)對於沒有被選中的老虎機,∆會隨著輪數的增大而增加,最終會大於其他被選中的老虎機。

因此,當進行了一定的輪數的時候,每個老虎機都有機會得到探索的機會。UCB演算法中p' + ∆的計算公式如下:

其中加號前面是第j個老虎機到目前的收益均值,後面的叫做bonus,本質上是均值的標準差,T是目前的試驗次數,n是該老虎機被試次數。

為什麼選擇上面形式的∆呢,還得從Chernoff-Hoeffding Bound說起:

5、程式碼實現

接下來,我們來實現兩個基本的Bandit演算法,UCB和Thompson sampling演算法。

5.1 UCB演算法

程式碼中有詳細的註釋,所以我直接貼完整的程式碼了:

import numpy as np

T = 1000  # T輪試驗
N = 10  # N個老虎機

true_rewards = np.random.uniform(low=0, high=1, size=N)  # 每個老虎機真實的吐錢概率
estimated_rewards = np.zeros(N)  # 每個老虎機吐錢的觀測概率,初始都為0
chosen_count = np.zeros(N)  # 每個老虎機當前已經探索的次數,初始都為0
total_reward = 0


# 計算delta
def calculate_delta(T, item):
    if chosen_count[item] == 0:
        return 1
    else:
        return np.sqrt(2 * np.log(T) / chosen_count[item])

# 計算每個老虎機的p+delta,同時做出選擇
def UCB(t, N):
    upper_bound_probs = [estimated_rewards[item] + calculate_delta(t, item) for item in range(N)]
    item = np.argmax(upper_bound_probs)
    reward = np.random.binomial(n=1, p=true_rewards[item])
    return item, reward


for t in range(1, T):  # 依次進行T次試驗
    # 選擇一個老虎機,並得到是否吐錢的結果
    item, reward = UCB(t, N)
    total_reward += reward  # 一共有多少客人接受了推薦

    # 更新每個老虎機的吐錢概率
    estimated_rewards[item] = ((t - 1) * estimated_rewards[item] + reward) / t
    chosen_count[item] += 1

5.2 Thompson sampling演算法

Thompson sampling演算法涉及到了beta分佈,因此我們使用pymc庫來產生服從beta分佈的隨機數,只需要一行程式碼就能在選擇合適的老虎機。

np.argmax(pymc.rbeta(1 + successes, 1 + totals - successes)) 

參考文獻