“利用定制的LLM引擎AI助手增强您的研究能力”

通过定制的LLM引擎AI助手,提升您的研究能力

简介

在信息泛滥的世界中,高效地访问和提取相关数据是无价的。ResearchBot是一个使用OpenAI的LLM(大型语言模型)和Langchain进行信息检索的前沿LLM动力应用项目。本文就是一个逐步手册,教你如何创建自己的ResearchBot以及它在现实生活中的帮助。它就像是拥有智能助手,可以从海量数据中找到你需要的信息。无论你是热爱编程还是对人工智能感兴趣,本指南都将帮助你利用定制的LLM动力人工智能助手提升你的研究能力。这将是你解锁LLM的潜力、改变你访问信息的方式的旅程。

学习目标

  • 了解LLM(大型语言模型)、Langchain、向量数据库和嵌入等更深层概念。
  • 探索LLM和ResearchBot在研究、客户支持和内容生成等领域的真实应用。
  • 发现将ResearchBot整合到现有项目或工作流程中以提高生产力和决策能力的最佳实践。
  • 构建ResearchBot以简化数据提取和问题回答的流程。
  • 了解LLM技术的趋势及其改变我们访问和使用信息的潜力。

本文是数据科学博文马拉松的一部分。

ResearchBot是什么?

ResearchBot是由LLMs驱动的研究助手。它是一款创新工具,可以快速访问和总结内容,成为跨不同行业的专业人士的好伴侣。

想象一下,你有一个个性化助手,可以阅读和理解多篇文章、文件和网页,并为你提供相关且简短的摘要。我们的ResearchBot的目标是减少研究所需的时间和精力。

实际应用案例

  • 金融分析:及时了解最新的市场新闻,并快速获取金融问题的答案。
  • 新闻报道:高效搜集文章的背景信息、来源和参考文献。
  • 医疗保健:获取当前的医学研究论文和摘要以进行研究。
  • 学术界:找到相关的学术论文、研究材料和研究问题的答案。
  • 法律研究:快速检索法律文件、裁决和法律问题的见解。

技术术语

向量数据库

存储文本数据向量嵌入的容器,对于高效的基于相似性的搜索至关重要。

通过理解用户查询意图和上下文,在搜索时不完全依赖关键词匹配。

嵌入

文本数据的数值表示,可以进行高效的比较和搜索。

项目的技术架构

技术架构
  • 我们使用嵌入模型为需要索引的信息或内容创建向量嵌入。
  • 将向量嵌入插入向量数据库,并与创建嵌入的原始内容进行关联。
  • 当应用程序发出查询时,我们使用相同的嵌入模型为查询创建嵌入,并使用这些嵌入查询数据库以获取相似的向量嵌入。
  • 这些相似的嵌入与用于创建它们的原始内容相关联。

ResearchBot如何工作?

工作原理

这种架构促进了存储、检索和与内容的交互,使我们的研究机器人成为信息检索和分析的强大工具。它利用向量嵌入和向量数据库,以便快速准确地进行内容搜索。

组件

  1. 文档:这些是您想要索引以供将来参考和检索的文章或内容。
  2. 拆分:这处理将文档分解为更小、可管理的块的过程。对于处理大型文档或文章,确保它们完全适合语言模型的约束条件并进行高效索引非常重要。
  3. 向量数据库:向量数据库是架构的重要部分。它存储从内容生成的向量嵌入。每个向量与其来源原始内容相关联,创建了数值表示和源材料之间的链接。
  4. 检索:当用户查询系统时,使用相同的嵌入模型为查询创建嵌入。然后,使用这些查询嵌入在向量数据库中搜索相似的向量嵌入。结果是一组大量相似的向量,每个向量与其原始内容来源相关联。
  5. 提示:定义用户与系统交互的位置。用户输入查询,系统处理这些查询,从向量数据库中检索相关信息,提供答案和对源内容的引用。

