從原理到策略算法再到架構(gòu)產(chǎn)品看推薦系統(tǒng)[轉(zhuǎn)]

作者 | HCY崇遠(yuǎn)

01 前言

本文源自于前陣子連續(xù)更新的推薦系統(tǒng)系列,前段時(shí)間給朋友整理一個(gè)關(guān)于推薦系統(tǒng)相關(guān)的知識(shí)教學(xué)體系,剛好自身業(yè)務(wù)中,預(yù)計(jì)明年初隨著業(yè)務(wù)規(guī)模增長(zhǎng),估摸著又要啟動(dòng)推薦相關(guān)的項(xiàng)目了,所以也是趁機(jī)把相關(guān)的知識(shí)結(jié)構(gòu)梳理了一遍。這這里重新做整理,并額外做了一些增減,讓整體邏輯會(huì)更通順一點(diǎn)。

整個(gè)文章的結(jié)構(gòu)邏輯,先從推薦系統(tǒng)的基礎(chǔ)知識(shí)結(jié)構(gòu)講起,然后由淺入深過(guò)渡到幾個(gè)推薦策略算法上,并且為每個(gè)推薦策略算法提供一些簡(jiǎn)單的入門Spark案例代碼,再?gòu)牟呗赃^(guò)渡到系統(tǒng)層級(jí),包括數(shù)據(jù)架構(gòu)、策略組合、效果評(píng)估等,最終再?gòu)纳蠈赢a(chǎn)品設(shè)計(jì)的角度去補(bǔ)充整個(gè)系統(tǒng)知識(shí)結(jié)構(gòu)。

整體來(lái)看,通篇并沒(méi)有涉及到特別高深的推薦算法(部分專門講這部分的文章,只有有一定基礎(chǔ)的朋友才更容易接受,本文章的邏輯略有不同),大多都是常規(guī)的策略模型,核心在于對(duì)整個(gè)推薦系統(tǒng)的知識(shí)結(jié)構(gòu)進(jìn)行解構(gòu),讓那些對(duì)于推薦系統(tǒng)感興趣的朋友能快速的建立起對(duì)于推薦系統(tǒng)的知識(shí)體系結(jié)構(gòu),甚至能夠通過(guò)文章中的算法案例,做簡(jiǎn)單的實(shí)踐,從而達(dá)到快速入門的目的。

友情提示:據(jù)不完全統(tǒng)計(jì),包含代碼片,通篇大約接近1萬(wàn)字,預(yù)計(jì)閱讀時(shí)間?我也不知道,哈哈,可以當(dāng)成短篇小說(shuō)來(lái)讀了。

02 推薦系統(tǒng)場(chǎng)景需求

本章節(jié)從場(chǎng)景的需求出發(fā),試圖來(lái)分析推薦系統(tǒng)需求的必須性,以及很多平臺(tái)言必談個(gè)性化推薦的現(xiàn)狀。

0.2.1 先從人工智能話題出發(fā)

說(shuō)推薦系統(tǒng)之前先掰掰人工智能,這個(gè)詞估計(jì)大家能能聽(tīng)得懂,畢竟是風(fēng)口上的名詞,想沒(méi)聽(tīng)過(guò)也難。那么問(wèn)題來(lái)了,你覺(jué)得推薦系統(tǒng)與人工智能有什么關(guān)系?

或許大半的人會(huì)認(rèn)為沒(méi)有半毛錢的關(guān)系,這讓我想到了前幾天周末在知乎上懟的一個(gè)問(wèn)題,問(wèn)題的核心就是:“現(xiàn)在大數(shù)據(jù)都很low了,大家都是去搞人智能了”。

這典型就是對(duì)于人工智能定義認(rèn)知的問(wèn)題,個(gè)人認(rèn)為人工智能就是一個(gè)偏業(yè)務(wù)的定義,多維度多學(xué)科交叉的概念,壓根兒就不好以技術(shù)維度去對(duì)比去評(píng)判。其核心的三要素就是:算法、計(jì)算能力以及數(shù)據(jù)。

圍繞大量的基礎(chǔ)數(shù)據(jù),對(duì)基礎(chǔ)數(shù)據(jù)進(jìn)行特征處理,然后構(gòu)建有用的業(yè)務(wù)算法模型,然后基于分布式的基礎(chǔ)架構(gòu)計(jì)算能力,將算法模型的用于實(shí)際的生產(chǎn)環(huán)境,以機(jī)器替代人工的作業(yè),以提升效果與效率,達(dá)到機(jī)器智能化的目標(biāo)。

那再回到推薦系統(tǒng)的話題,在過(guò)去傳統(tǒng)的門戶網(wǎng)站或者其他領(lǐng)域,也是有推薦場(chǎng)景的,不過(guò)大部分都是基于編輯或者運(yùn)營(yíng)手動(dòng)進(jìn)行配置推送,隨著對(duì)數(shù)據(jù)、對(duì)算法模型的進(jìn)一步應(yīng)用,才逐漸有算法機(jī)器替代人工進(jìn)行推薦,并且達(dá)到諸如“千人千面”、“個(gè)性化”推薦的效果。

所以,追究其本質(zhì),其實(shí)也是算法模型+計(jì)算過(guò)程+基礎(chǔ)數(shù)據(jù)的流程,并且最終達(dá)到了機(jī)器自動(dòng)化、智能化的效果,從廣義的角度來(lái)說(shuō),或許復(fù)雜一些的推薦系統(tǒng)或許也能納入人工智能的范疇了(真心怕那種一說(shuō)到人工智能=神經(jīng)網(wǎng)絡(luò)的選手)。

0.2.2 推薦與檢索兩種信息獲取的方式

說(shuō)到推薦系統(tǒng),就不得不說(shuō)一下搜索引擎。不管是搜索也好、推薦也好,他們都是信息獲取的一種機(jī)制,核心區(qū)別在于主動(dòng)與被動(dòng)。

搜索引擎是典型的主動(dòng)觸發(fā)的形態(tài),即用戶已經(jīng)有明確的信息獲取意圖,渴望得到自身既定的目標(biāo)信息,讓后通過(guò)搜索規(guī)則進(jìn)行最終信息的獲取。

比如,你好奇什么是人工智能,那么你就會(huì)用諸如谷歌、或者國(guó)產(chǎn)大百度去搜索,然后獲取到相關(guān)網(wǎng)頁(yè),去點(diǎn)擊查看,最終完成你了解人工智能這個(gè)信息獲取的目的。這就是檢索的機(jī)制,你先要告訴系統(tǒng)你的意圖,然后在給你篩選你要的信息。

而推薦系統(tǒng)則大大的不同,它是一種系統(tǒng)主動(dòng)的行為,對(duì)于用戶來(lái)說(shuō)是一個(gè)被動(dòng)的行為,被動(dòng)的接受系統(tǒng)推送過(guò)來(lái)的信息。那這樣強(qiáng)扭是不是很尷尬呢?怎么有這么SHA叉的機(jī)制?

其實(shí)不是的,尷尬的是推的不對(duì),東西推對(duì)了就尷尬了,比如你正在瀏覽一個(gè)信息,正在愁這個(gè)信息還沒(méi)解決你的問(wèn)題的時(shí)候,系統(tǒng)啪丟給你幾個(gè)新增的信息,說(shuō)這個(gè)幾個(gè)信息可能能解決的問(wèn)題,你一看我湊,這正是我要的,感謝萬(wàn)能的推薦系統(tǒng)!

所以,推薦核心解決的還是用戶額外信息獲取的問(wèn)題,以及提升用戶的進(jìn)一步轉(zhuǎn)化,停留時(shí)間的延長(zhǎng)(只要停留時(shí)間延長(zhǎng),商業(yè)轉(zhuǎn)化機(jī)會(huì)就會(huì)加大,也是粘性提升的體現(xiàn)),而問(wèn)題的核心就是要推的準(zhǔn),推的恰到好處,不然就是反作用。

因?yàn)橥扑]要解決的就是海量信息冗余,用戶在目的不算很明確的情況,進(jìn)一步幫助其確定目標(biāo),主動(dòng)篩選信息的過(guò)程,推的不好那對(duì)于用戶來(lái)說(shuō)就更冗余了。

