【推搜】embedding评估 | faiss的top-k向量检索

note

  • 在双塔模型召回中,正样本即用户点击过的物品,负样本:全体物品中负采样(简单做法)、被排序淘汰的物品(物品)。
  • faiss库使用三部曲:构建向量库;构建index,并将向量添加到index中;进行topk的检索。根据具体的场景使用合适的index,有的索引还支持GPU构建。

零、背景介绍

在推荐系统的召回阶段中,很多时候item变化不会很明显,所以一般会将item embedding存入faiss之类的向量数据库(也可以使用HnswLib等,注意要建立对应的向量索引),然后在线计算更新user embedding(可由用户的历史item序列的item embedding的mean pooling操作生成,也可以用其他方法),最后将user embedding和item embedding进行最近邻的topk查找。即典型的【离线存储】和【线上召回】策略。最初的双塔模型就是这么做的。
在这里插入图片描述

一、离线指标

hitrate和ndcg等,其中hitrate的指标计算如下:
在这里插入图片描述

二、降维可视化

可以通过将embedding进行PCA或者TSNE降维可视化,看同一类别item的embedding分布。
在这里插入图片描述

三、通过RS线上指标体现

很多时候更实际的是,embedding用于下游任务,最后看线上指标(如CTR/CVR指标等)。

四、LSH原理及多桶策略

4.1 LSH介绍

Locality Sensitive Hashing——LSH
局部敏感哈希的基本思想是希望让相邻的点落入同一个“桶”,这样在进行最近邻搜索时,我们仅需要在一个桶内,或相邻几个桶内的元素中进行搜索即可。

时间复杂度:如果保持每个桶中的元素个数在一个常数附近,我们就可以把最近邻搜索的时间复杂度降低到常数级别。

在这里插入图片描述
先看实验得出的一个结论(上图就是将高维的彩色点映射到低维的abc中):欧式空间中,将高维空间的点映射到低维空间,原本接近的点在低维空间中肯定依然接近,但原本远离的点则有一定概率变成接近的点。

(1)将高维embedding映射到低维:
由于 Embedding 大量使用内积操作计算相似度,因此我们也可以用内积操作来构建局部敏感哈希桶。假设 v 是高维空间中的 k 维 Embedding 向量,x 是随机生成的 k 维映射向量。那我们利用内积操作可以将 v 映射到一维空间,得到数值 h(v)=v⋅x。

(2)分桶:
一维空间也会部分保存高维空间的近似距离信息。因此,我们可以使用哈希函数 h(v) 进行分桶,公式为: h x , b ( v ) = ⌊ x ⋅ v + b w ] h^{x, b}(v)=\left\lfloor\frac{x \cdot v+b}{w}\right] hx,b(v)=wxv+b] 其中, ⌊ ⌋ 是向下取整操作, w 是分桶宽度,b 是 0 到 w 间的一个均匀分布随机变量,避免分桶边界固化。

因为如果总是固定边界,很容易让边界两边非常接近的点总是被分到两个桶里。这是我们不想看到的。所以随机调整b,生成多个hash函数,并且采用【或】的方式组合,就可以一定程度避免这些边界点的问题。

(3)用多个哈希函数同时分桶:
映射的过程会损失部分的距离信息,并且为了防止相近点误判,可以同时用m个哈希函数同时进行分桶操作——如果两个点都同时掉入同一个桶中,则他们是相似的点的概率就很大。就这样得到的候选集再通过遍历得到K邻近。

哈希策略是基于内积操作来制定的,内积相似度也是我们经常使用的相似度度量方法,事实上距离的定义有很多种,比如“曼哈顿距离”、“切比雪夫距离”、“汉明距离”等等。针对不同的距离定义,分桶函数的定义也有所不同,但局部敏感哈希通过分桶方式保留部分距离信息,大规模降低近邻点候选集的本质思想是通用的。

4.2 多桶策略

刚才3.1的使用多个哈希函数进行分桶操作:

(1)分桶栗子

