先薦|推薦系統簡論

推薦系統簡論.png

本文並不會介紹任何一種在實際中可行的推薦系統的實現或者算法,但是本文會以易於理解的方式,向你介紹一種認識推薦系統的角度。

 

什麼是推薦

推薦是一種古老的信息檢索方式,我國曆史記載最早的推薦在西漢,漢武帝元光元年初令郡國舉孝廉各一人,即舉孝舉廉各一人。實際上這種推薦方式已經包含了現代推薦系統的設計思想:分佈式、使用CF、分層結構。

隋朝,科舉制度開始興起,通過科舉考試,又爲人才推薦加入了排序分,發展到這裏,其實從架構上已經和現代推薦系統非常接近了。

所以人可以多讀歷史,當找不到前行的方向時,總能發現古人已經把解法或雷區以某種方式寫在了史書裏。

人類的歷史總是這樣。

 

什麼是推薦系統

推薦系統是一種把物料(item:文章、新聞、商品、主播、視頻)檢索、推薦並展現給用戶(user)的計算機系統。

推薦系統解決的問題是,當物料數量越來越多、種類越來越複雜,用戶的注意力和時間越來越有限的背景下,如何以可接受的代價,將用戶“需要”的內容檢索並推薦出來。這在互聯網不斷快速發展,進入後移動互聯網時代之後,成爲一個越來越有價值的問題。

更一般地講,在推薦系統所處的環境中,物料少的會有幾百上千,多的可能達到幾千萬甚至上億。而推薦系統服務的用戶數則從幾萬日活到幾億日活不等。毫無疑問,當物料和用戶數量增長時,推薦系統的複雜和技術挑戰會同等增長。

 

推薦系統的評估標準

理想:

推薦系統一般來講是企業用戶產品的一部分,作爲用戶產品,理應爲兩個目標服務:

1. 讓用戶開心:

在絕大多數情況下,用戶沒辦法告訴我他開心不開心,你也不是用戶肚子裏的蛔蟲。你只能用他的行爲數據來衡量他開心的程度。比如:他點擊了你的推薦結果(點擊率)+1;他點擊了你的推薦結果並且讀了100秒(平均60秒)+3;他收藏了你的推薦結果(收藏率)+0.5 ……

2. 讓公司開心:

畢竟錢不是大風吹來的,推薦系統也有ROI。比如說:推薦商品銷售金額;推薦的廣告分成等等。

現實:

現實中,推薦系統的評估存在一系列的現實問題:

1. 跟蹤更多的數據是有代價的

每增加一個數據,比如說收藏、比如說閱讀時長,都意味着推薦系統的數據鏈路變得更復雜了,出錯的可能性更大了,要付出的跟蹤和解釋數據的成本也提高了。最重要的是,在漏斗的路徑上,越往後,數據越少,數據越少,意味着隨機性或者外部不可控因素造成的波動越大。

2. 推薦的評估應該由用戶來做,但是有的case比其他case更重要

比如說你挑燈夜戰,奮鬥1000個小時終於成功搭建了一套推薦系統,然後運營×××姐突然看到了自己推薦結果中出現了少兒不宜的內容,一聲尖叫後暈了過去。爲了客觀描述這類情況,推薦系統工程師發明了一個專有名詞:bad case。

實際上是有一些case比其他case更bad,比如說出現了令人難堪的推薦結果,比如說出現了一些(某人的)直覺上不應當出現在推薦位置這個場景的物料,比如說一個推薦位返回的物料和之前極爲相似甚至其實就是同一個,等等。

妥協:

現代推薦系統的在線評估是基於工程實踐發展出來的一套科學同時又可行的方法論:

1. 追蹤1~2個核心數據,一般來說是CTR和CR,也就是轉化漏斗上的第一步和第二步,初期大多數實踐中選擇只追蹤CTR的提升。

2. 關注運營人員和主要用戶羣體反饋的Bad case,降低乃至消滅bad case的出現

 

