随着开源大型语言模型的性能一直进步,编写和剖析代码、举荐、文本摘要和问答(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的应用,然而只是功能性的简略介绍,这次咱们作为残缺的零碎设计,将他整合到一个真正可用的理论我的项目中。首先是装置必要的库:

 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对象。

该模块代码的残缺代码如下:

 # 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,并将其用于查问。

 # 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示意。

     vs = db.__dict__.get("docstore")     index_list = db.__dict__.get("index_to_docstore_id").values()     doc_cnt = db.index.ntotal

调用对象索引的办法reconstruct_n,能够实现向量空间的近似重建

    embeddings_vec = db.index.reconstruct_n()

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

    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进行可视化

     df = pd.DataFrame(doc_list,columns=['id','metadata','document','embedding'])

在持续进行可视化之前,还须要将问题和答案联合起来,咱们创立一个独自的问题以及答案的DF,而后与下面的df进行合并,这样可能显示问题和答案呈现的中央,在可视化时咱们能够高亮显示:

     # 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在文件和问题嵌入上的进行间隔大小的计算:

     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进行可视化,所有咱们间接调用即可

 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.2Note 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)的维基百科文档的向量空间进行可视化展现。

 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的反馈。通过调整某些矢量化参数,咱们可能影响其生成行为以进步精度。

https://avoid.overfit.cn/post/30168a23de744b3f91ec22da1725eb14

作者:Kennedy Selvadurai, PhD