基于LangChain搭建个人知识库

news2024/11/20 20:22:10

bar

前言

😺Hello,大家好,我是GISer Liu,😀😀 一名热爱AI技术的GIS开发者;在上一篇文章中,我们学习了LLM API的申请、应用以及提示词工程;

在本文中,作者将介绍如何从零开始构建个人知识库🎉🤓;主要内容包括:

  • 词向量和向量数据库概念
  • 申请Embedding Model API
  • 使用LangChain工具对文本数据进行处理
  • 基于文本数据搭建向量数据库并进行测试

一、词向量和向量数据库

1. 向量
① 概念

向量是一个既有方向又有大小的量,可以在空间中用来表示点或对象之间的关系。在数学和物理学中,向量表示大小和方向的物理量,如速度、加速度等。在自然语言处理中,向量用于表示词语、句子或文档的语义信息,使得机器能够理解和处理自然语言

数学上,向量可以表示为一组有序数字的列表,通常写成列向量的形式。例如,一个三维空间中的向量可以表示为:

v = [ v 1 , v 2 , v 3 ] T v = [v_1, v_2, v_3]^T v=[v1,v2,v3]T

其中, v 1 v_1 v1 v 2 v_2 v2 v 3 v_3 v3是向量的分量,上标 T T T表示向量的转置。

② 特点
  • 高维度:向量通常是高维度的每一维度代表对象的某一特征。在机器学习和深度学习中,常用一组向量来表示训练集的特征;例如,一个词向量可能包含300维或更多,每一维捕捉不同的语义特征

  • 相似性度量:可以通过计算向量之间的距离(如余弦相似度、欧几里得距离)来度量对象之间的相似性。余弦相似度衡量两个向量之间的角度,而欧几里得距离则是两个向量在空间中的直线距离。

③ 向量的度量公式
  • 余弦相似度(Cosine Similarity):余弦相似度是通过计算两个向量的内积除以其模的乘积来度量相似度。公式如下:

 cosine_similarity ( A , B ) = A ⋅ B ∥ A ∥ ∥ B ∥   \ \text{cosine\_similarity}(A, B) = \frac{A \cdot B}{\|A\| \|B\|} \  cosine_similarity(A,B)=A∥∥BAB 

其中, ( A ⋅ B ) (A \cdot B) (AB) 是向量A和B的内积, ∥ A ∥ \|A\| A ∥ B ∥ \|B\| B 分别是向量A和B的模。余弦相似度的取值范围从-1到1,其中1表示向量完全相同,0表示它们正交(或无关),-1表示它们完全相反。

  • 欧几里得距离(Euclidean Distance):欧几里得距离是通过计算两个向量之间的直线距离来度量相似度。公式如下:

 euclidean_distance ( A , B ) = ∑ i = 1 n ( A i − B i ) 2   \ \text{euclidean\_distance}(A, B) = \sqrt{\sum_{i=1}^{n} (A_i - B_i)^2} \  euclidean_distance(A,B)=i=1n(AiBi)2  

其中, A i A_i Ai B i B_i Bi 分别是向量A和B的第i个分量。

  • 欧几里得距离(Euclidean Distance):一种衡量两个向量之间距离的方法,通过计算两个向量在空间中的直线距离来表示相似度,距离越小,向量越相似

2. 词向量
① 概念

在自然语言处理的背景下,一个词向量通常是使用Word2Vec或GloVe等技术学习得到的。这些方法旨在捕捉词语在高维向量空间中的语义和句法关系。这种方法的思想是:在相似的上下文中使用的词语将具有相似的向量,从而允许我们对词语执行数学运算以理解它们的含义
vword_vector

例如,“king”这个词的向量可能接近“queen”这个词的向量,而远离“apple”这个词的向量,这反映“king”“queen”之间的相关性,而“king”“apple”之间没有相关性。这使我们能够执行诸如“king” - “man” + “woman”的操作,得到一个接近“queen”的向量,这是一种词类推理的形式。
queen

② 特点
  • 语义保持:相似的词在向量空间中的距离较近。例如,“国王”和“王后”的词向量会比“国王”和“苹果”的词向量距离更近。
  • 高效计算:词向量便于进行数学运算和相似性计算。例如,可以通过简单的向量加减操作,计算词语之间的关系(如“国王” - “男人” + “女人” ≈ “王后”)。
③ 词向量计算应用

在NLP中,词向量的运算经常用于各种任务,作者总结如下:

  1. 词性消除(Word Analogy):正如上文提到的案例,通过执行类似于"king" - “man” + "woman"的操作,我们可以找到一个与"queen"相似的词。这种技术被广泛用于语言模型和词嵌入的评估

  2. 情感分析(Sentiment Analysis):在情感分析任务中,词向量可以用于表示文档或句子的情感。例如,一个文档的情感可以被表示为其中所有词向量的平均值。然后,可以使用这些情感向量来训练分类器,以预测文档的情感是正面的还是负面的。

  3. 文本分类(Text Classification):词向量可以用于表示文档或句子的主题。例如,一个文档的主题可以被表示为其中所有词向量的平均值。然后,可以使用这些主题向量来训练分类器,以将文档分配到不同的类别。

  4. 机器翻译(Machine Translation):在机器翻译任务中,词向量可以用于表示词语在源语言和目标语言之间的语义关系。例如,一个词向量可以被表示为一个矩阵的乘积,其中矩阵捕捉了源语言和目标语言之间的语义转换。然后,可以使用这些转换向量来生成目标语言中的翻译

  5. 问答系统(Question Answering Systems):在问答系统中,词向量可以用于表示问题和答案之间的相似性。例如,一个问题可以被表示为其中所有词向量的平均值,一个答案也是如此。然后,可以使用这些问题和答案向量之间的相似性来选择最佳答案

