Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >可视化FAISS矢量空间并调整RAG参数提高结果精度

可视化FAISS矢量空间并调整RAG参数提高结果精度

作者头像
deephub
发布于 2024-03-01 09:19:44
发布于 2024-03-01 09:19:44
48000
代码可运行
举报
文章被收录于专栏:DeepHub IMBADeepHub IMBA
运行总次数:0
代码可运行

随着开源大型语言模型的性能不断提高,编写和分析代码、推荐、文本摘要和问答(QA)对的性能都有了很大的提高。但是当涉及到QA时,LLM通常会在未训练数据的相关的问题上有所欠缺,很多内部文件都保存在公司内部,以确保合规性、商业秘密或隐私。当查询这些文件时,会使得LLM产生幻觉,产生不相关、捏造或不一致的内容。

为了处理这一挑战的一种可用技术是检索增强生成(retrieve - augmented Generation, RAG)。它涉及通过在响应生成之前引用其训练数据源之外的权威知识库来增强响应的过程。RAG应用程序包括一个检索系统,用于从语料库中获取相关文档片段,以及一个LLM,用于使用检索到的片段作为上下文生成响应,所以语料库的质量及其在向量空间中的表示(称为嵌入)在RAG的准确性中发挥重要作用。

在本文中,我们将使用可视化库renumics-spotlight在2-D中可视化FAISS向量空间的多维嵌入,并通过改变某些关键的矢量化参数来寻找提高RAG响应精度的可能性。对于我们选择的LLM,将采用TinyLlama 1.1B Chat,这是一个紧凑的模型,与Llama 2相同的架构。它的优点是具有更小的资源占用和更快的运行时间,但其准确性没有成比例的下降,这使它成为快速实验的理想选择。

系统设计

QA系统有两个模块,如图所示。

LoadFVectorize模块加载pdf或web文档。对于最初的测试和可视化。第二个模块加载LLM并实例化FAISS检索,然后创建包含LLM、检索器和自定义查询提示的检索链。最后我们对它的向量空间进行可视化。

代码实现

1、安装必要的库

renumics-spotlight库使用类似umap的可视化,将高维嵌入减少到更易于管理的2D可视化,同时保留关键属性。我们在以前的文章中也介绍过umap的使用,但是只是功能性的简单介绍,这次我们作为完整的系统设计,将他整合到一个真正可用的实际项目中。首先是安装必要的库:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 pip install langchain faiss-cpu sentence-transformers flask-sqlalchemy psutil unstructured pdf2image unstructured_inference pillow_heif opencv-python pikepdf pypdf
 pip install renumics-spotlight
 CMAKE_ARGS="-DLLAMA_METAL=on" FORCE_CMAKE=1 pip install --upgrade --force-reinstall llama-cpp-python --no-cache-dir

上面的最后一行是安装带有Metal支持的llama- pcp -python库,该库将用于在M1处理器上加载带有硬件加速的TinyLlama。

2、LoadFVectorize模块

模块包括3个功能:

load_doc处理在线pdf文档的加载,每个块分割512个字符,重叠100个字符,返回文档列表。

vectorize调用上面的函数load_doc来获取文档的块列表,创建嵌入并保存到本地目录opdf_index,同时返回FAISS实例。

load_db检查FAISS库是否在目录opdf_index中的磁盘上并尝试加载,最终返回一个FAISS对象。

该模块代码的完整代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 # LoadFVectorize.py
 
 from langchain_community.embeddings import HuggingFaceEmbeddings
 from langchain_community.document_loaders import OnlinePDFLoader
 from langchain.text_splitter import RecursiveCharacterTextSplitter
 from langchain_community.vectorstores import FAISS
 
 # access an online pdf
 def load_doc() -> 'List[Document]':
     loader = OnlinePDFLoader("https://support.riverbed.com/bin/support/download?did=7q6behe7hotvnpqd9a03h1dji&version=9.15.0")
     documents = loader.load()
     text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=100)
     docs = text_splitter.split_documents(documents)
     return docs
 
 # vectorize and commit to disk
 def vectorize(embeddings_model) -> 'FAISS':
     docs = load_doc()
     db = FAISS.from_documents(docs, embeddings_model)
     db.save_local("./opdf_index")
    return db
 
 # attempts to load vectorstore from disk
 def load_db() -> 'FAISS':
     embeddings_model = HuggingFaceEmbeddings()
     try:
         db = FAISS.load_local("./opdf_index", embeddings_model)
    except Exception as e:
         print(f'Exception: {e}\nNo index on disk, creating new...')
         db = vectorize(embeddings_model)
     return db