背景:
假设有 A、B、C、D、E 五个点,有 h1和 h2两个分桶函数。
使用 h1来分桶时,A 和 B 掉到了一个桶里,C、D、E 掉到了一个桶里;
使用 h2来分桶时,A、C、D 掉到了一个桶里,B、E 在一个桶。
那么请问如果我们想找点 C 的最近邻点,应该怎么利用两个分桶结果来计算呢?

(1)用“且”(And)操作:
这样处理两个分桶结果之间的关系,则找到与点 C 在 h1函数下同一个桶的点,且在 h2函数下同一个桶的点,作为最近邻候选点。我们可以看到,满足条件的点只有一个,那就是点 D。也就是说,点 D 最有可能是点 C 的最近邻点。

作为多桶策略,可以最大程度地减少候选点数量。但是,由于哈希分桶函数不是一个绝对精确的操作,点 D 也只是最有可能的最近邻点,不是一定的最近邻点,因此,“且”操作其实也增大了漏掉最近邻点的概率。

(2)采用“或”(Or)操作:
找到与点 C 在 h1函数下同一个桶的点,或在 h2函数下同一个桶的点。这个时候,我们可以看到候选集中会有三个点,分别是 A、D、E。这样一来,虽然我们增大了候选集的规模,减少了漏掉最近邻点的可能性,但增大了后续计算的开销。

better:局部敏感哈希的多桶策略还可以更加复杂,比如使用 3 个分桶函数分桶,把同时落入两个桶的点作为最近邻候选点等等。

(2)取值建议:

  • 点数越多,我们越应该增加每个分桶函数中桶的个数;相反,点数越少,我们越应该减少桶的个数;
  • Embedding 向量的维度越大,我们越应该增加哈希函数的数量,尽量采用且的方式作为多桶策略;相反,Embedding 向量维度越小,我们越应该减少哈希函数的数量,多采用或的方式作为分桶策略。

(3)时间复杂度

局部敏感哈希能在常数时间得到最近邻的结果吗?
答案是可以的,如果我们能够精确地控制每个桶内的点的规模是 C,假设每个 Embedding 的维度是 N,那么找到最近邻点的时间开销将永远在 O(C⋅N) 量级。采用多桶策略之后,假设分桶函数数量是 K,那么时间开销也在 O(K⋅C⋅N) 量级,这仍然是一个常数。

4.3 局部敏感哈希实践

使用 Spark MLlib 完成 LSH 的实现。

在将电影 Embedding 数据转换成 dense Vector 的形式之后,我们使用 Spark MLlib 自带的 LSH 分桶模型 BucketedRandomProjectionLSH(我们简称 LSH 模型)来进行 LSH 分桶。其中最关键的部分是设定 LSH 模型中的 BucketLengthNumHashTables 这两个参数:
(1)BucketLength 指的就是分桶公式中的分桶宽度 w;
(2)NumHashTables 指的是多桶策略中的分桶次数。

和其他 Spark MLlib 模型一样,都是先调用 fit 函数训练模型,再调用 transform 函数完成分桶的过程。

def embeddingLSH(spark:SparkSession, movieEmbMap:Map[String, Array[Float]]): Unit ={
  //将电影embedding数据转换成dense Vector的形式,便于之后处理
  val movieEmbSeq = movieEmbMap.toSeq.map(item => (item._1, Vectors.dense(item._2.map(f => f.toDouble))))
  val movieEmbDF = spark.createDataFrame(movieEmbSeq).toDF("movieId", "emb")


  //利用Spark MLlib创建LSH分桶模型
  val bucketProjectionLSH = new BucketedRandomProjectionLSH()
    .setBucketLength(0.1)
    .setNumHashTables(3)
    .setInputCol("emb")
    .setOutputCol("bucketId")
  //训练LSH分桶模型
  val bucketModel = bucketProjectionLSH.fit(movieEmbDF)
  //进行分桶
  val embBucketResult = bucketModel.transform(movieEmbDF)
  
  //打印分桶结果
  println("movieId, emb, bucketId schema:")
  embBucketResult.printSchema()
  println("movieId, emb, bucketId data result:")
  embBucketResult.show(10, truncate = false)
  
  //尝试对一个示例Embedding查找最近邻
  println("Approximately searching for 5 nearest neighbors of the sample embedding:")
  val sampleEmb = Vectors.dense(0.795,0.583,1.120,0.850,0.174,-0.839,-0.0633,0.249,0.673,-0.237)
  bucketModel.approxNearestNeighbors(movieEmbDF, sampleEmb, 5).show(truncate = false)
}

