环境

步骤

安装ssh

使用root权限。

1
sudo -i

更新软件包列表。

1
apt-get update

安装OpenSSH服务器,其中openssh-server是 OpenSSH 服务器的包名,它提供了 SSH 服务,允许你通过 SSH 协议远程连接到该系统。

1
apt-get install openssh-server

检查是否安装成功。执行 ps -e | grep ssh会列出所有与 SSH 相关的进程。运行结果中,sshd 是 SSH 服务守护进程,表示 OpenSSH 服务器正在运行。

1
ps -e|grep ssh
图1

安装pdsh

pdsh是一个用于并行执行命令的工具,它可以在多台机器上同时执行相同的命令,非常适合在集群或多个远程主机上执行批量操作。

1
apt-get install pdsh

修改/etc/profile:

1
2
cd /etc
vim profile

在该文件增加以下一行代码,设置SSH为pdsh远程命令的执行方式。

1
export PDSH_RCMD_TYPE=ssh
图2

配置ssh密钥

生成一对 RSA 类型的 SSH 密钥,并且设置一个空的密码短语(即不使用密码)。

1
ssh-keygen -t rsa -P ""
图3

可以看到密钥这些保存的位置在/root下面,跳转到/root目录下,输入ls -a可以看到.ssh文件

图4

进入.ssh文件夹可以看到id_rsa和id_rsa.pub两个文件,前者保存着私钥,后者保存着公钥。

在.ssh文件夹中执行以下命令,这条命令将本地的 SSH 公钥 (id_rsa.pub) 添加到远程服务器上的authorized_keys文件中,从而允许使用对应的私钥进行 SSH 无密码登录。:

1
cat id_rsa.pub >>authorized_keys
图5

测试SSH 服务是否正常工作。第一次连接会询问是否确定继续连接,输入yes就行了。

1
ssh localhost
图6

安装Java 8

1
apt-get install openjdk-8-jdk

检查安装目录,如果是在根目录下安装,那么JAVA HOME路径应该是/usr/lib/jvm/java-1.8.0-openjdk-amd64,这个后面会用得到。

1
2
cd /usr/lib/jvm/
ls

设置java环境变量。

1
2
3
4
vim ~/.bashrc
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64
export PATH=$PATH:$JAVA_HOME/bin
source ~/.bashrc

通过echo $JAVA_HOME命令来检查环境变量是否设置正确,如果设置正确将会返回路径。

下载Hadoop

以下命令下载并解压了hadoop3.3.6,并将解压后的文件夹重命名为hadoop。我下载的路径是/home/Hadoop,这个路径后面用得到。

1
2
3
4
5
cd /home
mkdir Hadoop
wget https://downloads.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz
tar -xzf hadoop-3.3.6.tar.gz
mv hadoop-3.3.6 hadoop

执行完以上命令后应该能在/home/Hadoop/hadoop路径下看到以下文件。

图8

配置Hadoop文件

进入/home/Hadoop/hadoop/etc/hadoop目录,对这个目录下的一些文件进行配置。具体代码含义在此不赘述。

配置hadoop-env.sh

使用vim hadoop-env.sh命令修改hadoop-env.sh文件。该文件中大部分都是注释起来的,不用管。可以直接在文件末尾增加以下内容。

1
2
3
4
5
6
JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root
图9

配置core-site.xml

使用vim core-site.xml命令修改/home/Hadoop/hadoop/etc/hadoop目录下的core-site.xml文件。将文件中的configuration修改为以下内容。

1
2
3
4
5
6
7
8
9
10
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/home/Hadoop/hadooptest/hdata</value>
</property>
</configuration>
图10

配置hdfs-site.xml

与core-site.xml类似,在hdfs-site.xml中修改。注意只修改configuration内的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configuration>
<property>
<name>dfs.namenode.rpc-address</name>
<value>localhost:9000</value> <!-- 根据实际环境调整 -->
</property>
<property>
<name>dfs.namenode.http-address</name>
<value>localhost:50070</value> <!-- 根据实际环境调整 -->
</property>
<property>
<name>dfs.replication</name>
<value>1</value> <!-- 副本数可根据需求调整 -->
</property>
</configuration>

配置mapred-site.xml

同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=/home/Hadoop/hadoop</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=/home/Hadoop/hadoop</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=/home/Hadoop/hadoop</value>
</property>
</configuration>

配置yarn-site.xml

同理。

1
2
3
4
5
6
7
8
9
10
11
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
</configuration>

配置bash.bashrc文件

进入/etc目录,在该目录下vim bash.bashrc,在这个文件中增加以下配置:

1
2
3
4
5
6
7
export HADOOP_HOME="/home/Hadoop/hadoop"
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
export HADOOP_MAPRED_HOME=${HADOOP_HOME}
export HADOOP_COMMON_HOME=${HADOOP_HOME}
export HADOOP_HDFS_HOME=${HADOOP_HOME}
export YARN_HOME=${HADOOP_HOME}

在修改完后,运行以下代码使其生效。

1
source bash.bashrc

格式化hdfs文件系统

该命令会清空并格式化 Hadoop 的 NameNode,并创建所需的元数据文件。仅在第一次配置或重新初始化文件系统时使用。

1
hdfs namenode -format

启动HDFS

通过以下命令来启动namenode、datanode、secondarynamenode

1
2
3
$HADOOP_HOME/bin/hdfs --daemon start namenode
$HADOOP_HOME/bin/hdfs --daemon start datanode
$HADOOP_HOME/bin/hdfs --daemon start secondarynamenode

启动YARN集群。

1
$HADOOP_HOME/sbin/start-yarn.sh  

可通过http://localhost:8088访问ResourceManager Web UI

图11

可通过http://localhost:50070访问NameNode Web UI

图12

运行 MapReduce 作业

  1. 创建一个测试文件:

    1
    echo -e "hello world\nhello hadoop\nhadoop world" > input.txt

    上传到 HDFS:

    1
    2
    hdfs dfs -mkdir -p /user/ubuntu/input
    hdfs dfs -put input.txt /user/ubuntu/input
  2. 运行 Hadoop 自带的 WordCount 示例

    1
    hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar wordcount /user/ubuntu/input /user/ubuntu/output

    查看输出:

    1
    hdfs dfs -cat /user/ubuntu/output/part-r-00000
  3. 清理数据:

    1
    hdfs dfs -rm -r /user/ubuntu/output

NeuralCF

paper: Neural Collaborative Filtering

矩阵分解

在协同过滤中提到可以用矩阵分解的方法来解决向量过于稀疏的问题。从深度学习的视角看待矩阵分解模型,那么矩阵分解层的用户隐向量和物品隐向量完全可以看作⼀种Embedding方法。

矩阵分解方法中,我们先将用户向量和物品向量通过一个权重矩阵映射成稠密的低维隐向量,然后再将两个隐向量求内积得到相似度,这⾥的“相似度”就是对评分的预测。

因为矩阵分解模型结构较为简单,实际使用往往会欠拟合。

NeuralCF

NeuralCF简单来说,就是用更加复杂的神经网络来替换矩阵分解中的内积操作。

GMF

GMF即广义的矩阵分解模型。矩阵分解模型中采用了内积的互操作方式。为了进⼀步让向量在各维度上进⾏充分交叉,可以通过求用户隐向量和物品隐向量的哈达玛积(即长度相等的向量对应位置相乘)得到一个新的向量,再将新向量通过一个线性层拟合预测目标。

这种方式跟传统的矩阵分解相比,相当于给哈达玛积的每个维度加上了一个权重(事实上,内积就等于哈达玛积各个维度之和,而线性层相当于把哈达玛积各个维度做加权和)。因此这种方法表达能力更强,但仍然比较有限。

MLP

可以用MLP来替代矩阵分解中的内积操作,这使得模型能够拟合更加复杂的情况。具体而言,这种方法会先将用户隐向量和物品隐向量进行拼接,然后通过多个全连接层,最后映射成一维来拟合预测目标。这种方法结构上与DNN十分相似。

混合方法

除了用单种互操作,还可以把通过不同互操作网络得到的特征向量拼接起来,交由输出层进⾏⽬标拟合。

neuralCF

上图整合了前面提到的GMF和MLP方法。首先在embedding层映射成两组用户隐向量和物品隐向量,分别用于GMF和MLP。然后将GMF和MLP生成的向量进行拼接进入输出层,最终拟合预测目标。

NeuralCF实际上是一个模型框架,基于⽤户向量和物品向量这两个Embedding层,利⽤不同的互操作层进行特征的交叉组合,并且可以灵活地进行不同互操作层的拼接。

由于基于协同过滤的思想,NeuralCF模型也是基于共现矩阵,并没有引入更多其他类型的特征

PNN

paper: Product-based Neural Networks for User Response Prediction

PNN模型的提出同样是为了解决CTR预估问题。PNN使用乘积层(Product Layer)代替了Deep Crossing模型的stacking层,从而更有针对性地获取特征之间的交叉信息。在其余部分,PNN与Deep Crossing基本相同,因此在此重点讨论PNN模型的乘积层。

