目錄
熱搜是何方神聖
很多產品都會有類似於熱搜或者熱榜的功能,總的來說都是熱點數據的挖掘,屬於推薦系統範疇。如點評類的產品(豆瓣、大眾點評網、IMDB),資訊類的產品(騰訊新聞、今日頭條、天天資訊),問答類的產品(知乎、StackOverflow),視頻類的產品(騰訊視頻、bilibili),社交類產品(微博、小紅書)都會使用熱搜或者熱榜的形式來展示一些熱點數據。通過這種方式可以吸引用戶來觀看優質的內容,提高信息獲取效率,提高信息曝光與互動,(對於兔小巢可以減少重複提問,也能提高產品經理信息檢索效率)進而提高用戶使用產品的體驗以及用戶的粘性。
熱搜算法
筆者通過查詢資料發現,每個產品會根據其不同特徵採取的不同熱搜或者熱榜算法。如微博的熱搜更考慮短期內對熱點事件的搜索量、bilibili的熱榜會考慮時投幣數,播放量,點讚量,評論量等因素......因此,在考慮熱搜和熱榜的算法的時候,可以從自己產品的特點入手,引入產品中自有的因素,如時間、搜索量、評論數、點讚數等。
熱點數據的算法可以根據幾個重要特徵分為:1. 按時間維度;2. 按訪問量(搜索量、點擊量、播放量);3. 按評分;4. 以上兩種或以上的綜合考慮。
接下來,介紹本人在探索兔小巢熱搜過程中使用過的幾個算法(方案)。
簡單的詞頻統計
這個方案很簡單,就是對於每個詞統計其搜索頻率。打開es的搜索日誌,通過搜索日誌來統計詞頻,詞頻高的也就是所要的熱搜詞。雖然簡單直觀,但是缺點也很明顯,就是不能考慮時間因素,會導致某個搜索詞在一段時間被大量搜索後,在隨後一直占據熱搜榜首,新來的搜索詞無法凸顯出來。
牛頓冷卻算法
為了考慮進時間這個因素,並且熱度最好隨着時間跨度而下降。牛頓冷卻算法就是這麼一種考慮進時間因素的算法,並且能夠隨着時間推移,熱度可以快速下降。
原理:物體的冷卻速度,與其當前溫度與室溫之間的溫差成正比。簡單點說我們可以將熱詞排名想象成一個即自然冷卻的過程。可以利用物理學定律,建立「溫度」與「時間」之間的函數關係,構建一個「指數式衰減」的過程。可用以下式子表示:
其中w_freq表示詞頻,△t表示時間差。為了避免除數為0,結果出現負數的情況,以及實現隨着時間差越大,熱度下降越快的效果(最後趨於0),筆者對上式進行改造。得到如下式子:
為了能夠更加直觀的表示,筆者嘗試用上面的公式繪製了下圖:
上圖中,詞頻取區間[0-1000],時間差為[0-6]天。我們可以看到,詞頻越高最後的分數也就越高。且隨着時間差越大,分數也能夠快速下降。達到了符合筆者期望的結果。
牛頓冷卻算法雖然達到了筆者最初的預想效果,但是本算法在計算熱度的時候,前一兩天的熱度分數會比較高,而後面下降得太快,可能會導致無法很好考慮最近連續幾天都出現的搜索詞。
代碼(Go版):
高斯(正態)分布算法
上面的牛頓冷卻算法有個小問題就是下降過快,如果對於某個產品,最近幾個小時或者最近幾天的熱度都比較重要,那麼牛頓冷卻算法可能不再適用。因此,可以考慮引入高斯(正態)分布算法來解決這一問題。
原理:從統計學的意義上來看,很多數據的分布都會遵循類似下圖的概率分布。其公式為:我們可以將某天中的一個詞的搜索頻度看作一個在時間跨度上的正態分布,也就是熱度看作詞頻乘以一個時間跨度上正態分布概率,再做稍微改造得到下式:

