CTR学习笔记&代码实现1-深度学习的前奏LR->FFM


CTR学习笔记系列的第一篇,总结在深度模型称王之前经典LR,FM, FFM模型,这些经典模型后续也作为组件用于各个深度模型。模型分别用自定义Keras Layer和estimator来实现,哈哈一个是旧爱一个是新欢。特征工程依赖feature_column实现,这里做的比较简单在后面的深度模型再好好搞。完整代码在这里https://github.com/DSXiangLi/CTR

问题定义

CTR本质是一个二分类问题,是用户和广告相关特征, 是每个广告是否被点击,基础模型就是一个简单的Logistics Regression

考虑在之后TF框架里logistics可以简单用activation来表示,我们把核心的部分简化为以下

## LR模型2010年之前主流的CTR模型通常是最简单的logistics regression,模型可解释性强,工程上部署简单快捷。但最大的问题是依赖于大量的手工特征工程。

刚接触特征工程的同学可能会好奇为什么需要计算组合特征?

最开始我只是简单认为越细粒度的聚合特征Bias越小。接触了因果推理后,我觉得更适合用Simpson Paradox里的Confounder Bias来解释,不同聚合特征之间可能会相悖,例如各个年龄段的男性点击率均低于女性,但整体上男性的点击率高于女性。感兴趣的可以看看这篇博客因果推理的春天系列序 - 数据挖掘中的Confounding, Collidar, Mediation Bias

如果即想简化特征工程,又想加入特征组合,肯定就会想到下面的暴力特征组合方式。这个也被称作POLY2模型

但上述需要学习个参数,一方面复杂度高,另一方面对高维稀疏特征会出现大量是0的情况,模型无法学到样本中未曾出现的特征组合pattern,模型泛化性差。

于是降低复杂度,自动选择有效特征组合,以及模型泛化这三点成为后续主要的改进的方向。

GBDT+LR模型

2014年Facebook提出在GBDT叠加LR的方法,敲开了特征工程模型化的大门。GBDT输出的不是预测概率,而是每一个样本落在每一颗子树哪个叶节点的一个0/1矩阵。在只保留和target相关的有效特征组合的同时,避免了手工特征组合需要的业务理解和人工成本。

相较特征组合,我更喜欢把GBDT输出的特征向量,理解为根据target,对样本进行了聚类/降维,输出的是该样本所属的几个特定人群组合,每一棵子树都对应一种类型的人群组合。

但是!GBDT依旧存在泛化问题,因为所有叶节点的选择都依赖于训练样本,并且GBDT在离散特征上效果比较有限。同时也存在经过GBDT变换得到的特征依旧是高维稀疏特征的问题。

FM模型

2010年Rendall提出的因子分解机模型(FM)为降低计算复杂度,为增加模型泛化能力提供了思路

原理

FM模型将上述暴力特征组合直接求解整个权重矩,转化为求解权重矩阵的隐向量,这一步会大大增加模型泛化能力,因为权重矩阵不再完全依赖于样本中的特定特征组合,而是可以通过特征间的相关关系间接得到。同时隐向量把模型需要学习的参数数量从降低到个

同时FM通过下面的trick,把拟合过程的计算复杂度从降低到线性复杂度

代码实现-自定义Keras Layer

class FM_Layer(Layer):
    '''
    Input:
        factor_dim: latent vector size 
        input_shape: raw feature size
        activation
    output:
        FM layer output
    '''
    def __init__(self, factor_dim, activation = None, **kwargs):
        self.factor_dim = factor_dim
        self.activation = activations.get(activation) # if None return linear, else return function of identifier
        self.InputSepc = InputSpec(ndim=2) # Specifies input layer attribute. one Inspec for each input

super(FM_Layer,self).__init__(**kwargs)

def build(self, input_shape):
        '''
        input:
            tuple of input_shape
        output:
            w: linear weight
            v: latent vector
            b: linear Bias
        func:
            define all the necessary variable here
        '''
        assert len(input_shape) >=2
        input_dim = int(input_shape[-1])

self.w = self.add_weight(name = 'w0', shape = (input_dim, 1),
                                  initializer = 'glorot_uniform',
                                  trainable = True)

self.b = self.add_weight(name = 'bias', shape = (1, ),
                                  initializer = 'zeros',
                                  trainable = True)

self.v = self.add_weight(name = 'hidden_vector', shape = (input_dim, self.factor_dim),
                                 initializer = 'glorot_uniform',
                                  trainable = True)

super(FM_Layer, self).build(input_shape)# set self.built=True

def call(self, x):
        '''
        input:
            x(previous layer output)
        output:
            core calculation of the FM layer
        func:
            core calculcation of layer goes here
        '''
        linear_term = K.dot(x, self.w) + self.b
 
        # Embedding之和,Embedding内积: (1, input_dim) * (input_dim, factor_dim) = (1, factor_dim)
        sum_square = K.pow(K.dot(x, self.v),2)
        square_sum = K.dot(K.pow(x, 2), K.pow(self.v, 2))