3、主模块

主模块最初定义了以下模板的TinyLlama提示符模板:

<|system|>{context}</s><|user|>{question}</s><|assistant|>

另外采用来自TheBloke的量化版本的TinyLlama可以极大的减少内存,我们选择以GGUF格式加载量化LLM。

然后使用LoadFVectorize模块返回的FAISS对象,创建一个FAISS检索器,实例化RetrievalQA,并将其用于查询。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 # main.py
 from langchain.chains import RetrievalQA
 from langchain.prompts import PromptTemplate
 from langchain_community.llms import LlamaCpp
 from langchain_community.embeddings import HuggingFaceEmbeddings
 import LoadFVectorize
 from renumics import spotlight
 import pandas as pd
 import numpy as np
 
 # Prompt template 
 qa_template = """<|system|>
 You are a friendly chatbot who always responds in a precise manner. If answer is 
 unknown to you, you will politely say so.
 Use the following context to answer the question below:
 {context}</s>
 <|user|>
 {question}</s>
 <|assistant|>
 """
 
 # Create a prompt instance 
 QA_PROMPT = PromptTemplate.from_template(qa_template)
 # load LLM
 llm = LlamaCpp(
     model_path="./models/tinyllama_gguf/tinyllama-1.1b-chat-v1.0.Q5_K_M.gguf",
     temperature=0.01,
     max_tokens=2000,
     top_p=1,
     verbose=False,
     n_ctx=2048
 )
 # vectorize and create a retriever
 db = LoadFVectorize.load_db()
 faiss_retriever = db.as_retriever(search_type="mmr", search_kwargs={'fetch_k': 3}, max_tokens_limit=1000)
 # Define a QA chain 
 qa_chain = RetrievalQA.from_chain_type(
     llm,
     retriever=faiss_retriever,
     chain_type_kwargs={"prompt": QA_PROMPT}
 )
 
 query = 'What versions of TLS supported by Client Accelerator 6.3.0?'
 
 result = qa_chain({"query": query})
 print(f'--------------\nQ: {query}\nA: {result["result"]}')
 
 visualize_distance(db,query,result["result"])

向量空间可视化本身是由上面代码中的最后一行visualize_distance处理的:

visualize_distance访问FAISS对象的属性__dict__,index_to_docstore_id本身是值docstore-ids的关键索引字典,用于向量化的总文档计数由索引对象的属性ntotal表示。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     vs = db.__dict__.get("docstore")
     index_list = db.__dict__.get("index_to_docstore_id").values()
     doc_cnt = db.index.ntotal

调用对象索引的方法reconstruct_n,可以实现向量空间的近似重建

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    embeddings_vec = db.index.reconstruct_n()

有了docstore-id列表作为index_list,就可以找到相关的文档对象,并使用它来创建一个包括docstore-id、文档元数据、文档内容以及它在所有id的向量空间中的嵌入的列表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    doc_list = list() 
    for i,doc-id in enumerate(index_list):
        a_doc = vs.search(doc-id)
        doc_list.append([doc-id,a_doc.metadata.get("source"),a_doc.page_content,embeddings_vec[i]])

然后使用列表创建一个包含列标题的DF,我们最后使用这个DF进行可视化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     df = pd.DataFrame(doc_list,columns=['id','metadata','document','embedding'])

在继续进行可视化之前,还需要将问题和答案结合起来,我们创建一个单独的问题以及答案的DF,然后与上面的df进行合并,这样能够显示问题和答案出现的地方,在可视化时我们可以高亮显示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     # add rows for question and answer
     embeddings_model = HuggingFaceEmbeddings()
     question_df = pd.DataFrame(
         {
             "id": "question",
             "question": question,
             "embedding": [embeddings_model.embed_query(question)],
         })
     answer_df = pd.DataFrame(
         {
             "id": "answer",
             "answer": answer,
             "embedding": [embeddings_model.embed_query(answer)],
         })
     df = pd.concat([question_df, answer_df, df])

