暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

xDeepFM如何实现field-wise显式高阶特征交叉-模型结构之特征交叉(3)-DCN系列之xDeepFM(3.3)附代码

播播笔记 2022-04-13
1223

接上一篇《DCN-V2对特征交叉做了什么改进-模型结构之特征交叉(3)-DCN系列之DCN-V2(3.2)附代码》继续介绍特征交叉之DCN系列,本篇将介绍xDeepFM。DCN和DCN-V2实现的显式高阶特征交叉是bit-wise的,忽略了特征的域信息,针对此,xDeepFM对特征交叉层进行改进,实现了field-wise的显式高阶特征交叉。为了方便和上一篇连起来,这一篇的各种序号也都接上一篇。


目录

  • 3 xDeepFM

    • 3.1 xDeepFM原理

    • 3.2 特征交叉结构CIN

      • 3.2.1 field-wise交叉

      • 3.2.2 交叉组合压缩

    • 3.3 CIN特点

      • 3.3.1 特征显式高阶自动交叉

      • 3.3.2 网络复杂度

        • 3.3.2.1 空间复杂度

        • 3.3.2.2 时间复杂度

      • 3.3.3 CIN和其它特征交叉结构的关联

        • 3.3.3.1 CIN和CN

        • 3.3.3.2 CIN和FM

      • 3.3.4 优缺点

    • 3.4 CIN代码实现

  • 相关面试问题



3 xDeepFM


xDeepFM[3]出自论文《xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems》,由中科大、北大与微软合作发表在 KDD2018。xDeepFM虽然名字看起来很像DeepFM,但其模型结构和设计思路更接近DCN系列,通过特征交叉网络的设计,实现field-wise的显式高阶特征交叉,充分考虑了特征field的信息,弥补了DCN和DCN-V2进行bit-wise特征交叉时对field信息的丢失,从而实现更有效的特征交叉。


3.1 xDeepFM原理


xDeepFM的网络结构框架保持了DCN系列模型的特点:包括特征交叉部分和DNN部分,如图8所示,其特征交叉部分为CIN(Compressed Interaction Network)结构。不同于DCN模型,其特征交叉结构第L层的输出包含了从1阶到L+1阶的所有交叉组合项,因此可直接将特征交叉部分最后一层输出作为特征交叉结果;xDeepFM的特征交叉结构CIN第L层的输出结果只包含L+1阶的交叉组合项,因此需将CIN的每一层输出concat,再和线性部分和DNN输出结合,送入输出层得到结果。

图8 xDeepFM模型结构


3.2 特征交叉结构CIN


xDeepFM的特征交叉,是对输入特征进行field和field的两两组合,实现field-wise的交叉,再将两两组合的结果压缩到指定的filed个数。图9表示了特征交叉结构CIN的框架,每层操作得到对应阶数的交叉特征  ,对  在field维度进行sum pooling,得到  维的向量表征,再将所有层经过sum pooling后的向量表征进行concat,最终得到包括1阶到L+1阶(L为CIN的层数)的特征交叉结果。

图9 CIN结构


CIN每层所做的操作包括两部分:(1) 特征进行field-wise交叉,即在filed之间两两交叉组合,(2) 通过矩阵对两两交叉组合结果进行线性加权,把输出压缩到指定field个数的特征表征,如式子(10)和图10所示。

3.2.1 field-wise交叉


  表示的操作是特征的field-wise交叉,取前一层输出的交叉特征表征  ,即  个域的特征向量表征,和原始输入特征中所有m个域的特征向量表征,进行两两哈达玛积运算,交叉组合结果为  个向量表征。


3.2.2 交叉组合压缩


field之间的两两交叉组合结果  乘以  后求和,这一步是将交叉组合结果,即  个D维向量进行线性加权求和,压缩到  个field的向量表征,即  个D维向量。这一步操作可以理解把交叉组合结果送入一层线性全连接层,输入神经元个数为  ,输出神经元个数为  ,每个神经元是一个D维向量,全连接层的参数为  ,表示输出层每个神经元参数不共享,也就是输出层每个field向量有独立的参数。


