热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

特征建模之FiBiNet

FiBiNet:CombineFeatureimportancea

FiBiNet: Combine Feature importance and Bilinear feature Interaction for Click-Through Rate Prediction https://arxiv.org/abs/1905.0943


一、特征建模的重要性

推荐领域的深度CTR模型中的参数主要由两部分构成:特征Embedding参数和MLP层参数,假设模型中有1亿个特征,Embedding的维度是10维,MLP包括三层FC,神经元个数是1024/512/256,那么我们可以算出两部分的参数量分别是:

  • 特征Embedding参数: 1亿*10 = 10亿
  • MLP的参数: 1024*512*256 = 1.3亿

可以看出特征Embeding参数占比达到90%+,巨大的特征参数量是导致CTR模型容易过拟合的主要原因。同时值得考虑的是,这10亿个参数都是有用的么?不同的特征Embedding的重要程度一样么?如果答案是否定的,那应该如何入筛选对模型更有益的特征,如何度量每个特征的重要程度?

微博提出的FiBiNet系列模型就给出了解决上述问题的一种可行方案。

二、FiBiNet理论

为了解决业界现有模型存在的两个问题(一是没有度量CTR模型中特征的重要性,二是简单的使用inner product 或者 Hadamard product 无法很好的进行特征交叉),FiBiNet中设计了两个子网络,分别是建模特征重要性的SENet模块和建模特征交叉的Bilinear-Interaction Layer。

(一)、SENet


SENet: Squeeze-and-Excitation Networks ,https://arxiv.org/abs/1709.01507

SENet最初是在CV领域被提出,因其轻量化且具备显著的有效性被广泛应用。该模块重点关注了不同channel之间的关系,学习不同channel对最终预测的重要程度,对于CNN网络过程中的特征图U,SENet模块主要进行以下三步操作:

  1. Squeeze: 输入通道数为C的U,对每个cnannel 进行max pooling,选取最大值这一统计特征表征一个channel, 最终输出1 X 1 X C的张量。
  2. Excitation: 类似门控机制,使用两个FC层学习通道级别的权重,每个weight的含义可以理解维对应通道在最终预测时的重要度。
  3. Re-scale: 将求解的权重与最初的输入U相乘,得到加权后的输出。

借鉴该模块的设计思想,在推荐任务中考虑引入一个类似的门控系统,能够实现"系统预测不重要的特征的权重趋近于0,预测重要特征的权重越大越好",通过这种方式对特征重要性建模,使模型可以忽略掉低频特征、不重要特征的负面影响,对高频特征进行更好的建模。

下图是FiBiNet中的SENet模块,模块输入是所有的特征embedding [e1 e2 ... ef],输出是乘以特征权重后的weighted embeding [v1 v2 ... vf],具体过程为:

  1. Squeeze: 这一步的目的是得到每个特征embedding的统计值特征。采用mean pooling的方式选取每个特征的统计值特征zi,假设输入有f个slot,那么这一步输出shape为1 X f的张量。
  2. Excitation:这一步的目的是学习每个特征的权重。通过双层MLP网络对第一步输出的张量进行变换,将其映射到最终的权重特征空间,为了避免过拟合,往往会通过超参数r将第一个FC层设计为窄网络,将第二个FC设计为宽网络。同时采用ReLU激活函数打压不重要的特征,最终输出权重张量 [w1 w2 ... wf] 。 

      A=F_{ex}(z)=\sigma_{2}(W_{2}(\sigma_{1}(W_{1}))) \newline ~~~~~~~~~~~~~~~W_{1}\in R^{f\times \frac{f}{r}} \newline ~~~~~~~~~~~~~~~W_{2}\in R^{\frac{f}{r} \times f}

根据上述原理可以看出,推荐系统中SENet的本质是对离散特征做field-wise加权

(二)、Bilinear-Interaction Layer

为了更好的建模特征交叉,FiBiNet在任意两个特征进行交互时引入一个新的参数矩阵W,通过这个参数矩阵更精细地表征交互过程。具体来说,先计算特征vi和W的内积得到中间结果z,然后计算z与vj的哈达玛乘积(逐元素相乘)得到双线性交叉的结果。