pnn

如模型图所示,PNN的乘积层分为Z部分和P部分。其中Z部分较为简单,实际上就是所有特征的embedding向量直接进行拼接,在图中表现为所有特征都乘以常量1。

复杂的是P部分。P部分的元素。这里的g是一个可替换的映射函数,是特征的embedding向量。

在得到Z和P之后,两部分先分别映射成维的向量,然后再进行拼接输入上层神经网络。

关于g函数论文里给出了内积外积两种映射方法。

内积

一种方法是对所有特征的embedding向量两两做内积运算: 两个向量的内积是一个标量,因此最终P是一个N×N矩阵(N为特征域数量)。设embedding维度为M,则每次求内积复杂度为O(M),求P矩阵的复杂度为

接下来看P矩阵如何映射成向量。

从P到事实上就是一个全连接网络。对于向量中的每个元素,有。其中⊙表示内积。容易看出这一部分的复杂度为

复杂度可通过矩阵分解进行优化。

外积

使用外积操作时,g函数变为了: 两个embedding向量求外积的结果是M×M矩阵,因此生成的P是一个N×N×M×M的张量。显然会产生很大的时间复杂度。论文中提出使用“叠加”的方法来降低时间复杂度,也就是直接对所有外积进行求和: 可以看出,叠加矩阵P就是让所有embedding向量做一个sum pooling,再进行外积操作,最终得到的P是个M×M矩阵。

这样做虽然降低了复杂度,但是会丢失很多有价值的信息。比如,年龄和性别两个特征不在同一个向量空间中,不具备任何可比性,因此强行相加不具备实际意义。而同类的embedding可以进行相加,比如将⽤户浏览过的多个物品的embedding进⾏平均。

相比于全连接层无差别化的处理,PNN模型更有针对性地强调了不同特征之间的交互,从而让模型更容易捕获特征的交叉信息。

PNN模型的局限性在于,为了优化训练效率而采用的大量简化操作会丢失有价值的信息,并且对所有特征进行无差别的交叉忽略了原始特征向量中包含的有价值信息。

Wide&Deep

paper: Wide & Deep Learning for Recommender Systems

Wide&Deep模型由单层的wide部分和多层的deep部分组成,使模型兼具逻辑回归的记忆能力和深层神经网络的泛化能力。

  • 记忆能力:模型直接学习并利用历史数据中物品或特征的共现频率的能力。
  • 泛化能力:模型传递特征的相关性,以及发掘稀疏特征与最终标签相关性的能力。

有些特征对最终标签有直接的影响,这样的特征称为“强特征”。对于这样的特征,使用协同过滤、逻辑回归这样简单的模型可以让模型记住这些特征对推荐结果强有力的影响。使用深层神经网络特征会被反复交叉,模型对特征的记忆反而没有那么深刻。

而深度神经网络这样复杂的网络的优势在于:

  • 引入了隐向量,使得数据稀少的用户和物品也能生成隐向量,将全局数据传递到稀疏物品上。
  • 特征多次自动组合,可以深度挖掘数据中的潜在模式。

Wide&Deep模型将简单模型和深度神经网络结合在一起:

Wide&Deep

Google Play团队将已安装应用和曝光应用作为wide部分的输入。这个选择是基于业务理解进行的。这两个特征经过Embedding之后同样会作为deep部分的输入。

Wide部分

以已安装应用和曝光应用两个特征为例。Wide部分组合这两个特征的函数被称为交叉积变换函数 当第i个特征属于第k个组合特征时,的值为1,否则为0。d为输入特征的维度,输入特征为

这个函数乍一看很不直观,事实上它非常简单,我们可以通过例子来理解。

例如第k个组合特征为AND(user_installe d_app=netflix,impression_app=pandora),则只有当“user_installed_app=netflix”和“impression_app=pandora”这两个特征同时为1时,才为1,否则为0。

为什么呢?假设user_installed_app=netflix这个特征为,值为0,则,故而最后连乘的结果也为0。有(0, 0)、(0, 1)、(1, 0)、(1, 1)这四种组合情况,只有当出现(0, 1)这种情况时,才会为0。

以上是论文中举的例子,如果这个例子还是比较费解也没关系,这里将举一个更详细的例子。

假设有抖音、bilibili、小红书三款app,则输入的向量为,前三个维度表示用户是否安装抖音、bilibili、小红书,后三个维度表示抖音、bilibili、小红书三个app是否曝光过。经过交叉积变化可以得到一个维度为9的向量。如果输入的向量为[1, 0, 0]和[0, 0, 1],则表示用户已安装了抖音并且已向其曝光小红书。那么组合的结果为[0, 0, 0, 0, 0, 0, 1, 0, 0],其中第7个维度表示(已安装应用=抖音,曝光应用=小红书)的组合。事实上,输出向量的维度可以经过人工筛选出一些特征组合。

Deep部分

Deep部分实际上就是深度神经网络,将稀疏特征进行embedding,和稠密变量拼接后进入MLP网络。

Deep&Cross

paper: DCN:Deep & Cross Network for Ad Click Predictions

Wide&Deep模型的Wide部分需要经过人工筛选,十分繁琐。如果特征组合使用二阶FM表达能力会不足,用高阶的则复杂度会过高。

Deep&Cross的主要思想使用Cross网络替代原来的Wide部分。Cross网络可以进行自动的特征交叉,并且复杂度是线性的。

从模型图可以看出,DCN网络同样是将稀疏特征进行embedding,然后和稠密特征进行拼接,分别进入Cross网络和深度网络,再将两个网络的输出连接后进入输出层。

DCN

Cross网络

Cross网络的目的是增加特征之间的交互力度。假设第l层交叉层的输出向量为,那么第l+1层的输出向量为: 交叉层

每个交叉层首先对求外积,然后乘以一个权重向量,在加上偏置向量和得到输出。

我们可以看到每一层的将上一层的输出作为一个加项。这类似于残差网络,使得输出不会偏离太多。

交叉层通过求外积的方式实现了特征交叉。

举个简单的例子,假设,则: 可以看出包含了所有特征的二阶项。同理,可以获得更加高阶的交叉。

FM与深度学习的结合

FNN

paper: Deep learning over multi-field categorical data- Acase study on user response prediction

FNN的核心在于使用FM的隐向量完成Embedding层的初始化。

神经网络的参数初始化往往采用随机初始化。由于Embedding层的输入极端稀疏,而且Embedding层的参数量十分庞大,往往占据整个神经网络的大半,因此Embedding层的收敛速度非常缓慢。

FNN用FM模型训练好的各特征隐向量初始化Embedding层参数,使得初始化的参数更加接近目标最优点,加速神经网络收敛。

FM的函数如下: FM会给每个特征i训练一个隐向量。我们使用这些隐向量来初始化Embedding层的权重。具体而言,假设FM隐向量的维度为m,第i个特征域的第k维特征的隐向量是,那么隐向量的第l维 就会成为连接输⼊神经元k和Embedding神经元l之间连接权重的初始值。注意我们在训练FM时是没有划分特征域的,但在embedding时则需要每个特征域都会有对应的embedding层。

举个例子。假如特征域i有3个维度,即输入神经元有3个。这三个维度对应的隐向量分别为,那么对应的embedding向量维度应该为2(embedding向量的维度),这个embedding向量会初始化为

FNN模型存在很多不足,例如:

  • 两阶段的训练模型,应用过程不太方便,且调参困难;
  • 模型能力受限于FM表征能力的上限;
  • 只关注于高阶组合特征交叉,容易丢失记忆能力;

但是FNN为embedding预训练提供了借鉴思路。

DeepFM

paper: DeepFM: A Factorization-Machine based Neural Network for CTR Prediction

DeepFM同样是对Wide&Deep模型的改进。Wide&Deep模型的Wide部分不具备自动的特征组合能力,往往需要人为地进行特征的选择,这一过程十分繁琐,并且人的经验不一定可靠。Deep&Cross模型同样是基于此进行改进。Deep&Cross使用Cross网络代替Wide部分,而DeepFM使用FM来代替Wide部分。

DeepFM

DeepFM的FM部分和DNN部分共享相同的Embedding层。图中的权重1连接是默认权重为1的连接,权重不需要进行训练;而标准连接的权重需要进行训练。

再来回忆一下FM的函数: 在DeepFM模型中,FM层由一阶部分和二阶部分构成。FM层的一阶部分对输入的稀疏特征求加权和,即图中FM层的加法操作;FM层的二阶部分直接使用embedding向量作为隐向量,即将特征Embedding后的结果两两作内积。

DNN部分与常规的模型没有什么区别,这里不再说明。

模型最后的输出为

论文作者将DeepFM与其他模型进行了比较,具体在前面介绍其他模型时有所提及,这里不再展开。

DeepFM与其他模型比较

NFM

