文章目录

  • 1. 为什么要学习DSSM双塔模型
  • 2. DSSM模型理论知识
    • 2.1 DSSM模型的原理
    • 2.2 DSSM深度语义匹配模型整体结构
      • 2.2.1 输入层
      • 2.2.2 表示层
      • 2.2.3 匹配层
    • 2.3 DSSM模型的优缺点
  • 3. 推荐领域中的DSSM双塔模型
    • 3.1 从NLP领域跨界到推荐领域的DSSM
    • 3.2 朴素的DSSM双塔模型,2015
    • 3.3 百度的双塔模型
    • 3.4 谷歌的双塔模型,2019
  • 4. 实战广告推荐的双塔模型
    • 4.1 广告推荐业务场景
    • 4.2 广告推荐的DSSM双塔模型结构
      • 4.2.1 输入层
      • 4.2.2 表示层
      • 4.2.3 匹配层
  • 5. 总结
  • 6. 代码实现

本文主要介绍项目中用于商业兴趣建模的DSSM双塔模型。作为推荐领域中大火的双塔模型,因为效果不错并且对工业界十分友好,所以被各大厂广泛应用于推荐系统中。

通过构建user和item两个独立的子网络,将训练好的两个“塔”中的user embedding 和item embedding各自缓存到内存数据库中。线上预测的时候只需要在内存中计算相似度运算即可。DSSM双塔模型是推荐领域不中不得不会的重要模型。

1. 为什么要学习DSSM双塔模型

我们标签组主要的服务对象是广告主,服务目标是为广告主提供更好的广告转换效果。这里涉及到两种建模。

一种是自然兴趣建模,根据用户操作终端行为获得user-item关联,给不同的数据源打标获得item-tag关联,最后将上面两种关联进行join操作得到user-tag的关联实现给用户打上兴趣标签,这里相当于是从标签维度为广告主推荐人群。

另一种就是商业兴趣建模,在自然兴趣建模的基础上,从广告维度为广告主推荐人群,那么就需要目前大火的DSSM双塔模型了。

拿Youtube视频推荐系统举例,一般推荐系统中有两个流程。

第一步是召回模型,主要是进行初筛操作,从海量视频资源池中初步选择一部分用户可能感兴趣的视频数据子集,从数量上看可能是从千万级别筛选出百级别。

第二步是精排模型,主要作用是对上面找到的百级别的视频子集进一步精筛,从数量上看可能是从百级别筛选出几十级别。然后根据得分高低排序,生成一个排序列表作为用户的候选播放列表从而完成视频推荐任务。

我们广告推荐领域中使用的DSSM双塔模型是从广告维度为广告主推荐一定数量的人群,从数量上看是从百亿级别人群中找出百万级人群用于投放广告,所以是召回模型。

2. DSSM模型理论知识

2.1 DSSM模型的原理

DSSM(Deep Structured Semantic Models)也叫深度语义匹配模型,最早是微软发表的一篇应用于NLP领域中计算语义相似度任务的文章。

DSSM深度语义匹配模型原理很简单:获取搜索引擎中的用户搜索query和doc的海量曝光和点击日志数据,训练阶段分别用复杂的深度学习网络构建query侧特征的query embedding和doc侧特征的doc embedding,线上infer时通过计算两个语义向量的cos距离来表示语义相似度,最终获得语义相似模型。这个模型既可以获得语句的低维语义向量表达sentence embedding,还可以预测两句话的语义相似度。

2.2 DSSM深度语义匹配模型整体结构

DSSM模型总的来说可以分成三层结构,分别是输入层、表示层和匹配层。结构如下图所示:

2.2.1 输入层

输入层主要的作用就是把文本映射到低维向量空间转化成向量提供给深度学习网络。NLP领域里中英文有比较大的差异,在输入层处理方式不同。

(1) 英文场景

英文的输入层通过Word Hashing方式处理,该方法基于字母的n-gram,主要作用是减少输入向量的维度。举例说明,假如现在有个词boy,开始和结束字符分别用#表示,那么输入就是(#boy#)。将词转化为字母n-gram的形式,如果设置n为3,那么就能得到(#bo,boy,oy#)三组数据,将这三组数据用n-gram的向量来表示。

