上周算是我正式离职创业的第一周,拜访客户、行业交流、选办公场地、置办办公设备等等,很多时间不在电脑面前,所以上周没更新任何文章。嗯,那就这周补上,发两篇!

今天这篇是上周本来就想写的,就是OpenAI DevDay(开发者大会)之后,基于大模型及相关的创业项目前景如何。

土猛的员外
上周算是我正式离职创业的第一周,拜访客户、行业交流、选办公场地、置办办公设备等等,很多时间不在电脑面前,所以上周没更新任何文章。嗯,那就这周补上,发两篇!

今天这篇是上周本来就想写的,就是OpenAI DevDay(开发者大会)之后,基于大模型及相关的创业项目前景如何。

本文主要内容:
- 为什么一般情况下RAG的检索相关性存在问题?
- Rerank为什么可以解决这个问题?
- 几种常用Rerank组合评测;
- 如何在自己的产品中使用Rerank?
检索增强生成(RAG)是解决大语言模型(LLM)实际使用中的一套完整的技术,它可以有效解决LLM的三个主要问题:数据时效性、幻觉和数据安全问题(在我之前的文章《大模型主流应用RAG的介绍——从架构到技术细节》中有详细介绍)。但是随着RAG越来越火热,使用者越来越多,我们也会发现用的好的人/团队其实还是不多的。这也是RAG常被人吐槽的一点:入门简单,用好却非常难!
对于RAG的效果,我们之前已经做了很多方面的优化了,包括:
这些优化确实给我们带来了非常好的效果,但不够!我们在一些客户的实践过程中,还是发现相关性效果不佳,甚至造成了其中一个客户选择了其他方案(使用RAG+GPT-4的方案)。
我们还是坚持用国产大模型(如Baichuan2-13B、ChatGLM3-6B和QWen-14B等),毕竟主要服务的还是国内客户,加上现在接触的多数客户其实都有私有化部署的需求。所以我们进行了一段时间的探索,发现我们还有一项很有效的优化没有去做——ReRank。
所以,虽然Rerank优化我们还在做,但是今天我们可以先聊聊ReRank这个话题。
我们发现,在10月中旬之前,国内外的互联网上很难发现Rerank相关的话题。有少量人提到了,但是基本上都没有提到解决方案。我和小明在讨论Rerank的时候其实是先从提问题开始的。
在构建检索增强生成(RAG)管道时,一个关键组件是Retriver。我们有各种各样的Embedding模型可供选择,包括OpenAI、CohereAI和开源的Sentence-Transformers。此外,CohereAI和Sentence-Transformers也提供了一些重新排序器。
但是有了这些选项,我们如何确定最佳组合以获得一流的检索性能呢?我们如何知道哪种Embedding模型最适合我们的数据?或者哪个重新排名对我们的结果提升最大?
在这篇博文中,我们将使用LlamaIndex的检索评估(Retrieval Evaluation)模块来快速确定Embedding和重新排名模型的最佳组合。让我们开始吧!
让我们首先从理解检索评估(Retrieval Evaluation)中可用的度量标准开始。
为了衡量我们的检索系统的有效性,我们主要依赖于两个被广泛接受的指标:命中率和**平均倒数排名(MRR)**。让我们深入研究这些指标,了解它们的重要性以及它们是如何运作的。
命中率:
Hit rate计算在前k个检索文档中找到正确答案的查询比例。简单来说,它是关于我们的系统在前几次猜测中正确的频率。
平均倒数排名(MRR):
对于每个查询,MRR通过查看排名最高的相关文档的排名来评估系统的准确性。具体来说,它是所有查询中这些秩的倒数的平均值。因此,如果第一个相关文档是顶部结果,则倒数排名为1;如果是第二个,倒数是1/2,以此类推。
现在我们已经确定了范围并熟悉了参数,是时候深入实验了。想要亲身体验,你也可以使用我们的谷歌Colab笔记本
1 | !pip install llama-index sentence-transformers cohere anthropic voyageai protobuf pypdf |
1 | openai_api_key = 'YOUR OPENAI API KEY' |
本次实验我们将使用Llama2论文吧。
1 | !wget --user-agent "Mozilla" "https://arxiv.org/pdf/2307.09288.pdf" -O "llama2.pdf" |
让我们加载数据。我们将使用从第1页到第36页进行实验,不包括目录、参考文献和附录。
然后将该数据解析为节点,节点表示我们想要检索的数据块。我们确实使用chunk_size为512。
1 | documents = SimpleDirectoryReader(input_files=["llama2.pdf"]).load_data() |
为了评估的目的,我们创建了一个问题-上下文对的数据集。这个数据集可以被看作是我们数据中的一组问题及其相应的上下文。为了消除评估Embedding(OpenAI/ CohereAI)和重新排序(CohereAI)的偏见,我们使用Anthropic LLM来生成问题-上下文对。
让我们初始化一个prompt模板来生成问题-上下文对。
1 | # Prompt to generate questions |
过滤句子的功能,比如— Here are 2 questions based on provided context
1 | # function to clean the dataset |
为了识别最优的检索器,我们采用了Embedding模型和重新排序器的组合。首先,我们建立一个基本的VectorIndexRetriever。检索节点后,我们引入一个重新排序器来进一步优化结果。值得注意的是,在这个特殊的实验中,我们将similarity_top_k设置为10,并使用reranker选择top5。但是,您可以根据具体实验的需要随意调整此参数。我们在这里用OpenAIEmbedding显示代码,请参阅笔记本获取其他Embeddings的代码。
1 | embed_model = OpenAIEmbedding() |
为了评估我们的检索器,我们计算了平均倒数排名(MRR)和命中率指标:
1 | retriever_evaluator = RetrieverEvaluator.from_metric_names( |
我们对各种Embedding模型和重新排序器进行了测试。以下是我们考虑的模型:
向量模型:
Rerank模型:
值得一提的是,这些结果为这个特定数据集和任务的性能提供了坚实的见解。但是,实际结果可能会根据数据特征、数据集大小和其他变量(如chunk_size、similarity_top_k等)而有所不同。
下表展示了基于命中率和平均倒数排名(MRR)指标的评估结果:

cohererank(0.926966命中率,0.865262 MRR)和big-reranker-large(0.910112命中率,0.853993 MRR),表明与重排名工具的兼容性很强。CohereRerank的结果最好(0.865169命中率,0.805618 MRR)。CohereRerank (0.887640命中率,0.825843 MRR),它提供了实质性的性能提升。cohererank (0.915730命中率,0.847940 MRR)进一步放大,表明对重新排名的响应性较高。big -rerank -large的收益显著(命中率0.601124,MRR 0.578652),表明重新排名显著提升了它的性能。其性能不佳的一个潜在原因可能是Embedding针对8K上下文长度进行了优化。 cohererank相媲美或超过。CohereRerank,已经证明了它们将平均表现的Embedding转化为具有竞争力的能力,正如JinaAI所看到的那样。当考虑到命中率和MRR时,OpenAI + CohereRerank和Voyage + big-reranker-large的组合成为最热门的竞争者。
然而,cohererank/big-reranker-large reranker在各种Embeddings中所带来的持续改善,使它们成为提高搜索质量的突出选择,无论使用的Embedding是什么。
综上所述,为了实现命中率和MRR的峰值性能,OpenAI或Voyage Embeddings与cohererank/big-reranker-largeReranker的组合脱颖而出。
在这篇博文中,我们演示了如何使用各种Embeddings和重新排序器来评估和提高检索器的性能。以下是我们的最终结论。
OpenAI和 VoyageEmbeddings,特别是当与CohereRerank/big-reranker-large reranker配对时,为命中率和MRR设定了黄金标准。cohererank/big-reanker-large,怎么强调都不为过。它们在提高许多Embeddings的MRR方面发挥了关键作用,显示了它们在使搜索结果更好方面的重要性。原文:https://blog.llamaindex.ai/boosting-rag-picking-the-best-embedding-reranker-models-42d079022e83
本文是一篇英文翻译转载文章,主要介绍了HNSW算法。
原文链接:https://dataman-ai.medium.com/search-like-light-speed-1-hnsw-c5b0d4665926
我喜欢《玩具总动员》里的太空护林员“巴斯光年”,我喜欢他的口头禅“飞向无限!” 当我搜索信息时,我也享受找到正确信息的速度。这一切都是因为高速互联网和足够的带宽吗?不完全是!事实上,近乎即时搜索结果的算法是至关重要的。信息检索速度是计算机科学中的一个重要课题。随着文本、图像或音频数据的大型语言模型(大语言模型)的高维Embeddings,信息检索的速度是数据科学中的一个优先课题。
在这篇文章中,我将讨论:
本文及其后续系列解释了使巴斯光年的梦想成为可能的最先进算法。您将对这一领域及其应用的重要性有一个景观理解。您将有动手编码示例。我们开始吧。
NLP中的向量Embeddings
向量Embeddings是自然语言处理(NLP)中的一个基本概念,是单词、句子、文档、图像、音频或视频数据等对象的数字表示。这些Embeddings的目的是捕获它们所表示的对象的语义和上下文信息。
让我们首先描述一下单词Embeddings。2014年,一个突破性的想法Word2Vec(发音为“Word - to - Vector”)在自然语言处理中被提出,它将单词或短语转换或“嵌入”为数字的高维向量,称为单词Embeddings。这些词Embeddings捕捉词之间的语义和上下文关系,使机器能够理解和使用人类语言。图1显示了三维空间中的高维向量。“铁(iron)”这个词与“火药(gunpowder)”、“金属(metals)”和“钢(steel)”等词很接近,但与“有机(organic)”、“糖(sugar)”或“谷物(grain)”等不相关的词相去甚远。例如,猫和狗的概念可能很接近。

图 (1):文字Embeddings(图片来源:作者)
单词Embeddings可以实现单词的相似或不相似。这是一项了不起的创新。既然单词可以嵌入,为什么句子不能呢?这就是句子Embeddings的诞生。句子Embeddings捕获整个句子的语义和上下文信息,使机器能够理解和比较句子。生成句子Embeddings的常用方法包括Doc2Vec (Document-to-vector)。强大的基于llm的词Embeddings将成为NLP的标准,如BERT(来自Transformers的双向编码器表示)、ELMo(来自语言模型的Embeddings)、Llama(大型语言模型元AI,由Meta AI于2023年2月推出),以及OpenAI的多个模型。
既然文本可以作为向量嵌入,为什么不能嵌入图像呢?这就产生了图像Embeddings。卷积神经网络(cnn)和视觉几何组(VGG)用于生成图像Embeddings。图像Embeddings使图像检索和分类成为可能。
既然图像可以作为矢量嵌入,为什么不能嵌入音频呢?你说得对!音频Embeddings可以捕获音频数据的声学特征,并可以进行音频分类和检索。视频Embeddings如何?它们捕获图像特征流用于视频分类。那么地理空间Embeddings呢?当然可以。纬度和经度坐标等地理空间数据可以嵌入到高维向量中,便于信息检索。
Embeddings使一切变得简单。如果你有一篇文章,需要找到类似的文章,你只需要计算你的文章的向量到其他文章的向量之间的距离。最近的向量就是你的搜索结果。我们可以用k近邻法(KNN),对吧?然而,速度是个问题。KNN的搜索将使光年皱眉。对于巴斯光年来说,完成一次简单的搜索需要…需要不知道多少年。研究的挑战不是最近的邻居在哪里,而是“如何”找到它们。
k-最近邻(KNNs)无法跟上速度
假设你有一本新书,你想在图书馆找到类似的书。k-最近邻(KNN)将浏览书架上的每一本书,并将它们从最相似到最不相似的顺序排列,以确定最相似的书。你有耐心做这么麻烦的工作吗?相反,人工神经网络对图书馆中的图书进行预排序和索引。要找到与你的新书相似的书,你所需要做的就是去正确的楼层,正确的区域,正确的通道找到相似的书。此外,你通常不需要对前10本相似的书进行精确排名,比如100%、99%或95%的匹配度。这就是近似近邻(ANN)的思想。

让我们来了解一下为什么人工神经网络可以更有效地搜索。
近似最近邻(ANN)感觉像光速
ANN (Approximate Nearest Neighbors)对大数据进行预索引,方便快速搜索。在索引期间,构建数据结构以促进更快的查询。当您想为一个查询点找到近似的最近邻居时,您可以将该查询点提供给ANN算法。人工神经网络算法首先从数据集中识别一组可能接近查询点的候选数据点。使用预构建的数据结构选择候选对象。这一步骤大大减少了需要检查接近性的数据点的数量。在候选点被选中之前,ANN计算每个候选点与查询点之间的实际距离(如欧几里得距离、余弦相似度)。然后根据与查询点的距离/相似度对候选项进行排名。排名靠前的候选人作为近似近邻返回。在某些情况下,还可以设置距离阈值,只返回该阈值内的候选对象。人工神经网络背后的关键思想是,为了显著降低计算成本,它牺牲了找到绝对最近邻的保证。这些算法的目标是在计算效率和准确性之间取得平衡。
然而,在高维空间中,过去的实验表明ANN并不比KNN节省多少时间(见[4])。有几种创新的人工神经网络算法适用于高维空间。我将列出这些算法的字母表。您很快就会熟悉这些字母,并且可能更愿意在NLP社区中使用它们进行交流。让我们学习流行的最先进的算法。
最先进的快速搜索算法
这些不同的人工神经网络算法是不同的方法来形成数据结构,以实现有效的检索。有三种类型的算法:基于图的、基于哈希的和基于树的。
基于图的算法创建数据的图表示,其中每个数据点是一个节点,边表示数据点之间的接近性或相似性。最引人注目的是层次导航小世界图(HNSW)。
基于哈希的算法使用哈希函数将数据点映射到哈希码或桶。流行的算法包括:位置敏感哈希(LSH)、多索引哈希(MIH)和产品量化
基于树的算法将数据集划分为树状结构,以便快速搜索。流行的是kd树、球树和随机投影树(RP树)。对于低维空间(≤10),基于树的算法是非常有效的。
有几个流行的代码库:
NearestNeighbors类提供了一个简单的接口,可以使用LSH等技术进行精确和近似的最近邻搜索。使用上述代码库,您可以超级快速地执行搜索查询。您还需要了解其他库的变体。这里我只提到其中的三个。第一个是PyNNDescent。PyNNDescent是一个Python库,用于基于NN-descent的搜索算法,它是LSH的一个变体。第二个是NearPy。它支持多个距离度量和哈希族。第三个是PyKDTree。PyKDTree是一个Python库,用于基于kd树的最近邻(KNN)搜索。虽然kd树更常用于精确搜索,但PyKDTree也可以通过一些启发式优化用于近似搜索。
此外,如果您询问哪些算法和库执行速度最好,您只需要了解**ANN- benchmark **库,专门为对人工神经网络搜索算法进行基准测试而设计。它提供了一个标准化的框架来评估算法,如Annoy, FLANN, scikit-learn (LSHForest, KDTree, BallTree), PANNS, NearPy, KGraph, NMSLIB(非度量空间库),hnswlib, RPForest, FAISS, nndescent, PyNNDescent等等。
这不仅仅是最近邻在哪里,而是如何有效地找到他们。让我们学习第一个算法——HNSW。HNSW通常可以在几毫秒内从数百万个数据点中找到最近邻。
了解分层导航小世界图(HNSW)
HNSW是一种用于在高维空间中进行高效人工神经网络搜索的数据结构和算法。它是跳表和小世界图(SWG)结构的扩展,可以有效地找到近似的最近邻。如果我们先学习跳表和小世界图,学习HNSW就会很简单。
跳表是一种数据结构,用于维护一组已排序的元素,并允许进行高效的搜索、插入和删除操作。它是由William Pugh在1989年发明的。图(2)显示了数字[3、6、7、9、12、17、19、21、25、26]的排序链表。假设我们想找到目标19。当值小于目标时,我们向右移动。需要6步才能找到它。

图 (2): 排序链表
现在,如果列表的每个其他节点都有一个指向前面节点2的指针,如图3所示,可以将这些指针视为“高速公路”。数学规则是“当数值小于目标时向右移动”。需要4个步骤才能达到19。

图 (3): 跳表,其指针指向后面两个节点
这些高速公路加快了搜索速度。我们可以增加更多。现在,如果列表中每三个其他节点都有一个指向前面第三个节点的指针,如图(4)所示,那么只需要3步就可以到达19。
你可能会问,如何选择这些点作为”高速公路“?它们可以是预先确定的或随机选择的。这些节点的随机选择是Small World和NHSW中数据构建的重要步骤,我将在后面介绍。

图 (4): 跳表再升级,指向后面三个节点的指针
由跳表的思路延伸到Small World,我们来看看是怎么做的。
由跳表的思路延伸到Small World
小世界(small world)网络是一种特殊的网络,在这种网络中,你可以快速地联系到网络中的其他人或点。这有点像“凯文·培根的六度”(Six Degrees of Kevin Bacon)游戏,在这个游戏中,你可以通过一系列其他演员,在不到六个步骤的时间里,将任何演员与凯文·培根联系起来。
想象一下,你有一群朋友排成一个圆圈,如图5所示。每个朋友都与坐在他们旁边的人直接相连。我们称它为“原始圆”。
现在,这就是奇迹发生的地方。你可以随机选择将其中一些连接改变给圆圈中的其他人,就像图5中的红色连接线一样。这就像这些连接的“抢椅子”游戏。有人跳到另一把椅子上的几率用概率p表示。如果p很小,移动的人就不多,网络看起来就很像原来的圆圈。但如果p很大,很多人就会跳来跳去,网络就会变得有点混乱。当您选择正确的p值(不太小也不太大)时,红色连接是最优的。网络变成了一个小世界网络。你可以很快地从一个朋友转到另一个朋友(这就是“小世界”的特点)。

图 (5): small-world网络
现在让我们学习从小世界网络到可导航小世界的过渡。
从小世界到HNSW
现在我们要扩展到高维空间。图中的每个节点都是一个高维向量。在高维空间中,搜索速度会变慢。这是不可避免的“维度的诅咒”。HNSW是一种高级数据结构,用于优化高维空间中的相似性搜索。
让我们看看HNSW如何构建图的层次结构。HNSW从图(6)中的第0层这样的基础图开始。它通常使用随机初始化数据点来构建。

图 (6): HNSW
HNSW在层次结构中的基础层之上构造附加层。每个层将有更少的顶点和边的数量。可以把高层中的顶点看作是跳跃列表中访问“高速公路”的点。你也可以将这些顶点视为游戏“Six Degrees of Kevin Bacon”中的演员Kevin Bacon,其他顶点可以在不到6步的时间内连接到他。
一旦构建了上面的层次结构,数据点就被编入索引,并准备进行查询搜索。假设查询点是桃色数据点。为了找到一个近似最近的邻居,HNSW从入门级(第2层)开始,并通过层次结构向下遍历以找到最近的顶点。在遍历的每一步,算法检查从查询点到当前节点邻居的距离,然后选择距离最小的相邻节点作为下一个基本节点。查询点到最近邻居之间的距离是常用的度量,如欧几里得距离或余弦相似度。当满足某个停止条件(例如距离计算次数)时,搜索终止。
现在让我们看看HNSW是如何构建这些层的。
HNSW如何构建数据结构?
HNSW首先初始化一个空图作为数据结构的基础。该图表示一个接一个插入数据点的空间。HNSW将数据点组织成多层。每一层表示数据结构中不同级别的粒度。层数是预定义的,通常取决于数据的特征。
每个数据点随机分配到一个层。最高的一层用于最粗略的表示,随着层的向下移动,表示变得更精细。这个任务是用一个特定的概率分布来完成的,这个概率分布叫做指数衰减概率分布。这种分布使得数据点到达更高层的可能性大大降低。如果你还记得跳跃列表中随机选择的数据点作为“高速公路”,这里的一些数据点是随机选择到最高层的。在后面的代码示例中,我们将看到每层中的数据点数量,并且数据点的数量在更高层中呈指数级减少。
为了在每一层内有效地构建连接,HNSW使用贪婪搜索算法。它从顶层开始,试图将每个数据点连接到同一层内最近的邻居。一旦建立了一层中的连接,HNSW将使用连接点作为搜索的起点继续向下扩展到下一层。构建过程一直持续到处理完所有层,并且完全构建了数据结构。
让我们简单总结一下HNSW中数据结构的构造。让我也参考Malkov和Yashunin[3]中的符号,并在附录中解释HNSW算法。您可能会发现它们有助于更明确地理解HNSW的算法。HNSW声明一个空结构并逐个插入数据元素。它保持每个数据点每层最多有M个连接的属性,并且每个数据点的连接总数不超过最大值(Mmax)。在每一层中,HNSW找到与新数据点最近的K个邻居。然后,它根据距离更新候选数据点集和找到的最近邻居列表(W)。如果W中的数据点数量超过了动态候选列表(ef)的大小,则该函数从W中删除最远的数据点。
接下来,我将向您展示代码示例。该笔记本可通过此链接获得。
代码示例
接下来,让我们使用库FAISS执行HNSW搜索。我将使用NLP中包含新闻文章的流行数据集。然后,我使用“SentenceTransformer”执行Embeddings。然后,我将向您展示如何使用HNSW通过查询搜索类似的文章。
Data
1 | import pandas as pd |
输出形状为(120000,3),列为[‘ Class Index ‘, ‘ Title ‘, ‘ Description ‘]。我们对“描述”栏感兴趣。以下是排名前五的记录。
数据嵌入
出于说明的目的,我只使用10,000条记录进行Embeddings。
1 | sentences = train['Description'][0:10000] |
您需要pip安装“sentence_transformers”库。
1 | !pip install sentence_transformers |
然后让我们使用预训练模型“bert-base-nli-mean-tokens”来声明模型。在本页上有许多预先训练好的模型。
1 | model = SentenceTransformer('bert-base-nli-mean-tokens') |
然后我们将“句子”编码为“sentence_embeddings”。
1 | sentence_embeddings = model.encode(sentences) |
输出是10,000个列表。每个列表或向量的维数为768。下面是前5个Embeddings的输出。
array([[-0.26105028, 0.8585296 , 0.03941074, …, 1.0689917 , 1.1770816 , -0.74388623], [-0.2222097 , -0.03594436, 0.5209106 , …, 0.15727971, -0.3867779 , 0.49948674], [-0.3001758 , -0.41582862, 0.86036515, …, -0.6246218 , 0.52692914, -0.36817163], [ 0.3295024 , 0.22334357, 0.30229023, …, -0.41823167, 0.01728885, -0.05920589], [-0.22277102, 0.7840586 , 0.2004052 , …, -0.9121561 , 0.2918987 , -0.12284964]], dtype=float32)
这有助于保存Embeddings以备将来使用。
1 | with open(path + '/AG_news.npy', 'wb') as file: |
在上面的代码中,我使用了“npy”文件扩展名,这是NumPy数组文件的常规扩展名。下面是加载数据的代码:
1 | with open (path + '/AG_news.npy', 'rb') as f: |
有了这些Embeddings,我们就可以在HNSW数据结构中组织它们了。
使用FAISS构建NHSW数据结构索引
您需要像下面这样pip安装FAISS库:
1 | !pip install faiss-cpu --no-cache |
我将使用HNSWFlat(dim, m)类来构建HNSW。它需要预先确定的参数dim表示向量的维数,m表示数据元素与其他元素连接的边数。
1 | import faiss |
如前所述,HNSW指数的创建分为两个不同的阶段。在初始阶段,该算法采用概率分布来预测引入新数据节点的最上层。在接下来的阶段,收集每个数据点的最近邻居,然后用一个表示为m的值进行修剪(在我们的例子中是m=16)。整个过程是迭代的,从插入层开始,一直到底层。
HNSW中有两个重要参数“efConstruction”和“efSearch”。这两个参数控制着索引结构构建的效率和有效性。它们帮助您在HNSW索引结构中的索引构建和最近邻搜索操作的速度和质量之间取得平衡。
1 | index.hnsw.efConstruction = 40 |
我们已经声明了上面的数据结构。现在我们准备将数据“sentence_embeddings”一个接一个地插入到数据结构中:
1 | index.add(sentence_embeddings) |
一旦完成,我们可以检查HNSW数据结构中有多少数据元素:
1 | index.ntotal |
输出为10000。它是“sentence_embeddings”中的数据点数。接下来,HNSW建造了多少层?让我们来检查最大级别:
1 | # the HNSW index starts with no levels |
最高级别为2.0。这意味着有第0层,第1层和第2层。接下来,您可能想知道每层中数据元素的数量。让我们来看看:
1 | levels = faiss.vector_to_array(index.hnsw.levels) |
输出为array([0,9713,280,7])。“0”没有意义,你可以忽略它。它说第0层有9713个数据元素,第1层有280个元素,第2层只有7个元素。注意,9713 + 280 + 7 = 10000。您是否发现,较高层的数据元素数量比前几层呈指数级减少?这是因为数据元素的层分配采用指数衰减概率分布。
FAISS为HNSW搜索示例
假设我们的搜索查询是“经济繁荣与股市(economic booming and stock market)”。我们希望找到与我们的搜索查询相关的文章。我们将首先嵌入搜索查询:
1 | qry1 = model.encode(["economic booming and stock market"]) |
使用代码index.search(),搜索过程非常简单。这里k是最近邻居的个数。我们将其设置为5以返回5个邻居。index.search()函数返回两个值” d “和” I “。
1 | %%time |
索引列表的输出是[[1467 4838 4464 7461 8299]]。我们将使用这些索引打印出搜索结果。
注意,我使用“%%time”来度量执行时间。它输出
*CPU时间:user: 5.57 ms, sys: 5µs, total: 5.58 ms
这意味着搜索只需要几毫秒。这确实是令人难以置信的快!
距离输出列表为:[[158.19066 163.69077 164.47517 164.64172 164.64172]]
1 | for i in I[0]: |
输出:
‘Rising oil prices are expected to hit China’s growth rate this year.’
‘Developing countries are starting to flex their financial muscles and invest overseas.
‘The Tehran Stock Exchange has performed magnificently, but the market’s list of risks is outsized.’
‘Federal Express raised its earnings forecast, citing strong demand for its international express, ground and less-than-truckload services.’
‘Federal Express raised its earnings forecast, citing strong demand for its international express, ground and less-than-truckload services.’ (Our data have duplications)
这些文章都是关于经济和股票市场的新闻。搜索速度以毫秒计非常快。这不仅仅是结果在哪里的问题,而是如何快速得到结果的问题,不是吗?
您可以通过此链接下载笔记本进行上述搜索。
总结
我希望这篇文章能帮助你理解近似近邻(ANN),以及它是如何提供高效搜索的。这篇文章解释了不同的人工神经网络算法,包括基于图的HNSW,基于哈希的LSH或产品量化,以及基于树的KD-Trees。这篇文章解释了HNSW如何构建其数据结构并逐个插入数据元素。本文演示了如何使用FAISS库构建用于查询搜索的HNSW。在下一篇文章“搜索像光速- (2)LSH,”中,我将讨论基于哈希的算法。
附录
在Malkov和Yashunin[3]的论文中,算法1到5伪代码中提供了HNSW方法。伪代码给出了算法的具体定义。我将这些描述添加到伪代码中,因为一些读者可能会发现它们有助于理解HNSW。算法1、算法2和算法3或算法4中的一个用于完成数据结构。一旦数据结构完成,以后的任何查询搜索都只使用算法5。
让我们从算法1开始。
1 | Algorithm 1: INSERT() |
它在多层图中插入一个新元素q,保持每个元素每层最多有M个连接,并且每个元素的连接总数不超过Mmax的属性。该算法还保证连接元素之间的距离不大于某一最大距离,并且每层的连接数是均衡的。步骤如下:
set neighborhood (e) at layer lc to eNewConn:将层lc的e的连接集更新为新的set eNewConn。ep <- W:设置hnsw的进入点为q。if 1 > L:将hnsw的起始点设为q,因为新元素q现在是图的一部分。return hnsw:返回更新后的多层图hnsw。让我们看看算法2。
它在HNSW数据结构上执行K近邻搜索,以查找特定层lc中与查询元素q最近的K个元素。然后,它根据查询元素q与候选元素C和e之间的距离更新候选元素C的集合和找到的最近邻居列表W。最后,如果W中的元素数量超过了动态候选列表ef的大小,则该函数删除从W到q最远的元素。
1 | Algorithm 2: SEARCH-LAYER() |
以下是上述代码的步骤:
算法3.
这是一个简单的最近邻选择算法,它接受一个基本元素q、一组候选元素C和一些邻居M作为输入。它返回候选元素C集合中离q最近的M个元素。
1 | Algorithm 3: SELECT-NEIGHBORS-SIMPLE() |
步骤如下:
让我们看看算法4。
这是一个更复杂的最近邻选择算法,它接受一个基本元素q、一组候选元素C、若干个邻居M、一个层数lc和两个标志extendCandidates和keepPrunedConnections作为输入。它返回由启发式选择的M个元素。
1 | Algorithm 4: SELECT-NEIGHBORS-HEURISTIC() |
步骤如下:
最后,让我们看看算法5。
这个搜索算法与算法1基本相同。
1 | Algorithm 5: K-NN-SEARCH() |
步骤如下:
引用
Update: 2024-01-26

我们的TorchV Bot产品目前已经开始试用了,详情可以点击:https://www.luxiangdong.com/2024/01/25/lanuch-1
目前只接受企业用户试用,需要您填写一些信息,必要信息如下:
邮箱: 用来接收地址和账号
如何称呼您:
所服务的公司:
您的职位:
当然,如果您可以告诉我们您的使用场景,我们将更加感激!
对了,可以发送到yuanwai@mengjia.net
另外,也可以直接加我微信(lxdhdgss)联系我。
上周又新出来一个新的国产大语言模型——ChatGLM3-6B,据说这次他们还另外上线了3B和1.5B两个模型,但是没有选择开源。这类小体量的模型如果能力OK的话,应用前景是非常广的,可以作为桌面应用,甚至在手机上就可以部署。
好,不说这么多,今天我们先来看看ChatGLM3-6B的能力相比之前的ChatGLM2-6B是否有较大提升。
周末的时候我下载了ChatGLM3-6B的模型文件,这次的文件大小和ChatGLM2-6B几乎是差不多的。甚至我开始都觉得可能是同一份model文件,但是使用ll命令看详细字节数,每个文件的大小是不同的,所以我还是重新下载了一份。

说实话,对于ChatGLM2-6B我还是比较失望的,从年初的惊艳,到后面的泯然众人,ChatGLM2-6B在我们RAG应用中已经完全不适用了,连基本的归纳都做不好,我们本来是把离线大模型的希望都寄托给它的。后续在一些应用中,我们使用在线大模型是智谱(ChatGLM所属公司)的Lite版和Pro版,这两个能力上还行。
但是离线需求肯定是一个刚需,特别是对于企业客户,他们不希望自己的机密数据文件被上传到在线大模型,所以后面Baichuan2-13B出来之后,离线的大模型,我们就开始转向了百川。但是百川的13B也有个问题,就是对GPU要求很高,非负载状态下需要18GB的显存,也就是需要至少4090的显卡(24GB),而目前市面上数量较多的3090显卡(16GB)则无法胜任。所以对于此次ChatGLM3-6B的公布,我很激动,希望可以为后面的RAG应用做一个相对性价比较高的配置方案。
首先我必须说,我不去做科学的对比测试,如果你需要看官方评测,我也可以贴一个ChatGLM官方给出的2和3的比较:
| Model | GSM8K | MATH | BBH | MMLU | C-Eval | CMMLU | MBPP | AGIEval |
|---|---|---|---|---|---|---|---|---|
| ChatGLM2-6B-Base | 32.4 | 6.5 | 33.7 | 47.9 | 51.7 | 50.0 | - | - |
| Best Baseline | 52.1 | 13.1 | 45.0 | 60.1 | 63.5 | 62.2 | 47.5 | 45.8 |
| ChatGLM3-6B-Base | 72.3 | 25.7 | 66.1 | 61.4 | 69.0 | 67.5 | 52.4 | 53.7 |
我想要更加”老土“一些、更加直接一些的方式,我的评测方法是这样的:
ChatGLM2-6B、ChatGLM3-6B、Baichuan2-13B和ChatGPT-3.5。是的,Baichuan2-13B和ChatGPT3.5在参数体量上和6B完全不同,有点耍流氓,但是不妨作为一组对比,从更广度的对比上来看各个模型的能力;所以你看到了,这不是一次科学的评测。我最关心的还是大模型对于RAG应用中G(生成)的支撑效果。
我可以在说说我的环境:Intel i7 / 64GB / RTX 4090 / 2TB SSD,操作系统是Ubuntu Server 22.04,Python3.11,Conda环境。
至于测试过程,我就不一一录屏了,直接上评测结果,分为7项测试,最后再加一个总结。
实体和关系提取当然也可以使用NER,但是有大模型,为什么不用呢,而且它后面还可以做格式,所以在RAG应用中,我们喜欢用大模型来提取实体。而且,这种提取方式对于短句子特别有效——对的,就是对于用户的提问内容的实体提取。
Prompt:
请帮我分析一下这段话”我需要看2021年3月至7月(含)的浙江分公司的自研产品的销售数据报表“,如果包含时间,请帮我列出”时间:xxxx年xx月“这样的格式,如果包含机构,请帮我列出”机构:xxxx“,如果包含其他实体,请帮我列出”实体:xxxx/xxxx“
这个prompt中,我们如果可以将时间、机构和其他实体先提取出来,就可以使用元数据过滤,在这么多月份的文件,这么多分公司(机构)的文件中,直接过滤掉大量不相干的文件chunk,极大提高向量相似度计算的速度。对于RAG应用来说,这是很关键的一种优化能力。

/是个缺陷;/也给到了,但是它没有7月(含),也许是对中文理解的偏差吧。这是一道我用了半年多的题目,很多大模型被我考倒了。
Prompt:
小明老婆昨天生了一对龙凤胎,他们还有一个4岁的女儿,那么现在小明一共有几个孩子?分别是几个儿子,几个女儿?
这里需要大模型对中文有一定理解能力,知道龙凤胎是一男一女,然后要去计算4岁的女儿,要做加法。如果大模型的逻辑计算能力出色,那我们可以将很多有文字有数字的内容塞给它,直接计算。比如一篇用记事本记录的今日花销流水账,直接就可以计算出来。是大模型是否”聪明“的一大体现。

惊喜的事情来了!
这道题,居然只有被认为最弱的ChatGLM2-6B答对了!!!我之前是试过多次ChatGPT-3.5的,好像基本都是正确的,但是这次居然打错了,看来最近OpenAI算力吃紧,有意降低ChatGPT的能力是有一些依据的。
翻译算是一项常规技能了,特别是英文翻译成中文。
Prompt:
你是精通计算机科学的专家,并且是一位专业翻译人员,请帮我翻译以下英文内容:“With Large Language Models(LLM) such as Chat GPT, machines can interact with humans in ways that weren’t possible before. There’s a growing interest in fine-tuning these pre-trained LLM models to preserve data privacy and cater to domain-specific tasks, thanks to the democratization of open-source Large Language Models. We can find numerous posts discussing model fine-tuning and application of in-context learning (I’ve even written three articles myself.., each delving into a distinct technique). But it’s essential to grasp the fundamentals. When should you opt for fine-tuning, and when is in-context learning the better choice? How do you kickstart the process, and how do these methods fit into the bigger picture? What techniques are readily available for you to implement? In this post, we’re diving into the fine-tuning process for pretrained models, explore different fine-tuning methods, and provide a comparison of the latest state-of-the-art techniques.”
常规技能,意义就不评价了,对于中文支持的模型,这是必备项。

归纳应该算是RAG应用中非常重要的一项能力了,我们通过向量计算召回内容,然后让大模型进行归纳和润色,然后再输出最终结果。所以归纳的能力也不需要过多介绍了。
Prompt:
请帮我将分析一下文字的含义,然后使用不超过30个字进行归纳:我们可以看到有很多关于如何构建RAG应用程序的示例代码和开源框架(如LlamaIndex和Langchain),目前它们产出一些技术和代码案例,使用的托底LLM基本上都是OpenAI的ChatGPT。我们碰的一些已经在做RAG应用的企业,也是无一例外的在使用OpenAI。就像上面说的,这本身是正确的逻辑,因为先用目前的上限做测试,至少我们”未来可期“——等国产大模型慢慢追上来。但不得不说,ChatGPT虽然还不完美,但已经很强大了。看了这些案例之后,我们也能发现完全基于ChatGPT好像有点背离我们构建RAG的初衷,也存在一些风险: 违背设计初衷:RAG的设计初衷是减轻LLM的各类问题,比如前面说到的减少幻觉、减少费用,增加实时性。但重度依赖ChatGPT反而把RAG做成了一个大模型的客户端,有点违背帮LLM分担压力的初衷; 国内使用风险:在国内使用ChatGPT属于是两头堵的状况,OpenAI对中国大陆用户是明确禁用的,国家对于非可控信源也是有严格的使用说明的。如果你只是自己搭建在家里或者公司内容研究用,那可能风险相对较小。但是如果要基于LLM和RAG技术对外提供经营服务,那基本上你还是需要退回到使用国内经过网信办审核的大模型。 而像我们这样使用国产大模型,或使用自己本地部署的开源大模型,确实在整体效果上和OpenAI是存在一些差距的,有时候事倍功半,想想还是挺气人的。我们需要为大模型本身的能力差距做很多”胶水组件“,其实就是很多中间件,比如表格处理,使用OpenAI的话我们可以直接使用开源的PandasAI(我之前也介绍过这个库),它会产生一些代码执行语句,给到ChatGPT,就能生产很好的结果。但是在国产大模型里面,没有这么强的程序执行能力,我们就需要做一些表格存储、处理和计算的额外功能,像这样的例子还有很多。 但是这样做的好处是我们对RAG整个体系中各个环节都越来越清楚,可以快速定位到问题,知道怎么做一个胶水组件去解决问题。假以时日,国产大模型追上了GPT-3.5的水平了,那精通RAG内部各个细节的你,做出来的应用应该也会比别人的更优秀。
RAG应用中最重要的技能之一,要保证语义不发生偏转,不能南辕北辙。

这也是一个传统艺能了,这可以帮助我们分析文章中哪些内容是比较重要的,和传统的TF-IDF是反着来的。
Prompt:
请帮我找出在后面将要给出的内容中,一共提到了多少个ChatGPT。以下是需要查找的内容:我们可以看到有很多关于如何构建RAG应用程序的示例代码和开源框架(如LlamaIndex和Langchain),目前它们产出一些技术和代码案例,使用的托底LLM基本上都是OpenAI的ChatGPT。我们碰的一些已经在做RAG应用的企业,也是无一例外的在使用OpenAI。就像上面说的,这本身是正确的逻辑,因为先用目前的上限做测试,至少我们”未来可期“——等国产大模型慢慢追上来。但不得不说,ChatGPT虽然还不完美,但已经很强大了。看了这些案例之后,我们也能发现完全基于ChatGPT好像有点背离我们构建RAG的初衷,也存在一些风险: 违背设计初衷:RAG的设计初衷是减轻LLM的各类问题,比如前面说到的减少幻觉、减少费用,增加实时性。但重度依赖ChatGPT反而把RAG做成了一个大模型的客户端,有点违背帮LLM分担压力的初衷; 国内使用风险:在国内使用ChatGPT属于是两头堵的状况,OpenAI对中国大陆用户是明确禁用的,国家对于非可控信源也是有严格的使用说明的。如果你只是自己搭建在家里或者公司内容研究用,那可能风险相对较小。但是如果要基于LLM和RAG技术对外提供经营服务,那基本上你还是需要退回到使用国内经过网信办审核的大模型。 而像我们这样使用国产大模型,或使用自己本地部署的开源大模型,确实在整体效果上和OpenAI是存在一些差距的,有时候事倍功半,想想还是挺气人的。我们需要为大模型本身的能力差距做很多”胶水组件“,其实就是很多中间件,比如表格处理,使用OpenAI的话我们可以直接使用开源的PandasAI(我之前也介绍过这个库),它会产生一些代码执行语句,给到ChatGPT,就能生产很好的结果。但是在国产大模型里面,没有这么强的程序执行能力,我们就需要做一些表格存储、处理和计算的额外功能,像这样的例子还有很多。 但是这样做的好处是我们对RAG整个体系中各个环节都越来越清楚,可以快速定位到问题,知道怎么做一个胶水组件去解决问题。假以时日,国产大模型追上了GPT-3.5的水平了,那精通RAG内部各个细节的你,做出来的应用应该也会比别人的更优秀。
在RAG应用中,我们有时候需要统计各chunk或page中和问题相关的词频,以进行重排(rerank),所以统计也是有实际意义的。
上面这段话(从”以下是需要查找的内容:“开始),其实是有 6处 提到ChatGPT的,我们来看看各个模型的评测结果。

这个评测中,居然只有Baichuan2-13B是正确的,可怕。
生成SQL可以让我们通过语义理解来完成对本地数据库的查询,获取我们需要的数据,而不是通过程序员手动写SQL,或者只能按已经固化的SQL来进行某些变量(比如时间、实体等)的选择。
Prompt:
已知有两个mysql表,一个是代表销售人员的表:Persons,另一个是代表订单的表:Orders。具体的表定义如下: 表Persons: CREATE TABLE Persons (PersonID int, LastName varchar(255), FirstName varchar(255), Address varchar(255), City varchar(255), PRIMARY KEY (PersonID) ); 表Orders: CREATE TABLE Orders (O_Id int NOT NULL, OrderNo int NOT NULL, P_Id int, PRIMARY KEY (O_Id), FOREIGN KEY (P_Id) REFERENCES Persons(PersonID) ) 请帮我生成一个SQL语句,查询销售人员”Zhang san“的所有订单数据。
在RAG应用中,根据语义生成SQL的能力是非常强大的,我们真的可以摆脱程序员预设的SQL来做事情。

P_Id = 123456是什么鬼,而且OrderNo LIKE '%Zhang san%'也是不知所云了;Persons.LastName = 'Zhang san',还是败在了对中文的理解上啊。这项任务我最初觉得是有一些挑战的。
Prompt:
已知浙江分公司2021年3月至7月(含)的自研产品销售数据依次为118万元、671万元、446万元、902万元和1011万元,请帮我用python的pyplot制作一个可查看趋势的柱状图表,注意,月份等标签请使用中文,并且,计量单位使用”万元“。
在大模型的企业应用中,除了检索之外,我们很多时候也需要快速的得到一些数据分析指标,特别是一些趋势,用图表会更加直观,所以您直接说话,让应用(业务系统+数据库+大模型)立即给你直观的图表就变得很重要。一般我们会首先将语义转化为python代码,然后用matplotlib.pyplot等组件进行显示。当然,如果更复杂、更美观的分析图表,可以使用其他组件。

另外我贴一下各个模型给出的代码:
ChatGLM2-6B
1 | import matplotlib.pyplot as plt |
ChatGLM3-6B
1 | import pandas as pd |
Baichuan2-13B
1 | import matplotlib.pyplot as plt |
ChatGPT-3.5
1 | import matplotlib.pyplot as plt |
我们先看总结一下上面的评分:
| 分项 | ChatGLM2-6B | ChatGLM3-6B | Baichuan2-13B | ChatGPT-3.5 |
|---|---|---|---|---|
| 实体提取 | 0 | 0.5 | 1 | 1 |
| 逻辑计算 | 1 | 0 | 0 | 0 |
| 英文翻译 | 0.5 | 0.8 | 1 | 1 |
| 归纳润色 | 0 | 1 | 0.6 | 1 |
| 统计次数 | 0 | 0 | 1 | 0 |
| 生成SQL | 0 | 1 | 1 | 0 |
| 生成图表 | 0 | 0.8 | 1 | 1 |
| 总计 | 1.5 | 4.1 | 5.6 | 4 |
这个结果,惊不惊喜意不意外!Baichuan2-13B和ChatGLM2-6B居然都超过了ChatGPT-3.5。
当然,这只是我针对目前在RAG应用上所做的一些具体能力的测试,如果真的要全面测试,我相信ChatGPT-3.5依然会遥遥领先。而且ChatGPT-3.5对于中文的一些细节方面理解偏差,是主要的失分原因,不然它应该是得到最高的6分的。
另外声明一点,我不是为百川打广告,因为本人不认识任何百川的人员,但目前确实在试用百川的大模型,效果不错。
ChatGLM3-6B这次的能力让我感到惊艳,毕竟和百川13B在参数体量上相差两倍多,但是能力上比较接近,在RAG中非常重要的归纳和SQL生成两项中都非常出色。而且,最关键的事ChatGLM3-6B可以在RTX 3090上部署,这是非常涨粉的优势!
以上三个本地模型都没有经过微调,我相信大家可以在ChatGLM3-6B和Baichuan2-13B中选定其中一款,然后进行微调,去匹配自己的RAG应用。
最后,什么是行业大模型?难道装进去行业知识多就是这个行业的大模型吗?RAG就是来解决行业知识问题的。所以,在行业里面应用,我更倾向于专项大模型,比如金融行业,就需要更强的逻辑计算、SQL和图表生成能力;而文博行业,更倾向的润色、归纳和统计次数等能力。所以,我认为可以有专项大模型,只有行业应用,没有行业大模型。
本文主要内容:
- 近期关于RAG的一些行业交流
- RAG过度依赖ChatGPT好吗?
- RAG存在的一些问题
- 5个改善RAG性能的方法
近两周我们也在做一些RAG方面接地气的探索,所谓接地气,就是不仅仅研究产品技术,还包括和一些行业人士交流RAG在他们的业务场景中的需求。包括与金融、法律、高校、生产型工厂、贸易公司、电商、餐饮、知识产权和文旅等行业的人士交流,后续还安排了和政务、医疗、通信、能源、教育(非义务教育)和交通等行业的人士交流。说实在的,现在大家需要的基本都是Chat——快速根据已有文档,建立知识对话系统。但是随着交流的深入,我们和行业专家也发现了一些有意思的应用,绕开了问答系统,而且,这些系统往往更加适配行业客户的需要。

增加行业交流,是做产品PMF中的一个基础工作,使我们要知可以道技术能带来什么产品,而产品能为使用者带去什么价值。后续我们会继续交流,但是会把重心锁定在其中的两三个行业,做好真正有价值的产品。
通过近期的交流,也发现了一些问题,和大家分享一下。
假设我们有一个可以无限输入tokens长度的LLM,输入字符串的长度对生成式LLM的精度没有影响。除此之外,它的行为与所有其他流行的大语言模型完全相同。我们称这个模型为完美LLM。我们认为它是完美的,不是因为它有出色的性能,而是因为它有理想的无限输入tokens长度,这在今天是不可能的。无限制的输入tokens长度确实是一个吸引人的特性。事实上,像ClaudeAI就允许输入的tokens长度为100万!但是,100万tokens够了吗?因为它只相当于4-5MB,仍然比实际业务中的大量文档要小得多。
现在的问题是:当你拥有如此完美的LLM时,你还会考虑RAG架构吗?具有无限输入长度的完美LLM减少了构建复杂RAG的必要性。然而,可能是的,您仍然需要考虑RAG体系结构。RAG架构不仅克服了LLM输入长度的限制,而且降低了LLM调用的成本,提高了处理速度。生成式大语言模型必须按顺序处理内容:输入越长,速度越慢。
关于调用成本我想多说一句,传统的应用程序,随着并发量增加,其实边际成本是在下降的。当然它也有一个临界点,比如1千并发和1万并发需要的硬件资源会急剧上升。但是总体上来说,边际成本还是会随着并发量增加而下降。但是,这种情况目前在GPU算力主导的LLM身上没有发生,现在基本上还是存在着并发越高成本也越高的情况。也许后面会有所改善,但是目前我还没有看到这个迹象,如果你有看到不同的信息,也可以分享一下。
但这里我想提一个相反的意见!
我们可以看到有很多关于如何构建RAG应用程序的示例代码和开源框架(如LlamaIndex和Langchain),目前它们产出一些技术和代码案例,使用的托底LLM基本上都是OpenAI的ChatGPT。我们碰的一些已经在做RAG应用的企业,也是无一例外的在试用OpenAI。就像上面说的,这本身是正确的逻辑,因为先用目前的上限做测试,至少我们”未来可期“——等国产大模型慢慢追上来。但不得不说,ChatGPT虽然还不完美,但已经很强大了。看了这些案例之后,我们也能发现完全基于ChatGPT好像有点背离我们构建RAG的初衷,也存在一些风险:
而像我们这样使用国产大模型,或使用自己本地部署的开源大模型,确实在整体效果上和OpenAI是存在一些差距的,有时候事倍功半,想想还是挺气人的。我们需要为大模型本身的能力差距做很多”胶水组件“,其实就是很多中间件,比如表格处理,使用OpenAI的话我们可以直接使用开源的PandasAI(我之前也介绍过这个库),它会产生一些代码执行语句,给到ChatGPT,就能生产很好的结果。但是在国产大模型里面,没有这么强的程序执行能力,我们就需要做一些表格存储、处理和计算的额外功能,像这样的例子还有很多。
但是这样做的好处是我们对RAG整个体系中各个环节都越来越清楚,可以快速定位到问题,知道怎么做一个胶水组件去解决问题。假以时日,国产大模型追上了GPT-3.5的水平了,那精通RAG内部各个细节的你,做出来的应用应该也会比别人的更优秀。
我们直接将Prompt输入到LLM中,LLM会直接消化输入文本中的所有信息,最终结果的准确性仅取决于生成式LLM的性能。

对于普通的RAG应用程序,还有两个影响最终性能的组件:语义搜索方法和RAG实现。
RAG架构使用Embedding模型来生成真实知识和查询的向量。然后使用向量相似度匹配算法来检索最相关的内容。Embedding模型从文本中提取表征向量的能力是非常关键的。除了Embedding模型之外,在RAG开发中还有很多实现细节,这些细节也会严重影响最终的结果。也就是说,RAG输出的准确率等于LLM的准确率 x 语义搜索的准确率 x RAG信息保存率。

如果这三个因素都小于100%,RAG应用程序的预期精度会低于直接使用LLM模型的精度(我在之前的文章中已经提到过)。如果RAG的设计不合理,其性能会显著下降。当我们开始考虑我们的RAG应用程序设计时,这是要记住的第一个概念。否则,意想不到的结果会让我们非常失望。
很容易理解,LLM模型和语义搜索都不能达到100%的准确率。让我解释一下什么是RAG信息保存率。
我们提供给应用程序的文本语料库可以包含非常丰富的信息。让我们来看看语料库中的内容以及如何将信息输入LLM:

上图描述了文本语料库中的实体关系。实体分布在整个语料库中,引用关系也无处不在。在分块之后,实体在每个筒仓中受到约束,并且chunk之间的关系都被切断。在检索短语中,只有top-k的chunk会有机会被检索召回,然后发送给LLM处理输出。这意味着只有一部分实体和关系可以转发到LLM。如果需要广泛的关系知识来响应查询,LLM将会遇到麻烦。
除了实体关系之外,分块操作还会对输入中的其他各种类型的信息产生影响:
在大多数情况下,文本具有多层上下文信息。例如,《统计学习的要素》这本书有18章,每一章都专注于一个主题。每章有副标题和第二层副标题等。人们习惯于在语境中理解文章。分块策略使内容与上下文脱节。
文本的权重取决于它们在文档中的位置。文档开头和结尾的文字比中间的文字更重要。它们出现在一章的开头或结尾比出现在一章的中间更重要。
自然文本也经常使用显性和隐性语言连接来连接主题。例如,一个故事可能以“in the beginning”开头,然后以“then”、“therefore”、“after that”继续,直到以“finally”、“finally”等结尾。使用分块策略,这种连接不再是完整的。不仅谜题不见了,而且排序顺序也被打乱了。
这指的是描述单个主题的信息。使用分块,可能无法保证描述性信息集中在一起。想象一下你正在打电话,突然电话线被切断了。这取决于你的电话有多重要,当它发生时,影响范围从微不足道到非常令人沮丧。
如果我们把只使用分块和向量相似度搜索的抹布称为“香草抹布”,我们可以看到它们只能处理几种类型的查询,因为它们丢失了我们之前谈到的一些输入信息:
RAG应用程序在这类任务上的表现很差,因为只有少数chunk可以输入到LLM中,而且这些chunk是分散的。LLM会缺少很多辅助信息,比如元数据和世界知识。当然,元数据和世界知识都是可以去增加的,这也是我们现在的工作之一。
了解RAG能做什么和不能做什么,可以让我们为RAG寻找最适合的领域,避免强行进入错误的地方,比如千万别让RAG去写诗,它的算数能力也很差,我们现在是自己做了外挂程序(胶水组件)来完成这部分工作的。
讨论了RAG应用程序的局限性之后,让我们看看如何改进它的性能。
通常,在处理输入查询时,我们只是接受用户输入的任何内容。这并不理想,不仅因为存在prompt泄漏和prompt注入等安全风险,还因为性能也可能令人失望。
根据研究人员的说法,大语言模型对prompt中的拼写错误和措辞差异很敏感,为了确保大语言模型以其最佳性能运行,请考虑纠正所有错字并将输入改写为大语言模型更容易理解的形式。
在大多数情况下,用户发送简短的查询,比如“告诉我更多关于徐悲鸿的信息”。然后,将查询转换为Embedding向量,该向量捕获该特定查询的本质。使用直接查询进行语义搜索可能具有挑战性,因为:
为了避免这个问题,您可能希望首先使用LLM来增加用户查询。考虑下面的例子:
原始用户查询:跟我说说托尼·艾伯特。
在原始查询的基础上使用Bard重新表述的增强查询:
艾伯特的政治背景是什么?
托尼·阿博特最显著的成就是什么?
艾伯特的政治观点是什么?
托尼·阿博特的个人兴趣是什么?
托尼·阿博特卷入了哪些争议?
你能看到信息丰富程度的提高吗?增强查询提供了更多的特性,从而产生了更好的检索结果。此外,通过发送增强查询,LLM有机会告诉Embedding模型它需要什么,Embedding模型可以更好地为LLM提供高质量的块。这就是两种模式协同工作的方式。
块(chunk)大小是我们可以为RAG应用程序调优的为数不多的超级参数之一。为了获得更好的结果,建议使用较小的块大小。微软就有这样的分析:

在分割文本时,我们也可以选择不同的分割策略。最简单的方法是在单词的中间切断。我们也可以尝试不同的策略,比如在一个句子或段落的中间切断。为了得到更好的结果,我们可以重叠相邻的块。来自微软分析的分块策略比较:

Embedding模型的语义提取能力有限。它们在呈现多主题、多回合语料库时不如简单语料库有效。这就是为什么RAG更喜欢较短的块。那么块的大小是最好的呢?在微软的分析中,最小的块大小是512个tokens。一些企业级RAG应用程序中的块大小只有100个tokens。最小的块大小是否总是能获得更好的结果?
如前所述,分块策略会将文本语料库分解成小块,导致信息丢失。数据块越小,丢失的信息就越多。所以,有一个最优的块大小。过小的分块可能不太理想。然而,寻找最优块大小就像超参数调优一样。你必须用你的数据做实验。

微软的分析发现,大量重叠的分块可以提高准确性。为什么它有帮助,我们能否找到更好的方法来提高RAG性能?
重叠背后的原因是重叠可以帮助将相邻的块链接在一起,并为块提供更好的上下文信息。然而,即使是非常激进的25%重叠也只能将准确率提高1.5%,从42.4%提高到43.9%。这意味着这不是优化RAG性能的最有效方法。我们不能通过更多的重叠来进一步提高RAG的性能。记住,重叠分块甚至不能用于小块。
但是重叠有时候在处理代词的时候很有效,比如上一个chunk说到”徐悲鸿非常擅长画马“,然后下一句说”他画马的主要技法是xxxxx“,这时候如果缺少了重叠,就会让这个代词变成”无主之地“。
当然,目前还有一种非常流行的做法是引入知识图谱,包括NebulaGraph提出的Graph RAG。
在知识图谱的帮助下,RAG可以将这些关系存储在图数据库中。块之间的连接可以完全保留。如果关系推理对您的项目至关重要,那么这是一个非常可观的解决方案。
然而,带有知识图谱的RAG并不是没有挑战的。从非结构化文本中建立知识图谱是非常重要的。有很多关于从文本输入中提取实体-关系三元组的实验。当你需要将解决方案产品化时,情况就不同了。自动提取的实体和关系可能包含大量的噪声,忽略了太多的真实信息。你们必须非常仔细地检查产品的质量。即使在填充知识图谱之后,所支持的查询也与图数据库设计紧密耦合。
支持向量搜索的关系数据库不像带有Knowledge Graph的RAG那么花哨,它也是工具箱中非常重要的组件。像pgvector这样的数据库允许您将复杂的信息存储为列,同时保留语义搜索功能。它比知识图谱更容易与其他企业系统集成,也更灵活。
这些都是值得考虑的有效选择。唯一需要注意的是,许多支持矢量的图形数据库、搜索引擎和关系数据库并没有完全优化为向量数据库。它们在处理大规模向量索引时的速度可能不理想,特别是当它们必须经常更新索引时。可参考之前的一篇文章《》
有时,我们发现RAG不能很好地回答我们的问题。我们应该考虑以下问题,而不是盲目的捣腾一通:
让我们考虑下面的例子:
我们正在SharePoint网站上构建一个RAG应用程序。其中一个网页是关于所有的项目和他们的团队成员,包括所有人的个人资料。我们需要确保RAG准确地回答项目与团队成员的问题:然而,最初的结果非常令人失望。
初步调查显示,SharePoint网站没有以结构化的方式组织内容,从而使信息的从属关系易于理解。删除所有HTML标签后,网页内容如下所示:
1 | project A |
如果说人类很难区分谁是谁,那么RAG也很难区分。为了更好地组织信息,我们使用Python代码根据HTML属性将信息聚合在一起,将每个项目和团队成员的姓名分离到一个文本文件中,并将每个人的信息放入自己的文件中:
1 | file project_A.txt: |
生成的文本文件很小,这似乎与RAG分块实践不一致。原因是合并后的文件避免了分裂的问题,完全消除了噪音。有了新生成的文件,RAG可以毫无问题地回答诸如“谁在做项目x?”以及“亚当·斯密的爱好是什么?”
然而,当我们把问题转过来:“亚当·斯密在做哪个项目?”我们看到亚当·斯密被列在项目成员名单中。我们不太确定为什么Embedding模型不能捕捉到它。为了帮助LLM完成工作,我们可以使信息脱颖而出。我们在这个人的文件中添加了一行,明确地说明了项目的参与情况:
1 | file person_A.txt: |
这一行使RAG应用程序能够100%准确地回答上述问题。
RAG作为一项新兴技术,正在快速发展。我发现逐项研究它的组成部分对我有很大帮助。通过研究细节,我可以更深入地了解这项技术的利弊,并对一项新想法是否可行有一个基本预判。有一些非常流行的框架可以帮助我们更快地开发RAG应用程序。我发现其中的一些执行理念非常鼓舞人心。但是,我不建议仅仅因为它们很容易上手就开始学习或开发基于这些库的RAG,很多时候如果你真的要扎根进去,还是可以考虑一下”古法酿造“,会有更多惊喜的收获。
如果您已经阅读了本文,那么您肯定同意RAG是一个复杂的体系结构,所谓入门简单精通极难。很多国外的文章以及流行的框架掩盖了所有的细节,这让人们认为这些细节并不重要。当我们在项目中遇到问题时,会发现很难找到出路,因为有太多的实现细节。而且对于国内的程序员来说,这个难度还会极大提高,因为我们在正式环境中很难对接OpenAI的ChatGPT,除非你只是把应用部署在自己家里或者公司自用。抛开ChatGPT这个”超级“大模型,替换成可以本地部署的开源大模型或者国产大模型,如果你要达到一样的效果,那就需要在中间件和应用层做很多胶水组件。
如果你刚刚接触RAG不够,我还是建议你先从基本知识开始研究,了解内部细节,再用那些主流框架的时候,你才会收放自如。
0.Practical Considerations in RAG Application Design:https://pub.towardsai.net/practical-considerations-in-rag-application-design-b5d5f0b2d19b (本文很多内容来自该文章)
1.Prompt Robustness: How to Measure and How to Enhance:https://pub.towardsai.net/prompt-robustness-how-to-measure-and-how-to-enhance-644fb6493ec?source=post_page-----b5d5f0b2d19b--------------------------------
2.Azure Cognitive Search: Outperforming vector search with hybrid retrieval and ranking capabilities:https://techcommunity.microsoft.com/t5/azure-ai-services-blog/azure-cognitive-search-outperforming-vector-search-with-hybrid/ba-p/3929167?source=post_page-----b5d5f0b2d19b--------------------------------
3.What We Need to Know Before Adopting a Vector Database:https://medium.com/@kelvin.lu.au/what-we-need-to-know-before-adopting-a-vector-database-85e137570fbb?source=post_page-----b5d5f0b2d19b--------------------------------
4.Disadvantages of RAG:https://medium.com/@kelvin.lu.au/disadvantages-of-rag-5024692f2c53?source=post_page-----b5d5f0b2d19b--------------------------------
Update: 2024-01-26

我们的TorchV Bot产品目前已经开始试用了,详情可以点击:https://www.luxiangdong.com/2024/01/25/lanuch-1
目前只接受企业用户试用,需要您填写一些信息,必要信息如下:
邮箱: 用来接收地址和账号
如何称呼您:
所服务的公司:
您的职位:
当然,如果您可以告诉我们您的使用场景,我们将更加感激!
对了,可以发送到yuanwai@mengjia.net
另外,也可以直接加我微信(lxdhdgss)联系我。
本文导读:
- 实际业务中应该如何使用大模型的思考
- 记录一款大模型产品的0到1过程
在公司已经六年多了,六年多里收获很多成长,从纯研发leader开始,先后带过大数据产研团队、AI产研团队和运营团队,而且还作为独立销售和解决方案获得一些著名客户的订单,从一个coder变成了多面手。所以,在即将离别之际,不去讲其他纷争,只从产品角度去写两篇文章总结过去。上篇写今年之前三年的一些感想,主要记录力石小知的成长——《总结一下过去三年的产品心路》。这篇写的就是今年,在大语言模型(LLM)开始进入国内之后,我们如何思考它带来的影响,以及如何务实去落地一款产品。
本文有7000多字,有点长,Enjoy!
看过上篇的朋友应该知道,我们在2019年就已经开始了AI产品的研发,并且这几年有一定的市场品牌口碑和数据积累。也正因为有这样的“AI传统”,我们才有一定的人才储备(比如我们的小胡,B站ID:良睦路程序员,Github:yuanzhoulvpi2017),所以当今年大模型这波新的AI浪潮袭来的时候,我们算是入局比较早的:

而且相对其他同行来说,我们还有一个独特的优势就是数据积累。
小知3年多服务中产生的数据量极大,且格式就是“问答指令集”,非常适合SFT(有监督精度微调)。有朋友可能知道之前Dolly2大模型发布的时候,他们用了仅仅1.5万条自己生成的问答指令集,就基于gpt-neox训练出了一个很有竞争力的模型。所以就文旅行业来说,我们的微调工作在百万级挑选出来的数据帮助下就相对有效。加上我们有一个很有活力的团队,于是如何将LLM用在产品中就自然而然成为了我们团队的新任务之一。
这次大模型和以往的新技术的出现有所不同,大家一开始都是先从OpenAI的ChatGPT开始的,有点”开局即巅峰“的意思,以至于后面大家接触”车水马龙“会笑得人仰马翻。基于ChatGPT的普及,大模型的优点我就不赘述了,下面我们说说这位法学硕士(这是一个梗)的缺点。
Chat(问答系统)只是最基本的大模型应用,说白了就是OpenAI放出来的一个Demo。我们把大模型应用在企业业务中时,问题依然很多,主要包括:
上面说的还是OpenAI的问题,再说回到国内,我们的大模型的能力和OpenAI还是有很大差距的,包括一些常用的推理能力。
除了能力上的差距,国内AI大模型及相关产业的从业者还面临着更多的问题:
所以我们对于大模型在产品中的应用是相对较为谨慎的,目前主要的大模型应用思路包括以下几点:
好了,说了这么多思考,接下来我们开始来说说今年做的这款新产品的0到1.
我现在所处的行业是数字农文旅。
农业农村是个大市场,也是国家这几年重点在发展的赛道,各种项目资金可以说非常充沛。但是农村也有一个大问题,就是它是一个大行业,却不是大市场,更多的客户是非常分散的,且不是知识密集型行业,所以对大语言模型并不友好。加上这两年接触下来,涉及的项目还是以形象工程为主,真正的产品力无法深入体现,个人感觉也没有能力真正把产品力做出来。所以放弃数字乡村赛道。
接下来是旅游。很多人可能看到疫情后旅游市场的爆棚,但在行业内,我们可以看到旅游消费依然乏力,City Walk、郊游(公园搭帐篷)等形式火热兴起。对于景区、文旅集团等,目前的日子依然不太好过…。我一个朋友在10月9日截了一个上市文旅公司的二级市场走势图可以说明一些问题。但是最可怕的是,除了一些稀缺级别的景区和一些新型服务形式的景区之外,景区这种靠山吃山靠水吃水的服务形式,正在失去游客…
综合考虑之后,这次我瞄准的是文博行业,更具体的说就是博物馆(院)和遗址公园一类的客户。就像上面说到的City Walk的火热,文博场馆就是大家主要的目的地或参观游玩点之一。
可以这么说,这次产品赛道的选择,我没有去看政策影响和甲方的资金是否充沛这些传统的toG/toB玩法,而是主打toC。这次的甲方更多是站在合作伙伴的位置上,我们是帮助他们一起来赚钱的,所以更看重的是人去了哪里。这个观念也可以参考我之前的一篇文章《进击吧!硬地骇客————独立开发者时代来临》里面提到的市场环境变化。
因为我是良渚遗址项目(其中旅游这部分)的商务+售前,所以和良渚遗址的领导比较熟。有一次就请教了监测中心的孙主任,他对于文物活化的看法。这一问不得了,确实颠覆了我很多认知,因为之前我对于活化的思考更多实在VR这些方面上,当然基本上都需要大投入大建设。主任对VR/AR也基本上是认可的,但是他说技术不能解决核心问题,他觉得核心问题就几句话:”文物活化,就是要让观众看得懂、喜欢看,看了之后能传播!“(具体意思应该就是这样的,可能用词上更我写的更准确一些)。当时有种被电流击中的感觉,真正的内容如此简单直白。
有了这句话的影响,我就去找各种资料加深这种感觉,其中对我帮助最大的是窦文涛的《锵锵行天下·第三季》,里面有一期讲《富春山居图》的,非常精彩,确实讲出了很多我之前自己看展品根本看不出来的道道。

特写一:黄公望把自己也已经画进了《富春山居图》中,也是对他自己在富春江畔九年的写生生活留下一个缩影;
特写二:水的画法和山是有直接关系的,有直接冲刷山体的,有掠过山体的,也有打了回漩到山坳里泛起粼粼波光的,每种笔触是对山水的真实写照;
特写三:通过清晰度和识别度造成远近和悠远,营造江面被一层薄雾笼罩的视觉,通过这种技法,让观察者心中感觉到烟云,既为“烟云供养”的技法;
特写四:披麻皴技法,古代山石画中非常精髓的技法,利用中锋手法,高处做大结构、气势磅礴,低处(离观察者近)构思精巧。
这么一解读,是不是对这幅浙江博物馆的镇馆之宝(浙博只有前半卷,也就是《剩山图》,后半卷在台北故宫)有了更多了解。
通过这两件事,我对新产品有了一些初步构思,就是文化文物不是仅仅靠听就能听懂的,文化文物是需要视听同步才能做到“看得懂、喜欢看,看了之后有谈资”的。于是我觉得这里面是有一个空白市场,而且也是一个风险相对较小的产品——导游讲解机已经有十多年历史了,我们去升级迭代这个产品,去拿这一块市场。
我把调研分成两拨,第一拨调研是让我团队的一位我认为比较聪慧的同事(倩文)去的,她走了五个馆,写了大概万把字的调研报告,给了我第一手的信息。也是已通过这些资料,我坚定了做这个产品的决心,这里肯定是有市场空白的。
第二拨调研是我自己去的,而且还邀请了另外两个同事,一位是我们分管副总裁王总,另外一位是我们的产品总监欧阳。这一拨调研我有重点地将目标放在了我自己的原有客户身上。两个客户分别是宁波天一阁博物院和杭州的良渚古城(和博物院)。调研发现其实市面上已经有AR眼镜的产品,外形看上去其实还是挺酷的,但问题也很明显,主要是三点:
对于传统的导游机,它的问题还是无法用画面来展示文物背后的故事。
于是,我觉得也不需要再调研了,因为我已经可以给新产品做定义了。
我给新产品定的内部名称是”可交互AI讲解“。
下面是给这个产品做的几个定义:
基于这6点,我已经可以规划处一个产品了:
一个iPad作为主要载体,搭配耳机,播放的是关于当前文物的讲解视频。

后端的核心是使用AI进行自动化内容生产,但是我们不会真的去追求一步到位,而是先使用半自动化的方式。前期会让内容编辑人员参与内容建设,然后记录他们的生产过程,结合上线之后的用户喜好数据,逐步进行全自动内容生产流程。
后端的简易流程图如下:

Step1:内容运营人员挑选有趣的内容,一般是电子书、论文和网络内容,当然需要经过馆方审核。然后将内容制作成PDF、Markdown等格式,上传到系统;
Step2:系统的调度程序(TaskScheduler)开始运行,将文档进行内容提取;
Step3:图片的提取相对复杂,首先单独存储图片,如果有图示文字的,存入图片相关的描述文本;如果没有图示,那么将图片上下文存入图片描述文本;或者还有一种就是文本中有下标(比如[3]),图片在章节末尾的,那就存在下标的上下文到图片的描述文本。图片的描述文本进行embedding处理。描述文本和图片直接的实体关系通过RDS进行对应(多对多关系);
Step4:提存和存储文字,会先使用大模型进行润色,在Prompt里面设置较多角色、指令和带有行业knowhow的输出格式模板。润色之后的文字进行embedding处理。
Step5:中间略去一些处理细节,然后再以讲解文本为基础进行时间轴创建。讲解文本的chunk_size基本上是以句子为单元的,将讲解文本与图片描述文本进行语义匹配(相似度算法),自动组合视频时间轴,生成一段”伪视频“。为什么是”伪视频“,因为这时候其实是一个mp3播放器和一个带有转场特效的图片播放器的叠加。这个期间,运营人员可以对内容进行最后的调整;
Step6:合成最终视频的过程其实非常简单,其实就是录屏。

说实在的,这可能只描述了整个过程的50%流程,后面不断优化之后,整个流程不断增加新功能,整个生成过程也在不断完善。里面用到的技术也非常繁多,包括:
其实定义使用场景这个工作是在我们第二拨调研之前做好的,这次调研我们面向是业内专业人士和馆方领导,需要有PPT做演示,要让对方快速理解使用场景,才能获得市场成功。如果你看过我上一篇文章《总结一下过去三年的产品心路》的话,是不是”产品-市场对角线“的感觉又出来了。
你知道,仅仅靠语言描述是很难把一个产品讲清楚的,即使你会画一些简单的线框图,对于其他行业的人来说也不好理解。所以有必要用相对真实的画先描述一下使用场景。我们这个时代最大的好处就是有各种AI工具可以帮助我们完成自己不擅长的事情,比如我不是画家,也不是摄影师,但是我依然可以用Midjourney画出脑海中的使用场景,于是,就有了下面这两张图:

这个过程我肯定不太像说的太多,因为细节太过于繁琐,即使讲出来可能也索然无味,加上我也确实不能说太多,但有两点我觉得是可以分享的。
读过之前《三年产品心路》的朋友应该知道我之前带队做小知的时候,开始的半年是靠实力生撑过来的,人员抓肩见肘。但这次不一样,老板把AI产品线定义为公司最重要的核心竞争力,加上VP王总加入之后,确实为这个产品注入了相对丰富的产研资源。
产品由公司P序列最高的产品经理欧阳(P9)负责,研发方面也是P8、P9的同事领衔,比如我经常提到的著名开源作者小明,还有公司最好的前端六木。当然AI算法和大模型方面参与的还有最开始提到的”良睦路程序员“小胡。付萍带的内容运营团队也全数参加,前面提到第一次调研的倩文负责整个产品研发进度的把控。
整个产品团队最多有16人,常规也有9人,对我来说,这次打的就是富裕仗。所以欧阳经常感叹的一句话是,两个月前还只是一份PPT啊,现在就已经出来v0.8版本了。总结下来,组织力量是非常重要的,没有组织能力的保障,很多产品出不来:市场+产品+研发+AI+运营+项目管理,这样的团队配置我相信在我们行业内是不太有的,所以我对于分享产品过程也没有太刻意的保留。
”产品-市场对角线“里面很重要的一点就是持续收集需求,然后迭代升级。
这次我们拜访了多家潜在客户,收集了大量需求,最终选择在宁波天一阁博物院首先落地。原因可能是他们刚好有一个展会的契机,而且黄主任是我觉得为数不多的既懂技术(包括大模型)又懂文博专业知识的专家。我们和天一阁联名参办宁波数字经济展会是一个非常好的机会,因为如果只是和黄主任聊聊天,我觉得很多建议不会太深刻,因为和他自身没有太大利益关系。而共同去办展,他在很多时候比我还重视这件事情,所以获得的需求是真切的。
可交互AI讲解(后续可能会改名为”文博佳“)现在已经处于落地阶段,要处理的细节还有很多,11月份会在天一阁博物院率先落地。后面的几家意向博物馆也基本上做了联系,获得口头认可。
这算是又一个从Idea到产品的过程,不过这次理论推导更充分,也算是我在公司创造的第四个产品。这次的历程,最重要的是使用大模型对于实际业务结合的探索,虽然这次大模型在整个产品的技术组成中占比没过30%,但也是一个有意义的尝试。我们做产品最重要的不是炫技,而是要最终呈现用户价值,大模型、向量等在中间起到的作用是解决了内容生产效率的问题,这样我们才有胆量在前端(用户端)做到高要求,不然产品的持续力不够,内容断档,也最终只是昙花一现。
我目前更聚焦的是RAG方面的研发,大家都大模型、RAG感兴趣的话,可以关注我!
前面刚测试了Baichuan2-13B的本地化模型能力,对于我来说,归纳能力已经足够了。但是一旦深入RAG的应用,Baichuan2还是有一些弱势的,比如在表格的处理能力上,显得力不从心。让我们比较失望的是,目前国内一众模型里面,对这一块的处理能力都够呛,唯一可行的还是OpenAI的ChatGPT。
于是这两天也在空隙里面找可以本地化部署,对于RAG又比较友好的LLM,发现还真的有几个新的LLM出来了。
首先看到的是Mistral-7B,据说性能超过了Llama-13B。正当我准备好好研究一下的时候,又看到Wenqi Glantz老师在推Zephyr-7b-alpha,而且推荐理由里面有两点正是我想要的:
并且她还做了对比。
下面我们来看看Zephyr-7b-alpha的介绍,以及它的性能如何!
Zephyr其实是一个大语言模型的系列名称,Zephyr-7B-alpha是该系列的第一款型号,它是基于Mistral-7B-v0.1微调而来的,使用直接偏好优化(DPO)对公开可用的合成数据集进行混合训练。我们发现,删除这些数据集的内置对齐提高了MT Bench上的性能,可以让模型更加出类拔萃。当然,这个模型因为是MIT的协议,所以是可以免费商用的,和其他很多可商用模型一样,也是申明仅用于教育和研究目的,意思就是在商用上出什么幺蛾子我不管的。
我在它的官方提供的测试地址上做了测试,首先它是支持中文的(答案当然是不准的):

然后呢,它对于表格类型的问答,处理能力应该说比较弱吧,甚至有点不听指令(机器觉醒?)

Wenqi Glantz使用了EDD(评估驱动开发)的视角来探索zephyr-7b-alpha,使用LlamaIndex构建的多文档RAG管道,并与OpenAI模型GPT-3.5进行了比较。
zephyr-7b-alpha是一种7b参数类似gpt的模型,从Mistralai/Mistral-7B-v0.1微调而来。根据HuggingFace MT Bench,一个评估机器翻译(MT)系统质量的基准套件,zephyr-7b-alpha优于Llama-2-70b-chat-hf和Mistral-7B-Instruct-v0.1,请看下面的比较表。

LlamaIndex一直对各种LLM做兼容性跟踪评测,执行了一个LLM兼容性跟踪,从中我们了解到zephyr-7b-alpha是迄今为止唯一一个在高级RAG任务上表现良好的开源7b模型。

从上面的截图中可以看出,尽管zephyr-7b-alpha在大多数类别中都优于其他两个7b模型,但作为数据agent,它仍然有其局限性。让我们在下一节中继续进行评估POC时记下它。
BAAI/big-base-en-v1.5是由北京人工智能研究院(BAAI)开发的文本Embedding模型。它是一个在大量文本和代码数据集上训练的大型语言模型。它可以为各种NLP任务生成文本Embeddings,例如检索、分类、聚类和语义搜索。它是HuggingFace的MTEB(海量文本Embedding基准)排行榜上目前排名第二的Embedding模型,仅次于其更强大的兄弟big-large-en-v1.5。
我们将使用BAAI/big-base-en-v1.5作为评估POC的Embedding模型。
我们用两种实现策略评估了一个多文档RAG管道的能力:
正如上面关于zephyr-7b-alpha的部分所指出的,它对数据代理有限制。在本文中,我们将忽略递归检索器+文档代理的实现策略,而将重点放在相同的多文档RAG管道的元数据替换+节点句子窗口的策略上。我们将比较以下两种实现:
gpt-3.5-turbo + BAAI/bge-base-en-v1.5zephyr-7b-alpha + BAAI/bge-base-en-v1.5现在,让我们使用Embedding模型——BAAI/big-base-en-v1.5 ,请参阅下面的代码片段。注意,在构造ServiceContext时,embed_model现在指向local:BAAI/big-base-en-v1.5。
1 | #define LLM and embedding model |
这将触发将local:BAAI/big-base-en-v1.5下载到我们在Colab的环境中(你也可以用本地环境)。

现在,让我们试试zephyr-7b-alpha + BAAI/big-base-en-v1.5的组合。请参阅下面的代码片段。注意,在构建ServiceContext时,我将embed_model更改为local:BAAI/big-base-en-v1.5。
1 | import torch |
如果您在免费版Colab笔记本中运行上述代码,则在下载zephyr-7b-alpha 时可能会遇到错误。我不得不支付9.99美元的月费升级到专业版,并将硬件加速器的运行时类型更改为T4 GPU,并打开了High-RAM。这个小小的投资是非常值得的。

通过硬件调整,我们现在可以成功下载zephyr-7b-alpha。

让我们使用LlamaIndex的响应评估模块添加剩余的评估代码。
综上所述,使用LlamaIndex的响应评估模块的高级步骤包括:
DatasetGenerator自动生成评估问题。faithfulness和relevancy的评估者。BatchEvalRunner 来异步运行响应的评估。1 | from llama_index.evaluation import FaithfulnessEvaluator, RelevancyEvaluator |
让我们看看gpt-3.5-turbo和zephyr-7b-alpha的结果,两个测试的Embedding模型都是local:BAAI/big-base-en-v1.5。

让我们收集这两个评估的结果并把它们放在一起(忽略all-mpent-base-v2)。

我们将faithfulness和relevancy的数字映射到一个图表中,得到以下结果。

我们可以看到:
gpt-3.5-turbo + BAAI/big-base-en-v1.5相比,zephyr-7b-alpha + BAAI/big-base-en-v1.5的得分相同,faithfulness为0.93比0.93,relevancy为0.97比0.9。zephyr-7b-alpha + BAAI/big-base-en-v1.5需要4分钟才能完成,而其他选项大约需要10秒。哎呀!这当然不理想。zephyr-7b-alpha在评估的faithfulness和relevancy类别中确实看起来很有希望。然而,值得一提的是,HuggingFace:)对其局限性提出了几个关键点
Zephyr-7B-α没有通过RLHF等技术与人类偏好保持一致,也没有使用ChatGPT等循环过滤响应,因此模型可能产生有问题的输出(特别是在提示这样做的时候)。它也不知道语料库的大小和组成是用来训练基础模型的(Mistral-7B-v0.1),但是它很可能包括了网络数据和技术来源如书籍和代码的混合。
同样重要的是要记住,从评估结果中得出的结论只适用于我们处理的特定用例。zephyr-7b-alpha + BAAI/big-base-en-v1.5在我们的POC RAG管道中略优于GPT-3.5,但这并不意味着它在您的用例中会优于GPT-3.5。本文旨在传达评估驱动开发的重要性,甚至在选择大语言模型和Embedding模型时也是如此。使用EDD作为瑞士军刀来剖析和评估您的用例,以找到最合适的工具和实现策略,使您的RAG管道成功。
在本文中,我们通过EDD(评估驱动开发)的视角探索了zephyr-7b-alpha,使用rag相关技术:gpx-3.5-turbo + BAAI/big-base-en-v1.5和zephyr-7b-alpha + BAAI/big-base-en-v1.5。我们使用LlamaIndex的响应评估模块进行了评估,并比较了所有三种情况的结果。
我们可以得出结论,对于我们的POC RAG管道,zephyr-7b-alpha + BAAI/big-base-en-v1.5的组合略微优于OpenAI的ggt -3.5 + BAAI/big-base-en-v1.5。这对开源社区来说确实是一个鼓舞人心的消息!
最近一直和小明在搞RAG,我们认为这对于大部分公司和个人来说,就是大模型的未来,有应用才能更好推动大模型发展,至少近三年内应该是。前面也输出了一些RAG方面的文章,也有了一套精确度相对较高的技术产品。但是你知道的,RAG领域也在不断发展,而且我们确实也还面临着一些实际的问题,所以最近开始细细研究LlamaIndex——我觉得适用于RAG领域的话,可能会比LangChain更优秀。今天我们先来看看LlamaIndex的索引。
这是我自己列的一个结构图,当然是按我自己的理解画的,您只做参考吧,哈哈。

