最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

快看 esmm 模型理論與實踐

2023-02-18 18:06 作者:算法全棧之路  | 我要投稿

快看 esmm 模型理論與實踐

近兩年,多目標(biāo)學(xué)習(xí) (Multi-Task Learning,MTL) 甚囂塵上。除了因為國內(nèi)外大量互聯(lián)網(wǎng)公司業(yè)務(wù)與算法優(yōu)化步入深水區(qū),需要更復(fù)雜的網(wǎng)絡(luò)來應(yīng)對復(fù)雜的業(yè)務(wù)之外,很多MTL 深度學(xué)習(xí)模型結(jié)合業(yè)務(wù)場景進(jìn)行不斷突破的設(shè)計思想讓人驚艷不已,更是不由得讓人敬佩算法工程師( 我的同行們~ ) 的創(chuàng)新能力,而算法 的 魅力 也正在此處。在眾多的MTL模型中, 阿里巴巴在2018年發(fā)表的論文《Entire Space Multi-Task Model: An E?ective Approach for Estimating Post-Click Conversion Rate》提出的ESMM網(wǎng)絡(luò)絕對是具有極大借鑒意義的。

通常理解,MTL任務(wù)可以分為兩種,串行與并行。多個任務(wù)之間有比較強(qiáng)關(guān)聯(lián)的, 例如點(diǎn)擊率與轉(zhuǎn)化率,這一種通常我們可以使用esmm這種串行的任務(wù)進(jìn)行關(guān)系遞進(jìn)性與相關(guān)性建模。而對于多個任務(wù)之間相對比較獨(dú)立的,例如點(diǎn)擊率與用戶是否給出評論的評論率,通??梢赃x擇mmoe這種并行的任務(wù)進(jìn)行相關(guān)性與沖突性的建模。本文主要圍繞esmm網(wǎng)絡(luò)模型展開闡述。

一般來說,我們使用機(jī)器學(xué)習(xí)模型構(gòu)建推薦系統(tǒng)(包括推薦廣告及自然量) 給用戶推薦商品,會考慮很多的排序因子。結(jié)合用戶行為漏斗來看,用戶進(jìn)入某場景會經(jīng)歷:看到商品(曝光 view)-> 點(diǎn)擊 (click) ?-> 購買 ?(conversion) -> 好評 (good_comment ) 直到后面更深層次的次日留存,一周留存等。

我們知道,點(diǎn)擊率 ( CTR , 表示view->click, 為view/click )代表用戶對接觸到某個item并表現(xiàn)出對它的初步的興趣,想點(diǎn)進(jìn)去看看更詳細(xì)的內(nèi)容,這反映的是用戶參與度指標(biāo)。 而轉(zhuǎn)化率(CVR,表示click-> conversions,為conversion/click)則表示用戶對推薦的內(nèi)容很滿意,并且愿意為之付出更重的行為,好比是收藏轉(zhuǎn)發(fā)或則付費(fèi)購買,這反映的是用戶滿意度指標(biāo)。

從上面我們可以知道,轉(zhuǎn)化率與點(diǎn)擊率有一定關(guān)系,但是他們之間又沒有絕對的關(guān)系。但是對這個問題,很多人有一個先入為主的認(rèn)知即:點(diǎn)擊率高的item轉(zhuǎn)化率也高,這是不正確的。我們可以想象一個場景,假如百度的feed中,一個推薦的條目展示頭圖很丑或標(biāo)題相對不具有吸引力,它被某個用戶點(diǎn)擊的概率很低,但此條目內(nèi)容本身質(zhì)量很高且完美符合該用戶偏好,如果用戶點(diǎn)進(jìn)去,那么它被用戶轉(zhuǎn)化的概率就極高。這是一個被完全低估了的item,其內(nèi)在往往遠(yuǎn)高于外在。

書接上文,通常如果我們在排序的時候,想要提高這些真正優(yōu)秀的item的排名,就需要考慮點(diǎn)擊率CTR, CVR(click-> conversions)等因素, 甚至在有些場景會更進(jìn)一步的考慮好評率、次留率、周留率等更多因素。

因此,我們的排序策略公式的基礎(chǔ)格式可以長這樣:

` Rank_Score = CTR ?CVR * other_Rank_info ?`

我們根據(jù)公式最后得到的 Rank_Score 進(jìn)行排序即可。

注意: ?我們想要在推薦排序的時候考慮什么因素,就把該因素建模成一個排序因子融入到排序公式里就可以了。

當(dāng)然,我們可以使用多個模型來分別建模CTR、CVR等,每個目標(biāo)打出一個分?jǐn)?shù)。雖然這種方式具有很強(qiáng)的靈活性,但是也有很多的局限性。例如離線算法優(yōu)化與迭代上的人力維護(hù)成本、線上多模型預(yù)估路徑太長帶來的時間成本、數(shù)據(jù)不能通用以及預(yù)估場景帶來的偏差等諸多問題。

此時,就需要本文所要介紹的ESMM模型出場了。

(1) esmm 模型基礎(chǔ)理解

