推薦系統(tǒng)遇上深度學(xué)習(xí)(二)--FFM模型理論和實(shí)踐

推薦系統(tǒng)遇上深度學(xué)習(xí)系列:
推薦系統(tǒng)遇上深度學(xué)習(xí)(一)--FM模型理論和實(shí)踐:http://www.reibang.com/p/152ae633fb00

1胯杭、FFM理論

在CTR預(yù)估中败京,經(jīng)常會(huì)遇到one-hot類型的變量脐雪,one-hot類型變量會(huì)導(dǎo)致嚴(yán)重的數(shù)據(jù)特征稀疏的情況,為了解決這一問(wèn)題悦荒,在上一講中液斜,我們介紹了FM算法。這一講我們介紹一種在FM基礎(chǔ)上發(fā)展出來(lái)的算法-FFM(Field-aware Factorization Machine)狱杰。

FFM模型中引入了類別的概念,即field枫吧。還是拿上一講中的數(shù)據(jù)來(lái)講浦旱,先看下圖:

在上面的廣告點(diǎn)擊案例中宇色,“Day=26/11/15”九杂、“Day=1/7/14”、“Day=19/2/15”這三個(gè)特征都是代表日期的宣蠕,可以放到同一個(gè)field中例隆。同理,Country也可以放到一個(gè)field中抢蚀。簡(jiǎn)單來(lái)說(shuō)镀层,同一個(gè)categorical特征經(jīng)過(guò)One-Hot編碼生成的數(shù)值特征都可以放到同一個(gè)field,包括用戶國(guó)籍,廣告類型唱逢,日期等等吴侦。

在FFM中,每一維特征 xi坞古,針對(duì)其它特征的每一種field fj备韧,都會(huì)學(xué)習(xí)一個(gè)隱向量 v_i,fj。因此痪枫,隱向量不僅與特征相關(guān)织堂,也與field相關(guān)。也就是說(shuō)奶陈,“Day=26/11/15”這個(gè)特征與“Country”特征和“Ad_type"特征進(jìn)行關(guān)聯(lián)的時(shí)候使用不同的隱向量易阳,這與“Country”和“Ad_type”的內(nèi)在差異相符,也是FFM中“field-aware”的由來(lái)吃粒。

假設(shè)樣本的 n個(gè)特征屬于 f個(gè)field潦俺,那么FFM的二次項(xiàng)有 nf個(gè)隱向量。而在FM模型中徐勃,每一維特征的隱向量只有一個(gè)黑竞。FM可以看作FFM的特例,是把所有特征都?xì)w屬到一個(gè)field時(shí)的FFM模型疏旨。根據(jù)FFM的field敏感特性很魂,可以導(dǎo)出其模型方程。

可以看到檐涝,如果隱向量的長(zhǎng)度為 k遏匆,那么FFM的二次參數(shù)有 nfk 個(gè),遠(yuǎn)多于FM模型的 nk個(gè)谁榜。此外幅聘,由于隱向量與field相關(guān),F(xiàn)FM二次項(xiàng)并不能夠化簡(jiǎn)窃植,其預(yù)測(cè)復(fù)雜度是 O(kn^2)帝蒿。

下面以一個(gè)例子簡(jiǎn)單說(shuō)明FFM的特征組合方式。輸入記錄如下:

這條記錄可以編碼成5個(gè)特征巷怜,其中“Genre=Comedy”和“Genre=Drama”屬于同一個(gè)field葛超,“Price”是數(shù)值型,不用One-Hot編碼轉(zhuǎn)換延塑。為了方便說(shuō)明FFM的樣本格式绣张,我們將所有的特征和對(duì)應(yīng)的field映射成整數(shù)編號(hào)。

那么关带,F(xiàn)FM的組合特征有10項(xiàng)侥涵,如下圖所示。

其中,紅色是field編號(hào)芜飘,藍(lán)色是特征編號(hào)务豺。

2、FFM實(shí)現(xiàn)細(xì)節(jié)

這里講得只是一種FFM的實(shí)現(xiàn)方式嗦明,并不是唯一的冲呢。

損失函數(shù)
FFM將問(wèn)題定義為分類問(wèn)題,使用的是logistic loss招狸,同時(shí)加入了正則項(xiàng)