# (1, factor_dim) -> (1)
        quad_term = K.mean( (sum_square - square_sum), axis=1, keepdims = True) #

output = self.activation((linear_term+quad_term))

return output

def compute_output_shape(self, input_shape):
        # tf.keras回传input_shape是tf.dimension而不是tuple, 所以要cast成int
        return (int(input_shape[0]), self.output_dim)

FM和MF的关系

Factorizaton Machine 和Matrix Factorization听起来就很像,MF也确实是FM的一个特例。MF是通过对矩阵进行因子分解得到隐向量,但因为只适用于矩阵所以特征只能是二维,常见的是(user_id, item_id)组合。而同样是得到隐向量,FM将矩阵展平把离散特征都做one-hot,因此支持任意数量的输入特征。

FM和Embedding的关系

Embedding最常见于NLP中,把词的高维稀疏特征映射到低维矩阵embedding中,然后用交互函数,例如向量内积来表示词与词之间的相似度。而实际上FM计算的隐向量也是一种Embedding 的拟合方法,并且限制了只用向量内积作为交互函数。上述得到的就是Embedding向量本身。

FFM

2015年提出的FFM模型在FM的基础上加入了Field的概念

原理

上述FM学到的权重矩阵V是每个特征对应一个隐向量,两特征组合通过隐向量内积的形式来表达。FFM提出同一个特征和不同Field的特征组合应该有不同的隐向量,因此变成 其中F是特征所属Field的个数。以下数据中country,Data,Ad_type就是Field

FM两特征交互的部分被改写为以下,因此需要学习的参数数量从nk变为nf*k。并且在拟合过程中无法使用上述trick因此复杂度从FM的上升为。

代码实现-自定义model_fn

def model_fn(features, labels, mode, params):    '''    Field_aware factorization machine for 2 classes classification    '''    feature_columns, field_dict = build_features()

    field_dim = len(np.unique(list(field_dict.values())))

    input = tf.feature_column.input_layer(features, feature_columns)

    input_dim = input.get_shape().as_list()[-1]

    with tf.variable_scope('linear'):        init = tf.random_normal( shape = (input_dim,2) )        w = tf.get_variable('w', dtype = tf.float32, initializer = init, validate_shape = False)        b = tf.get_variable('b', shape = [2], dtype= tf.float32)

        linear_term = tf.add(tf.matmul(input,w), b)        tf.summary.histogram( 'linear_term', linear_term )

    with tf.variable_scope('field_aware_interaction'):        init = tf.truncated_normal(shape = (input_dim, field_dim, params['factor_dim']))        v = tf.get_variable('v', dtype = tf.float32, initializer = init, validate_shape = False)

        interaction_term = tf.constant(0, dtype =tf.float32)        # iterate over all the combination of features        for i in range(input_dim):            for j in range(i+1, input_dim):                interaction_term += tf.multiply(                    tf.reduce_mean(tf.multiply(v[i, field_dict[j],: ], v[j, field_dict[i],:])) ,                    tf.multiply(input[:,i], input[:,j])                )        interaction_term = tf.reshape(interaction_term, [-1,1])        tf.summary.histogram('interaction_term', interaction_term)

    with tf.variable_scope('output'):        y = tf.math.add(interaction_term, linear_term)        tf.summary.histogram( 'output', y )

    if mode == tf.estimator.ModeKeys.PREDICT:        predictions = {            'predict_class': tf.argmax(tf.nn.softmax(y), axis=1),            'prediction_prob': tf.nn.softmax(y)        }

        return tf.estimator.EstimatorSpec(mode = tf.estimator.ModeKeys.PREDICT,                                          predictions = predictions)

    cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits( labels=labels, logits=y ))

    if mode == tf.estimator.ModeKeys.TRAIN:        optimizer = tf.train.AdamOptimizer(learning_rate = params['learning_rate'])        train_op = optimizer.minimize(cross_entropy,                                     global_step = tf.train.get_global_step())

        return tf.estimator.EstimatorSpec(mode, loss = cross_entropy, train_op = train_op)    else:        eval_metric_ops = {            'accuracy': tf.metrics.accuracy(labels = labels,                                            predictions = tf.argmax(tf.nn.softmax(y), axis=1)),            'auc': tf.metrics.auc(labels = labels ,                                  predictions = tf.nn.softmax(y)[:,1]),            'pr': tf.metrics.auc(labels = labels,                                 predictions = tf.nn.softmax(y)[:,1],                                 curve = 'PR')        }        return tf.estimator.EstimatorSpec(mode, loss = cross_entropy, eval_metric_ops = eval_metric_ops)


