精通推荐算法27:行为序列建模之BST— 代码实现

news2024/11/24 16:25:34

1 引言

上文 精通推荐算法26:行为序列建模之BST— Transformer建模用户行为序列-CSDN博客
讲解了BST的背景和模型结构,本文给出其代码实现,供大家参考。

BST核心代码

Transformer已经成为了算法工程师的必备技能,因此这一节给出BST的Transformer层代码。代码基于Keras深度学习库实现,其中Keras版本为2.10

先看Multi-Head Self Attention的实现

class MultiHeadSelfAttention(keras.layers.Layer):
    def __init__(self, emb_dim, num_heads=4, **kwargs):
        """
        构建多头自注意力
        @param emb_dim: 输入embedding维度,必须为多头数目的整数倍
        @param num_heads: 多头的数目,一般取8、4、2等
        """
        self.emb_dim = emb_dim
        self.num_heads = num_heads

        # 定义q、k、v三个权重矩阵
        self.w_query = keras.layers.Dense(256)
        self.w_key = keras.layers.Dense(256)
        self.w_value = keras.layers.Dense(256)

        # 每个头内的向量维度,等于总维度除以多头数
        self.dim_one_head = 256 // num_heads

        # 全连接单元,将多个头连接起来。输出向量与原始输入向量维度相同,从而可以进行残差连接。
        self.w_combine_heads = keras.layers.Dense(emb_dim)

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

    def attention(self, query, key, value):
        """
        attention计算,softmax((q * k) / sqrt(dk, 0.5)) * v
        @param query: q向量
        @param key: k向量
        @param value: v向量
        @return:
        """
        # 1 内积,计算q和k的相关性权重,得到一个标量
        score = tf.matmul(query, key, transpose_b=True)

        # 2 计算向量长度,dk
        dim_key = tf.cast(tf.shape(key).shape[-1], tf.float32)

        # 3 缩放,向量长度上做归一化
        score = score / tf.math.sqrt(dim_key)

        # 4 Softmax归一化,使得权重处于0到1之间
        att_scores = tf.nn.softmax(score, axis=-1)

        # 权重乘以每个v向量,再累加起来,最终得到一个向量,维度与q、k、v相同
        att_output = tf.matmul(att_scores, value)
        return att_output, att_scores

    def build_multi_head(self, x_input, batch_size):
        """
        分割隐向量到多个head中,所以多头并不会带来维度倍增
        @param x_input: 输入向量,可以为q、k、v等向量
        @param batch_size:
        @return: 多头矩阵
        """
        x_input = tf.reshape(x_input, shape=(batch_size, -1, self.num_heads, self.dim_one_head))
        return tf.transpose(x_input, perm=[0, 2, 1, 3])

    def call(self, inputs, **kwargs):
        """
        多头自注意力计算部分
        @param inputs:
        @param kwargs:
        @return:
        """
        x_query = inputs[0]
        x_key = inputs[1]
        batch_size = tf.shape(x_query)[0]

        # 得到q向量,原始输入经过线性变换,然后再进行多头切割
        query = self.w_query(x_query)
        query = self.build_multi_head(query, batch_size)

        # 得到k向量
        key = self.w_key(x_key)
        key = self.build_multi_head(key, batch_size)

        # 得到v向量, v和k用同一个输入
        value = self.w_value(x_key)
        value = self.build_multi_head(value, batch_size)

        # attention计算
        att_output, att_scores = self.attention(query, key, value)
        att_output = tf.transpose(att_output, perm=[0, 2, 1, 3])  # [batch_size,seq_len,num_heads,dim_one_head]
        att_output = tf.reshape(att_output, shape=(batch_size, -1, self.dim_one_head * self.num_heads))

        # 多头合并,并进行线性连接输出
        output = self.w_combine_heads(att_output) # [batch_size,seq_len,emb_dim]
        return output

代码采用Keras标准的自定义Layer实现,重点看call函数和attention函数。call函数先将原始输入向量,通过线性连接,得到query、key、value三个向量。然后通过attention函数计算得到每个头的输出。最后通过一层线性连接来融合多头,得到一个与输入向量维度相同的输出向量。Attention函数的实现步骤为:

  1. 计算query和key内积,得到二者相关性权重
  2. 计算query和key向量长度,二者长度相同,后续归一化会用到
  3. 对第一步得到的权重进行缩放,相当于在向量长度上做归一化
  4. Softmax归一化,使得权重处于0到1之间
  5. 权重乘以每个v向量,再累加起来,最终得到一个向量,维度与q、k、v相同

再看Transformer的整体实现

