用Keras实现一个DeepFM

用Keras实现一个DeepFM

本文仅提供一个思路,有问题欢迎指出,转载请注明,谢谢。


一、数据格式

  在设计模型之间,首先要明确数据的格式应该是怎样的。我们假设现在要解决的问题是一个CTR预估问题,数据集是 (X,y) ( X , y ) ,每一个样本都是高度稀疏的高维向量。假设我们有两种 field 的特征,连续型和离散型,连续型 field 一般不做处理沿用原值,离散型一般会做One-hot编码。离散型又能进一步分为单值型和多值型,单值型在Onehot后的稀疏向量中,只有一个特征为1,其余都是0,而多值型在Onehot后,有多于1个特征为1,其余是0。
  下面给出一个两个样本的例子,其中shop_score是连续型field,gender是单值离散型field,interest是多值离散型field。可以看到shop_score的取值是实数,gender的取值是离散值,interest的取值是离散值序列。

label shop_score gender interest
0 0.2 male football, cooking
1 0.8 female cooking

对各field进行Onehot后,可见单值离散field对应的独热向量只有一位取1,而多值离散field对应的独热向量有多于一位取1,表示该field可以同时取多个特征值。

label shop_score gender=m gender=f interest=f interest=c
0 0.2 1 0 1 1
1 0.8 0 1 0 1

进一步,我们对每个field中的特征取值分别单独编码或联合编码,则确定了特征的index,这在libsvm和libffm数据格式中是需要的。

field feature encoding separate encoding union
shop_score (1) 1 1
gender (2) male 1 2
gender (2) female 2 3
interest (3) football 1 4
interest (3) cooking 2 5

libsvm格式:

011:0.21:0.82:13:14:15:15:1 0 1 : 0.2 2 : 1 4 : 1 5 : 1 1 1 : 0.8 3 : 1 5 : 1

libffm格式:

011:1:0.21:1:0.82:2:12:3:13:4:13:5:13:5:1 0 1 : 1 : 0.2 2 : 2 : 1 3 : 4 : 1 3 : 5 : 1 1 1 : 1 : 0.8 2 : 3 : 1 3 : 5 : 1

  可见,连续field和单值field对样本长度的贡献恒定为1,但多值离散型field可能会导致样本长度不一样。对不定长样本的处理方法自然是padding补零了,但我选择对每个多值field分别进行padding,原因有二。首先,若对样本整体进行padding,万一想要进行截断,可能会截掉某些连续field和单值field,分别padding则可以分别截断,而不影响其他的field。第二,对每个field的不同特征单独编码互不影响,不需要维护一个全局的字典,每次只需要处理一个field的特征,甚至可以实现并行处理以及节省内存的特征Encoding方案。
  FM所需的数据格式正是libsvm格式,既需要数值本身(Value),也需要特征取值在字典中的index(ID)。假如我们采用对每个field的不同特征取值单独编码的方式,则可以实现一些简便性优化。首先,数值型field的ID永远是1,因此可以省略ID;第二,单值离散型field的Value永远是1,因此可以省略Value;第三,多值离散型field可以用padding+masking的方式省略ID。
  给每个field分配ID和Value时,为了用0做padding,ID编码需要从1开始。如下所示,shop_score作为连续型特征,每个样本的ID和Value列表长度都是1,所有样本共用同一ID,而所有样本的Value保持原值;gender作为单值离散型field,每个样本的ID和Value列表长度都是1,ID是编码后的特征编号,由于是离散型,Value全是1;interest作为多值离散型field,ID和Value列表的长度应该取该field的最长长度,第一个样本的interestfield长度是2,因此两个样本的ID和Value列表长度都应padding补零到定长2,每个样本的ID列表是各特征取值的编码值,而Value在ID的非零位置上取1。

ID_shop_score = [[1], [1]] # 多余,可省略
Value_shop_score = [[0.2], [0.8]]

ID_gender = [[1], [2]]
Value_gender = [[1], [1]] # 多余,可省略

ID_interest = [[1,2], [2,0]]
Value_gender = [[1,1], [1,0]] # 多余,可省略

根据上面给出的规则,我对各种field提取ID和Value提供参考方法如下:

  • 连续型field
    • ID:np.ones()或舍弃
    • Value : 沿用原 ndarray
  • 单值离散型field
    • ID:sklearn.preprocessing.LabelEncoder()
    • Value: np.ones()或舍弃
  • 多值离散型field
    • ID:sklearn.preprocessing.LabelEncoder() + padding + 加一
    • Value: np.ones() + padding 或舍弃

二、一个DeepFM需要哪些模块

这里写图片描述

  在动手写代码之前,先要对模型结构做一个宏观地观察,看看具体要实现哪些模块。上图是DeepFM论文中给出的整体网络结构图,可见要实现一个DeepFM,实现两个部分即可:FM部分和DNN部分,FM又可以进一步分为一次项和二次项。
  从根源上,DeepFM的各模块共享同一输入,输入是由各个field的Onehot编码横向拼接而成的高维稀疏向量。首先,原始输入的各个field经过加权(实际上是Embedding为1维)后,求和可得一次项;其次,原始输入的各个field(不同长度)的Embedding(等长, k k 维latent vector),一方面两两内积,然后求和可得二次项,另一方面作为输入全连接到DNN。

这里写图片描述


三、Keras实现

FM部分

这里写图片描述

3.1 如何用Embedding实现 FM一次项

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值