传统推荐算法(五) FFM模型(2) 原理及实现

写在前面:

FM算法被用来解决解决稀疏数据下的特征组合问题.它的改进有不少.[1]中提出了基于FM的PITF(pairwise interaction tensor factorization)方法做个性化标签推荐.在2012年KDD杯中,PITF的通用版本factor model(论文称为FFMs)[2]被提了出来.而作者认为FFMs只考虑了"用户","物品"和"标签"三个特征等一系列问题,而提出了更加通用的FFMs版本FFM(Field-aware Factorization Machines)[3].

FFM论文:https://arxiv.org/abs/1701.04099
FFMppt:https://www.csie.ntu.edu.tw/~r01922136/slides/ffm.pdf
FFM第一作者分享的c++开源工具:https://github.com/ycjuan/libffm

1.FFM基本原理


推荐中的数据one-hot编码后得到的稀疏数据一般如上图所示.每一行是一个样本,每一列就是一个特征,一个样本有多少维,就有多少个特征;维度分为几类(User, Movie, Time…),就有几个field(当然field可以自己定义,不按照样本特征原先的类来).首先回顾一下FM中对特征交叉的处理:
3.png
而FFM做了如下改进:

4.png

f2就是j2所属的那个field,就是one-hot编码前这个特征所在的类.看出来了吧,就是将原先的wj1更加细化,原先的xj1只对应一个隐向量wj1,现在隐向量要看xj2所在的field类进行选择,参数量增加了.

当然了,当xj1和xj2同时不为0时的特征交叉才有意义.代码中也可以根据这个进行优化.


我们结合ppt看个例子:

一目了然.红字代表所在的field,蓝字代表特征,绿字代表特征的值,这里第三个field中两个特征同时存在,因此均为1.而FM对这些特征的交叉处理就是:
Screenshot_2019-07-29_11-19-51.png
C是xxj,xj2均不为0的二阶组合,<>是两个向量点击后求和.每个特征i都一个对应的隐向量wi.总共有w1,w2,w3,w4,w5五个隐向量,FM全用到了.
FFM的处理如下:
Screenshot_2019-07-29_11-19-52.png
很明显,
隐向量w1–>w11,w12,w13,w14,
隐向量w2–>w21,w22,w23,w24

向量原先有5个,现在共有5x4=20个.这个样本只用到了15个.


FFM第一作者分享的开源工具[6]只实现了之前展示的特征交叉项,本文的实验部分会把常数项和一次项加上(下面这个公式剪自[7],xi,xj同上面的xj1,xj2,都是指一个样本的不同特征的值):
20190729135443.png

2.注意事项

2.1 省略零值特征

零值特征相乘对结果无影响,实现过程中可以直接省略.

2.2 样本归一化

[8] 中指出,FFM默认是进行样本数据的归一化,否则很容易造成数据inf溢出.因此,样本层面的数据是推荐进行归一化的.

2.3 特征归一化

category类的特征one-hot编码中只取0或1,而continuous类的特征如果不作处理(比如10000),两者量值差距过大,很有可能因为量值差异造成不同特征对模型影响差距很大.

实际上,对于离散特征xi,其相关的隐向量wifj由其他所有非零的xj共同影响,当不同的xj之间量值差异很大,xifj很难同时照顾到"离群点xj"和其他xj.可能因此增加continuous类特征对模型影响,这是不合理的.

2.4 隐向量维度

由于和每个特征xi有关的隐向量通过field进一步细化,因此隐向量的维度相比FM的维度少了一些.论文中FFM取40和100维,FFM只用取4维:
Screenshot from 2019-07-30 10-00-25.png
论文中还指出,隐向量维度k值对结果影响不是很大,这也说明了FFM的k值不需要过大:
20190730095918.png

2.5 参数量

假设样本特征有n维,共有f个field,每个隐向量有k维.那么交叉项的参数量就是nfk.FM中交叉项参数量是nk.但是如2.4所说,FFM的隐向量维度更低.比如FM可能是几十或几百维,FFM就几维或几十维.但当field过多时,还是FFM参数多(一般是这样),而[3]也证明其效果更好.

2.6 时间复杂度

在FM那篇文章中我们分析过,可以使用多重求和的一些公式将交叉项进行优化,将时间复杂度由O(kn2})降到O(kn).这里显然无法做类似优化.因此特征交叉这一部分的时间复杂度就是O(kn2).所以,同样使用SGD,FFM训练时间一般更长(原代码中还有个FM,两者一比,FFM确实慢了不少).

2.7 SGD寻优小技巧

[8] 中讲了几个优化SGD寻优的小技巧,包括梯度分步计算,自适应学习率,OpenMP多核并行计算,SSE3指令并行编程.先保存下来,回头慢慢看(并不会).

3. tensorflow实现

首先看看这个类怎么实现的,里面有三个方法.不到60行代码.

数据的field_num=6,试验中使用了6类特征,5类离散特征,1类连续特征.离散特征进行one-hot编码,离散特征直接归一化.共有
24个特征,所以feature_nums=24,隐向量维度embedding_size=3.预测函数分为线性项(常数项和一次项)和交叉项:

class FFM(object):
    # field_nums: F = 6
    # feature_nums: N = 24
    # embedding_size:  k= 3
    def __init__(self, hparams, df_i, df_v):
        self.hparams = hparams
        tf.set_random_seed(self.hparams.seed)
        self.line_result = self.line_section(df_i, df_v)
        self.fm_result = self.fm_section(df_i, df_v)
        print(self.line_result, self.fm_result)
        self.logits = self.line_result+self.fm_result

线性项中使用到了df_i,shape为(?, 6), 以及df_v,shape为(?, 6).

样本的六个属性有24个不同的值,5个离散属性23个,1个连续属性1个,所以one-hot编码时共有24个特征.df_i记录了每个样本的六个属性中在这个排列中的id,取值区间为[0,24).对于离散属性,df_v取值为1,否则取值为特征归一化后的值.所以df_v是什么样的呢?就是类似这样的:
20190730111244.png

样本有6个属性,都只取一个值,所以每个样本有6个非零值.正如文中所说,计算时可只取非零特征值计算进行优化,所以我们看到的df_i和df_v都只有6维,而不是24维.

    def line_section(self, df_i, df_v):
        '''
        # df_i records the positon of i_th feature in 24
        # df_v records the value of i_th feature
        '''
        with tf.variable_scope('line'):
            weights = tf.get_variable('weights',
                                      shape=[self.hparams.feature_nums, 1],
                                      dtype=tf.float32,
                                      initializer=tf.initializers.glorot_uniform())
            batch_weights = tf.nn.embedding_lookup(weights, df_i) # (*, 6, 1)
            batch_weights = tf.squeeze(batch_weights, axis=2) # remove dimensions of size 1 ==> (*, 6)
            line_result = tf.multiply(df_v, batch_weights, name='line_w_x')
            biase = tf.get_variable('biase',
                                    shape=[1,1],
                                    dtype=tf.float32,
                                    initializer=tf.initializers.zeros())
            line_result = tf.add(tf.reduce_mean(line_result, axis=1, keepdims=True), biase) # (*, 1)
            
        return line_result

看完了常数项和一次项,再看看交叉项,懂了原理后,代码一看就清楚了:

    def fm_section(self, df_i, df_v):
        with tf.variable_scope('fm'):
            embedding = tf.get_variable('embedding',
                                        shape=[self.hparams.field_nums,
                                               self.hparams.feature_nums,
                                               self.hparams.embedding_size],
                                               dtype=tf.float32,
                                               initializer=tf.initializers.random_normal())
        fm_result = None
        for i in range(self.hparams.field_nums):
            for j in range(i+1, self.hparams.field_nums):
                vi_fj = tf.nn.embedding_lookup(embedding[j], df_i[:, i]) # (*, k)
                vj_fi = tf.nn.embedding_lookup(embedding[i], df_i[:, j]) # (*, k)
                wij = tf.reduce_sum(tf.multiply(vi_fj, vj_fi), axis=1, keepdims=True) # (*, 1)
                
                x_i = tf.expand_dims(df_v[:, i], 1) # (*, 1)
                x_j = tf.expand_dims(df_v[:, j], 1) # (*, 1)
                xij = tf.multiply(x_i, x_j) # (*, 1)
                
                if(fm_result == None):
                    fm_result = tf.multiply(wij, xij) # (*, 1)
                else:
                    fm_result += tf.multiply(wij, xij)
        return fm_result

感兴趣可以跑一下完整代码,有数据集:

https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20Traditional/MF/FFM

参考

[1] Rendle, S., & Schmidt-Thieme, L. (2010, February). Pairwise interaction tensor factorization for personalized tag recommendation. In Proceedings of the third ACM international conference on Web search and data mining (pp. 81-90). ACM.
[2] Jahrer, M., Toscher, A., Lee, J. Y., Deng, J., Zhang, H., & Spoelstra, J. (2012, August). Ensemble of collaborative filtering and feature engineered models for click through rate prediction. In KDDCup Workshop.
[3] Juan, Y., Lefortier, D., & Chapelle, O. (2017, April). Field-aware factorization machines in a real-world online advertising system. In Proceedings of the 26th International Conference on World Wide Web Companion (pp. 680-688). International World Wide Web Conferences Steering Committee.
[4] Rendle, S. (2010, December). Factorization machines. In 2010 IEEE International Conference on Data Mining (pp. 995-1000). IEEE.
[5] https://www.csie.ntu.edu.tw/~r01922136/slides/ffm.pdf
[6] https://github.com/ycjuan/libffm
[7] https://www.cnblogs.com/zhangchaoyang/articles/8157893.html
[8] https://tech.meituan.com/2016/03/03/deep-understanding-of-ffm-principles-and-practices.html
[9] https://www.cnblogs.com/zhangchaoyang/articles/8157893.html

公众号

更多精彩内容请移步公众号:推荐算法工程师

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值