class Transformer(keras.layers.Layer):
    def __init__(self, seq_len, emb_dim, num_heads=4, ff_dim=128, **kwargs):
        # position emb,位置编码向量
        self.seq_len = seq_len
        self.emb_dim = emb_dim
        self.positions_embedding = Embedding(seq_len, self.emb_dim, input_length=seq_len)

        # multi-head self attention层
        self.att = MultiHeadSelfAttention(self.emb_dim, num_heads)

        # 两层feed-forward全连接
        self.ffn = keras.Sequential([keras.layers.Dense(ff_dim, activation="relu"),
                                     keras.layers.Dense(self.emb_dim)])

        # 两次layerNorm,一次为input和attention输出残差连接,另一次为attention输出和全连接输出
        self.ln1 = keras.layers.LayerNormalization()
        self.ln2 = keras.layers.LayerNormalization()
        self.dropout1 = keras.layers.Dropout(0.3)
        self.dropout2 = keras.layers.Dropout(0.3)

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

    def call(self, inputs, **kwargs):
        """
        结构:一层multi-head self attention, 叠加两层feed-forward,中间加入layeNorm和残差连接
        @param inputs:原始输入向量,可以包括物品id、类目id、品牌id等特征的Embedding
        @param kwargs:
        @return:单层Transformer结构的输出
        """
        # 1 构建位置特征,并进行Embedding
        positions = tf.range(start=0, limit=self.seq_len, delta=1)
        positions_embedding = self.positions_embedding(positions)

        # 2 原始输入和位置向量加起来,也可以采用BST原文中的Concat方法
        x_key = inputs + positions_embedding

        # 3 multi-head self attention,利用layerNorm和输入进行残差连接
        att_output = self.att(inputs)
        att_output = self.dropout1(att_output)
        output1 = self.ln1(x_key + att_output)

        # 4 两层feed-forward全连接,利用layerNorm和输入进行残差连接
        ffn_output = self.ffn(output1)
        ffn_output = self.dropout2(ffn_output)
        output2 = self.ln2(output1 + ffn_output)   # [B, seq_len, emb_dim]

        # 5 平均池化,对序列进行压缩
        result = tf.reduce_mean(output2, axis=1)   # [B, emb_dim]
        return result

重点看call函数,其实现步骤为:

  1. 构建位置特征,并进行Embedding。此处为了方便,用位置的index来表示时序关系,与BST有一点细微区别。
  2. 原始输入向量和位置向量相加得到Transformer层的输入。也可以采用BST原文中的Concat方法
  3. 将Transformer层的输入送到Multi-Head Self Attention模块中经过LayerNorm后,再和输入进行残差。得到Attention模块的输出。
  4. 将Attention的输出送入到Feed-Forward模块中,它是两层全连接结构,经过LayerNorm后,再Attention的输出进行残差连接。得到Feed-Forward模块的输出。
  5. 将Feed-Forward模块输出的多个向量,进行平均池化,从而将不定长的序列压缩为一个固定长度的向量。

3 BST总结和思考

BST利用Transformer来建模用户行为序列,可以有效解决传统RNN模型容易出现的梯度弥散、串行耗时长等一系列问题,并取得了不错的业务效果。目前Transformer已经广泛应用在搜索推荐广告中,特别是用户行为序列建模中。BST作为一次完整的Transformer在大规模工业场景中的落地,其各种细节的处理和参数的配置,都非常值得学习和借鉴。

作者新书推荐

历经两年多,花费不少心血,终于撰写完成了这部新书。本文在5.4节中重点阐述了。

源代码:扫描图书封底二维码,进入读者群,群公告中有代码下载方式

微信群:图书封底有读者微信群,作者也在群里,任何技术、offer选择和职业规划的问题,都可以咨询。

详细介绍和全书目录,详见

《精通推荐算法》,限时半价,半日达icon-default.png?t=N7T8https://u.jd.com/mq5gLOH

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

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

相关文章

文档在线翻译软件推荐哪些?亲测好用的文档翻译器分享

处暑已至,秋风送爽,正是学习交流的好时节。想象一下,在翻阅外文文献或是与国际友人交流时,如果能有一款便捷的文档翻译软件免费版在手,是不是能让学习之路更加畅通无阻呢? 为了方便大家能够有更高效的学习…

UltraISO刻录Ubuntu镜像制作安装U盘

使用UltraISO 软件来刻录Ubuntu镜像启动盘: 首先下载UltraISO软件,然后点击试用,使用RAW的方式刻录就行!!!

【java】RuoYi-Vue前后端分离版本-登陆请求流程解析