LlamaIndex是一个大型语言模型(大语言模型)的数据框架,提供以下工具:
LlamaIndex是为初学者和高级用户设计的。高级API允许初学者使用LlamaIndex在5行代码中摄取和查询他们的数据。低级api允许高级用户自定义和扩展任何模块(数据连接器、索引、检索器、查询引擎、重新排序模块),以满足他们的需求。
以下是使用LlamaIndex的一些好处:
LlamaIndex中的索引是一种数据结构,它允许您从大量文本语料库中快速搜索和检索数据。它的工作原理是在语料库中的关键字或短语与包含这些关键字或短语的文档之间创建映射。这种映射允许您快速找到包含特定关键字或短语的所有文档,即使语料库非常大。
索引对于各种任务都很有用,例如:
LlamaIndex提供了各种不同的索引类型,每种类型都有自己的优缺点。特定任务的最佳索引类型将取决于任务的特定需求。
Llamaindex提供的结构或组织数据的索引类型:
让我们先了解一些术语。根据Llamaindex的官方文件:
为了便于理解,我们可以将节点视为大约1k个单词的文本块,而响应合成则是通过查看相关节点(文本块)来给出问题答案(响应)的LLM。这里大家也可以回看我之前的文章《最详细的文本分块(Chunking)方法,直接影响LLM应用效果》。
Llamaindex提供不同类型的索引,以便更好地组织和检索相关信息,并使用Embeddings和LLM高效地运行查询。我们将逐一查看主要Index:
List Index :
List Index是一个简单的数据结构,它将文档存储为节点序列。在索引构建期间,文档文本被分块、转换为节点并存储在一个列表中。这使得它成为检索包含特定关键字或短语的文档的非常有效的索引。

