2017年寫(xiě)在blog的文章诉字,搬運(yùn)過(guò)來(lái)。
年終了知纷,終于可以在需求的夾縫中喘息一會(huì)×昝梗回望2017年琅轧,最大的成就莫過(guò)于從0到1搭建起了一套支持多業(yè)務(wù)場(chǎng)景、高并發(fā)訪問(wèn)踊挠、高時(shí)效性的新聞推薦系統(tǒng)乍桂。這其中自是暗坑無(wú)數(shù),趁著還未淡忘效床,將系統(tǒng)搭建過(guò)程中遇到的困難與解決方法記錄于此睹酌。
0. 概況
以我們目前的推薦系統(tǒng)架構(gòu)為例:
推薦系統(tǒng)是個(gè)很復(fù)雜的工程,對(duì)于算法和工程上的能力都是一個(gè)挑戰(zhàn)剩檀。本文只是嘗試從幾個(gè)大模塊簡(jiǎn)述上手搭建推薦系統(tǒng)的過(guò)程憋沿,不會(huì)深入探討。然而要想推薦達(dá)到可觀的效果沪猴,深入挖掘每個(gè)模塊辐啄,研讀論文、優(yōu)化架構(gòu)是必不可少的运嗜。以下我會(huì)從數(shù)據(jù)壶辜、畫(huà)像(內(nèi)容/用戶(hù))、召回和排序幾個(gè)部分分別詳述担租。
1. 數(shù)據(jù)
推薦系統(tǒng)砸民,最重要的是數(shù)據(jù)。數(shù)據(jù)決定了算法的上界,再牛逼的算法也只是逼近這個(gè)上界而已岭参。因此搭建系統(tǒng)時(shí)便贵,首要考慮完善數(shù)據(jù)。這里數(shù)據(jù)包含兩類(lèi):內(nèi)容數(shù)據(jù)與用戶(hù)數(shù)據(jù)冗荸。
1.1. 內(nèi)容數(shù)據(jù)
這個(gè)很好理解承璃,內(nèi)容指的是推薦系統(tǒng)要推薦的item。電商就是商品蚌本,電影網(wǎng)站就是電影盔粹,我搭建的是新聞推薦系統(tǒng),所以?xún)?nèi)容就是新聞程癌。獲取手段可以是網(wǎng)站內(nèi)部發(fā)文舷嗡,也可以是外部抓取,基礎(chǔ)爬蟲(chóng)我就不贅述了嵌莉,另外內(nèi)容的版權(quán)問(wèn)題也是需要注意的进萄。抓取到之后我們需要對(duì)內(nèi)容落地,這一步的關(guān)鍵是數(shù)據(jù)格式的規(guī)范化锐峭≈惺螅考慮到我們的內(nèi)容很可能是從不同數(shù)據(jù)源抓取,有著不同格式沿癞,為了方便日后的利用援雇,大致需要遵從如下步驟,對(duì)原始數(shù)據(jù)進(jìn)行ETL:
1. 按推薦需求指定落地內(nèi)容字段
2. 對(duì)內(nèi)容字段進(jìn)行標(biāo)準(zhǔn)化處理椎扬,如正文提取惫搏、一致編碼
3. 選擇合適的存儲(chǔ)方式,如MySQL蚕涤、MongoDB筐赔、HDFS
需要明確的是,上述系列行為都是為最終的推薦服務(wù)的揖铜。首先茴丰,需要考慮業(yè)務(wù)側(cè)需要展現(xiàn)哪些屬性(如標(biāo)題、縮略圖蛮位、摘要)较沪;其次,還需要考慮算法側(cè)提取內(nèi)容特征需要哪些屬性(如正文失仁、發(fā)布時(shí)間)尸曼。我在系統(tǒng)搭建的過(guò)程中,遇到最頭疼的問(wèn)題就是在NLP時(shí)需要依據(jù)某個(gè)內(nèi)容屬性而源數(shù)據(jù)沒(méi)有抓取該屬性萄焦,因此做抓取前盡量考慮周全控轿,預(yù)留好一些字段是很有必要的冤竹。
以從騰訊網(wǎng)抓取的新聞部分屬性為例:
1.2. 用戶(hù)數(shù)據(jù)
搞定內(nèi)容之后,我們還需要了解用戶(hù)茬射,推薦的基礎(chǔ)也是用戶(hù)的行為鹦蠕。在新聞網(wǎng)站上,最簡(jiǎn)單的行為就是點(diǎn)擊在抛。一名用戶(hù)在網(wǎng)站上點(diǎn)擊了一條新聞钟病,我們可以認(rèn)為他對(duì)這條新聞感興趣,此時(shí)前端需要將這條記錄上報(bào)到后臺(tái)刚梭,后臺(tái)再將log落地肠阱。這個(gè)過(guò)程可以是實(shí)時(shí)的,也可以用消息隊(duì)列的方式分批處理朴读,總之屹徘,依據(jù)場(chǎng)景需求以及系統(tǒng)架構(gòu)能力。一條最簡(jiǎn)單的log如下:
user_id,news_id,timestamp?
user_id可以是用戶(hù)手機(jī)的IMEI標(biāo)識(shí)衅金、PC的MAC地址噪伊、瀏覽器的Cookie ID等等,總之是需要能唯一標(biāo)識(shí)用戶(hù)的序列氮唯。當(dāng)然這里涉及到的一個(gè)問(wèn)題是鉴吹,一個(gè)用戶(hù)可以在多個(gè)終端登錄,所以我們還需要用戶(hù)的登錄態(tài)來(lái)解決一對(duì)多的問(wèn)題您觉,比如用登錄QQ拙寡、微信賬號(hào)來(lái)做一個(gè)關(guān)聯(lián)映射。
上述列舉的log只包含最簡(jiǎn)單的信息琳水,復(fù)雜的推薦需要更多的信息,比如來(lái)源IP(用以識(shí)別用戶(hù)登錄地域)般堆,收藏在孝、評(píng)論等行為(造成不同興趣權(quán)重),曝光行為(用于之后CTR模型的訓(xùn)練)等等淮摔。
有了內(nèi)容數(shù)據(jù)和用戶(hù)數(shù)據(jù)之后私沮,我們已經(jīng)可以建立一些簡(jiǎn)單基于用戶(hù)行為的推薦策略了,比如itemCF和橙、userCF仔燕,具體實(shí)現(xiàn)方式我在之前的文章里寫(xiě)過(guò):文章鏈接,這里不再贅述魔招。但基于用戶(hù)行為的策略晰搀,往往在系統(tǒng)冷啟動(dòng)時(shí)表現(xiàn)不會(huì)太好,我們還需要更多維的推薦策略办斑。
2. 內(nèi)容畫(huà)像
眾所周知外恕,基于行為推薦需要一定的用戶(hù)行為積累杆逗,而新聞生產(chǎn)速度很快,時(shí)效性要求又比較高鳞疲,這時(shí)候我們需要一些 Content-based 方法來(lái)做推薦罪郊。內(nèi)容畫(huà)像是實(shí)現(xiàn)的基礎(chǔ)。
2.1. 文本分類(lèi)
分類(lèi)尚洽,是新聞?wù)Z義特征里顆粒度最粗的一個(gè)特征悔橄。根據(jù)分類(lèi)可以對(duì)文本有一個(gè)基本的語(yǔ)義劃分,可以讓用戶(hù)對(duì)興趣內(nèi)容有較為明顯的感知腺毫,所以分類(lèi)往往是內(nèi)容畫(huà)像的第一步癣疟。
在分類(lèi)之前,我們首先要制定統(tǒng)一的分類(lèi)體系拴曲,根據(jù)業(yè)務(wù)需求按顆粒度區(qū)分一/二級(jí)分類(lèi)争舞。這一步可以人工標(biāo)注,也可通過(guò)無(wú)監(jiān)督聚類(lèi)的方法澈灼【捍ǎ總之,這對(duì)于融合多來(lái)源叁熔、多類(lèi)型的內(nèi)容數(shù)據(jù)至關(guān)重要委乌。
分類(lèi)的方法有很多,傳統(tǒng)統(tǒng)計(jì)方法里如 Naive Bayesian荣回、SVM遭贸,深度學(xué)習(xí)里的 CNN、LSTM 等都可以勝任心软。不過(guò)在大多數(shù)情況下壕吹,傳統(tǒng)方法已經(jīng)可以做到很好的效果,且實(shí)現(xiàn)簡(jiǎn)單删铃,因此我們通常選擇前者耳贬。
2.2. 關(guān)鍵詞提取
分類(lèi)完成之后,可以說(shuō)我們的內(nèi)容畫(huà)像已經(jīng)初見(jiàn)端倪猎唁。然而咒劲,僅僅精確到分類(lèi)顆粒度的個(gè)性化推薦是很難滿(mǎn)足用戶(hù)的。用戶(hù)對(duì)于文章的興趣诫隅,往往精確到某個(gè)明星腐魂、某支球隊(duì),要捕捉到這種顆粒度的信息逐纬,只要依賴(lài)于關(guān)鍵詞蛔屹。
關(guān)鍵詞提取是對(duì)于文章中出現(xiàn)的具有代表語(yǔ)義作用的詞匯進(jìn)行提取,并賦予權(quán)重风题。這類(lèi)算法很多判导,baseline 的方法比如 tfidf嫉父、textrank,都能做到很好的效果眼刃。當(dāng)然绕辖,如果我們要做到更精確,還需要結(jié)合業(yè)務(wù)數(shù)據(jù)做一些人工規(guī)則擂红,比如將詞性仪际、實(shí)體、詞出現(xiàn)位置等特征與 baseline 方法進(jìn)行結(jié)合昵骤,或者用人工標(biāo)注的方法轉(zhuǎn)換為有監(jiān)督學(xué)習(xí)的問(wèn)題树碱。
2.3. 主題抽取
分類(lèi)和關(guān)鍵詞,顆粒度的跨度其實(shí)是比較大的变秦。在基于語(yǔ)義的個(gè)性化推薦過(guò)程中成榜,一些冷門(mén)關(guān)鍵詞往往比較難以命中,為了彌補(bǔ)這個(gè)真空蹦玫,文本主題的概念就派上用場(chǎng)了赎婚。
圖2-1 LDA示意圖(來(lái)源:由Slxu.public - 自己的作品,CC BY-SA 3.0樱溉,https://commons.wikimedia.org/w/index.php?curid=7922733)
諸如 pLSA挣输、LDA 的主題模型假設(shè)一篇文檔的生成過(guò)程是這樣的:
1. 作者從文檔 - 主題分布 θ 中隨機(jī)挑選一個(gè)主題 zi
2. 作者從主題 - 詞分布 φ 中隨機(jī)挑選一個(gè)詞 wj
3. 重復(fù)步驟1,直到文檔所有詞生成完成
LDA 與 pLSA 不同之處在于 LDA 還假設(shè)這兩個(gè)分布也不是固定的福贞,而是遵循兩個(gè)狄利克雷先驗(yàn)分布撩嚼。總之挖帘,這類(lèi)算法最終計(jì)算出的是文檔集合中存在的“隱分類(lèi)”完丽,表征文檔語(yǔ)義中存在的一些潛在關(guān)聯(lián)。主題的維度我們一般設(shè)置為較大的數(shù)字拇舀,這樣我們便擁有了一個(gè)顆粒度介于分類(lèi)與關(guān)鍵詞之間的特征舰涌。LDA 的實(shí)現(xiàn)方法可以參照之前的文章:文章鏈接
有了上述三類(lèi)特征后,內(nèi)容畫(huà)像已經(jīng)可以滿(mǎn)足大部分需求了你稚。須知,上文所說(shuō)的方法都是比較基礎(chǔ)的方式朱躺,像 CNN刁赖、RNN、Attention Model 都是可以嘗試的方法长搀,NLP 的研究和優(yōu)化需要投入大量的精力宇弛,如果想在這上面深挖,建議系統(tǒng)學(xué)習(xí) NLP 相關(guān)課程源请。
3. 用戶(hù)畫(huà)像
3.1. 興趣畫(huà)像
有了內(nèi)容畫(huà)像枪芒,我們?cè)賮?lái)計(jì)算用戶(hù)的興趣畫(huà)像就是水到渠成的事情了彻况。簡(jiǎn)單的方法就是根據(jù)用戶(hù)的行為,檢索到一定時(shí)間內(nèi)用戶(hù)所有有過(guò)正向行為(點(diǎn)擊/收藏/評(píng)論)的 news舅踪,把它們看成一篇內(nèi)容纽甘,對(duì)所有特征進(jìn)行線性加和,作為用戶(hù)在該時(shí)間窗內(nèi)的興趣畫(huà)像抽碌。用戶(hù) u 的當(dāng)天興趣畫(huà)像計(jì)算公式如下:
其中 m 為用戶(hù) u 在當(dāng)天產(chǎn)生正向行為的文檔集合悍赢,n 為文檔 i 的特征集合。θj?表示文檔 i 第 j 個(gè)特征的權(quán)重货徙,P(θj) 表示第 j 個(gè)特征的先驗(yàn)概率(這一步主要是為了減弱頭部文章對(duì)用戶(hù)畫(huà)像的影響左权,若某天某一類(lèi)特征的新聞很熱,那么有可能大多數(shù)用戶(hù)畫(huà)像里都會(huì)有這類(lèi)特征痴颊,但它并不能真正代表用戶(hù)的興趣傾向)赏迟。
隨著時(shí)間推移,用戶(hù)的興趣會(huì)發(fā)生遷移蠢棱,因此我們需要加上時(shí)間的影響因素:
yt?表示 t 時(shí)刻的用戶(hù)畫(huà)像锌杀,yt-1?表示上一時(shí)刻的畫(huà)像,λ 為時(shí)間衰減因子裳扯。
3.2. 基礎(chǔ)畫(huà)像
除了上述的用戶(hù)興趣畫(huà)像外抛丽,還有一些用戶(hù)的屬性是我們感興趣的,比如用戶(hù)的性別饰豺、年齡亿鲜、職業(yè)、所處地域冤吨,這部分可以根據(jù)業(yè)務(wù)特點(diǎn)來(lái)獲取蒿柳,這些我們稱(chēng)之為基礎(chǔ)畫(huà)像′鲶。基礎(chǔ)畫(huà)像雖然沒(méi)有興趣畫(huà)像顆粒度細(xì)致垒探,但在冷啟動(dòng)、地域強(qiáng)相關(guān)等業(yè)務(wù)場(chǎng)景也是比較重要的怠李。
在業(yè)務(wù)實(shí)踐中圾叼,我們發(fā)現(xiàn)用戶(hù)的興趣變化是很快的,并且很難用某一種狀態(tài)涵蓋住用戶(hù)所有的興趣范圍捺癞。比如當(dāng)我們?cè)跒g覽新聞時(shí)夷蚊,我們的近期瀏覽記錄也許的確反映了我的興趣變化,但也有可能我只是對(duì)熱點(diǎn)感興趣髓介,抑或是想試探一下不同領(lǐng)域的閱讀惕鼓,再或者僅僅是手抖點(diǎn)錯(cuò)了。再比如唐础,系統(tǒng)依據(jù)用戶(hù)所處地域推薦內(nèi)容箱歧,然而這個(gè)用戶(hù)有可能只是來(lái)外地出差矾飞,他更感興趣的可能依舊是常住地的新聞……無(wú)論如何,在計(jì)算畫(huà)像的時(shí)候我們無(wú)法確保用戶(hù)的意圖呀邢,因此在快速反饋用戶(hù)行為的同時(shí)洒沦,加上多狀態(tài)的用戶(hù)畫(huà)像是有必要的。通常我們的做法是分別記錄用戶(hù)的長(zhǎng)期和短期畫(huà)像驼鹅,在針對(duì)不同的畫(huà)像做不同的推薦召回微谓,以此滿(mǎn)足用戶(hù)不同狀態(tài)下的閱讀需求。
4. 個(gè)性化召回
說(shuō)完數(shù)據(jù)的基礎(chǔ)積累输钩,包括用戶(hù)畫(huà)像和內(nèi)容畫(huà)像的構(gòu)建豺型,接下來(lái)我們可以正式著手開(kāi)始推薦了。以新聞推薦舉例來(lái)說(shuō)买乃,推薦可以有很多策略姻氨,包括基于用戶(hù)興趣畫(huà)像語(yǔ)義的策略(興趣關(guān)鍵詞/分類(lèi)/主題相關(guān)),基于用戶(hù)行為的策略(itemCF/userCF/clusterCF)剪验,基于位置的策略(地域相關(guān))肴焊,基于社交關(guān)系的策略(SNS推薦)等等。
在一次個(gè)性化推薦中功戚,我們通常需要同時(shí)運(yùn)用多種策略娶眷。如果嘗試僅僅通過(guò)某種精細(xì)化的推薦策略(如關(guān)鍵詞/itemCF)進(jìn)行推薦的話(huà),用戶(hù)往往會(huì)在初期表現(xiàn)得很感興趣啸臀,而隨著數(shù)量增多届宠,用戶(hù)會(huì)逐漸疲勞。畢竟用戶(hù)的閱讀傾向往往是多元的乘粒,特別是在新聞?lì)I(lǐng)域豌注,絕大多數(shù)用戶(hù)除了自己的一畝三分地外,也會(huì)比較關(guān)注當(dāng)日熱點(diǎn)新聞灯萍,或者關(guān)注一些其他領(lǐng)域的潛在興趣轧铁。因此,我們通常是從多種策略拉取內(nèi)容旦棉,而后依據(jù)某種規(guī)則統(tǒng)一進(jìn)行排序齿风。這個(gè)過(guò)程一般稱(chēng)作召回。
4.1. 召回策略
4.1.1. 協(xié)同過(guò)濾
協(xié)同過(guò)濾(Collaborative Filtering)可說(shuō)是推薦系統(tǒng)里資歷最老最經(jīng)典的一種算法了绑洛,如 userCF聂宾、itemCF。原理是基于用戶(hù)對(duì)內(nèi)容的行為協(xié)同诊笤,為某一用戶(hù)沒(méi)有看過(guò)的某條內(nèi)容作出點(diǎn)擊預(yù)測(cè)。實(shí)現(xiàn)方法有很多種巾陕,如傳統(tǒng)的 Memory-based 方法讨跟、基于矩陣分解的方法(LFM/SVD/SDV++)纪他、基于 DNN 的方法。
Memory-based 方法很簡(jiǎn)單晾匠,是基于統(tǒng)計(jì)的一種算法茶袒。以 item-based CF 舉例:
根據(jù)用戶(hù)點(diǎn)擊行為,我們可以統(tǒng)計(jì)出 item-item 的共現(xiàn)矩陣(矩陣單元內(nèi)為 item i 與 item j 共同被用戶(hù)點(diǎn)擊的次數(shù))凉馆,再依此通過(guò)Jaccard相似度/余弦相似度/歐氏距離得出 item 相似度矩陣薪寓,最后根據(jù)用戶(hù)的點(diǎn)擊記錄檢索出 topK 相似的內(nèi)容推薦給用戶(hù)。在計(jì)算過(guò)程中需要考慮一些因素澜共,比如熱門(mén)物品對(duì)相似度計(jì)算的影響向叉、不同傾向的用戶(hù)的影響等等。
然而 Memory-based 方法不能解決的問(wèn)題是嗦董,當(dāng)我們的矩陣很稀疏時(shí)母谎,大多數(shù) item 和 item 之間是沒(méi)有關(guān)聯(lián)的(相似度為0),這也就造成最后我們召回的內(nèi)容覆蓋率很低京革,也許大多集中在頭部?jī)?nèi)容奇唤。于是基于矩陣分解的方法誕生了。
MF(Matrix Factorization)的原理是將一個(gè)高維稀疏矩陣分解成兩個(gè)低秩矩陣匹摇,其中 k 被稱(chēng)為隱向量維度咬扇。在原始的稀疏矩陣 R 中,大部分二階特征的關(guān)系系數(shù)是缺失的廊勃。而通過(guò)訓(xùn)練模型最小化 R 和預(yù)測(cè)矩陣 R‘ 的損失(如最小二乘)懈贺,可以求出任意 Ri,j 的值。
MF 可說(shuō)是大部分推薦系統(tǒng)里協(xié)同過(guò)濾的標(biāo)桿方法了供搀,但仍然存在一些問(wèn)題隅居。比如過(guò)于稀疏的矩陣對(duì)于最后評(píng)分的預(yù)測(cè)依然有很大影響,并且當(dāng)用戶(hù)特征或者內(nèi)容特征缺失(即冷啟動(dòng))時(shí)葛虐,無(wú)法進(jìn)行合理的預(yù)測(cè)胎源。此時(shí),基于深度學(xué)習(xí)的一些嘗試開(kāi)始了屿脐。如基于DNN實(shí)現(xiàn)涕蚤,可以很輕易地將內(nèi)容的一些語(yǔ)義特征,以及用戶(hù)的固有屬性與行為特征拼接在一起作為神經(jīng)網(wǎng)絡(luò)輸入來(lái)訓(xùn)練的诵,可以在之前行為協(xié)同的前提下加入對(duì)內(nèi)容特征的學(xué)習(xí)万栅,從而解決冷啟動(dòng)問(wèn)題。感興趣的同學(xué)可以閱讀相關(guān)論文西疤,在此不做展開(kāi)烦粒。
4.1.2. 基于內(nèi)容
基于內(nèi)容的召回主要是以之前 NLP 得到的內(nèi)容畫(huà)像為基礎(chǔ),以item 對(duì)應(yīng)分類(lèi)/主題/關(guān)鍵詞的權(quán)重建立召回,依據(jù)用戶(hù)畫(huà)像的相應(yīng)權(quán)重和內(nèi)容畫(huà)像的距離排序召回扰她。召回過(guò)程如下:
4.1.3. 基于用戶(hù)群
其實(shí)這種策略也是協(xié)同過(guò)濾的概念兽掰,當(dāng)用戶(hù)的粒度擴(kuò)大時(shí),可以為處于某一群體內(nèi)的單個(gè)用戶(hù)在興趣范圍內(nèi)帶來(lái)更多樣的閱讀內(nèi)容徒役,在一定程度上也是一種興趣探索孽尽。
首先我們需要對(duì)用戶(hù)分群,這里我們采用的是用戶(hù)畫(huà)像中的 topic 興趣(2000維)忧勿,相當(dāng)于對(duì)用戶(hù)進(jìn)行了降維杉女。降維的方法有很多,包括 autoencoder 等深度學(xué)習(xí)方法都可以嘗試鸳吸。不僅僅是用戶(hù)的文本興趣熏挎,用戶(hù)的人口屬性、閱讀記錄层释、社交關(guān)系等等都可以拼接進(jìn)來(lái)婆瓜,最終的目的都是將用戶(hù) embedding 成一個(gè)向量。
完成了用戶(hù)的向量化之后贡羔,接下來(lái)就是聚類(lèi)了廉白,傳統(tǒng)的 K-means 基本可以勝任大部分場(chǎng)景。如果需要多分類(lèi)或者體現(xiàn)層級(jí)關(guān)系的話(huà)乖寒,GMM和層次聚類(lèi)的算法也可以做一些嘗試猴蹂。
最終我們聚出一批類(lèi)簇,根據(jù)類(lèi)簇內(nèi)對(duì)不同內(nèi)容的相對(duì)點(diǎn)擊率(文章i在類(lèi)簇a中點(diǎn)擊率/文章i在所有類(lèi)簇中平均點(diǎn)擊率)排序楣嘁,對(duì)類(lèi)簇用戶(hù)進(jìn)行推薦磅轻。另外,也可以根據(jù)類(lèi)簇中用戶(hù)的傾向主題逐虚,給類(lèi)簇打上解釋性label聋溜,作為露出。
4.2. 倒排鏈
前文中叭爱,我們提到內(nèi)容數(shù)據(jù)入庫(kù)時(shí)的結(jié)構(gòu)是 itemID - detail 這種形式撮躁。而在召回過(guò)程中,我們要用到內(nèi)容畫(huà)像中的分類(lèi)买雾、主題等屬性把曼,若要通過(guò)遍歷 itemID 然后計(jì)算相似度無(wú)疑是不現(xiàn)實(shí)的。于是這里我們用到搜索引擎中一個(gè)常用的技術(shù)——倒排漓穿。相較于 itemID - detail 這種形式(我們稱(chēng)之為正排)嗤军,我們?cè)僖?detailX - itemID 的形式建立倒排鏈,提高召回檢索效率晃危。
比如在關(guān)鍵詞召回中叙赚,我們按如下格式建立倒排表。(這里以 json 格式實(shí)例,實(shí)際中會(huì)采用效率更高的序列化形式):
{
? ? 'tag_1':
? ? ? ? [ { itemID: '13', weight: 0.7 }, { itemID: '2', weight: 0.53 } ],
? ? 'tag_2':
? ? ? ? [ { itemID: '1', weight: 0.37 } ],
? ? ...
}
上述結(jié)構(gòu)中纠俭,索引的key是tag的編號(hào)沿量,每一個(gè)tagID下則對(duì)應(yīng)與之相關(guān)的文章摘要(示例中只包括文章ID和tag在此文章中的權(quán)重)按相關(guān)度排序的數(shù)組。隨后將倒排鏈加載到分布式索引里冤荆,在拿到用戶(hù)畫(huà)像的興趣tag后,我們根據(jù)tagID檢索出倒排鏈权纤,最后再根據(jù)倒排鏈中的itemID去正排里拉取詳情即可钓简。
4.3. 系統(tǒng)架構(gòu)
有了上述召回的基礎(chǔ),我們便可以初步搭建起推薦系統(tǒng)的架構(gòu)汹想。完整的推薦系統(tǒng)很龐大外邓、很復(fù)雜,基于不同的業(yè)務(wù)也會(huì)有不同的實(shí)踐方式古掏,這里只談一些基礎(chǔ)的损话、公用的部分,以作參考槽唾。
接入層:接收前端請(qǐng)求的CGI應(yīng)用丧枪,一般處理內(nèi)容詳情拉取、日志上報(bào)庞萍、各種人工規(guī)則干預(yù)拧烦、去重等任務(wù),計(jì)算邏輯簡(jiǎn)單钝计,I/O密集型任務(wù)恋博。這里我們用 Golang 實(shí)現(xiàn),看重他的goroutines處理高并發(fā)的能力私恬。
日志采集:消息隊(duì)列處理前后端的日志上報(bào)(點(diǎn)擊/曝光/負(fù)反饋)债沮,采用Kafka,實(shí)時(shí)打到 Spark Streaming 處理實(shí)時(shí)數(shù)據(jù)本鸣,同時(shí)定期落地到 hdfs 上用以離線處理疫衩。
畫(huà)像計(jì)算:用 Spark/Hadoop 按前文所述的方法批量計(jì)算長(zhǎng)期用戶(hù)畫(huà)像,一天計(jì)算一次永高,結(jié)果存入 HDFS隧土。
實(shí)時(shí)畫(huà)像:采用 Spark Streaming 直接拉取 Kafka 的流實(shí)時(shí)進(jìn)行衰減/合并計(jì)算,結(jié)果寫(xiě)入到 Redis命爬,供線上使用曹傀。因?yàn)槲覀兠刻爝€會(huì)計(jì)算一次長(zhǎng)期畫(huà)像,因此短期畫(huà)像只用保存一天即可饲宛。
召回索引:離線更新召回倒排表皆愉,定期刷新至線上召回集群的內(nèi)存里,以加快檢索速度。另外在召回模塊中幕庐,還需要實(shí)現(xiàn)諸如截?cái)嗑米丁⑦^(guò)濾等算子,用以在召回的過(guò)程中快速過(guò)濾曝光內(nèi)容异剥,截取topN的文章摘要等瑟由。
按照上述架構(gòu)搭建起來(lái)后的系統(tǒng)投入到線上使用,QPS在單機(jī)1k左右冤寿,在召回和接入上還有一些待優(yōu)化的地方歹苦。最終的信息流中,我們從個(gè)性化的多路召回中拿到了一批內(nèi)容督怜,最后根據(jù)文章質(zhì)量(點(diǎn)擊量/點(diǎn)擊率/閱讀時(shí)長(zhǎng))統(tǒng)一排序殴瘦,輸出到用戶(hù)側(cè),完成推薦号杠。這樣蚪腋,一個(gè)推薦系統(tǒng)的完整流程便完成了。
5. 排序模型
我們根據(jù)不同召回策略召回了一批文章姨蟋,并統(tǒng)一根據(jù)文章質(zhì)量排序輸出屉凯。但實(shí)際上,用戶(hù)的閱讀興趣還會(huì)受到很多其他因素的影響芬探。比如用戶(hù)所處的網(wǎng)絡(luò)環(huán)境神得,文章點(diǎn)擊率、時(shí)效性偷仿,用戶(hù)的年齡哩簿、性別,或者多種因素交叉的影響酝静,而排序最終決定了用戶(hù)優(yōu)先看到的內(nèi)容(最終推薦流是召回隊(duì)列的topN)节榜,因此排序過(guò)程是至關(guān)重要的。
5.1. 模型選擇
排序的問(wèn)題在機(jī)器學(xué)習(xí)中有很多可以使用的方法别智,應(yīng)用到推薦系統(tǒng)實(shí)際上就是一個(gè)二分類(lèi)問(wèn)題宗苍。以用戶(hù)、內(nèi)容薄榛、上下文的一些特征作為輸入讳窟,輸出是介于0(不感興趣)和1(感興趣)之間的一個(gè)概率,稱(chēng)作 pCTR(預(yù)測(cè)點(diǎn)擊率)敞恋。分類(lèi)算法有很多丽啡,出于規(guī)模和性能的考量,業(yè)界更多的還是使用線性的方法硬猫,傳統(tǒng)方法有 Logistic Regression 和 Factorization Machine补箍。
5.1.1. Logistic Regression
邏輯回歸是一個(gè)經(jīng)久不衰的統(tǒng)計(jì)分析方法改执,它的預(yù)測(cè)公式如下:
其中g(shù)(x)為sigmoid函數(shù),它的作用是將數(shù)值壓縮到(0,1)的范圍內(nèi)坑雅,函數(shù)曲線如下:
有了公式辈挂,我們便可以將已知的用戶(hù)特征和行為作為訓(xùn)練集的輸入和輸出,其中 x 表示輸入特征向量裹粤,θ 表示每一維特征對(duì)應(yīng)的權(quán)重终蒂。輸入特征我們可以大致分為用戶(hù)特征、行為特征和內(nèi)容特征遥诉,示例如下:
這其中每一個(gè)特征即是 x 向量中的一維后豫,也對(duì)應(yīng)著預(yù)測(cè)公式中 θ 向量中的一個(gè)權(quán)重,而我們模型訓(xùn)練的過(guò)程則是求出 θ 向量突那,最終我們可以通過(guò)線上的 x 向量實(shí)際輸入,代入公式最終得出預(yù)測(cè)點(diǎn)擊率 g(x)构眯。
5.1.2. 特征工程
當(dāng)然愕难,以上示例的特征取值如果直接使用 LR 進(jìn)行訓(xùn)練,效果肯定是不好的惫霸。因?yàn)?LR 學(xué)到的是變量的線性關(guān)系猫缭,而有一些特征取值卻并不具備線性相關(guān)。比如性別壹店,0代表男性猜丹,1代表女性,但這里的數(shù)值大小關(guān)系并沒(méi)有什么意義硅卢;再比如年齡射窒,不一定所有年齡段內(nèi),興趣都和年齡大小完全線性相關(guān)将塑。因此脉顿,在訓(xùn)練之前我們需要對(duì)特征做一些諸如離散化、數(shù)值分桶等操作点寥,盡量讓特征與結(jié)果表現(xiàn)出線性關(guān)系艾疟。
另外,有些特征單獨(dú)對(duì)分類(lèi)影響并不大敢辩,但與其他特征交叉影響就明顯了蔽莱。比如年齡X性別、分類(lèi)X關(guān)鍵詞戚长,因此盗冷,我們需要根據(jù)一些業(yè)務(wù)上的了解和經(jīng)驗(yàn)來(lái)決定如何進(jìn)行特征交叉(當(dāng)然我們可以直接將所有特征的笛卡爾積扔進(jìn)去訓(xùn)練,但對(duì)于訓(xùn)練效率來(lái)說(shuō)這通常是不現(xiàn)實(shí)的)历葛,往往在特征上的工作占了模型工作的絕大部分時(shí)間正塌,因?yàn)樘卣鞴こ痰馁|(zhì)量決定了模型效果的上限嘀略。
我們也可以用一些決策樹(shù)的方法來(lái)自動(dòng)選擇特征,比如 Facebook 在2014年提出的 GBDT+LR 的推薦模型乓诽,就是采用 GBDT(梯度提升決策樹(shù))的方法做特征選擇帜羊,并將樹(shù)的輸出直接作為 LR 的特征輸入,取得了比較好的效果鸠天。
5.1.3. Factorization Machine
特征工程是個(gè)耗時(shí)耗力讼育,且非常考驗(yàn)業(yè)務(wù)理解力的過(guò)程稠集,當(dāng)我們處在項(xiàng)目初期奶段,又不想花太多精力去做特征選擇時(shí),F(xiàn)M 算法便成了我們的另一種選擇剥纷。FM 的預(yù)測(cè)公式如下:
仔細(xì)對(duì)比上述公式和 LR 公式痹籍,可以看出 FM 本質(zhì)上其實(shí)就是在 LR 的基礎(chǔ)上增加了發(fā)掘二階特征關(guān)系的kernel,vi,vj 表示的是二階特征經(jīng)過(guò)矩陣分解后得到的低秩矩陣的值:
矩陣分解在推薦系統(tǒng)中很常用晦鞋,實(shí)質(zhì)上是將一個(gè)高維稀疏矩陣分解成兩個(gè)低秩矩陣蹲缠,其中 k 被稱(chēng)為隱向量維度。在原始的稀疏矩陣 R 中悠垛,大部分二階特征的關(guān)系系數(shù)是缺失的线定。而通過(guò)訓(xùn)練模型最小化 R 和預(yù)測(cè)矩陣 R‘ 的損失(如最小二乘),可以求出任意 Ri,j的值确买。FM 的kernel在此基礎(chǔ)上學(xué)習(xí)到任意二階特征的非線性關(guān)系斤讥,求得它們的權(quán)重。
5.2. 模型訓(xùn)練
確定模型后湾趾,我們需要根據(jù)目標(biāo)確認(rèn)損失函數(shù)芭商,比如回歸一般使用 RMSE,二分類(lèi)使用 Cross Entropy撑帖,然后我們就需要朝最小化損失函數(shù)的目的來(lái)訓(xùn)練參數(shù)了蓉坎。
求解函數(shù)有多種,如果數(shù)據(jù)量較小胡嘿,可以選擇批量訓(xùn)練的方式蛉艾,如傳統(tǒng)的梯度下降法: Batch Gradient Descent,也可以選擇擬牛頓法如 L-BFGS 衷敌,用二階導(dǎo)數(shù)求得更快的訓(xùn)練速度勿侯。它們的優(yōu)點(diǎn)是考慮到全部樣本,模型準(zhǔn)確缴罗,但缺點(diǎn)是數(shù)據(jù)量太大時(shí)訓(xùn)練速度很慢助琐。我們可以考慮每次采用小批量的樣本訓(xùn)練模型的 online learning,從而達(dá)到實(shí)時(shí)更新模型的效果面氓。方法如 SGD兵钮、FOBOS蛆橡、RDA、FTRL 等等掘譬,對(duì)比和公示詳解建議閱讀馮揚(yáng)的《在線最優(yōu)化求解》泰演,講得很詳細(xì),這里就不贅述了葱轩。
5.3. 在線預(yù)測(cè)
訓(xùn)練完成后睦焕,我們可以把模型文件(實(shí)質(zhì)是所有參數(shù)的取值)保存到本地,或者為了更高的執(zhí)行效率靴拱,把它們加載到內(nèi)存垃喊。預(yù)測(cè)的執(zhí)行步驟如下:
1. 召回內(nèi)容隊(duì)列
2. 線上的服務(wù)器從內(nèi)存讀取參數(shù)取值 θ
3. 拉取到內(nèi)容/用戶(hù)/上下文的實(shí)時(shí)特征 x
4. 代入預(yù)測(cè)公式,計(jì)算用戶(hù) u 對(duì)內(nèi)容 i 的點(diǎn)擊率
5. 依據(jù)點(diǎn)擊率對(duì)召回內(nèi)容排序并返回
每隔一段時(shí)間袜炕,模型更新之后需要將新訓(xùn)練得到的參數(shù)值刷新到預(yù)測(cè)服務(wù)器的內(nèi)存中本谜。當(dāng)特征維度很大時(shí)模型文件體積也很大,此時(shí)如何按時(shí)完成更新是個(gè)問(wèn)題偎窘,Parameter Server 是一類(lèi)解決這類(lèi)問(wèn)題的框架耕突。
5.4. Rerank
在排序完成之后,直接將排序結(jié)果呈現(xiàn)在用戶(hù)面前可能不是一個(gè)好的選擇评架。首先,產(chǎn)品需要有一些特殊的內(nèi)容形式作為自己的品牌形象炕泳;另外纵诞,完全根據(jù)模型規(guī)則走可能讓用戶(hù)興趣越來(lái)越窄,推薦內(nèi)容同質(zhì)化培遵,久而久之則會(huì)影響用戶(hù)對(duì)內(nèi)容產(chǎn)品的黏性浙芙。
解決這個(gè)問(wèn)題就是在排序之后再進(jìn)行一次 rerank,我們可以用人工規(guī)則的方式籽腕,或者貪心算法來(lái)確保最后推薦給用戶(hù)的 TOP10 內(nèi)容的多樣性嗡呼,以及插入一些對(duì)于用戶(hù)畫(huà)像里缺失興趣的探索。
6. 總結(jié)
推薦系統(tǒng)涉及到的東西很多皇耗,本文只是對(duì)各個(gè)環(huán)節(jié)作了些簡(jiǎn)單的概述南窗。如果要完善系統(tǒng)并真正滿(mǎn)足用戶(hù)的需求,則需要在各個(gè)環(huán)節(jié)都做深入的研究郎楼,希望大家共勉万伤。