關(guān)于信息的獲取,其實(shí)還有一種常見(jiàn)的形態(tài),那就是結(jié)構(gòu)化導(dǎo)航,比如電商平臺(tái)的分門別類羅列,門戶網(wǎng)站的結(jié)構(gòu)化頻道信息。它是通過(guò)把信息進(jìn)行結(jié)構(gòu)化了,構(gòu)建脈絡(luò)結(jié)構(gòu),幫助你去獲取你要的信息。不過(guò),這個(gè)就不在我們的討論范圍內(nèi)了。

0.2.3 推薦系統(tǒng)的場(chǎng)景

說(shuō)了這么多篇邏輯理論的東西,或許很多朋友依然對(duì)推薦系統(tǒng)沒(méi)有一個(gè)很場(chǎng)景化的認(rèn)知,比如具體什么場(chǎng)景?具體什么形態(tài)?

image

這是我在騰訊視頻上截的圖,這就是典型的視頻推薦場(chǎng)景,我不是鵝廠騰訊視頻業(yè)務(wù)的算法工程師,所以我無(wú)法回答你他們的推薦機(jī)制,但我可以告訴你,當(dāng)時(shí)我的觀看主體是“地球脈動(dòng)”,結(jié)合推薦列表,大伙兒可以揣摩一下他的推薦機(jī)制。當(dāng)前觀看的屬性相關(guān)?導(dǎo)演關(guān)聯(lián)性?我的觀看記錄偏好?從我個(gè)人的評(píng)估來(lái)看,這些因素應(yīng)該都有。

順帶說(shuō)一下的就是,一個(gè)完整的好的推薦系統(tǒng),一定不會(huì)單純的依賴于某個(gè)推薦算法,雖然這個(gè)系列的后面文章中,我會(huì)講一些推薦機(jī)制或者算法邏輯,甚至附上簡(jiǎn)單的案例代碼,但還是要提前說(shuō)一下這個(gè)問(wèn)題。

我們?cè)賮?lái)看幾個(gè)同樣是騰訊系的產(chǎn)品推薦場(chǎng)景:

image

QQ音樂(lè)平臺(tái)的推薦,分析來(lái)看應(yīng)該跟我當(dāng)前主頁(yè)音樂(lè)的風(fēng)格、以及我的歷史瀏覽相關(guān)。

image

這是閱文網(wǎng)站的小說(shuō)小說(shuō)推薦,即當(dāng)你瀏覽一本小說(shuō)時(shí),下面會(huì)給這個(gè)推薦列表,從其描述以及個(gè)人分析來(lái)看,好像與個(gè)人的行為相關(guān)性會(huì)小一些,應(yīng)該是基于大盤用戶的瀏覽軌跡做的關(guān)聯(lián)分析,進(jìn)而進(jìn)行關(guān)聯(lián)推薦。

image

最后是電商平臺(tái)的典型案例,即你在瀏覽商品時(shí),一般都有猜你喜歡模塊,并且推薦系統(tǒng)得以大放光彩,成為應(yīng)用領(lǐng)域里典型的應(yīng)用場(chǎng)景,還是得益于亞馬遜。當(dāng)年亞馬遜使用推薦算法幫助其提升了XX(具體多少忘了)的年度利潤(rùn),從此一炮而紅,基本上電商平臺(tái)中的推薦系統(tǒng)就成了標(biāo)配。

0.2.4 推薦系統(tǒng)的一些坑

看了這么多例子,再結(jié)合自己身邊實(shí)際的體驗(yàn),確實(shí)不難發(fā)現(xiàn),各色各樣的產(chǎn)品、平臺(tái),都在打造自己的推薦產(chǎn)品,恨不得用戶一直點(diǎn)下去,永不跳出。鑒于這種情況,那些尚未為自己產(chǎn)品或者平臺(tái)開(kāi)發(fā)推薦邏輯的,是不是感覺(jué)自己少了個(gè)推薦系統(tǒng),哈哈。

其實(shí)核心還是那句話,推薦本身就是個(gè)雙刃劍,用的不好只會(huì)讓用戶徒增煩惱,這里所說(shuō)的好不好,不單純是說(shuō)準(zhǔn)不準(zhǔn)的問(wèn)題,準(zhǔn)是前提,即推薦給用戶切身所需肯定是好的,但這還不夠,你還需要在他需要的時(shí)候給他推,時(shí)機(jī)不對(duì)、場(chǎng)景不對(duì),即使你推的東西再準(zhǔn),那也是瞎比推。

所以,即使你覺(jué)得你少了個(gè)推薦系統(tǒng),也是需要慎重,或許跟完這個(gè)系列會(huì)好點(diǎn)?正如上面說(shuō)的,一些坑還是需要注意的。上面所說(shuō)的推薦時(shí)機(jī)以及場(chǎng)景就不再重復(fù)了。

第一,好的推薦系統(tǒng)一般情況下很依賴于用戶的行為數(shù)據(jù),因?yàn)閺挠脩粜袨橹凶匀荒芤桓Q用戶的一些偏好所在,但實(shí)際情況是,用戶的行為數(shù)據(jù)并不是這么容易的,當(dāng)用戶行為數(shù)據(jù)不夠的時(shí)候,基于用戶行為的分析結(jié)論就是個(gè)偽命題,甚至?xí)涯銕蝈e(cuò)誤的方向。

第二,用戶的偏好一定是會(huì)時(shí)間偏移進(jìn)行轉(zhuǎn)變的,所以用戶行為的有效性又會(huì)是一個(gè)問(wèn)題。

第三,假設(shè)這個(gè)是新用戶呢?完全沒(méi)有軌跡信息,怎么破。

第四,實(shí)際影響用戶的選擇的因素太多,我們?nèi)菀紫萑胫饔^臆斷的誤區(qū),綜合性考慮是一個(gè)完善推薦系統(tǒng)的必須思考的地方。

第五,產(chǎn)品層面的邏輯有時(shí)候比底層算法更有效,典型如上面閱文的截圖例子,“喜歡這本書(shū)的人也喜歡”,這就是一種策略,也是一種推薦解釋,可解釋性會(huì)提升推薦的可信度,諸如還有一些交互方式、產(chǎn)品形態(tài)都是對(duì)推薦轉(zhuǎn)化有影響的。

03 推薦系統(tǒng)的基礎(chǔ)知識(shí)

基于上面章節(jié)的內(nèi)容,我們對(duì)于推薦系統(tǒng)的常見(jiàn)場(chǎng)景有了一個(gè)大概的認(rèn)知,這個(gè)章節(jié),我們從推薦系統(tǒng)本身的基礎(chǔ)知識(shí)進(jìn)行拆解,幫我們從理論上掌握更多關(guān)于推薦系統(tǒng)相關(guān)的知識(shí)。

0.3.1 推薦系統(tǒng)概述

在上個(gè)章節(jié),我們也大致的提到過(guò),需要先明確的一點(diǎn)就是推薦算法或者推薦機(jī)制并不嚴(yán)格等同推薦系統(tǒng),推薦系統(tǒng)是一個(gè)相對(duì)復(fù)雜的業(yè)務(wù)系統(tǒng),里頭涉及到數(shù)據(jù)的處理、架構(gòu)的構(gòu)成、推薦的邏輯機(jī)制,反饋數(shù)據(jù)的回收、效果的跟蹤、AB測(cè)試等等。

并且,很多我們耳熟能詳?shù)耐扑]算法,他只是解決的某種特定情況下的推薦機(jī)制問(wèn)題,而整個(gè)系統(tǒng)很多時(shí)候是復(fù)合了多種算法結(jié)果,綜合呈現(xiàn)的一種結(jié)果。但可以肯定的是,各種理論邏輯、算法機(jī)制是構(gòu)建推薦系統(tǒng)的核心支撐,所以,學(xué)習(xí)推薦系統(tǒng),首先學(xué)習(xí)各種推薦算法并沒(méi)有毛病。

推薦算法概述-基于內(nèi)容屬性相似的推薦

