深度学习 - 49.SIM 搜索兴趣网络 GSU 与 Soft Search 简单实现 By Keras

news2024/12/26 7:11:57

目录

一.引言

二.GSU  结构分析

1.Input Layer 输入层

2.Embedding Layer 嵌入层

3.Pooling Layer 池化层

4.MLP 深层网络

5.Soft Search 软搜索

三.GSU 结构实现 

1.Init 初始化

2.Build 构建

3.call 调用

4.GSU Layer 完整代码

四.GSU 模型训练

1.Input Layer

2.GSU Layer

3.GSU Model

4.模拟序列样本生成

5.模型 Fit

五.Soft Search 软搜索

1.获取 Embedding 参数

2.Soft Search

3.Soft Search 取 Top-K 

六.总结


一.引言

SIM 引入两阶段兴趣搜索,通过 GSU 通用搜索单元从长序列中快速匹配与 Item 相关的用户长期兴趣生成相关子用户序列 SBS,并通过 ESU 模拟候选  Item 与 SBS 之间的精确关系。其中 GSU 模块一般有 Soft Search 和 Hard Search 两种方法,下面基于 keras 简单实现下 Soft Search 软搜索并根据内积生成 SBS。

 

二.GSU  结构分析

GSU - General search Unit 指的是 SIM 模型的通用搜索模块单元,关于 SIM 的具体论文细节大家可以参考:SIM 论文笔记,本文主要基于 keras 实现简单 GSU 的 Soft Search,其模块如下图红框所示。

1.Input Layer 输入层

输入层包括 Target Ad 目标广告以及用户对应的长期序列行为,论文中认为 <= 14 天的行为为短期行为,> 14 天发生的行为为长期行为,并构造了 d-category metrics 进行了模型评估。

2.Embedding Layer 嵌入层

将上述 Ad-id 以及 b(i) 对应的 good-id lookup 获取对应 Embedding 嵌入。

Tips:

论文中认为用户长期行为与短期行为的分布不同,所以长期行为的 Embedding Layer 和短期行为对应的 Embedding Layer 并不共享,如下图红框所示,虽然 id 集合相同,但是 Embedding Layer 是分开构建的。

 

3.Pooling Layer 池化层

这里用长序列个每个 good Embedding 向量与 target Ad 的 Embedding 向量内积得到权重 Wi,然后 Wi 元素乘对应 good Embedding 做加权的 Sum Pooling,当然大家也可以尝试例如 Mean Pooling 这样的池化方式。

4.MLP 深层网络

最后一步遵循传统的 MLP 范式,将 Target Ad 的 Embedding 与长序列 Sum Pooling 得到的 Embedding 一起 Concat 传给 MLP 层,其层数分别为 200、80、2,与 DIN 类似,采用了 PRelu / Dice 激活函数,最后的 softmax 输出结果。  

5.Soft Search 软搜索

Soft Search 通过 Maximum Inner Product 最大内积搜索与 Query 相近的 Top-K Behavior In GSU,其中 DIN、DIEN 使用的序列长度一般为 150,这里论文 K 选择 150-200 的范围。 

 

三.GSU 结构实现 

全部依赖如下,其中 Dice 激活可以参考:DIN 实现 By Keras。

import numpy as np
import tensorflow as tf
from tensorflow.keras import Model
from tensorflow.keras.layers import Dense, Layer
from tensorflow.python.keras import Input
from tensorflow.python.ops.init_ops import TruncatedNormal
from DIN import Dice
from tensorflow.keras import backend as K
import heapq

1.Init 初始化

class GSULayer(Layer):

    def __init__(self, N=10000, embedding_dim=8, pooling_mode='sum', **kwargs):

        # 序列相关
        self.N = N
        self.embedding_dim = embedding_dim
        self.pooling_mode = pooling_mode
        self.T = None

        # 参数矩阵
        self.kernel = None
        self.weight_normalization = True

        self.DNN_3 = None
        self.DNN_2 = None
        self.DNN_1 = None

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

根据上面分析的 GSU Soft Search 模块,初始化定义如下变量: 

N - 商品库大小

embedding_dim - 嵌入向量维度

pooling_mode - [sum、mean] 求和与求平均

T - 用户序列长度

kernel - Embedding Layer 

weight_normalization - 权重是否归一化

DNN_I - MLP 对应的 200、80、2 的隐层

