李亞欣,蔡永香,張 根
(長江大學 地球科學學院,武漢 430100)
自1997年Resnick等人[1]首次提出用“推薦系統”(Recommender System,RS)一詞代替“協同過濾”來描述推薦技術以來,推薦系統實現了面向系統的探索階段,開始成為一個重要的研究領域[2].推薦系統按總體的功能劃分,可分為用戶建模模塊、推薦對象建模模塊、推薦算法模塊3個重要模塊[3].目前許多研究專注于其中某個或某幾個模塊的優化設計上,如文獻[4,5]側重于用戶建模優化,文獻[6]側重于對傳統協同過濾推薦的算法優化等.一個好的推薦系統在側重功能實現的同時還需要關注其整體性能的優化,如推薦算法計算時間過長時如何保證系統運行流暢,如何提高系統的可伸縮性等.此外,各推薦方法自身還存在著缺陷,比如典型的協調過濾推薦和新出現的基于網絡結構的推薦都存在冷啟動及稀疏性問題;基于內容的推薦,用戶將僅限于獲得跟以前類似的推薦結果,很難為用戶發現新的感興趣的信息[3].推薦系統在采用這些推薦方法解決問題時,如何揚長避短進行完善?
另外,用戶的偏好是一種理性和感性的共同選擇[7],用戶除了具有固定興趣偏好,由于時間、事件、場合等原因,也會在短時間內發生興趣焦點轉移,如某一用戶以前對財經類文章并不感興趣,但最近因為所在的公司要重組準備上市,也開始關注這方面的新聞,這就是興趣焦點的轉移.如何迎合用戶長期偏好的同時也能適應用戶短時間內的興趣變化?
本文設計了一種實時推薦與離線推薦相結合的推薦系統來解決上述問題,提出了待推薦池的概念,用于存儲準備推薦給用戶的數據,用戶獲取數據直接從待推薦池內提取,從而保證系統運行始終流暢;系統通過分析用戶實時數據進行實時推薦,分析用戶歷史數據進行離線推薦,二者結合可更好地貼近用戶長期偏好及短期興趣焦點;此外,系統采用控制模塊,通過設置該模塊控制調節不同的推薦結果,從而提高系統的可伸縮能力.此外,本文將該推薦系統應用于微信文章的推薦上,實時推薦采用基于內容數據的推薦方式,離線推薦采用基于用戶行為數據的推薦方式,文中介紹了具體實施方法,并對推薦效果進行了分析和總結.
目前已有的推薦方法有很多,根據不同的分類標準可以得到不同的分類結果.文獻[2]依據推薦方法使用的數據源不同,將推薦方法分為:基于內容數據的推薦、基于用戶行為數據的推薦、基于社交網絡數據的推薦等7種推薦方法.其中基于內容數據的推薦與基于用戶行為數據的推薦是最為常見的兩種推薦方式.
基于內容數據的推薦(content-based recommendation)的基本思想是對一個給定的用戶,推薦與他之前喜歡的項目在內容上有相似性的其他項目.而項目間相似度計算方法包括:向量空間模型、改進的向量空間模型、顯式決策模型、線性分類和機器學習等.基于用戶行為數據的推薦(user behavior-based recommendation)的基本思想是利用興趣相投,擁有共同經驗的群體喜好來給使用者推薦可能感興趣的項目[2].其最為廣泛的計算方法是協同過濾,包括基于用戶的協同過濾、基于項目的協調過濾以及基于模型的協同過濾[3].
單一的推薦方法存在各自的優缺點,如文獻[2]指出基于內容數據的推薦不需要一個很大的用戶社區或打分歷史,對單個用戶就可以產生推薦列表,但其推薦給用戶的項目與該用戶已經消費過的項目很相似,這使得不易發現用戶不熟悉但是潛在感興趣的項目種類,給用戶造成的驚喜度不高;基于用戶行為數據的推薦不需要預先獲得用戶或項目的特征,僅依賴于用戶的歷史行為給用戶興趣建模,從而為用戶進行推薦.但其過于依賴于用戶的歷史行為,這使得推薦方法無法滿足用戶短時間內的興趣變化等.
對于上述基于內容數據的推薦和基于用戶行為數據的推薦各自呈現的不足之處,一些研究者采用混合推薦策略來達到揚長避短的目的.混合推薦包括推薦結果的混合以及推薦算法的混合[3].前者各推薦方法獨立運行,只對推薦結果進行選擇,如文獻[8]通過設定標準從各自推薦產生的結果中進行判斷選擇;后者則是推薦方法間的嵌套使用,如文獻[9]在基于協同推薦的框架內混合基于網絡結構的推薦等,一般根據應用場景不同采取不同方式.
針對前面提出的問題,我們在推薦系統設計時著重考慮:① 關注用戶長期偏好以及短期興趣焦點變化,② 將推薦系統作為一個整體考慮系統運行的流暢性及可伸縮能力.為此,我們的設計思路為:
① 既然用戶的實時反饋信息,如閱讀,點贊,評論等,能反映用戶短時間內的興趣焦點,而對這些反饋信息進行長期累積,就能形成貼近用戶長期偏好的歷史數據.我們可以基于實時反饋信息進行實時推薦,基于歷史數據進行離線推薦,將二者結合,就能在考慮用戶長期固有偏好的同時也能關注用戶短時間內的興趣焦點變化.
② 系統運行不暢,往往是由于推薦計算時間過長,不能及時提供推薦結果導致.本文提出待推薦池這一概念,用于存儲準備推薦給用戶的數據集.推薦時,系統首先從待推薦池提取一定數量的數據推薦給用戶,再通過實時推薦與離線推薦產生推薦數據補充到待推薦池內,這樣即使推薦計算運行時間長,用戶依然能夠連續多次從待推薦池中提取數據,保證了系統運行始終流暢.待推薦池中的數據,除了在系統運行初期是按類別等比例隨機獲取的數據外,后面補充到待推薦池中的數據都是實時推薦與離線推薦的結果.因此,長時間運行后,池內數據會不斷貼近用戶的偏好,這樣通過分析池內數據,也可對本系統推薦效果進行評價.
③ 為了提高系統健壯性與可伸縮能力,在推薦系統中增加控制模塊.一方面,按照實時推薦模塊和離線推薦模塊產生的推薦數量,控制模塊能夠合理調節兩者份額,補充到待推薦池內,提高系統的健壯性.如當離線推薦數據少而實時推薦數據多時,會適當增大實時推薦比例,反之亦然.另一方面,當系統增加其它推薦算法時,很容易通過對該模塊進行設置,來選擇其它推薦算法的推薦結果,提高了系統的可伸縮能力.
本系統整體運行流程如圖1所示,具體步驟包括:
① 初始狀態下,將所要推薦的數據,如書籍、文本、圖片、電影、音樂等數據,隨機等比例地填充到待推薦池內,保證池內數據全面多樣;
② 每次推薦,系統從待推薦池內獲取定額數據推薦給用戶,如每次從池內提取10篇文章推薦給用戶;
③ 記錄模塊記錄用戶對本次推薦的反饋,如用戶對其中某些數據的閱讀,收藏,點贊,評論等行為信息;

