本文同步发表于:
- 知乎专栏: 推荐系统工程实践专栏
- 微信公众号: yanianthe的公众号
也欢迎关注我的微信公众号,一起学习成长吧~
文章目录
1. 深入浅出向量
1.1 什么是向量(embedding)?
在深度学习火热的当下,向量是一个无法逃避的概念,万物皆可embedding。
首先,embedding想要解决的问题是什么?
如何表达一个物品/元素?
在现实世界中,我们认识和理解一个事物,总是会想办法捕捉它的一些特质或者属性。比如路边一朵红色的野花,那么仅从颜色这个维度,我们可以使用离散的0,1来表达。如 【1,0,0】
但仅从颜色描述是不够的,还有花的种类,气味,高度,用途等等。把所有的属性枚举出来,排列好位置,元素对应属性的位置点亮,那么一朵沁人心脾的红色梅花 = 【1,0,0,1,0,0,1,0】
上面分别就是 one-hot 与multi-hot的编码标识,在传统机器学习中比较常见。但这样会遇到几个问题:
- 真实世界,物品描述的属性维度特别多,且值非常稀疏。(几十上百万个维度也是正常的)
- 物品的属性无法完整枚举到。(很多肉眼看不到的属性)
- 物品的属性包含离散和连续(比如价格属性可能就是连续的)
因此,为了更好的表达元素(包括度量元素之间的距离),假设元素有n个维度的属性(每个维度的意义都不是可解释的),那么元素的集合就是n维基向量张开的线性空间的子空间。空间中的点就是该元素在这个空间下的表达。
比如一个三维的空间,元素A 可能表示为 (x,y,z) = 【1.1,2.3,3,3】
元素在空间中的分布:
**总结:**个人理解,embedding 是 使用连续数值向量对元素的一种低维度表达(从可能几十万维度降低到n维度,n一般为 128,256等较低的数值)
当然这里在学习的时候,也有一些疑惑:
- 比如为什么可以假设样本可以映射到这样的空间?
- 为什么可以假设这个空间是线性的(任一点经过变化仍然在这个空间内),而不是其他空间?
这些线性代数的高深内容,已经远远超出本学渣的知识范畴,,,请算法和数学大佬们在评论区不吝赐教。
1.2 向量从何而来?
item向量的获得,是通过对样本分布的拟合学习,映射到线性空间对应点的过程。
记得2013年还在上大学时,课上讲的主流还是经典的矩阵分解做法。像SVD,SVD++ 以及隐语义模型。
隐语义假设 用户和 item 都可以使用 k 个维度的隐含向量表达。每个用户对item的偏好可以使用 Uk x Ik 得到一个分数。通过模型的学习最终会得到 user 和item的隐向量表达,即user embedding 和 item embedding。 embedding的size为 k维。
但随着神经网络的火爆,矩阵分解渐渐被摒弃。神经网络中,最经典的当属 word2vec 词向量模型。(详细可见详解word2vec模型)
经典word2vec模型只有1层hidden layer。所有单词采用one-hot进行编码(1.1小节)。
训练过程中, 使用滑动窗口获取训练的集合,输入 x词 与 x的上下文的关系,拟合神经网络中的参数。训练手链完毕后,可以得到一个矩阵
(v为向量的维度,N为item的总数)。矩阵转置一下,每一行就是 每个item的embedding表达。
此外,2016年youtube发表的论文,彻底点燃深度学习在推荐系统中的应用。
其中,将网络的倒数第二层作为user embedding。softmax层权重矩阵作为item的 embedding。(本文偏工程,算法相关有兴趣可以详见王哲重读Youtube深度学习推荐系统论文。向老哥的专栏学习)
当然后续也发展出很多很厉害的模型,可以学习到item 和user的向量,如 bert,以及一些graph embedding等。
1.3 向量有何用途?
到了这里,可能会问:得到了这些向量又有什么作用呢?
向量的其中一个特性是可以通过一些距离计算,来判断向量之间的相似度(也就是元素之间的相似度)。
通过相似度的计算,便可以实现 图片/音乐/视频/的搜索功能,比如以图搜图等。
在推荐系统/广告中,一般使用embedding做相似召回,比如用户最近观看了某个视频,则在库中查询与当前视频相似的topn个候选集,作为一路相似召回。
另外,一般 nlp内容理解也经常会根据一些内容特征预训练得到一个item embedding,有时候也会选择在排序侧,将预训练好的item embedding作为模型的特征输入。
2. 向量检索与近似算法
在计算向量相似度的时候,常见的距离算法有:
以常见的欧式距离来算,假设向量的维度为k,库总共有 n个item。则搜索一次的时间复杂度为 O(k*n),且整体的查询耗时与item库的大小线性相关,显然这种线上查询要遍历成百万上千万的暴力计算并不满足需求。
考虑到向量检索的大部分场景都是返回与该元素相似的topk个元素即可,比如推荐中的召回,只需召回近似100个item,也不关注item间的相对顺序,也无需100%准确,毕竟上游还会有粗排和精排。因此,工程上会使用近似最近邻搜索(Approximate Nearest Neighbor Search)来解决这个精度和效率上的问题。
2.1 常见的近似算法
由于暴力搜索是全局空间搜索,因此为了提高查询效率,大部分的ANN近似算法的核心都是:
将所有的数据集按照一定规则划分到一些子空间或者子集里,然后查询的时候用比较低的代价,确认在哪几个子集中搜索计算,这样就避免了全局遍历。
常见的 ann算法有 kd树,lsh,量化以及图的方式,我们带着 **如何对数据集划分子集(也可以看做是聚类)**的问题来窥探各个算法的原理与思路。
2.2 kd树
kd树作为一种树形模型,为了将数据集尽可能均匀的划分,kd树构建的时候,会选择向量上方差最大的维度(方差大数据区分度较好)。然后根据该维度上的中位数m,作为节点的切分值,小于m的划分到左边,大于m的划分到右边。同时递归的构建左子树和右子树。
什么时候停止构建树: 当集合的数据等于1个元素或者小于某个阈值时,节点不再分裂。
下图是一个二维向量的切分过程,根节点根据方差最大选择使用x进行切分。左右子树根据方差选择y维度进行切分。
kd树的构建,跟决策树的构建其实也比较类似,决策树则是通过信息增益进行划分。
kd树是如何做到最近似查询的呢?
-
给定查询元素Q,判断节点的切分维度,Q(n) <node.val 则走左子树,大于等于则走右子树
-
一直走到叶子节点,计算Q与叶子节点的距离,同时保存为当前最近距离。
-
在叶子节点进行回溯操作,每次回溯都判断Q与父节点中未被访问到的分支的距离
3.1 假如该分支 节点与Q的距离大于当前最近距离,则继续向上回溯,直至根节点
3.2 假如该分支节点与Q距离小于当前距离,则使用该分支,走 1 2 步直到叶子节点,更新叶子与Q的最近距离。
kd-树的每个节点上的切分维度x,相当于在这个维度上画了一堵墙,将整个数据集一分为二,实现了子空间的划分。
然后在查询时,先logn的从根节点遍历到第一个叶子节点上,之后就从这个叶子节点开始回溯。
回溯的目的其实是为了看未搜索空间是否有更近的节点。如何判断是否有呢?
以查询点Q为圆心,以当前最近距离为半径,画一个圆圈,回溯的时候用这个圆圈与每个划分节点进行判断(也就是看看圆圈与超