写在前面

最近大模型发展迅速,与之对应的向量化需求也被带动起来了,由此社区也衍生出很多模型,本文选几款,简单做下评测。

前置概念

为方便读者,先简单介绍几个概念。

概念1:Vector Embedding

也即向量化嵌入,举个例子:

想象一下,你是一位市场研究员,职责是分析消费者的购买行为,并为你的客户提供针对性的营销策略。在你的数据库中,有成千上万的消费者交易记录,每条记录都包含了消费者的个人信息、购买的商品、购买的时间和地点等信息。

在没有Vector Embedding的情况下,如果你想找出哪些消费者可能对新产品感兴趣,你可能需要手动查看每条交易记录,然后根据消费者的购买历史和商品的特点来进行判断。然而,这种方法可能忽略了消费者的其他重要特征,如他们的收入水平、兴趣爱好、生活方式等,导致分析的结果不够准确。

现在引入Vector Embedding,此时你就可以将每位消费者的个人信息和购买历史转化为一个多维的“消费者画像向量”。这个向量不仅包括了消费者的基本信息和购买历史,还包含了他们的收入水平、兴趣爱好、生活方式等各个方面的元素。换句话说,这些信息能反映出消费者的复杂特征。例如,一位经常购买高端护肤品的消费者画像向量,大概率与追求高品质生活的向量相近,而一位经常购买户外运动装备的消费者画像向量,大概率与追求健康生活方式和热爱自然的向量相近。此时,若想找出哪些消费者可能对新产品感兴趣,只需要计算出新产品的向量,并与大量消费者画像的向量进行对比,即可快速筛选出潜在的目标客户(目标客户的向量相似度高)。这个过程不再需要逐个查看消费者的个人信息和交易记录,大大简化了数据的处理过程。

更深入的概念可阅读附录[1]进行学习。

概念2:文档切分中的Chunk和Overlap

在处理较长文档或文本时,将其分割成若干小块,每块称为一个Chunk。在这个特定的切分策略中,每个Chunk由最多N个Token组成。Token通常指文本中的单词或符号,取决于具体语境。而Overlap是相邻Chunk之间共有的Token数。举个例子:

每Chunk 200 Token,Overlap 20。在这个例子中,每个Chunk由最多200个Token组成。Overlap为20,意味着相邻的Chunks会有20个Token是重复的,从而确保文本的连贯性。例如,如果某个文本段落共有230个Token,它将被分成两个Chunks:第一个Chunk将有200个Token,第二个Chunk将有30个Token(因为230-200=30),并且这两个Chunks之间将有20个Token重叠。

这种切分方式有助于确保在将长文档送入诸如大型语言模型进行Embedding或处理时,能够保持文本的语义连贯性,同时又能满足模型处理长度有限的输入的要求。切分文档时考虑Chunk的大小和Overlap的数量,对于提高模型处理效率和文本的语义理解都是十分重要的。

模型调研

与大模型类似,Embedding也是使用模型来实现的,只不过Embedding模型更为轻量。一般都在2G以内。

经调研(附录[6~10]),发现以下模型对中文的支持效果较好,且已经开源方便本地私有化部署:

可以看得出m3模型的优势是支持多语言,并且字符数扩展到了8192,这意味着BGE-M3能够高效地处理长篇幅的文档,满足对于长文档检索的需求。

以上几类模型的链接请参考附录[2~5],下文针对这几种模型进行效果评测。

模型部署

部署BGE模型,首先我们需要明确对应的工具库,目前主要方法有几类:

  1. Sentence-Transformers: Sentence-Transformers库是基于HuggingFace的Transformers库构建的,它专门设计用于生成句子级别的嵌入。它引入了一些特定的模型和池化技术,使得生成的嵌入能够更好地捕捉句子的语义信息。Sentence-Transformers库特别适合于需要计算句子相似度、进行语义搜索和挖掘同义词等任务。

  2. HuggingFace Transformers: HuggingFace的Transformers库是一个广泛使用的NLP库,它提供了多种预训练模型,如BERT、GPT-2、RoBERTa等。这些模型可以应用于各种NLP任务,如文本分类、命名实体识别、问答系统等。Transformers库支持多种编程语言,并且支持模型的微调和自定义模型的创建。虽然Transformers库的功能强大,但它主要关注于模型的使用,而不是直接提供句子级别的嵌入。

  3. Langchain集成的HuggingFaceBgeEmbeddings。与3一样。

  4. FlagEmbedding: 这是一个相对较新的库,其核心在于能够将任意文本映射到低维稠密向量空间,以便于后续的检索、分类、聚类或语义匹配等任务。FlagEmbedding的一大特色是它可以支持为大模型调用外部知识,这意味着它不仅可以处理纯文本数据,还能整合其他类型的信息源,如知识图谱等,以提供更丰富的语义表示。