paper: Neural Factorization Machines for Sparse Predictive Analytics

FM一般只能进行二阶的特征交叉,如果扩展到三阶以上会导致复杂度极高,这限制了FM模型的表达能力。NFM的核心思路在于,用一个表达能力更强的函数替代原FM中二阶隐向量内积的部分,即: NFM使用深度学习网络来替代FM的二阶部分,其模型结构如下:

NFM

NFM在embedding层和DNN之间引入了一个特征交叉池化层(Bi-Interaction Pooling),该层的数学表示为: 这个池化层和FM二阶部分的区别在于,这里用的是元素积,而FM用的是内积。事实上,内积就相当于把元素积的所有维度进行求和。

在经过池化后,向量继续进入上层神经网络。

相比于FM,NFM能进行更高阶的交叉。

相比于DNN,NFM在低层就能学到包含更多信息的组合特征,使后续隐藏层能更容易学到有用的高阶交叉特征,只需要很少的隐藏层就可以学到高阶的特征信息,使得模型结构更浅更简单。

图中的NFM忽略了低阶特征。如果加入一阶部分,相当于在Wide&Deep的Deep部分加入了特征交叉池化层,加强了特征交叉。

DIN

paper: Deep Interest Network for Click-Through Rate Prediction

DIN是阿里巴巴针对电商广告推荐提出的模型。DIN引入了用户历史行为特征。作者认为用户兴趣多种多样且变化多端,而用户的兴趣往往蕴含在其历史行为中。因此考虑用户的历史行为对于用户是否会点击广告很有帮助。DIN模型考虑用户的历史交互商品与当前商品广告的相关性,模拟出用户对目标商品的感兴趣程度,并将之作为一个特征输入模型。

模型结构

用户历史行为特征包括点击过的商品、店铺、类目等,对于用户的历史行为特征,DIN采用multi-hot的编码方式。

DIN

如模型结构图所示,在DIN的注意力机制中,候选广告作为query,用户历史行为特征作为key和value。key和query通过activation unit计算得到注意力分数(可以认为是每一个历史交互物品和候选广告的相关性),并使用注意力分数对value进行加权得到用户的兴趣表示。

这个过程可以理解为从用户的历史交互物品中提取出和候选广告相关的部分并进行加总。得到的向量是用户和候选物品相关的兴趣表示。这个向量和其他特征进行拼接,进入上层神经网络。

Activation unit的输入为key和value。key和value求元素积,并将其元素积、key、value三者拼接输入上层神经网络,得到最终的注意力分数(很多代码复现这里还加入了key和value之差,但我在原论文中似乎未找到关于这项操作的说明,可以根据实际效果进行激活单元结构的选择)。

训练细节

Mini-batch正则化

模型训练往往会产生过拟合的现象,常规的防止过拟合的做法有L1、L2正则化、dropout等。但在DIN的训练中,这些效果都不是很好。

当参数量过于庞大时,使用L1和L2正则化计算量会非常庞大。当不进行正则化时,使用SGD只需要更新当前batch非零稀疏特征的参数。而加入正则化后,每个batch都需要计算全部参数的l1或l2范数。

作者提出一种叫mini-batch aware regularization的方法,只需要计算每个小批次出现的稀疏特征参数的l2范数: 在这个函数中,是整个embedding矩阵,其中D是embedding向量的维度,K是总特征数。B是小批次的数量。表示是否非零。表示该mini-batch中特征j为1的样本数,因为这个函数会遍历整个输入特征矩阵(batch_size×K),每出现一次都会加一遍,因此需要除以以保证只加了一遍

进一步地,我们可以用表示小批次中是否存在第j个特征非零,然后将正则项近似为: 这与原来的函数区别在于,每项的系数不再固定为1了,而变成了。这意味着出现次数更多的特征会受到更小的惩罚。

Dice激活函数

PReLU是一个常用的激活函数,其定义如下: 其中。PReLU将0作为一个分界点,当每一层的输入有着不一样的分布时,固定地将0作为分界点并不合适。因此作者提出了Dice激活函数: 其中E[s]和Var[s]分别表示小批次的均值和方差。ϵ被设为常量。这里p(s)相当于对s进行标准化操作后再求sigmoid。这样p(s)就变成了一条平滑的曲线,并且曲线的中点为E[s]。Var[s]影响了p(s)函数的胖瘦,Var[s]越大,p(s)越胖。

PReLU和Dice

DIEN

paper: Deep Interest Evolution Network for Click-Through Rate Prediction

DIN将用户的兴趣考虑了进去。但是DIN所用的方法并不完全贴合实际情况,主要体现在用户的行为和兴趣并不完全等价,我们很难通过表现出来的行为去挖掘用户潜在的兴趣。DIN忽略了用户历史行为的序列信息,即只是基于所有历史购买行为综合推荐,而不是针对“下一次购买”推荐。

论文提出,人的兴趣是多种多样的,同一时刻下有着多种不同的兴趣,应该用兴趣状态来描述。每个兴趣都是动态变化的,都有属于它们各自的演化过程。并且兴趣的发展是有一定前后关联的。兴趣的多样性导致了兴趣漂移的现象:在相邻的访问中,用户的兴趣可能会存在很大的不同,并且用户的某个行为可能取决于很久前的行为。

基于兴趣的这些特点,作者引入了GRU的结构来从用户的序列信息中提取出用户的兴趣状态,模拟用户兴趣的演化规律。其中关键在于:

  • 从用户的历史行为中抽取出用户各个时刻下的兴趣状态;
  • 利用注意力机制找到与候选广告相关的那部分兴趣的演化过程,判断用户下一时刻对该兴趣的感兴趣程度。

DIEN使用兴趣进化网络(下图中彩色部分)来替换DIN中关于用户兴趣的embedding部分。兴趣进化网络分为行为序列层、兴趣抽取层、兴趣进化层三层,其中行为序列层与普通的embedding层一致,模拟用户兴趣的关键在于兴趣抽取层和兴趣进化层。

DIEN

兴趣抽取层

兴趣抽取层的基本结构是GRU。相比RNN,GRU解决了梯度消失的问题;相比LSTM,GRU的参数量更少。兴趣抽取层的作用在于,挖掘并提取出每个时刻下用户行为背后隐藏的兴趣状态。

每个GRU单元的公式如下: 其中是输入状态向量,也就是行为序列层的Embedding向量e(t),代表了用户的第t个行为。是GRU网络中第t个隐状态向量。表示元素积。

然而,隐状态向量仅捕获行为间的依赖关系,并不能有效表示兴趣。由于点击行为只和最终兴趣有关,因此常规的交叉熵损失函数只能监督最终的兴趣预测,而(t<T)不能得到有效的监督。因此,作者又提出了辅助损失,使用行为来监督兴趣状态的学习。除了使用真实的作为正样本以外,还从其他物品中选取一个作为负样本,这样就构成了一对正负样本(如模型图中的Auxiliary Loss部分)。表示第b个样本第t+1个行为的embedding向量。辅助损失函数为: 其中表示用户i的第t个隐藏状态。会乘以一个系数后加入到总损失中。

兴趣进化层

前面提到,兴趣具有多样性,并且存在兴趣漂移的现象。兴趣之间虽然可以相互影响,但每种兴趣都有其自己的进化路径。兴趣进化层的作用就在于,模拟与目标广告相关的兴趣进化路径。我们所关心的是和目标广告相关的兴趣,因此在兴趣进化层使用了注意力机制来提取出这部分兴趣。

兴趣进化层采用的注意力得分计算公式为: 其中为目标广告,计算出的相关性,随后通过softmax进行归一化处理。这里的注意力分数计算也可以使用更加复杂的模型,比如使用DIN的activation unit,以目标广告为query,隐状态向量为key。

随后将使用注意力得分和兴趣抽取层的输出作为输入,计算出兴趣进化层的隐状态。这个计算过程结合了注意力机制和GRU,作者介绍了几种方法。

AIGRU

GRU with attentional input (AIGRU)直接使用注意力分数来影响兴趣进化层的输入,即: AIGRU的想法非常简单,就是使用注意力分数进行放缩。但AIGRU的效果并不好,因为即便为0,即当前行为与目标广告完全无关,隐状态向量仍然会发生变化(若当前行为与目标广告完全无关,则我们期待的结果应该是,但AIGRU显然无法满足)。

AGRU

Attention based GRU(AGRU)的做法是用替换掉了更新门,即: 这种做法下,当为0时,隐状态将保留上一个时刻的状态。并且越大,对隐状态的更新程度也将越高。然而,因为是个标量,而被替换掉的更新门是个向量,会导致每个维度上乘以的系数变得一样,从而忽略了不同维度的重要性差异。

AUGRU

GRUwithattentional update gate (AUGRU)对AGRU进行了进一步改进: 相比于AGRU直接用替换掉更新门,AUGRU只是使用作为更新门的权重,这样既保留了AGRU的优点,又保留了不同维度的重要性差异。如此一来,每一步的局部激活都能强化相关兴趣的作用,减弱兴趣漂移的干扰。