從原始數(shù)據(jù)依賴的層面來(lái)說(shuō),常見(jiàn)的有基于內(nèi)容屬性的推薦機(jī)制,這種推薦邏輯很簡(jiǎn)單,只是單純的依賴物品之間的屬性相似來(lái)構(gòu)建推薦關(guān)系,容易理解,有時(shí)間還是有一定效果的,但實(shí)際上很多時(shí)候會(huì)存在這幾種情況,導(dǎo)致了這種原始推薦失效。

如果用戶瀏覽當(dāng)前的物品本身就不是用戶的菜,甚至是一個(gè)非優(yōu)質(zhì)信息(當(dāng)前主體不可控),再基于當(dāng)前物品進(jìn)行推薦就是個(gè)偽命題。

基于上面這條,即使當(dāng)前主體是用戶的目標(biāo),但再推類似主體會(huì)造成信息冗余,即當(dāng)前主體信息已經(jīng)解決了用戶的問(wèn)題。

所以,由于用戶行為的不可控,基于內(nèi)容屬性相似的推薦,風(fēng)險(xiǎn)還是挺高的,這是導(dǎo)致了這種原始直接的機(jī)制并不會(huì)得到廣泛的推廣。但與亂推薦相比,還是有一定正向作用的,畢竟用戶瀏覽的主體是自身選擇的結(jié)果,本身用戶對(duì)于其選擇的信息主體是有一定偏好性的。

推薦算法概述-基于用戶畫(huà)像的推薦

基于物品本身屬性的推薦,其實(shí)與個(gè)性化是沒(méi)有半毛錢的關(guān)系的,畢竟推薦候選集只跟物品主體有關(guān),與用戶無(wú)關(guān),就談不上什么個(gè)性化了。

而基于用戶畫(huà)像(更多人喜歡用基于用戶標(biāo)簽)的推薦,則更大程度上依賴于用戶的畫(huà)像屬性來(lái)推薦,這就體現(xiàn)了用戶偏好信息,根據(jù)偏好信息來(lái)選擇候選集。

這是一種很通用的做法,并且在大規(guī)模數(shù)據(jù)集情況下,很多實(shí)際的產(chǎn)生過(guò)程中喜歡使用這種機(jī)制。而用戶的畫(huà)像,或者更具體點(diǎn)用戶的興趣標(biāo)簽如何構(gòu)建呢?其實(shí)就是依賴用戶累積的行為數(shù)據(jù)了,通過(guò)行為數(shù)據(jù)生成用戶的興趣標(biāo)簽。

這看似是一種相對(duì)靠譜的做法,畢竟如果把用戶的愛(ài)好都分析清楚了,主動(dòng)給用戶做推薦不就顯得很個(gè)性化了嗎?在實(shí)際的場(chǎng)景中,首先,并不是所有用戶的行為都足夠用來(lái)表征其興趣偏好的,即我們會(huì)高估用戶的行為集合,從而產(chǎn)生有偏差的畫(huà)像屬性,更甚者,如果用戶完全沒(méi)有行為怎么辦呢?

其次,通常來(lái)說(shuō),用戶的興趣愛(ài)好是會(huì)隨時(shí)間遷移而改變的,所以,把我用戶的興趣程度以及其變化并不是一個(gè)容易的事情,更何況用戶實(shí)際的選擇還會(huì)受很多因素影響,比如,我當(dāng)前查找的一個(gè)信息并不是我之前掌握的信息,那意味著這些信息偏好在我的歷史軌跡中都體現(xiàn)不出來(lái),那單純的通過(guò)我的興趣去推薦就顯得不靠譜了。

但不管怎么說(shuō),根據(jù)用戶的偏好來(lái)做推薦,大方向肯定是沒(méi)有問(wèn)題的。

推薦算法概述-基于協(xié)同過(guò)濾的推薦

協(xié)同過(guò)濾,或許了解過(guò)推薦系統(tǒng)的的朋友,多多少少都能聽(tīng)過(guò)一些,并且協(xié)同推薦可是作為推薦領(lǐng)域典型案例的存在。

協(xié)同過(guò)濾同樣不會(huì)去研究物品的本身屬性,甚至也沒(méi)有空去構(gòu)建用戶的畫(huà)像標(biāo)簽,正如他的名字描述的一樣,他嚴(yán)重依靠于用戶的行為以及其周邊用戶的協(xié)同行為。舉個(gè)例子,為一個(gè)用戶推薦信息,那么我只需要參考其周邊用戶在看什么信息,就給他推薦什么信息就好了。

重點(diǎn)在于,如何限定周邊這個(gè)范圍,比如根據(jù)兩個(gè)用戶的行為,去構(gòu)建相關(guān)關(guān)系,從而判斷用戶之間的相似程度,把相似用戶的行為推薦給當(dāng)前用戶,這就是協(xié)同中典型的基于用戶推薦。

而如果以物品為維度,以用戶的購(gòu)買或者觀看記錄為向量,則可以構(gòu)建物品的相似度量,針對(duì)于每一個(gè)待推薦選項(xiàng),用戶的歷史軌跡就是其向量構(gòu)成,就可以判斷該用戶歷史的軌跡信息與當(dāng)前的待選物品的向量相關(guān)度了,從而判斷是否要推薦,這就是基于物品的協(xié)同邏輯。

與基于用戶畫(huà)像的推薦對(duì)比,這種推薦有一定幾率可以發(fā)現(xiàn)新物品,即并不嚴(yán)格依賴用戶的興趣。舉個(gè)例子,假設(shè)幾個(gè)信息的層級(jí)是ABC,并且ABC是層級(jí)遞進(jìn)關(guān)系,并不是同一個(gè)東西,對(duì)于一個(gè)用戶來(lái)說(shuō),他掌握的是A,則意味著他的興趣偏好大多偏向于A,根據(jù)興趣標(biāo)簽,其實(shí)是很難推薦這種遞進(jìn)相關(guān)的信息。

但是,如果其他用戶的學(xué)習(xí)軌跡都是A->B->C這種軌跡,這意味著ABC三者之間本身就有前后潛在邏輯關(guān)系存在的,基于協(xié)同,即可為該用戶在掌握A的基礎(chǔ)上,推薦BC的內(nèi)容,這也是基于興趣所做不到的地方。

當(dāng)前,基于協(xié)同行為的推薦,除了基于物品還有基于用戶,還有其他諸如基于模型的協(xié)同,典型如最近鄰模型、基于矩陣分解、以及基于圖關(guān)系模型的構(gòu)建的推薦機(jī)制。

推薦算法概述-其他

其實(shí)在我們實(shí)際的操作過(guò)程中,并不會(huì)嚴(yán)格的依賴于這種條條框框、只要合理即可行,比如我們完全可以把推薦問(wèn)題轉(zhuǎn)化為分類問(wèn)題,針對(duì)于每個(gè)待選項(xiàng),他都是YES OR NO的問(wèn)題,即一個(gè)二值分類。

又比如在上一篇我們學(xué)習(xí)的一個(gè)場(chǎng)景,即閱文網(wǎng)站的小說(shuō)推薦,還記得他的推薦標(biāo)題嗎?“喜歡這本書(shū)的人還喜歡”,這就是典型的“啤酒與尿布”的解法,即貨架思維,關(guān)聯(lián)銷售,也是可以作為一種推薦機(jī)制而存在的。

在比如微信朋友圈,微信一定是會(huì)研究用戶的朋友圈關(guān)系的,比如你對(duì)哪類朋友點(diǎn)贊、互動(dòng)行為最多,它是不是會(huì)考慮推薦你欣賞的朋友偏好內(nèi)容給你?畢竟微信是一個(gè)典型的熟人社交模型。

所以,推薦算法看似重要,但其實(shí)想想又沒(méi)有這么重要,很多時(shí)候并不是一成不變的,都要我們根據(jù)場(chǎng)景去考慮,最最最重要的是,需要我們用實(shí)際效果來(lái)選擇機(jī)制,也或許是多種機(jī)制共同生效的結(jié)果。

0.3.2 相似度量

在我們上面的推薦算法機(jī)制中,有個(gè)不得不提的操作處理就是各種相似相關(guān)度的計(jì)算,我們來(lái)簡(jiǎn)單分享一下幾種相似或者相關(guān)度量的方式。

歐幾里得距離(Euclidean Distance)

最常見(jiàn)的距離度量方式,衡量多維空間中兩點(diǎn)之間的絕對(duì)距離,要求維度的統(tǒng)一。