2.Build 构建

    def build(self, input_shape):
        # 获取序列长度
        history_shape, candidate_shape = input_shape
        self.T = history_shape[1]

        # N x embedding_dim 的参数矩阵
        self.kernel = self.add_weight(name='kernel',
                                      shape=(self.N, self.embedding_dim),
                                      initializer=TruncatedNormal,
                                      trainable=True)

        # 构建 DNN
        self.DNN_1 = Dense(200, activation=Dice())
        self.DNN_2 = Dense(80, activation=Dice())
        self.DNN_3 = Dense(1, activation='sigmoid')

        super(GSULayer, self).build(input_shape)

build 函数主要实现上述 init 变量的初始化,这里模型采用多输入模式,所以 history_shape 和 candidate_shape 是分开的,通过 history_shape 可以解析得到 T 序列长度。向量层大小为 N x embedding_dim,Dense 层的最后一层与原文略有不同,文章采用 Dense(2) + Softmax 的方式,这里采用 Dense(1) + Sigmoid 的方式。

3.call 调用

• 获取 Item、History 嵌入

    # 1.获取历史行为、候选集 Embedding 5x100x8, 5x1x8
    _history, _candidate = inputs  # [None, 10] [None, 1]
    # [None, T] => [None, T, embedding_dim]
    history_emd = tf.nn.embedding_lookup(self.kernel, _history)

    # [None, 1] => [None, 1, embedding_dim] => [None, T, embedding_dim]
    candidate_emb = tf.nn.embedding_lookup(self.kernel, _candidate)
    repeat_candidate_emb = K.repeat_elements(candidate_emb, self.T, axis=1)

直接 lookup 得到对应 Embedding 嵌入。

• Item 与 Good 计算内积

    # 内积计算权重
    dot_weights = tf.multiply(history_emd, repeat_candidate_emb)
    dot_weights = tf.reduce_sum(dot_weights, axis=2)

    if self.weight_normalization:
        dot_weights = tf.nn.softmax(dot_weights)

计算内积获得每个 Good 对应的权重 W,并根据参数决定是否归一化。

• 加权求和

    # 对 History 加权
    dot_weights = tf.reshape(dot_weights, [-1, self.T, 1])
    add_weights = history_emd * dot_weights

    # Sum Pooling
    if self.pooling_mode == "sum":
        pooling_output = tf.reduce_sum(add_weights, axis=1)
    else:
        pooling_output = tf.reduce_mean(add_weights, axis=1)

加权相乘后进行 pooling 操作,论文中采用 sum-pooling 求和方式池化。

• Concat && MLP

    # Concat
    candidate_emb = tf.reshape(candidate_emb, shape=(-1, self.embedding_dim))
    concat = tf.concat([candidate_emb, pooling_output], axis=-1)

    _output = self.DNN_1(concat)
    _output = self.DNN_2(_output)
    _output = self.DNN_3(_output)

Concat + MLP 的经典推荐模型范式,这里 DNN 也可以采用三层嵌套的写法。

4.GSU Layer 完整代码

class GSULayer(Layer):

    def __init__(self, N=10000, embedding_dim=8, pooling_mode='sum', **kwargs):

        # 序列相关
        self.N = N
        self.embedding_dim = embedding_dim
        self.pooling_mode = pooling_mode
        self.T = None

        # 参数矩阵
        self.kernel = None
        self.weight_normalization = True

        self.DNN_3 = None
        self.DNN_2 = None
        self.DNN_1 = None

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

    def build(self, input_shape):
        # 获取序列长度
        history_shape, candidate_shape = input_shape
        self.T = history_shape[1]

        # N x embedding_dim 的参数矩阵
        self.kernel = self.add_weight(name='kernel',
                                      shape=(self.N, self.embedding_dim),
                                      initializer=TruncatedNormal,
                                      trainable=True)

        # 构建 DNN
        self.DNN_1 = Dense(200, activation=Dice())
        self.DNN_2 = Dense(80, activation=Dice())
        self.DNN_3 = Dense(1, activation='sigmoid')

        super(GSULayer, self).build(input_shape)

    def call(self, inputs, **kwargs):
        # 1.获取历史行为、候选集 Embedding 5x100x8, 5x1x8
        _history, _candidate = inputs  # [None, 10] [None, 1]
        # [None, T] => [None, T, embedding_dim]
        history_emd = tf.nn.embedding_lookup(self.kernel, _history)

        # [None, 1] => [None, 1, embedding_dim] => [None, T, embedding_dim]
        candidate_emb = tf.nn.embedding_lookup(self.kernel, _candidate)
        repeat_candidate_emb = K.repeat_elements(candidate_emb, self.T, axis=1)

        # 内积计算权重
        dot_weights = tf.multiply(history_emd, repeat_candidate_emb)
        dot_weights = tf.reduce_sum(dot_weights, axis=2)

        if self.weight_normalization:
            dot_weights = tf.nn.softmax(dot_weights)

        # 对 History 加权
        dot_weights = tf.reshape(dot_weights, [-1, self.T, 1])
        add_weights = history_emd * dot_weights

        # Sum Pooling
        if self.pooling_mode == "sum":
            pooling_output = tf.reduce_sum(add_weights, axis=1)
        else:
            pooling_output = tf.reduce_mean(add_weights, axis=1)

        # Concat
        candidate_emb = tf.reshape(candidate_emb, shape=(-1, self.embedding_dim))
        concat = tf.concat([candidate_emb, pooling_output], axis=-1)

        _output = self.DNN_1(concat)
        _output = self.DNN_2(_output)
        _output = self.DNN_3(_output)

        return _output

    def compute_output_shape(self, input_shape):
        return input_shape[0][0], 1

