天天看點

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

1 原文(點選下載下傳)

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

2 FFM模型

FFM(Field-aware Factorization Machine)最初的概念來自Yu-Chin Juan(阮毓欽,畢業于中國台灣大學,現在美國Criteo工作)與其比賽隊員,是他們借鑒了來自Michael Jahrer的論文[14]中的field概念提出了FM的更新版模型。通過引入field的概念,FFM把相同性質的特征歸于同一個field。以上面的廣告分類為例,“Day=26/11/15”、“Day=1/7/14”、“Day=19/2/15”這三個特征都是代表日期的,可以放到同一個field中。同理,商品的末級品類編碼生成了550個特征,這550個特征都是說明商品所屬的品類,是以它們也可以放到同一個field中。簡單來說,同一個categorical特征經過One-Hot編碼生成的數值特征都可以放到同一個field,包括使用者性别、職業、品類偏好等。在FFM中,每一維特征 x i x_i xi​,針對其他特征所在field f j f_j fj​,都會學習一個隐向量 v i , f j v_{i,f_j} vi,fj​​,是以,隐向量不僅與特征相關,也與field相關。也就是說,“Day=26/11/15”這個特征與“Country”特征和“Ad_type"特征進行關聯的時候使用不同的隐向量,這“Country”和“Ad_type”的内在差異相符,也是FFM中“field-aware”的由來。

假設樣本的 n n n個特征屬于 f f f個field,那麼FFM的二次項有 n f nf nf個隐向量。而在FM模型中,每一維特征的隐向量隻有一個。FM可以看作FFM的特例,是把所有特征都歸屬到一個field時的FFM模型。根據FFM的field敏感特性,可以導出其模型方程。

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

其中 f j f_j fj​是第 j個特征所屬的field。如果隐向量的長度為 k k k,那麼FFM的二次參數有 n f k nfk nfk 個,遠多于FM模型的 n k nk nk個。此外,由于隐向量與field相關,FFM二次項并不能夠化簡,其預測複雜度是 O ( k n 2 ) O(kn^2) O(kn2) 此外,内積 &lt; v i , f j , v j , f i &gt; &lt;v_{i,f_j} ,v_{j,f_i}&gt; <vi,fj​​,vj,fi​​>表示讓特征 i 與 特征 j j的 field 關聯,同時讓特征 j 與 i i的 field 關聯,由此可見,FM的交叉是針對特征之間的,而FFM是針對特征與 field 之間的。

正是因為FM可以看成FFM中所有特征都屬于同一個 field 的特例,故而FM中的每個特征都使用一個隐向量進行表達,而FFM則對每個特征是使用 f 個隐向量表達,因為FFM一共有f個field。另外,值得強調的是,論文中尤其指出,雖然 FFM 相比于 FM,其時間複雜度提升到為 O ( k n 2 ) O(kn^2) O(kn2) ,參數數量提升到 n f k nfk nfk 個,但對每個特征的 f 個 k長度的隐向量表示,其長度 k 相較于FM有了大幅降低,即文中的: k F F M &lt; &lt; k F M k_{FFM} &lt;&lt; k_{FM} kFFM​<<kFM​,即每個特征表達從 1 個長度 k F M k_{FM} kFM​ 的表達式變成了 f 個長度為 k F F M k_{FFM} kFFM​的表達式

2.1 舉個栗子

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

這條記錄可以編碼成5個特征,其中“Genre=Comedy”和“Genre=Drama”屬于同一個field,“Price”是數值型,不用One-Hot編碼轉換。為了友善說明FFM的樣本格式,我們将所有的特征和對應的field映射成整數編号。

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

FFM組合特征有10項:

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

其中,紅色是field編号,藍色是特征編号,綠色是此樣本的特征取值。二次項的系數是通過與特征field相關的隐向量點積得到的,二次項共有 n ( n − 1 ) / 2 n(n−1)/2 n(n−1)/2 個。

這個公式猛一看顯得眼花缭亂,看的時候隻關注特征即可,即 &lt; v i , f j , v j , f i &gt; &lt;v_{i,f_j} ,v_{j,f_i}&gt; <vi,fj​​,vj,fi​​>中的特征 v下标 i, j​,公式第一行就是 i=1, j=2⋯5​ ,再填補特征 v​的右下角标 fi,fj 。

2.2 優化求解

采用随機梯度下降求解參數,即每次隻選一條資料進行訓練。

FFM進行二分類時(1, -1),模型的損失函數為logistic loss:

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

注意,logistic loss有兩種表達式,另一種參見邏輯回歸,類别為(1, 0)時的損失函數。

2.3 增加Field資訊

對LIBSVM資料組織形式:

Lable feat1:val1 feat2:val2 ...,
           

其中(feat, val) 對訓示特征索引和值,而對于FFM,将資料組織形式擴充為:

label filed1:feat1:val1 field2:feat2:val2 ...
           

即将相應的域 filed 配置設定到每個特征。

2.3.1 Categorical Features

