比你更了解你自己 — 淺談Web在推薦系統扮演的角色
推薦系統與我們的生活
在Web2.0當道的世代,在生活中接觸到的項目都與推薦息息相關,一早起床打開Spotify,點播喜愛的音樂,聽完一首發現系統推薦了其他相似的音樂,就會不自覺的一首首的聆聽下去。中午午休時間打開Youtube,看近期要上映的電影預告片,看完後系統馬上又跳出這部電影的預告解析或幕後花絮影片,這時午休還沒結束的你,想必會想繼續點下一部影片來觀看。上述的行為無形中,增加了我們在平台上的使用時間,不自覺的感到上癮,這就是推薦系統的魔力。
推薦系統(Recommendation System) 是什麼
推薦系統可以想像成是一種過濾資訊的系統,藉由分析和預測用戶對事物的偏好,進而推薦用戶感興趣的內容,來創造出更多的商業價值(e.g. 用戶黏著度,商品購買率)。
浪LIVE,身為台灣第一大即時串流直播平台,我們希望能讓用戶有機會認識更多主播,與主播有更多的互動。因此 浪LIVE 網站的動態牆頁面 ,導入了推薦系統的技術,藉由推薦出更多用戶有可能會感興趣的貼文,提升用戶的在線時長,增加用戶認識其他主播的機會。
推薦系統常見的方法
而推薦系統是如何運作,背後推薦的方法有哪些,常見的三種方法介紹如下。
- 內容過濾(Content Based Filtering)
- 協同過濾(Collaborative Filtering)
- 混合方法(Hybrid Approach)
內容過濾 (Content Based Filtering)
內容過濾是用內容的屬性來進行推薦,例如電影類型、音樂風格、食物種類 …等等。找出內容之間相似的地方來進行分類。例如在Netflix觀看一部亞洲動作電影,看完就推薦歐美動作電影。
而此方法的缺點是只會做比較保守的推薦,因為推薦系統只會挑選用戶偏好的內容,不會推薦其他類別的內容。
協同過濾 (Collaborative Filtering)
協同過濾是依照群體用戶行為來進行推薦,「聽完這首音樂的人,下一首聽了什麼」,藉由分析以及歸納出群體用戶的行為模式,來找出與你相似行為的用戶。
協同過濾又可以細分兩個方法:
- User-Based:計算用戶之間的相似度,將相似用戶喜好的內容推薦給用戶
- Item-Based:計算內容之間的相似度,把相似的內容推薦給用戶
此方法會遇到幾個缺點:
- 冷啟動(Cold Start):沒有足夠的訊息可以做分析,造成推薦效果不如預期。例如新內容或新註冊的用戶沒有足夠的交易紀錄,導致能分析的資訊過少。
- 資料稀疏(Data Sparsity):如果平台上某個類別的內容過多,會讓用戶在瀏覽內容時無法看到其他類別的內容,造成搜集資料上的稀疏。
混合方法 (Hybrid Approach)
混合方法意思同時採取上述兩種方法來建立推薦系統,在平台建立的初期,「內容數量」跟「用戶數量」都還偏少的情況,就適合採用內容過濾(Content Based Filtering)方法來做推薦,避免遇到冷啟動的問題。當經過一段時間,內容跟用戶數累積到一個程度後,就適合採取協同過濾(Collaborative Filtering)的方法,來推薦出更多不同類別的內容給用戶。
推薦系統的建置
有了上述推薦方法的概念後,接著來看 浪LIVE 的推薦系統是如何建置。
首先進到 浪LIVE 推薦動態頁,此頁的貼文內容都是推薦系統計算出來推薦給用戶看的,推薦系統會根據用戶過往的行為,例如是否看到(曝光)、按讚、留言、分享等行為來推薦用戶可能感興趣的貼文。
假如用戶對這篇貼文感興趣,停留了一段時間,我們就會認為用戶有看到(曝光)此篇貼文。接下來用戶可能會點開貼文閱讀內容,如果用戶有在此篇貼文上做互動,更能說明用戶對此篇貼文是特別感興趣的。
接著將用戶互動的相關數據送到後台,後台從資料庫中取出資料集進行資料的整理,接著進行資料分析與推薦演算法的模型訓練,將分析後的結果轉化成有價值的知識,來推薦出用戶有可能感興趣的內容。
Web在推薦系統中扮演的最主要角色就是搜集資料,並與 AI工程團隊討論哪些指標是有意義,要被搜集的。像是搜集用戶與貼文互動的行為(曝光、按讚、留言、分享)都是很重要的指標。
接下來的章節會探討「看到(曝光)」這個指標如何搜集?怎麼樣的行為算「看到」? 技術實作的內容會以 javascript 以及 react 作演示。
怎麼樣的行為算「看到 (曝光)」
我們在意的是貼文是否被用戶「看到」,考慮到了兩個面向:
- 貼文是否在用戶的可視範圍內。
- 貼文在可視範圍內停留了多久。
因此我們可以定義出符合有意義的資料場景:貼文完整的呈現在用戶的畫面, 並且用戶在此貼文的時間停留了超過2秒,就算一次有效的曝光。
根據上述的情境,搜集到的資料才是有意義的。試想,如果用戶只是很快速的滑過此篇貼文,又或者是貼文只有在螢幕露出不到10%,就會認為此篇貼文用戶還尚未看到(曝光)。而這種需要偵測畫面上的某個元素是否出現在可視範圍的場景,就很適合用 Intersection Observer API 的技術來實現,接著來認識一下 Intersection Observer API的技術。
以前的實作方式
以前實作方式是註冊scroll監聽事件,並且利用 Element.getBoundingClientRect
計算元素和可視範圍的相對位置。
這樣做法的缺點是必須每一次scroll都去計算元素是否在可視範圍內,並且透過 getBoundingClientRect,clientLeft, clientTop,offsetLeft,offsetTop 取得元素相對位置,都會使瀏覽器重新計算整個頁面的佈局 (reflow / repaint),造成效能降低。
我們用便利商店來舉例:在傳統的便利商店,當有客人從店門口經過時,店員會主動上前問說有沒有需要什麼服務,如果客人有需要就服務,如果沒需要就回到櫃台上處理其他事情。
這樣的做法很明顯是沒效率又耗時的,因為每次經過的客人,店員就要上前詢問一次,店員除了服務客人之外也有很多店內的事情需要處理。所以這種 主動監聽的方法是會降低效率的。
Intersection observer API 的出現
而 Intersection observer API 的出現,就是用來解決以前要偵測元素是否已經進入「可視範圍」的痛點,核心精神是
當被觀察的對象是否進入或離開可視範圍,並且與父層元素或瀏覽器重疊到某個百分比時,觸發callback做某件事
延續上述便利商店的例子,在現代化的便利商店都有自動門的設計,每當顧客踏進店裡或離開,自動門發出聲音來提醒店員有客人進出,如果客人進來,就上前詢問客人有什麼需要。
- 客人 => 被觀察的對象
- 便利商店 => 父層元素或瀏覽器
- 客人踏進店裡,自動門發出聲音 => 觸發條件
- 自動門發出聲音後,店員開始服務客人 => 滿足觸發條件後要做的事情
跟傳統便利商店最大的差別,在於店員終於不用主動去詢問客人了,因為會有自動門來通知店員,現在有客人進來,此時店員再上前去服務即可。
有了這概念後,接著來看 Intersection observer 要如何實作
Intersection observer API 實作範例
創建一個intersection observer物件
在創建一個intersection observer物件時, 需設定
- options:觸發的條件
- callback:滿足觸發條件後要做的事情
Intersection observer API — Option屬性介紹
options 是用來設定在哪些條件下要觸發callback函式,有三個屬性可以設定,分別是 root, rootMargin,threshold。
root:
預觀察的元素,不特別指定或是null時,即為瀏覽器的可視範圍。
rootMargin:
root 周圍的margin,用來放大或縮小可視範圍。
rootMargin有兩種寫法,如果要依照每個方向設定,可寫成 rootMargin: 20px 10px 40px 10px (上右下左)
。如果4個方向都一樣,可直接簡寫成一個值 50px
threshold:
設定目標元素與root重疊多少比例時,觸發callback
可設定單一數字,或是一連串的數字
- threshold = 0.75 代表目標元素出現在可視範圍內 75%,就會觸發
- theshold =[0, 0.25, 0.5, 0.75, 1]代表目標元素出現在可視範圍的比例,每25%就會觸發
intersection observer 除了設定觀察對象 observer.observe()
方法外, 還有提供 observer.unobserve()
跟 observer.disconnect()
方法
1. 取消觀察某個對象 observer.unobserve()
若要取消觀察某個對象,則可以使用 unobserve
,就像透過JS註冊事件,如果不再使用時,會用 removeEventListener
一樣。
2. 暫時取消觀察所有對象 observer.disconnect()
若要暫時性的取消觀察對象,可以使用observer.disconnect()
,要注意的地方是使用此方法時 ,observer的物件並不會消失,只是沒有在觀測元素而已
了解完如何偵測用戶在可視範圍內,接著下個探討的項目是:
貼文在可視範圍內停留了多久
貼文在可視範圍內停留了多久
要計算一篇貼文停留了多久,實作步驟如下:
- 宣告一個目標元素
div
div
被渲染在畫面上時,用一個計數器來計算停留時間- 時間到了則觸發
callback
函式,用來通知此貼文已滿足停留時間的條件
在第2步驟,實作計數器很容易踩到一個坑,就是在倒數的過程中,如何避免畫面一直不停的渲染。舉個例子來說明,我們想計算此貼文是否已經停留超過5秒,所以理想上只要計數器算到5秒,再調用callback函式來通知已經滿足條件即可,在中間計數的過程中 (1秒、2秒、3秒、4秒) 畫面是不需要重新被渲染的。
我們來看個計數器畫面會一直渲染的範例:
因為計數器在倒數的過程中,一直更改到state,所以畫面才會一直渲染。
為了解決上述問題,我們使用了useRef的技術。
useRef 在 React的官方文件說到:
useRef
returns a mutable ref object whose.current
property is initialized to the passed argument (initialValue
). The returned object will persist for the full lifetime of the component.
意思是說 useRef回傳的是一個物件,只有一個屬性值是 current
,React會確保useRef 回傳的屬性不會被重新創造,無論怎麼更改 current的值,都會指向同一個reference。
有了useRef的概念後,我們修改程式碼,讓計數器畫面只會在倒數到指定的秒數才會渲染
使用useRef後的改善成果:
(完整程式碼範例可參考此連結 )
到目前為止,已經介紹完「看到(曝光)」這個指標如何搜集,以及什麼樣的行為算「看到」,接下來會介紹用什麼方式來評量推薦系統。
數據分析指標 — 社群媒體互動率
在評量推薦系統的成果,會以「社群媒體互動率」來做計算,我們在意的是推薦給用戶的貼文,是否真的有打中用戶,讓用戶願意跟貼文有互動行為,而按讚跟留言都是最直接的互動指標。
我們想要看每篇貼文觸及到的人數,有多少用戶有跟貼文有做互動,因此互動率的公式定義為:
公式: 互動人數 / 觸及總人次- 互動人數: 有按讚或是有評論的去重複人數
- 觸及總人次: 每篇貼文觸及的人數去重複,再做相加
以 浪LIVE 9月份的數據為例:
- 有按讚或是有評論的去重複人數:6.5萬多人
- 貼文觸及的總人次:165萬人
因次根據上述的定義公式計算,互動率就會是接近4%
結語
導入推薦系統最大的目的,就是希望能增加用戶在平台的黏著度。透過一篇篇的推薦貼文,讓用戶有機會接觸更多「可能會有興趣」的主播,甚至進入直播間,與主播進行互動 (追蹤、聊天、送禮 … 等),進而增加平台收益。
未來我們會朝著提供更方便的貼文互動介面,以及搜集更多值得觀察的指標 (例如收藏),回饋給推薦系統,來提升互動率。
此篇文章的介紹在整個推薦系統架構中只是冰山一角,目的是希望能讓讀者對於推薦系統有初步的認識,了解到 浪LIVE 的動態牆是如何導入推薦系統的技術、了解Web在推薦系統中扮演的角色,以及是如何評量推薦系統的成果。
如果此篇文章有幫助到你,拍手加追蹤給個鼓勵吧。