四.GSU 模型训练

下面基于 GSU Layer 与模拟输入进行 Soft Search。

1.Input Layer

    # 批次大小、序列长度、商品类目
    batch_size, T, N = 5, 10000, 1000000

    # 多输入模型
    historyForGSU = Input(shape=(T,), dtype='int32', name='history')
    candidateForGSU = Input(shape=(1,), dtype='int32', name='candidate')

历史行为序列长度为 T,候选集长度为 1,论文中候选集的长度可以达到 54000,是先前 MIMN 记忆网络的 54x。

2.GSU Layer

    # Sum Pooling
    GSU = GSULayer(N=N, embedding_dim=8, pooling_mode='sum', name="GSU")
    outputByGSU = GSU([historyForGSU, candidateForGSU])

基础的多输入模型,采用 sum pooling。

3.GSU Model

    # 交叉熵二分类模型
    GSU_model = Model(inputs=[historyForGSU, candidateForGSU], outputs=[outputByGSU])
    GSU_model.compile(optimizer='rmsprop',
                      loss={'GSU': 'binary_crossentropy'},
                      loss_weights={'GSU': 1})
    GSU_model.summary()

添加损失函数与优化器,模型 summary 如下,论文中嵌入维度为 4,这里嵌入维度为 8,可以看到随着商品库数量的增加,模型参数的数量也很可观。

 

4.模拟序列样本生成

def genSamplesWithLabel(batch_size=5, T=10, N=1000, seed=0):
    np.random.seed(seed)
    # 用户历史序列
    user_history = np.random.randint(0, N, size=(batch_size, T))
    # 候选 Item
    user_candidate = np.random.randint(0, N, size=(batch_size, 1))
    # Labels
    sample_labels = np.random.randint(0, 2, size=batch_size)
    return user_history, user_candidate, sample_labels

# 用户历史行为序列 && 候选商品 ID
history, candidate, labels = genSamplesWithLabel(batch_size, T, N)

样本样例如下:

 

5.模型 Fit

    # GSU 长期序列行为训练
    GSU_model.fit([history, candidate], labels, epochs=10, batch_size=128)

示例样本,fit 只是走通流程,metric 不做参考:

 

五.Soft Search 软搜索

为了进一步加快万长度用户行为 Top-K 搜索速度,基于嵌入向量 E,论文中采用亚线性时间最大内积搜索方法 ALSH,搜索与目标项相关的 Top-K 行为。之前论文中还介绍过局部敏感 Hash 分桶的快速缓存搜索方法,这里示例使用最大堆 Heap 实现简易 Maximum Inner Product 的 Top-K 方法。

1.获取 Embedding 参数

kernel = np.array(GSU_model.get_layer("GSU").get_weights()[0])

kernel 即为商品的嵌入层,其维度为 N x embedding_dim,本例中 Kernel Size = (1000000, 8)。
 

2.Soft Search

通过 heapq.nlargers 实现 Top-K Good 的排序,然后基于 Top-K Good 的 Index 索引构建 SBS 用户长期行为子序列,送到后面的 ESU 通过 Attention 机制进行精确搜索。

class SoftSearch:

    def __init__(self, _kernel, _K):
        self.kernel = _kernel
        self.K = _K
        print("Kernel Size:", kernel.shape)

    def genTopK(self, good_history, target):

        _topKGoods = []
        _topKScores = []

        for sample in zip(good_history, target):
            score = []
            cur_seq, cur_tar = sample
            cur_tar_emd = np.squeeze(kernel[cur_tar])
            for seq in cur_seq:
                score.append(np.dot(kernel[seq], cur_tar_emd))
            # 获取下标
            topKIndex = heapq.nlargest(self.K, range(len(score)), score.__getitem__)
            # 获取数值
            topKScore = heapq.nlargest(self.K, score)

            _topKGoods.append(topKIndex)
            _topKScores.append(topKScore)

        return _topKGoods, _topKScores

