特征交叉-MaskNet文章总结代码实现

news2024/11/23 13:25:28

MaskNet 这个模型是微博21年提出的,23年twitter(X)开源的推荐系统排序模块使用的backbone结构。
核心思想是认为DNN为主的特征交叉是addictive,交叉效率不高;所以设计了一种multiplicatvie的特征交叉
如何设计muliplicative特征交叉呢?
1)首先设计了一个instance-guide-mask,下图是instance-guide-mask的设计,其实就是两层feed-forward-layer,第一层把原始输入维度扩增,第二层再还原回去,总结而言,就是这个公式:
V m a s k = W d 2 ( R e l u ( W d 1 V e m b + β d 1 ) ) + β d 2 V_{mask} = W_{d2}(Relu(W_{d1} V_{emb} + \beta_{d1})) + \beta_{d2} Vmask=Wd2(Relu(Wd1Vemb+βd1))+βd2

𝑉 𝑒 𝑚 𝑏 ∈ R 𝑚 = 𝑓 × 𝑘 𝑉_{𝑒𝑚𝑏}∈ R^{𝑚=𝑓 ×𝑘} VembRm=f×k 输入的embedding结果, f是输入特征的数量,k是特征embedding维度。
最终输出的是一个处理后的embedding向量,后面简称为mask
instance-guide-mask2) 这个embedding得到后,怎么使用呢,这个就是MaskBlock干的事情。
主要有两种使用,一个是对embedding进行处理,图里LN-EMB里的LN指的是Layer Normalization
V 𝑚 𝑎 𝑠 𝑘 𝑒 𝑑 𝐸 𝑀 𝐵 = V 𝑚 𝑎 𝑠 𝑘 ⊙ L N _ E M B ( V 𝑒 𝑚 b ) V_{𝑚𝑎𝑠𝑘𝑒𝑑𝐸𝑀𝐵} = V_{𝑚𝑎𝑠𝑘} ⊙ LN\_EMB(V_{𝑒𝑚b}) VmaskedEMB=VmaskLN_EMB(Vemb), 把mask的结果和LN-EMB 进行element-wide product, 然后在接一个linear,LN后应用在Relu做下非线性激活,这个就是MaskBLock的全部了, 总结成一个公式:
V o u t p u t = L N _ H I D ( W i V m a s k e d E M B ) = R e L U ( L N ( W i ( V m a s k ⊙ L N _ E M B ( V 𝑒 𝑚 b ) ) ) V_{output} = LN\_HID(W_i V_{maskedEMB}) = ReLU(LN(W_i (V_{mask} ⊙ LN\_EMB(V_{𝑒𝑚b}))) Voutput=LN_HID(WiVmaskedEMB)=ReLU(LN(Wi(VmaskLN_EMB(Vemb)))
Embedding除了对Embedding进行element-wide-product,还可以对神经网络的输出再和mask做一次处理,这个就是另一种mask的应用方式:
V o u t p u t = L N _ H I D ( W i V m a s k d H I D ) = R e L U ( L N ( W i ( V m a s k ⊙ V o u t p u t p ) ) ) V_{output} = LN\_HID(W_i V_{maskdHID}) = ReLU(LN(W_i(V_{mask} ⊙ V_{output}^p))) Voutput=LN_HID(WiVmaskdHID)=ReLU(LN(Wi(VmaskVoutputp)))
在这里插入图片描述
1) 2) 结束之后,文章的核心内容也基本结束,后面3)是MaskBlock的应用
3)MaskNet
所有特征都和Instance-guide-mask进行运算,可以是串行的也可以是并行的。
串行的第一个是一个MaskBlock on feature embedding,后面接的都是MaskBlock on MaskBlock;
并行的比较简单,每一个都是一个MaskBlock on feature embedding,然后concat到一起
在这里插入图片描述

二 Implementation
1)torch 代码实现,摘录自twitter开源代码:

def _init_weights(module):
  if isinstance(module, torch.nn.Linear):
    torch.nn.init.xavier_uniform_(module.weight)
    torch.nn.init.constant_(module.bias, 0)