我們可以將某天中的一個詞的搜索頻度看作一個在時間跨度上的正態分布,也就是熱度看作詞頻乘以一個時間跨度上正態分布概率,再做稍微改造得到下式:
上式中w_freq是詞的搜索頻次、△t表示時間跨度以及△t_max表示數據中最大的時間跨度。取詞頻為[0-1000]、△t為[0-7]、△t_max為7,依據上述公式,可以繪製如下圖:
對比牛頓冷卻算法,可以從圖中看到前幾天熱度隨時間下降得更加平緩。
代碼(Go版):
假設以下是某產品一周的搜索記錄:
計算結果(按得分從高到低):
分析上面的結果,最明顯的是支付失敗和常見問題兩個搜索詞在不同算法下,其最終熱度也是不一樣的。可以看出牛頓冷卻算法更加考慮最近一天的數據,對於時間跨度較長的搜索詞,其計算出來的分數下降得比較快。而高斯分布算法會將近幾天的搜索量的熱度提高,這點在常見問題比支付識別熱度得分高近兩倍可以比較明顯的看出。
其他的如時間干預法,反對票算法(適用於有點讚和踩的產品)、貝葉斯平均法(適用於避免某個因素對最後影響過大)、今日頭條熱榜算法等,由於並不適用於兔小巢業務,筆者就沒有去嘗試實現,如果讀者有興趣可以查看參考文獻或自行查閱資料。
對於上述算法,筆者從兔小巢業務出發(目前只需考慮搜索量以及時間因素),在和組內人員和產品探討後最終選擇了高斯分布算法。前文也提到,不同的業務,具有不同的特徵,因此讀者在設計熱搜算法時應該從產品業務的客觀特徵出發,選用或設計更符合產品特徵的熱搜算法。
兔小巢所需熱搜業務場景分析
談完算法,需要回歸到兔小巢業務本身,分析整個兔小巢業務邏輯,從而設計出更符合兔小巢業務的熱搜模塊架構。
兔小巢產品特點:
兔小巢類似於論壇,每個產品都是一個獨立社區。
有的產品用戶比較多,搜索量也大。
有的產品用戶比較少,搜索量少,甚至可能一兩周之內都不會有人搜索。
從特點出發,分析潛在的問題,給出解決方案:
兔小巢每個產品都是一個獨立社區。因此不能像其他應用(如微博)那樣,對全局的搜索詞進行統計。而是需要每一個產品內部自己維護一個搜索記錄,最後根據每個產品的搜索記錄計算出各自的熱搜詞。
有的產品用戶比較多,搜索量也大。如果將每天的搜索記錄都寫到一行記錄中去的話,會導致比較久遠已經失去時效性的記錄無法刪除,同時記錄表也會越來越大。故最好採用按天記錄的方式,這樣一來對於超過一周或者一個月的搜索記錄可以刪除,減輕數據庫負擔。
有的產品用戶比較少,搜索量少,甚至可能一兩周之內甚至更長時間內都不會有人搜索。如果像大產品一樣頻繁的啟動熱搜計算,會導致計算資源浪費。對於這種情況,可以採取的策略是最近一天或者兩天有搜索記錄了,再去啟動熱搜計算。如果最近一天或者兩天沒有搜索記錄,則維持原有的熱搜計算結果,不更新。
兔小巢熱搜模塊架構
結合上述兔小巢業務分析,熱搜模塊架構方案如下:
每個產品的當天的搜索詞,都記錄到redis的sorted set結構中。次日凌晨更新到數據庫中,每個產品每天的搜索記錄做一行表記錄,並刪除redis中的key。redis的key為<code>product_search_history:[產品id]:[日期]</code>。
啟動定時(一天或者兩天)腳本,定時掃描數據庫,判斷每個產品是否有新的搜索記錄。如果有新的搜索記錄,那麼就將該產品最近7天(或者60天)的搜索記錄取出,利用選用的熱搜算法進行進行。最後將計算出來排名前十的熱搜詞持久化到數據庫。