3.Soft Search 取 Top-K 

论文中 Top-K 的 K 取 200 送入 GSU,这里示例取 10 跑通 Demo。 

    K = 10
    search = SoftSearch(kernel, K)
    topKGoods, topKScores = search.genTopK(history, candidate)

    for SBS, Score in zip(topKGoods, topKScores):
        print(SBS, Score)

其中 SBS 的长度为 K,后面的 Scores 是内积后排序的依据,这里是从大到小取 Top。 

 

六.总结

至此,简易的 GUS with Soft Search 就实现了,论文中由于软搜索与硬搜索得到的 SBS 序列相近且大部分为对应 Item 的同 Cate 类型商品,在权衡性能收益与资源消耗后,阿里巴巴选择使用 Hard Search 即 KKV 的形式进行 GSU 的通用搜索,但是这不妨碍 Soft Search 可以作为一种 Embedding 预训练的方式挖掘长期商品对应的向量。

工程上阿里巴巴采用 Tree 的形式构造 KKV 存储结构。 

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

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

相关文章

日语文法PPT截图1-15

B站课程链接 aspect 关注事件的流动 相当于英语中现在进行时 这些是简体&#xff0c;是不能表达对听话人的礼貌的。 格助词一般只能接在名词后面&#xff0c;表达名词在句子中所做的成分。 副助词除了可以接在名词的后…

UDP和TCP详解

1. UDP 1.1 UDP协议段格式 16位UDP长度, 表示整个数据报(UDP首部UDP数据)的最大长度, 即数据报最大大小为2^16byte 64KB如果校验和出错, 就会直接丢弃 1.2 UDP特点 1.2.1 无连接不可靠 无连接 知道对端的IP和端口号就直接进行传输, 不需要建立连接不可靠 没有确认机制, 没有…

小型双轮差速底盘实现触须避障

1. 功能说明 在R023d机器人车体上安装2个 触须传感器 &#xff0c;实现机器人小车避障功能。 2. 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#xff09;‍ 扩展板 Bigfish2.1扩展…

netstat 连接通信的信息和状态

netstat t 只显示tcpu只显示udpnnum 数字形式显示地址和端口号l listen 显示监听端口 pprogram 显示进程aall 所有连接和监听r显示路由表 netstat -lnp 显示服务监听端口tcpudpsocket &#xff0c;socket 文件也用来同一台服务器的进程之间通信的。 netsta…

12.2RAC 实例State 为 UNKNOWN

去年10月份接手一个12.2 RAC环境&#xff0c;使用crsctl status res -t查看&#xff0c;发现有个实例状态是UNKNOWN。一直都没有停机维护时间&#xff0c;在测试环境中也没有模拟不出来&#xff0c;原以为删除实例再添加可以解决了。就一直等待维护机会来处理&#xff0c; ASM…

114个ChatGPT全网最热话题

前言 当你使用ChatGPT时&#xff0c;你可能有很多问题需要回答。ChatGPT是一个非常强大的自然语言处理工具&#xff0c;可以用于许多不同的任务&#xff0c;包括聊天机器人、智能客服、文本生成和语言翻译等。 在这篇博客中&#xff0c;我将介绍一些关于如何问ChatGPT的技巧和…

RS-485收发器MS2552可pin对pin兼容THVD1552

THVD15xx 是一系列抗噪 RS-485/RS-422 收发器&#xff0c;专用于在恶劣的工业环境中运行。这些器件的总线引脚可耐受高级别的 IEC 电气快速瞬变 (EFT) 和 IEC 静电放 电 (ESD) 事件&#xff0c;从而无需使用其他系统级保护组件。每个器件由 5V 单电源供电。该系列中的器件具有扩…

【手机建站】安卓Termux+cpolar内网穿透,搭建外网可以访问的网站 - 无公网IP

文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 概述 Termux是一个Android终端仿真应用程序&#xff0c;用于在 Android 手机上搭建一个完整的Linux 环境&#xff0c;能够实现Linux下的许多基本操作&#xff0c;不需要root权限Termux就…

常见的dos操作命令

创建abc目录文件&#xff08;md abc&#xff09;&#xff0c;进入abc文件&#xff08;cd abc&#xff09;&#xff0c;删除abc文件&#xff08;rd abc&#xff09; dir c:\测试 显示指定路径下所有文件和目录的信息 copy c:\测试.txt c:\测试 复制文件命令 ren c:\…