物品冷启动指的是如何对新发布的物品做分发。优化物品冷启动在UGC平台尤为重要,这是因为新物品数量巨大,内容质量良莠不齐,分发非常困难。

  • 新物品缺少与用户的交互,推荐难度大、效果差。
  • 扶持新发布、低曝光的物品,可以增强作者发布意愿。

优化冷启动的目标

  • 精准推荐:克服冷启的困难,把新物品推荐给合适的用户,不引起用户反感。
  • 激励发布:流量向低曝光新物品倾斜,激励发布。
  • 挖掘高潜:通过初期小流量的试探,找到高质量的物品,给与流量倾斜。

物品冷启动的指标

  • 作者侧指标:发布渗透率、人均发布量
    • 发布渗透率=当日发布人数/日活人数(发布一篇或以上的用户算一个发布人数)。
    • 人均发布量=当日发布笔记数/日活人数。
  • 用户侧指标:
    • 新笔记指标:点击率、交互率
    • 大盘指标:消费时长、日活、月活
  • 内容侧指标(非必需):高热物品占比

召回

冷启动召回的难点是缺少用户交互,还没学好物品 ID embedding,导致双塔模型效果不好。而且缺少用户交互会导致 ItemCF 不适用。因此物品冷启动需要新的召回通道。

改造双塔模型

新物品缺少用户交互,还没学好物品 ID embedding,如果要使用双塔模型需要解决如何确定物品ID的问题。有两种解决方案:

新物品使用default embedding

  • 物品塔做ID embedding时,让所有新物品共享一个学出来的ID,而不是用自己真正的ID。
  • Default embedding:共享的ID对应的embedding向量。
  • 到下次模型训练的时候,新物品才有自己的ID embedding向量。

利用相似物品的embedding向量

  • 查找top k内容最相似(根据标签、文字、图片等)的高曝物品。

  • 把k个物品的向量取平均作为新物品的向量。

可以设置多个召回池,如1小时新笔记、6小时内新笔记、24小时内新笔记、30天内新笔记,让新笔记有更多曝光机会。所有召回池共享一个双塔模型,因此不会增加训练代价。

类目召回

系统维护从类目到笔记的索引,每个类目下的笔记列表根据时间倒排。做召回时,根据用户画像取回用户感兴趣的几个类目下的笔记列表,每个笔记列表取最新的k篇笔记做为召回结果。

同样的原理还可以实现基于关键词的召回。

这样的方法有以下缺点

  • 只对刚刚发布的新笔记有效,发布几小时后就再没有机会被召回。
  • 弱个性化,不够精准。

聚类召回

聚类召回是基于物品内容的召回通道。它假设如果用户喜欢一个物品,那么用户会喜欢内容相似的其他物品。可以事先训练一个神经网络,基于物品的类目和内容,把物品映射到向量。对物品向量做聚类,划分为1000个cluster,记录每个cluster的中心方向(k-means聚类,用余弦相似度)。

聚类索引

  • 当一个新物品发布后,用神经网络把它映射到一个特征向量。
  • 从1000个向量(对应1000个cluster)中找到最相似的向量,作为新物品的cluster。
  • 索引:cluster→物品ID列表(按时间倒排)。

线上召回

  • 给定用户ID,找到塔的last-n交互的物品列表,把这些物品作为种子物品。
  • 每个种子物品映射到向量,寻找最相似的cluster。
  • 从每个cluster的笔记列表中,取回最新的m篇笔记,最多取回mn个新物品。

聚类召回同样只对新发布的笔记有效,但聚类召回的推荐比简单按类目推荐更加精准。

需要训练一个神经网络来将内容映射成向量。例如对于图文内容,可以用CNN处理图片信息,用BERT处理文字信息,分别生成一个向量并做拼接,再用一个全连接层映射成新的向量。具体训练如下:

  • 使用模型预测种子物品、正样本、负样本的向量
  • 鼓励尽可能小。使用Triplet hinge loss或Triplet logistic loss,这两个损失函数见召回的双塔模型训练。

正样本的选取可以采用人工标注的方式,但这样成本太高。

可以利用算法自动选取正样本:

  • 筛选条件:
    • 只用高曝光物品作为二元组(因为有充足的用户交互信息)。
    • 两个物品有相同的二级类目。
  • 用ItemCF的物品相似度选取正样本。

负样本可以直接从全体物品中随机选取内容较丰富、质量高的物品。

Look-Alike人群扩散

Look-Alike是互联网广告常用的方法,也可以应用在推荐系统。Look-Alike 适用于发布一段时间、但是点击次数不高的物品。

Look-Alike主要思想如下:

  • 如果用户与物品发生点击、点赞等交互,则认为用户对物品可能感兴趣。
  • 把有交互行为的用户作为新物品的种子用户。
  • 用look-alike在种子用户的相似用户中扩散。
    • 取种子用户向量(双塔模型生成)的平均作为物品向量表征(近线更新,即分钟级别的更新),存储在向量数据库。
    • 做推荐时,拿用户向量对向量数据库做最近邻查找。

物品从发布到热门,主要的透出渠道会经历三个阶段:

  • 类目召回、聚类召回。它们是基于内容的召回通道,适用于刚刚发布的物品。
  • Look-Alike 召回。它适用于有点击,但是点击次数不高的物品。
  • 双塔、ItemCF、Swing 等等。它们是基于用户行为的召回通道,适用于点击次数较高的物品。

流量调控

流量调控是物品冷启动最重要的一环,直接影响作者发布指标。工业中往往要把流量向新物品倾斜,这是为了:

  • 提高作者创作积极性,提高发布渗透率、人均发布量。
  • 让每个新物品都能获得足够曝光,以挖掘优质笔记,提高高热物品占比。

流量调控的发展通常会经历这几个阶段:

  • 在推荐结果中强插新物品。
  • 对新物品做提权(boost):给新物品的分数乘以一个系数。
    • 容易实现,投入产出比好。
    • 曝光量对提权系数很敏感,很难精确控制曝光量。
  • 通过提权,对新物品做保量
    • 例如保证物品24小时内获得100次曝光。
    • 在原有提权系数上,离保量目标越远乘以一个更大的系数。
    • 提权系数=
    • 因为推荐链路、提权系数、线上环境等问题,保量成功率往往远低于100%。
    • 不能直接给所有新物品一个很大的提权系数,否则会把物品推荐给不合适的受众。
  • 差异化保量。
    • 结合内容质量以及作者历史作品质量给予物品额外的保量目标。

AB测试

推荐系统常用的AB测试只考察用户侧消费指标(新笔记点击率、交互率,大盘指标),而冷启动的AB测试还需要额外考察作者侧发布指标(发布渗透率和人均发布量)。

用户侧实验

推荐系统标准的AB测试中,将所有用户分为实验组和对照组,将所有物品按不同的策略向两组用户分别做推荐,看是否存在diff。

将这种方法应用到冷启动的AB测试,可能会导致推全后与AB测试存在差异。例如这样一个实验:

  • 限定:保量100次曝光。
  • 假设:新物品曝光越多,用户使用APP时长越低。
  • 新策略:把新物品排序时的权重增大两倍。
  • 结果(只看用户消费指标):
    • AB测试的diff是负数(实验组不如对照组)。
    • 如果推全,diff会缩小。

这是因为对实验组的新物品做提权,则实验组会看到更多的新物品。而新物品做保量100次曝光是确定的,新物品在实验组取得更多曝光,就会在对照组取得更少曝光,从而使diff变大。实验组策略的变化会对对照组造成影响,所以实验会和推全结果存在差异。

作者侧实验

作者侧实验比用户侧实验更加复杂,已知的实验方案都存在缺陷。

方案一

  • 新笔记的作者分为实验组和对照组,实验组使用新策略,对照组使用原策略,同时面向所有用户做推荐。
  • 这种方案新物品之间会抢流量。例如新策略是把新物品的权重增大两倍,由于实验组和对照组的新物品存在竞争关系,实验组的策略会对对照组造成较大影响,实验组的物品会得到更多的曝光而对照组的物品曝光将大大减少。
  • 此外,如果新笔记和老笔记存在竞争关系,那么AB测试时只有50%带策略的新笔记跟所有老笔记抢流量,而推全后则是100%,实验和推全结果会存在差异。

方案二

  • 用户也分为实验组和对照组,实验组的新物品只向实验组的用户推荐,对照组的新物品只向对照组的用户推荐。

  • 这种方案两组新物品不会抢流量,实验结果更可信。但是,新物品仍然会和老物品抢流量,AB测试和推全结果有差异。并且,这会导致给用户推荐的内容池较少一半,影响用户体验,降低消费测指标,结果得不偿失。

方案三

  • 老笔记也分为实验组和对照组,实验组的新物品和老物品都只对实验组的用户做推荐,对照组亦然。
  • 这种方案相当于把整个平台一分为二,实验结果更加准确。但会严重损害消费指标。