使用Word Hashing方法存在的问题是可能造成冲突。因为两个不同的词可能有相同的n-gram向量表示。下图是在不同的英语词典中分别使用2-gram和3-gram进行Word Hashing时的向量空间以及词语碰撞统计:


可以看出在50W词的词典中如果使用2-gram,也就是两个字母的粒度来切分词,向量空间压缩到1600维,产生冲突的词有1192个(这里的冲突是指两个词的向量表示完全相同,因为单词储量实在有限,本来想找几个例子说明下,结果没找到)。如果使用3-gram向量空间压缩到3W维,产生冲突的词只有22个。综合下来论文中使用3-gram切分词。

(2) 中文场景

中文输入层和英文有很大差别,首先要面临的是分词问题。如果要分词推荐jieba或者北大pkuseg,不过现在很多模型已经不进行分词了,比如BERT中文的预训练模型就直接使用单字作为最小粒度了。

2.2.2 表示层

DSSM模型表示层使用的是BOW(bag of words)词袋模型,没有考虑词序的信息。不考虑词序其实存在明显的问题,因为一句话可能词相同,但是语义则相差十万八千里,比如“我爱女朋友”和“女朋友爱我”可能差距蛮大的(这个小伙伴们自己体会)。

下图是DSSM表示层的结构:


最下面的Term Vector到Word Hashing将词映射到3W维的向量空间中。然后分别经过两层300维度的隐藏层,最后统一输出128维度的向量。

2.2.3 匹配层

现在我们把query和doc统一转换成了两个128维的语义向量,接下来如何计算它们的语义相似度呢?通过cos函数计算这两个向量的余弦相似度就可以了,公式如下:

2.3 DSSM模型的优缺点

先说说DSSM模型的优点:

  • 解决了LSA、LDA、Autoencoder等方法存在的字典爆炸问题,从而降低了计算复杂度。因为英文中词的数量要远远高于字母n-gram的数量;
  • 中文方面使用字作为最细切分粒度,可以复用每个字表达的语义,减少分词的依赖,从而提高模型的泛化能力;
  • 字母的n-gram可以更好的处理新词,具有较强的鲁棒性;
  • 使用有监督的方法,优化语义embedding的映射问题;
  • 省去了人工特征工程;
  • 采用有监督训练,精度较高。传统的输入层使用embedding的方式(比如Word2vec的词向量)或者主题模型的方式(比如LDA的主题向量)做词映射,再把各个词的向量拼接或者累加起来。由于Word2vec和LDA都是无监督训练,会给模型引入误差。

再说说DSSM模型的缺点:

  • Word Hashing可能造成词语冲突;
  • 采用词袋模型,损失了上下文语序信息。这也是后面会有CNN-DSSM、LSTM-DSSM等DSSM模型变种的原因;
  • 搜索引擎的排序由多种因素决定,用户点击时doc排名越靠前越容易被点击,仅用点击来判断正负样本,产生的噪声较大,模型难以收敛;
  • 效果不可控。因为是端到端模型,好处是省去了人工特征工程,但是也带来了端到端模型效果不可控的问题。

3. 推荐领域中的DSSM双塔模型

3.1 从NLP领域跨界到推荐领域的DSSM

DSSM深度语义匹配模型最早是应用于NLP领域中计算语义相似度任务。因为语义匹配本身是一种排序问题,和推荐场景不谋而合,所以DSSM模型被自然的引入到推荐领域中。DSSM模型分别使用相对独立的两个复杂网络构建用户相关特征的user embedding和item相关特征的item embedding,所以称为双塔模型。

3.2 朴素的DSSM双塔模型,2015

双塔模型最大的特点是user和item是独立的两个子网络,对工业界十分友好。将两个塔各自缓存,线上预测的时候只需要在内存中进行相似度运算即可。下面是2015年朴素的DSSM双塔模型结构:

3.3 百度的双塔模型


百度的双塔模型分别使用复杂的网络对用户相关的特征和广告相关的特征进行embedding,分别形成两个独立的塔,在最后的交叉层之前用户特征和广告特征之间没有任何交互。这种方案就是训练时引入更多的特征完成复杂网络离线训练,然后将得到的user embedding和item embedding存入redis这一类内存数据库中。线上预测时使用LR、浅层NN等轻量级模型或者更方便的相似距离计算方式。这也是业界很多大厂采用的推荐系统的构造方式。