class MaskBlock(torch.nn.Module):
  def __init__(self, 
              mask_block_config: config.MaskBlockConfig, 
              input_dim: int, 
              mask_input_dim: int) -> None:
    super(MaskBlock, self).__init__()
    self.mask_block_config = mask_block_config
    output_size = mask_block_config.output_size

    if mask_block_config.input_layer_norm: 
       # twitter实现的这里layer normalization做了可配置的
      self._input_layer_norm = torch.nn.LayerNorm(input_dim)
    else:
      self._input_layer_norm = None
    # instace-guide-mask第一层aggregation的神经元数量配置
    # 如果指定了压缩量,就是input * 压缩量;如果没有,那么也可以手动指定大小
    if mask_block_config.reduction_factor:
      aggregation_size = int(mask_input_dim * mask_block_config.reduction_factor)
    elif mask_block_config.aggregation_size is not None:
      aggregation_size = mask_block_config.aggregation_size
    else:
      raise ValueError("Need one of reduction factor or aggregation size.")

    # instance-guide-mask is here
    # 两层Linear
    self._mask_layer = torch.nn.Sequential(
      torch.nn.Linear(mask_input_dim, aggregation_size),
      torch.nn.ReLU(),
      torch.nn.Linear(aggregation_size, input_dim),
    )
    # 参数初始化
    self._mask_layer.apply(_init_weights)
    self._hidden_layer = torch.nn.Linear(input_dim, output_size)
    self._hidden_layer.apply(_init_weights)
    self._layer_norm = torch.nn.LayerNorm(output_size)

  def forward(self, net: torch.Tensor, mask_input: torch.Tensor):
    # LN
    if self._input_layer_norm:
      net = self._input_layer_norm(net)
    # self._mask_layer(mask_input)-- V_mask
    # net * V_mask
    hidden_layer_output = self._hidden_layer(net * self._mask_layer(mask_input))
    return self._layer_norm(hidden_layer_output)


class MaskNet(torch.nn.Module):
  def __init__(self, 
               mask_net_config: config.MaskNetConfig, 
               in_features: int):
    super().__init__()
    self.mask_net_config = mask_net_config
    mask_blocks = []
    if mask_net_config.use_parallel:
      total_output_mask_blocks = 0
      # 从local_prod参数看,用了4个block
      for mask_block_config in mask_net_config.mask_blocks:
        mask_blocks.append(MaskBlock(mask_block_config, in_features, in_features))
        total_output_mask_blocks += mask_block_config.output_size
      self._mask_blocks = torch.nn.ModuleList(mask_blocks)
    else:
      input_size = in_features
      for mask_block_config in mask_net_config.mask_blocks:
        mask_blocks.append(MaskBlock(mask_block_config, input_size, in_features))
        input_size = mask_block_config.output_size

      self._mask_blocks = torch.nn.ModuleList(mask_blocks)
      total_output_mask_blocks = mask_block_config.output_size

    if mask_net_config.mlp:
      self._dense_layers = mlp.Mlp(total_output_mask_blocks, mask_net_config.mlp)
      self.out_features = mask_net_config.mlp.layer_sizes[-1]
    else:
      self.out_features = total_output_mask_blocks
    self.shared_size = total_output_mask_blocks

  def forward(self, inputs: torch.Tensor):
    if self.mask_net_config.use_parallel:
      # 并行化的网络结构实现
      mask_outputs = []
      # 对于多个Block,每一个block输入都是一样,只是其中学习到的参数有所不同
      for mask_layer in self._mask_blocks:
        # mask_input,net 都是inputs
        mask_outputs.append(mask_layer(mask_input=inputs, net=inputs)) 
      # Share the outputs of the MaskBlocks.
      all_mask_outputs = torch.cat(mask_outputs, dim=1)
      # 最终输出处理
      output = (
        all_mask_outputs
        if self.mask_net_config.mlp is None
        else self._dense_layers(all_mask_outputs)["output"])
      return {"output": output, "shared_layer": all_mask_outputs}
    else:
      # 串行
      net = inputs
      for mask_layer in self._mask_blocks:
        # mask_input 是inputs,net输入是上一层的输出
        net = mask_layer(net=net, mask_input=inputs)
      # Share the output of the stacked MaskBlocks.
      output = net if self.mask_net_config.mlp is None else self._dense_layers[net]["output"]
      return {"output": output, "shared_layer": net}

2)tensorflow实现
摘录自EasyRec(阿里开源推荐工具)

# Copyright (c) Alibaba, Inc. and its affiliates.
import tensorflow as tf
from tensorflow.python.keras.layers import Activation
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import Layer
from easy_rec.python.layers.keras.blocks import MLP
from easy_rec.python.layers.keras.layer_norm import LayerNormalization
from easy_rec.python.layers.utils import Parameter