從論文中可以看出,esmm 網(wǎng)絡(luò)的最初提出,是為了解決電商購物場景中給用戶推薦商品時 CVR 模型預(yù)估估不準(zhǔn)的問題。當(dāng)然我們沒有說絕對的什么模型就能估計的準(zhǔn)確,但是算法的逐漸優(yōu)化,不就是讓模型越來越接近事情的本質(zhì)的過程嗎。

(1.1) CVR相對CTR任務(wù)的不同

不同于CTR預(yù)估,CVR預(yù)估面臨的問題要更加復(fù)雜。這其中主要的包括:

(1) Data Sparsity (DS) 數(shù)據(jù)稀疏 。cvr訓(xùn)練樣本不足,轉(zhuǎn)化的樣本遠(yuǎn)遠(yuǎn)少于點(diǎn)擊樣本,導(dǎo)致模型的embeding訓(xùn)練不充分。(通常CTR為1~3%左右,而CVR則隨不同場景差異非常大,購物下載場景一般也為1~10%左右) 。

(2) Sample Selection Bias (SSB) 樣本選擇偏差 。我們以前很多時候訓(xùn)練cvr模型,用點(diǎn)擊轉(zhuǎn)化的作為正樣本,點(diǎn)擊未轉(zhuǎn)化的作為負(fù)樣本,訓(xùn)練好模型去線上預(yù)估打分參與排序公式的計算,勝出的item才能獲得新一輪的曝光機(jī)會。但這中間涉及到一個重要問題: 我們使用的是點(diǎn)擊轉(zhuǎn)化數(shù)據(jù)來作為樣本,而在參與排序的時候,那個時候更在曝光之前,并且需要打分的item和要用的特征數(shù)量級甚至可以達(dá)到全體item候選集。

這就違背了我們使用機(jī)器學(xué)習(xí)模型的一個根本原則:IID原則。 我們訓(xùn)練模型的數(shù)據(jù)和預(yù)估的數(shù)據(jù)不是獨(dú)立同分布的,訓(xùn)練集是預(yù)估集的一個子集,并且是非常少的一個子集。

并且中間涉及著一個問題就是:原始的CVR表示用戶點(diǎn)擊了某個item,他被轉(zhuǎn)化的概率,而對于那些沒有被點(diǎn)擊的item,假設(shè)他們被用戶點(diǎn)擊了,它們是否會被轉(zhuǎn)化? 如果直接使用0作為它們的label,會很大程度上誤導(dǎo)CVR模型的學(xué)習(xí)。

曾經(jīng)作者在國內(nèi)某互聯(lián)網(wǎng)頭部大廠工作的時候,就曾遇到這個問題,當(dāng)時這個問題換了個馬甲就把我坑慘了,整整花了2個星期才解決!?。?/strong>。記得當(dāng)時,這個問題的表現(xiàn)形式是:離線實驗的時候auc/gauc/logloss均是正常,但是線上的時候預(yù)估打分均為0。經(jīng)過拆分打印網(wǎng)絡(luò)各個部分輸出,最終發(fā)現(xiàn)是離線訓(xùn)練的模型里,保存了30多G的embeding文件,但是到線上的時候,依然有大量的sparse Id 找不到,embeding lookup不到。深究其根本原因,不就是因為樣本選擇偏差導(dǎo)致的離線模型沒見過線上模型的大量特征嗎。

可見樣本選擇偏差不光會導(dǎo)致預(yù)估估計偏差的問題,甚至也會以由于沒見過特征而導(dǎo)致打分完全不可用的形式出現(xiàn)。從這個問題,我們也可以收獲到一個經(jīng)驗:當(dāng)我們理論上論證到一個問題的時候,可以更進(jìn)一步的想想這個理論問題在實踐中會有什么樣的表現(xiàn)形式出現(xiàn)。正著推很容易的問題,逆反過來就要了我的小命了。遇到理論問題在實踐上多思考一步,不然就會像我一葉障目不見泰山,換了馬甲就不認(rèn)識了,此處只想大書三個字,坑坑坑?。?!

(3) 反饋延遲 。 這個問題算是cvr和ctr相比最眾所周知的一個不同了。 一般點(diǎn)擊反饋到曝光,也就1s以內(nèi),而轉(zhuǎn)化到點(diǎn)擊的反饋,隨場景不同甚至可以達(dá)到1~10天不等。

在淘寶京東等購物場景還好,轉(zhuǎn)化數(shù)據(jù)是閉環(huán)內(nèi)部可達(dá)的,用戶購買數(shù)據(jù)是內(nèi)部可以拿到的; 而在像是小米華為的應(yīng)用商店里App的激活率預(yù)估,激活數(shù)據(jù)則依賴于第三方廠家或廣告主的回傳,這種數(shù)據(jù)延遲都非常嚴(yán)重,2~5天是常態(tài),這就給我們預(yù)估轉(zhuǎn)化率帶來了很大的困難。

對于反饋延遲這個問題,問題是客觀存在的,并且并不能從根本上讓他不產(chǎn)生,但是緩解的方法有很多。其中就包括:

(1) 設(shè)計模型對反饋延遲進(jìn)行建模。

(2) 構(gòu)建滾動式數(shù)據(jù)pipline回跑全量回傳數(shù)據(jù)模型 + ?2天延遲并且去掉依然未回傳數(shù)據(jù)訓(xùn)練的模型。

(3) 對CVR進(jìn)行分時間段加權(quán)校準(zhǔn)。

(1.2) esmm 如何解決問題?

書接上文,針對上文提到三點(diǎn)困難中的前兩點(diǎn),esmm從模型從模型結(jié)構(gòu)設(shè)計與損失函數(shù)設(shè)計上就分別做出了很優(yōu)秀的解答。

首先是數(shù)據(jù)稀疏,既然你因為稀疏數(shù)據(jù)導(dǎo)致embeding訓(xùn)練不好,那我就讓你和數(shù)據(jù)不那么稀疏的模型共享embeing查找表, 這個從模型結(jié)構(gòu)的設(shè)計來解決。

而對于樣本選擇偏差,既然常規(guī)訓(xùn)練點(diǎn)擊轉(zhuǎn)化率cvr的方式有偏差,那就引入曝光下點(diǎn)擊且轉(zhuǎn)化率來解決,這個從模型的損失函數(shù)loss設(shè)計來解決 。在論文中,曝光下點(diǎn)擊且轉(zhuǎn)化率也叫CTCVR。

抱歉,這里我自己造了一個詞叫曝光下點(diǎn)擊且轉(zhuǎn)化率 , 對應(yīng)的CTR就是曝光下點(diǎn)擊CTR。因為CTR本就是曝光到點(diǎn)擊的概率,所以曝光CTR就是CTR。但是原始的CVR 是點(diǎn)擊到轉(zhuǎn)化的概率,而曝光下點(diǎn)擊且轉(zhuǎn)化率 是曝光到點(diǎn)擊且轉(zhuǎn)化的概率,這就顯示出其中的區(qū)別了。 更準(zhǔn)確的說,CVR預(yù)估模型的本質(zhì),不是預(yù)測" item被點(diǎn)擊,然后被轉(zhuǎn)化"的概率(CTCVR),而是“假設(shè)item已經(jīng)被點(diǎn)擊,那么它被轉(zhuǎn)化”的概率(CVR)。

基于此,我們來看下論文里對點(diǎn)擊率(CTR)、轉(zhuǎn)化率(CVR)、點(diǎn)擊然后轉(zhuǎn)化(CTCVR)三者關(guān)聯(lián)的建模:

其中: ?z,y ?分別表示 conversion 和 click 。

我們可以看到: pCVR和pCTCVR這里均是條件概率。 不同的是: pCVR 是曝光且點(diǎn)擊的條件下,轉(zhuǎn)化的概率,注意這里點(diǎn)擊是已經(jīng)確定的,作為條件概率的條件存在; 而pCTCVR 是 曝光的情況下,點(diǎn)擊并且轉(zhuǎn)化的條件概率, 預(yù)估的是點(diǎn)擊和轉(zhuǎn)化同時發(fā)生的概率。注意區(qū)別兩者的不同。

注意到,在全部樣本空間中,CTR對應(yīng)的label為click,而CTCVR對應(yīng)的label為click & conversion,他們均是在已知曝光的情況下進(jìn)行預(yù)估的,分別是曝光下點(diǎn)擊率和曝光下點(diǎn)擊且轉(zhuǎn)化率,所以 這兩個任務(wù)均是可以使用全部樣本的,因此也就不在存在樣本選擇偏差的問題了。

我們應(yīng)該可以看到 , esmm 的整體邏輯結(jié)構(gòu)是基于 “乘法” 的關(guān)系設(shè)計的: pCTCVR=pCVR * pCTR 。同樣我們是不是也可以從公司反推出 pCVR = pCTCVR / pCTR 這個結(jié)果呢 ?例如分別獨(dú)自的去訓(xùn)練 CTCVR任務(wù)模型和CTR任務(wù)模型,然后兩者相除得到pCVR的分?jǐn)?shù)? ?當(dāng)然這樣做也可以,但它就有個明顯的缺點(diǎn): 真實場景預(yù)測出來的pCTR、pCTCVR值都非常小,“除”的方式容易造成數(shù)值上的不穩(wěn)定,而且有可能是的pCVR的值大于1,這是明顯不符合實際情況的。

(2) esmm 模型詳解

上文我們已經(jīng)說明了esmm從理論上解決了CVR模型存在的數(shù)據(jù)稀疏和樣本選擇偏差的問題,下面我們看下該網(wǎng)絡(luò)的結(jié)構(gòu):

從上面 esmm 模型的結(jié)構(gòu)圖中,我們可以看到,esmm 模型以pCVR為主任務(wù),并且引入了CTR和CTCVR兩個輔助任務(wù)。 CVR網(wǎng)絡(luò)和CTR網(wǎng)絡(luò) ,兩個子網(wǎng)絡(luò)可以具有相同的網(wǎng)絡(luò)結(jié)構(gòu),當(dāng)然也可以不同。甚至兩者用的特征可以有部分相同,也可以有部分不同。相同的那部分可以共享embeding起到 知識遷移 的作用。