3.4 谷歌的双塔模型,2019

2019年谷歌推出自己的双塔模型,文章的核心思想是:在大规模的推荐系统中,利用双塔模型对user-item对的交互关系进行建模,从而学习【用户,上下文】向量和【item】向量的关联。针对大规模流数据,提出in-batch softmax损失函数与流数据频率估计方法更好的适应item的多种数据分布。

利用双塔模型构建Youtube视频推荐系统,对于用户侧的塔根据用户观看视频特征构建user embedding,对于视频侧的塔根据视频特征构建video emebdding。两个塔分别是相互独立的网络。

4. 实战广告推荐的双塔模型

4.1 广告推荐业务场景

讲了上面一大堆,就是为了这一节构建咱们广告推荐的DSSM双塔模型。对应到咱们的广告业务就是构建DSSM双塔模型,用户侧输入用户对广告的历史行为特征(包括点击、下载、付费等)从而得到固定长度的user embedding,同理广告侧输入广告特征得到相同长度的ad embedding,分别存入redis内存数据库中。

线上infer时给定一个广告ad,然后分别和全量用户求相似度,找到“距离最近”的user子集,对这部分人群投放广告从而完成广告推荐任务。

4.2 广告推荐的DSSM双塔模型结构

模型整体结构如下图所示,也分成三层:输入层、表示层和匹配层。

4.2.1 输入层

模型训练分成两座不同的“塔”分别进行,其实也就是两个不同的神经网络。其中一座塔是用于生成user embedding。输入用户特征训练数据,用户特征包括用户稠密特征和用户稀疏特征,其中用户稠密特征进行one-hot编码操作,用户稀疏特征进行embedding降维到低维空间(64或者32维),然后进行特征拼接操作。广告侧和用户侧类似。

关于里面的特征,不在于你要什么,而在于你有什么。整个工程超级复杂的就是这块的特征工作。这里不再赘述。

4.2.2 表示层

得到拼接好的特征之后会提供给各自的深度学习网络模型。用户特征和广告特征经过各自的两个全连接层后转化成了固定长度的向量,这里得到了维度相同的user embedding和ad embedding。各塔内部的网络层数和维度可以不同,但是输出的维度必须是一样的,这样才能在匹配层进行运算。项目中user embedding和ad embedding 维度都是32。

4.2.3 匹配层

模型训练好了之后会分别得到user embedding和ad embedding,将它们存储到redis这一类内存数据库中。如果要为某个特定的广告推荐人群,则将该广告的ad embedding分别和所有人群的user embedding计算cos相似度。选择距离最近的N个人群子集作为广告投放人群,这样就完成了广告推荐任务。模型训练过程中将cos函数得到的结果进入sigmoid函数和真实标签计算logloss,查看网络是否收敛。模型评估主要使用auc指标。

小结下,本节讲了下我们使用DSSM双塔模型完成广告推荐任务。模型整体结构分成输入层、表示层和匹配层。首先在输入层处理数据获取特征;然后在表示层通过深度学习网络得到user embedding和ad embedding;最后在匹配层进行广告推荐。

5. 总结

本篇主要介绍了项目中用于商业兴趣建模的DSSM双塔模型。作为推荐领域中大火的双塔模型,最大的特点是效果不错并且对工业界十分友好,所以被各大厂广泛应用于推荐系统中。

通过构建user和item两个独立的子网络,将训练好的两个塔中的user embedding 和item embedding各自缓存到内存数据库中。线上预测的时候只需要在内存中进行相似度运算即可。

首先,讲了下DSSM语义匹配模型的理论知识,最早是应用于NLP领域中用于语义相似度任务;然后,因为都是排序问题,所以引入到推荐领域。从朴素的DSSM双塔模型到各大长的双塔模型;最后,讲了下我们使用DSSM双塔模型实战到广告推荐场景。

6. 代码实现

数据集链接:https://pan.baidu.com/s/1Wv3WARahGVMGEroCrhcGng
提取码:iyq9