class MaskBlock(Layer):
  """MaskBlock use in MaskNet.
  Args:
    projection_dim: project dimension to reduce the computational cost.
    Default is `None` such that a full (`input_dim` by `aggregation_size`) matrix
    W is used. If enabled, a low-rank matrix W = U*V will be used, where U
    is of size `input_dim` by `projection_dim` and V is of size
    `projection_dim` by `aggregation_size`. `projection_dim` need to be smaller
    than `aggregation_size`/2 to improve the model efficiency. In practice, we've
    observed that `projection_dim` = d/4 consistently preserved the
    accuracy of a full-rank version.
  """
  def __init__(self, params, name='mask_block', reuse=None, **kwargs):
    super(MaskBlock, self).__init__(name=name, **kwargs)
    self.config = params.get_pb_config()
    self.l2_reg = params.l2_regularizer
    self._projection_dim = params.get_or_default('projection_dim', None)
    self.reuse = reuse
    self.final_relu = Activation('relu', name='relu')

  def build(self, input_shape):
    if type(input_shape) in (tuple, list):
      assert len(input_shape) >= 2, 'MaskBlock must has at least two inputs'
      input_dim = int(input_shape[0][-1])
      mask_input_dim = int(input_shape[1][-1])
    else:
      input_dim, mask_input_dim = input_shape[-1], input_shape[-1]
    # 这里实现和pytorch一样
    if self.config.HasField('reduction_factor'):
      aggregation_size = int(mask_input_dim * self.config.reduction_factor)
    elif self.config.HasField('aggregation_size') is not None:
      aggregation_size = self.config.aggregation_size
    else:
      raise ValueError('Need one of reduction factor or aggregation size for MaskBlock.')
    # instance-guide-mask第一层      
    self.aggr_layer = Dense(
        aggregation_size,
        activation='relu',
        kernel_initializer='he_uniform',
        kernel_regularizer=self.l2_reg,
        name='aggregation')
    # instance-guide-mask第二层
    self.weight_layer = Dense(input_dim, name='weights')
    # 对比pytorch实现,增加了projection_dim, 低秩矩阵(详见DCN)
    if self._projection_dim is not None:
      logging.info('%s project dim is %d', self.name, self._projection_dim)
      self.project_layer = Dense(
          self._projection_dim,
          kernel_regularizer=self.l2_reg,
          use_bias=False,
          name='project')
    if self.config.input_layer_norm:
      # 推荐在调用MaskBlock之前做好 layer norm,否则每一次调用都需要对input做ln
      if tf.__version__ >= '2.0':
        self.input_layer_norm = tf.keras.layers.LayerNormalization(
            name='input_ln')
      else:
        self.input_layer_norm = LayerNormalization(name='input_ln')

    if self.config.HasField('output_size'):
      self.output_layer = Dense(
          self.config.output_size, use_bias=False, name='output')
          
    # tensorflow遗留问题,兼容1/2
    if tf.__version__ >= '2.0':
      self.output_layer_norm = tf.keras.layers.LayerNormalization(
          name='output_ln')
    else:
      self.output_layer_norm = LayerNormalization(name='output_ln')
    super(MaskBlock, self).build(input_shape)

  def call(self, inputs, training=None, **kwargs):
    if type(inputs) in (tuple, list):
      net, mask_input = inputs[:2]
    else:
      net, mask_input = inputs, inputs
    # LN
    if self.config.input_layer_norm:
      net = self.input_layer_norm(net)
    # tensorflow实现aggregate层和projection层是分开的,上面pytorch是用一个sequence
    if self._projection_dim is None:
      aggr = self.aggr_layer(mask_input)
    else:
      u = self.project_layer(mask_input)
      aggr = self.aggr_layer(u)
   # 得到mask结果
    weights = self.weight_layer(aggr)
    # elemnet-wide product
    masked_net = net * weights

    if not self.config.HasField('output_size'):
      return masked_net
    # 最终处理,一个Liner+layer norm层
    hidden = self.output_layer(masked_net)
    ln_hidden = self.output_layer_norm(hidden)
    return self.final_relu(ln_hidden)