推薦系統的數學抽象

任何計算機系統,都對一系列輸入,給一系列輸出。讓我們拋開推薦系統的實際實現,只關注我們希望他完成的任務,在數學上進行一個抽象。

所以,推薦系統就是上式中的F,在已知所有物料信息的情況下,當用戶來訪問推薦系統時,根據獲取的用戶信息,通過推薦系統的一系列計算,返回推薦列表作爲結果。

聽起來很簡單是不是?

F應該具有什麼性質

我們希望這個函數(不考慮具體實現方式)有這樣一些好的性質:

1.     總是能返回結果

因爲你希望無論如何,推薦系統在所有情況下都可以返回結果,當然你最後可以決定用還是不用這些結果,但是沒有結果是不可接受的,那意味着前端體驗的不美觀和不一致。這會導致現代的推薦系統總是多層次的計算結構,每一層都比上一層更加簡單,也更魯莽,以保證在最壞的情況下也有結果。

2.     返回結果的速度很快

因爲現代的用戶客戶端體驗要求,無論你是個多麼出衆的推薦系統,如果你不能在數百毫秒內完成結果的計算、返回、渲染,用戶下一秒就轉身走了。

3. 推薦系統可以24小時不間斷工作

F是一個有參數的函數,這意味着,我們在運行過程中,可以對F進行一定的修改,使得F的返回結果按照我們期望的方式發生變化,並且這些變化可以被我們觀察到。

F是一個CTR、CR很高,bad case很少甚至不存在的函數

更爲艱難而且隱藏的問題是:對於先薦來講,F必須是一個不依賴輸入的函數。因爲我們面對的客戶,物料集和用戶可能有數千甚至上萬種不同的格式。比如說,實現 f ( x , y ) = x + y 看起來是個很簡單的任務。但是如果x、y可以是int、double、string、bigint呢?如果x、y可以是數組呢?如果他們是矩陣、張量、甚至x、y是兩個函數呢?如果他們可以是文件呢?如果他們大到一臺機器的內存甚至磁盤都放不下呢?這是通用推薦系統設計中,難度最大的問題之一。

物料的表達

你應該知道,任何一個推薦系統中物料數應當>1,否則就沒有推薦的必要了。那麼顯然應當用一種類似表格的對齊的數據結構來把物料表達出來。

這個表達應該具有怎樣的性質呢?

1. 儘量包含這個物料的全部信息,比如說,一個蘋果,請問是廊坊產的還是美國德州產的,是綠色的還是紅色的還是×××的?口感是甜的還是酸的?是脆的還是沙的?

2. 儘量少包含不必要的信息,因爲存儲和處理是要錢的。系統的複雜度都是輸入的複雜度帶來的。而且一路向下傳遞。

3. 所以最終你表達一個物料,應當包含所有會顯著影響推薦系統效果的信息。當然這裏並不會告訴你到底多少信息纔夠顯著,因爲這個信息是通過大量實驗和實踐纔可以獲得的,換句話來說,就是要錢的。但是我們可以介紹一些常見的表達方式,以文本物料(新聞)爲例:

1. 新聞的時空信息,新聞是什麼時候發佈的、寫的是什麼地區的新聞?

2. 新聞的標題和正文。但是在推薦系統中,參與計算的表達並不是標題、正文的原文。因爲這種信息對於機器,是很不友好的。我們一般會通過技術把原文轉化成關鍵詞列表、向量、實體詞、情感向量等等對於機器更加友好的格式。

3. 一些屬性,比如說新聞的分類、作者、標籤,等等,這些信息一般通過某種啓發式的方法計算出來。啓發式也是算法工程師發明出來的一個非常好的形容詞,用普通話來說,就是我也沒有一個數學公式證明這個方法是好的,但是這個方法聽起來能行得通,行得通比沒有要強。這樣的方法,就是啓發式的。比如說如果一篇文章裏如果包含體育運動的名詞,而且還比較多,就算體育類的新聞。