使用列表索引的优点
在查询的过程中发生了什么?
在查询期间,如果没有指定其他查询参数,LlamaIndex只是将列表中的所有节点加载到响应合成模块中。

当用户输入关键字或短语时,查询列表索引。然后,索引扫描节点列表以查找包含关键字或短语的文档。然后将匹配查询的文档返回给响应合成模块:

使用List Index的缺点或问题:
向量索引是一种将文档文本转化为向量的索引,向量通常是由编码器transformer模型(如Bert等)生成的,它们也被称为Embeddings模型(参考前面关于embedding的文章《Embedding——从入门到生产使用》)。向量表示文本的含义,可以根据用户的查询来查找相关文档。

使用LlamaIndex来创建向量Index是非常简单,可以从文档中直接提取文本建立索引,可以看下面这段代码:
1 | from llama_index import Document, VectorStoreIndex |
或者我们也可以从先生成Node,才建立索引,我推荐这种方式,因为后面更好管理:
1 | from llama_index import Document, VectorStoreIndex |
使用向量索引的优点:
我们看看向量索引在查询的时候发生了什么:

总的来说,向量索引与传统查询的索引相比,更具备语义属性,可以让我们根据语义来检索,而不仅仅是按关键词匹配等字面意思来搜索。而且向量索引使用高效、可扩展能力强,且易于创建。当然了,它也有不好的一方面,就是如果您使用的不是本地embedding模型,而是使用OpenAI的text-embedding-ada-003、通义千问的embedding模型等,那么是需要成本的,而且量大的话可能很昂贵,即使使用免费的BGE、m3e等本地模型,也是需要一些硬件资源的。对了,说一点开心的,本地的embedding模型的话,我们自己是可以微调训练的,可以参考我之前的文章《手工微调embedding模型》。
Tree Index也是一种索引类型,它将文档的文本存储在树状结构中。树中的每个节点表示其子文档的摘要。我相信看这篇文章的朋友应该多少都接触过树的数据结构,通过遍历树并查找与查询相关的节点,树索引可用于查找与给定查询相关的文档。
为什么用树?我们通过和数组、链表的比较,使用大学知识简单复习一下树的优点:
数组:支持随机访问,进过排序的数组在查找的时候效率极高——O(1),但是数组的insert、delete等操作需要变动后续的所有元素,复杂度就会变大——O(n);
链表:和数组相反,链表的插入删除等效率极高,只需要将前后连接点打断,插入,再接好,所以时间复杂度一般是O(1)。但是它的查询速度就慢了,需要全部遍历才能找到相应元素,一般时间复杂度是O(n)。当然,链接有很多变种,比如散列链表的查询复杂度也是可以做到O(1)的;
树:树结构算是一个折中吧,特别是二叉树,它的查询和操作(插入、删除等)的复杂度都是O(logn)。二分法的威力就是n越大,它的上升曲线会越来越平缓。比如一组有序数字的猜大小的游戏中,8个数字(n=8),只要猜3次;32个数字猜5次,但是1024个数字也只需要10次。且很关键的一点,二叉树的效率非常稳定,这一点在开发中对于性能的可预估性非常重要。