总的来说,FlagEmbedding强调的是稠密向量的生成和外部知识的融合;HuggingFace Transformers提供了一个广泛的预训练模型集合,适用于多种NLP任务;而Sentence-Transformers则专注于生成高质量的句子嵌入,适合那些需要深入理解句子语义的应用场景。

结合上述说明,以及翻阅网上各类文章,发现使用 Sentence-Transformers 居多,因此本文选用它。

安装 sentence-transformers(在Linux机器安装吧,Windows机器各种报错):

pip install -U sentence-transformers

基于Sentence-Transformers的向量化方法:

from sentence_transformers import SentenceTransformer
sentences_1 = ["样例数据-1", "样例数据-2"]
sentences_2 = ["样例数据-3", "样例数据-4"]
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
embeddings_1 = model.encode(sentences_1, normalize_embeddings=True)
embeddings_2 = model.encode(sentences_2, normalize_embeddings=True)
similarity = embeddings_1 @ embeddings_2.T
print(similarity)

上面的例子演示了句子的向量化过程。额外解释一下倒数第二行:

embeddings_1 @ embeddings_2.T 是 Python 中的一种矩阵乘法运算。这里使用了 @ 符号来表示矩阵的点积(dot product)操作。具体来说:

  • embeddings_1 是一个二维数组(或称为矩阵),其中每一行代表一个句子在向量空间中的嵌入表示。

  • embeddings_2 也是一个二维数组,其结构与 embeddings_1 相同,但包含不同的句子的嵌入表示。

  • embeddings_2.Tembeddings_2 的转置,这意味着将它的行列互换。这样,原来的每行变成了每列,原来的每列变成了每行。

当执行 embeddings_1 @ embeddings_2.T 时,Python 会计算两个矩阵的点积。结果是一个新的二维数组,其中的每个元素是 embeddings_1 中的一行和 embeddings_2.T 中对应列的乘积之和。在这个上下文中,它实际上是在计算两组句子嵌入之间的相似度矩阵。

例如,如果 embeddings_1 和 embeddings_2.T 分别是以下的矩阵:

embeddings_1: [ [e11, e12, e13], [e21, e22, e23] ]
embeddings_2.T: [ [f11, f21], [f12, f22], [f13, f23] ]

那么点积的结果将是:

similarity: [ [e11*f11 + e12*f12 + e13*f13, e11*f21 + e12*f22 + e13*f23],
              [e21*f11 + e22*f12 + e23*f13, e21*f21 + e22*f22 + e23*f23] ]

这个结果矩阵中的每个元素代表了原始句子对之间的某种形式的相似度分数。

有了工具集后,把模型文件下载到本地并加载即可。

评测方法

到网上随便找一篇文章:

据传,菜煎饼起源于13世纪中期,当时明军与元军在峄州展开激战,当地人民死伤惨重。后来,从山西洪洞一带移民至此的民众,仅靠官府发放的半斤粮食无法充饥,便将五谷掺水,用石磨研磨成浆糊,放在铁板上,用竹片摊成“薄纸”,并大量包装蔬菜、野菜、草根和树叶,以此充饥。

菜煎饼是山东鲁南地区的一种大众食品,制作原料主要有面粉、杂粮、鸡蛋等,老少兼宜,俗称“中国热狗”,流行于枣庄、济宁、临沂、徐州等鲁南地,后传布周围省市。上个世纪七十年代,枣庄农村的生活还是很匮乏的,老百姓的主食以煎饼为主,煎饼的主要原料是地瓜干,条件好一点的可稍放点小麦,刚烙煎饼时鏊子凉,需把鏊子烧热擦些油才容易把煎饼从鏊子上揭下来,这样烙出的煎饼就很厚,稍等一会儿,煎饼凉了又板又硬,很难下咽。因此我们枣庄人把烙煎饼时前几张和后几张煎饼称为滑鏊子煎饼或滑塌子。这样的煎饼很难下咽,但丢了又可惜,精明的母亲们就将大白菜,土豆丝,粉条,豆腐切碎加点猪油,放上辣椒面,花椒面和盐,做成了所谓的菜煎饼,这样一来不但滑鏊子煎饼解决了,并且做出的煎饼还特别好吃,这样一传十、十传百,于是菜煎饼就在农村各家各户传开了!

