搭建向量知识库
向量以及向量知识库
向量词与向量
词向量:是一种以单词为单位的将每个单词转化为实数向量的技术,这些实数可以被计算机更好的理解,如果是相近的理念或者相关的对象在向量空间中距离很近
词向量实际上将单词转化为固定的静态向量,在一定程度上捕获表达文本中的语义信息,但是忽略了单词在不同语境中的意思会受到影响这个事实,所以在RAG的时候一般使用通用文本向量,和词向量不同的是向量的量化单位是输入文本
RAG:(检索增强生成)
- 向量比文字更适合检索,如果数据库是文字,通过关键词方法找到对应的语义信息,通过计算问题的余弦相似度,欧氏距离,余弦距离,等指标获得问题与数据在语义层次上的相似度
- 向量比其他的媒体的综合信息能力更强,传统的文字,声音,图像,视频等很难构建关联,但是用向量可以将多种数据映射为统一的向量模式
搭建RAG系统,往往可以通过向量模型构造向量
- 使用各个公司的API接口
- 在本地使用向量模型将数据转化为向量
向量数据库
向量数据库用于高效的计算管理大向量数据的解决方法,主要是关注向量数据的特征和相似性。数据表示为向量形式,对数据的存储,处理和检索的算法处理效率大于传统的数据库
数据处理
数据读取
读取PDF,使用PyMuPDFLoader
from langchain_community.document_loaders import PyMuPDFLoader
# 创建一个 PyMuPDFLoader Class 实例,输入为待加载的 pdf 文档路径
loader = PyMuPDFLoader("../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf")
# 调用 PyMuPDFLoader Class 的函数 load 对 pdf 文件进行加载
pdf_pages = loader.load()
pdf_page = pdf_pages[1]
print(f"载入后的变量类型为:{type(pdf_pages)},", f"该 PDF 一共包含 {len(pdf_pages)} 页")
print(f"每一个元素的类型:{type(pdf_page)}.",
f"该文档的描述性数据:{pdf_page.metadata}",
f"查看该文档的内容:\n{pdf_page.page_content}",
sep="\n------\n")
读取MarkDown文件
from langchain_community.document_loaders.markdown import UnstructuredMarkdownLoader
loader = UnstructuredMarkdownLoader("../../data_base/knowledge_db/prompt_engineering/1. 简介 Introduction.md")
md_pages = loader.load()
print(f"载入后的变量类型为:{type(md_pages)},", f"该 Markdown 一共包含 {len(md_pages)} 页")
md_page = md_pages[0]
print(f"每一个元素的类型:{type(md_page)}.",
f"该文档的描述性数据:{md_page.metadata}",
f"查看该文档的内容:\n{md_page.page_content[0:][:200]}",
sep="\n------\n")
数据清洗
希望数据库的数据是有序的,优质的,精简的,可能需要将文本中的回车删除,使用正则匹配删除\n
import re
pattern = re.compile(r'[^\u4e00-\u9fff](\n)[^\u4e00-\u9fff]', re.DOTALL)
pdf_page.page_content = re.sub(pattern, lambda match: match.group(0).replace('\n', ''), pdf_page.page_content)
print(pdf_page.page_content)
# 删除其他的无意义的符号
pdf_page.page_content = pdf_page.page_content.replace('•', '')
pdf_page.page_content = pdf_page.page_content.replace(' ', '')
print(pdf_page.page_content)
文档分割
由于单个文档会超过模型支持的文本的上下文,导致检索的知识超出模型的处理能力,对文档进行分割,将文档长度按照固定的规则分割成若干的chunk,Langchain中文文本分割根据Chunk_size(块大小)和chunk_overlap进行分割
- chunk_size: 指每个块包含的字符和Token
- chunk_overlap 指两个块之间共享的字符数量,用于保持上下文的连贯性,避免分割丢失上下文
Langchain 提供多种文档分割方式,区别在怎么确定块与块之间的边界、块由哪些字符/token组成、以及如何测量块大小
- RecursiveCharacterTextSplitter(): 按字符串分割文本,递归地尝试按不同的分隔符进行分割文本。
- CharacterTextSplitter(): 按字符来分割文本。
- MarkdownHeaderTextSplitter(): 基于指定的标题来分割markdown 文件。
- TokenTextSplitter(): 按token来分割文本。
- SentenceTransformersTokenTextSplitter(): 按token来分割文本
- Language(): 用于 CPP、Python、Ruby、Markdown 等。
- NLTKTextSplitter(): 使用 NLTK(自然语言工具包)按句子分割文本。
- SpacyTextSplitter(): 使用 Spacy按句子的切割文本。
搭建使用向量数据库
前序配置
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.document_loaders import UnstructuredMarkdownLoader
# 遍历文件路径并把实例化的loader存放在loaders里
loaders = []
for file_path in file_paths:
file_type = file_path.split('.')[-1]
if file_type == 'pdf':
loaders.append(PyMuPDFLoader(file_path))
elif file_type == 'md':
loaders.append(UnstructuredMarkdownLoader(file_path))
# 下载文件并存储到text
texts = []
for loader in loaders: texts.extend(loader.load())
text = texts[1]
print(f"每一个元素的类型:{type(text)}.",
f"该文档的描述性数据:{text.metadata}",
f"查看该文档的内容:\n{text.page_content[0:]}",
sep="\n------\n")
构建Chrome数据库
向量检索
向量检索
sim_docs = vectordb.similarity_search(question,k=3)
print(f"检索到的内容数:{len(sim_docs)}")
for i, sim_doc in enumerate(sim_docs):
print(f"检索到的第{i}个内容: \n{sim_doc.page_content[:200]}", end="\n--------------\n")
最大边际相关性使得在保持相关性时候可以增加内容的丰富性
核心思想是在选择一个相关性高的文档之后,选择一个与选择的文档相关性低但是内容丰富的文档,增加多样性
mmr_docs = vectordb.max_marginal_relevance_search(question,k=3)
for i, sim_doc in enumerate(mmr_docs):
print(f"MMR 检索到的第{i}个内容: \n{sim_doc.page_content[:200]}", end="\n--------------\n")