彻底弄懂Java中的toString方法

在Java中&#xff0c;所有的类都默认显式或者隐式继承自Object类&#xff0c;包括你定义的类。而Object类本身有一个toString()方法&#xff0c;用于返回一个表示该对象的字符串&#xff0c;该方法返回的是一个字符串类型的值。 如果你的类没有重写该方法&#xff0c;那么它就会…

【C++】21.智能指针

1.为什么需要智能指针 C无gc new/malloc出来的资源 是需要我们去手动释放 1.忘记释放 2.发生异常安全问题 new/malloc fun()://throw 异常 delete/free 最终都导致资源的泄漏 利用智能指针更好的去解决此类问题 2.智能指针 1RAII RAII&#xff08;Resource Acquisit…

javaIO之各种流的分类与实际应用

目录 1、初识 Java IO2、传输方式划分3、操作对象划分3.1文件3.2数组&#xff08;内存&#xff09;3.3管道3.4基本数据类型3.5缓冲3.6打印3.7对象序列化/反序列化3.8转换 1、初识 Java IO IO&#xff0c;即in和out&#xff0c;也就是输入和输出&#xff0c;指应用程序和外部设备…

Java BIO 和 NIO 使用,有什么区别

Java 中的 I/O 操作主要有两种方式&#xff1a;BIO 和 NIO。BIO&#xff08;Blocking I/O&#xff09;是同步阻塞 I/O 模型&#xff0c;而 NIO&#xff08;Non-Blocking I/O&#xff09;是异步非阻塞 I/O 模型。这两种 I/O 模型在编写网络应用程序时有着不同的优缺点&#xff0…

Intel® ZTNA RA 23.03 release

摘要 传统的防火墙、入侵检测系统都是基于物理边界的&#xff0c;默认墙内安全&#xff0c;墙外不安全。随着应用程序和用户现在更可能在外围而不是内部&#xff0c;这种安全模型已经不再适用于当今复杂的网络场景。而“零信任”脱离了这种传统的安全模型&#xff0c;不再区别对…

论文浅尝 | 常识问答中的忠诚知识图解释

笔记整理&#xff1a;邹铭辉&#xff0c;天津大学硕士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://aclanthology.org/2022.emnlp-main.743 动机 知识图谱通常被用作常识问答的信息来源&#xff0c;同时也可以用来解释模型对答案的选择。纳入图谱中事实信息的一个常…

诺贝尔奖得主Warshel:用计算化学揭开生命底层分子运行机制|智源大会嘉宾风采...

导读 复杂化学系统的多尺度建模可以用于计算机辅助药物设计、疾病致病机制、早期诊断生物标记、创新药物开发&#xff0c;这些具有划时代意义的研究成果&#xff0c;都凝结着计算化学研究先驱Arieh Warshel夜以继日的努力。 Warshel的传奇人生始于以色列一家公社的鱼塘&#xf…

【强烈推荐】3dMax自动展UV神器UV-Packer插件

UV-Packer是一款快速、精确的UV自动展开工具。这是一个一键式的解决方安&#xff0c;可以解决将展开的多边形排序和压缩成UV片的艰巨工作。 【适用版本】 3dMax2015-2024 【主要特性】 最小的UV区域浪费 确定良好 UV 包装的第一条规则是未覆盖的 UV 区域有多少。 浪费的空间…

操作系统的发展史(DOS/Windows篇)

操作系统的最强入门科普&#xff08;Unix/Linux篇&#xff09; 上一篇文章&#xff0c;小枣君介绍了Unix和Linux操作系统的诞生和发展。今天这篇&#xff0c;我再来说说微软的DOS和Windows系列。 █ DOS操作系统 上期提到&#xff0c;20世纪70年代&#xff0c;伴随着计算机技术…

360QPaaS参编信通院《组装式应用开发平台研究报告》| 应用前沿

在数字化转型的大背景下&#xff0c;“组装式应用” 成为行业重要战略趋势之一。数字化相较于信息化&#xff0c;强调基于信息数据反哺业务&#xff0c;业务进一步促进系统的迭代优化。组装式应用平台就是一种以业务为中心的模块化组件构成。组装式应用协力提供更灵活的组装式部…

Apache Iceberg 中引入索引提升查询性能

动手点关注 干货不迷路 ‍ ‍Apache Iceberg 是一种开源数据 Lakehouse 表格式&#xff0c;提供强大的功能和开放的生态系统&#xff0c;如&#xff1a;Time travel&#xff0c;ACID 事务&#xff0c;partition evolution&#xff0c;schema evolution 等功能。 本文将讨论火山…