八十年代末期,农村土地实行了联产承包责任制已有多年,农民在农忙季节忙耕种,农闲时便有了剩余时间,有的农村妇女就到街上摆地摊卖菜煎饼挣点零花钱。一辆三轮车,一盘小饼鍪,一个蜂窝炉,一个切菜板,几样时令蔬菜,食客现场点菜,业主现场烙制,简简单单的营生,成为枣庄街头一道风景。许许多多的农村人多了一个贴补家用的挣钱机会,人们生活也多了一道风味小吃。到了九十年代末期,就连一些男人也走上了街头卖起了菜煎饼。

1993年5月,山东省劳动厅在枣庄举办特级厨师培训班,聘请江苏省淮安商业技工学校一行6人赴枣庄讲学,这六人当中有校领导、高级讲师、特级厨师,途中经台儿庄区招待所午餐,席上菜肴丰盛,但惟有“菜煎饼”被其六人齐呼:“天下第一美食”。

山东菜煎饼如何做呢?首先要热锅,放油(油要多),下豆腐中火翻炒至金黄,放入之前切好的粉条,继续翻炒几分钟,加入适量的盐。再放入切好的韭菜,翻炒几下搅匀即可(千万不可炒过了,韭菜要生生的),撒味精出锅。将煎饼摊开,用勺子舀上适量的韭菜馅儿,用勺背整匀。好了之后,可以将两边向中间折叠形成长方形,一张煎饼就做好了。

按照前文的样例,写一段脚本进行评测(为方便演示,简单地根据段落进行拆分):

import sys
import torch
from sentence_transformers import SentenceTransformer # 加载预训练的句子嵌入模型(指定本地模型文件路径)
model = SentenceTransformer(sys.argv[1])# 定义句子列表
sentences_1 = ["据传,菜煎饼起源于13世纪中期,当时明军与元军在峄州展开激战,当地人民死伤惨重。后来,从山西洪洞一带移民至此的民众,仅靠官府发放的半斤粮食无法充饥,便将五谷掺水,用石磨研磨成浆糊,放在铁板上,用竹片摊成“薄纸”,并大量包装蔬菜、野菜、草根和树叶,以此充饥。"]
sentences_2 = ["菜煎饼是山东鲁南地区的一种大众食品,制作原料主要有面粉、杂粮、鸡蛋等,老少兼宜,俗称“中国热狗”,流行于枣庄、济宁、临沂、徐州等鲁南地,后传布周围省市。上个世纪七十年代,枣庄农村的生活还是很匮乏的,老百姓的主食以煎饼为主,煎饼的主要原料是地瓜干,条件好一点的可稍放点小麦,刚烙煎饼时鏊子凉,需把鏊子烧热擦些油才容易把煎饼从鏊子上揭下来,这样烙出的煎饼就很厚,稍等一会儿,煎饼凉了又板又硬,很难下咽。因此我们枣庄人把烙煎饼时前几张和后几张煎饼称为滑鏊子煎饼或滑塌子。这样的煎饼很难下咽,但丢了又可惜,精明的母亲们就将大白菜,土豆丝,粉条,豆腐切碎加点猪油,放上辣椒面,花椒面和盐,做成了所谓的菜煎饼,这样一来不但滑鏊子煎饼解决了,并且做出的煎饼还特别好吃,这样一传十、十传百,于是菜煎饼就在农村各家各户传开了!"]
sentences_3 = ["八十年代末期,农村土地实行了联产承包责任制已有多年,农民在农忙季节忙耕种,农闲时便有了剩余时间,有的农村妇女就到街上摆地摊卖菜煎饼挣点零花钱。一辆三轮车,一盘小饼鍪,一个蜂窝炉,一个切菜板,几样时令蔬菜,食客现场点菜,业主现场烙制,简简单单的营生,成为枣庄街头一道风景。许许多多的农村人多了一个贴补家用的挣钱机会,人们生活也多了一道风味小吃。到了九十年代末期,就连一些男人也走上了街头卖起了菜煎饼。"]
sentences_4 = ["1993年5月,山东省劳动厅在枣庄举办特级厨师培训班,聘请江苏省淮安商业技工学校一行6人赴枣庄讲学,这六人当中有校领导、高级讲师、特级厨师,途中经台儿庄区招待所午餐,席上菜肴丰盛,但惟有“菜煎饼”被其六人齐呼:“天下第一美食”。"]
sentences_5 = ["山东菜煎饼如何做呢?首先要热锅,放油(油要多),下豆腐中火翻炒至金黄,放入之前切好的粉条,继续翻炒几分钟,加入适量的盐。再放入切好的韭菜,翻炒几下搅匀即可(千万不可炒过了,韭菜要生生的),撒味精出锅。将煎饼摊开,用勺子舀上适量的韭菜馅儿,用勺背整匀。好了之后,可以将两边向中间折叠形成长方形,一张煎饼就做好了。"]
# 获取句子的嵌入向量表示、
sentences_embeddings_1 = model.encode(sentences_1, normalize_embeddings=True)
sentences_embeddings_2 = model.encode(sentences_2, normalize_embeddings=True)
sentences_embeddings_3 = model.encode(sentences_3, normalize_embeddings=True)
sentences_embeddings_4 = model.encode(sentences_4, normalize_embeddings=True)
sentences_embeddings_5 = model.encode(sentences_5, normalize_embeddings=True)
# 合并所有的句子嵌入表示
all_sentences_embeddings = torch.cat([sentences_embeddings_1, sentences_embeddings_2, sentences_embeddings_3, sentences_embeddings_4, sentences_embeddings_5], dim=0) # 定义查询句子
queries_1 = ["菜煎饼的制作原料有哪些?"]
queries_2 = ["菜煎饼的组成是什么?"]
queries_3 = ["做菜煎饼需要什么?"]
# 获取查询句子的嵌入向量表示
queries_embeddings_1 = model.encode(queries_1, normalize_embeddings=True)
queries_embeddings_2 = model.encode(queries_2, normalize_embeddings=True)
queries_embeddings_3 = model.encode(queries_3, normalize_embeddings=True) # 计算查询句子与所有句子的相似度
similarity_queries_1_sentences = queries_embeddings_1 @ all_sentences_embeddings.T
similarity_queries_2_sentences = queries_embeddings_2 @ all_sentences_embeddings.T
similarity_queries_3_sentences = queries_embeddings_3 @ all_sentences_embeddings.T
# 打印相似度结果(几个问题都是在问制作原料,从字面来看,我们预期三个查询与sentences_2 和 sentences_5 的相似度最高)
print("Query 1 Similarity:", similarity_queries_1_sentences)
print("Query 2 Similarity:", similarity_queries_2_sentences)
print("Query 3 Similarity:", similarity_queries_3_sentences)