明可夫斯基距離(Minkowski Distance)

明氏距離是歐氏距離的擴(kuò)展,是對(duì)多個(gè)距離度量公式的概括性的表述(可以看到,當(dāng)p=2時(shí),其實(shí)就是歐式距離)。

曼哈頓距離(Manhattan Distance)

曼哈頓距離來(lái)源于城市區(qū)塊距離,是將多個(gè)維度上的距離進(jìn)行求和后的結(jié)果,即當(dāng)上面的明氏距離中p=1時(shí)得到的距離度量。

//還有其他的一些距離度量,但是都不太常用,最常用的依然是歐式距離度量。

向量空間余弦相似度(Cosine Similarity)

余弦相似度用向量空間中兩個(gè)向量夾角的余弦值作為衡量?jī)蓚€(gè)個(gè)體間差異的大小。相比距離度量,余弦相似度更加注重兩個(gè)向量在方向上的差異,而非距離或長(zhǎng)度上。

皮爾森相關(guān)系數(shù)(Pearson Correlation Coefficient)

即相關(guān)分析中的相關(guān)系數(shù)r,分別對(duì)X和Y基于自身總體標(biāo)準(zhǔn)化后計(jì)算空間向量的余弦?jiàn)A角。基于內(nèi)容的推薦,還有一點(diǎn)需要注意的就是,對(duì)于物品自身屬性,如果屬性值過(guò)少,我們需要適當(dāng)進(jìn)行擴(kuò)大維度,如果維度過(guò)多,則需要進(jìn)行降維。

關(guān)于降維和升維,都是一個(gè)很大的研究方向,大體上可以說(shuō)一下幾種常見(jiàn)的方式。例如降維,我們可以進(jìn)行維度聚類、主題抽取,進(jìn)一步把相關(guān)維度進(jìn)行合并,進(jìn)一步減少維度;而對(duì)于升維,我們可以把維度進(jìn)行矩陣化,例如假設(shè)物品X有A和B兩個(gè)維度屬性,那么我們通過(guò)生成AB矩陣的方式,把維度擴(kuò)充到AB個(gè)維度。

0.3.3 冷啟動(dòng)問(wèn)題的解決

所謂冷啟動(dòng),即在推薦系統(tǒng)初期時(shí),沒(méi)有任何用戶與物品的交集信息,即無(wú)用戶的行為軌跡,無(wú)法通過(guò)類似協(xié)同或者用戶偏好等方式進(jìn)行推薦,這種時(shí)候,我們就稱推薦系統(tǒng)處于冷啟動(dòng)狀態(tài)。

這種情況,我們需要盡快的累積起第一批用戶行為軌跡。我們可以通過(guò)基于內(nèi)容的推薦,或者做一些其他類似的操作,快速有效的進(jìn)行物品推薦。一段時(shí)間后,累積到一定的用戶行為時(shí),整個(gè)系統(tǒng)就能夠正常使用協(xié)同過(guò)濾等方式進(jìn)行推薦了。

但是,針對(duì)于新加入的用戶,或者新加入的物品,同樣也是出于冷啟動(dòng)狀態(tài)的,這個(gè)時(shí)候,我們通過(guò)需要對(duì)這種物品或者用戶做特殊的處理。

除了基于內(nèi)容屬性的推薦,我們還有其他的一些策略用于彌補(bǔ)這種行為數(shù)據(jù)不足的情況,比如典型的熱度模型,推薦熱點(diǎn)信息這種行為雖然low,但是從整體的反饋來(lái)看,還是有一定效果的,此外,還可以根據(jù)一些統(tǒng)計(jì)學(xué)上的結(jié)論,進(jìn)行基于統(tǒng)計(jì)分析結(jié)論的推薦。

除此之外,我們也可以通過(guò)其他渠道收集用戶的數(shù)據(jù),比如用戶注冊(cè)的時(shí)候所填寫(xiě)的個(gè)人資料,這些都是可以作為推薦的原始依賴數(shù)據(jù)。

0.3.4 馬太效應(yīng)

馬太效應(yīng)或者說(shuō)長(zhǎng)尾效應(yīng),即熱者愈熱,實(shí)際舉例來(lái)說(shuō)就是,在實(shí)際的購(gòu)買場(chǎng)景中,由于你推薦的次數(shù)越多,部分優(yōu)質(zhì)的商品購(gòu)買或者點(diǎn)擊的次數(shù)就越多,形成的用戶購(gòu)買軌跡就越多,所以得到的推薦機(jī)會(huì)就越多,進(jìn)而產(chǎn)生的推薦也越多,變得越熱。

隨著不斷迭代,子子孫孫無(wú)窮盡也,這樣得到推薦的商品就會(huì)集中在少部分商品中,而大部分長(zhǎng)尾商品是沉寂的,一個(gè)推薦系統(tǒng)如果長(zhǎng)時(shí)間處于長(zhǎng)尾效應(yīng)中,造成推薦疲勞,其推薦效果就會(huì)減弱。

所以,一個(gè)好的推薦系統(tǒng),要考慮到適當(dāng)?shù)耐诰蜷L(zhǎng)尾商品,通過(guò)真的個(gè)性化,把適當(dāng)?shù)拈L(zhǎng)尾商品送到真正需要他們的人手里,在實(shí)際的操作過(guò)程中,我們可以適當(dāng)?shù)倪M(jìn)行熱度降權(quán),從而讓一些中下層的商品得到更多的曝光機(jī)會(huì),當(dāng)然前提是保證點(diǎn)擊率的情況下。

另外一個(gè)場(chǎng)景會(huì)形成馬太效應(yīng)的是熱度模型,即我們的熱度榜單,長(zhǎng)時(shí)間的高居榜首,一定會(huì)獲得更多的點(diǎn)擊,而點(diǎn)擊越多其熱度越高,但我們的信息是需要保持新鮮度的,不然點(diǎn)擊率遲早會(huì)下架的。

所以,我們使用一些機(jī)制讓處于頭部的商品或者信息降權(quán),時(shí)間衰減是一個(gè)比較通用的做法,即隨著時(shí)間的遷移,其整體熱度會(huì)不斷的下降,至于說(shuō)下降的方式,速率就看模型的設(shè)計(jì)了。

0.3.5 AB測(cè)試

關(guān)于推薦的效果,之前我們說(shuō)過(guò)其核心的考核標(biāo)準(zhǔn)就是點(diǎn)擊率,點(diǎn)擊的越多說(shuō)明推薦的越準(zhǔn)確,用戶的停留時(shí)長(zhǎng)也會(huì)越長(zhǎng),只要把用戶留在平臺(tái)中,機(jī)會(huì)總是會(huì)有的。其實(shí)就是一層漏斗嘛?這一層的基數(shù)越大,下一層轉(zhuǎn)換的量就會(huì)越高,這也是推薦系統(tǒng)的核心存在意義。

并且之前也說(shuō)到過(guò),一個(gè)不好的推薦系統(tǒng)有時(shí)間反而會(huì)形成反向作用,所以,一個(gè)推薦系統(tǒng)的迭代更新至關(guān)重要。離線的效果評(píng)估一定是要做的,最起碼在離線實(shí)驗(yàn)的階段需要保證當(dāng)前的效果優(yōu)于線上效果,才能進(jìn)行迭代。

但是,實(shí)際情況是復(fù)雜的,對(duì)于推薦的模型來(lái)說(shuō),離線的實(shí)驗(yàn)其實(shí)并沒(méi)有想象中靠譜,那么,就丟到線上去真多真槍的實(shí)驗(yàn)一把,就知道效果了。但是,實(shí)際的生產(chǎn)環(huán)境中,任何一點(diǎn)轉(zhuǎn)化波動(dòng)的影響都是極其嚴(yán)重的,誰(shuí)也不敢拿實(shí)際生產(chǎn)開(kāi)玩笑。

于是,就有了AB測(cè)試機(jī)制的產(chǎn)生,所謂AB測(cè)試機(jī)制,即將流量分為AB兩類,A流量走原始的舊模型,B流量走新模型,同步測(cè)試同步對(duì)比,效果一目了然。