4. 與該篇新聞相似的新聞列表,這種表達是計算上的妥協,因爲相似性最終是通過用戶的行爲(CF等矩陣方法)或者屬性等信息計算的,但是實際上,這些計算對時間和空間的要求並不利於在線進行,因而我們往往會使用離線的方法計算好這些相似性,而直接把相似性計算的結果作爲物料的表達存儲起來。

表達決定了這些物料信息存儲在計算機系統中的數據結構,並最終影響了下游使用這些表達的算法,比如說各種倒排索引、B樹、哈希表等等。

用戶信息的表達

類似地,用戶信息的表達,與物料有着相同的原則,因而我們往往通過這樣的方式來表達用戶:

1. 用戶是誰(也就是用戶的唯一標識),其實,在大多數情況,這是個並不那麼簡單的問題,一般來講,我們寧願用用戶所使用設備的ID來作爲這個標識。如果是一個非常成熟有用戶註冊系統的業務,我們可能會使用用戶ID來作爲這個標識;

2. 人口統計學等相對靜態的信息,如用戶註冊時填寫的信息,用戶的年齡性別,用戶的註冊地區;

3. 用戶設備所能採集到的信息,比如IP地址,比如說設備的型號,客戶端的版本號,等等等等;

4. 用戶最近有行爲的物料的抽象。假設你玩知乎,你一定知道了解一個人很簡單的一個辦法,就是看看他最近讚了什麼回答,關注了什麼話題,甚至寫了什麼回答。但是你把這些文章ID存下來是不夠的,你需要抽取一些有一定泛化能力的信息。有一些最簡單的推薦系統,實現的方式,就是把一個人看過的文章裏的關鍵詞抽取出來,設定一個權重,然後放到搜索引擎裏每次sample一些搜一下。這樣的推薦系統有的還拉到了幾億融資,活的非常好。我不會告訴你是誰的。

什麼叫做泛化

本文多處會提及這個動詞,需要做出一定的解釋。

英文:Generalize

直譯的話,似乎應該叫做推廣,推而廣之,當然叫泛而化之可能更文雅一些。比如說,你是一個喜歡看ABP-100的男生,這句話可能對於90%的讀者都很費解。但是我說你是一個喜歡看日本愛情動作片的男生,這句話可能有50%的讀者都明白了。所以“日本愛情動作片” 是一個比 “ABP-100” 更有泛化能力的描述。也容易去推廣,因爲我可以給你推薦更多的日本愛情動作片。機器是沒有領域知識的,所以你提供給機器的物料和用戶的表達,最好有一定的泛化能力。

對,這就是泛化。

當然如果你的系統做得足夠好,ABP-100也是他可以理解的,系統越強大,數據越多,自身的泛化能力越高,對於輸入信息的泛化能力要求越低。不信你百度一下?

 

探索和利用 (Explore & Exploit)

想象一個場景:

你,推薦系統工程師,人生贏家,年級輕輕,月入超過北京市公積金繳存上限。今天面前是爹媽新約的相親對象。

最近放的幾個電影看了嗎?(沒話找話,推薦系統算法稱之爲“探索“)

不喜歡看電影(探索失敗,負反饋,記下了,接下來不和這個妹子聊電影了)

那你看話劇嗎?(換一個話題,推薦系統算法稱之爲category)

看呀,上週剛在xxxooo看了暗戀桃花源(探索成功,記下了,跟這個妹子聊話劇)

那個zzzyyy你看了嗎?那個誰誰和誰誰演的(這就叫做利用)

所以研究推薦系統可以使得你生活得更好。

探索和利用,以下簡稱EE,爲的是解決這樣的問題:不論如何,在一個用戶應用中,用戶沒看過的內容總是大多數,有時是因爲用戶是一個全新的新用戶,有時是因爲在他沒看過的內容裏沒有過任何行爲或者特徵可以推斷這些沒看過的內容裏他會喜歡什麼。那麼你總要試一試,不試一試就永遠不知道。