import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt# 1. 读取和处理数据
df_user = pd.read_csv("./datas/ml-1m/users.dat",sep="::", header=None, engine="python",names = "UserID::Gender::Age::Occupation::Zip-code".split("::"))df_movie = pd.read_csv("./datas/ml-1m/movies.dat",sep="::", header=None, engine="python",names = "MovieID::Title::Genres".split("::"))df_rating = pd.read_csv("./datas/ml-1m/ratings.dat",sep="::", header=None, engine="python",names = "UserID::MovieID::Rating::Timestamp".split("::"))df_rating.to_csv("./datas/ml-latest-small/ratings_1m.csv", index=False)import collections# 只取频率最高的电影分类
genre_count = collections.defaultdict(int)
for genres in df_movie["Genres"].str.split("|"):for genre in genres:genre_count[genre] += 1# 只保留最有代表性的题材
def get_highrate_genre(x):sub_values = {}for genre in x.split("|"):sub_values[genre] = genre_count[genre]return sorted(sub_values.items(), key=lambda x:x[1], reverse=True)[0][0]
df_movie["Genres"] = df_movie["Genres"].map(get_highrate_genre)# 给列新增数字索引列¶
# 目的是:防止embedding过大
def add_index_column(param_df, column_name):values = list(param_df[column_name].unique())value_index_dict = {value:idx for idx,value in enumerate(values)}param_df[f"{column_name}_idx"] = param_df[column_name].map(value_index_dict)add_index_column(df_user, "UserID")
add_index_column(df_user, "Gender")
add_index_column(df_user, "Age")
add_index_column(df_user, "Occupation")
add_index_column(df_movie, "MovieID")
add_index_column(df_movie, "Genres")df_user.to_csv("./datas/ml-latest-small/tensorflow_user_datawithindex.csv", index=False)df_movie.to_csv("./datas/ml-latest-small/tensorflow_movie_datawithindex.csv", index=False)
df_movie.head()# 合并成一个df
df = pd.merge(pd.merge(df_rating, df_user), df_movie)
df.drop(columns=["Timestamp", "Zip-code", "Title"], inplace=True)num_users = df["UserID_idx"].max() + 1
num_movies = df["MovieID_idx"].max() + 1
num_genders = df["Gender_idx"].max() + 1
num_ages = df["Age_idx"].max() + 1
num_occupations = df["Occupation_idx"].max() + 1
num_genres = df["Genres_idx"].max() + 1min_rating = df["Rating"].min()
max_rating = df["Rating"].max()df["Rating"] = df["Rating"].map(lambda x : (x-min_rating)/(max_rating-min_rating))#构建训练数据集
df_sample = df.sample(frac=0.1)
X = df_sample[["UserID_idx","Gender_idx","Age_idx","Occupation_idx","MovieID_idx","Genres_idx"]]
y = df_sample.pop("Rating")# 2. 搭建双塔模型并训练
def get_model():"""函数式API搭建双塔DNN模型"""# 输入user_id = keras.layers.Input(shape=(1,), name="user_id")gender = keras.layers.Input(shape=(1,), name="gender")age = keras.layers.Input(shape=(1,), name="age")occupation = keras.layers.Input(shape=(1,), name="occupation")movie_id = keras.layers.Input(shape=(1,), name="movie_id")genre = keras.layers.Input(shape=(1,), name="genre")# user 塔user_vector = tf.keras.layers.concatenate([layers.Embedding(num_users, 100)(user_id),layers.Embedding(num_genders, 2)(gender),layers.Embedding(num_ages, 2)(age),layers.Embedding(num_occupations, 2)(occupation)])user_vector = layers.Dense(32, activation='relu')(user_vector)user_vector = layers.Dense(8, activation='relu',name="user_embedding", kernel_regularizer='l2')(user_vector)# movie塔movie_vector = tf.keras.layers.concatenate([layers.Embedding(num_movies, 100)(movie_id),layers.Embedding(num_genres, 2)(genre)])movie_vector = layers.Dense(32, activation='relu')(movie_vector)movie_vector = layers.Dense(8, activation='relu',name="movie_embedding", kernel_regularizer='l2')(movie_vector)# 每个用户的embedding和item的embedding作点积dot_user_movie = tf.reduce_sum(user_vector * movie_vector, axis=1)dot_user_movie = tf.expand_dims(dot_user_movie, 1)output = layers.Dense(1, activation='sigmoid')(dot_user_movie)return keras.models.Model(inputs=[user_id, gender, age, occupation, movie_id, genre], outputs=[output])model = get_model()
model.compile(loss=tf.keras.losses.MeanSquaredError(),optimizer=keras.optimizers.RMSprop())fit_x_train = [X["UserID_idx"],X["Gender_idx"],X["Age_idx"],X["Occupation_idx"],X["MovieID_idx"],X["Genres_idx"]]from datetime import datetime
TIMESTAMP = "{0:%Y-%m-%dT%H-%M-%S/}".format(datetime.now())
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="./logs/logs_"+TIMESTAMP)history = model.fit(x=fit_x_train,y=y,batch_size=32,epochs=5,verbose=1,callbacks=[tensorboard_callback]
)# 3. 模型的预估-predictinputs = df.sample(frac=1.0)[["UserID_idx","Gender_idx","Age_idx","Occupation_idx","MovieID_idx", "Genres_idx"]].head(10)# 对于(用户ID,召回的电影ID列表),计算分数
model.predict([inputs["UserID_idx"],inputs["Gender_idx"],inputs["Age_idx"],inputs["Occupation_idx"],inputs["MovieID_idx"],inputs["Genres_idx"]])#模型的保存
model.save("./datas/ml-latest-small/tensorflow_two_tower.h5")new_model = tf.keras.models.load_model("./datas/ml-latest-small/tensorflow_two_tower.h5")new_model.predict([inputs["UserID_idx"],inputs["Gender_idx"],inputs["Age_idx"],inputs["Occupation_idx"],inputs["MovieID_idx"],inputs["Genres_idx"]])#4. 保存模型的embedding可用于召回user_layer_model = keras.models.Model(inputs=[model.input[0], model.input[1], model.input[2], model.input[3]],outputs=model.get_layer("user_embedding").output
)
user_embeddings = []
for index, row in df_user.iterrows():user_id = row["UserID"]user_input = [np.reshape(row["UserID_idx"], [1, 1]),np.reshape(row["Gender_idx"], [1, 1]),np.reshape(row["Age_idx"], [1, 1]),np.reshape(row["Occupation_idx"], [1, 1])]user_embedding = user_layer_model(user_input)embedding_str = ",".join([str(x) for x in user_embedding.numpy().flatten()])user_embeddings.append([user_id, embedding_str])df_user_embedding = pd.DataFrame(user_embeddings, columns = ["user_id", "user_embedding"])
df_user_embedding.head()output = "./datas/ml-latest-small/tensorflow_user_embedding.csv"
df_user_embedding.to_csv(output, index=False)# 得到movie embeddingmovie_layer_model = keras.models.Model(inputs=[model.input[4], model.input[5]],outputs=model.get_layer("movie_embedding").output
)movie_embeddings = []
for index, row in df_movie.iterrows():movie_id = row["MovieID"]movie_input = [np.reshape(row["MovieID_idx"], [1, 1]),np.reshape(row["Genres_idx"], [1, 1])]movie_embedding = movie_layer_model(movie_input)embedding_str = ",".join([str(x) for x in movie_embedding.numpy().flatten()])movie_embeddings.append([movie_id, embedding_str])df_movie_embedding = pd.DataFrame(movie_embeddings, columns = ["movie_id", "movie_embedding"])
df_movie_embedding.head()

