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ì)于交叉特征,只能寫兩重循環(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)看看效果吧:
-
編碼
+關(guān)注
關(guān)注
6文章
957瀏覽量
54933 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4345瀏覽量
62911 -
代碼
+關(guān)注
關(guān)注
30文章
4823瀏覽量
68955
原文標(biāo)題:推薦系統(tǒng)遇上深度學(xué)習(xí)(二)--FFM模型理論和實(shí)踐
文章出處:【微信號(hào):AI_shequ,微信公眾號(hào):人工智能愛(ài)好者社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論