class MaskNet(Layer):
  def __init__(self, params, name='mask_net', reuse=None, **kwargs):
    super(MaskNet, self).__init__(name=name, **kwargs)
    self.reuse = reuse
    self.params = params
    self.config = params.get_pb_config()
    if self.config.HasField('mlp'):
      p = Parameter.make_from_pb(self.config.mlp)
      p.l2_regularizer = params.l2_regularizer
      self.mlp = MLP(p, name='mlp', reuse=reuse)
    else:
      self.mlp = None

    self.mask_layers = []
    for i, block_conf in enumerate(self.config.mask_blocks):
      params = Parameter.make_from_pb(block_conf)
      params.l2_regularizer = self.params.l2_regularizer
      mask_layer = MaskBlock(params, name='block_%d' % i, reuse=self.reuse)
      self.mask_layers.append(mask_layer)

    if self.config.input_layer_norm:
      if tf.__version__ >= '2.0':
        self.input_layer_norm = tf.keras.layers.LayerNormalization(
            name='input_ln')
      else:
        self.input_layer_norm = LayerNormalization(name='input_ln')

  def call(self, inputs, training=None, **kwargs):
    # 与pytorch版本对比,对输入也进行了一次layer norm
    if self.config.input_layer_norm:
      inputs = self.input_layer_norm(inputs)
    # 下面的并行/串行实现逻辑无差
    if self.config.use_parallel:
      mask_outputs = [
          mask_layer((inputs, inputs)) for mask_layer in self.mask_layers
      ]
      all_mask_outputs = tf.concat(mask_outputs, axis=1)
      if self.mlp is not None:
        output = self.mlp(all_mask_outputs, training=training)
      else:
        output = all_mask_outputs
      return output
    else:
      net = inputs
      for i, _ in enumerate(self.config.mask_blocks):
        mask_layer = self.mask_layers[i]
        net = mask_layer((net, inputs))

      if self.mlp is not None:
        output = self.mlp(net, training=training)
      else:
        output = net
      return output

Reference:
MaskNet: Introducing Feature-Wise Multiplication to CTR Ranking Models by Instance-Guided Mask
tesorflow实现
twitter-alg-ml

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246032.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(7) 探索Python函数的无限可能:从递归到Lambda的奇妙之旅

欢迎进入Python编程的奇幻世界!在这个课程中,我们将一起探索编程的乐趣,通过生动有趣的方式,培养编程的逻辑思维和创造力,该课程适合有一定基础的中学及以上学生及成年人。 以下是我们课程的大纲: 【Python:趣味编程,探索未来】 目录 1. 前言2. 认识我们的“魔法咒语”…

算法日记 32 day 动态规划(完全背包)

同样是背包问题,但01背包和完全背包是两个类型的问题。 完全背包: 完全背包与01背包的区别在于物品的个数是否是无限的。除此之外,在解决01背包的时候dp的背包遍历的顺利是倒序,为的是保证物品只被添加一次,而完全背包…

实用功能,觊觎(Edge)浏览器的内置截(长)图功能

Edge浏览器内置截图功能 近年来,Edge浏览器不断更新和完善,也提供了长截图功能。在Edge中,只需点击右上角的“...”,然后选择“网页捕获”->“捕获整页”,即可实现长截图。这一功能的简单易用,使其成为…

计算机网络:应用层知识点概述及习题

网课资源: 湖科大教书匠 1、概述 习题1 1 在计算机网络体系结构中,应用层的主要功能是 A. 实现进程之间基于网络的通信 B. 通过进程之间的交互来实现特定网络应用 C. 实现分组在多个网络上传输 D. 透明传输比特流 2 以下不属于TCP/IP体系结构应用层范畴…

【LSTM实战】跨越千年,赋诗成文:用LSTM重现唐诗的韵律与情感

本文将介绍如何使用LSTM训练一个能够创作诗歌的模型。为了训练出效果优秀的模型,我整理了来自网络的4万首诗歌数据集。我们的模型可以直接使用预先训练好的参数,这意味着您无需从头开始训练,即可在自己的电脑上体验AI作诗的乐趣。我已经为您准…

鸿蒙网络编程系列50-仓颉版TCP回声服务器示例

1. TCP服务端简介 TCP服务端是基于TCP协议构建的一种网络服务模式,它为HTTP(超文本传输协议)、SMTP(简单邮件传输协议)等高层协议的应用程序提供了可靠的底层支持。在TCP服务端中,服务器启动后会监听一个或…

基于 SpringBoot 的作业管理系统【附源码】

基于 SpringBoot 的作业管理系统 效果如下: 系统注册页面 学生管理页面 作业管理页面 作业提交页面 系统管理员主页面 研究背景 随着社会的快速发展,信息技术的广泛应用已经渗透到各个行业。在教育领域,课程作业管理是学校教学活动中的重要…

怎么只提取视频中的声音?从视频中提取纯音频技巧

在数字媒体的广泛应用中,提取视频中的声音已成为一项常见且重要的操作。无论是为了学习、娱乐、创作还是法律用途,提取声音都能为我们带来诸多便利。怎么只提取视频中的声音?本文将详细介绍提取声音的原因、工具、方法以及注意事项。 一、为什…