假设有f个slot/field, 特征embedding的维度是k, 实现Bilinear-Interaction Layer时有三种不同形式。

 1.Field-All Type

  • P_{ij}=V_i\cdot W\odot V_j

    借用王树森老师的图总结一下FiBiNet的设计:

    1. RecSys中,首先将离散特征embed化,每个离散特征对应一个K维的embedding向量,得到embeding矩阵M;

    2. 在原有DNN结构基础上,FiBiNet新增了红框中的子网络结构:

      a. 直接把M所有的特征Embeding拼接,产出张量A;

      b. 对M中所有特征进行Bilinear运算,产出张量B;

      c. 先将M中所有特征进行SENet运算,然后再通过Bilinear运算,产出张量C。

    3. 将A、B、C与连续特征拼接到一起,作为上层网络的输入。

    三、FiBiNet实践

    基于paddle框架实现了FiBiNet,这里主要给出SENet和Bilinear interaction Layer两个模块的的实现代码。

    (一)、SENet

    def _senet(self, all_emb, reduction_ratio=3):"""Func:implementation of senetArgs:all_emb: a lod tensor, shape is (-1 slot_nums, embed_dim)reduction_ratio: integer, the ratio of fcOutput:a lod tensor, shape is (-1 slot_nums, embed_dim)"""slot_nums = all_emb.shape[1] # 获取特征个数fc_unit = max(1, slot_nums // reduction_ratio) # 计算FC层的神经元个数################## squeeze ##################squeeze_emb = layers.reduce_mean(all_emb, dim=-1) # (-1, slot_nums, 1)falten_emb = layers.flatten(squeeze_emb) # (-1, slot_nums) print('feature nums is ' + str(slot_nums))print('falten_emb shape is:' + str(falten_emb.shape))################## excitation ##################weight = layers.fc(input=falten_emb, size=fc_unit, act='relu',param_attr =fluid.ParamAttr(learning_rate=1.0,initializer=fluid.initializer.NormalInitializer(loc=0.0, scale=self._init_range / (slot_nums ** 0.5)),name="se_w_1"),bias_attr =fluid.ParamAttr(learning_rate=1.0,initializer=fluid.initializer.NormalInitializer(loc=0.0, scale=self._init_range / (slot_nums ** 0.5)),name="se_b_1")) # (-1, fc_unit)weight = layers.fc(input=weight, size=slot_nums, act='relu',param_attr =fluid.ParamAttr(learning_rate=1.0,initializer=fluid.initializer.NormalInitializer(loc=0.0, scale=self._init_range / (fc_unit ** 0.5)),name="se_w_2"),bias_attr =fluid.ParamAttr(learning_rate=1.0,initializer=fluid.initializer.NormalInitializer(loc=0.0, scale=self._init_range / (fc_unit ** 0.5)),name="se_b_2")) # (-1, slot_nums)################## re_weight ##################out = layers.elementwise_mul(all_emb, layers.unsqueeze(weight, axes=[2])) # (-1, slot_nums, embed_dim) * (-1, slot_nums, 1)print('senet out shape is: ' + str(out.shape)) # (-1 slot_nums embed_dim)return out

    (二)、Bilinear Interaction Layer

    def _bilinear_interaction_layer(self, all_emb, mode):"""Func:implementation of bilinear interaction layerArgs:all_emb: an embedding which has concated all embed , shape is (-1, slot_nums, embed_dim)"""slot_nums = all_emb.shape[1]embed_dim = all_emb.shape[2]emb_list = layers.split(all_emb, num_or_sections=slot_nums, dim=1)emb_list = [layers.squeeze(emb, axes=[1]) for emb in emb_list] # list, ele shape is (-1 embed_dim)if mode == "field_all":# 构建一个共享的参数矩阵W = layers.create_parameter(shape=[embed_dim, embed_dim], dtype='float32')# 先计算点积vidots = [layers.matmul(emb, W) for emb in emb_list] # (-1 embed_dim)# 计算Hadamard Productp_ij = [fluid.layers.elementwise_mul(vidots[i], emb_list[j])for i, j in itertools.combinations(range(slot_nums), 2)] # (-1 embed_dim)output = layers.concat(p_ij, axis=-1) # (-1 embed_dim * slot_nums)return outputelif mode == "field_each":# 构建参数矩阵,数量与slot_nums保持一致W_list = [layers.create_parameter(shape=[embed_dim, embed_dim], dtype='float32') for _ in range(slot_nums)]# 计算点积vidots = [layers.matmul(emb_list[1], W_list[i]) for i in range(slot_nums)]# 计算 Hadamard productp_ij = [layers.elementwise_mul(vidots[i], emb_list[j])for i, j in itertools.combinations(range(slot_nums), 2)] # (-1 embed_dim)output = layers.concat(p_ij, axis=-1) # (-1 embed_dim * slot_nums)return outputelif mode == "field_interaction":W_list = [layers.create_parameter(shape=[embed_dim, embed_dim], dtype='float32') for _, _ in itertools.combinations(range(slot_nums), 2)]p_ij = [layers.elementwise_mul(layers.matmul(v[0], w), v[1])for v, w in zip(itertools.combinations(emb_list, 2), self.W_list)]else:raise NotImplementedError

    三、FiBiNet存在的问题

           原文中把所有的特征embedding都进行双线性特征交叉,这一部分会带来巨大的参数量,也导致线上推理时长和内存存储的增加,因此在实现时,可以根据具体业务,选择出必要的特征进行交叉。