这也是RAG技术的核心原理;🙂

③ 构建词向量的方法
  • 使用大模型厂商提供的Embedding API

    • 优势便于使用,模型性能优秀,能够处理大量数据。例如,OpenAI的GPT-3和Google的BERT API可以直接使用现有的高质量词向量
    • 劣势依赖外部服务,可能存在成本和隐私问题。例如,调用外部API服务可能需要支付高额费用,并且数据需要传输到外部服务器 ,可能存在数据泄露风险
  • 本地搭建嵌入模型

    • 优势数据私密性好,无需依赖外部服务,可根据需要自定义模型。例如,公司内部的数据可以通过本地搭建的Word2Vec或BERT模型进行训练,确保数据不外泄。
    • 劣势需要大量计算资源和时间来训练模型。例如,训练一个高质量的MistralQwen模型需要高性能的GPU集群和大量的训练数据,普通玩家带不动。
④ 词向量生成示例
  • Word2Vec:假设有以下简化的句子:“I like deep learning”Word2Vec模型通过上下文窗口来训练词向量,例如,窗口大小为2时,“like”的上下文是“I”和“deep”。模型将这些上下文词汇与目标词汇一起训练,生成词向量。

训练文本:

(I, like), (like, I), (like, deep), (deep, like), (deep, learning), (learning, deep)

可能生成的词向量:

I -> [0.2, -0.1, 0.3, ...]
like -> [0.4, 0.2, -0.5, ...]
deep -> [-0.3, 0.8, 0.1, ...]
learning -> [0.5, -0.7, 0.2, ...]

3. 向量数据库
① 什么是向量数据库

向量数据库是专门用于存储、索引和检索高维向量数据的数据库。它广泛应用于推荐系统、图像识别和语义搜索等领域。与传统的关系型数据库不同,向量数据库能够高效处理非结构化数据和高维向量

② 向量数据库的原理

向量数据库通过将数据对象表示为向量,并使用相似性度量方法(如余弦相似度、欧几里得距离等)来实现高效的相似性搜索。例如,给定一个查询向量,向量数据库可以快速找到与之最相似的向量,并返回对应的对象

  • 具体的数学公式已在上文中给出;
  • Embedding的过程:将数据转换为向量的过程通常被称为“嵌入”。嵌入是一种技术,用于将数据从高维空间映射到低维空间,同时保留数据的结构 。在向量数据库的背景下,嵌入用于将数据对象转换为可以高效存储和搜索的向量。
③ 向量数据库的特点及优势
  • 速度与性能:使用各种索引技术(如HNSWLSH等)来实现快速搜索。例如,HNSW(Hierarchical Navigable Small World)索引能够在大规模数据集上实现毫秒级的相似性搜索。
  • 可扩展性:能够存储和管理大量的非结构化数据,并随着数据量的增加保持性能稳定。例如,向量数据库可以轻松扩展到数亿级的向量数据,同时保持高效的查询性能。
  • 灵活性:能够处理多种类型的数据,包括文本、图像、音频等(各种类型的数据都可以转化为向量存储)。例如,在图像搜索应用中,可以将图像表示为向量,并通过向量数据库进行相似图像搜索(这也是百度或谷歌识图的原理 )。
  • HNSW(Hierarchical Navigable Small World):一种高效的近似最近邻搜索算法,通过构建层次化的小世界图,实现快速的向量相似性搜索。
  • LSH(Locality-Sensitive Hashing):一种用于高维数据相似性搜索的哈希算法,通过将相似的数据映射到相同的哈希桶,实现快速的近似相似性搜索。
④ 主流的向量数据库
  • 开源
    • Faiss:由Meta(原FaceBook) AI Research开发,适用于高效相似性搜索。Faiss支持多种索引方法,如IVF(Inverted File)、HNSW等,并能够处理大规模数据集。
    • Chroma:支持多种向量索引和检索方法,适用于各种应用场景。Chroma通过结合多种索引技术,实现高效的向量搜索和管理。
    • Weaviate:支持多种媒体类型的数据嵌入和搜索。Weaviate不仅支持文本向量,还支持图像、视频等多媒体向量的存储和检索。
    • Qdrant:高性能、可扩展的向量搜索引擎,适合大规模数据集。Qdrant采用了多种优化技术,实现了高效的向量索引和检索。

本文中,作者将以Faiss向量数据库为例:构建向量数据库😂

  • 商业
    • Pinecone:提供高性能向量搜索和存储解决方案,集成简单,适合企业应用。Pinecone支持多种向量索引方法,并提供API接口,方便企业集成和使用。
    • Milvus:支持亿级数据量的向量搜索,具备高扩展性和高可用性。Milvus由Zilliz开发,广泛应用于推荐系统、图像识别等领域,提供了多种索引方法和查询优化技术。