使用树索引的优点:
在查询过程中发生了什么?
child_branch_factor=1),查询在给定父节点的情况下选择一个子节点。如果child_branch_factor=2,查询将在每个级别选择两个子节点。
使用树索引的缺点:
总的来说,树索引是索引和查询大型文本数据集的强大工具。它们对于查找相关文档非常有效,可伸缩,并且相对容易创建。但是,它们比其他类型的索引更难创建,并且在空转查询时效率较低。
关键字表索引是一种将文档的关键字存储在表中的索引,我觉得这更加类似Map<k,v>或者字典的结构。表中的每一行代表一个关键字,每一列代表一个文档。通过在表中查找关键字,可以使用表索引来查找包含给定关键字的文档。

使用关键字表索引的优点:
看看查询的过程:

使用关键字Index的缺点或问题:
总的来说,关键字表索引是索引和查询大型文本数据集的强大工具。它们对于查找包含特定关键字、可伸缩且相对容易创建的文档非常有效。但是,它们在查找包含多个关键字的文档时效率较低,在查找包含大量关键字的文档时可伸缩性较差,在查找包含同义词或相关关键字的文档时准确性较差。
我们讨论了5种类型的索引,它们如何存储数据,在查询时如何工作,以及每种索引的优缺点。因此,您可以根据自己的要求和约束条件选择最适合的Index。有一些高级类型的索引,如知识图谱索引等,后面再单独讲,需要结合知识图谱本身的一些概念。
对LlamaIndex的研究是因为它确实很优秀,很多理念也给了我们极大启发。就像小明说的,他想好好学习LlamaIndex,然后用Java也写一个简化版,因为他要拯救广大面对AI大浪而处于彷徨中的Java程序员,哈哈。
1.Different types of indexes in LlamaIndex to improve your RAG system:https://uttu-parashar.medium.com/different-types-of-indexes-in-llamaindex-to-improve-your-rag-system-fa9c49f40559
2.LlamaIndex官方文档:https://docs.llamaindex.ai/en/stable/core_modules/data_modules/documents_and_nodes/root.html