Java多态的优势和弊端

1. public class text {public static void main(String[] args) {animal dnew dog();d.eat();// dog a (dog) d;//类似强制转换//a.lookhome();/* if(d instanceof dog){dog a(dog)d;a.lookhome();}else if(d instanceof cat){cat c(cat) d;c.work();}else{System.out.print…

FPGA 14 ,硬件开发板分类详解,FPGA开发板与普通开发板烧录的区别

目录 前言 在嵌入式系统开发中,硬件开发板是工程师常用的工具之一。不同类型的开发板有不同的特点和用途,其中最常见的两大类是普通开发板和FPGA开发板。这里分享记录,这两类开发板的分类,并深入探讨它们在烧录过程中的具体区别…

冲破AI 浪潮冲击下的 迷茫与焦虑

在这个科技日新月异的时代,人工智能如汹涌浪潮般席卷而来,不断改变我们的生活。你是否对 AI 充满好奇,却不知它将如何改变你的工作与生活?又是否会在 AI 浪潮的冲击下陷入迷茫与焦虑?《AI 时代:弯道超车新思…

时序论文23|ICML24谷歌开源零样本时序大模型TimesFM

论文标题:A DECODER - ONLY FOUNDATION MODEL FOR TIME - SERIES FORECASTING 论文链接:https://arxiv.org/abs/2310.10688 论文链接:https://github.com/google-research/timesfm 前言 谷歌这篇时间序列大模型很早之前就在关注&#xff…

Redis的基本使用命令(GET,SET,KEYS,EXISTS,DEL,EXPIRE,TTL,TYPE)

目录 SET GET KEYS EXISTS DEL EXPIRE TTL redis中的过期策略是怎么实现的(面试) 上文介绍reids的安装以及基本概念,本章节主要介绍 Redis的基本使用命令的使用 Redis 是一个基于键值对(KEY - VALUE)存储的…

大疆上云api开发

目前很多公司希望使用上云api开发自己的无人机平台,但是官网资料不是特别全,下面浅谈一下本人开发过程中遇到的一系列问题。 本人使用机场为大疆机场2,飞机为M3TD,纯内网使用 部署 链接: 上云api代码. 首先从github上面拉去代码 上云api代码github. 后…

实现管易云到金蝶云星空的数据无缝集成

管易云数据集成到金蝶云星空:案例分享 在企业信息化系统中,数据的高效流动和准确对接是业务顺利运行的关键。本文将聚焦于一个具体的系统对接集成案例——通过轻易云数据集成平台实现管易云数据到金蝶云星空的无缝迁移,方案名称为“wk_店铺_…

Ubuntu上安装MySQL并且实现远程登录

目录 下载网络工具 查看网络连接 更新系统软件包; 安装mysql数据库 查看mysql数据库状态 以数字ip形式显示mysql的监听状态。(默认监听端口是3306) 查看安装mysql数据库时系统创建的目录信息。 根据查询到的系统用户名以及随机密码&a…

卷积神经网络各层介绍

目录 1 卷积层 2 BN层 3 激活层 3.1 ReLU(Rectified Linear Unit) 3.2 sigmoid 3.3 tanh(双曲正切) 3.4 Softmax 4 池化层 5 全连接层 6 模型例子 1 卷积层 卷积是使用一个卷积核(滤波器)对矩阵进…

LVS

一、 lvs简介 LVS:Linux Virtual Server ,负载调度器,内核集成,章文嵩,阿里的四层 SLB(Server LoadBalance) 是基 于 LVSkeepalived 实现 LVS 官网 : http://www.linuxvirtualserver.org/ LVS 相关术语 VS: Virtual Serve…

使用 Elastic AI Assistant for Search 和 Azure OpenAI 实现从 0 到 60 的转变

作者:来自 Elastic Greg Crist Elasticsearch 推出了一项新功能:Elastic AI Assistant for Search。你可以将其视为 Elasticsearch 和 Kibana 开发人员的内置指南,旨在回答问题、引导你了解功能并让你的生活更轻松。在 Microsoft AI Services…

掺铒光纤激光器

一、光纤激光器的特点 实现灵活的激光光源(窄线宽、可调谐、多波长、超短光脉冲源)易获得高功率、高的光脉冲能量激光波长与光纤通信传输窗口相匹配采用激光器泵浦形式(半导体激光器泵浦)热稳定性、价格低廉、易小型化 二、放大…