試完以後,就要儘快的反饋回來,調整下一步的推薦結果。這就是探索和利用。

 

召回

召回其實是推薦系統爲了現實所做的一種妥協,因爲人類設計的系統,總是通過分層,拆分的方式,把一個複雜的大問題,變成一個個相對簡單的小問題。

首先思考一個問題,一個新聞網站,可以每天更新2萬篇新聞,假如說最近一週的新聞都可以作爲推薦內容,那麼每個給定的時刻,大約有14萬篇文章可供推薦。

方案1:

每當有一個人來到這個推薦系統時,我掃描一遍這14萬篇文章,選擇當前最適合他的10篇文章給到他。那麼爲了在200毫秒內完成計算(我沒有考慮任何通信的時間),我的算法需要1.4微秒內就完成對這個人+任意一篇文章的匹配度判斷(我們先不考慮這個匹配度代表什麼)。這個算法好不好我們不知道,但是一定很簡單。

方案2:

每當有一個人來到這個推薦系統時,我先通過一些代價更小的算法,比如說sample 1000個出來,然後對這1000篇文章用比方案1時間上覆雜100倍的更好的算法進行評分。

實際上,和你的直覺一樣,方案2是work並且更有效的。

 

召回算法需要什麼樣的性質?

時間複雜度足夠低,最好是O(1)的

比隨機Sample要強,強得越多越好

所以召回一般來講是一些“啓發式”的算法,所謂啓發式,就是算法都這麼簡單了,你也別指望有什麼太靠譜的數學證明,但是確實work。

一般來講,這種算法很簡單,無非某種索引上的檢索,比如:所有包含“ABP“的視頻,這可以通過倒排索引來實現;所有今天/三天內的新聞,這可以通過B樹來實現;所有離線計算出來的和這個人最近看過的10篇文章相似的文章,這可以通過Hash來實現。

這幾類索引實現的召回,基本覆蓋了90%以上的召回算法。

根據某些簡單的實時特徵計算出來的key,再用這個key去1.中的索引去查詢。比如我做一個模型,根據這個用戶最近看過的category,預測他下一步可能看的category。

通過召回進入下游的物料,叫做排序候選集。一般來講,候選集的大小在50~1000之間,當然,限制他的主要因素是有多少機器。

 

排序算法

排序算法,就是一個函數,這個函數輸入是一些用戶特徵和候選集中的物料特徵,他的輸出是模擬這個用戶(在當前的時空狀態下)對這個物料進行打分。比如說,模擬這個用戶是否會選擇點擊這個物料。

從這個角度來講,有點像考試,考試的目的是預先判斷這個物料會不會被用戶喜歡,既然是考試,那麼考試會有的毛病,排序算法都有,考試沒有的毛病,算法也有。

考試太簡單了,不能充分考察物料,推薦算法工程師發明了一個名詞,叫做模型維度,基本就表達了這個考試的難度和覆蓋面,一般來講維度越高,考的題目覆蓋物料的能力就越廣,自然篩選能力就越準,當然這並不總是成立

考試太複雜了,其實複雜本身並沒有錯,錯的是複雜在沒必要甚至有害的地方。比如說政治考試,總是考今年的國家大事,這個卷子如果放在三年以後,可能就沒有什麼區分度。或者你明明要考數學,卷子裏卻考了語文。這樣篩選出來的可能都是一羣非常會寫論文但是不會搞數學的人。當然複雜還有一個問題,就是我們經常強調的,機器不是大風吹來的,複雜的考卷,自然要佔用更多的判卷老師和紙張,不環保。