當(dāng)然,在實(shí)際的AB測(cè)試流程中,首先流量是可以自由分配的,一般情況下新模型在最終確認(rèn)之前流量一定是少量的,隨著模型逐漸被驗(yàn)證,流量比重會(huì)逐漸加大,最終確認(rèn)后流量全部導(dǎo)向新模型,完成新模型的正式上線。

并且,通常,在實(shí)際的環(huán)境中,或許我們會(huì)同時(shí)有十多個(gè)甚至是幾十個(gè)新模型在同時(shí)實(shí)驗(yàn),每個(gè)模型調(diào)整的因子都不一樣,最終選擇最適合的因素進(jìn)行調(diào)整,達(dá)到效果最優(yōu),這也就是AB測(cè)試機(jī)制的魅力所在。

所以,打造一個(gè)好的AB測(cè)試系統(tǒng),首先流量是需要可控的,其次模型的迭代上線是需要高度靈活的,最后,肯定是需要有完整的數(shù)據(jù)回收、數(shù)據(jù)分析對(duì)比機(jī)制存在的。

04 基于內(nèi)容屬性的推薦策略算法

從這個(gè)章節(jié)開(kāi)始,我們將從理論進(jìn)一步過(guò)渡到具體的推薦策略算法,我們先從最簡(jiǎn)單的基于內(nèi)容屬性相關(guān)的策略算法著手。

0.4.1 最簡(jiǎn)單的推薦機(jī)制

如標(biāo)題,既然是“最最最簡(jiǎn)單”的推薦系統(tǒng),其實(shí)也不能說(shuō)是推薦系統(tǒng),之前也說(shuō)了,系統(tǒng)是一個(gè)復(fù)合的完整系統(tǒng),所以這里說(shuō)推薦機(jī)制可能會(huì)更恰當(dāng)些。結(jié)合之前大致陳述的一些推薦機(jī)制,最最最簡(jiǎn)單的推薦機(jī)制,無(wú)疑是基于主體屬性相似或者相關(guān)推薦了,連個(gè)性化都說(shuō)不上,鐵定最最最簡(jiǎn)單了。

說(shuō)到這,說(shuō)不定有些人不愿意干了,既然如此簡(jiǎn)陋的推薦機(jī)制,不看也罷。BUT,真的不要小看基于內(nèi)容相似的推薦,存在即合理。我們?cè)谶M(jìn)行信息獲取時(shí),其實(shí)本身就是具有一定識(shí)別能力的,這意味著我們最終選擇查看的信息都是經(jīng)過(guò)我們大腦大致思考的結(jié)論,這意味著這些信息是有參考價(jià)值的。

并且,在很多時(shí)候,我們是需要同類信息進(jìn)行我們獲取到的信息進(jìn)行補(bǔ)全的,完善我們對(duì)目標(biāo)信息的獲取程度。基于這個(gè)考量,基于內(nèi)容屬性的推薦其實(shí)是說(shuō)的過(guò)去的。

別不服,我們來(lái)上例子,還是以前面文章那個(gè)騰訊視頻的推薦場(chǎng)景圖為例。

image.gif

圖是在使用騰訊視頻觀看視頻時(shí),我親手截推薦欄位的內(nèi)容,補(bǔ)充一下背景(很重要,請(qǐng)注意):

1 我當(dāng)時(shí)觀看的是應(yīng)該是《藍(lán)色星球第二季》紀(jì)錄片

2 我經(jīng)常在騰訊視頻上看的一般是大片,并且一般是國(guó)外的

3 由于是VIP賬號(hào),梓塵兄也經(jīng)常用這個(gè)賬號(hào)看動(dòng)畫(huà)片,諸如《小豬佩奇》之類的

4 在騰訊視頻上,紀(jì)錄片中,我只看過(guò)《地球脈動(dòng)》和《藍(lán)色星球》,并且,我真不是紀(jì)錄片的愛(ài)好者,只是喜歡這兩部而已

基于上面我提供的個(gè)人行為數(shù)據(jù),再結(jié)合看這批推薦列表,不難發(fā)現(xiàn),上面有很多的紀(jì)錄片,你覺(jué)得跟我們當(dāng)時(shí)正在瀏覽的內(nèi)容有沒(méi)有關(guān)系?或者你認(rèn)為我行為記錄中很多紀(jì)錄片的記錄?又或者是我是紀(jì)錄片的狂熱者,導(dǎo)致了騰訊視頻給我猛推紀(jì)錄片。

所以,連騰訊視頻都會(huì)考慮基于當(dāng)前瀏覽內(nèi)容的屬性進(jìn)行推薦(并且是大范圍),你還覺(jué)得這種做法十分之LOW嗎?當(dāng)然你也可以認(rèn)為騰訊視頻推的不準(zhǔn),瞎J吧推,也是可以的,我也認(rèn)同,不是非常準(zhǔn)(哈哈,《地球脈動(dòng)》所有我都看過(guò)了,還給我瞎推,上面給推的沒(méi)幾個(gè)有欲望去點(diǎn)的,給騰訊視頻推薦的開(kāi)發(fā)兄弟們打臉了,不好意思)。

我只想表達(dá)的是,這種簡(jiǎn)單的推薦機(jī)制,在整個(gè)推薦系統(tǒng)中真的是不可缺少的部分,很多時(shí)候簡(jiǎn)單并不代表無(wú)效,類似上面這種情況,我可以舉出太多有名有姓的實(shí)際案例來(lái),說(shuō)多了沒(méi)意義,所以,咱繼續(xù)。

0.4.2 過(guò)程并沒(méi)有這么簡(jiǎn)單

從直接的推薦機(jī)制來(lái)看,整個(gè)實(shí)現(xiàn)流程看著真的很簡(jiǎn)單,但是在實(shí)際的操作過(guò)程中,還是有一些東西值得探討以及注意的。

第一、首先是,相似計(jì)算的過(guò)程

之前文章有大致提到過(guò),相似或者相關(guān)計(jì)算還是有很多可以選擇的,他們每一種都有各自的特點(diǎn)以及適應(yīng)性。以相似計(jì)算中使用最多的歐式距離與余弦相似為例,專業(yè)點(diǎn)的說(shuō)法就是余弦?jiàn)A角可以有效規(guī)避個(gè)體相同認(rèn)知中不同程度的差異表現(xiàn),更注重維度之間的差異,而不注重?cái)?shù)值上的差異,而歐式距離則是對(duì)個(gè)體異常數(shù)值會(huì)比較敏感。

這意味著,在我們需要區(qū)分異常樣本時(shí),使用距離計(jì)算會(huì)更恰當(dāng),聚個(gè)栗子,比如電商領(lǐng)域中高價(jià)值與低價(jià)值用戶的區(qū)分,其實(shí)我們核心是想把他們的差異性拉大的,得以體現(xiàn)出對(duì)比,這個(gè)時(shí)候使用余弦就是不合理的。

在回歸到距離上說(shuō),市面上除了歐式距離,還有好幾種距離度量,諸如馬氏、曼哈頓距離等等,其實(shí)其度量側(cè)重都是不一樣的,我們需要結(jié)合實(shí)際的場(chǎng)景去使用。還有更偏向于相關(guān)度量的皮爾森相關(guān)系數(shù)等。

第二、需要解決相似計(jì)算中,計(jì)算矩陣過(guò)大的問(wèn)題

按照標(biāo)準(zhǔn)流程,假設(shè)有1萬(wàn)個(gè)物品,則對(duì)于每個(gè)物品來(lái)說(shuō),需要與其他物品計(jì)算與其的相似度或者相關(guān)度,然后再排個(gè)序,取TopN形成自身的待推薦列表。那么,簡(jiǎn)單的數(shù)學(xué)題來(lái)了10000*10000=10000萬(wàn)次計(jì)算,這顯然是不合理的。

所以,優(yōu)化這個(gè)過(guò)程是必然的,關(guān)鍵是如何優(yōu)化。核心思想其實(shí)就是初篩嘛!把那些完全沒(méi)啥多大鳥(niǎo)關(guān)系的直接干掉,省掉計(jì)算相似的過(guò)程,節(jié)省資源。如何篩選?一個(gè)比較常見(jiàn)的做法是,尋找核心關(guān)鍵影響因素,保證關(guān)鍵因素的相關(guān)性。