使用 LSH 模型对电影 Embedding 进行分桶得到的五个结果打印:

+-------+-----------------------------+------------------+
|movieId|emb                          |bucketId          |
+-------+-----------------------------+------------------------+
|710    |[0.04211471602320671,..]     |[[-2.0], [14.0], [8.0]] |
|205    |[0.6645985841751099,...]     |[[-4.0], [3.0], [5.0]]  |
|45     |[0.4899883568286896,...]     |[[-6.0], [-1.0], [2.0]] |
|515    |[0.6064003705978394,...]     |[[-3.0], [-1.0], [2.0]] |
|574    |[0.5780771970748901,...]     |[[-5.0], [2.0], [0.0]]  |
+-------+-----------------------------+------------------------+

可以使用多桶策略:BucketId 这一列,因为我们之前设置了 NumHashTables 参数为 3(用了3个分桶/哈希函数),所以每一个 Embedding 对应了 3 个 BucketId。

4.4 LSH的一个实践栗子

场景:现在已知3个用户的特征向量,我们想要找到ABC三个用户之间的相似关系,当然最简单的就是用余弦cos计算,而如果用LSH局部敏感哈希如下pyspark版本的栗子:

import os
import re
import hashlib

from pyspark import SparkContext, SparkConf
from pyspark import Row
from pyspark.sql import SQLContext, SparkSession
from pyspark.sql.functions import *
from pyspark.sql.types import *
from pyspark.sql.functions import udf,collect_list, collect_set
from pyspark.ml.feature import MinHashLSH, BucketedRandomProjectionLSH
from pyspark.ml.linalg import Vectors, VectorUDT

# 控制spark服务启动
spark = SparkSession.builder.appName('app_name').getOrCreate()
spark.stop()
spark = SparkSession.builder.appName('app_name').getOrCreate()

class PySpark(object):
    # 装饰器
    @staticmethod
    def execute(df_input):
        """
        程序入口,需用户重载
        :return:必须返回一个DataFrame类型对象
        """
        # step 1:读入DataFrame
        df_mid = df_input.select('id','name','data','mat')
        print("--------读入的dataframe--------")
        df_mid.show()
        
        # step 2:特征向量预处理
        def mat2vec(mat):
            """
            定义UDF函数,将特征矩阵向量化
            :return:返回相似度计算所需的VectorUDT类型
            """
            arr = [0.0]*len(mat)
            for i in range(len(mat)):
                if mat[i]!='0':
                    arr[i]=1.0
            return Vectors.dense(arr)
        # udf自定义函数,第一个参数为函数,第二个参数为返回值类型
        udf_mat2vec = udf(mat2vec,VectorUDT())
        df_mid = df_mid.withColumn('vec', udf_mat2vec('mat')).select(
                                 'id','name','data','mat','vec')
        print("--------特征向量处理后的的dataframe--------")
        df_mid.show()
        
        # step 3:计算相似度
        ## MinHashLSH,可用EuclideanDistance
        minlsh = MinHashLSH(inputCol="vec", outputCol="hashes", seed=123, numHashTables=3)
        model_minlsh = minlsh.fit(df_mid)
        ## BucketedRandomProjectionLSH
        brplsh = BucketedRandomProjectionLSH(inputCol="vec", 
                                             outputCol="hashes",  
                                             seed=123, 
                                             bucketLength=10.0, 
                                             numHashTables=10)
        model_brplsh = brplsh.fit(df_mid)

        # step 4:计算(忽略自相似,最远距离限制0.8)
        ## model_brplsh类似,可用EuclideanDistance
        df_ret = model_minlsh.approxSimilarityJoin(df_mid, df_mid, 0.8, distCol='JaccardDistance').select(
                col("datasetA.id").alias("id"),
                col("datasetA.name").alias("name"),
                col("datasetA.data").alias("data"),
                col("datasetA.mat").alias("mat"),
                col("JaccardDistance").alias("distance"),
                col("datasetB.id").alias("ref_id"),
                col("datasetB.name").alias("ref_name"),
                col("datasetB.data").alias("ref_data"),
                col("datasetB.mat").alias("ref_mat")
            ).filter("id != ref_id")
        return df_ret