【java】RuoYiBootstrap多模块版本-登陆请求流程解析 这里它用到了一个安全管理框架Spring Security 你可以通过这篇文章《Spring Security 详解》 去了解它,怎么使用 登陆请求流程逻辑图 Created with Raphal 2.3.0 (1)开始 (2&a…

基于yolov5猫狗检测

项目简介 该项目使用YOLOv5深度学习框架来检测图像或视频中的猫和狗。YOLOv5(You Only Look Once v5)是一种高效的物体检测模型,能够快速准确地识别出图像中的目标。本项目具有以下特点: 图像检测:用户可以通过上传图…

Nginx-企业高性能web服务器 超长完整版!只有你想不到 没有你学不到的满满干货!!

Web服务基础介绍 Web 服务器访问流程 按下回车时浏览器根据输入的 URL 地址发送请求报文给服务器。服务器接收到请求报文,会对请求报文进行处理。服务器将处理完的结果通过响应报文返回给浏览器。浏览器解析服务器返回的结果,将结果显示出来。 1. 输入…

苹果手机视频误删怎么恢复?看完拍手叫好的4个方法

试想一下,当你在翻看苹果手机相册的视频,正沉浸在过往的美好回忆中时,手指一不小心触碰到了屏幕上的删除按钮,手机上的视频就这样消失了……面对这样的意外情况,苹果手机视频误删怎么恢复呢?别急&#xff0…

Nuxt学习_基础知识(一)

文章学习来源,nuxt中文网 1. 安装nuxt 指令 npx create-nuxt-app t_nuxt或yarn create nuxt-app f_nuxt 执行指令后按需选择添加自己所需要的相关依赖,若安装出现报错等问题 清除npm、yarn缓存 npm cache clean --force yarn cache clean切换安装命令切…

NSIS - 创建桌面应用程序(Client-Side, CS 或者称为本地应用程序)的安装包

B站视频 C# winform Costura.Fody将多个dll打包生成一个可执行的exe文件中_哔哩哔哩_bilibili 博客 NSIS打包教程 Wnform程序打包-罗分明网络博客 补充:(以下面代码为例) ; 该脚本使用 HM VNISEdit 脚本编辑器向导产生; 安装程序初始定义常量 !define PRODUCT_NAME "sql…

9个超强查找下载化学学科文献的数据库 建议收藏

一、CAS(美国化学文摘社)数据库 CAS SciFinder Discovery Platform 是由全球科学信息引领者CAS(美国化学文摘社)出品的新一代的权威科学研究工具,是化学及相关学科智能研究平台,提供全球全面、可靠的化学及…

图片转PDF?小case!这几步操作,让你秒变职场小旋风

嘿,大家在忙碌的工作里,经常得处理一堆文件和照片,尤其是当你想把一堆照片弄成一个PDF文件时,这事儿就显得特别重要。不管是为了做报告、提项目建议还是整理日常工作资料,把照片转成PDF格式,都能让我们工作…

知识付费小程序引领线上直播

亲爱的朋友们,欢迎来到“探索未知领域,知识付费小程序引领知识新探索”线上直播课程! 在这个信息爆炸的时代,知识的获取从未如此便捷,但高质量、有深度的知识却仍需我们精心筛选。本次直播课程,将聚焦于知识…

conda切换32位运行环境,解决无法在64位系统中安装32位py

当前系统大部分都64位的,我的conda也是64位的,但是如果需要创建32位的py环境,会发现 conda create -n DouyinLive32 python3.7 --force创建的仍然是32位的,为此我们可以使用切换命令切换。 按一下Windows键,输入Prom…

大数据-94 Spark 集群 SQL DataFrame DataSet RDD 创建与相互转换 SparkSQL

点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…

数据资产入表,全流程实施指南!

数据成为生产要素已是社会共识,但不是所有数据都有资产价值。数据资源当中被重复使用的那部分才会资产化,具有流通中的定价,有些数据资产被专业开发变成数据产品,具有商品价值。从数据原始资源到数据产品,再到数据资产…

华为LTC流程体系的内涵(附PPT分享)

往期回顾: 企业4A架构:数字化转型的底层方法论(附TOGAF资料下载) PPT分享:数据治理的方法论、设计思路与方案(干货) 浅谈数字化转型方法论 110页PPT:xx业务流程优化(BPR&#xff…

Linux压缩和解压

目录 压缩和解压类 gzip/gunzip指令 zip/unzip指令 tar指令 压缩和解压类 gzip/gunzip指令 gzip用于压缩文件,gunzip用于解压缩文件。 解压后去掉了gz的后缀。 zip/unzip指令 ​​​​​​​ 将文件压缩后发给别人,别人再解压。 将整个文件压…

Python | Leetcode Python题解之第354题俄罗斯套娃信封问题

题目: 题解: class Solution:def maxEnvelopes(self, envelopes: List[List[int]]) -> int:if not envelopes:return 0n len(envelopes)envelopes.sort(keylambda x: (x[0], -x[1]))f [1] * nfor i in range(n):for j in range(i):if envelopes[j]…

利用srs进行视频流转发

框图如下 docker-compose.yaml如下 rtmp2rtc.conf的配置如下 就增加了 #配置如下 forward {enabled on;#开启转发backend http://192.168.0.131:6789/api/v1/forward; #有视频流数据后会调用这个接口} #回调的参数如下 Received payload: {actionon_forward, server_idvid-k2…

字节微前端框架Garfish

Garfish 是字节跳动开源的微前端框架,旨在应对现代 Web 应用在前端生态繁荣与应用日益复杂化背景下的挑战。本文将介绍如何使用 Garfish,提供代码示例,并与另一流行的微前端框架 Qiankun 进行对比分析。 安装 Garfish 首先,安装…

深度学习11--GAN进阶与变种

基础 GAN 存在的问题 在开始讲解变种之前,首先讲一下GAN 存在的问题。第一个问题就是判别器D太强了,损失都是0。假设判别器D能力强,G vl生成的图片与真实图片相差巨大,G v2生成的图片与真实图片相差不多,但是判别器都能…