经过之前一段时间的捣腾,个人感觉我们的RAG应用已经来到了一个全新的层面,在语义理解(相关度)和准确度上都有了长足进步。
但是问题来了。之前和菊厂的业务交流中,对方明确提出一些客户的私有化部署意愿是很明确,主要原因是数据安全。这段时间和一些销售人员、潜在客户交流的时候也收到了类似信息。而我们现在的整套技术栈中,唯一还不能私有化的是LLM。而这也是最终客户最担心的问题——他们的数据被送进云上的公共LLM,不管这些LLM在数据安全上如何申明,对于一些数据就是资产的客户来说依然存在忧虑。所以我们最近就在开始做LLM的本地化测试。
在选本地化的LLM之前,我们先根据实际情况定义一些选择标准:
定义了选择标准之后,我们就来实际试用了。待选的其实也不多,我们自己有一个全量训练的BLOOM-7B,然后另外两个待选是ChatGLM2-6B和Baichuan2-13B。如果您还有其他更好的选择,可以私信我,哈。
部署在公司内网的自训练BLOOM-7B是今年3月份就训练好的,当时也算是走在比较前面的,至少在我们文旅数字化行业,好像当时是没有的。BLOOM-7B在问答方面能力还是可以的,在文旅方面因为有我们自己多年的语料沉淀,在我们覆盖的客户景区的问答上,在当时效果超过ChatGPT。

