2019騰訊廣吿算法大賽方案分享(冠軍)

2019-09-23 11:07:12

來自公眾號:機器學習初學者


本文提供2019年騰訊廣吿算法大賽冠軍的代碼分享。

俞士綸(Philip S. Yu)教授的評價“冠軍隊伍已經在有意無意使用“廣度學習”的方法”,評委講到“這是最接近騰訊真實業務的方案”。(本文作者:王賀)


寫在前面

隊伍介紹:哈爾濱工業大學二年級碩士生劉育源、中山大學微軟亞洲研究院聯合培養博士生郭達雅和京東算法工程師王賀。

本文將給出冠軍完整方案,全文內容架構將依託於答辯PPT,具體細節也會結合代碼進行講解。當然,思路為主,代碼為輔,希望這篇分享能夠給予你更多的啟發。

下面就讓跟隨我一起探索這榮獲最高嘉獎的方案 !

賽題理解

賽題鏈接:

https://algo.qq.com

  1. 數據

歷史日誌數據:廣吿請求時間、用户id、廣吿位id、競價廣吿信息等

用户信息數據:包含用户id、年齡、性別、地域、行為興趣等

廣 吿 數 據:廣吿操作信息、廣吿靜態信息

2. 目標

預測廣吿的日曝光量

3. 評價指標

評價指標由兩部分組成,準確性指標和出價單調性指標。

最終得分是將兩個指標組合一起,前者控制準確性,後者控制單調性。

4. 訓練目標

這裏我們對訓練目標進行了不斷優化,首先是最基本的訓練目標,即廣吿日曝光量。然後考慮到0會導致梯度不平滑,所以對訓練目標做了log變換,保證梯度平滑。

為了符合業務直覺,我們在訓練時進行了單調性的考慮,而不是在訓練後進行單調性修正。即考慮了出價變量,保證訓練出來的結果符合單調性。

最後將基礎曝光的訓練目標和考慮單調性的訓練目標進行結合,也就得到了最後一個公式,即一個模型預測基本訓練目標,一個模型預測考慮單調性的訓練目標。

5. 數據集劃分

這部分也是我們隊伍的一個關鍵提分點,能從87.6提升到87.8,當然在我剛87.x分段時,能提升4個千分點。

我們知道複賽A榜訓練集和測試集是連續的,即10號-22號訓練集,23號為測試集。複賽B榜則是不連續的,沒有給出23號的標籤,直接預測24號。

面對這種“跨天”預測,難度是非常大的,因為日期越近的信息是越與當天相近的,因此前一天的信息是非常重要的。

所以我們利用“遠程監督”的方式,就是利用現有的標註數據,即10-22號數據,訓練一個模型,給未標註數據(23號數據)進行標註,然後再將10-22與23號合併成訓練集進行訓練,預測最終的結果。

特徵工程

1. 特徵提取思路

提取思路主要從兩部分考慮,歷史信息和整體信息,更細緻些就是前一天、最近五天、五折交叉統計和除當天外所有天的統計特徵。

接下來我們構造了四個基礎特徵,大部分的統計特徵都是圍繞着這四個來構造的。當然我這裏還考慮了商品ID和賬户ID的構造,代碼如下:

# 構造基本特徵
for col in ['aid','goods_id','account_id']:
result = logs.groupby([col,'day'], as_index=False)['isExp'].agg({
col+'_cnts' : 'count',
col+'_sums' : 'sum',
col+'_rate' : 'mean'
})
result[col+'_negs'] = result[col+'_cnts'] - result[col+'_sums']
data = data.merge(result, how='left', on=[col,'day'])

2. 如何構造新廣吿的特徵

初賽A 總廣吿:1954 舊廣吿: 1361 新廣吿:593 新廣吿佔比:30.348%

初賽B 總廣吿:3750 舊廣吿: 1382 新廣吿:2368 新廣吿佔比:63.147%

上面是對初賽新舊廣吿的統計,當然複賽也存在大量的新廣吿,複賽B榜新舊廣吿基本55開。新廣吿是沒有歷史信息的,所以如何構造新廣吿的特徵,對新廣吿進行歷史和整體性的描述成了提分的關鍵。

這裏我進行了模糊構造,雖然我們不知道新廣吿的歷史信息,但是我們知道廣吿賬户ID下面所包含舊廣吿的歷史信息。因此,將廣吿賬户ID與舊廣吿的廣吿竟勝率進行組合,可以構造出廣吿賬户ID下廣吿竟勝率的均值/中位數等。這樣我們就可以得到了新廣吿在廣吿賬户ID下廣吿竟勝率的統計值。