图10表示了对两两交叉组合结果(  个向量表征)的压缩操作,类似CNN中的卷积核操作,将  个D维向量形成的tensor   压缩到  个D维向量形成的矩阵  ,这也是CIN中Compressed的命名原因。

图10 CIN单层操作


3.3 CIN特点


CIN的设计思路和DCN中特征交叉结构(CN)的设计的类似,因此具有DCN的特点:(1) 实现了显式高阶特征自动交叉,(2) 特征交叉阶数由网络深度决定。


3.3.1 特征显式高阶自动交叉


CIN的输出包括了从2阶到L+1阶的所有交叉组合,也可以从归纳法理解。


  是由  和  先进行filed-wise两两组合,此时结果包括了所有2阶交叉组合项;再对两两组合结果线性加权,这一步操作不改变结果对所有2阶交叉组合的包含性质。  由  和  进行对应操作得到,因此包括了所有3阶交叉组合项。以此类推,第L层的输出则包括所有L+1阶交叉组合项。将CIN每层结果concat,则得到了所有2阶到L+1阶的交叉组合,再和Linear部分concat,则得到了所有1阶到L+1阶的交叉组合项。


3.3.2 网络复杂度


3.3.2.1 空间复杂度


从CIN的结构可以看出,每层的参数参数量由  决定。  大小为  (其中假设CIN每层输出field数量相同,均为H,m为输入特征  的filed数量),由于输出层每个filed对应的参数  不共享,因此有H个  ,一共T层,故参数量共为  (  )。可以看出,CIN的空间复杂度和每个field特征维度D无关,这也侧面说明了特征交叉的field-wise性质。


对于权重矩阵  ,可利用矩阵分解,按照  将其分解为两个低秩矩阵(大小为  )相乘,从而将空间复杂度降为  。


3.3.2.2 时间复杂度


CIN每层的操作包括两部分:(1) field两两组合,进行哈达玛乘积操作,(2) 两两组合结果线性加权。每两个field之间的哈达玛积操作时间复杂度为D,一共有  种组合,因此第一部分的计算量为  。得到  种组合后进行线性加权,得到H个field向量表征,因此在第一部分计算量的基础上再乘以H,即  。由于CIN一共有T层,故CIN的时间复杂度为  ,即  (其中N为  的维度,也就是m个field,每个field为D维,平铺后维度为N)。CIN的时间复杂度比较高,可能导致模型在线上推理耗时久,带来服务超时问题,这也是CIN在落地到具体业务场景的主要挑战。


3.3.3 CIN和其它特征交叉结构的关联


3.3.3.1 CIN和CN


CIN和CN的设计出发点类似,通过每层得到更高阶的交叉特征。不同的是,(1) CN是bit-wise,CIN是field-wise的;(2) CIN在  层没有加上上一层的输出,因此,CIN第  层只包含了  阶的交叉特征,整个CIN的输出需要通过对每一层的输出concat,从而得到所有阶的交叉特征;而CN在  层加上了上一层的输出,因此  层的输出包含了从1阶到  阶交叉特征,整个CN的输出只需要取最后一层的输出结果即可。


3.3.3.2 CIN和FM


乍看CIN和FM没有太多相似点,但从两者的原理可以发现,CIN是进阶版的FM,模型名eXtreme Deep Factorization Machine(xDeepF)也侧面反应了两者的关联。


FM结构是对每种交叉组合中的两个field向量进行内积操作,即哈达玛积操作后sum pooling,得到每种组合的标量表征,最后再对所有组合求和。FM中内积操作可以理解为CIN中将  按照field维度sum pooling;FM中对所有组合求和,则相当于没有CIN中的矩阵参数  所有元素均为1。可以看出,当CIN只有一层,  ,且加权矩阵  所有元素均为1时,CIN等价于FM。


3.3.4 优缺点


了解了CIN的特点后,xDeepFM的优缺点则很明显。


其优点包括:(1) 模型可实现特征的显式高阶自动交叉;(2) 实现了field-wise的特征交叉,充分考虑特征在同一个field中的信息


xDeepFM的缺点也很明显,CIN的时间复杂度高,在模型线上推理时耗时多,可能会导致线上服务超时问题。