圖1 推薦流程圖
④ 用戶獲取下次推薦數據時,實時推薦開始運行.提取記錄模塊內的所有記錄信息,實時推薦模塊根據這些信息計算并產生推薦數據;
⑤ 統計模塊獲取記錄模塊信息(提取后記錄模塊清空),與前面獲取的歷史紀錄合并,離線推薦模塊根據這些統計信息計算并產生推薦數據;
⑥ 控制模塊監控待推薦池,對實時推薦數據與離線推薦數據進行去重處理,包括與用戶的歷史數據比較去重,與待推薦池比較去重,實時與離線推薦數據比較去重;然后,根據待推薦池內缺額數,通過加權或計算TopN的方式,組合兩類推薦數據,補充到待推薦池內.
待推薦池以隊列形式存儲,池內最大容積固定,每次推薦時提取池內排名在前的定額數據給用戶.控制模塊根據池內缺額數,結合實時推薦數據與離線推薦數據補充到待推薦池.
初始狀態下,推薦程序由于無用戶歷史數據和即時反饋數據,無法進行實時推薦與離線推薦,系統自動將各類數據等比例隨機選取填充到待推薦池內,一方面保證池內數據全面多樣,使用戶在池內能夠獲得所有類型數據;另一方面,系統后續可從用戶對各類數據的反饋中獲取用戶的興趣焦點與偏好,從而進行實時推薦與離線推薦.
推薦初期,用戶歷史數據沒達到一定規模,實時推薦正常而離線推薦無法有效進行.這時,提取實時推薦數據中至多需補充數額量的數據,將這些數據的80%插入到待推薦池隊首,其余20%數據插入到隊尾,保證下次推薦給用戶的數據既能圍繞用戶當前的興趣焦點,又能讓用戶有機會接觸到池內其它種類的數據,此外也保證了待推薦池內的數據量.
隨著用戶歷史數據不斷積累,離線推薦開始正常運行.補充待推薦池時,組合兩類推薦數據至需補充數額量,將其中實時推薦部分插入到隊首,使下次推薦的數據中包含大量實時推薦數據與原池內少量數據,保證推薦給用戶的數據大部分圍繞用戶的興趣焦點上,還有小部分是池內存儲的用戶之前可能感興趣的數據;將離線推薦數據部分插入到隊尾,逐漸改變待推薦池內數據類型,使其逐漸貼合用戶的歷史偏好.
為保證系統流暢性,用戶每次獲取數據時,直接從待推薦池內提取,然后控制模塊選擇調節兩種推薦方法的結果數據進行數據補充.當多次推薦用戶無反饋,即實時推薦模塊不能正常產生推薦數據,而同時用戶的歷史數據稀疏,離線推薦模塊也沒有推薦數據時,會出現待推薦池內數據不斷減少的現象,這時就需要設置預警閥值,以避免待推薦池內數據量為空.當池內數據小于預警閥值時,系統需要及時補充數據,所補充的數據可依據統計模塊中已有的用戶偏好信息產生,若沒有,可按照初始數據填充方式,將各類數據等比例隨機選取,產生一定量的數據進行補充.
將上述推薦系統應用到微信文章推薦上.微信文章是一種數量大、種類多的文章類型.文章數據主要來源于微信精選網站(https://fzn.cc/),使用Python爬蟲Scrapy進行數據抓取,提取包括美文哲理、幽默笑話、趣味測試、經驗總結、美食美景及保健養生6大類文章類型.同時補充搜狗微信網站(https://weixin.sogou.com/)中搞笑、養生堂、旅游、美食類文章數據,微信群網站(https://weixinqun.com/article)中養生之道、搞笑段子、人氣美食、旅游美景、人生哲理、經驗總結類文章數據.
(1)數據存儲形式
采用MySQL數據庫與Redis數據庫存儲文章數據.MySQL是一個開源的關系型數據庫管理系統,文章數據爬取后存在該數據庫中;而Redis是一個基于內存的,存儲形式為Key-Value的非關系型數據庫(NoSql)[10],推薦系統中的待推薦池及其他數據池均使用Redis數據庫存儲.
為實現微信文章推薦,每個用戶擁有六個數據池(如下表1所示),各數據池的命名格式為“用戶賬號_數據池特性_存儲類型”.
(2)推薦方式選擇
本推薦過程中,實時推薦采用基于內容數據的推薦方式,根據用戶近期閱讀的文章,將含有這些文章標簽的文章推薦給用戶,從而聚焦在用戶興趣焦點上.離線推薦采用基于用戶行為數據的推薦方式,將相似用戶閱讀過而本用戶沒有閱讀過的文章推薦給用戶,拓展推薦數據的興趣廣度.
文獻[2]指出基于內容數據的推薦方式需要進行預處理獲得其項目特性.在來源網站上,微信文章被設置多個標簽來代表其特性,標簽數多為3個,但存在無標簽、少標簽、多標簽、標簽不規范等問題,為此,需要對獲取的文章進行預處理,規范文章標簽.使用Python結巴分詞結合TF-IDF統計方法實現.
文章標簽設置標準如圖2所示.選取方法如下:

表1 數據池設計表

圖2 標簽提取規則
① 使用Python結巴分詞,提取3個標題關鍵詞,7個除標題關鍵詞外的內容關鍵詞,兩類關鍵詞提取詞數不足時,以能取到的詞數為準;
② 將所有文章的所有關鍵詞進行統計計數形成一個關鍵詞統計排名;
③ 針對每篇文章,從標題關鍵詞中提取TF-IDF最大的一個標題關鍵詞,同理從內容關鍵詞中提取一詞.然后針對剩余的最多8個關鍵詞,對照關鍵詞統計排名獲得排名最高的一個高頻詞,以這三個關鍵詞作為文章標簽.
分詞過程中,引用用戶字典,確保如“微信”“朋友圈”等網絡詞語能夠被識別出來;引用停用詞詞庫,避免分詞結果出現常用而無意義的詞語;引用同義詞詞庫(如菜鳥和新手同義),使詞語標準化;引用包含詞詞庫(如顏色包含白色、灰色等),使標簽更具代表性.
確定標簽提取規則后,我們對527篇具備標簽的微信文章進行測試,以提取的最多10個關鍵詞是否包含標簽詞作為分詞合格標準,以最終提取的3個關鍵詞是否包含標簽詞作為提取合格標準進行測試,測試結果如圖3所示.分詞合格率為91%,提取合格率為87%,基本滿足需求.然后對所有文章進行標簽處理.以3個優先(優先保留合格標簽,優先添加標題關鍵詞,優先保留標簽與關鍵詞的重疊詞)為標準結合關鍵詞統計排名進行設置,如圖4所示.

圖3 標簽詞提取測試情況

圖4 標簽設置規則
3.3.1 實時推薦
文章標簽規范化處理后,每篇文章固定存在3個標簽代表文章特性.實時推薦模塊采用基于內容數據的推薦方式進行推薦,具體流程如圖5所示.
圖5中文章流行度涉及到標簽流行度[11]的概念,標簽流行度是指用戶查看的所有文章中某標簽出現的頻率高低,計算方法如式(1)所示

其中,T(u)表示用戶u閱讀的文章所有標簽集合,Freq(u,ti)表示用戶u閱讀所有文章中標簽ti的次數.
文章流行度是指用戶對文章的偏好程度,可以用文章標簽流行度累加和表示,如式(2)如下:

其中,T(r)表示文章r的所有標簽.對于本文研究的微信文章,該公式所計算的就是每篇文章中三個標簽的流行度之和.
3.3.2 離線推薦
用戶統計模塊統計用戶閱讀的所有文章的id、類型以及標簽,離線推薦模塊根據這些歷史數據采用基于用戶行為數據的推薦方式進行推薦,具體推薦流程如圖6所示.

圖6 離線推薦流程
(1)群組劃分
用戶統計模塊中類型統計池對用戶閱讀的所有文章的類型進行統計,即記錄了用戶閱讀美文哲理、幽默笑話、趣味測試、經驗總結、美食美景及保健養生6類文章的文章數.將該數據記為一個代表用戶閱讀類型偏好的六維向量,計算兩用戶間余弦相似度,余弦值越接近1表示越相似,越接近0表示越不相似.計算方法如式(3)所示,其中xi,yi分別表示用戶x與用戶y在統計模塊中類型統計池內i類型文章的統計數.

在用戶群體中隨機抽取一名用戶,計算該用戶與剩余所有用戶的余弦相似度,取閥值為0.8,將余弦相似度大于等于0.8的用戶以及該用戶劃分為一個群組從用戶群體中剝離.在余下的用戶群體中再隨機抽取一名用戶,如上述方法計算,直至將用戶群體完全劃分.
(2)尋找相似用戶
用戶標簽是指用戶經常看的文章標簽集合,以該數據代表用戶閱讀主題偏好.統計模塊中標簽統計池對用戶閱讀的所有文章的標簽進行了統計,利用這些數據可以計算用戶標簽,具體方法為:去除標簽統計池內用戶只閱讀過一次的標簽,然后將剩余標簽根據標簽流行度(式(1))排序,排名前80%的標簽集合作為用戶標簽.通過計算用戶間標簽重疊率,重疊率越接近1越相似,計算方法如式(4)所示,其中labelsi表示用戶i的標簽,overlap(i,j)表示用戶i與用戶j的標簽重疊率:

群組劃分后,對于每個群組,遍歷群組成員,計算該成員與其他成員的標簽重疊率,取閥值0.5,重疊率大約等于0.5的成員即為該成員的最終相似用戶.
(3)被分享池
將這些相似用戶的已閱讀池內的數據,經過去重處理,記錄到本用戶的被分享池內.被分享池內的文章數據即為離線推薦數據.
3.3.3 整體推薦策略
本推薦系統中實時推薦在每次用戶獲取數據時運行一次,運行次數取決于用戶的獲取次數,而離線推薦每天定時執行一次,所產生的推薦數據存儲在用戶的被分享池內.
控制模塊一方面對傳遞過來的實時/離線推薦數據進行去重處理,包括與用戶的歷史數據比較去重,與待推薦池內數據比較去重,實時與離線推薦數據比較去重.另一方面,監控待推薦池內數據量變化,以加權及具體場景具體分析的方式組合兩類推薦數據補充待推薦池,具體組合方式如表2所示.當需要增添其他推薦方式時,通過對該模塊進行設置,結合這些推薦算法的推薦結果,從而提高了系統的可伸縮能力,如圖7所示.
本文設計的推薦系統,其目的主要在于讓推薦的數據能夠貼合用戶的偏好,可以通過對待推薦池內數據進行分析,考查本系統推薦效果.
以10 788篇微信文章進行推薦測試,設置用戶待推薦池容積為100,每次推薦,提取池內10篇文章數據給用戶.系統模擬100名用戶,每名用戶進行了100次推薦操作,為貼近實際情況,保證模擬效果,所模擬的用戶一方面需要具有一定的興趣偏好,另一方面還能按照一定概率閱讀不在該偏好內的其他文章.因此,對模擬用戶做如下設置:① 每名用戶隨機具有1~3個感興趣的文章類型.② 每次推薦,用戶會隨機閱讀1~3篇偏好類型的文章.③ 每次推薦,用戶有20%的概率閱讀1篇不在偏好類型之內的其他類型的文章.推薦過程中,前50次只進行實時推薦,第50次以后加入離線推薦,每進行25次推薦,統計所有用戶當前待推薦池內,含有2個及以上用戶標簽的文章所占比例,計算最大值、最小值、平均值、兩平均值總和以及該總和的增長比例.實驗結果如表3所示.

表2 兩類推薦數據組合方式

圖7 整體推薦流程
含有2個及以上用戶標簽的文章可以認為是用戶感興趣的文章.初始狀態下,待推薦池內含2個及以上用戶標簽的文章所占比例為0.根據表3所示,經過25次推薦,占比約占22.97%,之后每25次推薦,達到44.39%,50.75%,53.48%,分別增長了93.25%,14.33%,5.38%.經過100次推薦后,含用戶標簽的文章總計約占91.76%,其中能夠代表用戶感興趣的文章(含有2個及以上用戶標簽的文章)約占53.48%,只含有1個用戶標簽的文章約占38.28%,如表4所示.從池內文章占比變化可以看出,待推薦池內文章能夠逐漸貼近用戶偏好.

表3 待推薦池內含2個及以上用戶標簽的文章占比變化表

表4 待推薦池最終各類文章占比
本文設計了一種結合實時推薦與離線推薦的推薦系統,能夠保證系統運行流暢、具備可伸縮能力以及能夠適應用戶長期偏好及短期興趣焦點變化.基于該系統,實現了對于微信文章的推薦實驗,采用易理解、可操作、效果可見的推薦方式,在實驗中,推薦方式具備如下特點:① 用戶歷史數據稀疏不影響推薦系統運行;② 只采集了用戶閱讀行為,無評分機制;③ 系統具備可伸縮能力,能夠根據各推薦模塊產生的推薦數據量,調節兩類推薦數據比例,并且可以增添其他推薦方式;④ 保證了推薦系統始終運行流暢.此外,對于推薦系統評價方面,本系統通過對待推薦池內數據分析來對本系統推薦效果進行評價,實驗表明待推薦池內數據能夠逐步貼近用戶興趣偏好.