df_in = spark.createDataFrame([
    (1001,"A","xxx","1010001000010000001001101010000"),
    (1002,"B","yyy","1110001000010000000011101010000"),
    (1003,"C","zzz","1101100101010111011101110111101")],
    ['id', 'name', 'data', 'mat'])
df_out = PySpark.execute(df_in)
df_out.show()

有以下的几个注意点:

  • 一开始的三个向量是对用户的特征化描述(如one hot向量一样)
  • 可以学习这种装饰器的写法(很常见);pyspark中调用LSH有两种方法:MinHashLSH, BucketedRandomProjectionLSH两个选择,前者基于Jaccard距离,后者基于欧式距离。
  • pyspark中的udf函数参数,第一个参数是传入的函数,第二个参数是该udf的函数返回值;
  • 对应的结果如下,可以从第三个表看出,A和B之间的distance为0.27(表示不太相似),而A和B,分别与C之间的distance值都是大约为0.75(表示相似,通过看向量之间的数值也可以发现是符合的)。
--------读入的dataframe--------
+----+----+----+--------------------+
|  id|name|data|                 mat|
+----+----+----+--------------------+
|1001|   A| xxx|10100010000100000...|
|1002|   B| yyy|11100010000100000...|
|1003|   C| zzz|11011001010101110...|
+----+----+----+--------------------+

