本文主要是為了講解xDeepFM的框架茬缩,及如何用tensorflow去實(shí)現(xiàn)主干部分择卦,如果需要直接拆箱可用,可以參考:xDeepFM醉鳖,我的部分代碼也來(lái)自于其中捡硅,這邊主要是和大家一起對(duì)比著看下,xDeepFM到底做了哪些事情盗棵?我的工程實(shí)現(xiàn)代碼等待我司項(xiàng)目上線穩(wěn)定后開(kāi)源壮韭。
XDeepFM到底extreme在哪里北发?
首先,我在做論壇帖子推薦的時(shí)候遇到這么一個(gè)問(wèn)題(問(wèn)題真實(shí)泰涂,問(wèn)題內(nèi)容純屬虛構(gòu))鲫竞,用戶A:帶有如下標(biāo)簽[籃球、足球逼蒙、健身]从绘,用戶B:帶有如下標(biāo)簽[籃球,電腦是牢,蔡徐坤]僵井,在使用deepfm做精排的時(shí)候,常常會(huì)把A和B所看的內(nèi)容互相推薦驳棱,很明顯批什,A是運(yùn)動(dòng)達(dá)人,而B(niǎo)是二次元達(dá)人社搅,這樣推薦是存在很大問(wèn)題的驻债。
我在處理的時(shí)候,采取了兩種套路:
- 改變Memorization為attention網(wǎng)絡(luò)形葬,強(qiáng)化feature直接的聯(lián)系合呐,讓B中的電腦與蔡徐坤進(jìn)行綁定,而不是讓籃球電腦蔡徐坤進(jìn)行混合綁定笙以,讓Memorization去記憶的時(shí)候進(jìn)行權(quán)重傾斜
- Memorization通常為低階特征交互淌实,那我就升高階數(shù),svm告訴我們猖腕,在越高的維度上我們?cè)接锌赡馨褦?shù)據(jù)集進(jìn)行越離散的切分拆祈,XDeepFM就相當(dāng)于把DeepFM中的1維+2維,變成了1維+2維+(l+1)維特征組合
XDeepFM如何實(shí)現(xiàn)的倘感?
網(wǎng)上大多數(shù)版本都是甩出的正方體圖+公式放坏,我覺(jué)得很不清晰,這邊就不貼了老玛,我直接貼代碼及解釋?zhuān)?/p>
nn_input = tf.reshape(nn_input, shape=[-1, int(field_num), self.hparams.dim])
我們知道轻姿,無(wú)論是deepfm還是XDeepFM在初始化的時(shí)候,都會(huì)把feature進(jìn)行onehotencoding后向量化逻炊,然后再壓縮成一個(gè)[batch,dim*
FIELD_COUNT]的input互亮,這邊只是將其還原成網(wǎng)上常見(jiàn)的正方體的形式:[batch,field_num,dim]
split_tensor0 = tf.split(hidden_nn_layers[0], self.hparams.dim * [1], 2)
這個(gè)就是XDeepFM最有意思的地方,我們正常理解都是把特征按照不同的特征進(jìn)行切分組合余素,在XDeepFM它卻按照dim進(jìn)行切開(kāi)然后進(jìn)行點(diǎn)擊豹休,而且是外積,所以大家難以理解的就在這邊桨吊,用數(shù)據(jù)來(lái)說(shuō)就是:
tf.convert_to_tensor(np.arange(1,13).reshape(2,2,3),dtype=tf.int32)
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]], dtype=int32)
切割完為:
[array([[[ 1],
[ 4]],
[[ 7],
[10]]], dtype=int32),
array([[[ 2],
[ 5]],
[[ 8],
[11]]], dtype=int32),
array([[[ 3],
[ 6]],
[[ 9],
[12]]], dtype=int32)]
這邊沒(méi)有按照[1,2,3]這種方式去切分威根,而是按照[[1],[4]]這種方式凤巨,在dim上進(jìn)行切分。
dot_result_m = tf.matmul(split_tensor0, split_tensor, transpose_b=True)
這一步也是很有意思洛搀,沒(méi)有常規(guī)意義上做點(diǎn)擊dot敢茁,做的外積,生生的把向量鋪開(kāi)了:
array([[[[ 1, 4],
[ 4, 16]],
[[ 49, 70],
[ 70, 100]]],
[[[ 4, 10],
[ 10, 25]],
[[ 64, 88],
[ 88, 121]]],
[[[ 9, 18],
[ 18, 36]],
[[ 81, 108],
[108, 144]]]], dtype=int32)
把剛才的[[1],[4]]按照外積的形式去處理了留美,并得到了一個(gè)[bacth_size,dim,field_nums[0] *
field_nums[-1]]的形式彰檬。再通過(guò)[1,field_nums[-1] * field_nums[0],output]進(jìn)行卷積處理相當(dāng)于在dim的每一維上進(jìn)行融合,融合的是所有特征dim的每一維谎砾。最后才sum_pooling操作逢倍,外接一個(gè)輸出層壓縮到1個(gè)輸出值。
CIN為什么要搞這么復(fù)雜景图,比deepfm好在哪较雕?
看代碼就知道,剛才CIN的過(guò)程可以進(jìn)行N次挚币,這里面的l代表著從0層開(kāi)始了l次的特征組合亮蒋,比起deepfm的2次,對(duì)特征的組合更深妆毕,能解釋的空間更為復(fù)雜
CIN很完美嗎慎玖?
雖然作者在論文剛開(kāi)始的時(shí)候就吐槽了DCN的低端,認(rèn)為DCN其實(shí)就是init層的N次交叉设塔,但是我認(rèn)為DCN的殘差項(xiàng)保證了特征的1~l+1特征都有,而CIN中去除了殘差項(xiàng)远舅,雖然更快了闰蛔,但是相當(dāng)于丟棄了1~l階特征的組合結(jié)果,不見(jiàn)得可取
實(shí)操經(jīng)驗(yàn)分享图柏?
- XDeepFM比起循環(huán)神經(jīng)網(wǎng)絡(luò)是快了不止一個(gè)量級(jí)的序六,基于DNN+CNN這種架構(gòu)下,并行策略保證它很高效的執(zhí)行速度蚤吹。
- 如果是baseline的嘗試例诀,建議優(yōu)先DeepFM,絕大多數(shù)情況下裁着,DeepFM已經(jīng)滿足了我們的基礎(chǔ)需求繁涂。我司實(shí)際項(xiàng)目的效果下XDeepFM在離線數(shù)據(jù)集上目前也只有0.1%的提升,但是代碼量及code review的壓力卻大了很多二驰。
- 在走XDeepFM的路子時(shí)候扔罪,可以考慮把DeepFM中的Memorization層加入DIN中的Attention,完全不遜色桶雀。
更多代碼內(nèi)容歡迎follow我的個(gè)人Github矿酵,如果有任何算法唬复、代碼疑問(wèn)都?xì)g迎通過(guò)郵箱發(fā)消息給我。