什么敬拓,這是logisitc loss?第一眼看到我是懵逼的裙戏,邏輯回歸的損失函數(shù)我很熟悉啊乘凸,不是長(zhǎng)這樣的啊累榜?其實(shí)是我目光太短淺了营勤。邏輯回歸其實(shí)是有兩種表述方式的損失函數(shù)的,取決于你將類別定義為0和1還是1和-1壹罚。大家可以參考下下面的文章:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/6340129.html葛作。當(dāng)我們將類別設(shè)定為1和-1的時(shí)候,邏輯回歸的損失函數(shù)就是上面的樣子猖凛。

隨機(jī)梯度下降

訓(xùn)練FFM使用的是隨機(jī)梯度下降方法赂蠢,即每次只選一條數(shù)據(jù)進(jìn)行訓(xùn)練,這里還有必要補(bǔ)一補(bǔ)梯度下降的知識(shí)辨泳,梯度下降是有三種方式的虱岂,截圖取自參考文獻(xiàn)3:

總給人一種怪怪的感覺(jué)。batch為什么是全量的數(shù)據(jù)呢菠红,哈哈第岖。

3、tensorflow實(shí)現(xiàn)代碼

本文代碼的github地址:
https://github.com/princewen/tensorflow_practice/tree/master/recommendation-FFM-Demo

這里我們只講解一些細(xì)節(jié)试溯,具體的代碼大家可以去github上看:

生成數(shù)據(jù)
這里我沒(méi)有找到合適的數(shù)據(jù)蔑滓,就自己產(chǎn)生了一點(diǎn)數(shù)據(jù),數(shù)據(jù)涉及20維特征遇绞,前十維特征是一個(gè)field键袱,后十維是一個(gè)field:

def gen_data():
    labels = [-1,1]
    y = [np.random.choice(labels,1)[0] for _ in range(all_data_size)]
    x_field = [i // 10 for i in range(input_x_size)]
    x = np.random.randint(0,2,size=(all_data_size,input_x_size))
    return x,y,x_field

定義權(quán)重項(xiàng)
在ffm中,有三個(gè)權(quán)重項(xiàng)试读,首先是bias杠纵,然后是一維特征的權(quán)重,最后是交叉特征的權(quán)重:

def createTwoDimensionWeight(input_x_size,field_size,vector_dimension):
    weights = tf.truncated_normal([input_x_size,field_size,vector_dimension])

    tf_weights = tf.Variable(weights)

    return tf_weights

def createOneDimensionWeight(input_x_size):
    weights = tf.truncated_normal([input_x_size])
    tf_weights = tf.Variable(weights)
    return tf_weights

def createZeroDimensionWeight():
    weights = tf.truncated_normal([1])
    tf_weights = tf.Variable(weights)
    return tf_weights

計(jì)算估計(jì)值
估計(jì)值的計(jì)算這里不能項(xiàng)FM一樣先將公式化簡(jiǎn)再來(lái)做钩骇,對(duì)于交叉特征,只能寫(xiě)兩重循環(huán),所以對(duì)于特別多的特征的情況下倘屹,真的計(jì)算要爆炸呀银亲!

def inference(input_x,input_x_field,zeroWeights,oneDimWeights,thirdWeight):
    """計(jì)算回歸模型輸出的值"""

    secondValue = tf.reduce_sum(tf.multiply(oneDimWeights,input_x,name='secondValue'))

    firstTwoValue = tf.add(zeroWeights, secondValue, name="firstTwoValue")

    thirdValue = tf.Variable(0.0,dtype=tf.float32)
    input_shape = input_x_size

    for i in range(input_shape):
        featureIndex1 = I
        fieldIndex1 = int(input_x_field[I])
        for j in range(i+1,input_shape):
            featureIndex2 = j
            fieldIndex2 = int(input_x_field[j])
            vectorLeft = tf.convert_to_tensor([[featureIndex1,fieldIndex2,i] for i in range(vector_dimension)])
            weightLeft = tf.gather_nd(thirdWeight,vectorLeft)
            weightLeftAfterCut = tf.squeeze(weightLeft)

            vectorRight = tf.convert_to_tensor([[featureIndex2,fieldIndex1,i] for i in range(vector_dimension)])
            weightRight = tf.gather_nd(thirdWeight,vectorRight)
            weightRightAfterCut = tf.squeeze(weightRight)

            tempValue = tf.reduce_sum(tf.multiply(weightLeftAfterCut,weightRightAfterCut))

            indices2 = [I]
            indices3 = [j]

            xi = tf.squeeze(tf.gather_nd(input_x, indices2))
            xj = tf.squeeze(tf.gather_nd(input_x, indices3))

            product = tf.reduce_sum(tf.multiply(xi, xj))

            secondItemVal = tf.multiply(tempValue, product)

            tf.assign(thirdValue, tf.add(thirdValue, secondItemVal))

    return tf.add(firstTwoValue,thirdValue)

定義損失函數(shù)
損失函數(shù)我們就用邏輯回歸損失函數(shù)來(lái)算,同時(shí)加入正則項(xiàng):

lambda_w = tf.constant(0.001, name='lambda_w')
lambda_v = tf.constant(0.001, name='lambda_v')

zeroWeights = createZeroDimensionWeight()

oneDimWeights = createOneDimensionWeight(input_x_size)

thirdWeight = createTwoDimensionWeight(input_x_size,  # 創(chuàng)建二次項(xiàng)的權(quán)重變量
                                       field_size,
                                       vector_dimension)  # n * f * k

y_ = inference(input_x, trainx_field,zeroWeights,oneDimWeights,thirdWeight)

l2_norm = tf.reduce_sum(
    tf.add(
        tf.multiply(lambda_w, tf.pow(oneDimWeights, 2)),
        tf.reduce_sum(tf.multiply(lambda_v, tf.pow(thirdWeight, 2)),axis=[1,2])
    )
)

loss = tf.log(1 + tf.exp(-input_y * y_)) + l2_norm

train_step = tf.train.GradientDescentOptimizer(learning_rate=lr).minimize(loss)

訓(xùn)練
接下來(lái)就是訓(xùn)練了纽匙,每次只用喂一個(gè)數(shù)據(jù)就好:

input_x_batch = trainx[t]
input_y_batch = trainy[t]
predict_loss,_, steps = sess.run([loss,train_step, global_step],
                         feed_dict={input_x: input_x_batch, input_y: input_y_batch})

跑的是相當(dāng)?shù)穆耱穑覀儊?lái)看看效果吧:

參考文章

1、https://tech.meituan.com/deep-understanding-of-ffm-principles-and-practices.html
2烛缔、https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/6340129.html
3馏段、https://www.cnblogs.com/pinard/p/5970503.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市践瓷,隨后出現(xiàn)的幾起案子院喜,更是在濱河造成了極大的恐慌,老刑警劉巖晕翠,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喷舀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡淋肾,警方通過(guò)查閱死者的電腦和手機(jī)硫麻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)樊卓,“玉大人拿愧,你說(shuō)我怎么就攤上這事÷刀” “怎么了赶掖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)七扰。 經(jīng)常有香客問(wèn)我奢赂,道長(zhǎng),這世上最難降的妖魔是什么颈走? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任膳灶,我火速辦了婚禮,結(jié)果婚禮上立由,老公的妹妹穿的比我還像新娘轧钓。我一直安慰自己,他們只是感情好锐膜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布毕箍。 她就那樣靜靜地躺著,像睡著了一般道盏。 火紅的嫁衣襯著肌膚如雪而柑。 梳的紋絲不亂的頭發(fā)上文捶,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音媒咳,去河邊找鬼粹排。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涩澡,可吹牛的內(nèi)容都是我干的顽耳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼妙同,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼射富!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起粥帚,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胰耗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后茎辐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體宪郊,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年拖陆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弛槐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡依啰,死狀恐怖乎串,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情速警,我是刑警寧澤叹誉,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站闷旧,受9級(jí)特大地震影響长豁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忙灼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一匠襟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧该园,春花似錦酸舍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至双妨,卻和暖如春淮阐,著一層夾襖步出監(jiān)牢的瞬間叮阅,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工枝嘶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帘饶,地道東北人哑诊。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓群扶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親镀裤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竞阐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容