比如,在實(shí)際的生產(chǎn)操作過(guò)程中,很多時(shí)候會(huì)通過(guò)關(guān)鍵屬性是否基本匹配作為判斷依據(jù),或者直接通過(guò)搜索構(gòu)建進(jìn)行檢索初篩,把大致相關(guān)的先過(guò)濾一遍,通過(guò)這種簡(jiǎn)單而粗暴的方式,其實(shí)已經(jīng)能把大部分相關(guān)度很低的候選集給過(guò)濾掉,對(duì)于整體計(jì)算量級(jí)來(lái)說(shuō),計(jì)算復(fù)雜度直接下降。

第三、多個(gè)因子如何權(quán)衡影響權(quán)重

基于屬性計(jì)算相似,從整體上來(lái)看,其實(shí)一般主體都不止一個(gè)屬性,那么計(jì)算相關(guān)的時(shí)候到底看那個(gè)屬性呢?或者說(shuō)哪些屬性應(yīng)該占有更高的權(quán)重,哪些因素是次要因素。

還是以上面的騰訊視頻的推薦為例,從結(jié)果上來(lái)反推相似推薦的部分(當(dāng)然,實(shí)際情況不詳哈,只是推斷而已),顯然當(dāng)前視頻的類別占了很大的權(quán)重(記錄片),除此之外包括導(dǎo)演啊,一些其他特征屬性應(yīng)該也會(huì)參考在內(nèi)的。

回到常規(guī)問(wèn)題,如何確定影響權(quán)重是個(gè)操作難題。最簡(jiǎn)單并且實(shí)際上還挺有效的一種方式就是專家評(píng)判法,即通過(guò)權(quán)威經(jīng)驗(yàn)來(lái)劃定影響因子的權(quán)重,還有就是通過(guò)標(biāo)注的樣本進(jìn)行反向擬合每種因素的占比權(quán)重。除此之外還有一些其他學(xué)術(shù)上的方法,包括什么主成分分析法,層次分析法,還有什么熵權(quán)法,其實(shí)都是找因子影響能力的主次關(guān)系。

最終確定好了影響因素,在實(shí)際上線回收到數(shù)據(jù)之后,依然是需要逐步的進(jìn)行權(quán)重影響調(diào)整的,我們可以通過(guò)結(jié)果的樣本數(shù)據(jù),進(jìn)行LR的回歸擬合,尋找最合適的權(quán)重配比。

0.4.3 最簡(jiǎn)單的推薦策略算法實(shí)踐

說(shuō)了這么多理論,不能光說(shuō)不練,標(biāo)題上寫(xiě)著“附Spark案例”,很多人都是沖著這來(lái)的呢,前面BB了這么多屁話,還不見(jiàn)代碼。來(lái),我們這就上正文。

不過(guò)不用期待過(guò)多,畢竟這只是一個(gè)簡(jiǎn)單的相似計(jì)算的過(guò)程而已,所以權(quán)當(dāng)屬性實(shí)驗(yàn)數(shù)據(jù)以及Spark開(kāi)發(fā)了,高手可以略過(guò)了。

一、實(shí)驗(yàn)數(shù)據(jù)簡(jiǎn)介

image.gif

其實(shí)看到這三部分?jǐn)?shù)據(jù)的簡(jiǎn)介,一些老手估計(jì)已經(jīng)知道是什么數(shù)據(jù)了,是的,就是那份有名的電影數(shù)據(jù)集(MovieLens開(kāi)放數(shù)據(jù)),并且取的是完全版的那份,簡(jiǎn)直成了推薦系統(tǒng)的標(biāo)配實(shí)驗(yàn)數(shù)據(jù)了。

三個(gè)文件,其中電影數(shù)據(jù)集共1萬(wàn)多個(gè)電影基礎(chǔ)數(shù)據(jù),評(píng)分?jǐn)?shù)據(jù)集最大共100萬(wàn)條評(píng)分?jǐn)?shù)據(jù),以及10萬(wàn)條的用戶對(duì)電影的打標(biāo)簽數(shù)據(jù),總大小約為幾百兆,不大,但是用來(lái)做實(shí)驗(yàn)玩玩那是相當(dāng)足夠了。

二、推薦機(jī)制邏輯

我們的核心計(jì)算邏輯還是內(nèi)容屬性上的相似嘛,所以核心是看看圍繞電影,有哪些屬性是可以抽取出來(lái)的,并且參與計(jì)算的。

第一,電影的類別,基于上面騰訊視頻的考慮,其實(shí)這個(gè)顯然很重要,而電影的類別信息存儲(chǔ)于電影數(shù)據(jù)集中,并且是一對(duì)多的關(guān)系,即一個(gè)電影可以對(duì)應(yīng)多個(gè)類目,我們需要進(jìn)行切割,由于計(jì)算這個(gè)維度相似的時(shí)候,是多對(duì)多的關(guān)系,天然的計(jì)算相似或者相關(guān)的特征。

第二、電影的播放年份,電影的年份其實(shí)有種潛在的關(guān)聯(lián)關(guān)系,舉個(gè)例子可以說(shuō)明,比如說(shuō)零幾年的電影與現(xiàn)狀的電影風(fēng)格是不同的,當(dāng)時(shí)間跨度有一定差距時(shí),這個(gè)還是蠻明顯的。關(guān)于電影的年份數(shù)據(jù),從數(shù)據(jù)樣本可以知道,它隱藏在電影的名字中,寫(xiě)個(gè)正則過(guò)濾出來(lái)即可。至于說(shuō)如何計(jì)算這個(gè)維度的相關(guān),我們可以用兩者差距來(lái)算相關(guān),比如年份絕對(duì)值越遠(yuǎn),這個(gè)值越小,越近甚至是重疊就越大。

第三,電影的標(biāo)簽,電影本身是沒(méi)有標(biāo)簽屬性的,但它有用戶對(duì)他打的標(biāo)簽信息,所以我們需要進(jìn)一步處理,把它變成電影的屬性,需要清洗、規(guī)整以及處理。標(biāo)簽本身也是多對(duì)多的關(guān)系,同樣可以計(jì)算相似度,比如歐式或者余弦。

第四、電影的名稱,名稱上進(jìn)行尋找關(guān)聯(lián)性,聽(tīng)上去很扯,但其實(shí)有一定的邏輯在里頭,比如我在視頻網(wǎng)站搜索“三國(guó)”,顯然我期望從名稱上尋找三國(guó)相關(guān)題材的視頻,他們就是在名稱上建立起關(guān)聯(lián)關(guān)系的,所以,名稱從某種程度上來(lái)說(shuō),可以體現(xiàn)相關(guān)性。在計(jì)算相似或者相關(guān)方式上,我們可以進(jìn)行分詞,去除停詞,然后再以詞維度進(jìn)行余弦計(jì)算。

第五、候選集電影的評(píng)分,對(duì)于做推薦來(lái)說(shuō),首先需要保證的推薦的候選集一定是優(yōu)質(zhì)的,從這個(gè)維度上說(shuō),拋開(kāi)其他因素,那么就是整體評(píng)分高的電影是相對(duì)優(yōu)質(zhì)的電影。在處理的過(guò)程中,由于一個(gè)電影對(duì)應(yīng)多個(gè)評(píng)分,所以,我們需要進(jìn)行進(jìn)行歸一計(jì)算,最簡(jiǎn)單的做法就是計(jì)算整體評(píng)分的平均值,作為電影的評(píng)分?jǐn)?shù)據(jù),評(píng)分過(guò)低的數(shù)據(jù)直接從候選集中干掉,又大大的降低了計(jì)算次數(shù)。

三、代碼邏輯

Spark2.0之后,不用再構(gòu)建sparkcontext了,以創(chuàng)建一個(gè)復(fù)合多功能的SparkSession替代,可以正常的從HDFS讀取文件,也可以從Hive中獲取DataFrame等等。

val sparkSession = SparkSession

.builder()

.appName("base-content-Recommand") //spark任務(wù)名稱

.enableHiveSupport()

.getOrCreate()

那三個(gè)表可以先load到Hive中,然后spark直接從Hive中讀取,形成DataFrame。