這裏可以構造前一天、最近五天、五折交叉統計和除當天外所有天等統計特徵。

3. 進一步擴展

經過上面的構造,可以得到很多新構造的統計特徵,可以是前一天的、最近五天的,或者五折交叉統計的。我把這些值成為“假數值”,相對的就是“真數值”,即每天我們都知道廣吿的競爭總次數(從10-24號數據,包括測試集)。將假數值和真數值進行交叉,如廣吿競爭勝率(假)*廣吿競爭總數(真),這樣就能得到的更接近真實值的特徵。

4. word2vec和deepwalk

word2vec

這裏我們提取了用户的曝光記錄,並將其轉化為文本序列,然後使用word2vec算法對廣吿進行嵌入,就可以得到關於廣吿ID的embedding,或者商品ID的embedding。

具體構建文本序列方式,首先是對日誌數據按天進行排序,然後是按天構建uid的行為序列並轉化為文本。代碼如下:

#log日誌數據,pivot主鍵(uid),f(aid)
sentence=[]
dic={}
day=0
log=log.sort_values(by='request_day')
log['day']=log['request_day']

for item in log[['day',pivot,f]].values:
if day!=item[0]:
for key in dic:
sentence.append(dic[key])
dic={}
day=item[0]
try:
dic[item[1]].append(str(int(item[2])))
except:
dic[item[1]]=[str(int(item[2]))]
for key in dic:
sentence.append(dic[key])

接下來就是構建廣吿ID的embedding向量,代碼如下:

model = Word2Vec(sentence, size=L, window=10, min_count=1, workers=10,iter=10)

values=set(log[f].values)
w2v=[]

for v in values:
try:
a=[int(v)]
a.extend(model[str(v)])
w2v.append(a)
except:
pass

out_df=pd.DataFrame(w2v)
names=[f]

這裏不僅可以構造uid到廣吿id,還可以是uid到商品id,uid到賬户id。

DeepWalk

在推薦場景下,數據對象之間更多呈現的是圖結構。典型的場景是由用户行為數據生成的和廣吿的全局關係圖。這個時候word2vec就不能很好的展現這層關係,所以我們選擇了Graph Embeding的方式,具體的使用了DeepWalk,可以將用户的曝光記錄轉化為關係圖。這裏引用阿里論文中的一張圖,來展現DeepWalk的算法流程:

DeepWalk的算法流程(引自阿里論文)

第一步:構建用户的行為序列

第二步:我們基於這些行為序列構建了物品關係圖,可以看出,物品A,B之間的邊產生的原因就是因為用户U1先後購買了物品A和物品B,所以產生了一條由A到B的有向邊。如果後續產生了多條相同的有向邊,則有向邊的權重被加強。在將所有用户行為序列都轉換成物品相關圖中的邊之後,全局的物品相關圖就建立起來了。

第三步:採用隨機遊走的方式隨機選擇起始點,重新產生物品序列。

第四步:最終將這些物品序列輸入word2vec模型,生成最終的物品Embedding向量

具體對應代碼如下:

https://github.com/guoday/Tencent2019_Preliminary_Rank1st

這裏有兩個問題:

1. 只有在日誌中曝光過的廣吿才會有相應的嵌入向量,通過廣吿有無嵌入向量,會泄露了無曝光廣吿的標籤

2. 測試數據中存在曝光非0但無嵌入向量的廣吿,這在訓練集中是不存在的,導致訓練測試不一致

這裏我們給出瞭解決方法,即隨機掩蓋掉5%廣吿的嵌入向量,保證訓練集中也能出現無曝光的廣吿。

模型介紹

輸入部分分為四組,分別是類別特徵、經過Key-Value Memory處理的稠密特徵、Word2Vec和DeepWalk得到了embedding向量。然後進入Batch Norm Layer,最後是MLP層。

壓縮交互網絡CIN

我們使用了壓縮交互網絡(CIN),它考慮了以下因素:

(1)交互是在向量層次上應用的,而不是在位層次上;

(2)高階特徵交互是明確測量的;

(3)網絡的複雜度不會隨着相互作用的程度。

每個維度上的外積用於特徵交互。張量  是進一步學習的中間結果。

具體的可以看論文xDeepFM:

xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems

鏈接:

