0. 導(dǎo)語(yǔ)
推薦系統(tǒng)里面有兩個(gè)經(jīng)典問(wèn)題:EE 問(wèn)題和冷啟動(dòng)問(wèn)題。前者涉及到平衡準(zhǔn)確和多樣,后者涉及到產(chǎn)品算法運(yùn)營(yíng)等一系列東西。bandit 算法是一種簡(jiǎn)單的在線學(xué)習(xí)算法,常常用于嘗試解決這兩個(gè)問(wèn)題,本文為你介紹基礎(chǔ)的 bandit 算法及一系列升級(jí)版,以及對(duì)推薦系統(tǒng)這兩個(gè)經(jīng)典問(wèn)題的思考。
1. 什么是 bandit 算法
1.1 為選擇而生
我們會(huì)遇到很多選擇的場(chǎng)景。上哪個(gè)大學(xué),學(xué)什么專(zhuān)業(yè),去哪家公司,中午吃什么,等等。這些事情,都讓選擇困難癥的我們頭很大。那么,有算法能夠很好地對(duì)付這些問(wèn)題嗎?
當(dāng)然有!那就是 bandit 算法!
bandit 算法來(lái)源于歷史悠久的賭博學(xué),它要解決的問(wèn)題是這樣的1:
一個(gè)賭徒,要去搖老虎機(jī),走進(jìn)賭場(chǎng)一看,一排老虎機(jī),外表一模一樣,但是每個(gè)老虎機(jī)吐錢(qián)的概率可不一樣,他不知道每個(gè)老虎機(jī)吐錢(qián)的概率分布是什么,那么每次該選擇哪個(gè)老虎機(jī)可以做到最大化收益呢?這就是多臂賭博機(jī)問(wèn)題 (Multi-armed bandit problem, K-armed bandit problem, MAB)。
怎么解決這個(gè)問(wèn)題呢?最好的辦法是去試一試,不是盲目地試,而是有策略地快速試一試,這些策略就是 bandit 算法。
這個(gè)多臂問(wèn)題,推薦系統(tǒng)里面很多問(wèn)題都與他類(lèi)似:
假設(shè)一個(gè)用戶對(duì)不同類(lèi)別的內(nèi)容感興趣程度不同,那么我們的推薦系統(tǒng)初次見(jiàn)到這個(gè)用戶時(shí),怎么快速地知道他對(duì)每類(lèi)內(nèi)容的感興趣程度?這就是推薦系統(tǒng)的冷啟動(dòng)。
假設(shè)我們有若干廣告庫(kù)存,怎么知道該給每個(gè)用戶展示哪個(gè)廣告,從而獲得最大的點(diǎn)擊收益?是每次都挑效果最好那個(gè)么?那么新廣告如何才有出頭之日?
我們的算法工程師又想出了新的模型,有沒(méi)有比 A/B test 更快的方法知道它和舊模型相比誰(shuí)更靠譜?
如果只是推薦已知的用戶感興趣的物品,如何才能科學(xué)地冒險(xiǎn)給他推薦一些新鮮的物品?
這些問(wèn)題本質(zhì)上全都是關(guān)乎如何選擇。只要是關(guān)于選擇,都可以簡(jiǎn)化成一個(gè)多臂賭博機(jī)問(wèn)題,畢竟小賭怡情嘛,人生何處不賭博。
1.2 bandit 算法與推薦系統(tǒng)
在推薦系統(tǒng)領(lǐng)域里,有兩個(gè)比較經(jīng)典的問(wèn)題常被人提起,一個(gè)是 EE 問(wèn)題,另一個(gè)是用戶冷啟動(dòng)問(wèn)題。
什么是 EE 問(wèn)題?又叫 exploit-explore 問(wèn)題。exploit 就是:對(duì)用戶比較確定的興趣,當(dāng)然要利用開(kāi)采迎合,好比說(shuō)已經(jīng)掙到的錢(qián),當(dāng)然要花;explore 就是:光對(duì)著用戶已知的興趣使用,用戶很快會(huì)膩,所以要不斷探索用戶新的興趣才行,這就好比雖然有一點(diǎn)錢(qián)可以花了,但是還得繼續(xù)搬磚掙錢(qián),不然花完了就得喝西北風(fēng)。
用戶冷啟動(dòng)問(wèn)題,也就是面對(duì)新用戶時(shí),如何能夠通過(guò)若干次實(shí)驗(yàn),猜出用戶的大致興趣。
我想,屏幕前的你已經(jīng)想到了,推薦系統(tǒng)冷啟動(dòng)可以用 bandit 算法來(lái)解決一部分。
這兩個(gè)問(wèn)題本質(zhì)上都是如何選擇用戶感興趣的主題進(jìn)行推薦,比較符合 bandit 算法背后的 MAB 問(wèn)題。
比如,用 bandit 算法解決冷啟動(dòng)的大致思路如下:
用分類(lèi)或者 Topic 來(lái)表示每個(gè)用戶興趣,也就是 MAB 問(wèn)題中的臂(Arm),我們可以通過(guò)幾次試驗(yàn),來(lái)刻畫(huà)出新用戶心目中對(duì)每個(gè) topic 的感興趣概率。
這里,如果用戶對(duì)某個(gè) topic 感興趣(提供了顯式反饋或隱式反饋),就表示我們得到了收益,如果推給了它不感興趣的 topic,推薦系統(tǒng)就表示很遺憾 (regret) 了。
如此經(jīng)歷 “選擇 - 觀察 - 更新 - 選擇” 的循環(huán),理論上是越來(lái)越逼近用戶真正感興趣的 topic 的。
1.3 怎么選擇 bandit 算法?
現(xiàn)在來(lái)介紹一下 bandit 算法怎么解決這類(lèi)問(wèn)題的。bandit 算法需要量化一個(gè)核心問(wèn)題:錯(cuò)誤的選擇到底有多大的遺憾?能不能遺憾少一些?
王家衛(wèi)在《一代宗師》里寄出一句臺(tái)詞:
人生要是無(wú)憾,那多無(wú)趣?
而我說(shuō):算法要是無(wú)憾,那應(yīng)該是過(guò)擬合了。
所以說(shuō):怎么衡量不同 bandit 算法在解決多臂問(wèn)題上的效果?首先介紹一個(gè)概念,叫做累積遺憾 (regret)2:
RT=T∑i=1(wopt?wB(i))=Tw??T∑i=1wB(i)RT=∑i=1T(wopt?wB(i))=Tw??∑i=1TwB(i)
這個(gè)公式就是計(jì)算 bandit 算法的累積遺憾,解釋一下:
首先,這里我們討論的每個(gè)臂的收益非 0 即 1,也就是伯努利收益。
然后,每次選擇后,計(jì)算和最佳的選擇差了多少,然后把差距累加起來(lái)就是總的遺憾。
wB(i)wB(i)是第 i 次試驗(yàn)時(shí)被選中臂的期望收益,w?w?是所有臂中的最佳那個(gè),如果上帝提前告訴你,我們當(dāng)然每次試驗(yàn)都選它,問(wèn)題是上帝不告訴你,所以就有了 bandit 算法,我們就有了這篇文章。
這個(gè)公式可以用來(lái)對(duì)比不同 bandit 算法的效果:對(duì)同樣的多臂問(wèn)題,用不同的 bandit 算法試驗(yàn)相同次數(shù),看看誰(shuí)的 regret 增長(zhǎng)得慢。
那么到底不同的 bandit 算法有哪些呢?
1.4 常用 bandit 算法
Thompson sampling 算法
thompson sampling 算法簡(jiǎn)單實(shí)用,因?yàn)樗挥幸恍写a就可以實(shí)現(xiàn)3。簡(jiǎn)單介紹一下它的原理,要點(diǎn)如下:
假設(shè)每個(gè)臂是否產(chǎn)生收益,其背后有一個(gè)概率分布,產(chǎn)生收益的概率為 p。
我們不斷地試驗(yàn),去估計(jì)出一個(gè)置信度較高的 “概率 p 的概率分布” 就能近似解決這個(gè)問(wèn)題了。
怎么能估計(jì) “概率 p 的概率分布” 呢? 答案是假設(shè)概率 p 的概率分布符合 beta(wins, lose)分布,它有兩個(gè)參數(shù): wins, lose。
每個(gè)臂都維護(hù)一個(gè) beta 分布的參數(shù)。每次試驗(yàn)后,選中一個(gè)臂,搖一下,有收益則該臂的 wins 增加 1,否則該臂的 lose 增加 1。
每次選擇臂的方式是:用每個(gè)臂現(xiàn)有的 beta 分布產(chǎn)生一個(gè)隨機(jī)數(shù) b,選擇所有臂產(chǎn)生的隨機(jī)數(shù)中最大的那個(gè)臂去搖。
以上就是 Thompson 采樣,用 python 實(shí)現(xiàn)就一行:
import? numpy as np
import? pymc
#wins 和 trials 是一個(gè)N維向量,N是賭博機(jī)的臂的個(gè)數(shù),每個(gè)元素記錄了
choice = np.argmax(pymc.rbeta(1 + wins, 1 + trials - wins))
wins[choice] += 1
trials += 1
UCB 算法
UCB 算法全稱(chēng)是 Upper Confidence Bound(置信區(qū)間上界),它的算法步驟如下4:
初始化:先對(duì)每一個(gè)臂都試一遍
按照如下公式計(jì)算每個(gè)臂的分?jǐn)?shù),然后選擇分?jǐn)?shù)最大的臂作為選擇:ˉxj(t)+√2lntTj,txˉj(t)+2ln?tTj,t
觀察選擇結(jié)果,更新 t 和TjtTjt。其中加號(hào)前面是這個(gè)臂到目前的收益均值,后面的叫做 bonus,本質(zhì)上是均值的標(biāo)準(zhǔn)差,t 是目前的試驗(yàn)次數(shù),TjtTjt是這個(gè)臂被試次數(shù)。
這個(gè)公式反映一個(gè)特點(diǎn):均值越大,標(biāo)準(zhǔn)差越小,被選中的概率會(huì)越來(lái)越大,同時(shí)哪些被選次數(shù)較少的臂也會(huì)得到試驗(yàn)機(jī)會(huì)。
Epsilon-Greedy 算法
這是一個(gè)樸素的 bandit 算法,有點(diǎn)類(lèi)似模擬退火的思想:
選一個(gè) (0,1) 之間較小的數(shù)作為 epsilon
每次以概率 epsilon 做一件事:所有臂中隨機(jī)選一個(gè)
每次以概率 1-epsilon 選擇截止到當(dāng)前,平均收益最大的那個(gè)臂。
是不是簡(jiǎn)單粗暴?epsilon 的值可以控制對(duì) Exploit 和 Explore 的偏好程度。越接近 0,越保守,只想花錢(qián)不想掙錢(qián)。
樸素 bandit 算法
最樸素的 bandit 算法就是:先隨機(jī)試若干次,計(jì)算每個(gè)臂的平均收益,一直選均值最大那個(gè)臂。這個(gè)算法是人類(lèi)在實(shí)際中最常采用的,不可否認(rèn),它還是比隨機(jī)亂猜要好。
以上五個(gè)算法,我們用 10000 次模擬試驗(yàn)的方式對(duì)比了其效果如圖,實(shí)驗(yàn)代碼來(lái)源5:
算法效果對(duì)比一目了然:UCB 算法和 Thompson 采樣算法顯著優(yōu)秀一些。
至于你實(shí)際上要選哪一種 bandit 算法,你可以選一種 bandit 算法來(lái)選 bandit 算法。。。
2.bandit 算法與線性回歸
2.1 UCB 算法
UCB 算法在做 EE(Exploit-Explore)的時(shí)候表現(xiàn)不錯(cuò),但它是上下文無(wú)關(guān) (context free) 的 bandit 算法,它只管埋頭干活,根本不觀察一下面對(duì)的都是些什么特點(diǎn)的 arm,下次遇到相似特點(diǎn)但不一樣的 arm 也幫不上什么忙。
UCB 解決 Multi-armed bandit 問(wèn)題的思路是:用置信區(qū)間。置信區(qū)間可以簡(jiǎn)單地理解為不確定性的程度,區(qū)間越寬,越不確定,反之亦反之。
每個(gè) item 的回報(bào)均值都有個(gè)置信區(qū)間,隨著試驗(yàn)次數(shù)增加,置信區(qū)間會(huì)變窄(逐漸確定了到底回報(bào)豐厚還是可憐)。 每次選擇前,都根據(jù)已經(jīng)試驗(yàn)的結(jié)果重新估計(jì)每個(gè) item 的均值及置信區(qū)間。 選擇置信區(qū)間上限最大的那個(gè) item。
“選擇置信區(qū)間上界最大的那個(gè) item” 這句話反映了幾個(gè)意思:
如果 item 置信區(qū)間很寬(被選次數(shù)很少,還不確定),那么它會(huì)傾向于被多次選擇,這個(gè)是算法冒風(fēng)險(xiǎn)的部分;
如果 item 置信區(qū)間很窄(備選次數(shù)很多,比較確定其好壞了),那么均值大的傾向于被多次選擇,這個(gè)是算法保守穩(wěn)妥的部分;
UCB 是一種樂(lè)觀的算法,選擇置信區(qū)間上界排序,如果時(shí)悲觀保守的做法,是選擇置信區(qū)間下界排序。
2.2 UCB 算法加入特征信息
Yahoo! 的科學(xué)家們?cè)?2010 年發(fā)表了一篇論文6,給 UCB 引入了特征信息,同時(shí)還把改造后的 UCB 算法用在了 Yahoo! 的新聞推薦中,算法名叫 LinUCB,劉鵬博士在《計(jì)算廣告》一書(shū)中也有介紹 LinUCB 在計(jì)算廣告中的應(yīng)用7。
單純的老虎機(jī)回報(bào)情況就是老虎機(jī)自己內(nèi)部決定的,而在廣告推薦領(lǐng)域,一個(gè)選擇的回報(bào),是由 User 和 Item 一起決定的,如果我們能用 feature 來(lái)刻畫(huà) User 和 Item 這一對(duì) CP,在每次選擇 item 之前,通過(guò) feature 預(yù)估每一個(gè) arm(item)的期望回報(bào)及置信區(qū)間,選擇的收益就可以通過(guò) feature 泛化到不同的 item 上。
為 UCB 算法插上了特征的翅膀,這就是 LinUCB 最大的特色。
LinUCB 算法做了一個(gè)假設(shè):一個(gè) Item 被選擇后推送給一個(gè) User,其回報(bào)和相關(guān) Feature 成線性關(guān)系,這里的 “相關(guān) feature” 就是 context,也是實(shí)際項(xiàng)目中發(fā)揮空間最大的部分。
于是試驗(yàn)過(guò)程就變成:用 User 和 Item 的特征預(yù)估回報(bào)及其置信區(qū)間,選擇置信區(qū)間上界最大的 item 推薦,觀察回報(bào)后更新線性關(guān)系的參數(shù),以此達(dá)到試驗(yàn)學(xué)習(xí)的目的。LinUCB 基本算法描述如下:
對(duì)照每一行解釋一下 (編號(hào)從 1 開(kāi)始):
設(shè)定一個(gè)參數(shù)αα,這個(gè)參數(shù)決定了我們 Explore 的程度
開(kāi)始試驗(yàn)迭代
獲取每一個(gè) arm 的特征向量xa,txa,t
開(kāi)始計(jì)算每一個(gè) arm 的預(yù)估回報(bào)及其置信區(qū)間
如果 arm 還從沒(méi)有被試驗(yàn)過(guò),那么:
用單位矩陣初始化AaAa
用 0 向量初始化baba,
處理完沒(méi)被試驗(yàn)過(guò)的 arm
計(jì)算線性參數(shù)θθ
用θθ和特征向量xa,txa,t計(jì)算預(yù)估回報(bào), 同時(shí)加上置信區(qū)間寬度
處理完每一個(gè) arm
選擇第 10 步中最大值對(duì)應(yīng)的 arm,觀察真實(shí)的回報(bào)rtrt
更新AatAat
更新batbat
算法結(jié)束
注意到上面的第 4 步,給特征矩陣加了一個(gè)單位矩陣,這就是嶺回歸(ridge regression),嶺回歸主要用于當(dāng)樣本數(shù)小于特征數(shù)時(shí),對(duì)回歸參數(shù)進(jìn)行修正8。對(duì)于加了特征的 bandit 問(wèn)題,正符合這個(gè)特點(diǎn):試驗(yàn)次數(shù)(樣本)少于特征數(shù)。
每一次觀察真實(shí)回報(bào)之后,要更新的不止是嶺回歸參數(shù),還有每個(gè) arm 的回報(bào)向量baba。
2.3 詳解 LinUCB 的實(shí)現(xiàn)
根據(jù)論文給出的算法描述,其實(shí)很好寫(xiě)出 LinUCB 的代碼9,麻煩的只是構(gòu)建特征。
代碼如下,一些必要的注釋說(shuō)明已經(jīng)寫(xiě)在代碼中。
class LinUCB:
def __init__(self):
self.alpha = 0.25
self.r1 = 1 # if worse -> 0.7, 0.8
self.r0 = 0 # if worse, -19, -21
# dimension of user features = d
self.d = 6
# Aa : collection of matrix to compute disjoint part for each article a, d*d
self.Aa = {}
# AaI : store the inverse of all Aa matrix
self.AaI = {}
# ba : collection of vectors to compute disjoin part, d*1
self.ba = {}
self.a_max = 0
self.theta = {}
self.x = None
self.xT = None
# linUCB
def set_articles(self, art):
# init collection of matrix/vector Aa, Ba, ba
for key in art:
self.Aa[key] = np.identity(self.d)
self.ba[key] = np.zeros((self.d, 1))
self.AaI[key] = np.identity(self.d)
self.theta[key] = np.zeros((self.d, 1))
"""
這里更新參數(shù)時(shí)沒(méi)有傳入更新哪個(gè)arm,因?yàn)樵谏弦淮蝦ecommend的時(shí)候緩存了被選的那個(gè)arm,所以此處不用傳入
另外,update操作不用阻塞recommend,可以異步執(zhí)行
"""
def update(self, reward):
if reward == -1:
pass
elif reward == 1 or reward == 0:
if reward == 1:
r = self.r1
else:
r = self.r0
self.Aa[self.a_max] += np.dot(self.x, self.xT)
self.ba[self.a_max] += r * self.x
self.AaI[self.a_max] = linalg.solve(self.Aa[self.a_max], np.identity(self.d))
self.theta[self.a_max] = np.dot(self.AaI[self.a_max], self.ba[self.a_max])
else:
# error
pass
"""
預(yù)估每個(gè)arm的回報(bào)期望及置信區(qū)間
"""
def recommend(self, timestamp, user_features, articles):
xaT = np.array([user_features])
xa = np.transpose(xaT)
art_max = -1
old_pa = 0
# 獲取在update階段已經(jīng)更新過(guò)的AaI(求逆結(jié)果)
AaI_tmp = np.array([self.AaI[article] for article in articles])
theta_tmp = np.array([self.theta[article] for article in articles])
art_max = articles[np.argmax(np.dot(xaT, theta_tmp) + self.alpha * np.sqrt(np.dot(np.dot(xaT, AaI_tmp), xa)))]
# 緩存選擇結(jié)果,用于update
self.x = xa
self.xT = xaT
# article index with largest UCB
self.a_max = art_max
return self.a_max
2.4 怎么構(gòu)建特征
LinUCB 算法有一個(gè)很重要的步驟,就是給 User 和 Item 構(gòu)建特征,也就是刻畫(huà) context。在原始論文里,Item 是文章,其中專(zhuān)門(mén)介紹了它們?cè)趺礃?gòu)建特征的,也甚是精妙。容我慢慢表來(lái)。
原始用戶特征
人口統(tǒng)計(jì)學(xué):性別特征(2 類(lèi)),年齡特征(離散成 10 個(gè)區(qū)間)
地域信息:遍布全球的大都市,美國(guó)各個(gè)州
行為類(lèi)別:代表用戶歷史行為的 1000 個(gè)類(lèi)別取值
原始文章特征
URL 類(lèi)別:根據(jù)文章來(lái)源分成了幾十個(gè)類(lèi)別
編輯打標(biāo)簽:編輯人工給內(nèi)容從幾十個(gè)話題標(biāo)簽中挑選出來(lái)的
原始特征向量都要?dú)w一化成單位向量。還要對(duì)原始特征降維,以及模型要能刻畫(huà)一些非線性的關(guān)系。用 Logistic Regression 去擬合用戶對(duì)文章的點(diǎn)擊歷史,其中的線性回歸部分為:
?TuW?a?uTW?a
擬合得到參數(shù)矩陣 W,可以將原始用戶特征(1000 多維)投射到文章的原始特征空間(80 多維),投射計(jì)算方式:
ψu(yù)def=?TuWψu(yù)=def?uTW
這是第一次降維,把原始 1000 多維降到 80 多維。
然后,用投射后的 80 多維特征對(duì)用戶聚類(lèi),得到 5 個(gè)類(lèi)簇,文章頁(yè)同樣聚類(lèi)成 5 個(gè)簇,再加上常數(shù) 1,用戶和文章各自被表示成 6 維向量。
Yahoo! 的科學(xué)家們之所以選定為 6 維,因?yàn)閿?shù)據(jù)表明它的效果最好10,并且這大大降低了計(jì)算復(fù)雜度和存儲(chǔ)空間。
我們實(shí)際上可以考慮三類(lèi)特征:U(用戶),A(廣告或文章),C(所在頁(yè)面的一些信息)。
前面說(shuō)了,特征構(gòu)建很有發(fā)揮空間,算法工程師們盡情去揮灑汗水吧。
總結(jié)一下 LinUCB 算法,有以下優(yōu)點(diǎn):
由于加入了特征,所以收斂比 UCB 更快(論文有證明);
特征構(gòu)建是效果的關(guān)鍵,也是工程上最麻煩和值的發(fā)揮的地方;
由于參與計(jì)算的是特征,所以可以處理動(dòng)態(tài)的推薦候選池,編輯可以增刪文章;
特征降維很有必要,關(guān)系到計(jì)算效率。
3.bandit 算法與協(xié)同過(guò)濾
3.1 協(xié)同過(guò)濾背后的哲學(xué)
推薦系統(tǒng)里面,傳統(tǒng)經(jīng)典的算法肯定離不開(kāi)協(xié)同過(guò)濾。協(xié)同過(guò)濾背后的思想簡(jiǎn)單深刻,在萬(wàn)物互聯(lián)的今天,協(xié)同過(guò)濾的威力更加強(qiáng)大。協(xié)同過(guò)濾看上去是一種算法,不如說(shuō)是一種方法論,不是機(jī)器在給你推薦,而是 “集體智慧” 在給你推薦。
它的基本假設(shè)就是 “物以類(lèi)聚,人以群分”,你的圈子決定了你能見(jiàn)到的物品。這個(gè)假設(shè)很靠譜,卻隱藏了一些重要的問(wèn)題:作為用戶的我們還可能看到新的東西嗎?還可能有驚喜嗎?還可能有圈子之間的更迭流動(dòng)嗎?這些問(wèn)題的背后其實(shí)就是在前面提到過(guò)的 EE 問(wèn)題(Exploit & Explore)。我們關(guān)注推薦的準(zhǔn)確率,但是我們也應(yīng)該關(guān)注推薦系統(tǒng)的演進(jìn)發(fā)展,因?yàn)?“推薦系統(tǒng)不止眼前的 Exploit,還有遠(yuǎn)方的 Explore”。
做 Explore 的方法有很多,bandit 算法是其中的一種流派。前面也介紹過(guò)幾種 bandit 算法,基本上就是估計(jì)置信區(qū)間的做法,然后按照置信區(qū)間的上界來(lái)進(jìn)行推薦,以 UCB,LinUCB 為代表。
作為要尋找詩(shī)和遠(yuǎn)方的 bandit 浪漫派算法,能不能和協(xié)同過(guò)濾這種正統(tǒng)算法結(jié)合起來(lái)呢?事實(shí)上已經(jīng)有人這么嘗試過(guò)了,叫做 COFIBA 算法,具體在題目為 Collaborative Filtering Bandits11和 Online Clustering of Bandits12)的兩篇文章中有詳細(xì)的描述,它就是 bandit 和協(xié)同過(guò)濾的結(jié)合算法,兩篇文章的區(qū)別是后者只對(duì)用戶聚類(lèi)(即只考慮了 User-based 的協(xié)同過(guò)濾),而前者采用了協(xié)同聚類(lèi)(co-clustering,可以理解為 item-based 和 user-based 兩種協(xié)同方式在同時(shí)進(jìn)行),后者是前者的一個(gè)特殊情況。下面詳細(xì)介紹一下這種結(jié)合算法。
3.2 bandit 結(jié)合協(xié)同過(guò)濾
很多推薦場(chǎng)景中都有這兩個(gè)規(guī)律:
相似的用戶對(duì)同一個(gè)物品的反饋可能是一樣的。也就是對(duì)一個(gè)聚類(lèi)用戶群體推薦同一個(gè) item,他們可能都喜歡,也可能都不喜歡,同樣地,同一個(gè)用戶會(huì)對(duì)相似的物品反饋相同。這是屬于協(xié)同過(guò)濾可以解決的問(wèn)題;
在使用推薦系統(tǒng)過(guò)程中,用戶的決策是動(dòng)態(tài)進(jìn)行的,尤其是新用戶。這就導(dǎo)致無(wú)法提前為用戶準(zhǔn)備好推薦候選,只能 “走一步看一步”,是一個(gè)動(dòng)態(tài)的推薦過(guò)程。
每一個(gè)推薦候選 item,都可以根據(jù)用戶對(duì)其偏好不同(payoff 不同)將用戶聚類(lèi)成不同的群體,一個(gè)群體來(lái)集體預(yù)測(cè)這個(gè) item 的可能的收益,這就有了協(xié)同的效果,然后再實(shí)時(shí)觀察真實(shí)反饋回來(lái)更新用戶的個(gè)人參數(shù),這就有了 bandit 的思想在里面。
舉個(gè)例子,如果你父母給你安排了很多相親對(duì)象,要不要見(jiàn)面去相一下?那需要提前看看每一個(gè)相親對(duì)象的資料,每次大家都分成好幾派,有說(shuō)好的,有說(shuō)再看看的,也有說(shuō)不行的;你自己也會(huì)是其中一派的一員,每次都是你所屬的那一派給你集體打分,因?yàn)樗麄兪呛湍?“三觀一致的人”,“誠(chéng)不欺我”;這樣從一堆資料中挑出分?jǐn)?shù)最高的那個(gè)人,你出去見(jiàn) TA,回來(lái)后把實(shí)際感覺(jué)說(shuō)給大家聽(tīng),同時(shí)自己心里的標(biāo)準(zhǔn)也有些調(diào)整,重新給剩下的其它對(duì)象打分,打完分再去見(jiàn),周而復(fù)始……
以上就是協(xié)同過(guò)濾和 bandit 結(jié)合的思想。
另外,如果要推薦的候選 item 較多,還需要對(duì) item 進(jìn)行聚類(lèi),這樣就不用按照每一個(gè) item 對(duì) user 聚類(lèi),而是按照每一個(gè) item 的類(lèi)簇對(duì) user 聚類(lèi),如此以來(lái),item 的類(lèi)簇?cái)?shù)相對(duì)于 item 數(shù)要大大減少。
3.3 COFIBA 算法
基于這些思想,有人提出了算法 COFIBA(讀作 coffee bar)13,簡(jiǎn)要描述如下:
在時(shí)刻 t,用戶來(lái)訪問(wèn)推薦系統(tǒng),推薦系統(tǒng)需要從已有的候選池子中挑一個(gè)最佳的物品推薦給他,然后觀察他的反饋,用觀察到的反饋來(lái)更新挑選策略。 這里的每個(gè)物品都有一個(gè)特征向量,所以這里的 bandit 算法是 context 相關(guān)的。 這里依然是用嶺回歸去擬合用戶的權(quán)重向量,用于預(yù)測(cè)用戶對(duì)每個(gè)物品的可能反饋(payoff),這一點(diǎn)和 linUCB 算法是一樣的。
對(duì)比 LinUCB 算法,COFIBA 算法的不同有兩個(gè):
基于用戶聚類(lèi)挑選最佳的 item(相似用戶集體決策的 bandit)
基于用戶的反饋情況調(diào)整 user 和 item 的聚類(lèi)(協(xié)同過(guò)濾部分)
整體算法過(guò)程如下:
核心步驟是,針對(duì)某個(gè)用戶 i,在每一輪試驗(yàn)時(shí)做以下事情:
首先計(jì)算該用戶的 bandit 參數(shù) W(和 LinUCB 相同),但是這個(gè)參數(shù)并不直接參與到 bandit 的選擇決策中(和 LinUCB 不同),而是用來(lái)更新用戶聚類(lèi)的;
遍歷候選 item,每一個(gè) item 表示成一個(gè) context 向量了。
每一個(gè) item 都對(duì)應(yīng)一套用戶聚類(lèi)結(jié)果,所以遍歷到每一個(gè) item 時(shí)判斷當(dāng)前用戶在當(dāng)前 item 下屬于哪個(gè)類(lèi)簇,然后把對(duì)應(yīng)類(lèi)簇中每個(gè)用戶的 M 矩陣 (對(duì)應(yīng) LinUCB 里面的 A 矩陣),b 向量(payoff 向量,對(duì)應(yīng) linUCB 里面的 b 向量)聚合起來(lái),從而針對(duì)這個(gè)類(lèi)簇求解一個(gè)嶺回歸參數(shù)(類(lèi)似 LinUCB 里面單獨(dú)針對(duì)每個(gè)用戶所做),同時(shí)計(jì)算其 payoff 預(yù)測(cè)值和置信上邊界
每個(gè) item 都得到一個(gè) payoff 預(yù)測(cè)值及置信區(qū)間上界,挑出那個(gè)上邊界最大的 item 推出去(和 LinUCB 相同)
觀察用戶的真實(shí)反饋,然后更新用戶自己的 M 矩陣和 b 向量(更新個(gè)人的,對(duì)應(yīng)類(lèi)簇里其他的不更新)
以上是 COFIBA 算法的一次決策過(guò)程。在收到用戶真實(shí)反饋之后,還有兩個(gè)計(jì)算過(guò)程:
更新 user 聚類(lèi)
更新 item 聚類(lèi)
如何更新 user 和 item 的聚類(lèi)呢?示意圖為:
解釋一下這個(gè)圖。
(a) 這里有 6 個(gè) user,8 個(gè) item,初始化時(shí),user 和 item 的類(lèi)簇個(gè)數(shù)都是 1
(b1) 在某一輪試驗(yàn)時(shí),推薦系統(tǒng)面對(duì)的用戶是 4。推薦過(guò)程就是遍歷 1~8 每個(gè) item,然后看看對(duì)應(yīng)每個(gè) item 時(shí),user4 在哪個(gè)類(lèi)簇中,把對(duì)應(yīng)類(lèi)簇中的用戶聚合起來(lái)為這個(gè) item 預(yù)測(cè) payoff 和 CB。這里假設(shè)最終 item5 勝出,被推薦出去了。
(b2)在時(shí)刻 t,item 有 3 個(gè)類(lèi)簇,需要更新的用戶聚類(lèi)是 item5 對(duì)應(yīng)的 user4 所在類(lèi)簇。更新方式:看看該類(lèi)簇里面除了 user4 之外的用戶,對(duì) item5 的 payoff 是不是和 user4 相近,如果是,則保持原來(lái)的連接邊,否則刪除原來(lái)的連接邊。刪除邊之后重新構(gòu)建聚類(lèi)結(jié)果。這里假設(shè)重新構(gòu)建后原來(lái) user4 所在的類(lèi)簇分裂成了兩個(gè)類(lèi)簇:{4,5} 和 {6}
? 更新完用戶類(lèi)簇后,item5 對(duì)應(yīng)的類(lèi)簇也要更新。更新方式是:對(duì)于每一個(gè)和 item5(被推薦出的那個(gè) item) 還存在連接邊的 item j,都去構(gòu)造一個(gè) user 的近鄰集合 N,這個(gè)集合的用戶對(duì) item j 有相近的 payoff,然后看看 N 是不是和剛剛更新后的 user4 所在的類(lèi)簇相同,是的話,保留 item5 和 item j 之間的連接邊,否則刪除。這里假設(shè) item 3 和 item 5 之間的連接邊被刪除。item3 獨(dú)立后給他初始化了一個(gè)聚類(lèi)結(jié)果:所有用戶還是一個(gè)類(lèi)簇。
簡(jiǎn)單來(lái)說(shuō)就是這樣:
User-based 協(xié)同過(guò)濾來(lái)選擇要推薦的 item,選擇時(shí)用了 LinUCB 的思想
根據(jù)用戶的反饋,調(diào)整 User-based 和 Item-based 的聚類(lèi)結(jié)果
Item-based 的聚類(lèi)變化又改變了 User 的聚類(lèi)
不斷根據(jù)用戶實(shí)時(shí)動(dòng)態(tài)的反饋來(lái)劃分 User-Item 矩陣
4. 總結(jié)
Exploit-Explore 這一對(duì)矛盾一直客觀存在,bandit 算法是公認(rèn)的一種比較好的解決 EE 問(wèn)題的方案。除了 bandit 算法之外,還有一些其他的 explore 的辦法,比如:在推薦時(shí),隨機(jī)地去掉一些用戶歷史行為(特征)。
解決 Explore,勢(shì)必就是要冒險(xiǎn),勢(shì)必要走向未知,而這顯然就是會(huì)傷害用戶體驗(yàn)的:明知道用戶肯定喜歡 A,你還偏偏以某個(gè)小概率給推薦非 A。
實(shí)際上,很少有公司會(huì)采用這些理性的辦法做 Explore,反而更愿意用一些盲目主觀的方式。究其原因,可能是因?yàn)椋?/p>
互聯(lián)網(wǎng)產(chǎn)品生命周期短,而 Explore 又是為了提升長(zhǎng)期利益的,所以沒(méi)有動(dòng)力做;
用戶使用互聯(lián)網(wǎng)產(chǎn)品時(shí)間越來(lái)越碎片化,Explore 的時(shí)間長(zhǎng),難以體現(xiàn)出 Explore 的價(jià)值;
同質(zhì)化互聯(lián)網(wǎng)產(chǎn)品多,用戶選擇多,稍有不慎,用戶用腳投票,分分鐘棄你于不顧。
已經(jīng)成規(guī)模的平臺(tái),紅利杠杠的,其實(shí)是沒(méi)有動(dòng)力做 Explore 的;
基于這些,我們?nèi)绻朐谧约旱耐扑]系統(tǒng)中引入 Explore 機(jī)制,需要注意以下幾點(diǎn):
用于 Explore 的 item 要保證其本身質(zhì)量,縱使用戶不感興趣,也不至于引起其反感;
Explore 本身的產(chǎn)品需要精心設(shè)計(jì),讓用戶有耐心陪你玩兒;
深度思考,這樣才不會(huì)做出腦殘的產(chǎn)品,產(chǎn)品不會(huì)早早夭折,才有可能讓 Explore 機(jī)制有用武之地。
參考文獻(xiàn)
https://en.wikipedia.org/wiki/Multi-armed_bandit?
http://nbviewer.jupyter.org/github/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/blob/master/Chapter6_Priorities/Chapter6.ipynb#?
https://en.wikipedia.org/wiki/Thompson_sampling?
http://hunch.net/~coms-4771/lecture20.pdf?
https://gist.github.com/anonymous/211b599b7bef958e50af?
http://www.research.rutgers.edu/~lihong/pub/Li10Contextual.pdf?
《計(jì)算廣告:互聯(lián)網(wǎng)商業(yè)變現(xiàn)的市場(chǎng)與技術(shù)》p253, 劉鵬,王超著?
https://en.wikipedia.org/wiki/Tikhonov_regularization?
https://github.com/Fengrui/HybridLinUCB-python/blob/master/policy_hybrid.py?
http://www.gatsby.ucl.ac.uk/~chuwei/paper/isp781-chu.pdf?
http://arxiv.org/abs/1401.8257?
http://arxiv.org/abs/1502.03473?
https://github.com/qw2ky/CoLinUCB_Revised/blob/master/COFIBA.py?