但是,接入到RAG之后,发现归纳能力不太行。而且近期因为新的开源大模型出来很多,所以暂时也没有打算再去优化这个7B的大模型。所以,首先试用和淘汰的就是我们自己训练的BLOOM-7B,这个行业,真的一日千里啊,3月份训练的大模型,当时是小甜甜,现在已经是牛夫人了。
目前我们使用的是智谱的标准版(API,在线版),效果应该说很棒,所以我们首先想到的是ChatGLM2-6B的开源版本。

ChatGLM2-6B因为已经有商业许可,所以两个月前就做了部署,把AutoModel.from_pretrained的指向从本地改成Hugging Face的模型地址让它更新到最新版本,然后接上我们的RAG做测试。说实话,使用同样的Prompt,ChatGLM2-6B的归纳能力只能说是复读机级别的(⊙o⊙)。LLM的归纳能力,太简单了可以通过Prompt优化改成复杂的,但是已经让它按最简单的方式去归纳了,依然是复读机的结果,那就不适用了。

因为ChatGLM2目前开放的就是6B和130B两个版本,但是130B的对于我们来说已经远超定义的选择范围,所以就放弃ChatGLM2了。
百川大模型是我们一个在老牌中厂的小伙伴给我们推荐的,据说他们内部已经在实用,而且效果不错,于是我就开始转向Baichuan2-13B。