设计冷启动AB测试方案时,需要考虑几个问题:

  • 实验组、对照组新物品会不会抢流量?
  • 新物品、老物品怎么抢流量?
  • 同时各类笔记、用户,会不会让内容池变小?
  • 如果对新笔记做保量,会发生什么?

在做推荐时,除了考虑用户是否对物品感兴趣,还要考虑推荐物品的多样性。如果多样性做得好,可以显著提升推荐系统的核心业务指标。因此,在做完精排后,还需要结合物品的多样性指标进行重排,以选出最终的推荐物品。

物品相似性有两种度量方法:

  • 基于物品属性标签,例如根据一级类目、二级类目、品牌计算相似度:
    • 物品i:美妆、彩妆、香奈儿
    • 物品j:美妆、香水、香奈儿
    • 相似度:
    • 对三个相似度求加权和,权重由人为规定
  • 基于物品向量表征
    • 使用基于内容的向量表征。利用nlp和cv算法提取物品特征。可以使用CLIP方法。
    • 不使用双塔模型学到的物品向量,因为推荐系统头部现象很严重,曝光和点击都集中在少数物品,双塔模型学不好新物品和长尾物品的向量表征。

在推荐的链路上,在粗排和精排的后处理阶段,综合排序模型打分和多样性分数做选择。

  • 粗排和精排用多目标模型对物品做pointwise打分。
  • 对于物品i,模型输出点击率、交互率的预估,融合成分数
  • 后处理阶段从n个物品选出k个,既要求总分高,也需要有多样性。

MMR

Maximal Marginal Relevance (MMR)来自于搜索算法,根据精排打分和物品相似度,从 n 个物品中选出 k 个价值高、且多样性好的物品。

MMR多样性算法的步骤如下:

  • 已选中的物品S初始化为空集,未选中的物品R初始化为全集{1,···,n}。

  • 选择精排分数最高的物品,从集合R移到S。

  • 做k-1轮循环:

    • 计算未选中物品集合R中所有物品的分数。,其中:

    • 选出分数最高的物品,将其从R移到S。

DPP

行列式点过程 (determinantal point process, DPP) 是一种经典的机器学习方法,是目前推荐系统重排多样性公认的最好方法。

超平形体

一组向量可以确定一个k维超平行体

  • 要求k≤d,比如d=3维空间中有k=2维平行四边形。

  • 如果v_1,···,v_k线性相关,则体积vol(P)=0。

我们可以借助k维超平行体的体积来衡量物品多样性

  • 给定k个物品,把他们表征维单位向量
  • 用超平行体的体积衡量物品的多样性,体积介于0和1之间。
  • 如果k个单位向量两两正交,则体积最大化,vol=1。

关于k维超平行体的体积,有如下计算方法:

  • 将k个向量作为矩阵的列向量。
  • 若d≥k,则行列式与体积满足:

行列式点过程DPP

基于以上原理,DPP用行列式来衡量物品多样性,其目标函数可写为: Hulu的论文将DPP应用在推荐系统,使用物品相似度来调节物品得分: 我们令k×k矩阵。设A为n×n矩阵,它的(i,j)元素为。给定向量,需要的时间计算A。为A的一个子矩阵。如果i,j∈S,则的一个元素。那么,DPP得分可以写成: DPP是个组合优化问题,要求从{1,···,n}中选出一个大小为k的子集S。通常用贪心算法来近似求解DPP。用S表示已选中的物品,R表示未选中的物品,则每一轮从R中选择一个物品i,满足: 也就是说,我们所选择的i,既要价值尽可能高,又要使i加入后物品多样性尽可能高。

但是,要求解这个问题需要计算行列式很多次,而计算行列式需要矩阵分解,代价很大。

  • 对于单个i,计算的行列式需要时间。
  • 对于所有的i∈R,计算行列式需要时间
  • 我们需要选出k个物品,因此复杂度为
  • 计算矩阵A的时间复杂度为,因此总时间复杂度为

Hulu的论文设计了一种数值算法,仅需的时间就可以从n个物品挑选出k个物品:

  • Cholesky分解,其中L是下三角矩阵。
  • 的行列式为
  • 已知,则可以快速求出所有的Cholesky分解,进而求出其行列式。

滑动窗口

在MMR中,我们每一轮都要选出一个物品i,使得: 这里的S指已选中物品的集合。当|S|很大时,很难找出物品i与S中的物品不相似了。即当|S|很大时,相似性sim(i,j)总是约等于1,导致MMR方法失效。

可以使用滑动窗口:设置一个滑动窗口W,比如最近选中的10个物品,用W代替MMR公式中的S: 这种方案下,只考虑最近选中的物品,要求距离较近的物品相似度低,而距离较远的物品相似度可以较高。这也符合实践的需要,当间隔较远时,物品相似度高并不会影响用户体验。

同样的,滑动窗口也可以应用到DPP,使用最近选中物品集合W代替S:

业务规则约束

在实际业务中往往有很多规则应用在重排阶段,这些规则和MMR、DPP等算法结合来满足用户的多样性体验。以小红书为例,可能会出现以下规则:

  • 最多连续出现k篇某种笔记(如图文笔记、视频笔记)。
  • 每k篇笔记最多出现1篇某种笔记(如运营推广笔记)。
  • 前t篇笔记最多出现k篇某种笔记(小红书前4篇笔记为首屏,更容易被用户看到)。

这些业务规则可以和MMR和DPP结合,每一轮都先用规则排除掉R中的部分物品,从剩下的物品中做选择。例如前k篇笔记都是图文笔记,那么下一篇笔记要把图文笔记都排除掉。

推荐系统需要经过召回、粗排、精排、重排等过程才能将物品推荐给用户。其中粗排和精排使用类似的模型,粗排使用更小的模型对召回的结果进行打分,筛选出更少的笔记后交给更大更准确的精排模型打分,以此提高效率。

以小红书为例,排序中常用的指标有:点击率(点击次数/曝光次数)、点赞率(点赞次数/点击次数)、收藏率(收藏次数/点击次数)、转发率(转发次数/点击次数)等。

排序模型预估以上多种分数,并融合这些分数,根据融合的分数做排序、截断。

多目标模型

多目标模型在工业界被广泛运用。

多目标模型

如图,各类特征向量concatenation后一起输入神经网络,神经网络输出的向量再分别输入四个神经网络,即得到点击率、点赞率、收藏率、转发率的预测。由于使用了sigmoid激活函数,四个预估值都介于0,1之间。使用用户的真实行为作为标签,例如一个用户点击并转发了物品,但未点赞、收藏,则四个标签为(1,0,0,1)。可以把四种行为分别作为一个二元分类任务,使用交叉熵作为损失函数,则总损失函数可表示为: 其中为人为设置的权重。

训练中往往存在类别不平衡的问题,例如每100次曝光,可能只有10次点击,而有90次未点击。因此需要对负样本做降采样,只保留一小部分负样本,让正负样本数量平衡。

但是,经过降采样后,负样本变少,又会导致预估点击率大于真实点击率:

  • 正样本、负样本数量为,负样本采样率为α,则实际使用个负样本。

  • 真实点击率:(期望)

  • 预估点击率:(期望)

因此需要预估值校准

MMoE

MMoE全称Multi-gate Mixture-of-Experts,是对多目标模型的改进。

MMoE
  • 将特征向量输入n个神经网络(称为“专家”,通常为4或8个,图中为3个),生成n个向量。
  • 使用神经网络生成一个n维向量,作为n个“专家”生成向量的权重。
  • 对n个向量做加权平均,再经过一个神经网络生产点击率的预估值。
  • 同理,如果需要预估点赞率等其他指标,则添加更多生成权重的神经网络。

在实践中,MMoE常常会产生极化现象

  • softmax输出值一个接近1,其余接近0,导致只有一个“专家”起作用,退化成简单的多目标模型。
  • 在训练时,可以对softmax的输出使用dropout。
    • softmax输出的n个数值以10%的概率被mask,即每个专家都有10%的概率被丢弃。
    • 这使得模型不会过分依赖某个“专家”,否则当其权重被mask时,将导致严重偏差。

MMoE的使用未必会带来提升,具体应根据实践结果选择。

融合预估分数

前面讲到多目标模型可以对点击率、点赞率等指标做出预估,而要对物品进行排序,需要将这些指标进行融合得到最终的预估分数。以下是几种融分公式。

  • 简单加权和:将预估的点击率、点赞率、收藏率等指标直接做加权平均。

  • 点击率乘以其他项的加权和: 该公式有其实际意义,例如点击率×点赞率=点赞次数/曝光次数。

  • 海外某短视频APP融分公式: 其中w和α都是超参数。

  • 国内某短视频APP融分公式:

    • 根据预估时长,对n篇候选视频做排序

    • 如果某视频排名第,则它得分

    • 对点击、点赞、转发、评论等预估分数做类似处理。

    • 最终融合分数:

  • 国内某电商的融分公式:

    • 电视转化流程:曝光→点击→加购→付款
    • 最终融合分数:

视频播放建模

除了点击、点赞、收藏、转发、评论等指标外,视频排序还需考虑播放时长完播

播放时长建模:

  • 将多目标模型最后一个全连接层的输出记作z。设p=sigmoid(z)。

  • 实际观测的播放时长记作t。

  • 做训练:用p拟合y=t/(1+t),最小化交叉熵损失

  • 做推理:把exp(z)作为播放时长的预估。

视频完播建模:

  • 回归方法

    • 例:视频长度10分钟,实际播放4分钟,则实际播放率为y=0.4。

    • 让预估播放率p拟合y:

  • 二元分类方法

    • 定义完播指标,比如完播80%,则10分钟的视频播放>8分值作为正样本,播放<8分钟作为负样本。
    • 做二元分类训练模型。
  • 不能直接把预估的完播率用到融分公式,否则对长视频不公平。可以用函数f(视频时长)拟合完播率关于视频时长的函数。线上预估完播率,然后做调整: 再把调整后的结果作为融分公式的一项。

排序模型的特征

前面谈及排序模型时,使用到了用户特征、物品特征、统计特征、场景特征作为模型的输入。下面介绍这些特征。

用户画像

  • 用户ID(在召回、排序中做embedding)
  • 人口统计学属性:性别、年龄等。
  • 账号信息:新老、活跃度······
  • 感兴趣的类目、关键词、品牌。

物品画像

  • 物品ID(在召回、排序中做embedding)
  • 发布时间
  • GeoHash(经纬度编码)、所在城市。
  • 标题、类目、关键词、品牌······
  • 字数、图片数、视频清晰度、标签数······

用户统计特征

  • 用户最近30天(7天、1天、1小时)的曝光数、点击数、点赞数、收藏数······
  • 按物品类目分桶

物品统计特征

  • 笔记最近的曝光数、点击数、点赞数、收藏数······
  • 按照用户性别、用户年龄、用户地域等分桶
  • 作者特征(作品数、粉丝数、消费指标等)

场景特征

  • 用户定位GeoHash(经纬度编码)、城市。
  • 当前时刻(分段,做embedding)。
  • 是否周末、节假日。
  • 手机品牌、手机型号、操作系统等。

要将以上特征转化为特征向量,需要经过特征处理

  • 离散特征:做embedding。
    • 用户ID、物品ID、作者ID
    • 类目、关键词、城市、手机品牌等
  • 连续特征:做分桶,变成离散特征。
    • 年龄、笔记字数、视频长度
  • 连续特征:其他变换。
    • 曝光数、点击数、点赞数等数值做log(1+x)(减小极端值带来的影响)
    • 转化为点击率、点赞率等值,并做平滑

很多特征无法覆盖100%的样本,例如很多用户未填写年龄、未开启GPS权限。因此做特征工程时,需要想办法提高特征覆盖率,以使模型更准确。

粗排模型

推荐需要经过粗排、精排两个过程,如果粗排对几千个物品打分,那精排可能只对几百个物品打分。因此,粗排单次推理代价必须小,而可以适当牺牲预估的准确度。

前面讲到的排序模型采用的时前期融合的方式,即先对所有特征做concatenation,再输入神经网络。这样的方式线上推理代价大,如果有n个候选物品则要做n次推理。这样的模型往往用于精排。

而用来做召回的双塔模型使用的是后期融合的方式,先用用户特征和物品特征分别计算出特征向量,再求余弦相似度。这种方式线上对于每个用户只需要做一次用户特征的推理,代价很小,但预估准确性也更低。

小红书在粗排阶段使用了介于二者之间的三塔模型。

三塔模型
  • 用户塔很大,因为对于每个用户用户塔只需要做一次推理。
  • 物品塔需要对新物品做推理,但物品塔的输出向量会被缓存,因此可以避免绝大多数推理。
  • 对每个物品交叉塔都需要做推理,而且因为统计特征会随时变化,交叉塔的输出向量不能缓存。因此交叉塔会做得足够小(往往只有一层)。
  • 模型上层同样对每个物品都要做一次推理,因此该模型的推理大部分计算量在模型上层。

LastN

LastN,即用户最近 n 次点击、点赞、收藏、转发等行为是推荐系统中重要的特征,可以帮助召回和排序变得更精准。

用户的LastN序列是用户最近交互过的N个物品,将这些物品的ID embedding成向量并取平均得到一个向量,作为用户的一种特征,和其他特征一起输入神经网络。

这种方法适用于双塔模型、三塔模型、精排模型。

在实践中,可以用点击、点赞、收藏等的LastN序列分别生成一个特征向量,再把这些向量拼接起来。除了使用物品ID,还可以使用物品其他特征做embedding,再和ID的embedding拼接起来。

DIN模型

DIN模型是对LastN序列建模的一种方法,效果优于简单的平均。DIN 的本质是注意力机制。

DIN采用了加权平均代替普通平均,权重是候选物品向量与LastN物品向量的相似度。

DIN模型只用于精排模型,而不用于双塔模型、三塔模型。这是因为双塔模型中物品表征离线存储,用户塔无法看到候选物品特征。

SIM模型

DIN模型的计算量受N影响,因此N的规模不能太大,一般只能为几百,导致模型无法看到用户的长期兴趣。

SIM模型的目标是保留用户的长期行为序列,而且计算量不会过大。

SIM模型对DIN做出改进:

  • DIN对LastN向量做加权平均,权重是相似度。
  • 如果某LastN物品与候选物品差异很大,则权重接近零。
  • 可以快速排除掉与候选物品无关的LastN物品,降低注意层的计算量。

SIM的步骤如下:

查找

  • Hard Search:保留LastN物品中与候选物品类目相同的。
  • Soft Search:最近邻查找,将候选物品向量作为query查找LastN中k个相似度最高的。
  • 前者更快,后者效果更好。一般用前者就行。

注意力机制

  • 将DIN中的LastN向量换成TopK向量即可。
  • SIM记录用户的长期行为,LastN可能存在很久之前的交互,因此可以加入时间信息:
    • 用户与某个LastN物品的交互时刻距今为δ;
    • 对δ做离散化,在做embedding,变成向量d
    • 将物品向量x和时间向量d拼接在一起,作为一个LastN物品的表征。

因式分解机FM

线性模型对输入的特征取加权和,作为对目标的预估。如果先做特征交叉,再用线性模型,通常可以取得更好的效果。如果做二阶特征交叉,那么参数量为O(特征数量平方),计算量大,而且容易造成过拟合。因式分解机(Factorized Machine, FM)用低秩矩阵分解的方式降低参数量,加速计算。

若有x个特征,则线性模型可表示为: 加入二阶交叉特征: 引入特征交叉后,模型表达能力更强,也能取得更好的效果。但同时,直接加入二阶交叉特征引入了O(d^2)级别的参数量,大大提高了计算量。

相比于直接训练一个d×d的矩阵作为交叉项的参数,因式分解机只训练一个d×k的矩阵V(k<<d),将该矩阵第i列和第j列的内积作为交叉项的参数: 如此一来,参数量便大大减小。任何可以用线性模型(比如线性回归、逻辑回归)解决的问题,都可以用 FM 解决。但当前FM已经不常使用了。

FFM

FFM在FM的基础上引入了特征域。例如,⽤户的性别分为男、⼥、未知三类,那么对⼀个⼥性⽤户来说,采⽤ one-hot ⽅式编码的特征向量为[0,1,0],这个三维的特征向量就是⼀个“性别”特征域,其中第一维表示“是否为男性”,第二维表示“是否为女性”,第三位表示“是否未知”。

城市、学历都可以作为特征域。假如一个特征组合为(男,北京,本科),在FM中,我们对于每个特征都训练了一个隐向量v,例如男和北京的交叉特征权重应该是。而在FFM中,我们对于每个特征不是训练一个隐向量,而是m-1个隐向量,其中m为特征域数量。也就是说,当一个特征和其他每个特征域的特征交叉时,所使用的隐向量都是不同的。对于男性这个特征,我们会训练出。那么,我们的模型就变成了: 其中表示所在的特征域。

相⽐FM,FFM引⼊了特征域的概念,为模型引⼊了更多有价值的信息,使模型的表达能⼒更强 。但是复杂度也有所上升。

深度交叉网络DCN

深度交叉网络(Deep & Cross Networks, DCN)用来代替简单的全连接网络,可以用于召回双塔模型、粗排三塔模型、精排模型。DCN 由一个深度网络和一个交叉网络组成,交叉网络的基本组成单元是交叉层

交叉层

一个交叉层做了如下运算: 其中⊙为哈达玛积运算,即按位置相乘。一个交叉网络可以由多个交叉层叠加而成,类似ResNet。

深度交叉网络由一个深度网络和一个交叉网络组成:

深度交叉网络

特征向量经过全连接网络和交叉网络后分别得到一个向量,两个向量合并再输入一个全连接层得到最终的输出。

深度交叉网络可以用来代替召回、排序模型中的神经网络,如双塔模型中的用户塔物品塔,且往往能取得更好的效果,得到工业界的广泛认可。

LHUC网络结构

LHUC 的起源是语音识别,后来被应用到推荐系统,LHUC 可以用于精排。快手将其称为 PPNet,现在已经在业界广泛落地。

推荐系统中的LHUC模型

LHUC的模型结构如上图所示。模型的输入为用户特征和物品特征,输出为一个向量。需要注意的是,图中的两个神经网络由多个全连接层和一个**2*sigmoid激活函数组成,将最终的结果数值大小限定到(0,2)范围内。这样可以在和其他向量做哈达玛积后,放大某些数值,缩小某些数值**,具体放大哪些缩小哪些则受用户特征影响。

将LHUC用于推荐系统,门控神经网络(2 x sigmoid)的梯度不要传递到用户ID embedding特征,需要对其做 stop gradient。

LHUC模型原来用来做语音识别,输入分别为语音信号和说话者的特征,根据说话者的特征对语音信号的输出进行调节。

FiBiNet

学习FiBiNet需要先了解SENet和Bilinear Cross。

SENet

SENet 是计算机视觉中的一种技术,可以用在推荐系统中对特征做动态加权

SENet

假设有m个特征向量(这些向量的长度可不同),对这些向量做变换得到一个m维向量。将该m维向量作为权重对原来的特征向量做加权,得到新的特征向量。SENet的作用在于对离散特征做field-wise加权,放大或削弱某些特征的作用。比如用户ID Embbedding是64维向量,则这64个元素算一个field,获得相同的权重。m个特征向量即m个fields。

Bilinear Cross

Bilinear Cross用于做Field间特征交叉,即将两个fields做交叉得到新的特征。Field间特征交叉通常有以下几种方式。

  • 内积:将两个特征向量做内积得到一个实数。如果有m个离散特征,就会得到m^2个实数。
  • 哈达玛积:将两个特征向量做哈达玛积得到一个向量。如果有m个离散特征,就会得到m^2个向量。内积和哈达玛积都要求向量维度相同。
  • Bilinear Cross
    • 内积:。如果有m个fields则需要个参数矩阵,参数量非常大,因此只能人工指定一些重要的、相关的特征做交叉。
    • 哈达玛积:。如果有m个fields则会产生m^2个向量,往往也是人工指定一些特征做交叉。

FiBiNet

FiBiNet结合了以上两种技术。

FiBiNet

传统的模型中,离散特征经过embedding后直接和连续特征拼起来作为神经网络的输入。FiBi加入了SENet和Bilinear,具体如上图,其中Bilinear输出的是几个交叉后的向量,再将这些向量做拼接得到一个高维向量。

矩阵补充

模型训练

矩阵补充模型首先通过两个Embedding矩阵将用户向量和物品向量映射成低维向量,再将二者求内积得到用户u对物品i的预估兴趣分数。

训练的目标函数为: 其中y是用户u对物品i的真实兴趣分数。该目标函数的目的是最小化预估兴趣分数和真实兴趣分数的距离,以此训练Embedding矩阵。训练后,我们便可使用作为兴趣分数来补充共现矩阵中没有产生过交互的用户和物品。

线上服务

训练得到用户和物品的embedding,将embedding的结果存储到key-value表,用户的key-value表中key为用户id,value为embedding后的向量。

线上服务过程如下:

  • 把用户id作为key查询用户的向量,记作a。
  • 最邻近查找:查找用户最有可能感兴趣的k个物品,作为召回结果。
    • 第i号物品的embedding向量记作
    • 内积是用户对第i号物品兴趣的预估。
    • 返回内积最大的k个物品。

但是矩阵补充实际效果并不好,原因如下:

  • 为利用物品、用户属性信息。
  • 负样本选取方式不对。
  • 内积不如余弦相似度,平方损失函数不如交叉熵损失。

双塔模型

双塔模型(two-tower)也叫 DSSM,是推荐系统中最重要的召回通道。

双塔模型有两个塔:用户塔、物品塔。两个塔各输出一个向量,作为用户、物品的表征。两个向量的余弦相似度作为对兴趣的预估。

用户塔
物品塔

模型训练

将物品样本分为正样本(用户感兴趣的物品)和负样本(用户不感兴趣的物品)。

  • Pointwise: 独立看待每个正样本、负样本,做简单的二元分类

  • Pairwise: 每次取一个正样本、一个负样本

  • Listwise: 每次取一个正样本、多个负样本

Pointwise训练

工业界一般使用这种方法。

把召回看作二分类任务:

  • 对于正样本,鼓励cos(a,b)接近+1。
  • 对于负样本,鼓励cos(a,b)接近-1。

其中a为用户的表征,b为物品的表征。正负样本数量通常控制在1:2或1:3。

Pairwise训练

Pairwise每次取一个正样本、一个负样本。基本想法是要鼓励cos(a,b+)大于cos(a,b-),b+是正样本的表征,b-是负样本的表征。

损失函数:

  • Triplet hinge loss

    • 如果cos(a,b+)大于cos(a,b-)+m,则没有损失

    • 否则,损失等于cos(a,b-)+m-cos(a,b+)

  • Triplet logistic loss σ大于0的超参数。

Listwise训练

Listwise每次取一个正样本、多个负样本。这些物品样本与用户表征的余弦相似度经过softmax后,要使正样本尽可能大(接近1),使负样本尽可能小(接近0)。损失函数使用交叉熵损失。

listwise训练

正负样本

模型应用

双塔模型训练完成后,通过两个步骤投入实际应用:

  • 离线存储:用物品塔计算每个物品的特征向量b,把<特征向量b,物品ID>保存到向量数据库用作最近邻查找。

  • 线上召回:需要推荐时,调用用户塔线上计算用户向量a,并用a作为query查询向量数据库,查找和a余弦相似度最高的k个物品向量,返回这k个物品ID。

之所以存储物品向量而线上计算用户向量,是因为:

  • 每次召回只用到一个用户向量,而用到大量物品向量,线上计算物品向量代价过大。
  • 用户特征变化较快,而物品特征相对稳定,离线存储用户向量不利于推荐效果。

模型需要定期做更新,分为全量更新(天级别)和增量更新(实时):

  • 全量更新:每天做一次全量更新,训练整个模型,包括embedding和全连接层。
    • 将昨天的数据random shuffle。
    • 在昨天模型参数的基础上(不受增量更新影响),用昨天的数据训练1epoch。
    • 训练完成后发布新的用户塔和物品向量。
  • 增量更新:做online learning更新模型参数,只更新ID Embedding。
    • 用户兴趣会随时发生变化,因此需要及时更新模型。
    • 实时收集线上数据,做流式处理,生成TFRecord文件。
    • 对模型做online learning,增量更新ID Embedding参数。
    • 发布用户ID Embedding(推荐时使用用户ID查询用户ID Embedding),供用户塔在线上计算用户向量。

全量更新不受一天中不同时段用户使用习惯差异的影响,随机打乱数据训练效果更好。而增量更新可以动态捕捉用户的兴趣变化。因此二者需要结合使用。

双塔模型+自监督学习

双塔模型学不好低曝光物品的向量表征:

  • 推荐系统的头部效应严重
    • 少部分物品占据大部分点击
    • 大部分物品点击次数不高
  • 高点击物品的表征学得好,长尾物品的表征学得不好。

通过自监督学习做data augmentation可以更好地学习长尾物品的向量表征:

  • 对每个物品做特征两类特征变换,并通过物品塔得到两个特征向量b'和b''。
  • 对于同一个物品的两个特征向量应使其相似度高。
  • 对于不同物品的特征向量应使其相似度低。

特征变换

对物品做特征变换时主要有几种方式:

  • Random Mask:随机选取一些离散特征(比如类目),把它们遮住。例如:

    • 某物品的类目特征是u={数码,摄影}。
    • mask后的类目特征变成u'={default}。
  • Dropout:随机丢弃某个多值离散特征50%的值。

    • 多值离散特征:可以有多个取值的特征,例如类目。
    • 例如某个物品的类目特征u={数码,摄影} dropout后变成了u'={美妆}。
  • 互补特征(complementary)

    • 假设物品一个有4种特征:{ID,类目,关键词,城市}。
    • 随机分成两组:{ID,关键词}和{类目,城市}。
    • 由{ID,default,关键词,default}计算得到物品表征1。
    • 由{default,类目,default,城市}计算得到物品表征2。
    • 应使物品表征1和物品表征2尽可能相似。
  • Mask一组关联的特征

    • 离线计算特征两两之间的关联,用互信息(mutual information)衡量: 其中p(u)表示某特征取值为u的概率,p(u,v)表示某两个特征分别取值u、v的概率。

    • 设一共有k种特征,则两两之间的MI构成k×k矩阵。

    • 随机选一个特征作为种子,mask种子及其相关的k/2种特征。

    • 效果好但成本高。