⑤向量数据库应用(可选)
  1. 推荐系统:在电子商务平台中,通过分析用户的浏览历史和购买行为,将用户和商品表示为向量,并使用向量数据库实现个性化推荐。例如,亚马逊使用商品向量和用户向量计算相似度,为用户推荐可能感兴趣的商品。
  2. 图像识别:在图像搜索引擎中,将图像表示为向量,并通过向量数据库实现相似图像搜索。例如,Google Photos通过图像嵌入向量,实现了快速的相似图像检索和管理。
  3. 语义搜索:在搜索引擎中,通过将查询和文档表示为向量,并使用向量数据库实现语义匹配。例如,ElasticSearch结合向量搜索功能,实现了更加智能的语义搜索,提高了搜索结果的相关性。

二、使用Embedding API

1.Embedding API申请
①OpenAI

在上一篇文章中,作者已经给出了申请OpenAI API的过程,因此这里我们只介绍使用的Embedding模型:
OpenAI Embedding
上图是官网的截图,作者这里优化了一下:

模型名称描述输出维度价格
text-embedding-3-large适用于英语和非英语任务的最强大的嵌入模型3,0729615 token/$
text-embedding-3-small比第二代ada嵌入模型性能更好1,53662500 token/$
text-embedding-ada-002最强大的第二代嵌入模型,取代了16个第一代模型1,53612500 token /$

显而易见,text-embedding-3-small 最具性价比

②文心千帆
模型名称描述更新时间语言
Embedding-V1基于百度文心大模型技术的文本表示模型,将文本转化为用数值表示的向量形式,用于文本检索、信息推荐、知识挖掘等场景。2024-06-20中文
bge-large-zh由智源研究院研发的中文版文本表示模型,可将任意文本映射为低维稠密向量,以用于检索、分类、聚类或语义匹配等任务,并可支持为大模型调用外部知识。2024-06-20中文
bge-large-en由智源研究院研发的英文版文本表示模型,可将任意文本映射为低维稠密向量,以用于检索、分类、聚类或语义匹配等任务,并可支持为大模型调用外部知识。2024-06-20英文
tao-8k由Huggingface开发者amu研发并开源的长文本向量表示模型,支持8k上下文长度,模型效果在C-MTEB上居前列,是当前最优的中文长文本embeddings模型之一。2024-06-20中文

所有模型的功能都是根据输入内容生成对应的向量表示。

③ Mistral

通用,之前的文章中已经给出了详细的API申请方案,这里Mistral当前只有一个Embedding模型:

模型名称描述输出维度最大Token调用参数
Mistral Embeddings一个将文本转换成1024维数值向量的模型。嵌入模型使得检索和检索增强生成应用成为可能。它在MTEB上实现了55.26的检索分数。10248kmistral-embed

相关参数可以看看官方文档:https://docs.mistral.ai/capabilities/embeddings/

③智谱AI

GLM-Embedding

模型名称描述
Embedding-2文本向量模型,将输入的文本信息进行向量化表示,以便于结合向量数据库为大模型提供外部知识库,提高大模型推理的准确性。

官网文档:https://open.bigmodel.cn/dev/api#emohaa

2. Embedding API代码测试
①OpenAI
  • 测试调用代码如下:
import os
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv


# 读取本地/项目的环境变量。
# find_dotenv()寻找并定位.env文件的路径
# load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中  
# 如果你设置的是全局的环境变量,这行代码则没有任何作用。
_ = load_dotenv(find_dotenv())

# 如果你需要通过代理端口访问,你需要如下配置
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'
os.environ["HTTP_PROXY"] = 'http://127.0.0.1:7890'

def openai_embedding(text: str, model: str=None):
    # 获取环境变量 OPENAI_API_KEY
    api_key=os.environ['OPENAI_API_KEY']
    client = OpenAI(api_key=api_key)

    # embedding model:'text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'
    if model == None:
        model="text-embedding-3-small"

    response = client.embeddings.create(
        input=text,
        model=model
    )
    return response

response = openai_embedding(text='要生成 embedding 的输入文本,字符串形式。')

  • 代码结果
作者没有额度了...,大家可以测试一下
② Mistral

代码如下:

# Mistral测试
import os
from mistralai.client import MistralClient

api_key = api_key = os.getenv('MISTRAL_API_KEY') 

client = MistralClient(api_key=api_key)

embeddings_response = client.embeddings(
    model="mistral-embed",
    input=["什么是GIS?", "如何学习GIS."],
)

print(embeddings_response.data[0].embedding)

运行结果如下:

[-0.025238037109375, -0.0018138885498046875, 0.0482177734375, -0.0030612945556640625, -0.0041656494140625, -0.0060272216796875, 0.037994384765625, 0.0193634033203125, -0.0011968612670898438, -0.0174560546875, -0.05242919921875, 0.05255126953125, 0.0081787109375, -0.003032684326171875, -0.03790283203125, 0.028076171875, -0.0045318603515625, 0.0204925537109375, 0.005374908447265625, -0.007598876953125, -0.020172119140625, 0.0255279541015625, -0.031341552734375, 0.059814453125, -0.0181732177734375, -0.02008056640625, -0.0401611328125, -0.020599365234375, -0.022064208984375, -0.01178741455078125, 0.001678466796875, -0.0137939453125, -0.018524169921875, 0.0186309814453125, -0.0006866455078125, -0.00031375885009765625, -0.003204345703125, -0.035675048828125, 0.0206146240234375, -0.01253509521484375, 0.00957489013671875, -0.0013856887817382812, -0.010833740234375, -0.03802490234375, -0.021759033203125, -0.0135040283203125, 0.020751953125, -0.01392364501953125, 0.0186309814453125, -0.04107666015625, 0.01346588134765625, 0.048126220703125, 0.02197265625, -0.0011663436889648438, -0.0174713134765625, -0.00502777099609375, 0.0231170654296875, -0.06884765625,...]
③智谱AI

调用代码:

from zhipuai import ZhipuAI
  
client = ZhipuAI(api_key="your api key") 
response = client.embeddings.create(
    model="embedding-2", #填写需要调用的模型名称
    input="你好",
)
本地搭建开源Embedding模型

ollama_embedding
此模型可以基于Ollama调用,下面是调用代码:

ollama部署nomic-embed-text:

ollama pull nomic-embed-text

python调用:

import ollama
ollama.embeddings(model='nomic-embed-text', prompt='The sky is blue because of rayleigh scattering')
```bash
略

三、文本数据处理

0.环境配置
  • 安装第三方库
# 基于Mistral AI 和 Faiss的向量数据库
# %pip install -qU langchain-mistralai
# %pip install -qU nltk
# %pip install -qU langchain_community
# !sudo apt-get install sqlite3 libsqlite3-dev
# %pip install langchain_openai
# %pip install unstructured
# %pip install "unstructured[pdf]"
# %pip install markdown
# %pip install pypdf
# %pip install faiss-cpu
# %pip install -qU langchain-text-splitters

作者使用的是Linux服务器上的Jupyter,windows用户可以使用!替代%或者直接使用%;

  • 导入必要库
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
from langchain.text_splitter import CharacterTextSplitter,NLTKTextSplitter
from langchain_community.document_loaders import DirectoryLoader,UnstructuredMarkdownLoader,PyPDFLoader#用于加载各种文件数据
#import magic
import os
import nltk
1.源文档选取

作者是GIS开发者,工作中经常遇到GIS理论和原理性问题,为了保证GIS开发快速理解GIS理论,避免低效检索,作者这里决定构建一个包含GIS、遥感等原理的文章作为知识库,方便工作中快速查阅,能更好的进行优化WorkFlow,作者选择的案例文件如图,数据保存在./folder/文件目录下:
files

各位读者可以自行选择需要导入到数据库中的文件;🤓

2. 数据读取

由上图可知,我们的数据主要包含以下三种格式:

  • TXT
  • PDF
  • Markdown

因此我们需要构建三个文件加载器TextLoader,PyPDFLoader,UnstructuredMarkdownLoader;
数据读取代码如下:

from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

import getpass
import os
from dotenv import load_dotenv, find_dotenv


# 读取本地/项目的环境变量。
_ = load_dotenv(find_dotenv())
api_key =  os.getenv('MISTRAL_API_KEY') 
from langchain_mistralai import ChatMistralAI

llm = ChatMistralAI(model="mistral-large-latest") # 创建LLM

# 选择加载的文件,作为GIS开发者,作者选择了Markdown、PDF、TXT三种格式的数据,保存为/folder文件夹下
# 分别构建三个类型的DocumentLoader

txt = TextLoader("./folder/GIS原理与应用名词解释.txt").load()
pdf = PyPDFLoader("./folder/85个你必须知道的常用遥感名词.pdf").load()
md = UnstructuredMarkdownLoader("./folder/遥感物理基础.md").load()

print(txt[0].page_content[:100],len(txt[0].page_content))
print(pdf[0].page_content[:100],len(pdf[0].page_content))
print(md[0].page_content[:100],len(md[0].page_content))   

运行查看结果:
load_result
这说明我们得到的是多个列表,其中的每个元素都是一个文本对象,可以通过其page_content属性查看对应的文本内容;

我们能看到,其输出的结果中,文本格式和原有文件中一样带有空行;

3. 数据清洗

数据清洗是处理文本数据的关键步骤之一,目的是去除无用信息、噪声和错误,提高数据质量。在本例中,由于文档中存在大量空行和无关字符,如果不进行处理经过嵌入(Embedding)后会产生大量无用信息,影响数据库存储效率和向量相似度计算精度

因此,我们需要对文本数据进行清洗,主要包括以下两个方面:

  1. 删除无用空行:使用正则表达式匹配连续的空行,并将其替换为单个空行。
  2. 删除无关字符:根据具体应用场景和文档类型,删除文本中无关的字符,如空格、标点符号等。但是需要注意,对于某些特定类型的文档,如代码文档,需要保留缩进和某些符号,以保证语义的完整性和可读性。
    以下是数据清洗代码:
# 数据清洗
import re

# 定义清洗模式
pattern = re.compile(r'[^\S\n]|(\n{2,})', re.MULTILINE)

# 遍历所有文档
for doc in txt + pdf + md:
    # 对每个文档进行清洗
    doc.page_content = re.sub(pattern, lambda match: '\n' if match.group(1) else '', doc.page_content)

# 打印每个文档的前100个字符和长度
for doc in txt + pdf + md:
    print(doc.page_content[:100], len(doc.page_content))

在这段代码中,作者使用正则表达式匹配空白字符和连续的空行,并将其替换为单个空行。同时,我们保留了文本中的其他符号,以保证语义的完整性和可读性。通过这样的清洗处理,我们可以提高文本数据的质量,提高存储效率和向量相似度计算精度

运行查看结果:

clear_result
可以看到,空行已经被删除完毕;

  • 数据清洗的标准取决于我们知识库的应用场景和文档类型
  • 对于代码文档,我们要保持缩进,不能删除符号
  • 对于文本,我们可以删除缩进,但是不能删除太多符号;保证语义完整,易于区分
4. 文档分割

在构建向量知识库时,由于单个文档的长度通常会超过模型支持的上下文(Token)长度,因此需要对文档进行分割,将其切分成多个较小的块(chunk,然后将每个块转化为词向量,存储到向量数据库中。这样在检索时,可以以块为单位进行检索,每次检索到 k 个块,作为模型可以参考的知识来回答用户问题。

Langchain 提供了多种文档分割方式,每种方式在确定块与块之间的边界、块由哪些字符/token组成、以及如何测量块大小方面都有所不同。以下是一些常用的文本分割方式:

  1. RecursiveCharacterTextSplitter(): 按字符串分割文本,递归地尝试按不同的分隔符进行分割文本。
  2. CharacterTextSplitter(): 按字符来分割文本。
  3. MarkdownHeaderTextSplitter(): 基于指定的标题来分割 markdown 文件。
  4. TokenTextSplitter(): 按 token 来分割文本。
  5. SentenceTransformersTokenTextSplitter(): 使用 Sentence-Transformers 库按 token 来分割文本。
  6. Language(): 用于 CPP、Python、Ruby、Markdown 等编程语言文件的分割。
  7. NLTKTextSplitter(): 使用 NLTK(自然语言工具包)按句子分割文本。
  8. SpacyTextSplitter(): 使用 Spacy 按句子的切割文本。

在使用这些分割方法时,需要根据具体的应用场景和文档类型选择适合的分割方式,以及设定合适的分割参数,如块大小( c h u n k _ s i z e chunk\_size chunk_size)和块与块之间的重叠大小( c h u n k _ o v e r l a p chunk\_overlap chunk_overlap)。

作者这里使用了 RecursiveCharacterTextSplitter 进行文本分割:

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 创建一个分割器实例
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200, chunk_overlap=50,
    length_function=len,
    is_separator_regex=False,
)

# 遍历所有文档
all_text = ""
for doc in txt + pdf + md:
    content = doc.page_content
    # 将文档的 page_content 属性连接到 all_text 中
    all_text += content

# 对文本进行分割
split_docs = text_splitter.create_documents([all_text])

# 打印分割后的文档块和块的数量
print(split_docs, len(split_docs))

运行代码,查看结果:
split_result
可以看到,文档文本数据已经被切分为546块大小;

因为作者的数据资料都是一些名词解释,因此设定了块大小为 200,块与块之间的重叠大小为 50。保证向量检索语义不会丢失太多;😲通过这样的分割处理,我们可以将长文档切分成适合模型处理的块,提高检索和回答问题的效率和精度

作者这里整理了一下LangChain支持的文档分割方法,感兴趣的读者可以自行学习:

名称类别拆分方式添加元数据描述
递归式RecursiveCharacterTextSplitter, RecursiveJsonSplitter用户定义的字符列表递归地拆分文本。这种拆分尝试保持相关文本片段彼此靠近。这是开始拆分文本的推荐方式。
HTMLHTMLHeaderTextSplitter, HTMLSectionSplitterHTML特定字符根据HTML特定字符拆分文本。值得注意的是,这将添加有关该片段来源的相关信息(基于HTML)。
MarkdownMarkdownHeaderTextSplitterMarkdown特定字符根据Markdown特定字符拆分文本。值得注意的是,这将添加有关该片段来源的相关信息(基于Markdown)。
代码多种语言代码(Python、JS)特定字符根据特定于编程语言的字符拆分文本。有15种不同的语言可供选择。
令牌多个类别令牌根据令牌拆分文本。存在几种不同的方法来测量令牌。
字符CharacterTextSplitter用户定义的字符根据用户定义的字符拆分文本。这是较简单的方法之一。
[实验性] 语义块SemanticChunker句子首先按句子拆分。然后,如果它们在语义上足够相似,就将彼此靠近的句子组合在一起。由Greg Kamradt提供。
5.构建向量数据库

我们已经切分好了文本数据,现在我们需要将切分好的文本数据转化为向量格式,并且存储到向量数据库中,这里由于作者的OpenAI 的API key没有余额了 🤐🤐,作者使用Mistral封装了一个LangChain可用的Embedding模型;封装代码如下:

# 封装Mistral Embedding 
from __future__ import annotations

import logging
import os
from typing import Dict, List, Any

from langchain.embeddings.base import Embeddings
from langchain.pydantic_v1 import BaseModel, root_validator

logger = logging.getLogger(__name__)

class MistralAIEmbeddings(BaseModel, Embeddings):
    """`MistralAI Embeddings` embedding models."""

    client: Any
    """`mistralai.MistralClient`"""

    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """
        实例化MistralClient为values["client"]

        Args:
            values (Dict): 包含配置信息的字典,必须包含 client 的字段.

        Returns:
            values (Dict): 包含配置信息的字典。如果环境中有mistralai库,则将返回实例化的MistralClient类;否则将报错 'ModuleNotFoundError: No module named 'mistralai''.
        """
        from mistralai.client import MistralClient
        api_key = os.getenv('MISTRAL_API_KEY')
        if not api_key:
            raise ValueError("MISTRAL_API_KEY is not set in the environment variables.")
        values["client"] = MistralClient(api_key=api_key)
        return values

    def embed_query(self, text: str) -> List[float]:
        """
        生成输入文本的 embedding.

        Args:
            texts (str): 要生成 embedding 的文本.

        Return:
            embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表.
        """
        embeddings = self.client.embeddings(
            model="mistral-embed",
            input=[text]
        )
        return embeddings.data[0].embedding

    def embed_documents(self, texts: List[str]) -> List[List[float]]:
        """
        生成输入文本列表的 embedding.

        Args:
            texts (List[str]): 要生成 embedding 的文本列表.

        Returns:
            List[List[float]]: 输入列表中每个文档的 embedding 列表。每个 embedding 都表示为一个浮点值列表。
        """
        return [self.embed_query(text) for text in texts]

开始向量化,构建向量数据库,并且保存向量数据库到本地;作者这里使用的Faiss向量数据库Chroma有Sqlite3的Bug,时间太短,暂时搁置😣

# 对文本数据进行向量化,这里作者使用自己封装的Mistral Embedding
embeddings_model = MistralAIEmbeddings()

# 构建向量数据框
db = FAISS.from_documents(split_docs, embeddings_model)

# # 保存向量数据库到本地
db.save_local("./db/GIS_db")

代码运行后,可以看到,当前文件./db/目录下面已经多出来一个向量数据库GIS_db
db_result

OK,🎉🎉🎉🎉🏆现在我们向量数据库已经构建完毕,下面测试一下!

6.进行向量相似度检索

现在,我们输入“电磁辐射”,然后对其进行向量相似性查询,计算出相似度最高的3个文档切块:
代码如下:

# 使用向量数据库进行检索
# 加载向量数据库
loaded_db = FAISS.load_local("./db/GIS_db", embeddings_model, allow_dangerous_deserialization=True) # 允许来自危险源的数据库
# 计算相似度并检索最相似的文档
query = "电磁辐射"
docs = loaded_db.similarity_search(query, k=3) # 相似度最高的前3个chunk
# 输出检索结果
for doc in docs:
    print(doc.page_content+"\n-----------------\n")

输出结果如下:
search
如此一来,我们就得到了与用户prompt最接近的辅助信息,这些辅助信息会经过Prompt Engine提示词工程被打包为一个Prompt发送给LLM,这样得到的回答就有了一定的保证和可信度;这就是RAG的核心思想;总体思路也可以用下面这张图来表示,各位读者可以阅读一下:
rag-core
OK,今天就到这儿了!🤓🤓🤓🏆🏆🎉

5. 完整代码
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
from langchain.text_splitter import CharacterTextSplitter,NLTKTextSplitter
from langchain_community.document_loaders import DirectoryLoader,UnstructuredMarkdownLoader,PyPDFLoader#用于加载各种文件数据
#import magic
import os
import nltk
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
import re
import getpass
from dotenv import load_dotenv, find_dotenv


# 读取本地/项目的环境变量。
_ = load_dotenv(find_dotenv())
api_key =  os.getenv('MISTRAL_API_KEY') 
from langchain_mistralai import ChatMistralAI

llm = ChatMistralAI(model="mistral-large-latest") # 创建LLM

# 选择加载的文件,作为GIS开发者,作者选择了Markdown、PDF、TXT三种格式的数据,保存为/folder文件夹下
# 分别构建三个类型的DocumentLoader

txt = TextLoader("./folder/GIS原理与应用名词解释.txt").load()
pdf = PyPDFLoader("./folder/85个你必须知道的常用遥感名词.pdf").load()
md = UnstructuredMarkdownLoader("./folder/遥感物理基础.md").load()

print(txt[0].page_content[:100],len(txt[0].page_content))
print(pdf[0].page_content[:100],len(pdf[0].page_content))
print(md[0].page_content[:100],len(md[0].page_content))   

# 数据清洗

# 定义清洗模式
pattern = re.compile(r'[^\u4e00-\u9fff](\n)[^\u4e00-\u9fff]', re.DOTALL)

# 遍历所有文档
for doc in txt + pdf + md:
    # 对每个文档进行清洗
    doc.page_content = re.sub(pattern, lambda match: match.group(0).replace('\n', ''), doc.page_content)

# 打印每个文档的前100个字符和长度
for doc in txt + pdf + md:
    print(doc.page_content[:100], len(doc.page_content))

# 封装Mistral Embedding 
from __future__ import annotations

import logging
import os
from typing import Dict, List, Any

from langchain.embeddings.base import Embeddings
from langchain.pydantic_v1 import BaseModel, root_validator

logger = logging.getLogger(__name__)

class MistralAIEmbeddings(BaseModel, Embeddings):
    """`MistralAI Embeddings` embedding models."""

    client: Any
    """`mistralai.MistralClient`"""

    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """
        实例化MistralClient为values["client"]

        Args:
            values (Dict): 包含配置信息的字典,必须包含 client 的字段.

        Returns:
            values (Dict): 包含配置信息的字典。如果环境中有mistralai库,则将返回实例化的MistralClient类;否则将报错 'ModuleNotFoundError: No module named 'mistralai''.
        """
        from mistralai.client import MistralClient
        api_key = os.getenv('MISTRAL_API_KEY')
        if not api_key:
            raise ValueError("MISTRAL_API_KEY is not set in the environment variables.")
        values["client"] = MistralClient(api_key=api_key)
        return values

    def embed_query(self, text: str) -> List[float]:
        """
        生成输入文本的 embedding.

        Args:
            texts (str): 要生成 embedding 的文本.

        Return:
            embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表.
        """
        embeddings = self.client.embeddings(
            model="mistral-embed",
            input=[text]
        )
        return embeddings.data[0].embedding

    def embed_documents(self, texts: List[str]) -> List[List[float]]:
        """
        生成输入文本列表的 embedding.

        Args:
            texts (List[str]): 要生成 embedding 的文本列表.

        Returns:
            List[List[float]]: 输入列表中每个文档的 embedding 列表。每个 embedding 都表示为一个浮点值列表。
        """
        return [self.embed_query(text) for text in texts]

# 数据切分
from langchain.text_splitter import RecursiveCharacterTextSplitter


# 切分文档
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200, chunk_overlap=50,
        length_function=len,
    is_separator_regex=False,
    )



# 遍历所有文档
all_text = ""
for doc in txt + pdf + md:
    content = doc.page_content
    # 将文档的 page_content 属性连接到 all_text 中
    all_text += content

split_docs = text_splitter.create_documents([all_text])

print(split_docs,len(split_docs))

# 对文本数据进行向量化,这里作者使用自己封装的Mistral Embedding
embeddings_model = MistralAIEmbeddings()

# 构建向量数据框
db = FAISS.from_documents(split_docs, embeddings_model)

# # 保存向量数据库到本地
db.save_local("./db/GIS_db")


# 使用向量数据库进行检索

# 加载向量数据库
loaded_db = FAISS.load_local("./db/GIS_db", embeddings_model, allow_dangerous_deserialization=True)


# 计算相似度并检索最相似的文档
query = "电磁辐射"
docs = loaded_db.similarity_search(query, k=3) # 相似度最高的前3个chunk

# 输出检索结果
for doc in docs:
    print(doc.page_content+"\n-----------------\n")

文章参考

  • OpenAI官方文档
  • DeepSeek官方文档
  • Mistral官方文档
  • ChatGLM官方文档

项目地址

  • Github地址
  • 拓展阅读
  • 专栏文章

thank_watch

如果觉得我的文章对您有帮助,三连+关注便是对我创作的最大鼓励!或者一个star🌟也可以😂.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1866768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

观测云 VS 开源自建

观测云是一款面向全技术栈的监控观测一体化产品方案,具备强大而丰富的功能,目标是帮助最终用户提升监控观测的能力,化繁为简,轻松的构建起完整的监控观测体系。同时能够帮助整个企业的开发技术团队从统一的观测能力上获得完整的收…

机器学习辅助的乙醇浓度检测(毕设节选)

目录 1.为什么要机器学习 2. 神经网络一般组成 3.BP神经网络工作过程 4.评价指标 5.实操代码 1.为什么要用机器学习 人工分析大量的谐振模式,建立各种WGM的响应与未知目标之间的关系,是一个很大的挑战。机器学习(ML)能够自行识别全谱的全部特征。作为…

linux高级编程(进程)(1)

进程: 进程的含义? 进程是一个程序执行的过程,会去分配内存资源,cpu的调度 进程分类: 1、交互式进程 2、批处理进程 shell脚本 3、 守护进程 进程与程序的区别: 1)程序是…

古文字识别笔记

前置知识 部件:大部分的汉字是由若干组笔画结构拼合而成的,这些相对独立的笔画结构称为「部件」。 部件是大于基本笔画(例如:点、横、撇、捺等)而小于或等同于 偏旁 的结构单位。 例如「测」字有三个部件:…

Qt开发 | Qt控件 | QTabWidget基本用法 | QListWidget应用详解 | QScrollArea应用详解

文章目录 一、QTabWidget基本用法二、QListWidget应用详解1.列表模式1.1 基本操作1.2 添加自定义item1.3 如何添加右键菜单1.4 QListWidget如何删除item 2.图标模式 三、QScrollArea应用详解 一、QTabWidget基本用法 QTabWidget 是 Qt 框架中的一个类,它提供了一个选…

微深节能 堆取料机动作综合检测系统 格雷母线

系统描述: 格雷母线高精度位移测量系统,包括格雷母线、天线箱、地址解码器、地址编码器四个部分组成。 格雷母线类似一把有刻度的尺子,沿着移动机车运行轨道方向上铺设,格雷母线长度可以根据工程需要长度来定制。天线箱类似指针&a…

企智汇软件:机电工程项目管理系统智能化管理,洞悉项目全貌!

在机电工程领域,项目管理的复杂性要求系统不仅要能够处理大量的数据和信息,还要能够提供实时的洞察和分析,以支持快速而明智的决策。企智汇机电工程项目管理系统正是为了满足这些需求而设计的,它通过一系列先进的功能,…

Vue-Ci搭建项目

项目创建 vue-cli 官方提供的一个脚手架,用于快速生成一个vue的项目模板;预先定义 好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个 骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速; 主要的功能 ● 统一的目录结构 ● 本地调试 热部署 ● 单元…

教师资格证考试笔试报名流程

文章目录 前言笔试报名流程一、登录官网二、选择报考省份三、注册报名账号四、确认考试承诺五、填报个人信息六、上传个人照片七、查看个人信息八、笔试报名九、等待审核十、考试缴费最后(必看)附录1. 中小学教师资格考试网2. 广东省教资笔试报名通知&am…

ChatGPT在程序开发中的应用:提升生产力的秘密武器

在当今飞速发展的科技时代,程序开发已经成为许多企业和个人必不可少的技能。然而,编写代码并非总是顺风顺水,面对复杂的算法、繁琐的调试、持续不断的需求变更,程序员们常常感到压力山大。在这种情况下,ChatGPT应运而生…

像素流送技术,潜力巨大还是功能不足?

像素流送技术作为UE引擎自带的一款扩展性模块,尽管这项技术拥有一定的潜力,但与市场上成熟的云推流产品相比,它在配套功能方面仍显薄弱。 都有哪些具体表现呢? 一、维护和更新成本高 像素流送云推流需要开发团队在研发阶段投入…

五子棋纯python手写,需要的拿去

import pygame,sys from pygame import * pygame.init()game pygame.display.set_mode((600,600)) gameover False circlebox [] # 棋盘坐标点存储 box [] def xy():for x in range(0,800//40): for y in range(0,800//40): box.append((x*40,y*40)) xy() defaultColor wh…

周转车配料拣货方案

根据周转车安装的电子标签,被悬挂的扫码器扫到墨水屏显示的二维码,投屏发送配料拣货的数据。 方便快捷分拣物料

未来出行新选择——加油宝APP,让您的每一次加油都充满智慧与便捷!

一、前言 随着科技的飞速发展,智能手机已经成为我们生活中不可或缺的一部分。为了满足广大车主对便捷、高效加油服务的需求,我们倾力打造了全新的加油宝APP。这款APP不仅为您提供一站式的加油服务,还融合了多项创新功能,让您的出…

低碳短视频:成都柏煜文化传媒有限公司

低碳短视频:绿色传播的新风尚 随着全球气候变化和环境问题日益严峻,低碳生活已经成为人们追求的新风尚。在这个背景下,低碳短视频应运而生,以其独特的方式传播绿色理念,推动低碳生活方式的普及。成都柏煜文化传媒有限…

nextjs-创建layouts共用UI和独立pages页面

原文链接:https://nextjs.org/learn/dashboard-app/creating-layouts-and-pages 01-nextjs起步02-css样式03-处理字体和图片05-页面之间的导航跳转更多 到目前为止,您的应用程序只有一个主页。让我们学习如何使用布局和页面创建更多路由。 本章目标 …

智慧芯片,点亮未来——免费可视化大屏模板

一、什么是智慧芯片可视化大屏? 智慧芯片可视化大屏,集成了先进芯片技术和可视化技术。它不仅能够实时处理海量数据,还能以直观、生动的图像形式展示给用户,让复杂的数据变得一目了然。如图: 山海鲸可视化智慧芯片大屏…

实战|记一次java协同办公OA系统源码审计

前言 因为笔者也是代码审计初学者,写得不好的地方请见谅。该文章是以项目实战角度出发,希望能给大家带来启发。 审计过程 审计思路 1、拿到一个项目首先要看它使用了什么技术框架,是使用了ssh框架,还是使用了ssm框架&#xff…

阿里提出MS-Diffusion:一键合成你喜爱的所有图像元素,个性化生成新思路!

文本到图像生成模型的最新进展极大地增强了从文本提示生成照片级逼真图像的能力,从而增加了人们对个性化文本到图像应用的兴趣,尤其是在多主题场景中。然而,这些进步受到两个主要挑战的阻碍: 需要根据文本描述准确维护每个参考主题…

黑马程序员Java基础学习,涉及精细知识点复习【持续更新】

文章目录 01java基础java基础面向对象1.类:2.成员变量:类中方法外的变量,不能赋值3.成员方法:4.java内存分配:5.成员变量有初始值,局部变量没有初始值。6.this关键字:7.封装:8.构造方…