langchain+qwen1.5-7b-chat搭建本地RAG系统

已开源:https://github.com/stay-leave/enhance_llm

概念

检索增强生成(Retrieval Augmented Generation, RAG)是一种结合语言模型和信息检索的技术,用于生成更准确且与上下文相关的输出。

通用模型遇到的问题,也是RAG所擅长的:

  1. 知识的局限性: RAG 通过从知识库、数据库、企业内部数据等外部数据源中检索相关信息,将其注入到模型提示词中,使模型能够利用这些信息进行推理和生成。这弥补了模型仅基于公开训练数据的不足,让其在面对实时性、非公开或离线数据时也能提供相关内容。

  2. 幻觉问题: 由于语言模型的底层运作基于数学概率,其输出本质上是一系列数值运算结果,因此在模型知识匮乏或不擅长的领域,可能会出现幻觉问题,即生成与现实脱节或错误的内容。RAG通过将检索到的准确且相关的信息作为输入的一部分,降低了幻觉问题的发生几率,使模型生成的内容与现实更为一致。

  3. 数据安全性: 对于企业来说,数据安全至关重要,将私有数据上传到第三方平台进行训练存在风险。RAG 可以在企业内部构建知识库或数据库,并从中检索信息,将其注入模型提示词。这一过程可以在本地进行,无需将数据上传到外部,从而确保数据安全。

基本工作流程包括三个主要步骤:

1. 检索(Retrieve)

  • 输入查询: 用户通过输入查询或问题来开始这个流程。
  • 相似性搜索: 系统将用户查询通过嵌入模型转换为向量,并在外部知识源中的向量数据库中进行相似性搜索。
  • 返回相关信息: 搜索会返回与查询最接近的前 k 个数据对象(上下文信息),这些对象来自于知识库、数据库或其他数据源。

2. 增强(Augment):

  • 填入模板: 用户查询与检索到的上下文信息被填入到一个提示模板中。
  • 构建完整提示: 这个模板整合了查询和相关信息,构建出一个完整的提示词,用于指导模型生成。

3. 生成(Generate)

  • 输入到 LLM: 构建好的提示被输入到大型语言模型(LLM),比如 GPT 或 Qwen。
  • 生成内容: 模型根据提示词中的信息生成相关内容,包括回答、文本或其他输出。

RAG 的优势

  • 即时性: 通过检索外部信息源,RAG 能够即时更新模型的知识,让其对实时性、非公开或离线的数据也能提供有效回应。
  • 准确性: 注入的相关信息提升了模型输出的准确性,减少了幻觉问题。
  • 数据安全: 可以在内部构建知识库,从而确保敏感数据不会外泄。

在这里插入图片描述这是有道开源的QAnything,可以拿来直接用,使用MILVUS数据库和langchain。画的这个图很好。

可以根据这个图来分析RAG系统的构建流程:
part1:indexing
1.数据读取:各种类型的数据都可以读取。
2.文档切分:因为一篇文档可能很大,模型的上下文窗口有限;用户query的答案一般只会在文档的某部分。
3.向量嵌入:文本向量化。
4.向量入库:将稠密向量存入向量数据库,如mivlus, Chroma,Faiss。个人觉得第一个最好,但是个人配置太麻烦,选择第二个。
在这里插入图片描述

part2:Retrieval and generation
1.检索:根据query从向量数据库中检索相关文档。
2.prompt增强:将召回的文档填入prompt作为外部知识。
3.生成:LLM基于增强的prompt生成回复。

part1:indexing

索引阶段,包含数据读取,切片,向量化,入库。
langchain提供了开发文档:https://python.langchain.com/docs/modules/data_connection/document_transformers/

数据读取

使用langchain_community.document_loaders,读取PDF,CSV文件。

from langchain_community.document_loaders.csv_loader import CSVLoader
from langchain_community.document_loaders import PyPDFLoader


# 读取csv,返回list
def load_csv(path):
    # 每条记录为一个元素
    loader = CSVLoader(
        file_path="project_1/data/clean.csv",
        encoding='utf-8' # 编码
    )
    data = loader.load()
    return data 


# 读取pdf,返回list
def load_pdf(path):
    # 是以每页为一个元素的
    loader = PyPDFLoader("project_1/data/1.pdf")
    pages = loader.load_and_split()
    return pages

csv的返回,取前两个:
在这里插入图片描述pdf的返回,取第一个:
在这里插入图片描述

文档切块