原创不易,转载请注明出处


参考资料:

LearningDeep Structured Semantic Models for Web Search using Clickthrough Data

Sampling-bias-corrected neural modeling for largecorpus item recommendations

推荐系统中的双塔模型相关推荐

  1. 涨点利器:推荐系统中对双塔模型的各种改造升级(上)

    双塔各种改造方法概览: 大型推荐系统通常会将整个推荐链路拆分成召回.粗排.精排和重排等多个模块,以达到推荐效果和计算性能之间的平衡. 由于召回模型的候选item通常是海量的全库物品.粗排模型的候选it ...

  2. 从DSSM语义匹配到Google的双塔深度模型召回和广告场景中的双塔模型思考

    ▼ 相关推荐 ▼ 1.基于DNN的推荐算法介绍 2.传统机器学习和前沿深度学习推荐模型演化关系 3.论文|AGREE-基于注意力机制的群组推荐(附代码) 4.论文|被"玩烂"了的协 ...

  3. 推荐系统中特征交叉模型之——DeepWide/DeepFM/NFM

    前言 上篇文章中我们引入了推荐系统中特征交叉的概念,以及介绍了一些常见的特征交叉方法,这篇文章我们将详细地讨论一下推荐系统中特征交叉地模型,他们的特点,以及他们为什么会这样.本文中介绍的模型有Wide ...

  4. 推荐系统中精排模型的多目标优化

    0.背景 优化单个目标训练而得的点击率预估模型偏向过重,容易引发bad case(如信息流推荐中单一优化pCTR引起标题党) 从整体上拓宽用户从点击到转化漏斗的宽度,而不是单独拓宽某一层 满足多业务方 ...

  5. 【推荐系统】DSSM双塔模型浅析

    文章目录 一.DSSM模型 1.1 DSSM模型架构 1.2 模型原理 二.负样本构造的6个常用方法 2.1 曝光未点击数据 2.2 全局随机选择负例 2.3 Batch内随机选择负例 2.4 曝光数 ...

  6. 推荐系统(十七)双塔模型:微软DSSM模型(Deep Structured Semantic Models)

    推荐系统(十七)双塔模型:微软DSSM模型(Deep Structured Semantic Models) 推荐系统系列博客: 推荐系统(一)推荐系统整体概览 推荐系统(二)GBDT+LR模型 推荐 ...

  7. 推荐系统中粗排扮演的角色和算法发展历程

    猜你喜欢 1.如何搭建一套个性化推荐系统? 2.某视频APP推荐详解(万字长文) 3.微博推荐算法实践与机器学习平台演进 4.腾讯PCG推荐系统应用实践 5.强化学习算法在京东广告序列推荐场景的应用 ...

  8. Embedding技术在推荐系统中的应用

    编辑:子墨为客 来源:<深度学习推荐系统>笔记,并进行补充和说明 1.Embedding 是什么 Embedding是用一个低维稠密的向量来"表示"一个对象(这里的对象 ...

  9. 召回模型:DSSM双塔模型

    文章目录 DSSM(2013) DNN for Computing Semantic Features Word Hashing Youtube双塔模型(2019) Modeling Framewor ...

最新文章

  1. adb命令中的keyevent事件
  2. Kernel中如何操作CPU及外设寄存器
  3. Linux系统用户账号的管理技巧
  4. HCIE理论-IPV6
  5. 染用计算机语言,着色器语言
  6. CPP 获取目录下的文件
  7. 如何进行项目管理?企业项目管理常见的组织形式有哪些?
  8. 登录界面BootStramp模板
  9. python中可迭代对象是什么意思_python中可迭代对象是什么
  10. 谷歌Chrome浏览器主页被毒霸篡改
  11. String 翻转字符串
  12. 分享三个网页访问(点击)统计脚本,展示访问来源地图分布
  13. 基于混合高斯分布的EM算法提取声音特征并识别男女性别
  14. 在线博客系统——获取用户信息,退出登录
  15. 营销思维篇:透漏22个我赚钱的小秘密
  16. 源码天空java新闻_Java UpdateRequest类代码示例
  17. linux查看内存、cpu占用情况
  18. 房贷利率下调 现在是买房的时机吗?
  19. 唐朝为什么出现多次官修家谱的现象?唐朝家谱的形式有什么变化?
  20. 三维动画制作软件测试指标,三维动画设计师需要具备哪些条件

热门文章

  1. 博通蓝牙linux,好消息,使用Broadcom蓝牙的有福了,新驱动,新选择,BrcmPatchRAM...
  2. 国内AdSense/Youtube如何回款?AdSense/Youtube无限额结汇
  3. 多元正态分布的性质和定理
  4. \t\t工信部:要求对网站主办者身份信息当面核验
  5. 2021-10-23数据中心基础设施三大件:计算、存储和网络。网络是最大的问题!
  6. visio使用小知识
  7. H3c Smart-link 实验
  8. 《Adobe Illustrator CS6中文版经典教程(彩色版)》—第0课0.15节使用“外观”面板与效果...
  9. mysql实现增量备份
  10. h3c linux驱动 wn612_产品技术-H3C WN612双频无线网卡(802.11n)-新华三集团-H3C