其中,pCVR 和 pCTR 共享embeding查找表 , 這樣 pCVR 任務(wù)就可以共享pCTR任務(wù)的樣本數(shù)據(jù)來訓(xùn)練embeding ,可以解決數(shù)據(jù)稀疏的問題。如果兩個模型用的特征有部分不同的話,每個子網(wǎng)絡(luò)依然輸入全部,只是取值的時候各取所需的獨(dú)有的字段特征即可。不同任務(wù)即有公共共享的特征,也有各自任務(wù)所專享的獨(dú)有的特征。

一種不錯的實踐是將用戶行為序列(用戶歷史以及實時行為序列)進(jìn)行共享,對pCTR和pCVR任務(wù)均是有幫助的。當(dāng)然我們也可以靈活調(diào)整自己各個子任務(wù)所需要的特征數(shù)據(jù)與結(jié)構(gòu)。 在線上進(jìn)行打分的時候,我們可以通過模型同時得到pCTR、pCVR這兩個子網(wǎng)絡(luò)的輸出以及pCTCVR。

而訓(xùn)練esmm模型,我們輸入的樣本數(shù)據(jù)除了訓(xùn)練pCTR和pCTCVR任務(wù)的特征之外, 每行樣本是帶有2列標(biāo)簽的,分別是 click與conversion 。

這里我們選擇曝光了有點(diǎn)擊的作為 pCTR任務(wù) 的正樣本,曝光了沒點(diǎn)擊的作為PCTR任務(wù)的負(fù)樣本; 而選擇曝光了有點(diǎn)擊的并且有轉(zhuǎn)化的作為PCTCVR任務(wù)的正樣本,其余的作為 pCTCVR任務(wù) 的負(fù)樣本。

注意:這里對于pCTCVR任務(wù)的 樣本構(gòu)造 在多啰嗦兩句。 (1) 沒有點(diǎn)擊的,自然沒有轉(zhuǎn)化,在實際業(yè)務(wù)數(shù)據(jù)中會發(fā)現(xiàn)部分沒有點(diǎn)擊而有轉(zhuǎn)化的數(shù)據(jù),可以直接丟棄( 這里包括click 為 0, 而 conversion為0和1,conversion=1直接丟棄,conversion=0為負(fù)樣本)。(2) 有點(diǎn)擊的,如果沒有轉(zhuǎn)化的樣本,自然是負(fù)樣本(這里包括click 為 0, 而 conversion為0 為負(fù)樣本)。(3) 而有點(diǎn)擊并且有轉(zhuǎn)化的,為正樣本(這里包括click 為 1, 而 conversion為1 為正樣本)。

因此兩列 label 四個去值中,對于pCTCVR任務(wù)來說,一種情況丟棄,2種情況為負(fù),唯有一種情況為為正。

從上圖中也可以看出: pCVR只是一個中間變量,沒有顯式的監(jiān)督標(biāo)簽,所以我們也稱是隱式訓(xùn)練CVR。而 pCTR 任務(wù)和 pCTCVR 任務(wù)則是esmm 在整個樣本空間要估計的。

所以 esmm 的 ?loss 函數(shù)包括兩部分:CTR損失函數(shù)和CTCVR損失,但不包括CVR任務(wù)的損失函數(shù)。因此 ,整個訓(xùn)練任務(wù)的損失函數(shù)為:

其中, θctr和θcvr分別是CTR網(wǎng)絡(luò)和CVR網(wǎng)絡(luò)的參數(shù),L(?)是交叉熵?fù)p失函數(shù)。在下文,會介紹一種修改該損失以求得轉(zhuǎn)化率和點(diǎn)擊率相互靠攏,效果雙雙提升的自定義損失函數(shù)的方法。

(3) ?esmm 模型實踐與思考

記得以前看抖音上一段字節(jié)跳動張一鳴的采訪,他曾經(jīng)說:我們對一個事物的認(rèn)知,才是我們真正的競爭力。對事物認(rèn)知越深刻,你就越有競爭力。

書接上文,esmm 模型用模型結(jié)構(gòu)設(shè)計和損失函數(shù)設(shè)計解決了實際業(yè)務(wù)中模型優(yōu)化的痛點(diǎn)問題,這對于我們在運(yùn)用機(jī)器學(xué)習(xí)模型到自身的業(yè)務(wù)的過程中,是不是也可以受到很多啟發(fā)呢?

首先我們需要對自身業(yè)務(wù)進(jìn)行深入思考,很重要的一點(diǎn)就是: 我們訓(xùn)練模型的目的主要是為了學(xué)習(xí)的什么,什么是模型中比較重要的點(diǎn),各個部分起的作用是什么,現(xiàn)有模型中間還遺留有什么問題,我們突出什么點(diǎn)可以帶來什么收益,我們可以選擇什么方式進(jìn)行設(shè)計(比如模型結(jié)構(gòu)和損失函數(shù))可以達(dá)到我們突出這個特性的目的? 只有真正的的結(jié)合業(yè)務(wù)深入思考了,才能逐漸驅(qū)動我們?nèi)ゲ粩嗤诰蚵癫卦谄渲械摹敖鹱印?,最終創(chuàng)新出令人驚艷的設(shè)計。