这里使用RecursiveCharacterTextSplitter,通过特定字符来分割,默认的字符列表是 [“\n\n”, “\n”, " ", “”]
块的大小是指字符的数量。

from langchain_text_splitters import RecursiveCharacterTextSplitter


text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200, # 指定每个文本块的目标大小,这里设置为200个字符。
    chunk_overlap=50, # 指定文本块之间的重叠字符数,这里设置为50个字符。
    length_function=len, # 用于测量文本长度的函数,这里使用Python内置的`len`函数。
    is_separator_regex=False, # 指定`separators`中的分隔符是否应被视为正则表达式,这里设置为False,表示分隔符是字面字符。
    separators=["\n\n",  "\n",   " ",    ".",    ",",     ",",  "。", ] # 定义用于分割文本的分隔符列表。
)

pages = load_pdf("project_1/data/1.pdf")

texts = text_splitter.split_documents([pages[0].page_content])

返回也是一个list,可以看到两两之间是有重叠部分的。
在这里插入图片描述

向量化

使用BGE模型

from langchain.embeddings import HuggingFaceBgeEmbeddings
model_name = "project_2/bge-large-zh-v1.5"
model_kwargs = {'device': 'cuda'}
# # 当向量都被规范化(归一化)后,它们的范数都是1。
# 余弦相似度的计算只需要向量的点积运算,从而减少了计算的复杂度,加快了处理速度。
hf = HuggingFaceBgeEmbeddings(
    encode_kwargs = {'normalize_embeddings': True} 
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs,
    query_instruction="为这个句子生成表示以用于检索相关文章:"
)

embedding = hf.embed_query("你好!")
len(embedding)

存储嵌入

这里使用chroma数据库,参考:https://python.langchain.com/docs/integrations/vectorstores/chroma/

# 快速创建数据库
from langchain_chroma import Chroma
db = Chroma.from_documents(
   documents = texts, 
   embedding = hf,
   ids = None,
   collection_name = 'test1',
   collection_metadata = {"hnsw:space": "cosine"},
   persist_directory = 'project_1/chroma_db'
   )

# 相似度方法通过查询文本检索数据
query = "网络首发是什么"
docs = db.similarity_search(query)
print(docs[0].page_content)

检索出4个文本块
在这里插入图片描述

part2:Retrieval and generation

现在真正的RAG了

检索

可以直接将Chroma转换为检索器,会根据相关性分数返回

# 创建一个检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 10})

query = "萧炎是谁?"

# Get relevant documents ordered by relevance score
docs = retriever.invoke(query)

在这里插入图片描述

生成

将召回转为字符串,输入到prompt中,使用模型推理。

# 实例化自定义模型
llm = Qwen(mode_name_or_path = "/root/autodl-tmp/Qwen1.5-7B-Chat")

prompt = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)

output_parser = StrOutputParser()

# 构建 chain
chain = prompt | llm | output_parser

res = chain.invoke(
                    {
                    "context": format_docs(reordered_docs),
                    "question": "小医仙是谁?"
                    }
                    )

print(res)

直接用query进行召回
在这里插入图片描述Qwen1.5-7B-Chat的回复,看起来重点都抓住了。
在这里插入图片描述sft后的回答,训坏了
在这里插入图片描述dpo后的回答,怎么又拉起来了
在这里插入图片描述更新:
自己手动实现多轮对话
在这里插入图片描述
在这里插入图片描述使用dpo效果就是不错

在这里插入图片描述这个是sft的,训久了,过拟合。

在这里插入图片描述原始的模型结构,也不错。

在这里插入图片描述这个是4b的回答,参数小就是不行。

以上便是基础RAG的全流程,可以理解为两阶段的实现。

参考:
1.https://huggingface.co/docs/transformers/main_classes/text_generation
2.https://zhuanlan.zhihu.com/p/688926320
3.https://api.python.langchain.com/en/latest/vectorstores/langchain_community.vectorstores.chroma.Chroma.html#langchain_community.vectorstores.chroma.Chroma.from_documents
4.https://www.cnblogs.com/AlwaysSui/p/18144181
5.https://github.com/datawhalechina/self-llm/blob/master/Qwen1.5/02-Qwen1.5-7B-Chat%20%E6%8E%A5%E5%85%A5langchain%E6%90%AD%E5%BB%BA%E7%9F%A5%E8%AF%86%E5%BA%93%E5%8A%A9%E6%89%8B.md
6.https://python.langchain.com/docs/expression_language/get_started/

  • 11
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵海之森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值