http://xueshu.baidu.com/usercenter/paper/show?paperid=6cd34089953a68a3f89066228cb2bdd5&site;=xueshu_se

Key-Value Memory

這裏將介紹鍵值存儲(Key-Value Memory)的神經模型實現浮點數到向量的映射。

具體步驟:

(1)Key addressing部分,尋址過程,此處用到softmax函數。計算上述選出的每個memory的概率值。 

(2)Attention部分,對於不同的Key addression部分的重要性不同,所以使用attention給予不同的權重概率。

(3)Value reading部分,對上一個步驟的概率下進行加權求和得到答案信息。 

規則統計

先讓我們進行一些基本的數據分析,這裏看的是歷史曝光數據。

一個廣吿在不同的廣吿位上有不同的勝率
同廣吿的請求數在不同的日期存在差異

可以看出對於同一個廣吿在不同廣吿位上有着不同的勝率,並且在不同的日期,它的請求數也存在很大的差異。針對這兩點因素,也就能構造出統計方式來計算廣吿日曝光量。

其中,  表示廣吿在廣吿位  上的歷史勝率。  表示廣吿在廣吿位 上發出的請求總數。

這裏我們對比了三種計算方法,可以看出,直接用歷史曝光填充效果最差。接下來就是用競爭勝率*請求數,分數會高很多,更近一步就是按廣吿位分開計算,達到最好的效果。這裏是在驗證集上進行的計算。

歷史勝率  的具體計算方式

先來看一張圖

不同天數填充在驗證集上的得分

最直觀的感受就是,離預測當天越近的數據準確度越高,所以相應的權重也應該越大。這樣我們就可以給歷史每天不同的權重,然後進行加權。就可以得到歷史勝率的計算公式,如下:

權重  計算方式

我們提出了三種計算方法:

方式1:

方式2: 

方式3: 

這裏使用線性搜索尋找最優參數(  ),依據驗證集分數來確定最優參數的選擇。

在最優參數下三總方式在驗證集上的得分對比

這裏我們對三種權重計算方式進行對比,並添加最後一直接填充作為對照實驗。可以明顯看出方法三在驗證集上的效果最好。

最終融合

這裏使用兩種融合方式,分別是算術平均和幾何平均。

算術平均:

幾何平均: 

由於  的評分規則,算術平均會使融合的結果偏大,如:

顯然不符合  的直覺,越小的值對評分影響越大,算術平均會導致更大的誤差。所以選擇幾何平均,能夠使結果偏向小值,如下:

模型、規則以及不同融合方式驗證集得分對比:

更細緻的融合方式:

模型和規則在不同的轉化類型上的得分
  • 無論是模型還是規則,預測結果在不同的轉化類型上得分差異都很大

  • 模型和規則在不同的轉化類型上得分也存在差 異,上圖表示了模型和規則在不同轉化類型上的表現。

根據模型和規則在不同轉化類型上的得分表現,調整權重值,線上可以獲得0.5個千的提升 。

結果分析

可以看出LightGBM單模既可以得到第一名的成績,不過,為了追求更高的分數,我們團隊也做了更多的嘗試。

主要創新

提出了一種基於Key-Value Memory的浮點數映射成向量的方法

  • 相較於直接使用浮點數,該方法保留更多的語義信息

  • 相較於分桶並作為類別特徵的方法,該方法的相鄰向量具有相關性

  • 相較於數值×向量的方法,該方法具有非線性的特點

解決Word2Vec和DeepWalk等無監督學習造成的數據泄露問題

  • 充分利用了曝光日誌記錄,基於用户行為對廣吿進行聚類

問題思考

本次比賽雖然使用到出價,但並沒有將出價作為特徵輸入模型中。不同的出價其廣吿的競爭力會有所不同,將直接影響了曝光量,因此出價是非常重要的特徵。

  • 加入約束條件保證模型的單調性

  • 設計出價單調遞增的模型,如輸出為 

本次比賽並沒有用到用户屬性相關數據,根據廣吿投放人羣信息,或許可以獲得更多有用的內容。

總結

本文提供2019年騰訊廣吿算法大賽冠軍的代碼分享。

github:

https://github.com/guoday/Tencent2019_Preliminary_Rank1st

作者公眾號:kaggle競賽寶典,一個用心分享算法的地方!



●編號880,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

Python編程

更多推薦25個技術類公眾微信

涵蓋:程序人生、算法與數據結構、黑客技術與網絡安全、大數據技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、數據庫、運維等。

    已同步到看一看
    在看



    熱點新聞