對線型模型,分類特征通常轉換為多個二進制特征。

Yes P:ESPN A:Nike G:Male

Yes P-ESPN:1 A-Nike:1 G-Male:1
           

将每個類别作為一個filed,然後以上資料執行個體就變為:

Yes P:P-ESPN:1 A:A-Nike:1 G:G-Male:1
           

2.3.2 Numerical Features

考慮以下示例:

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

AR: accept rate of the conference Hidx: h-index of the author Cite:

number of citations of the author

一共有兩個可能方式配置設定filed,

naive的方式是将每個特征作為dummy filed,生成的資料如下:

Yes AR:AR:45.73 Hidx:Hidx:2 Cite:Cite:3
           

然後,dummy filed可能不會增加判定資訊, 因為它們隻是特征的重複。

另一種方式是離散化數值特征為類别,使用如同對待類别特征的設定添加filed資訊,生成的資料如下形式:

Yes AR:45:1 Hidex:2:1 Cite:3:1
           

其中AR特征四舍五入為整數。

缺點:這種方式的缺點是難以判定最佳的離散設定,如到底是将45.73設定為45.7、45、40或者是"int(log(45.73))",此外,離散化可能會損失資訊。

2.3.3Single-filed Features

一些資料集上,所有特征屬于單個filed,這樣将導緻增加filed域毫無意義,這種情況多出來在NLP資料集中。考慮以下樣本:

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

2.4 FFM使用指導原則

FFM包含大量類别型特征的資料集更有效,需要将類别型特征進行dummy編碼(轉換成了特征稀疏形式);
如果轉換後的資料集不足夠稀疏,則FFM的field域提升效果不明顯(極端示例,NLP中常出現的一個文本特征集對應一個filed,此時field起不到作用);
将FFM适用到數值型特征資料集上沒有明顯優勢(事實上,作者實驗驗證得到,将FFM适用到全數值特征,當采用dummy filed,即将數值特征當做類别特征進行處理時候,FFM與FM表現近似,filed域起不到作用;而若将數值特征進行離散化處理之後,FFM表現比FM要好,但都差于直接使用dummy filed的效果)。

總結一句話就是FFM,FM均是針對大規模、特征類、稀疏場景(可通過對類别進行dummy編碼得到)具有明顯優勢。

2.5 應用

在DSP的場景中,FFM主要用來預估站内的CTR和CVR,即一個使用者對一個商品的潛在點選率和點選後的轉化率。

CTR和CVR預估模型都是線上下訓練,然後用于線上預測。兩個模型采用的特征大同小異,主要有三類:使用者相關的特征、商品相關的特征、以及使用者-商品比對特征。使用者相關的特征包括年齡、性别、職業、興趣、品類偏好、浏覽/購買品類等基本資訊,以及使用者近期點選量、購買量、消費額等統計資訊。商品相關的特征包括所屬品類、銷量、價格、評分、曆史CTR/CVR等資訊。使用者-商品比對特征主要有浏覽/購買品類比對、浏覽/購買商家比對、興趣偏好比對等幾個次元。

為了使用FFM方法,所有的特征必須轉換成

“field_id:feat_id:value”

格式,field_id代表特征所屬field的編号,feat_id是特征編号,value是特征的值。數值型的特征比較容易處理,隻需配置設定單獨的field編号,如使用者評論得分、商品的曆史CTR/CVR等。categorical特征需要經過One-Hot編碼成數值型,編碼産生的所有特征同屬于一個field,而特征的值隻能是0或1,如使用者的性别、年齡段,商品的品類id等。除此之外,還有第三類特征,如使用者浏覽/購買品類,有多個品類id且用一個數值衡量使用者浏覽或購買每個品類商品的數量。這類特征按照categorical特征處理,不同的隻是特征的值不是0或1,而是代表使用者浏覽或購買數量的數值。按前述方法得到field_id之後,再對轉換後特征順序編号,得到feat_id,特征的值也可以按照之前的方法獲得。

CTR、CVR預估樣本的類别是按不同方式擷取的。CTR預估的正樣本是站内點選的使用者-商品記錄,負樣本是展現但未點選的記錄;CVR預估的正樣本是站内支付(發生轉化)的使用者-商品記錄,負樣本是點選但未支付的記錄。建構出樣本資料後,采用FFM訓練預估模型,并測試模型的性能。

《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

由于模型是按天訓練的,每天的性能名額可能會有些波動,但變化幅度不是很大。這個表的結果說明,站内CTR/CVR預估模型是非常有效的。

在訓練FFM的過程中,有許多小細節值得特别關注。

1、樣本歸一化。對樣本進行歸一化,否則容易造成資料溢出,梯度計算失敗

2、特征歸一化。為了消除不同特征取值範圍不同造成的問題,需要對特征進行歸一化

3、Early stopping。一定要設定該政策,FFM很容易過拟合

4、省略零值特征。零值特征對模型沒有任何貢獻,省略零值特征,可以提高FFM模型訓練和預測的速度,這也是稀疏樣本采用FFM的顯著優勢