执行时,把模型本地路径作为第一个参数传入即可,如:

time python test.py ./bge-large-zh-v1.5
time python test.py ./bge-m3
time python test.py ./m3e-base
time python test.py ./tao-8k

其中的time是为了测试程序运行耗时。

评测结果

作者在本机CPU执行,机器配置为:4核CPU(i7)、16GB内存。

评测结果如下:

看起来,bge-m3效果最好,tao-8k也不错。但需要注意的是,这两个模型执行耗时也最高。

总结

本文选用常见的几类中文友好的开源Embedding模型进行了简单效果评测,发现bge-m3和tao-8k的效果不错。有条件的读者可以将其部署在GPU上进行评测,应该会更快。另外,也可以使用更为全面的数据集进行评估。

在实际的生产环境中,还要进行压力测试,评估文档传入的性能。

最后,欢迎关注微信公众号xiaoxi666,一起交流一起玩儿~

附录

[1] 向量数据库: https://guangzhengli.com/blog/zh/vector-database/

[2] bge-large-zh-v1.5: https://hf-mirror.com/BAAI/bge-large-zh-v1.5

[3] bge-m3: https://hf-mirror.com/BAAI/bge-m3

[4] m3e-base: https://hf-mirror.com/moka-ai/m3e-base

[5] tao-8k: https://hf-mirror.com/amu/tao-8k

[6] 智源开源最强语义向量模型BGE: https://zhuanlan.zhihu.com/p/648448793

[7] 新一代通用向量模型BGE-M3: https://zhuanlan.zhihu.com/p/680537154

[8] 实战对比OpenAI、BGE-Large以及阿里Embedding模型效果: https://zhuanlan.zhihu.com/p/658775304​

[9] Embedding模型的选择: https://zhuanlan.zhihu.com/p/673483110

[10] 中文Embedding模型优劣数据评测: https://zhuanlan.zhihu.com/p/679166797

