DeepRS(002)--FFM模型理论与实践

DeepRS(002)–FFM模型理论与实践

背景

FFM(Field-aware Factorization Machine)最初的概念来自Yu-Chin Juan(阮毓钦,毕业于中国台湾大学,现在美国Criteo工作)与其比赛队员,是他们借鉴了来自Michael Jahrer的论文中的field概念提出了FM的升级版模型。通过引入field的概念,FFM把相同性质的特征归于同一个field

FFM模型

以广告分类为例,“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敏感特性,可以导出其模型方程
在这里插入图片描述

其中, f j { f }_{ j } fj 是第 j 个特征所属的field。如果隐向量的长度为 k,那么FFM的二次参数有 n f k { nfk } nfk 个,远多于FM模型的 n k { nk } nk 个。此外,由于隐向量与field相关,FFM二次项并不能够化简,其预测复杂度是 O ( k n 2 ) O(k{ n }^{ 2 }) O(kn2)

下面以一个例子简单说明FFM的特征组合方式。输入记录如下
在这里插入图片描述
这条记录可以编码成5个特征,其中“Genre=Comedy”和“Genre=Drama”属于同一个field,“Price”是数值型,不用One-Hot编码转换。为了方便说明FFM的样本格式,我们将所有的特征和对应的field映射成整数编号
在这里插入图片描述
那么,FFM的组合特征有10项,如下图所示:
在这里插入图片描述
其中,红色是field编号,蓝色是特征编号,绿色是此样本的特征取值。二次项的系数是通过与特征field相关的隐向量点积得到的,二次项共有 n ( n − 1 ) 2 { \frac { n(n-1) }{ 2 } } 2n(n1)

FFM求解

模型方程
在这里插入图片描述
损失函数
在这里插入图片描述
其中:
在这里插入图片描述
注:逻辑回归其实是有两种表述方式的损失函数的,取决于你将类别定义为0和1还是1和-1;当我们将类别设定为1和-1的时候,逻辑回归的损失函数就是上面的样子。具体可参考 FM算法及FFM算法

代码实现

  1. 生成数据
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
  1. 定义权重项
#交叉特征的权重
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

#bias项
def createZeroDimensionWeight():
    weights = tf.truncated_normal([1])
    tf_weights = tf.Variable(weights)
    return tf_weights
  1. 计算估计值
    估计值的计算这里不能项FM一样先将公式化简再来做,对于交叉特征,只能写两重循环,所以对于特别多的特征的情况下,真的计算特别慢
def inference(input_x, input_x_field, zeroWeights, oneDimWeights, thirdWeight):
    """计算回归模型输出的值"""
	#一阶特征
    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)
  1. 定义损失函数
# 分别得到 偏置项、一次、二次项权重
zeroWeights = createZeroDimensionWeight()
oneDimWeights = createOneDimensionWeight(input_x_size)
thirdWeight = createTwoDimensionWeight(input_x_size,  # 创建二次项的权重变量
                                       field_size,
                                       vector_dimension)

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

参考文献

美团 深入FFM原理与实践
推荐系统-FFM模型理论详解与实现
推荐系统遇上深度学习(二)–FFM模型理论和实践

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

召唤师的峡谷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值