参考资料

  1. S. Rendle, “Factorization machines,” in Proceedings of IEEE International Conference on Data Mining (ICDM), pp. 995–1000, 2010
  2. Yuchin Juan,Yong Zhuang,Wei-Sheng Chin,Field-aware Factorization Machines for CTR Prediction。
  3. 盘点前深度学习时代阿里、谷歌、Facebook的CTR预估模型
  4. 前深度学习时代CTR预估模型的演化之路:从LR到FFM
  5. 推荐系统召回四模型之:全能的FM模型
  6. 主流CTR预估模型的演化及对比
  7. 深入FFM原理与实践

(0)

相关推荐

  • 【TensorFlow2.0】以后我们再也离不开Keras了?

    TensorFlow2.0 Alpha版已经发布,在2.0中最重要的API或者说到处都出现的API是谁,那无疑是Keras.因此用过2.0的人都会吐槽全世界都是Keras.今天我们就来说说Keras这 ...

  • 深度学习尝鲜-DeepFM模型原理

    一.背景 精排模型是推荐系统中效果产出最重要的模块,深度排序模型是推荐领域应用最广迭代最快的技术领域.近年来各大公司纷纷抛弃了原有的传统机器学习模型转向深度排序模型的研究和应用,提出了非常多结合工业应 ...

  • 【NLP实战】如何基于Tensorflow搭建一个聊天机器人

    实战是学习一门技术最好的方式,也是深入了解一门技术唯一的方式.因此,NLP专栏计划推出一个实战专栏,让有兴趣的同学在看文章之余也可以自动动手试一试. 本篇介绍如何基于tensorflow快速搭建一个基 ...

  • 如何训练AI玩飞机大战游戏

    虽然没有谷歌强大的集群和DeepMind变态的算法的团队,但基于深度强化学习(Deep Q Network DQN )的自制小游戏AI效果同样很赞.先上效果图: 下面分四个部分,具体给大家介绍. /1 ...

  • 【生成模型】解读显式生成模型之完全可见置信网络FVBN

    上一期为大家说明了什么是极大似然法,以及如何使用极大似然法搭建生成模型,本期将为大家介绍第一个显式生成模型完全可见置信网络FVBN. 作者&编辑 | 小米粥 1 完全可见置信网络 在完全可见置 ...

  • CTR学习笔记&代码实现2-深度ctr模型 MLP->Wide&Deep

    背景 这一篇我们从基础的深度ctr模型谈起.我很喜欢Wide&Deep的框架感觉之后很多改进都可以纳入这个框架中.Wide负责样本中出现的频繁项挖掘,Deep负责样本中未出现的特征泛化.而后续 ...

  • 行动学习笔记(05)行动学习的经典方法之六顶思考帽

    故事看完啦 是不是超有才,超有趣呢! 接下来敲黑板,划重点啦! 温馨提示: 本期文章有点长哦~ 搬好小板凳 拿出小本本 耐心看下去 必会有收获 白色思考帽 1.白色显得中立而客观.白帽思维代表客观的事 ...

  • JPG学习笔记5(附完整代码)

    JPG压缩的第4步是哈夫曼编码.下面主要介绍JPEG是如果进行哈夫曼编码的. 图片引用自"Compressed Image File Formats JPEG, PNG, GIF, XBM, ...

  • (2条消息) 六种人体姿态估计的深度学习模型和代码总结

    姿态估计的目标是在RGB图像或视频中描绘出人体的形状,这是一种多方面任务,其中包含了目标检测.姿态估计.分割等等.有些需要在非水平表面进行定位的应用可能也会用到姿态估计,例如图形.增强现实或者人机交互 ...

  • 深度学习中的 Attention 机制总结与代码实现(2017-2021年)

    作者丨mayiwei1998 来源丨GiantPandaCV 编辑丨极市平台 极市导读 由于许多论文中的网络结构通常被嵌入到代码框架中,导致代码比较冗余.本文作者对近几年基于Attention网络的核 ...

  • tensorflow 学习笔记-- 深度学习中epochs batchsize iteration的概念

    深度学习框架中涉及很多参数,如果一些基本的参数如果不了解,那么你去看任何一个深度学习框架是都会觉得很困难,下面介绍几个新手常问的几个参数. batch 深度学习的优化算法,说白了就是梯度下降.每次的参 ...

  • HALCON 20.11:深度学习笔记(10)

    HALCON 20.11.0.0中,实现了深度学习方法. 本章解释了如何在训练和推理阶段使用基于深度学习的分类. 基于深度学习的分类是一种对一幅图像分配一组置信值的方法.这些置信度值表明图像属于每个可 ...

  • HALCON 20.11:深度学习笔记(12)

    HALCON 20.11.0.0中,实现了深度学习方法. 本章解释了如何使用基于深度学习的语义分割,包括训练和推理阶段. 通过语义分割,我们使用深度学习(DL)网络将输入图像的每个像素分配到一个类. ...

  • HALCON 20.11:深度学习笔记(11)

    HALCON 20.11.0.0中,实现了深度学习方法. 本章讲解了如何使用基于深度学习的对象检测. 通过对象检测,我们希望在图像中找到不同的实例,并将它们分配给一个类.实例可以部分重叠,但仍然可以区 ...