3 python實作

1導入包

import tensorflow as tf
import numpy as np
import pandas as pd
import os
           

2、設定權重參數

def create2DimensionalWeight(input_x_size, field_size, vector_dimension):
    return tf.Variable(tf.truncated_normal([input_x_size, field_size, vector_dimension]))
           
def create1DimensionalWeight(input_x_size):
    return tf.Variable(tf.truncated_normal([input_x_size]))
           
def create0DimensionalWeight():
    return tf.Variable(tf.truncated_normal([1]))
           

3、預測

def inference(input_x, input_x_field, zero_D_Weights, one_D_Weights, third_D_Weigths):
    secondValue = tf.reduce_sum(tf.multiply(one_D_Weights, input_x))
    firstTwoValue = tf.add(zero_D_Weights, secondValue)
    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(third_D_Weigths, vectorLeft) # 在third_D_weights中查找索引為vectorLeft的值
            weightLeftAfterCut = tf.squeeze(weightLeft) # 删除尺寸為 1  的次元
            vectorRight = tf.convert_to_tensor([[featureIndex2, fieldIndex1, i] for i in range(vector_dimension)])
            weightRight = tf.gather_nd(third_D_Weigths, vectorRight) # 在third_D_weights中查找索引為vectorRight的值
            weightRightAfterCut = tf.squeeze(weightRight) # 删除尺寸為 1  的次元
            tempValue = tf.reduce_sum(tf.multiply(weightLeftAfterCut, weightRightAfterCut)) # 計算兩個向量的點積
            xi = tf.squeeze(tf.gather_nd(input_x, indices=[i]))
            xj = tf.squeeze(tf.gather_nd(input_x, indices=[j]))
            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)
           

4、生成資料, 兩類标簽, 每個資料input_x_size個特征,屬于兩個field,每個field10個特征

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

5、設定損失函數,

def Loss(y_, one_D_Weights, third_D_Weigths):
    l2_norm = tf.reduce_sum(
    tf.add(
        tf.multiply(lambda_w, tf.pow(one_D_Weights, 2)),
        tf.reduce_sum(tf.multiply(lambda_v, tf.pow(third_D_Weigths, 2)),axis=[1,2])
        )
    )
    return tf.log(1 + tf.exp(input_y * y_)) + l2_norm
           

6、訓練

def train(loss):
    return tf.train.GradientDescentOptimizer(learning_rate=lr).minimize(loss,  global_step=global_step)
           

7、運作

if __name__ == '__main__':
    input_x_size = 20 # 20個特征
    field_size = 2 # 兩個field
    vector_dimension = 3 # 向量
    total_plan_train_steps = 20
    batch_size = 1 # 使用SGD,每一個樣本進行依次梯度下降,更新參數
    all_data_size = 50 # 資料集規模
    lr = 0.01
    MODEL_SAVE_PATH = "FFM_Model"
    MODEL_NAME = "FFM"
    input_x = tf.placeholder(shape=[input_x_size], dtype=tf.float32)
    input_y = tf.placeholder(dtype=tf.float32)
    lambda_w = tf.constant(0.001)
    lambda_v = tf.constant(0.001)
    global_step = tf.Variable(0, trainable=True)  
    zero_D_Weights = create0DimensionalWeight()
    one_D_Weights = create1DimensionalWeight(input_x_size)
    third_D_Weigths = create2DimensionalWeight(input_x_size, field_size, vector_dimension)
    trainx, trainy, trainx_field = generate_data(all_data_size, input_x_size)
    y_ = inference(input_x, trainx_field, zero_D_Weights, one_D_Weights, third_D_Weigths)
    loss = Loss(y_, one_D_Weights, third_D_Weigths)
    train_step = train(loss)
    saver = tf.train.Saver()
    sess = tf.Session()
    init = tf.global_variables_initializer()
    sess.run(init)  
    for i in range(total_plan_train_steps):
        for t in range(all_data_size):
            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})
            print("After  {step} training   step(s)   ,   loss    on    training    batch   is  {predict_loss} "
                      .format(step=i, predict_loss=predict_loss))

            saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=steps)
            writer = tf.summary.FileWriter(os.path.join(MODEL_SAVE_PATH, MODEL_NAME), tf.get_default_graph())
            writer.close()
           
《Field-aware Factorization Machines for CTR Prediction》FFM模型整理及python代碼1 原文(點選下載下傳)2 FFM模型3 python實作巨人

巨人

1、石曉文的學習日記:https://www.jianshu.com/p/781cde3d5f3d

2、代碼: https://github.com/princewen/tensorflow_practice/blob/master/recommendation/recommendation-FFM-Demo/FFM_model.py

3、非常詳細的介紹: https://blog.csdn.net/dby_freedom/article/details/84899120

4、3 的 代碼: https://github.com/LLSean/data-mining/blob/master/ffm/util.py

5、原文連結: https://dl.acm.org/citation.cfm?id=2959134

繼續閱讀