训练模型

首先对点击做随机抽样,得到n对用户-物品二元组,作为一个batch训练双塔。这种方式下热门物品抽到的概率更高。

接着从全体物品中均匀抽样,得到m个物品,作为一个batch,用来训练物品塔。这种方式下所有物品被抽到概率相同。对这些物品做两类特征变换,物品塔输出两组向量:

如图,取第一组中的向量,分别与第二组中的向量取余弦,要使正样本的余弦值尽可能接近1,其余尽可能接近0。使用交叉熵损失函数。

训练模型

如此得到了第i个物品的损失函数: 对m个物品的损失取平均作为自监督学习的损失: 双塔模型的损失和自监督学习的损失共同构成整个模型的损失函数,其中超参数α用来调节自监督的作用:

“协同过滤”就是协同⼤家的反馈、评价和意见⼀起对海量的信息进⾏过滤,从中筛选出⽬标⽤户可能感兴趣的信息的推荐过程。

  • 共现矩阵:用横坐标表示物品,纵坐标表示用户,矩阵元素值表示用户对物品的感兴趣程度。

基于用户的协同过滤

基于用户的协同过滤(userCF)向用户推荐相似用户感兴趣的物品。在为一名用户推荐物品时,首先找到与该用户相似度最高的n个用户,根据共现矩阵计算这些用户对每个物品的感兴趣程度,再将各个物品的得分进行排序,选择得分最高的一系列物品进行推荐。

以上总结了userCF的总体逻辑,但关于用户相似度的计算排序的过程需要可量化的方式。以下进行详细介绍。

用户相似度计算

我们可用共现矩阵中的行向量表示相应用户的用户向量(共现矩阵行向量中的每个值表示了该用户对一个物品的感兴趣程度),这样计算用户相似度便转化为了计算向量相似度,两个向量之间常⽤的相似度计算⽅法有如下⼏种。

余弦相似度

余弦相似度衡量了用户向量i和用户向量j之间的夹角大小,夹角越小则相似度越大。

皮尔逊相关系数

皮尔逊相关系数使用用户平均分对各独立评分进行修正,减小用户评分偏置的影响。 其中, 代表⽤户i对物品p的评分。 代表⽤户i对所有物品的平均评分,P代表所有物品的集合。

以上使用的是用户平均分,同理也可引入物品平均分,不再赘述。

最终结果排序

一般利⽤⽤户相似度和相似⽤户的评价的加权平均获得⽬标⽤户的评价预测。 其中 表示用户u和用户s的相似度。

获得用户p对各个物品的评价预测后,将这些得分进行排序即可得到推荐列表。

userCF存在以下缺点:

  • 互联网应用场景下用户数往往非常大,而存储用户相似度矩阵需要n^2的空间复杂度。
  • 用户的历史数据向量往往非常稀疏,对于只有⼏次购买或者点击⾏为的⽤户来说,找到相似⽤户的准确度⾮常低。

基于物品的协同过滤

基于物品的协同过滤(ItemCF)通过计算共现矩阵中物品列向量的相似度得到物品之间的相似矩阵,再找到⽤户的历史正反馈物品的相似物品进⾏进⼀步排序和推荐。

ItemCF的具体步骤如下:

  • 基于历史数据构建共现矩阵(用户为行,物品为列)
  • 计算两两列向量(即物品向量)的相似性,构建物品相似度矩阵
  • 利用物品相似度矩阵,针对目标用户历史正反馈物品查找TOP K个物品
  • 对于相似物品集合中的物品,利用相似度分值进行排序

如果一个物品与多个历史正反馈物品相似,则相似度需要累加: 其中H是目标用户的正反馈物品集合,权重w为物品间相似度,R是用户对物品的已有评分。

Swing模型

Swing模型和ItemCF十分相似,仅在物品相似度的计算上有所不同。Swing模型的相似度计算方式如下: 其中w_1为喜欢物品i_1的用户集合,v为的交集,overlap计算两个用户喜欢的物品交集的大小,α为参数。

这个相似度计算公式下,v越大两个物品的相似度也越大,引入overlap是为了削弱“小圈子”的影响,例如当两个人同属一个微信群,则被转发到该群的所有链接都可能被两个人同时点开,即便这些链接毫不相关。

总结

  • UserCF基于⽤户相似度进⾏推荐,使其具备更强的社交特性。⽤户能够快速得知与⾃⼰兴趣相似的⼈最近喜欢的是什么,即使某个兴趣点以前不在⾃⼰的兴趣范围内,也有可能通过“朋友”的动态快速更新⾃⼰的推荐列表。这样的特点使其⾮常适⽤于新闻推荐场景。

  • ItemCF更适⽤于兴趣变化较为稳定的应⽤,⽤户在⼀个时间段内更倾向于寻找⼀类商品,这时利⽤物品相似度为其推荐相关物品是契合⽤户动机的 ,例如电商和视频推荐。

协同过滤是⼀个⾮常直观、可解释性很强的模型,但它并不具备较强的泛化能⼒。热门的物品具有很强的头部效应,容易跟⼤量物品产⽣相似性;⽽尾部的物品由于特征向量稀疏,很少与其他物品产⽣相似性,导致很少被推荐。

另外,协同过滤仅利⽤⽤户和物品的交互信息,⽆法有效地引⼊⽤户年龄、性别、商品描述、商品分类、当前时间等⼀系列⽤户特征、物品特征和上下⽂特征。

决策树

分类树

如何选择每个结点划分的特征?

  • 最大化纯度

什么时候停止划分?

  • 结点只有一种类别

  • 划分当前结点会导致树的深度超过最大限制

  • 当划分带来的信息增益小于某个阈值

  • 结点中的样本数小于某个阈值

纯度:用熵值来衡量 其中表示正样本的比例,表示负样本的比例。当时,H取得最大值1。在这个式子中,我们假定

得到每个子结点的熵值后,根据每个结点的样本数对熵值进行加权平均。然后用原本结点的熵减去分裂后子结点熵的加权平均,得到分裂产生的信息增益 我们可以总结决策树的学习过程:

  • 所有样本一开始都放在根结点
  • 计算按所有可能特征划分的信息增益,选择信息增益最大的特征
  • 根据该特征将数据集划分为左右分支
  • 使用以上划分方法对左右子树进行划分,直到满足停止条件

以上基于每个特征只有两个分类的情况。当特征有更多分类时,需要使用one-hot编码。

对于连续特征, 需要选取一个阈值将所有样本划分为两类。阈值的选取需要经过多次尝试,一种方法是将所有样本的特征值排序,然后依次选两两特征值的中点作为阈值,如果有10个样本则要进行9次尝试。然后从所有阈值产生的信息增益中选取最大的。

回归树

回归树用于预测一个连续的值。

回归树叶结点的值为落入该结点的所有样本的平均值。

划分效果使用每个结点内所有样本的方差的加权平均进行评估。

使用根节点的方差减去每个子结点方差的加权平均,得到信息增益。

树的集成

使用单个决策树可能对数据集中的微小变化高度敏感。

Bagging

可以采用有放回抽样的方法来构建多个训练集。例如原训练集有N个样本,则对训练集进行N次有放回抽样,得到一个新的训练集。使用这种方法可以得到多个不同的训练集,从而训练出多棵(经验值为64~128)决策树。最终模型结果可由这些树投票决定。

事实上,如果仅使用以上方法构建多棵决策树,构建出来的决策树可能不会有太大区别。

随机森林:当我们在选择特征划分一个结点时,假如一共有n个特征,那么我们可以从中随机选取k个(k<n,例如),然后把划分特征选取的范围限定在这k个特征里。

Boosting

Bagging方法是并行地训练多棵决策树,而Boosting方法则是依次训练,并且每棵决策树会更多地关注在前面的决策树预测效果不好的样本,从而将多个较弱的模型组合成一个较强的模型。

当我们进行有放回抽样以构造一棵决策树时,我们可以以更高的概率抽取已经训练好的决策树预测效果不好的样本。

梯度提升树(GBDT)

  • 代表时间t的模型,
  • 每个时间步使用拟合,即将预测的残差作为标签
  • ,其中η为超参数。之所以不直接让η=1是为了避免过拟合

可以使用XGBoost等包。

决策树和神经网络

决策树通常适用于结构化数据(即表格数据),而像图像、视频、文本这样的非结构化数据不太适合使用决策树。

决策树模型的运行速度很快,并且小型决策树是可解释的(我们可以打印出决策树,观察每个结点来弄清楚这棵决策树是怎么做决策的)。

相比之下,神经网络适合几乎所有数据,并且适合用来做迁移学习,但是神经网络的运行速度比较慢。除此之外,我们可以使用梯度下降同时训练多个整合在一起的神经网络,而决策树一次只能训练一个。

0%