應試教育,最後自然導致學生被訓練成考試的機器。對於物料來說,最後推薦出一堆標題黨。當然大多數推薦系統都沒有活到看到這個事情發生的那一天,你可以暫時不用擔心這一點。

 本文並不介紹推薦排序算法的實現,如果你感興趣,可以瞭解一下LR、GBDT、DNN等等算法,當然對於閱讀到本文這裏的讀者來說,要麼你不需要知道得這麼具體,要麼你早已經比作者本人更瞭解了。

 

過濾和多樣性

我們常常舉一個例子,推薦就好像你去飯館吃飯,你跟服務員點了個“隨便”。

當然,這是一個非常牛掰的飯館,後廚有幾百萬個並行工作的廚師,全世界能做的菜譜都在這裏了。那麼你可以理解召回和排序,是廚師選材、做菜的過程。接下來,是要上菜的。過濾和多樣性這個環節,正是完成上菜這個工作,因爲上菜中有一些顯然的人的先驗性知識可以幫助你。

比如說,吃過的菜就不要再上了。絕大多數推薦系統會過濾用戶最近已經消費過的內容。當然在不同的推薦系統場景中,“消費”一詞的定義不同。比如說信息流式推薦中,只要出現在feed流的內容,就可以認爲已經被用戶消費了,一段時間內不可再推薦。而在一篇文章最後推薦的相關文章,往往認爲用戶沒有點進去的話,就沒有消費。

比如說,不要把非常類似的菜,一起上。這符合人的直覺,一般認爲,同時推薦兩個非常相似的內容,是一個bad case。但是在不同的場景中,“非常相似“的定義也有所不同。比如說一個商品推薦系統,比如說淘寶中,會認爲同時推薦兩個儘管item_id不同,但都是iPhoneX的商品,是不好的。在新聞的場景中,推薦兩個僅僅是標題略有不同,但是內容99%一樣的新聞,是不好的。

比如說,推薦的內容儘量要豐富多彩,因爲推薦系統一般預估用戶喜好的準確度都不太高,既然準確度不高,那就要用覆蓋率來補。你可能因爲某種我不知道的原因現在剛好不想吃牛肉,但是我同時推薦豬牛羊魚肉給你,這樣一來,你每個都不喜歡的概率就下降了。

 

推薦系統的分層

我們在本文的前半部分介紹過,人對推薦系統很大的一個期望就是:總是能返回結果。

這其實並不是一個簡單的事情,尤其是當推薦系統變得越來越複雜的時候

有時候,是因爲算法太複雜,你寫過濾規則的時候忘記了你一共只有1000個物料,過濾完了,已經沒有物料可以推薦了。

有時候,是因爲算法中的某個環節掛了,只要是人類造的系統,就有可能掛掉,所以這個環節沒有返回任何物料結果。

所以分層設計並不難理解。

算法的分層,當執行的時候,每到最後一個環節,我會問推薦系統,我們現在返回幾個物料?如果達到了標準,我就會返回結果。如果沒有,我就開始執行下一步的更簡單的算法,試圖返回更多的結果。那麼在這種設計中你需要保證,最後一層是一個特別簡單、一定能返回出結果的算法。

系統的分層,不光算法會掛掉,系統也會。所以你希望即使某個機房被雷劈了,你的推薦系統也不至於馬上掛掉。於是你會爲你係統關鍵的環節做備份,把功能分佈在不同的機器甚至機房裏。

 



一件事情需重複三次以上時,就值得爲之設計一個系統。當一個系統被不斷重複造出來,就值得爲之設計一個通用的平臺、框架。CRM、ERP、數據庫、即時通信、搜索引擎,人類的計算機系統發展史不斷證明着這一點。

先薦就是這樣一個基於人工智能技術的推薦平臺,他努力吸取了前人的智慧和錯誤,並且還在不斷成長。

相信有一天他會和所有前輩的系統一樣,讓每一個媒體,每一個網站,每一個app應用,每一個企業都可以像使用office一樣,擁有最好最棒的推薦系統技術。

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章