3.4 CIN代码实现


对CIN的原理理解清楚后,其代码实现也很简单。


代码5  xDeepFM特征交叉结构CIN代码实现

    def xdeepfm_cin(params, input_layer, mode, layer_dropout=1):
    '''
    cross feature module: CIN
    Reference: KDD2018, xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems
    params: the model config params
    input_layer: [B, F, D]
    '''
    layer_name = 'cross_layers_cin'


    field_size = input_layer.shape[1].value # return the F number input_layer [B, F, D]
    field_dim = input_layer.shape[
    2].value # the embedding size of each domain, in this model, the size of all fields is same
    output_size = params.cross_layers['out_dim']
    layer_num = params.cross_layers['layer_num']


    kernel_initializer = tf.glorot_uniform_initializer()


    embedding_layer_0 = tf.transpose(input_layer, [0, 2, 1]) # [B, D, F]
    embedding_layer_extend_0 = tf.expand_dims(embedding_layer_0, axis=3) # [B, D, F, 1]
    embedding_layer_l = embedding_layer_0 # [B, D, F]


    embedding_layer_out = []


    for i in range(layer_num):
    index = i + 1
    embedding_layer_extend_l = tf.expand_dims(embedding_layer_l, axis=2) # [B, D, 1, F]


    kernel = tf.get_variable(
    name="{0}_kernel_{1}".format(layer_name, index),
    shape=[field_dim, field_dim, field_dim],
    dtype=tf.float32,
    initializer=kernel_initializer,
    ) # [F, F, F]
    embedding_layer_m_0_l = tf.multiply(
    embedding_layer_extend_0, # [B, D, F, 1]
    embedding_layer_extend_l, # [B, D, 1, F]
    name="{0}_m_0_l_{1}".format(layer_name, index),
    ) # [B, D, F, F]
    embedding_layer_l = tf.tensordot(
    embedding_layer_m_0_l, # [B, D, F, F]
    kernel, # [F, F, F]
    axes=([2, 3], [1, 0]),
    name="{0}_m_0_l_w_{1}".format(layer_name, index),
    ) # [B, D, F]


    if layer_dropout < 1.0:
    embedding_layer_l = tf.layers.dropout(embedding_layer_l, rate=(1 - layer_dropout),
    training=(mode == tf.estimator.ModeKeys.TRAIN),
    name="{0}_layer_{1}_dropout".format(layer_name, index),
    )


    # sum at D-axis
    embedding_layer_out.append(tf.reduce_sum(embedding_layer_l, axis=1, keepdims=False)) # layer_num * [B, F]


    # all rank cross feature concat
    embedding_layer_l = tf.concat(embedding_layer_out, axis=1) # [B, layer_num*F]


    # 可按实际情况是否线性变换
    output_w = tf.get_variable(
    "{0}_output_w".format(layer_name),
    shape=[field_size * field_dim, output_size],
    dtype=tf.float32,
    initializer=kernel_initializer,
    )
    output_layer = tf.matmul(embedding_layer_l, output_w, name="{0}_output".format(layer_name)) # [B, Output]


    return output_layer




    相关面试问题

    • xDeepFM的网络结构

    • CIN结构、原理、参数量

    • CIN的优缺点,和DCN、FM的关联和区别




    reference

    [3] xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems。https://arxiv.org/pdf/1803.05170.pdf




    下一篇将总结特征交叉的系列模型,后续将陆续分享多目标、激活函数等


    以及朋友们有什么想要一起探讨的,可以在评论区告诉我,共同学习一起进步呀~


    推荐系列文章:

    - 推荐模型结构-特征交叉

    - 基础知识类

    - 工具类


    微信最近对公众号的推送算法改版较大,为了更好地找到我,动动手对这个公众号置顶呀~


    公众号的内容会同时在知乎专栏更新,有兴趣的朋友可以在知乎找我玩(知乎账号:婷播播),知乎专栏:推荐学习笔记


    生活的思考和记录会在另一个公众号更新,有兴趣的朋友也可以动手关注一下

    文章转载自播播笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论