阿里近幾年公開的推薦領(lǐng)域算法有許多肴裙,既有傳統(tǒng)領(lǐng)域的探索如MLR算法,還有深度學(xué)習(xí)領(lǐng)域的探索如entire -space multi-task model艰亮,Deep Interest Network等颤陶,同時(shí)跟清華大學(xué)合作展開了強(qiáng)化學(xué)習(xí)領(lǐng)域的探索娃圆,提出了MARDPG算法。
上一篇挠乳,我們介紹了MLR算法挂脑,通過分而治之的思想改進(jìn)了傳統(tǒng)的LR算法,使其能夠擬合更復(fù)雜的線性關(guān)系欲侮。這一篇崭闲,我們來簡單理解和實(shí)現(xiàn)一下阿里在去年提出的另一個(gè)重要的推薦系統(tǒng)模型-深度興趣網(wǎng)絡(luò)(DIN,Deep Interest Network). 該方法由蓋坤大神領(lǐng)導(dǎo)的阿里媽媽的精準(zhǔn)定向檢索及基礎(chǔ)算法團(tuán)隊(duì)提出,充分利用/挖掘用戶歷史行為數(shù)據(jù)中的信息來提高CTR預(yù)估的性能威蕉。
1刁俭、背景
深度學(xué)習(xí)在CTR預(yù)估領(lǐng)域已經(jīng)有了廣泛的應(yīng)用,常見的算法比如Wide&Deep韧涨,DeepFM等牍戚。這些方法一般的思路是:通過Embedding層,將高維離散特征轉(zhuǎn)換為固定長度的連續(xù)特征虑粥,然后通過多個(gè)全聯(lián)接層如孝,最后通過一個(gè)sigmoid函數(shù)轉(zhuǎn)化為0-1值,代表點(diǎn)擊的概率娩贷。即Sparse Features -> Embedding Vector -> MLPs -> Sigmoid -> Output.
這種方法的優(yōu)點(diǎn)在于:通過神經(jīng)網(wǎng)絡(luò)可以擬合高階的非線性關(guān)系第晰,同時(shí)減少了人工特征的工作量。
不過族檬,阿里的研究者們通過觀察收集到的線上數(shù)據(jù)允耿,發(fā)現(xiàn)了用戶行為數(shù)據(jù)中有兩個(gè)很重要的特性:
Diversity:用戶在瀏覽電商網(wǎng)站的過程中顯示出的興趣是十分多樣性的。
Local activation: 由于用戶興趣的多樣性茧泪,只有部分歷史數(shù)據(jù)會(huì)影響到當(dāng)次推薦的物品是否被點(diǎn)擊甜熔,而不是所有的歷史記錄圆恤。
這兩種特性是密不可分的。
舉個(gè)簡單的例子腔稀,觀察下面的表格:
Diversity體現(xiàn)在年輕的母親的歷史記錄中體現(xiàn)的興趣十分廣泛盆昙,涵蓋羊毛衫、手提袋焊虏、耳環(huán)弱左、童裝、運(yùn)動(dòng)裝等等炕淮。而愛好游泳的人同樣興趣廣泛,歷史記錄涉及浴裝跳夭、旅游手冊涂圆、踏水板、馬鈴薯币叹、冰激凌润歉、堅(jiān)果等等。
Local activation體現(xiàn)在颈抚,當(dāng)我們給愛好游泳的人推薦goggle(護(hù)目鏡)時(shí)踩衩,跟他之前是否購買過薯片、書籍贩汉、冰激凌的關(guān)系就不大了驱富,而跟他游泳相關(guān)的歷史記錄如游泳帽的關(guān)系就比較密切。
針對上面提到的用戶行為中存在的兩種特性匹舞,阿里將其運(yùn)用于自身的推薦系統(tǒng)中褐鸥,推出了深度興趣網(wǎng)路DIN,接下來赐稽,我們就一起來看一下模型的一些實(shí)現(xiàn)細(xì)節(jié)叫榕,然后我們會(huì)給出一個(gè)簡化版的tensorflow實(shí)現(xiàn)。
2姊舵、模型設(shè)計(jì)
整體框架
我們先來看一下推薦系統(tǒng)的整體框架:
整個(gè)流程可以描述為:
1.檢查用戶歷史行為數(shù)據(jù)
2.使用matching module產(chǎn)生候選ads晰绎。
3.通過ranking module做point-wise的排序,即得到每個(gè)候選ads的點(diǎn)擊概率括丁,并根據(jù)概率排序得到推薦列表荞下。
4.記錄下用戶在當(dāng)前展示廣告下的反應(yīng)(點(diǎn)擊與否),作為label。
特征設(shè)計(jì)
本文將所涉及到的特征分為四個(gè)部分:用戶特征锄弱、用戶行為特征考蕾、廣告特征、上下文特征会宪,具體如下:
其中肖卧,用戶行為特征是multi-hot的,即多值離散特征掸鹅。針對這種特征塞帐,由于每個(gè)涉及到的非0值個(gè)數(shù)是不一樣的,常見的做法就是將id轉(zhuǎn)換成embedding之后巍沙,加一層pooling層葵姥,比如average-pooling,sum-pooling句携,max-pooling榔幸。DIN中使用的是weighted-sum,其實(shí)就是加權(quán)的sum-pooling矮嫉,權(quán)重經(jīng)過一個(gè)activation unit計(jì)算得到削咆。這里我們后面還會(huì)再介紹到。
BaseModel
在介紹DIN之前蠢笋,我們先來看一下一個(gè)基準(zhǔn)模型拨齐,結(jié)構(gòu)如下:
這里element-wise的意思其實(shí)就是元素級別的加減,同時(shí)昨寞,可不要忽略廣播的存在喲瞻惋。一個(gè)元素和一個(gè)向量相乘,也可以看作element-wise的援岩,因?yàn)檫@個(gè)元素會(huì)廣播成和向量一樣的長度嘛歼狼,嘻嘻。
可以看到享怀,Base Model首先吧one-hot或multi-hot特征轉(zhuǎn)換為特定長度的embedding蹂匹,作為模型的輸入,然后經(jīng)過一個(gè)DNN的part凹蜈,得到最終的預(yù)估值限寞。特別地,針對multi-hot的特征仰坦,做了一次element-wise+的操作履植,這里其實(shí)就是sum-pooling,這樣悄晃,不管特征中有多少個(gè)非0值玫霎,經(jīng)過轉(zhuǎn)換之后的長度都是一樣的凿滤!
Deep Interest Network
Base Model有一個(gè)很大的問題,它對用戶的歷史行為是同等對待的庶近,沒有做任何處理翁脆,這顯然是不合理的。一個(gè)很顯然的例子鼻种,離現(xiàn)在越近的行為反番,越能反映你當(dāng)前的興趣。因此叉钥,對用戶歷史行為基于Attention機(jī)制進(jìn)行一個(gè)加權(quán)罢缸,阿里提出了深度興趣網(wǎng)絡(luò)(Deep Interest Network),先來看一下模型結(jié)構(gòu):
Attention機(jī)制簡單的理解就是投队,針對不同的廣告枫疆,用戶歷史行為與該廣告的權(quán)重是不同的。假設(shè)用戶有ABC三個(gè)歷史行為敷鸦,對于廣告D息楔,那么ABC的權(quán)重可能是0.8、0.1扒披、0.1值依;對于廣告E,那么ABC的權(quán)重可能是0.3谎碍、0.6、0.1洞焙。這里的權(quán)重蟆淀,就是Attention機(jī)制即上圖中的Activation Unit所需要學(xué)習(xí)的。
為什么要引入這一個(gè)機(jī)制呢澡匪?難道僅僅是通過觀察歷史數(shù)據(jù)拍腦袋決定的么熔任?當(dāng)然不是,如果不用Local activation的話唁情,將會(huì)出現(xiàn)下面的情況:假設(shè)用戶的興趣的Embedding是Vu疑苔,候選廣告的Embedding是Va,用戶興趣和候選的廣告的相關(guān)性可以寫作F(U,A) = Va * Vu甸鸟。如果沒有Local activation機(jī)制的話惦费,那么同一個(gè)用戶對于不同的廣告,Vu都是相同的抢韭。舉例來說薪贫,如果有兩個(gè)廣告A和B,用戶興趣和A刻恭,B的相似性都很高瞧省,那么在Va和Vb連線上的廣告都會(huì)有很高的相似性。這樣的限制使得模型非常難學(xué)習(xí)到有效的用戶和廣告的embedidng表示。
在加入Activation Unit之后鞍匾,用戶的興趣表示計(jì)算如下:
其中交洗,Vi表示behavior id i的嵌入向量,比如good_id,shop_id等橡淑。Vu是所有behavior ids的加權(quán)和构拳,表示的是用戶興趣;Va是候選廣告的嵌入向量梳码;wi是候選廣告影響著每個(gè)behavior id的權(quán)重隐圾,也就是Local Activation。wi通過Activation Unit計(jì)算得出掰茶,這一塊用函數(shù)去擬合暇藏,表示為g(Vi,Va)。
3濒蒋、模型細(xì)節(jié)
3.1 評價(jià)指標(biāo)GAUC
模型使用的評價(jià)指標(biāo)是GAUC盐碱,我們先來看一下GAUC的計(jì)算公式:
我們首先要肯定的是,AUC是要分用戶看的沪伙,我們的模型的預(yù)測結(jié)果瓮顽,只要能夠保證對每個(gè)用戶來說,他想要的結(jié)果排在前面就好了围橡。
假設(shè)有兩個(gè)用戶A和B暖混,每個(gè)用戶都有10個(gè)商品,10個(gè)商品中有5個(gè)是正樣本翁授,我們分別用TA拣播,TB,F(xiàn)A收擦,F(xiàn)B來表示兩個(gè)用戶的正樣本和負(fù)樣本贮配。也就是說,20個(gè)商品中有10個(gè)是正樣本塞赂。假設(shè)模型預(yù)測的結(jié)果大小排序依次為TA泪勒,F(xiàn)A,TB宴猾,F(xiàn)B圆存。如果把兩個(gè)用戶的結(jié)果混起來看,AUC并不是很高仇哆,因?yàn)橛?個(gè)正樣本排在了后面辽剧,但是分開看的話,每個(gè)用戶的正樣本都排在了負(fù)樣本之前税产,AUC應(yīng)該是1怕轿。顯然偷崩,分開看更容易體現(xiàn)模型的效果,這樣消除了用戶本身的差異撞羽。
但是上文中所說的差異是在用戶點(diǎn)擊數(shù)即樣本數(shù)相同的情況下說的阐斜。還有一種差異是用戶的展示次數(shù)或者點(diǎn)擊數(shù),如果一個(gè)用戶有1個(gè)正樣本诀紊,10個(gè)負(fù)樣本谒出,另一個(gè)用戶有5個(gè)正樣本,50個(gè)負(fù)樣本邻奠,這種差異同樣需要消除笤喳。那么GAUC的計(jì)算,不僅將每個(gè)用戶的AUC分開計(jì)算碌宴,同時(shí)根據(jù)用戶的展示數(shù)或者點(diǎn)擊數(shù)來對每個(gè)用戶的AUC進(jìn)行加權(quán)處理杀狡。進(jìn)一步消除了用戶偏差對模型的影響。通過實(shí)驗(yàn)證明贰镣,GAUC確實(shí)是一個(gè)更加合理的評價(jià)指標(biāo)呜象。
3.2 Dice激活函數(shù)
從Relu到PRelu
Relu激活函數(shù)形式如下:
Relu激活函數(shù)在值大于0時(shí)原樣輸出,小于0時(shí)輸出為0碑隆。這樣的話導(dǎo)致了許多網(wǎng)絡(luò)節(jié)點(diǎn)的更新緩慢恭陡。因此又了PRelu,也叫Leaky Relu上煤,形式如下:
這樣休玩,及時(shí)值小于0,網(wǎng)絡(luò)的參數(shù)也得以更新劫狠,加快了收斂速度拴疤。
從PReLU到Dice
盡管對Relu進(jìn)行了修正得到了PRelu,但是仍然有一個(gè)問題嘉熊,即我們認(rèn)為分割點(diǎn)都是0遥赚,但實(shí)際上扬舒,分割點(diǎn)應(yīng)該由數(shù)據(jù)決定阐肤,因此文中提出了Dice激活函數(shù)
Dice激活函數(shù)的全稱是Data Dependent Activation Function,形式如下:
其中讲坎,期望和方差的計(jì)算如下:
可也看到孕惜,每一個(gè)yi對應(yīng)了一個(gè)概率值pi。pi的計(jì)算主要分為兩步:將yi進(jìn)行標(biāo)準(zhǔn)化和進(jìn)行sigmoid變換晨炕。
3.3 自適應(yīng)正則 Adaptive Regularization
CTR中輸入稀疏而且維度高衫画,通常的做法是加入L1、L2瓮栗、Dropout等防止過擬合削罩。但是論文中嘗試后效果都不是很好瞄勾。用戶數(shù)據(jù)符合長尾定律long-tail law,也就是說很多的feature id只出現(xiàn)了幾次弥激,而一小部分feature id出現(xiàn)很多次进陡。這在訓(xùn)練過程中增加了很多噪聲,并且加重了過擬合微服。
對于這個(gè)問題一個(gè)簡單的處理辦法就是:直接去掉出現(xiàn)次數(shù)比較少的feature id趾疚。但是這樣就人為的丟掉了一些信息,導(dǎo)致模型更加容易過擬合以蕴,同時(shí)閾值的設(shè)定作為一個(gè)新的超參數(shù)糙麦,也是需要大量的實(shí)驗(yàn)來選擇的。
因此丛肮,阿里提出了自適應(yīng)正則的做法赡磅,即:
1.針對feature id出現(xiàn)的頻率,來自適應(yīng)的調(diào)整他們正則化的強(qiáng)度腾供;
2.對于出現(xiàn)頻率高的仆邓,給與較小的正則化強(qiáng)度;
3.對于出現(xiàn)頻率低的伴鳖,給予較大的正則化強(qiáng)度节值。
計(jì)算公式如下:
4、效果展示
下圖是對Local Activation效果的一個(gè)展示榜聂,可以看到搞疗,對于候選的廣告是一件衣服的時(shí)候,用戶歷史行為中跟衣服相關(guān)的權(quán)重較高须肆,而非衣服的部分匿乃,權(quán)重較低。
下圖是對使用不同正則項(xiàng)的結(jié)果進(jìn)行的展示豌汇,可以發(fā)現(xiàn)幢炸,使用自適應(yīng)正則的情況下,模型的驗(yàn)證集誤差和驗(yàn)證集GAUC均是最好的拒贱。
下圖對比了Base Model和DIN的實(shí)驗(yàn)結(jié)果宛徊,可以看到,DIN模型在加入Dice激活函數(shù)以及自適應(yīng)正則之后逻澳,模型的效果有了一定的提升:
5闸天、實(shí)戰(zhàn)DIN
本文的github地址為:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-DIN-Demo
參考的github地址為:https://github.com/zhougr1993/DeepInterestNetwork/tree/master/din
這里我們只給出一些模型細(xì)節(jié)的實(shí)現(xiàn),具體的數(shù)據(jù)處理以及其他方面的內(nèi)容大家可以根據(jù)上面兩個(gè)地址進(jìn)行學(xué)習(xí):
數(shù)據(jù)準(zhǔn)備
按照下面的方法下載數(shù)據(jù):
Dice激活函數(shù)
這里實(shí)現(xiàn)的Dice激活函數(shù)沒有根據(jù)上一步的均值方差來計(jì)算這一步的均值方差斜做,而是直接計(jì)算了這個(gè)batch的均值方差苞氮。我們可以根據(jù)計(jì)算出的均值方差對x進(jìn)行標(biāo)準(zhǔn)化(代碼中被注釋掉了),也可以直接調(diào)用batch_normalization來對輸入進(jìn)行標(biāo)準(zhǔn)化瓤逼。
注意的一點(diǎn)是笼吟,alpha也是需要訓(xùn)練的一個(gè)參數(shù)库物。
import tensorflow as tf
def dice(_x,axis=-1,epsilon=0.0000001,name=''):
alphas = tf.get_variable('alpha'+name,_x.get_shape()[-1],
initializer = tf.constant_initializer(0.0),
dtype=tf.float32)
input_shape = list(_x.get_shape())
reduction_axes = list(range(len(input_shape)))
del reduction_axes[axis] # [0]
broadcast_shape = [1] * len(input_shape) #[1,1]
broadcast_shape[axis] = input_shape[axis] # [1 * hidden_unit_size]
# case: train mode (uses stats of the current batch)
mean = tf.reduce_mean(_x, axis=reduction_axes) # [1 * hidden_unit_size]
brodcast_mean = tf.reshape(mean, broadcast_shape)
std = tf.reduce_mean(tf.square(_x - brodcast_mean) + epsilon, axis=reduction_axes)
std = tf.sqrt(std)
brodcast_std = tf.reshape(std, broadcast_shape) #[1 * hidden_unit_size]
# x_normed = (_x - brodcast_mean) / (brodcast_std + epsilon)
x_normed = tf.layers.batch_normalization(_x, center=False, scale=False) # a simple way to use BN to calculate x_p
x_p = tf.sigmoid(x_normed)
return alphas * (1.0 - x_p) * _x + x_p * _x
Activation Unit
這里的輸入有三個(gè),候選廣告queries贷帮,用戶歷史行為keys艳狐,以及Batch中每個(gè)行為的長度。這里為什么要輸入一個(gè)keys_length呢皿桑,因?yàn)槊總€(gè)用戶發(fā)生過的歷史行為是不一樣多的毫目,但是輸入的keys維度是固定的(都是歷史行為最大的長度),因此我們需要這個(gè)長度來計(jì)算一個(gè)mask诲侮,告訴模型哪些行為是沒用的镀虐,哪些是用來計(jì)算用戶興趣分布的。
經(jīng)過以下幾個(gè)步驟得到用戶的興趣分布:
- 將queries變?yōu)楹蚹eys同樣的形狀B * T * H(B指batch的大小沟绪,T指用戶歷史行為的最大長度刮便,H指embedding的長度)
- 通過三層神經(jīng)網(wǎng)絡(luò)得到queries和keys中每個(gè)key的權(quán)重,并經(jīng)過softmax進(jìn)行標(biāo)準(zhǔn)化
- 通過weighted sum得到最終用戶的歷史行為分布
def attention(queries,keys,keys_length):
'''
queries: [B, H]
keys: [B, T, H]
keys_length: [B]
'''
queries_hidden_units = queries.get_shape().as_list()[-1]
queries = tf.tile(queries,[1,tf.shape(keys)[1]])
queries = tf.reshape(queries,[-1,tf.shape(keys)[1],queries_hidden_units])
din_all = tf.concat([queries,keys,queries-keys,queries * keys],axis=-1) # B*T*4H
# 三層全鏈接
d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att')
d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att')
d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att') #B*T*1
outputs = tf.reshape(d_layer_3_all,[-1,1,tf.shape(keys)[1]]) #B*1*T
# Mask
key_masks = tf.sequence_mask(keys_length,tf.shape(keys)[1])
key_masks = tf.expand_dims(key_masks,1) # B*1*T
paddings = tf.ones_like(outputs) * (-2 ** 32 + 1) # 在補(bǔ)足的地方附上一個(gè)很小的值绽慈,而不是0
outputs = tf.where(key_masks,outputs,paddings) # B * 1 * T
# Scale
outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)
# Activation
outputs = tf.nn.softmax(outputs) # B * 1 * T
# Weighted Sum
outputs = tf.matmul(outputs,keys) # B * 1 * H 三維矩陣相乘恨旱,相乘發(fā)生在后兩維,即 B * (( 1 * T ) * ( T * H ))
return outputs
參考文獻(xiàn)
1坝疼、蓋坤演講視頻:http://www.itdks.com/dakalive/detail/3166
2搜贤、論文:Deep Interest Network for Click-Through Rate Prediction
3、github:https://github.com/zhougr1993/DeepInterestNetwork
歡迎關(guān)注個(gè)人公眾號:小小挖掘機(jī)
添加微信sxw2251钝凶,可以拉你進(jìn)入小小挖掘機(jī)技術(shù)交流群喲仪芒!