这里使用使用np.linalg.norm在文件和问题嵌入上的进行距离大小的计算:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     question_embedding = embeddings_model.embed_query(question)
     # add column for vector distance
     df["dist"] = df.apply(                                                                                                                                                                         
         lambda row: np.linalg.norm(
             np.array(row["embedding"]) - question_embedding
         ),axis=1,)

因为spotlight可以对df进行可视化,所有我们直接调用即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 spotlight.show(df)

这一步将在浏览器窗口中启动spotlight。

运行测试

1、基本测试

下面是我们选择的样本问题:

What versions of TLS supported by Client Accelerator 6.3.0?

正确答案是:

Client Accelerator 6.3.0 supports TLS 1.1 or 1.2.

以下额外信息可能包含在响应中。

You must enable this capability using the following CLI command on the Client Accelerator:(config) # policy id <id-number> ssl backend client-tls-1.2Even though TLS 1.1 is not mentioned in the syntax of this command, using this command automatically enables support for both TLS 1.1 and 1.2.

然后我们看看TinyLlama对上述问题的回答:

Client Accelerator 6.3.0 supports TLS 1.1 or 1.2 as the default supported TLS versions with Client Accelerator. You must enable this capability using the following CLI command on the Client Accelerator:```(config) # policy id <id-number> ssl backend client-tlss1.2```Note that this command only applies to TLS 1.1 or TLS 1.2. If you need to support older TLS versions, you can use the ssl backend command with the client-tlss1.0 or client-tlss1.1 option instead.

看起来与实际答案非常相似,但它并不完全正确,因为这些TLS版本不是其默认值。

那么我们来看看他是从哪些段落中找到的答案呢?

在可以spotlight中使用visible 按钮来控制显示的列。按“dist”对表格进行排序,在顶部显示问题、答案和最相关的文档片段。查看我们文档的嵌入,它将几乎所有文档块描述为单个簇。这是合理的,因为我们原始pdf是针对特定产品的部署指南,所以被认为是一个簇是没有问题的。

单击Similarity Map选项卡中的过滤器图标,它只突出显示所选的文档列表,该列表是紧密聚集的,其余的显示为灰色,如图下所示。

2、测试块大小和重叠参数

由于检索器是影响RAG性能的关键因素,让我们来看看影响嵌入空间的几个参数。TextSplitter的块大小chunk size(1000,2000)和/或重叠overlap (100,200)参数在文档分割期间是不同的。

对所有组合的对于输出似乎相似,但是如果我们仔细比较正确答案和每个回答,准确答案是(1000,200)。其他回答中不正确的细节已经用红色突出显示。我们来尝试使用可视化嵌入来解释这一行为:

从左到右观察,随着块大小的增加,我们可以观察到向量空间变得稀疏且块更小。从底部到顶部,重叠逐渐增多,向量空间特征没有明显变化。在所有这些映射中整个集合仍然或多或少地呈现为一个单一的簇,并只有几个异常值存在。这种情况在生成的响应中是可以看到的因为生成的响应都非常相似。

如果查询位于簇中心等位置时由于最近邻可能不同,在这些参数发生变化时响应很可能会发生显著变化。如果RAG应用程序无法提供预期答案给某些问题,则可以通过生成类似上述可视化图表并结合这些问题进行分析,可能找到最佳划分语料库以提高整体性能方面优化方法。

为了进一步说明,我们将两个来自不相关领域(Grammy Awards和JWST telescope)的维基百科文档的向量空间进行可视化展示。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 def load_doc():
     loader = WebBaseLoader(['https://en.wikipedia.org/wiki/66th_Annual_Grammy_Awards','https://en.wikipedia.org/wiki/James_Webb_Space_Telescope'])
     documents = loader.load()
     ...

只修改了上面代码其余的代码保持不变。运行修改后的代码,我们得到下图所示的向量空间可视化。

这里有两个不同的不重叠的簇。如果我们要在任何一个簇之外提出一个问题,那么从检索器获得上下文不仅不会对LLM有帮助,而且还很可能是有害的。提出之前提出的同样的问题,看看我们LLM产生什么样的“幻觉”