我们首先选择的是13B,因为7B/6B这个级别的我们上面已经试用了,在技术层面没有碾压式的升级之前,参数体量有时候就决定了能力。对于Base和Chat,因为我们现在更多考察的还是归纳能力,所以为了方面考虑,选择了Chat版本。所以目前选择的就是Baichuan2-13B-Chat。
接下来就是考虑量化版本,官方给出了资源表:
| Precision | Baichuan2-7B | Baichuan2-13B |
|---|---|---|
| bf16 / fp16 | 15.3 GB | 27.5 GB |
| 8bits | 8.0 GB | 16.1 GB |
| 4bits | 5.1 GB | 8.6 GB |
按一张RTX 4090(24GB)来算的话,最佳的应该是Baichuan2-13B的8bits版本。但是官方只提供了fp16和4bits两个版本,唯独没有8bits量化版本。
我的做法是先部署fp16,先试试效果呗。
第一步是git clone代码,然后在Hugging Face上,models的过程是比较痛苦的,三个bin文件近28GB,最近几个月我发现直接下载(wget)总是无法成功,而我的Ubuntu服务器是没有科学上网的。所以只能在家里的Mac上先下载,直到今天凌晨2点才完成(好困~),然后传到Ubuntu上。

先用fp16先安装,把cli_demo.py里面的model地址改成本地的,这样不用每次运行的时候都会查询并下载最新版权重文件,主要是等待时间太长了。
cli_demo.py
1 | print("init model ...") |
运行程序:
1 | python cli_demo.py |
居然跑起来了,但是,推理的速度跟乌龟爬一样慢!
两个回合之后,直接就OutOfMemoryError了。
那现在只能把fp16量化为8bits,还好,官网有提到量化的方法。
我们需要先安装两个lib:
1 | pip install xformers |
然后在原来的models平级目录mkdir一个models_8,然后我用了一个比较偷懒的做法,直接把cli_demo.py复制为quan8.py。

下面修改这个新复制的quan8.py。
quan8.py
1 | def init_model(): |
我是比较偷懒的方式,因为当时太困了!
其他的代码我不管(运行的时候会报错,但不影响),就修改了init_model()函数。AutoModelForCausalLM.from_pretrained里面的第一个参数是原权重文件,save_pretrained里面是量化出来的int8权重文件保存的目录。
再次修改cli_demo.py,将model指向到int8的模型
cli_demo.py
1 | def init_model(): |
再次运行cli_demp.py,这次运行成功了,而且推理速度满足要求,棒!
然后就到了检验Baichuan2-13B的归纳能力的时候了,我直接复制了小明给我的Prompt。

速度、归纳效果,都还可以!那私有化,暂时就先选型Baichuan2-13B了。
对了,其实Baichuan2-13B的逻辑能力是有短板的,我经常用的一道测试题,它答得不好。

这次大模型的选型测试主要还是为了私有化的需要,如果没有私有化(甚至完全断网)需求的话,个人感觉智谱的API还是挺好用的。本次选择除了最开始定义的三个过滤条件外,其实还有一个就是可商用,ChatGLM2-6B、BLOOM-7B和Baichuan2-13B都是符合这一条件的。