我們可以發(fā)覺一點(diǎn)就是:很多優(yōu)秀的模型設(shè)計都是在為了更好的學(xué)習(xí)用戶的某一特性,而引入一個新的輔助模塊,構(gòu)建一個輔助任務(wù),并添加輔助損失到最后的任務(wù)損失中以突出該模塊的訓(xùn)練。無論是當(dāng)前介紹的 esmm還是阿里巴巴的另一篇講 dien 的論文,又或則是 將EGES的圖用于冷啟動任務(wù)的模型設(shè)計均是如此。當(dāng)一個模塊沒辦法在原始結(jié)構(gòu)中的loss回傳過程中得到很好訓(xùn)練的時候,我們是否可以構(gòu)建新的輔助損失專門針對當(dāng)前模塊進(jìn)行訓(xùn)練呢?

我們在 企業(yè)級機(jī)器學(xué)習(xí) Pipline - 排序模型 這篇文章里曾經(jīng)說過數(shù)據(jù)和模型是“一體兩面“的,這里要再次強(qiáng)調(diào)一下:首先就是樣本設(shè)計與網(wǎng)絡(luò)結(jié)構(gòu)需要相契合,隨著思考的深入,我們需要不斷的去調(diào)整網(wǎng)絡(luò)結(jié)構(gòu)以及靈活構(gòu)建輸入模型的數(shù)據(jù),我們需要知道什么形式的特征可以用什么方式去建模學(xué)習(xí)以及哪種方式可以得到好的效果。如果在某個場景沒有足夠的先驗知識,那就開始瘋狂實驗吧~

俗話說的好啊~ “工欲善其事,必先利其器“,寫代碼調(diào)模型也是如此。隨心所欲的調(diào)整模型,這不光要求我們對DNN模型內(nèi)部各個部分的數(shù)據(jù)形式以及如何流動這些場景是非常清晰的,更要求我們對一些模型構(gòu)建工具,例如:tensorflowpytorch 以及如何進(jìn)行自定義損失要真的進(jìn)行深刻的理解與應(yīng)用,最終達(dá)到如臂使指的 “無矩“ 的大成境界。代碼是最能表達(dá)一個程序員思想的東西,有問題多看源碼哈,會受益匪淺的~

閑話就說到這里吧 ~

因為esmm模型其實是在平滑CTR與CVR模型之間的很多差異,進(jìn)行知識遷移與優(yōu)劣勢互補(bǔ)。在上文提到的以前的文章 企業(yè)級機(jī)器學(xué)習(xí) Pipline - 排序模型 ?中效果評估模塊,我們也曾經(jīng)提到要監(jiān)控線上模型打出的CTR和CVR打分等情況。 在我們的實際業(yè)務(wù)中,我們可以仔細(xì)分析下我們要推薦的item,根據(jù)線上用戶反饋數(shù)據(jù)圈出來一些分類。例如:在item中,ctr與cvr均很高的真正優(yōu)質(zhì)item, ctr高而cvr低的華而不實的item,ctr低而cvr高的有外表平庸內(nèi)涵豐富的item,以及ctr與cvr雙低的劣質(zhì)item。

我們可以仔細(xì)分析下,針對各個分類的item,設(shè)計符合各自特點(diǎn)的模型,在模型優(yōu)化迭代已經(jīng)進(jìn)入深水區(qū)的時候,應(yīng)該是一個不錯的優(yōu)化點(diǎn)。

在上文我們已經(jīng)說過,線上esmm我們可以同時得到pCTR、pCVR兩個子網(wǎng)絡(luò)的輸出以及pCTCVR的值,當(dāng)然pCVR也可以取pCTCVR除以pCTR得到,但是因為數(shù)據(jù)過小出發(fā)產(chǎn)生波動太過于劇烈。記得以前有一篇講蘑菇街esm實踐的文章中,說他們有設(shè)計了一種 CVR的值域控制的公式,感興趣的可以去詳細(xì)了解一下。

如果直接用pCTCVR替換CTR打分,會普遍遇到 CVR 效果有提升而CTR效果下降的問題。其實不光ctr跌和cvr漲,ctr和cvr 各自均有上升和下跌兩種趨勢,組合起來就有四種情況。這種上線之后,一個任務(wù)變好一個任務(wù)變差的情況,就涉及到多目標(biāo)任務(wù)中多個目標(biāo)的融合情況,俗稱為多目標(biāo)學(xué)習(xí)中的 “蹺蹺板“ 問題。

對于多任務(wù)融合問題,我們可以在損失函數(shù)中 設(shè)定pCTR任務(wù)和pCVR任務(wù)的Loss所占的比重 ,例如:

