2019年XNUMX月至XNUMX月,舉辦了社群網路提要排名競賽
SNA 黑客松
這是第三次以該名稱舉辦黑客馬拉松。 它由社交網路ok.ru組織,任務和數據分別與該社交網路直接相關。
在這種情況下,SNA(社會網絡分析)更正確地理解為不是對社交圖的分析,而是對社交網絡的分析。
- 2014 年,任務是預測一個貼文會獲得多少讚。
- 2016年-VVZ任務(也許你很熟悉),更接近社交圖譜的分析。
- 2019 年,根據用戶喜歡貼文的可能性對用戶的 feed 進行排名。
2014年我不敢說,但2016年和2019年,除了資料分析能力之外,還需要大數據的處理能力。 我認為正是機器學習和大數據處理問題的結合吸引了我參加這些比賽,我在這些領域的經驗幫助我獲勝。
機器學習訓練營
2019年在平台舉辦比賽
比賽於7月3日線上開始,共設XNUMX個任務。 任何人都可以在網站上註冊下載
任務
來源資料提供使用者 ID (userId) 和貼文 ID (objectId)。 如果向使用者顯示帖子,則資料包含一行,其中包含 userId、objectId、使用者對此帖子的反應(回饋)以及一組各種功能或圖片和文字的連結。
用戶身份 | 物件ID | 所有者 ID | 反饋 | 圖片 |
---|---|---|---|---|
3555 | 22 | 5677 | [已按讚、已點擊] | [哈希1] |
12842 | 55 | 32144 | [不喜歡] | [哈希2,哈希3] |
13145 | 35 | 5677 | [點擊,分享] | [哈希2] |
測試資料集包含類似的結構,但缺少回饋欄位。 任務是預測回饋欄位中「喜歡」反應的存在。
提交文件的結構如下:
用戶身份 | 排序列表[objectId] |
---|---|
123 | 78,13,54,22 |
128 | 35,61,55 |
131 | 35,68,129,11 |
此指標是使用者的平均 ROC AUC。
更詳細的數據描述可以在以下位置找到:
線上舞台
線上階段,任務分為3部分
協同系統 — 包括圖像和文字以外的所有特徵;Изображения — 僅包含有關影像的資訊;文本 — 僅包含有關文字的資訊。
線下階段
離線階段,資料包含所有特徵,而文字和圖像稀疏。 資料集中的行數增加了 1,5 倍,而資料集中的行數已經很多了。
問題的解決
由於我是在工作中做履歷的,所以我從「圖像」任務開始了這次比賽的旅程。 提供的資料包括 userId、objectId、ownerId(發佈貼文的群組)、建立和顯示貼文的時間戳,當然還有該貼文的圖片。
基於時間戳產生多個特徵後,下一個想法是採用在 ImageNet 上預先訓練的神經元的倒數第二層,並將這些嵌入發送到 boosting。
結果並不令人印象深刻。 我想,來自 imagenet 神經元的嵌入是無關緊要的,我需要製作自己的自動編碼器。
花了很多時間,結果卻沒有改善。
特徵生成
處理圖像需要花費很多時間,所以我決定做一些更簡單的事情。
正如您立即看到的,資料集中有幾個分類特徵,為了不打擾太多,我只使用了 catboost。 解決方案非常棒,無需任何設置,我立即就到達了排行榜的第一行。
有相當多的數據,並且以 parquet 格式佈局,因此我毫不猶豫地採用了 scala 並開始在 Spark 中編寫所有內容。
比影像嵌入提供更多成長的最簡單的功能:
- objectId、userId 和ownerId 在資料中出現的次數(應與流行度相關);
- userId 從ownerId 看到了多少貼文(應與使用者對群組的興趣相關);
- 有多少個唯一的 userId 查看了ownerId 的貼文(反映了群組受眾的規模)。
從時間戳記可以取得使用者觀看動態的一天中的時間(早上/下午/晚上/晚上)。 透過組合這些類別,您可以繼續產生特徵:
- 晚上userId登入了幾次;
- 該帖子最常在什麼時間顯示(objectId)等等。
所有這些都逐漸改善了指標。 但訓練資料集的大小約為20M記錄,因此添加特徵大大減慢了訓練速度。
我重新思考了使用數據的方法。 雖然資料是依賴時間的,但我並沒有看到「未來」有任何明顯的資訊洩露,不過,為了以防萬一,我將其分解如下:
提供給我們的訓練集(二月和三月的兩週)分為兩部分。
該模型根據過去 N 天的數據進行訓練。 上述聚合是基於所有資料(包括測試)建構的。 同時,出現了可以建構目標變數的各種編碼的資料。 最簡單的方法是重複使用已經建立新功能的程式碼,並簡單地向其提供不會接受訓練且目標 = 1 的資料。
因此,我們得到了類似的特徵:
- userId在群組ownerId中看過貼文幾次;
- userId對群組ownerId中的貼文按讚了多少次;
- userId 喜歡的來自ownerId 的貼文的百分比。
也就是說,事實證明 平均目標編碼 資料集的一部分,用於分類特徵的各種組合。 原則上,catboost 也會建立目標編碼,從這個角度來看沒有任何好處,但是,例如,可以計算喜歡該群組中貼文的唯一用戶的數量。 同時,主要目標也達到了——我的資料集減少了好幾倍,並且可以繼續產生特徵。
雖然 catboost 只能根據喜歡的反應建立編碼,但回饋還有其他反應:轉發、不喜歡、不喜歡、點擊、忽略,可以手動完成編碼。 我重新計算了各種聚合並消除了重要性較低的特徵,以免資料集膨脹。
那時我已經以遙遙領先的優勢位居第一。 唯一令人困惑的是圖像嵌入幾乎沒有增長。 這個想法就是把一切都奉獻給 catboost。 我們對 Kmeans 影像進行聚類並得到一個新的分類特徵 imageCat。
以下是手動過濾和合併從 KMeans 獲得的簇後的一些類別。
基於 imageCat 我們生成:
- 新的分類功能:
- userId 最常查看哪一張 imageCat;
- 哪一個imageCat最常顯示ownerId;
- userId 最常喜歡哪一張 imageCat;
- 各種計數器:
- imageCat查看了多少個唯一的userId;
- 大約 15 個相似特徵加上如上所述的目標編碼。
文本
圖像比賽的結果很適合我,我決定嘗試文字。 我以前沒怎麼處理過文本,愚蠢的是,我在 tf-idf 和 svd 上度過了這一天。 然後我看到了 doc2vec 的基線,它正是我所需要的。 稍微調整 doc2vec 參數後,我得到了文字嵌入。
然後我簡單地重用了圖像的程式碼,其中我用文字嵌入替換了圖像嵌入。 結果,我在文字比賽中獲得了第二名。
協同系統
還有一場比賽我還沒用棍子「戳」過,從排行榜上的AUC來看,這場比賽的結果應該對線下舞台的影響最大。
我獲取了來源資料中的所有特徵,選擇了分類特徵,並計算了與影像相同的聚合,除了基於影像本身的特徵之外。 僅僅將它放入 catboost 就讓我獲得了第二名。
catboost 優化的第一步
一個第一名和兩個第二名讓我很高興,但大家都知道我沒有做任何特別的事情,這意味著我可能會失去位置。
比賽的任務是對用戶內的貼文進行排名,而這段時間我一直在解決分類問題,也就是優化錯誤的指標。
讓我舉一個簡單的例子:
用戶身份 | 物件ID | 預測 | 基本事實 |
---|---|---|---|
1 | 10 | 0.9 | 1 |
1 | 11 | 0.8 | 1 |
1 | 12 | 0.7 | 1 |
1 | 13 | 0.6 | 1 |
1 | 14 | 0.5 | 0 |
2 | 15 | 0.4 | 0 |
2 | 16 | 0.3 | 1 |
讓我們做一個小小的重新安排
用戶身份 | 物件ID | 預測 | 基本事實 |
---|---|---|---|
1 | 10 | 0.9 | 1 |
1 | 11 | 0.8 | 1 |
1 | 12 | 0.7 | 1 |
1 | 13 | 0.6 | 0 |
2 | 16 | 0.5 | 1 |
2 | 15 | 0.4 | 0 |
1 | 14 | 0.3 | 1 |
我們得到以下結果:
模型 | AUC | 使用者1 AUC | 使用者2 AUC | 平均曲線下面積 |
---|---|---|---|---|
選項1 | 0,8 | 1,0 | 0,0 | 0,5 |
選項2 | 0,7 | 0,75 | 1,0 | 0,875 |
如您所見,提高整體 AUC 指標並不意味著提高使用者內的平均 AUC 指標。
加速
在「協作系統」競賽線上階段結束前 5 分鐘,Sergey Shalnov 將我移至第二名。 我們一起走更遠的路。
線下階段準備
我們憑藉 RTX 2080 TI 顯示卡保證了線上階段的勝利,但 300 萬盧布的主要獎金,甚至很可能是最後的第一名,都迫使我們工作了兩週。
事實證明,Sergey 也使用了 catboost。 我們交換了想法和特點,我了解到
查看報告讓我想到,我們需要將所有參數恢復為預設值,並且只有在修復一組功能之後才能非常仔細地進行設定。 現在,一次訓練大約需要 15 個小時,但一個模型的速度比在具有排名的整合中獲得的速度要好。
特徵生成
在協作系統競賽中,大量特徵被評估為對模型很重要。 例如, 審計權重_spark_svd - 最重要的標誌,但沒有關於它的含義的資訊。 我認為根據重要特徵來計算各種聚合是值得的。 例如,按使用者、按群組、按物件計算平均auditweights_spark_svd。 使用沒有進行訓練且target = 1的資料也可以計算出相同的結果,即平均值 審計權重_spark_svd 按使用者按他喜歡的對象。 除此以外的重要標誌 審計權重_spark_svd,有好幾個。 這裡是其中的一些:
- 審計權重Ctr性別
- 審計權重CtrHigh
- 使用者所有者計數器創建喜歡
例如,平均 審計權重Ctr性別 根據userId結果發現這是一個重要的特徵,就像平均值一樣 使用者所有者計數器創建喜歡 透過用戶Id+所有者Id。 這應該已經讓您認為您需要理解這些欄位的含義。
同樣重要的特徵是 審核權重點贊數 и 審核權重顯示計數。 將其中一個除以另一個,就得到了一個更重要的特徵。
資料外洩
競賽和生產建模是非常不同的任務。 在準備資料時,很難考慮到所有細節並且不傳達有關測試中目標變數的一些重要資訊。 如果我們正在創建生產解決方案,我們將在訓練模型時盡量避免使用資料外洩。 但如果我們想贏得競爭,那麼資料外洩就是最好的功能。
研究了數據可以看出,根據objectId值 審核權重點贊數 и 審核權重顯示計數 變化,這意味著這些特徵的最大值的比率將比顯示時的比率更好地反映轉換後的情況。
我們發現的第一個洩漏是 auditweightsLikesCountMax/auditweightsShowsCountMax.
但如果我們更仔細地觀察數據呢? 讓我們按演出日期排序並獲得:
物件ID | 用戶身份 | 審核權重顯示計數 | 審核權重點贊數 | 目標(被喜歡) |
---|---|---|---|---|
1 | 1 | 12 | 3 | 可能不會 |
1 | 2 | 15 | 3 | 也許是吧 |
1 | 3 | 16 | 4 |
當我發現第一個這樣的例子時,我感到很驚訝,結果證明我的預測並沒有實現。 但是,考慮到物件內這些特徵的最大值有所增加,我們並不懶惰,決定尋找 審核權重顯示計數下一個 и 審核權重點贊數下一個,即下一時刻的值。 透過添加一個功能
(auditweightsShowsCountNext-auditweightsShowsCount)/(auditweightsLikesCount-auditweightsLikesCountNext) 我們迅速跳躍。
透過尋找以下值可以使用類似的洩漏 使用者所有者計數器創建喜歡 在 userId+ownerId 內,例如, 審計權重Ctr性別 在 objectId+userGender 內。 我們發現了 6 個類似的洩漏字段,並從中提取了盡可能多的信息。
到那時,我們已經從協作特徵中擠出了盡可能多的信息,但沒有回到圖像和文字競賽。 我有一個好主意想檢查一下:直接基於圖像或文字的特徵在相關競賽中能給出多少分數?
圖片和文字比賽都沒有出現洩漏,但那時我已經返回了預設的 catboost 參數,清理了程式碼並添加了一些功能。 總計為:
解決方法 | 很快 |
---|---|
最大影像 | 0.6411 |
最多無影像 | 0.6297 |
第二名成績 | 0.6295 |
解決方法 | 很快 |
---|---|
最多有文字 | 0.666 |
最大無文本 | 0.660 |
第二名成績 | 0.656 |
解決方法 | 很快 |
---|---|
最大程度的協作 | 0.745 |
第二名成績 | 0.723 |
很明顯,我們不太可能從文字和圖像中榨取太多東西,在嘗試了一些最有趣的想法後,我們停止了與它們的合作。
協作系統中進一步產生的功能並沒有增加,我們開始排名。 在線上階段,分類和排名整合為我帶來了小幅提升,事實證明,這是因為我對分類的訓練不足。 包括 YetiRanlPairwise 在內的所有誤差函數都沒有產生接近 LogLoss 的結果(0,745 與 0,725)。 QueryCrossEntropy 還抱有希望,但無法啟動。
線下階段
離線階段,資料結構保持不變,但有細微變化:
- 標識符 userId、objectId、ownerId 被重新隨機化;
- 一些標誌被移除,一些標誌被重新命名;
- 數據增加了約1,5倍。
除了列出的困難之外,還有一個很大的優點:團隊分配了一台具有 RTX 2080TI 的大型伺服器。 我很喜歡htop很久了。
只有一個想法——簡單地複製已經存在的東西。 在花了幾個小時在伺服器上設定環境後,我們逐漸開始驗證結果是否可重現。 我們面臨的主要問題是資料量的增加。 我們決定稍微減少負載並設定catboost參數ctr_complexity=1。 這會稍微降低速度,但我的模型開始工作,結果很好 - 0,733。 Sergey 與我不同,他沒有將資料分成兩部分並對所有資料進行訓練,儘管這在線上階段給出了最好的結果,但在離線階段卻遇到了許多困難。 如果我們採用生成的所有功能並嘗試將它們推入 catboost,那麼在線階段將什麼都不起作用。 Sergey 做了型別最佳化,例如,將 float2 型別轉換為 float64。
這些結果足以獲勝,但我們隱藏了我們的真實速度,無法確定其他團隊是否也在做同樣的事情。
戰鬥到最後一刻
Catboost 調整
我們的解決方案被完全複製,我們添加了文字資料和圖像的特徵,所以剩下的就是調整 catboost 參數。 Sergey 在 CPU 上進行了少量迭代的訓練,而我在 ctr_complexity=1 的 CPU 上進行了訓練。 還剩一天,如果您只是添加迭代或增加 ctr_complexity,那麼到早上您可以獲得更好的速度併步行一整天。
在離線階段,透過簡單地選擇不是網站上的最佳解決方案,可以輕鬆隱藏速度。 我們預計在提交結束前的最後幾分鐘排行榜會發生巨大變化,因此決定不再停止。
從Anna的影片中我了解到,要提高模型的質量,最好選擇以下參數:
- 學習率 — 預設值是根據資料集的大小計算的。 增加learning_rate需要增加迭代次數。
- l2_leaf_reg — 正規化係數,預設值3,最好從2到30中選擇。減少該值會導致過擬合增加。
- 裝袋溫度 - 將隨機化加入樣本中物件的權重中。 預設值為 1,其中權重從指數分佈中得出。 減小該值會導致過度擬合的增加。
- 隨機強度 — 影響特定迭代中分割的選擇。 random_strength 越高,選擇低重要性分割的機會就越高。 在隨後的每次迭代中,隨機性都會降低。 減小該值會導致過度擬合的增加。
其他參數對最終結果的影響要小得多,所以我沒有嘗試選擇它們。 在 ctr_complexity=1 的 GPU 資料集上進行迭代訓練需要 20 分鐘,並且縮減資料集上所選的參數與完整資料集上的最佳參數略有不同。 最後,我對 30% 的數據進行了大約 10 次迭代,然後對所有數據進行了大約 10 次迭代。 結果是這樣的:
- 學習率 我比預設增加了40%;
- l2_leaf_reg 保持原樣;
- 裝袋溫度 и 隨機強度 減少到0,8。
我們可以得出結論,該模型在預設參數下訓練不足。
當我看到排行榜上的結果時,我非常驚訝:
模型 | 1模型 | 2模型 | 3模型 | 合奏 |
---|---|---|---|---|
無需調音 | 0.7403 | 0.7404 | 0.7404 | 0.7407 |
帶調音 | 0.7406 | 0.7405 | 0.7406 | 0.7408 |
我自己得出的結論是,如果不需要快速應用模型,那麼最好將參數的選擇替換為使用非最佳化參數的多個模型的集合。
Sergey 正在優化資料集的大小以在 GPU 上運行。 最簡單的選擇是切斷部分數據,但這可以透過多種方式完成:
- 逐漸刪除最舊的資料(二月初),直到資料集開始適合記憶體;
- 刪除重要性最低的特徵;
- 刪除只有一個條目的 userId;
- 僅保留測試中的 userId。
最終,將所有選項組合成一個整體。
最後的合奏
到最後一天深夜,我們已經建立了一個模型集合,結果為 0,742。 一夜之間,我用 ctr_complexity=2 啟動了模型,而不是 30 分鐘,而是訓練了 5 個小時。 凌晨 4 點才開始統計,我製作了最後一個合奏,在公共排行榜上的得分為 0,7433。
由於解決問題的方法不同,我們的預測並沒有很強的相關性,這使得整體有了很好的成長。 為了獲得良好的集成,最好使用原始模型預測預測(prediction_type ='RawFormulaVal')並設定scale_pos_weight = neg_count / pos_count。
在網站上你可以看到
其他解決方案
許多團隊都遵循推薦系統演算法的規範。 我不是這個領域的專家,無法評估它們,但我記得兩個有趣的解決方案。
尼古拉·阿諾欣的解決方案 。 Nikolay 身為 Mail.ru 的員工,並沒有申請獎品,因此他的目標不是達到最大速度,而是獲得易於擴展的解決方案。- 評審團獲獎團隊的決定是基於
這篇文章來自臉書 ,無需手動工作即可實現非常好的圖像聚類。
結論
我印象最深刻的是:
- 如果資料中有分類特徵,並且您知道如何正確進行目標編碼,那麼最好嘗試 catboost。
- 如果您正在參加比賽,您不應該浪費時間選擇學習率和迭代之外的參數。 更快的解決方案是製作多個模型的整合。
- Boostings 可以在 GPU 上學習。 Catboost 在 GPU 上可以非常快速地學習,但它會佔用大量記憶體。
- 在思路的開發和測試過程中,最好設定一個小的rsm~=0.2(僅限CPU)和ctr_complexity=1。
- 與其他團隊不同,我們的模型整體有了很大的改善。 我們只是交換想法並用不同的語言寫作。 我們採用了不同的方法來分割數據,我認為每種方法都有自己的錯誤。
- 目前尚不清楚為什麼排名優化的表現比分類優化差。
- 我獲得了一些處理文本的經驗,並了解了推薦系統的製作方式。
感謝組織者給予我們的情感、知識和獎品。
來源: www.habr.com