//從hive中,獲取rating評(píng)分?jǐn)?shù)據(jù)集,最終形成如下格式數(shù)據(jù)(movie,avg_rate)

val movieAvgRate = sparkSession.sql("select movieid,round(avg(rate),1) as avg_rate? from tx.tx_ratings group by movieid").rdd.map{

f=>

(f.get(0),f.get(1))

}

//獲取電影的基本屬性數(shù)據(jù),包括電影id,名稱,以及genre類別

val moviesData = sparkSession.sql("select movieid,title,genre from tx.tx_movies").rdd

//獲取電影tags數(shù)據(jù),這里取到所有的電影tag

val tagsData = sparkSession.sql("select movieid,tag from tx.tx_tags").rdd

對(duì)tag進(jìn)行處理,很多tag其實(shí)說(shuō)的是同一個(gè)東西,我們需要進(jìn)行一定程度上的合并,這樣才能讓tag更加的合理(有朋友有意見(jiàn)了,就一個(gè)實(shí)驗(yàn)案例而已,搞這么復(fù)雜),舉個(gè)簡(jiǎn)單例子,blood、bloods、bloody其實(shí)都是想說(shuō)這個(gè)電影很血腥暴力,但是不同的人使用的詞不同的(這點(diǎn)大伙兒可以自由查看實(shí)驗(yàn)數(shù)據(jù)),所以我們需要進(jìn)行一定程度上的合并。

val tagsStandardizeTmp = tagsStandardize.collect()

val tagsSimi = tagsStandardize.map{

f=>

var retTag = f._2

if (f.2.toString.split(" ").size == 1) {

var simiTmp = ""

val tagsTmpStand = tagsStandardizeTmp

.filter(

._2.toString.split(" ").size != 1 )

.filter(f._2.toString.size < _.2.toString.size)

.sortBy(

._2.toString.size)

var x = 0

val loop = new Breaks

tagsTmpStand.map{

tagTmp=>

val flag = getEditSize(f._2.toString,tagTmp._2.toString)

if (flag == 1){

retTag = tagTmp._2

loop.break()

}

}

((f._1,retTag),1)

} else {

((f._1,f._2),1)

}

}

其中g(shù)etEditSize是求取,兩個(gè)詞的編輯距離的,編輯距離在一定時(shí)候,進(jìn)行合并,具體邏輯見(jiàn)代碼了,不復(fù)雜。

def getEditSize(str1:String,str2:String): Int ={

if (str2.size > str1.size){

0

} else {

//計(jì)數(shù)器

var count = 0

val loop = new Breaks

//以較短的str2為中心,進(jìn)行遍歷,并逐個(gè)比較字符

val lengthStr2 = str2.getBytes().length

var i = 0

for ( i <- 1 to lengthStr2 ){

if (str2.getBytes()(i) == str1.getBytes()(i)) {

//逐個(gè)匹配字節(jié),相等則計(jì)數(shù)器+1

count += 1

} else {

//一旦出現(xiàn)前綴不一致則中斷循環(huán),開(kāi)始計(jì)算重疊度

loop.break()

}

}

//計(jì)算重疊度,當(dāng)前綴重疊度大于等于2/7時(shí),進(jìn)行字符串合并,從長(zhǎng)的往短的合并

if (count.asInstanceOf[Double]/str1.getBytes().size.asInstanceOf[Double] >= (1-0.286)){

1

}else{

0

}

}

}

繼續(xù)對(duì)tag進(jìn)行處理,統(tǒng)計(jì)tag頻度,取TopN個(gè)作為電影對(duì)應(yīng)的tag屬性。

val movieTag = tagsSimi.reduceByKey(+).groupBy(k=>k._1._1).map{

f=>

(f._1,f._2.map{

ff=>

(ff._1._2,ff.2)

}.toList.sortBy(

._2).reverse.take(10).toMap)

}

接下來(lái)處理年齡、年份和名稱,這個(gè)會(huì)簡(jiǎn)單點(diǎn),進(jìn)行分詞處理的話,怎么簡(jiǎn)單怎么來(lái)了,直接使用第三方的HanLP進(jìn)行關(guān)鍵詞抽取作為分詞結(jié)果,直接屏蔽了停用詞。

val moviesGenresTitleYear = moviesData.map{

f=>

val movieid = f.get(0)

val title = f.get(1)

val genres = f.get(2).toString.split("|").toList.take(10)

val titleWorlds = HanLP.extractKeyword(title.toString, 10).toList

val year = movieYearRegex.movieYearReg(title.toString)

(movieid,(genres,titleWorlds,year))

}

取年份的正則函數(shù)如下,是個(gè)Java寫(xiě)的精通工具類(Scala和Java混寫(xiě),簡(jiǎn)直無(wú)比美妙)。

package utils;

import java.util.regex.Matcher;