model.compile(
????optimizer=opt,
????loss=['binary_crossentropy',?'binary_crossentropy'],
????loss_weights=[1.0,?1.0],
????metrics=[
????????tf.keras.metrics.AUC(),
????????tf.keras.metrics.BinaryAccuracy()
)

這種方式是將該比重比例固定的寫法,這個值可能需要根據(jù)各自的業(yè)務(wù)數(shù)據(jù)進(jìn)行適當(dāng)?shù)恼{(diào)整,選擇出最合適的值。

Google2016年在youtube 視頻推薦場景提出的 "Deep Neural Network for YouTube Recommendation" 論文中,有提出一種基于視頻觀看時長進(jìn)行Loss 加權(quán) 的損失函數(shù)也給我們帶來了很大的啟發(fā),那我們是否可以根據(jù)某個item的歷史某一段時間內(nèi)的CTR和CVR對pCTR以及pCTCVR任務(wù)進(jìn)行加權(quán)呢? 當(dāng)然這里只是提出一種可能,可以選擇別的合適的指標(biāo)個性化的對兩個任務(wù)進(jìn)行Loss調(diào)整。

當(dāng)然,我們也可以使用一個可學(xué)習(xí)的權(quán)重參數(shù),讓模型自己去動態(tài)的學(xué)習(xí)如何適配二者任務(wù)的權(quán)重。這樣也是有利有弊吧,利就是過度平滑,沒有自己設(shè)定值的那么生硬,而弊就是過于靈活,當(dāng)我們想限制性調(diào)整的時候就需要換一種方式了。

另一種方式就是在兩個任務(wù)之間,添加 gate 門網(wǎng)絡(luò) ,不從損失函數(shù)入手,而從底層embedign之后的某些layer入手,這樣就和 MMOE網(wǎng)絡(luò) 有互通的地方了,感興趣的可以自己下去嘗試哈。

最值得稱道 的是另一種方式:假設(shè)我們上線了esmm模型之后,點(diǎn)擊下降而轉(zhuǎn)化上升,那我們是不是可以 構(gòu)建一條曝光到轉(zhuǎn)化的通路呢,在損失函數(shù)中加上一項: Loss(y, F(xi,Pcvr)),強(qiáng)制 要求模型的轉(zhuǎn)化和點(diǎn)擊相互靠攏,以求達(dá)到兩個任務(wù)均有正向收益的雙贏局面 。

我不知道這些點(diǎn)我說清楚了沒有,感興趣的可以在公眾號留言加群交流哦~

(4)代碼時光

開篇先吼一嗓子: talk is cheap , show me the code !!! ?

哎, 終于再次寫到代碼時光了,寫一篇小作文比上班一整天還累?。。?!

對于esmm模型,網(wǎng)上找了一圈,均是基于tensorflow 的estimator接口和別的一些方法實現(xiàn)的,而作者非常推崇的一種做法是使用 tensorflow 的featureColumn接口來處理特征,而使用 tf keras 的中階API來進(jìn)行網(wǎng)絡(luò)結(jié)構(gòu)的定制,這樣 兼顧了特征處理的便利性與網(wǎng)絡(luò)結(jié)構(gòu)靈活的可定制性 。

但是這種組合的代碼作者搜遍的網(wǎng)絡(luò)居然沒有找到適合自己的,于是狠下心肝了兩天,自己手寫了一套算法代碼,在這里分享給大家,有問題歡迎留言討論 ~

#?歡迎關(guān)注微信公眾號:?算法全棧之路?
#?-*-?coding:?utf-8?-*-

import?numpy?as?np
import?os
import?argparse
import?tensorflow?as?tf
import?log_util
import?params_conf
from?date_helper?import?DateHelper
import?data_consumer

os.environ['TF_CPP_MIN_LOG_LEVEL']?=?'2'
os.environ["CUDA_VISIBLE_DEVICES"]?=?"-1"


def?init_args():
????parser?=?argparse.ArgumentParser(description='dnn_demo')
????parser.add_argument("--mode",?default="train")
????parser.add_argument("--train_data_dir")
????parser.add_argument("--model_output_dir")
????parser.add_argument("--cur_date")
????parser.add_argument("--log",?default="../log/tensorboard")
????parser.add_argument('--use_gpu',?default=False,?type=bool)
????args?=?parser.parse_args()
????return?args


def?get_feature_column_map():
????key_hash_size_map?=?{
????????"adid":?10000,
????????"site_id":?10000,
????????"site_domain":?10000,
????????"site_category":?10000,
????????"app_id":?10000,
????????"app_domain":?10000,
????????"app_category":?1000,
????????"device_id":?1000,
????????"device_ip":?10000,
????????"device_type":?10,
????????"device_conn_type":?10,
????}

????feature_column_map?=?dict()
????for?key,?value?in?key_hash_size_map.items():
????????feature_column_map.update({key:?tf.feature_column.categorical_column_with_hash_bucket(
????????????key,?hash_bucket_size=value,?dtype=tf.string)})

????return?feature_column_map


def?build_embeding():
????feature_map?=?get_feature_column_map()
????feature_inputs_list?=?[]

????def?get_field_emb(categorical_col_key,?emb_size=16,?input_shape=(1,)):
????????#?print(categorical_col_key)
????????embed_col?=?tf.feature_column.embedding_column(feature_map[categorical_col_key],?emb_size)
????????#?層名字不可以相同,不然會報錯
????????dense_feature_layer?=?tf.keras.layers.DenseFeatures(embed_col,?name=categorical_col_key?+?"_emb2dense")
????????feature_layer_inputs?=?dict()

????????#?input和DenseFeatures必須要用dict來存和聯(lián)合使用,深坑?。?!
????????feature_layer_inputs[categorical_col_key]?=?tf.keras.Input(shape=(1,),?dtype=tf.dtypes.string,
???????????????????????????????????????????????????????????????????name=categorical_col_key)
????????#?保存供?model?input?使用.
????????feature_inputs_list.append(feature_layer_inputs[categorical_col_key])
????????return?dense_feature_layer(feature_layer_inputs)

????embeding_map?=?{}
????for?key,?value?in?feature_map.items():
????????#?print("key:"?+?key)
????????embeding_map.update({key:?get_field_emb(key)})

????return?embeding_map,?feature_inputs_list


def?build_dnn_net(net,?params_conf,?name="ctr"):
????#?可以在下面接入殘差網(wǎng)絡(luò)
????for?i,?dnn_hidden_size?in?enumerate(params_conf.DNN_HIDDEN_SIZES):??#?DNN_HIDDEN_SIZES?=?[512,?128,?64]
????????net?=?tf.keras.layers.Dense(dnn_hidden_size,?activation="relu",?name="overall_dense_%s_%s"?%?(i,?name))(net)
????return?net


def?build_model(emb_map,?inputs_list):
????#?需要特殊處理和交叉的特征,以及需要短接殘差的特征,可以單獨(dú)拿出來
????define_list?=?[]
????adid_emb?=?emb_map["adid"]
????device_id_emd?=?emb_map["device_id"]
????ad_x_device?=?tf.multiply(adid_emb,?device_id_emd)

????define_list.append(ad_x_device)

????#?直接可以拼接的特征
????common_list?=?[]
????for?key,?value?in?emb_map.items():
????????common_list.append(value)

????#?embeding?contact
????net?=?tf.keras.layers.concatenate(define_list?+?common_list)

????#?構(gòu)建?ctr?和?cvr?各自的?網(wǎng)絡(luò)
????ctr_output?=?build_dnn_net(net,?params_conf,?"ctr")
????cvr_output?=?build_dnn_net(net,?params_conf,?"cvr")

????ctr_pred?=?tf.keras.layers.Dense(1,?activation="sigmoid",?name="ctr_sigmoid")(ctr_output)
????cvr_pred?=?tf.keras.layers.Dense(1,?activation="sigmoid",?name="cvr_sigmoid")(cvr_output)

????#?CTCVR?=?CTR?*?CVR
????ctcvr_pred?=?tf.keras.layers.Multiply(name="ctcvr_pred")([ctr_pred,?cvr_pred])

????#?注意:?這里定義了網(wǎng)絡(luò)結(jié)構(gòu)的骨架,設(shè)定了網(wǎng)絡(luò)的輸入輸出,而在model.fit那里輸入的參數(shù)個數(shù),必須和這里的輸入輸出相對應(yīng),否則會報維度對不上的錯誤。
????#?因為esmm?的模型結(jié)構(gòu)限定了每條樣本有兩個標(biāo)簽,對應(yīng)了這里的[ctr_pred,?ctcvr_pred]?兩個輸出,可以求的損失。
????model?=?tf.keras.models.Model(inputs=inputs_list,?outputs=[ctr_pred,?ctcvr_pred])

????return?model,?ctr_pred,?ctcvr_pred


def?train():
????output_root_dir?=?"{}/{}/{}".format(params_conf.BASE_DIR,?args.model_output_dir,?args.cur_date)
????os.mkdir(output_root_dir)
????model_full_output_dir?=?os.path.join(output_root_dir,?"model_savedmodel")
????#?print?info?log
????log_util.info("model_output_dir:?%s"?%?model_full_output_dir)

????#?重置keras的狀態(tài)
????tf.keras.backend.clear_session()
????log_util.info("start?train...")
????train_date_list?=?DateHelper.get_date_range(DateHelper.get_date(-1,?args.cur_date),
????????????????????????????????????????????????DateHelper.get_date(0,?args.cur_date))
????train_date_list.reverse()
????print("train_date_list:"?+?",".join(train_date_list))

????#?load?data?from?tf.data,兼容csv?和?tf_record
????train_set,?test_set?=?data_consumer.get_dataset(args.train_data_dir,?train_date_list,
????????????????????????????????????????????????????get_feature_column_map().values())
????#?train_x,?train_y?=?train_set

????log_util.info("get?train?data?finish?...")

????emb_map,?feature_inputs_list?=?build_embeding()
????log_util.info("build?embeding?finish...")

????#?構(gòu)建模型
????model,?ctr_pred,?ctcvr_pred?=?build_model(emb_map,?feature_inputs_list)
????log_util.info("build?model?finish...")

????def?my_sparse_categorical_crossentropy(y_true,?y_pred):
????????return?tf.keras.sparse_categorical_crossentropy(y_true,?y_pred,?from_logits=True)

????opt?=?tf.keras.optimizers.Adam(params_conf.LEARNING_RATE)

????#?注意這里設(shè)定了2個損失分別對應(yīng)[ctr_pred,?ctcvr_pred]?這兩個任務(wù)
????#?loss_weights=[1.0,?1.0]這種方式可以固定的調(diào)整2個任務(wù)的loss權(quán)重。
????model.compile(
????????optimizer=opt,
????????loss=['binary_crossentropy',?'binary_crossentropy'],
????????loss_weights=[1.0,?1.0],
????????metrics=[
????????????tf.keras.metrics.AUC(),
????????????tf.keras.metrics.BinaryAccuracy(),
????????????tf.keras.metrics.Recall(),
????????????tf.keras.metrics.Precision()]
????)
????model.summary()
????#?tf.keras.utils.plot_model(model,?'multi_input_and_output_model.png',?show_shapes=True,?dpi=150)

????print("start?training")

????#?需要設(shè)置profile_batch=0,tensorboard頁面才會一直保持更新
????tensorboard_callback?=?tf.keras.callbacks.TensorBoard(
????????log_dir=args.log,
????????histogram_freq=1,
????????write_graph=True,
????????update_freq=params_conf.BATCH_SIZE?*?200,
????????embeddings_freq=1,
????????profile_batch=0)

????#?定義衰減式學(xué)習(xí)率
????class?LearningRateExponentialDecay:

????????def?__init__(self,?initial_learning_rate,?decay_epochs,?decay_rate):
????????????self.initial_learning_rate?=?initial_learning_rate
????????????self.decay_epochs?=?decay_epochs
????????????self.decay_rate?=?decay_rate

????????def?__call__(self,?epoch):
????????????dtype?=?type(self.initial_learning_rate)
????????????decay_epochs?=?np.array(self.decay_epochs).astype(dtype)
????????????decay_rate?=?np.array(self.decay_rate).astype(dtype)
????????????epoch?=?np.array(epoch).astype(dtype)
????????????p?=?epoch?/?decay_epochs
????????????lr?=?self.initial_learning_rate?*?np.power(decay_rate,?p)
????????????return?lr

????lr_schedule?=?LearningRateExponentialDecay(
????????params_conf.INIT_LR,?params_conf.LR_DECAY_EPOCHS,?params_conf.LR_DECAY_RATE)

????#?該回調(diào)函數(shù)是學(xué)習(xí)率調(diào)度器
????lr_schedule_callback?=?tf.keras.callbacks.LearningRateScheduler(lr_schedule,?verbose=1)

????#?訓(xùn)練
????#?注意這里的train_set?可以使用for循環(huán)迭代,tf?2.0默認(rèn)支持eager模式
????#?這里的train_set?包含兩部分,第一部分是feature,第二部分是label?(?click,?click?&?conversion)
????#?注意這里是?feature,(click,?click?&?conversion),第二項是tuple,不能是數(shù)組或列表[],不然報數(shù)據(jù)維度不對,坑死爹了。
????model.fit(
????????train_set,
????????#?train_set["labels"],
????????#?validation_data=test_set,
????????epochs=params_conf.NUM_EPOCHS,??#?NUM_EPOCHS?=?10
????????steps_per_epoch=params_conf.STEPS_PER_EPHCH,
????????#?validation_steps=params_conf.VALIDATION_STEPS,
????????#
????????#?callbacks=[tensorboard_callback,?lr_schedule_callback]
????)

????#?模型保存
????tf.keras.models.save_model(model,?model_full_output_dir)

????#?tf.saved_model.save(model,?model_full_output_dir)
????print("save?saved_model?success")


if?__name__?==?"__main__":
????print(tf.__version__)
????tf.compat.v1.disable_eager_execution()

????#?run?tensorboard:
????#?tensorboard?--port=8008?--host=localhost?--logdir=../log
????args?=?init_args()
????if?args.mode?==?"train":
????????train()

到這里,快看esmm模型理論與實踐就寫完成了,歡迎留言交流 ~

碼字不易,覺得有收獲就點(diǎn)贊、分享、再看三連吧~

歡迎掃碼關(guān)注作者的公眾號: 算法全棧之路

- END -


快看 esmm 模型理論與實踐的評論 (共 條)

分享到微博請遵守國家法律
陕西省| 阿坝县| 绿春县| 华蓥市| 乌拉特前旗| 太原市| 凤翔县| 莱西市| 瑞昌市| 临沭县| 九龙坡区| 建水县| 松原市| 页游| 台东县| 太和县| 唐海县| 贵定县| 伊川县| 孙吴县| 临猗县| 西和县| 信阳市| 玉田县| 铜鼓县| 彩票| 神农架林区| 五台县| 郎溪县| 汕尾市| 鹤壁市| 安平县| 景东| 临桂县| 湘阴县| 蓬莱市| 宁城县| 尉犁县| 荃湾区| 凭祥市| 湘潭县|