LangChain中的文档加载器

使用文档加载器从源中加载数据,形成文档的形式。文档是一段文本和相关元数据。例如,有用于加载简单的“txt”文件、加载文章或博客的文本内容,甚至用于加载 YouTube 视频的转录的文档加载器。

有多种类型的文档加载器:

示例 – TextLoader

这段代码展示了Langchain中TextLoader的功能。它将文本数据从现有文件“Langchain.txt”加载到TextLoader类中,为进一步处理做好准备。’file_path’变量存储加载文件的路径,供将来使用。

# 从langchain.document_loaders模块中导入TextLoader类
from langchain.document_loaders import TextLoader

# 通过指定要加载的文件来考虑TextLoader类,这里是"Langchain.txt"
loader = TextLoader("Langchain.txt")

# 从提供的文件("Langchain.txt")加载内容到TextLoader类
loader.load()

# 检查'loader'实例的类型,应为'TextLoader'
type(loader)

# 与TextLoader关联的文件路径在'file_path'变量中
loader.file_path
TextLoaders

LangChain中的文本拆分器

文本拆分器负责将文档拆分成较小的文档。这些较小的单元使得更容易处理和高效处理内容。在我们的ResearchBot项目中,我们使用文本拆分器来准备数据以进行进一步的分析和检索。

我们为什么需要文本拆分器?

LLM具有令牌限制。因此,我们需要将较大的文本拆分成较小的块,以使每个块的大小小于令牌限制。

手动将文本拆分为块的方法

# 从维基百科中提取一些随机文本
text = #填入随机文本

# 假设LLM令牌限制为100,在我们的代码中,我们可以做简单的操作,例如
text[:100]
text
chunk

嗯,但我们想要完整的单词,并且要对整个文本进行处理,也许我们可以使用Python的分割函数

words = text.split(" ")len(words)chunks = []s = ""for word in words:    s += word + " "    if len(s)>200:        chunks.append(s)        s = ""        chunks.append(s)chunks[:2]
Chunks

将数据分成块可以使用原生的Python,但这是一个繁琐的过程。如果需要,您可能需要尝试连续使用多个分隔符来确保每个块不超过相应LLM的令牌长度限制。

Langchain通过文本分割器类提供了更好的方法。Langchain中有多个文本分割器类可供使用。

1. 字符文本分割器

该类旨在根据特定的分隔符(如段落、句号、逗号和换行符)将文本分割成较小的块。它更适用于将文本分解为多个块,以便进行进一步处理。

from langchain.text_splitter import CharacterTextSplittersplitter = CharacterTextSplitter(    separator = "\n",    chunk_size=200,    chunk_overlap=0)chunks = splitter.split_text(text)len(chunks)for chunk in chunks:    print(len(chunk))
Character TextSplitter

如您所见,尽管我们给定了200个块大小,但由于拆分是基于\n进行的,所以最终创建的块的大小大于200。

Langchain中的另一个类可以用于根据一系列分隔符递归地拆分文本。这个类叫做RecursiveTextSplitter。让我们看看它是如何工作的。

2. 递归文本分割器

这是一种通过递归分析文本中的字符来操作的文本分割器。它尝试通过不同的字符组合迭代地找到不同的分割方法,直到找到有效地分割文本和不同类型的外壳。