import java.util.regex.Pattern; /**

Desc: 抽取年份公式

/ public class movieYearRegex {

private? static String moduleType = ".

\(([1-9][0-9][0-9][0-9])\).*";

public static void main(String[] args){

System.out.println(movieYearReg("GoldenEye (1995)"));

}

public static int movieYearReg(String str){

int retYear = 1994;

Pattern patternType = Pattern.compile(moduleType);

Matcher matcherType = patternType.matcher(str);

while (matcherType.find()) {

retYear = Integer.parseInt(matcherType.group(1));

}

return retYear;

}

}

通過(guò)join進(jìn)行數(shù)據(jù)合并,生成一個(gè)以電影id為核心的屬性集合。

val movieContent = movieTag.join(movieAvgRate).join(moviesGenresTitleYear).map{

f=>

//(movie,tagList,titleList,year,genreList,rate)

(f._1,f._2._1._1,f._2._2._2,f._2._2._3,f._2._2._1,f._2._1._2)

}

相似計(jì)算開(kāi)始之前,還記得我們之前說(shuō)的嗎,可以進(jìn)行候選集閹割,我們先根據(jù)一些規(guī)則裁剪一下候選集。

val movieConetentTmp = movieContent.filter(f=>f._6.asInstanceOf[java.math.BigDecimal].doubleValue() < 3.5).collect()

然后真正的開(kāi)始計(jì)算相似,使用余弦相似度計(jì)算,取排序之后的Top20作為推薦列表。

val movieContentBase = movieContent.map{

f=>

val currentMoiveId = f._1val currentTagList = f._2//[(tag,score)]val currentTitleWorldList = f._3val currentYear = f._4val currentGenreList = f._5val currentRate = f._6.asInstanceOf[java.math.BigDecimal].doubleValue()val recommandMovies = movieConetentTmp.map{? ff=>? ? val tagSimi = getCosTags(currentTagList,ff._2)? ? val titleSimi = getCosList(currentTitleWorldList,ff._3)? ? val genreSimi = getCosList(currentGenreList,ff._5)? ? val yearSimi = getYearSimi(currentYear,ff._4)? ? val rateSimi = getRateSimi(ff._6.asInstanceOf[java.math.BigDecimal].doubleValue())? ? val score =0.4*genreSimi +0.25*tagSimi +0.1*yearSimi +0.05*titleSimi +0.2*rateSimi? ? (ff._1,score)}.toList.sortBy(k=>k._2).reverse.take(20)(currentMoiveId,recommandMovies)

}.flatMap(f=>f._2.map(k=>(f._1,k._1,k._2))).map(f=>Row(f._1,f._2,f._3))

最后,將結(jié)果存入Hive中,Hive中提前建好結(jié)果表。

//我們先進(jìn)行DataFrame格式化申明

val schemaString2 = "movieid movieid_recommand score"

val schemaContentBase = StructType(schemaString2.split(" ")

.map(fieldName=>StructField(fieldName,if (fieldName.equals("score")) DoubleType else? StringType,true)))

val movieContentBaseDataFrame = sparkSession.createDataFrame(movieContentBase,schemaContentBase)

//將結(jié)果存入hive,需要先進(jìn)行臨時(shí)表創(chuàng)建

val userTagTmpTableName = "mite_content_base_tmp"

val userTagTableName = "mite8.mite_content_base_reco"

movieContentBaseDataFrame.registerTempTable(userTagTmpTableName)

sparkSession.sql("insert into table " + userTagTableName + " select * from " + userTagTmpTableName)

到這里,基本大的代碼邏輯就完了,可能還有一些邊邊角角的代碼遺漏了,但不妨礙主干了。

05 融合用戶興趣的推薦才個(gè)性

接上個(gè)章節(jié),我們給了一個(gè)最最最簡(jiǎn)單的推薦系統(tǒng)機(jī)制,即基于內(nèi)容屬性的相似或者相關(guān)推薦,我們知道這種推薦機(jī)制基本只基于內(nèi)容本身的屬性進(jìn)行推薦,與用戶沒(méi)有半毛錢關(guān)系,所以,當(dāng)然也就說(shuō)不上個(gè)性化了。

0.5.1 個(gè)性化與用戶畫(huà)像

在說(shuō)具體的情況之前,我們先來(lái)思考一個(gè)問(wèn)題,什么是個(gè)性化?個(gè)性化一定是與人相關(guān)的,只有人才有個(gè)性,每個(gè)人可能都有自己的個(gè)性,推送的信息如果能滿足用戶的個(gè)性,才是一個(gè)好的推薦系統(tǒng),才具有足夠的智能。

而今天,我們要討論的就是,如何讓推薦從非智能的過(guò)程演變?yōu)橹獣杂脩魝€(gè)性,基于用戶偏好進(jìn)行推薦,從而變得更“聰明”點(diǎn),也就是智能化。

要實(shí)現(xiàn)推薦個(gè)性化,那么先需要對(duì)用戶進(jìn)行分析,分析用戶的偏好,然后根據(jù)偏好來(lái)做推薦,就順其自然了。而要分析用戶的偏好,那么自然就少不了對(duì)用戶行為的分析。

所以,核心還是用戶畫(huà)像的分析,然后我們?cè)倩谟脩舢?huà)像屬性進(jìn)行推薦,由于用戶畫(huà)像體現(xiàn)的是每個(gè)用戶的偏好數(shù)據(jù),所以,不管怎么樣,這種推薦機(jī)制或多或少都是能體現(xiàn)一些個(gè)性化的東西的。

need-to-insert-img

image

沿著這個(gè)路徑,我們依然是結(jié)合實(shí)際數(shù)據(jù)以及代碼案例來(lái)分解這個(gè)個(gè)性化推薦的過(guò)程。

0.5.2 基于用戶畫(huà)像的個(gè)性化推薦策略

整個(gè)案例代碼的邏輯是,我們先根據(jù)行為數(shù)據(jù),進(jìn)行用戶的畫(huà)像描述抽取,然后再結(jié)合用戶的畫(huà)像數(shù)據(jù)為用戶進(jìn)行信息推薦,注意,這里與之前的實(shí)例不同的是,我們是基于用戶進(jìn)行推薦的,而上個(gè)實(shí)例是在瀏覽某個(gè)內(nèi)容的時(shí)候,進(jìn)行相關(guān)內(nèi)容推薦,這里以及進(jìn)化到了根據(jù)人進(jìn)行推薦了。

實(shí)踐數(shù)據(jù)源

關(guān)于數(shù)據(jù)源,依然使用的是上個(gè)案例中的實(shí)驗(yàn)數(shù)據(jù),不清楚的見(jiàn)上一個(gè)章節(jié)(最簡(jiǎn)單的推薦系統(tǒng))的原始數(shù)據(jù)說(shuō)明,從上次的數(shù)據(jù)源說(shuō)明情況看,實(shí)際上打標(biāo)行為數(shù)據(jù)有10萬(wàn)條,評(píng)分?jǐn)?shù)據(jù)有100萬(wàn)條,相對(duì)于電影內(nèi)容數(shù)據(jù)實(shí)體來(lái)說(shuō),其實(shí)已經(jīng)算不少了,所以,不用擔(dān)心,針對(duì)于有行為記錄的用戶,或多或少還是能描述出他們各自的一些行為偏好的。

用戶興趣標(biāo)簽提取

基于上個(gè)小節(jié)的流程圖,所以,在實(shí)踐中,我們首先需要做的就是用戶興趣標(biāo)簽的提取。我們核心擁有的就是用戶對(duì)電影的打標(biāo)簽數(shù)據(jù),以及用戶對(duì)電影的評(píng)分?jǐn)?shù)據(jù)。

所以,從上面兩個(gè)行為數(shù)據(jù)集中,我們可以嘗試提取以下幾個(gè)維度的用戶偏好數(shù)據(jù):

用戶對(duì)電影類目偏好數(shù)據(jù),即用戶對(duì)那些類目偏好。

用戶的偏好興趣標(biāo)簽,即通過(guò)用戶對(duì)電影的偏好標(biāo)簽打標(biāo)行為,進(jìn)一步可以提取用戶的興趣標(biāo)簽。

用戶的偏好年份,通過(guò)打分?jǐn)?shù)據(jù),描述用戶偏好哪個(gè)年代的電影。

我們先解決用戶的偏好標(biāo)簽問(wèn)題,我們已有的是用戶對(duì)電影的打標(biāo)行為數(shù)據(jù),實(shí)際上這是電影層級(jí)的標(biāo)簽,所以我們需要在這個(gè)基礎(chǔ)上,最終直接為用戶映射上這些特征標(biāo)簽。

所以,我們需要對(duì)單個(gè)用戶的所有打標(biāo)數(shù)據(jù)進(jìn)行合并(標(biāo)簽會(huì)做預(yù)處理),然后如果用戶對(duì)剛好打標(biāo)的電影有評(píng)分的話,還需要帶上評(píng)分權(quán)重,最終合并這些標(biāo)簽,形成用戶帶權(quán)重(本身的頻度、對(duì)應(yīng)電影的評(píng)分)的標(biāo)簽集,這就是用戶的一些興趣點(diǎn)。

對(duì)于類目偏好,說(shuō)起來(lái)就簡(jiǎn)單了,比如通過(guò)評(píng)分表,我們把對(duì)應(yīng)所有的電影都取出來(lái),然后分析其類目,將評(píng)分作為對(duì)應(yīng)類目的權(quán)重,然后將類目進(jìn)行合并,最終求取用戶的類目偏好合集。

對(duì)于電影年份,過(guò)程與上述取類目的過(guò)程類似,最終獲取到年份偏好。

電影數(shù)據(jù)的處理

假設(shè)在上面的基礎(chǔ)上,我們已經(jīng)獲取了用戶層級(jí)的畫(huà)像屬性信息,比如偏好的電影類別,偏好的特征標(biāo)簽,偏好的某些年份的電影(同個(gè)時(shí)代電影具有一些相同的電影,比如10年前的電影風(fēng)格與現(xiàn)在的儼然不同,年份在某種程度上說(shuō)還是有影響的,雖然很弱)。

接下來(lái),我們需要繪制候選集電影的屬性(取之前,做一些初篩過(guò)濾操作,減少計(jì)算量),對(duì)應(yīng)用戶的屬性,同樣是三個(gè),其中年份、類目都是直接存放于電影表中,唯一需要額外處理的就是特征Tag了,由于不同人對(duì)不同的電影進(jìn)行Tag標(biāo)記,上面進(jìn)行用戶畫(huà)像繪制的時(shí)候,是以人為維度的,現(xiàn)在已電影為維度,進(jìn)行標(biāo)簽合并,最終同樣可以形成電影維度的標(biāo)簽集。

關(guān)聯(lián)推薦計(jì)算

每個(gè)維度分別進(jìn)行計(jì)算相似度或者相關(guān)度,然后不同維度進(jìn)行合并歸一計(jì)算最終的用戶與電影的相關(guān)度。最外層我們依然以權(quán)重模型去做,以經(jīng)驗(yàn)來(lái)看,類目是最重要的,其次是Tag,最后才是年份屬性,至于最終怎么調(diào)整還是需要根據(jù)實(shí)際反饋數(shù)據(jù)來(lái)做微調(diào),現(xiàn)在就拍腦袋吧。

image


作者:daos

鏈接:http://www.lxweimin.com/p/db181f4b5f2b

來(lái)源:簡(jiǎn)書(shū)

簡(jiǎn)書(shū)著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,489評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,290評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,776評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,510評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,866評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,036評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,585評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,331評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,536評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,754評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,154評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,469評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,273評(píng)論 3 399
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,505評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容