推荐阅读
  • 基于词向量计算文本相似度1.测试数据:链接:https:pan.baidu.coms1fXJjcujAmAwTfsuTg2CbWA提取码:f4vx2.实验代码:imp ... [详细]
  • 抠图前vsPython自动抠图后在日常的工作和生活中,我们经常会遇到需要抠图的场景,即便是只有一张图片需要抠,也会抠得我们不耐烦ÿ ... [详细]
  • 世界人工智能大赛OCR赛题方案!
     Datawhale干货 作者:阿水,北京航空航天大学,Datawhale成员本文以世界人工智能创新大赛(AIWIN)手写体OCR识别竞赛为实践背景,给出了OCR实践的常见思路和流 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 本文介绍了一个Python函数same_set,用于判断两个相等长度的数组是否包含相同的元素。函数会忽略元素的顺序和重复次数,如果两个数组包含相同的元素,则返回1,否则返回0。文章还提供了函数的具体实现代码和样例输入输出。 ... [详细]
  • 如何优化Webpack打包后的代码分割
    本文介绍了如何通过优化Webpack的代码分割来减小打包后的文件大小。主要包括拆分业务逻辑代码和引入第三方包的代码、配置Webpack插件、异步代码的处理、代码分割重命名、配置vendors和cacheGroups等方面的内容。通过合理配置和优化,可以有效减小打包后的文件大小,提高应用的加载速度。 ... [详细]
  • tcpdump 4.5.1 crash 深入分析
    tcpdump 4.5.1 crash 深入分析 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • 在本教程中,我们将看到如何使用FLASK制作第一个用于机器学习模型的RESTAPI。我们将从创建机器学习模型开始。然后,我们将看到使用Flask创建AP ... [详细]
  • mapreduce源码分析总结
    这篇文章总结的非常到位,故而转之一MapReduce概述MapReduce是一个用于大规模数据处理的分布式计算模型,它最初是由Google工程师设计并实现的ÿ ... [详细]
  • 语义分割系列3SegNet(pytorch实现)
    SegNet手稿最早是在2015年12月投出,和FCN属于同时期作品。稍晚于FCN,既然属于后来者,又是与FCN同属于语义分割网络 ... [详细]
  • python3下载mapbox矢量切片通过观察mapbox的页面开发者工具里的network可以发现,打开矢量切片和字体切片pbf和prite图标的链接, ... [详细]
  • Jupyter 使用Anaconda 虚拟环境内核
    Anaconda虚拟环境中使用JupyterNotebook安装好Anaconda之后,进入AnacondaPrompt,创建虚拟环境, ... [详细]
author-avatar
手机用户2602925995
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有