作者 | 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)并注明出處。