from langchain.text_splitter import RecursiveCharacterTextSplitterr_splitter = RecursiveCharacterTextSplitter(    separators = ["\n\n", "\n", " "],  # 分隔符列表     chunk_size = 200,  # 每个块的大小    chunk_overlap  = 0,  # 块之间的重叠大小     length_function = len  # 计算大小的函数,)chunks = r_splitter.split_text(text)for chunk in chunks:    print(len(chunk))    first_split = text.split("\n\n")[0]first_splitlen(first_split)  second_split = first_split.split("\n")second_splitfor split in second_split:    print(len(split))    second_split[2]second_split[2].split(" ")
splitter

让我们了解一下我们是如何形成这些块的:

first_split

递归文本分割器使用了一个分隔符列表,即separators = [“\n\n”, “\n”, “.”]

所以现在它将首先使用\n\n进行分割,如果结果块的大小超过了这个情景中的200块大小参数,那么它将使用下一个分隔符,即\n。

second_split

第三次分割超过200个字符。现在将尝试使用第三个分隔符,即空格 ‘ ‘ 进一步分割

final_split

当使用空格进行分割(即 second_split[2].split(” “)),它将分开每个单词,然后将这些块合并,使其大小接近200。

向量数据库

现在,考虑一种情况,需要存储数百万甚至数十亿个单词嵌入,这在实际应用中非常重要。关系型数据库虽然能够存储结构化数据,但由于其在处理如此大量的数据方面的限制,可能不太适用。

这就是向量数据库发挥作用的地方。向量数据库被设计为高效存储和检索向量数据,使其适用于单词嵌入。

向量数据库通过使用语义搜索革新了信息检索。它们利用单词嵌入和智能索引技术的威力,使搜索更快速、更准确。

向量索引与向量数据库的区别是什么?

独立的向量索引(如FAISS(Facebook AI相似性搜索))可以提高向量嵌入的搜索和检索能力,但缺乏在数据库中存在的某些功能。另一方面,向量数据库专为管理向量嵌入而构建,提供了多个优点,优于使用独立的向量索引。

FAISS

步骤:

1:为文本列创建源嵌入

2:为向量构建FAISS索引

3:对源向量进行归一化并添加到索引中

4:使用相同的编码器对搜索文本进行编码并归一化输出向量

5:在创建的FAISS索引中搜索相似向量

df = pd.read_csv("sample_text.csv")
df# Step 1 : Create source embeddings for the text column
from sentence_transformers import SentenceTransformer
encoder = SentenceTransformer("all-mpnet-base-v2")
vectors = encoder.encode(df.text)
vectors# Step 2 : Build a FAISS Index for vectors
import faiss
index = faiss.IndexFlatL2(dim)# Step 3 : Normalize the source vectors and add to index
index.add(vectors)
index# Step 4 : Encode search text using same encoder
search_query = "looking for places to visit during the holidays"
vec = encoder.encode(search_query)
vec.shapes
vec = np.array(vec).reshape(1,-1)
vec.shape# Step 5: Search for similar vector in the FAISS index
distances, I = index.search(vec, k=2)
distances
row_indices = I.tolist()[0]
row_indices
df.loc[row_indices]

如果我们检查此数据集,

data

我们将使用单词嵌入将这些文本转换为向量

vectors

考虑我的搜索查询 = “寻找假期期间参观的地方”

Results

它正在使用旅游类别的语义搜索,提供与我的查询最相似的2个结果。

当您执行搜索查询时,数据库使用诸如局部敏感哈希(LSH)等技术来加快处理速度。LSH将相似的向量分组到桶中,从而实现更快速和更有针对性的搜索。这意味着您不必将查询向量与每个存储的向量进行比较。

检索

当用户查询系统时,使用相同的嵌入模型为查询创建嵌入。然后,使用这些查询嵌入来搜索与向量数据库中的相似向量嵌入。结果是一组相似的向量,每个向量与其原始内容源相关联。

检索的挑战

语义搜索中的检索显示出多个挑战,如GPT-3等语言模型所施加的令牌限制。当处理多个相关数据块时,会超出响应限制。

材料方法

在这种模式中,它涉及从向量数据库中收集所有相关数据块,并将它们合并成一个提示(个体)。这个过程的主要缺点是超出令牌限制,导致响应不完整。

材料

Map Reduce方法

为了克服令牌限制挑战和简化检索QA过程,该过程提供了一种解决方案,即将相关数据块而不是合并为一个提示(个体),如果有4个数据块,则通过分别的隔离LLM传递一切。这些问题提供了上下文信息,使语言模型能够独立关注每个数据块的内容。这将导致每个数据块的单个答案集。最后,进行最终LLM调用,将所有这些独立答案组合起来,根据从每个数据块中获得的见解找到最佳答案。

Map Reduce

ResearchBot的工作流程

(1) 加载数据

在这一步中,导入数据,如文本或文档,并准备进行进一步处理,以便进行分析。

#提供要抓取数据的URLs= UnstructuredURLLoader(urls=[    "",    ""])data = loaders.load() len(data)

(2) 拆分数据以创建块

将数据分成更小、更易管理的部分或块,便于处理和处理大型文本或文档。

text_splitter = RecursiveCharacterTextSplitter(    chunk_size=1000,    chunk_overlap=200)# 选取split_documents而不是split_text以获得块.docs = text_splitter.split_documents(data)len(docs)docs[0]

(3) 为这些块创建嵌入并将它们保存到FAISS索引中

将文本块转换为数字向量表示(嵌入),并存储在Faiss索引中,优化相似向量的检索。

# 使用openAIEmbeddings创建块的嵌入embeddings = OpenAIEmbeddings()# 传递文档和嵌入以创建FAISS向量索引vectorindex_openai = FAISS.from_documents(docs, embeddings)# 将创建的向量索引存储在本地file_path="vector_index.pkl"with open(file_path, "wb") as f:    pickle.dump(vectorindex_openai, f)        if os.path.exists(file_path):    with open(file_path, "rb") as f:        vectorIndex = pickle.load(f)

(4) 检索给定问题的相似嵌入并调用LLM以检索最终答案

对于给定的查询,我们检索相似的嵌入,并使用这些向量与语言模型(LLM)交互,以便简化信息检索并提供最终答案给用户的问题。

# 使用所需的参数初始化LLMllm = OpenAI(temperature=0.9, max_tokens=500) chain = RetrievalQAWithSourcesChain.from_llm(  llm=llm,   retriever=vectorIndex.as_retriever())chainquery = "" # 提出您的问题 langchain.debug=Truechain({"question": query}, return_only_outputs=True)

最终应用

在使用所有这些阶段(文档加载器、文本拆分器、向量数据库、检索、提示)并借助streamlit构建应用程序之后,我们完成了ResearchBot的构建。

URL

这是页面中的一个部分,其中插入了博客或文章的url。我提供了2023年发布的最新iPhone手机的链接。在开始构建这个应用程序ResearchBot之前,每个人都会有一个问题,既然已经有了ChatGPT,为什么还要构建这个ResearchBot。答案如下:

ChatGPT的答案:

ChatGPT

ResearchBot的答案:

Research Bot

这里,我的查询是“Apple Iphone 15的价格是多少?”

这些数据来自于2023年,而ChatGPT 3.5并没有这些数据,但我们已经用最新的Iphone信息训练了我们的ResearchBot。因此,我们通过我们的ResearchBot得到了我们需要的答案。

使用ChatGPT的3个问题:

  1. 复制粘贴文章内容很繁琐。
  2. 我们需要一个综合知识库。
  3. 字数限制-3000个字

结论

我们在现实世界的场景中见证了语义搜索和向量数据库的概念。我们的ResearchBot能够使用语义搜索从向量数据库高效地检索答案,展示了深度LLM在信息检索和问答系统领域的巨大潜力。我们已经开发出了一个非常受欢迎的工具,它能够轻松查找和总结重要信息,并具有高级别的搜索功能。对于那些寻求知识的人来说,这是一个强大的解决方案。这项技术为信息检索和问答系统打开了新的视野,为寻求数据驱动的洞见的任何人带来了变革。

常见问题

本文中显示的媒体不是Analytics Vidhya所拥有的,并且是作者自行决定使用的。