Embedding 模型部署及效果评测的更多相关文章

  1. PyTorch专栏(六): 混合前端的seq2seq模型部署

    欢迎关注磐创博客资源汇总站: http://docs.panchuang.net/ 欢迎关注PyTorch官方中文教程站: http://pytorch.panchuang.net/ 专栏目录: 第一 ...

  2. 如何使用flask将模型部署为服务

    在某些场景下,我们需要将机器学习或者深度学习模型部署为服务给其它地方调用,本文接下来就讲解使用python的flask部署服务的基本过程. 1. 加载保存好的模型 为了方便起见,这里我们就使用简单的分 ...

  3. 混合前端seq2seq模型部署

    混合前端seq2seq模型部署 本文介绍,如何将seq2seq模型转换为PyTorch可用的前端混合Torch脚本.要转换的模型来自于聊天机器人教程Chatbot tutorial. 1.混合前端 在 ...

  4. 【百度飞桨】手写数字识别模型部署Paddle Inference

    从完成一个简单的『手写数字识别任务』开始,快速了解飞桨框架 API 的使用方法. 模型开发 『手写数字识别』是深度学习里的 Hello World 任务,用于对 0 ~ 9 的十类数字进行分类,即输入 ...

  5. whylogs工具库的工业实践!机器学习模型流程与效果监控 ⛵

    作者:韩信子@ShowMeAI 机器学习实战系列:https://www.showmeai.tech/tutorials/41 本文地址:https://www.showmeai.tech/artic ...

  6. osg项目经验1<MFC+OSG中模型点选效果>

    点选主要是重载osg的GUIEventHandler, class CPickHandler : public osgGA::GUIEventHandler{ //自定义回调函数名:CPickHand ...

  7. 学习笔记TF022:产品环境模型部署、Docker镜像、Bazel工作区、导出模型、服务器、客户端

    产品环境模型部署,创建简单Web APP,用户上传图像,运行Inception模型,实现图像自动分类. 搭建TensorFlow服务开发环境.安装Docker,https://docs.docker. ...

  8. Tensorflow Serving 模型部署和服务

    http://blog.csdn.net/wangjian1204/article/details/68928656 本文转载自:https://zhuanlan.zhihu.com/p/233614 ...

  9. 【tensorflow-转载】tensorflow模型部署系列

    参考 1. tensorflow模型部署系列: 完

  10. Slim模型部署多GPU

    1 多GPU原理 单GPU时,思路很简单,前向.后向都在一个GPU上进行,模型参数更新时只涉及一个GPU. 多GPU时,有模型并行和数据并行两种情况. 模型并行指模型的不同部分在不同GPU上运行. 数 ...

随机推荐

  1. 机器学习-线性分类-SVM支持向量机算法-12

    目录 1. 铺垫 感知器算法模型 2. SVM 算法思想 3. 硬分割SVM总结 支持向量机(Support Vector Machine, SVM)本身是一个二元分类算法,是对感知器算法模型的一种扩 ...

  2. RLHF · PbRL | 速通 ICLR 2024 RLHF

    检索关键词:ICLR 2024.reinforcement learning.preference.human feedback. https://openreview.net/search?term ...

  3. 【SHELL】在指定格式的文件中查找字符串

    在指定格式的文件中查找字符串 grep -nr "string" --include=*.{c,cpp,h} 在排除指定格式的文件中查找字符串 grep -nr "str ...

  4. 【C/C++】sscanf函数和正则表达式

    此文所有的实验都是基于下面的程序:char str[10];for (int i = 0; i < 10; i++) str[i] = '!';执行完后str的值为str = "!!! ...

  5. [转帖]Linux命令(51)——ipcs命令

    https://cloud.tencent.com/developer/article/1380589 1.命令简介 ipcs命令用于报告Linux中进程间通信设施的状态,显示的信息包括消息列表.共享 ...

  6. [转帖]Percolator分布式事务模型原理与应用

    https://zhuanlan.zhihu.com/p/59115828 Percolator 模型 Percolator[1] 是 Google 发表在 OSDI'2010 上的论文 Large- ...

  7. [转帖]ssd/san/sas/磁盘/光纤/RAID性能比较

    https://plantegg.github.io/2022/01/25/ssd_san%E5%92%8Csas%E7%A3%81%E7%9B%98%E6%80%A7%E8%83%BD%E6%AF% ...

  8. [转帖]Linux | crontab定时任务及开机自启项

    本文总结如何使用 crontab 创建定时任务及开启自启项 1. 本文基于 CentOS 7.9 系统所写. Updated: 2022 / 9 / 5 Linux | crontab定时任务及开机自 ...

  9. [转帖]THE OSWATCHER ANALYZER USER'S GUIDE

    oswbba THE OSWATCHER ANALYZER USER'S GUIDE Carl DavisMay 7, 2019 To see how to use this tool and it' ...

  10. Springboot 使用nacos鉴权的简单步骤

    Springboot 使用nacos鉴权的简单步骤 背景 前端时间nacos爆出了漏洞. 因为他的默认token固定,容易被利用. 具体的问题为: QVD-2023-6271 漏洞描述:开源服务管理平 ...