Client Accelerator 6.3.0 supports the following versions of Transport Layer Security (TLS):

  1. TLS 1.2\2. TLS 1.3\3. TLS 1.2 with Extended Validation (EV) certificates\4. TLS 1.3 with EV certificates\5. TLS 1.3 with SHA-256 and SHA-384 hash algorithms

这里我们使用FAISS用于向量存储。如果你正在使用ChromaDB并想知道如何执行类似的可视化,renumics-spotlight也是支持的。

总结

检索增强生成(RAG)允许我们利用大型语言模型的能力,即使LLM没有对内部文档进行训练也能得到很好的结果。RAG涉及从矢量库中检索许多相关文档块,然后LLM将其用作生成的上下文。因此嵌入的质量将在RAG性能中发挥重要作用。

在本文中,我们演示并可视化了几个关键矢量化参数对LLM整体性能的影响。并使用renumics-spotlight,展示了如何表示整个FAISS向量空间,然后将嵌入可视化。Spotlight直观的用户界面可以帮助我们根据问题探索向量空间,从而更好地理解LLM的反应。通过调整某些矢量化参数,我们能够影响其生成行为以提高精度。

作者:Kennedy Selvadurai, PhD

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DeepHub IMBA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
48days强训——day6
示例1 输入: [9,3,7],[6,3] 返回值: {1,0,0,0} 说明: 如题面解释 示例2 输入: [0],[6,3] 返回值: {6,3} 备注: 1≤n,m≤1061≤n,m≤106 0≤ai,bi≤90≤ai​,bi​≤9
秋邱
2025/03/29
410
48days强训——day6
​LeetCode刷题实战43:字符串相乘
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
程序员小猿
2021/01/20
4470
​LeetCode刷题实战43:字符串相乘
链表以及字符串数据求和及乘积问题
本篇文章分为三个部分也就是三道题来对一系列大数求和积问题做一下解答已经总结,这里正如题目所说的链表,字符串等,这些也不过是一个形式,其实可以归为一类,因此这里我们要知道真正的侧重点是在于如何去求和以及乘积? 这里也就是我们不能盲目的直接相加啊,相乘啊去求,因此可以把加以及乘的具体步骤模拟一下,最后得到最后答案,说白了也就是我们要一位一位来,而不是‘’一口气‘’!
羑悻的小杀马特.
2025/01/23
540
链表以及字符串数据求和及乘积问题
一天一大 lee(克隆图)难度:中等-Day20200813
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
前端小书童
2020/09/24
2970
一天一大 lee(克隆图)难度:中等-Day20200813
高精度算法解析与实现(c++)
在计算机科学中,高精度运算是指超出普通数据类型(如int或long long)所能表示的数值范围的计算。这些计算涉及到处理非常大的整数或浮点数,常见的应用场景包括大数加减乘除、密码学、科学计算等。在这篇博客中,我们将学习几种常见的高精度算法,并通过具体代码实现其功能,涵盖高精度加法、减法、乘法和除法。
平凡之路.
2025/02/22
2010
【C++】 —— 笔试刷题day_6
看题目我们可以发现,题目所给的数组范围特别大,所以,我们使用int、long long肯定是不行的;
星辰与你
2025/03/17
590
【C++】 —— 笔试刷题day_6
【Leetcode】string类刷题
接着,创建两个索引,begin和end,一个从前往后找,找到一个字母停止,另一个从后面找,找到字母停止,然后进行交换,保证begin<end,比较简单,代码如下:
用户11029103
2024/04/20
1070
【Leetcode】string类刷题
算法讲解之字符串
本文主要讲解算法中和字符串结合的题目,跟字符串结合的算法题种类丰富,主要是跟别的算法结合,下面介绍几道比较经典的题目~
用户11316056
2024/10/16
970
算法讲解之字符串
算法0基础之高精度加法模板+解题报告
文章目录 前言 高精度算法的实现 高精度加法 例题 前言 🤞秋名山码民的主页🤞 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 🙏作者水平很有限,如果发现错误,一定要及时告知作者 高精度算法存在的意义: 在c++中变量的最大范围也不过是64位的大小,可是在实际的数据中难免出现超出范围的,从而由字符串(数组)引申出来了高精度的计算,用字符串来模拟每一位数字,用算术模拟计算高精度加法,高精度乘法 高精度算法的实现 高精度加法 for (int i = 0; i < maxlen; ++i) { av = (
秋名山码神
2022/12/13
3480
【蓝桥杯每日一题】3.16
刷题就像打游戏,蓝桥杯是终极大BOSS,每天的真题都是小怪——虽然爆率低,但装备(知识)掉不停!
f狐o狸x
2025/03/17
1051
【蓝桥杯每日一题】3.16
算法基础(二)| 高精度算法详解
适用于c++,java和python没有这个问题,因为java有大整数类,python自带,默认数是无限大。
timerring
2022/09/27
8480
算法基础(二)| 高精度算法详解
【C++经典例题】基于字符串实现大数相乘问题
在实际编程中,我们经常会遇到需要处理大整数的情况。由于编程语言中内置整数类型(如 int、long 等)有其表示范围的限制,当需要处理的整数超出这些范围时,就不能直接使用内置类型进行计算。
倔强的石头
2025/04/15
990
【C++经典例题】基于字符串实现大数相乘问题
运筹学与最优化理论基础——高精度加减乘除(C++实现)
在写单纯形算法时,发现了高精度分数存在bug与不足,所以必须对相关函数进行修改。主要有bug的函数是string DIVIDE_INT(string str1,string str2,int flag),之前是为了运算简单起见,对于特殊除数与被除数进行特定的判断来减小计算复杂度,但是发现存在逻辑bug,判断这些条件之后,未直接返回结果使得程序仍然会执行正常的除法操作,因此对这个bug进行修正。同时为了方便之后的单纯型算法的编写,在此又特意添加两个函数int Compare2Zero()和int Compare2Fraction(Fraction fraction),分别来比肩与0和分数fraction的大小。 在写两阶段单纯形算法时,发现了高精度分数中缺少相关取反和取倒数等接口导致代码出现大量重复代码。因此再次对高精度分数类进行修改。主要实现了分数取反和分数取倒数,并将整体代码进行了优化。由于两个函数过于简单,因此不对这两个函数进行讲解。
AI那点小事
2020/04/20
1.3K0
运筹学与最优化理论基础——高精度加减乘除(C++实现)
leetcode刷题(68)——43. 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
老马的编程之旅
2022/06/22
3330
leetcode刷题(68)——43. 字符串相乘
高精度原理介绍及代码实现
高精度算法(High Accuracy Algorithm)的出现是为了处理超大数据的数学计算问题。在一般的科学计算中,我们可能会遇到需要计算小数点后几百位甚至更多的数字,或者处理几千亿、几百亿这样的大数字。这些数字超出了标准数据类型(如整型、实型)能够表示的范围,因此无法直接在计算机中正常存储和计算。
用户11039529
2024/05/24
1600
高精度原理介绍及代码实现
【C++】高精度算法讲解
高精度运算也称之为大数运算。即:在变量运算对象的数值范围为任何数据类型所无法容纳的情况下,采用整数数组存储(用字符串表示数字)。
Karos
2023/01/03
1.4K0
【C++】高精度算法讲解
【每日一题】43. Multiply Strings
Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.
公众号-不为谁写的歌
2020/09/02
3530
【C++】string的9道OJ题
1. 这道题的难度算非常简单的了,我们可以定义两个变量来表示数组首尾位置的有效字符的下标,然后分别从前和从后向中间遍历,只要遇到字母就停下来,利用库函数swap进行交换。
举杯邀明月
2023/04/12
4300
【C++】string的9道OJ题
高精度算法和链表
计算机最初、也是最重要的应用就是数值运算。在编程进行数值运算时,有时会遇到运算的精度要求特别高,远远超过各种数据类型的精度范围;有时数据又特别大,远远超过各种数据类型的极限值。这种情况下,就需要进行“高精度运算”。
楚客追梦
2023/10/18
1990
【Day24】 LeetCode算法题 (注释详细+解题思路)[43. 字符串相乘 ] [1800. 最大升序子数组和]
解题思路: 我们需要获得两个字符串表示的正整数num1和num2的乘积,而且记过依旧以字符串形式输出。
.29.
2022/11/15
3400
【Day24】 LeetCode算法题 (注释详细+解题思路)[43. 字符串相乘 ] [1800. 最大升序子数组和]
相关推荐
48days强训——day6
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验