--------特征向量处理后的的dataframe--------
+----+----+----+--------------------+--------------------+
|  id|name|data|                 mat|                 vec|
+----+----+----+--------------------+--------------------+
|1001|   A| xxx|10100010000100000...|[1.0,0.0,1.0,0.0,...|
|1002|   B| yyy|11100010000100000...|[1.0,1.0,1.0,0.0,...|
|1003|   C| zzz|11011001010101110...|[1.0,1.0,0.0,1.0,...|
+----+----+----+--------------------+--------------------+

+----+----+----+--------------------+------------------+------+--------+--------+--------------------+
|  id|name|data|                 mat|          distance|ref_id|ref_name|ref_data|             ref_mat|
+----+----+----+--------------------+------------------+------+--------+--------+--------------------+
|1001|   A| xxx|10100010000100000...|0.2727272727272727|  1002|       B|     yyy|11100010000100000...|
|1003|   C| zzz|11011001010101110...|              0.75|  1001|       A|     xxx|10100010000100000...|
|1002|   B| yyy|11100010000100000...|              0.76|  1003|       C|     zzz|11011001010101110...|
|1002|   B| yyy|11100010000100000...|0.2727272727272727|  1001|       A|     xxx|10100010000100000...|
|1001|   A| xxx|10100010000100000...|              0.75|  1003|       C|     zzz|11011001010101110...|
|1003|   C| zzz|11011001010101110...|              0.76|  1002|       B|     yyy|11100010000100000...|
+----+----+----+--------------------+------------------+------+--------+--------+--------------------+

这里也可以和最常用余弦相似度方法对比一下:

import numpy as np
from numpy import random

# numpy 计算 cos 距离
def cosine_distance(a, b):
    if a.shape != b.shape:
        raise RuntimeError("array {} shape not match {}".format(a.shape, b.shape))
    if a.ndim==1:
        a_norm = np.linalg.norm(a)
        b_norm = np.linalg.norm(b)
    elif a.ndim==2:
        a_norm = np.linalg.norm(a, axis=1, keepdims=True)
        b_norm = np.linalg.norm(b, axis=1, keepdims=True)
    else:
        raise RuntimeError("array dimensions {} not right".format(a.ndim))
    # 计算cos即相似度
    similiarity = np.dot(a, b.T)/(a_norm * b_norm.T)
    return similiarity

# a = random.randint(size(2, 4))
a = np.array([1,2,3,4,5])
b = np.array([5,4,0,2,1])
ans = cosine_distance(a, b)
ans
# 0.5169078021169037

如果是用两个相同的向量最后的cos值自然是1了。

五、faiss向量检索使用

5.1 faiss的基本操作

在这里插入图片描述

  • Faiss:Facebook 开源的向量相似检索库:https://github.com/facebookresearch/faiss,类似的有annoy向量检索库。从多媒体文档中快速搜索出相似的条目——这个场景下的挑战是基于查询的传统搜索引擎无法解决的。Faiss使用的数据流如上图所示。可以通过如下命令下载:
#cpu 版本
conda install faiss-cpu -c pytorch
# GPU 版本
conda install faiss-gpu cudatoolkit=8.0 -c pytorch # For CUDA8
conda install faiss-gpu cudatoolkit=9.0 -c pytorch # For CUDA9
conda install faiss-gpu cudatoolkit=10.0 -c pytorch # For CUDA10

linux上的安装:

# CPU安装
pip install faiss-cpu
# GPU安装
pip install faiss-gpu

按照三部曲进行一个简单的栗子(如下),这里使用最基础的索引类型indexFlatL2,即对embedding进行简单的L2距离搜索:

import numpy as np

# 1. 构建向量库
d = 64                                           # 向量维度
nb = 100000                                      # index向量库的数据量
nq = 10000                                       # 待检索query的数目
np.random.seed(1234)
xb = np.random.random((nb, d)).astype('float32')   # index向量库的向量
xq = np.random.random((nq, d)).astype('float32')   # 待检索的query向量

# 2. 构建index, 并将向量添加到index中
import faiss
index = faiss.IndexFlatL2(d)    # 创建索引时必须指定向量的维度d
print(index.is_trained)         # 输出为True,代表该类index不需要训练,只需要add向量进去即可
index.add(xb)                   # 将向量库中的向量加入到index中
print(index.ntotal)             # 输出index中包含的向量总数,为100000

# 3. 进行topk的检索
k = 4                     # topK的K值, 即对于每个需要检索的向量 找出top4个最相似的embedding
D, I = index.search(xq, k)# xq为待检索向量矩阵,返回的I为每个待检索query最相似TopK的索引list,D为其对应的距离
# 显示前5个待检索embedding的top4检索结果
print(I[:5])
print(D[:5])

最后返回的D为距离,I是返回的每个待检索向量对应的topk列表,如I[: 5]就显示了前五个用户对应的top4个item检索向量:
在这里插入图片描述
上面的faiss.IndexFlatIP也可以换成其他的,注意faiss中最重要的是索引index,其他的API介绍见下表:

MethodClass nameindex_factoryMain parametersBytes/vectorExhaustiveComments
Exact Search for L2IndexFlatL2“Flat”d4*dyesbrute-force
Exact Search for Inner ProductIndexFlatIP“Flat”d4*dyesalso for cosine (normalize vectors beforehand)
Hierarchical Navigable Small World graph explorationIndexHNSWFlat'HNSWx,Flat`d, M4*d + 8 * Mno
Inverted file with exact post-verificationIndexIVFFlat“IVFx,Flat”quantizer, d, nlists, metric4*dnoTake another index to assign vectors to inverted lists
Locality-Sensitive Hashing (binary flat index)IndexLSH-d, nbitsnbits/8yesoptimized by using random rotation instead of random projections
Scalar quantizer (SQ) in flat modeIndexScalarQuantizer“SQ8”ddyes4 bit per component is also implemented, but the impact on accuracy may be inacceptable
Product quantizer (PQ) in flat modeIndexPQ“PQx”d, M, nbitsM (if nbits=8)yes
IVF and scalar quantizerIndexIVFScalarQuantizer“IVFx,SQ4” “IVFx,SQ8”quantizer, d, nlists, qtypeSQfp16: 2 * d, SQ8: d or SQ4: d/2nothere are 2 encodings: 4 bit per dimension and 8 bit per dimension
IVFADC (coarse quantizer+PQ on residuals)IndexIVFPQ“IVFx,PQy”quantizer, d, nlists, M, nbitsM+4 or M+8nothe memory cost depends on the data type used to represent ids (int or long), currently supports only nbits <= 8
IVFADC+R (same as IVFADC with re-ranking based on codes)IndexIVFPQR“IVFx,PQy+z”quantizer, d, nlists, M, nbits, M_refine, nbits_refineM+M_refine+4 or M+M_refine+8no

5.2 基于faiss的向量召回

  • 序列推荐:将用户历史item_emb进行pooling后的user_emb(当然也可以不用mean pooling,用注意力机制就变成DIN模型了,可以参考图文解读:推荐算法架构——精排),和item_emb进行内积得到score偏好分数。交叉熵损失函数的参数即socre和标签item_id(该用户交互的item_id)对应的embedding,典型的多分类问题。
  • 基于Faiss的向量召回:下面就是如一开始背景说的,一般在faiss向量数据库中离线存储训练好的item embedding,然后在线拼好用户user embedding或者根据用户历史item序列进行mean pooling之类的操作得到user embedding,最后两者进行faiss的topk检索。
# 第一步:我们获取所有Item的Embedding表征,然后将其插入Faiss(向量数据库)中
item_embs = model.output_items().cpu().detach().numpy()
item_embs = normalize(item_embs, norm='l2')
gpu_index = faiss.IndexFlatIP(hidden_size)
gpu_index.add(item_embs)

# 第二步:根据用户的行为序列生产User的向量表征
user_embs = model(item_seq,mask,None,train=False)['user_emb']
user_embs = user_embs.cpu().detach().numpy()

# 第三步:对User的向量表征在所有Item的向量中进行Top-K检索
# Inner Product近邻搜索,D为distance,I是index
D, I = gpu_index.search(user_embs, topN)  

5.3 faiss进阶操作

(1)使用GPU加速检索

可以使用gpu加速(注意安装gpu版的faiss),如创建索引时将IndexFlatL2替换为GpuIndexFlatL2

注意:报错module ‘faiss‘ has no attribute ‘StandardGpuResources‘还没解决,留个坑,查了下说是faiss版本的问题,但是换了几个版本还是。。

(2)倒排索引(inverted index)

为什么需要倒排索引呢,首先看下面这样的表:
在这里插入图片描述
如果想搜索还有字符串winter的哪一行,使用sql语句Select * from Quotes where quote_text like '%winter%'语句需要遍历所有行 开销大,一种基础理想的倒排索引操作就是转为如下的结构,这样就能直接找出winter对应的quote_id
在这里插入图片描述

(3)其他索引

附上忘记在哪里看到的一张不同类型的index索引相关总结图:
在这里插入图片描述

Reference

[1] Metrics for Evaluating Quality of Embeddings for Ontological
Concepts

[2] 向量检索库Faiss使用指北
[3] https://pytorch.org/docs/stable/generated/torch.nn.GRU.html?highlight=gru#torch.nn.GRU
[4] Faiss官方文档:https://github.com/facebookresearch/faiss
[5] GRU4Rec v2 - Recurrent Neural Networks with Top-k Gains for Session-based Recommendations
[6] Faiss从入门到实战精通
[7] 某乎:深度ctr预估中id到embedding目前工业界主流是端到端直接学习还是预训练
[8] https://github.com/facebookresearch/faiss
[9] 相似度检索Faiss模型
[10] Faiss(3):基于IndexIVFPQ的demo程序
[11] IndexFlatL2、IndexIVFFlat、IndexIVFPQ三种索引方式示例
[12] facebook. Product quantization for nearest neighbor search(faiss的原论文)
[13] What is an inverted index?(介绍倒排索引)
[14] A brief explanation of the Inverted Index
[15] Indexing 2: inverted index.Victor Lavrenko
[16] 通俗讲解faiss(推荐)
[17] 如何加速faiss python search速度?
[18] 推荐系统(3):倒排索引在召回中的应用

<think>好的,我现在需要回答用户关于rerank模型和embedding模型区别及应用场景的问题。首先,我得回忆一下这两种模型的基本概念。 Embedding模型主要用于将文本转化为向量,捕捉语义信息,方便后续的相似度计算或检索。比如在索时,把用户查询和文档都转成向量,然后计算相似度排序。而Rerank模型则是在初步检索结果的基础上进一步优化排序,通常用更复杂的模型来提升准确性。 接下来,用户提供的引用材料里有几个关键点。引用[3]提到Rerank模型是在Embedding召回的候选文档中进一步筛选,使用深度学习模型捕捉语义关系。这说明两者的应用流程上有先后关系,Embedding负责初步召回,Rerank负责精细排序。引用[2]和[4]则指出两者在应用领域和技术特点上的差异,比如Embedding用于数据表示,Rerank用于结果优化,且Rerank直接影响用户体验。 需要明确两者的核心区别:Embedding生成向量表示,Rerank进行重排序。应用场景上,Embedding多用于召回阶段,Rerank在精排阶段。技术层面,Embedding可能更注重高效的向量生成,而Rerank需要复杂的模型处理上下文和特征。 还要考虑用户可能的疑问,比如为什么需要分开使用?因为Embedding快速召回大量相关文档,但可能不够精准,Rerank则处理少量数据,但更精确,两者结合平衡效率和效果。同时,引用[4]提到商业API的成本问题,比如Embedding可能按字符计费,而Rerank可能需要更多计算资源,所以实际应用中需权衡。 最后,组织回答结构,先定义两种模型,再对比区别,最后给出应用场景和注意事项。确保使用中文,正确引用标记,并生成相关问题。</think>### Rerank模型与Embedding模型的区别及对比 #### 一、核心定义差异 1. **Embedding模型** 通过将文本映射到高维向量空间实现语义表示,核心公式为: $$ \text{Embedding}(text) \rightarrow \mathbf{v} \in \mathbb{R}^d $$ 例如将句子"机器学习"转换为$[0.23, -1.4, ..., 0.76]$的向量。主要用于**语义相似度计算**和**候选召回**,例如从百万级文档库中筛选出Top 100相关文档[^3]。 2. **Rerank模型** 在初步召回结果基础上进行精细化排序,核心公式为: $$ \text{Score}(query, doc) \rightarrow s \in \mathbb{R} $$ 例如使用BERT模型计算查询"深度学习框架"与文档的匹配度得分。专注于提升**排序精度**,通常处理数百条候选数据[^3]。 #### 二、技术特性对比 | 维度 | Embedding模型 | Rerank模型 | |---------------|-------------------------------|-----------------------------| | **输入输出** | 单文本→向量 | (查询,文档)对→相关性得分 | | **计算复杂度** | O(n)(批量编码) | O(n^2)(两两交互计算) | | **典型结构** | 浅层神经网络(如Word2Vec) | 深度交叉网络(如BERT-Cross) | | **延迟要求** | <10ms(实时召回| 50-200ms(精排阶段) | #### 三、应用场景示例 1. **索引擎工作流** ```mermaid graph LR A[用户查询] --> B{Embedding模型} B --> C[召回1000文档] C --> D{Rerank模型} D --> E[返回Top10结果] ``` 在此流程中,Embedding完成**粗筛**,Rerank实现**精排**,两者配合使准确率提升40%以上[^1]。 2. **荐系统案例** - 电商场景:先用Embedding匹配"运动鞋"相关商品,再用Rerank结合用户画像(浏览历史、价格敏感度)重排序 - 视频平台:Embedding召回"科幻电影"后,Rerank根据观看时长、评分、画质偏好调整顺序 #### 四、实践注意事项 1. **数据维度控制** Embedding维度通常选择768-1024(平衡精度与计算成本),Rerank输入需包含: - 用户行为特征(点击率、停留时长) - 上下文特征(设备类型、时间段) - 多模态信息(缩略图向量、音频指纹) 2. **部署优化** - Embedding建议使用FAISS/Qdrant加速检索[^4] - Rerank可采用蒸馏技术(如TinyBERT)压缩模型规模 - 长文本处理时,Embedding需分块编码,Rerank使用滑动窗口评估[^4] 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山顶夕景

小哥哥给我买个零食可好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值