【大模型LLMs】RAG实战:基于LlamaIndex快速构建RAG链路(Qwen2-7B-Instruct+BGE Embedding)

本文主要是介绍【大模型LLMs】RAG实战:基于LlamaIndex快速构建RAG链路(Qwen2-7B-Instruct+BGE Embedding),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【大模型LLMs】RAG实战:基于LlamaIndex快速构建RAG链路(Qwen2-7B-Instruct+BGE Embedding)

  • 1. 环境准备
  • 2. 数据准备
  • 3. RAG框架构建
    • 3.1 数据读取 + 数据切块
    • 3.2 构建向量索引
    • 3.3 检索增强
    • 3.4 main函数
  • 参考

基于LlamaIndex框架,以Qwen2-7B-Instruct作为大模型底座,bge-base-zh-v1.5作为embedding模型,构建RAG基础链路。数据集选用cmrc2018数据集(该数据集禁止商用)

1. 环境准备

安装LlamaIndex的相关依赖

pip install llama-index
pip install llama-index-llms-huggingface
pip install llama-index-embeddings-huggingface
pip install datasets

从Hugging Face安装将要使用的LLMs以及embedding model,这里我们选择Qwen/Qwen2-7B-Instruct作为大模型底座,选择BAAI/bge-base-zh-v1.5作为embedding模型,用于对文档进行向量化表征
这里介绍快速下载huggingface模型的命令行方法:

1. 首先安装依赖
pip install -U huggingface_hub
pip install -U hf-transfer 
2. 设置环境变量(设置hf环境变量为1用于提升下载速度;设置镜像站地址)
export HF_HUB_ENABLE_HF_TRANSFER=1
export HF_ENDPOINT="https://hf-mirror.com"
3. 安装相应的模型(以Qwen2-7B-Instruct为例,前面是huggingface上的模型名,后面是本地下载的路径)
huggingface-cli download Qwen/Qwen2-7B-Instruct --local-dir ./Qwen2-7B-Instruct
huggingface-cli download BAAI/bge-base-zh-v1.5 --local-dir ./bge-base-zh-v1.5

2. 数据准备

使用开源数据集 cmrc2018 构建本地知识库,该数据集由人类专家在维基百科段落上标注的近2万个真实问题组成,包含上下文(可以用于构建本地知识库)、问题和答案

在这里插入图片描述

我们读取cmrc的train数据集,并将question,answer以及context存储到本地data目录下的cmrc-eval-zh.jsonl文件下,代码如下:

import json
from tqdm import tqdm
from datasets import load_datasetcmrc = load_dataset("cmrc2018")
with open("../data/cmrc-eval-zh.jsonl", "w") as f:for train_sample in tqdm(cmrc["train"]):qa_context_mp = {"question": train_sample["question"],"reference_answer": train_sample["answers"]["text"][0],"reference_context": train_sample["context"]}f.write(json.dumps(qa_context_mp, ensure_ascii=False) + "\n")

在这里插入图片描述

3. RAG框架构建

在具体构建rag链路之前,我们初始化一个日志记录logger,用于记录运行过程中的信息

import logging# 设置logger
logging.basicConfig(level=logging.DEBUG,filename='../out/output.log',  # 替换成保存日志的本地目录datefmt='%Y/%m/%d %H:%M:%S',format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s'
)
logger = logging.getLogger(__name__)

3.1 数据读取 + 数据切块

读取第二节中构建的cmrc-eval-zh.jsonl文件,将reference_context字段读取出来保存到list中作为后续知识库的文本,另外将question-answer pair进行保存,用于后续模型的推理。然后构建llama_index.core的Document对象。如果是本地的txt文档知识库,也可以直接使用SimpleDirectoryReader方法

from llama_index.core import Document, SimpleDirectoryReaderdef get_documents_qa_data(documents_path):# 遍历jsonl文件,读取reference_context、question、reference_answer字段text_list = []qa_data_mp = []with open(documents_path, 'r', encoding="utf-8") as f:for line in f:sample = json.loads(line)if sample["reference_context"] not in text_list:text_list.append(sample["reference_context"])qa_data_mp.append({"question": sample["question"],"reference_answer": sample["reference_answer"]})# 构建Document对象列表documents = [Document(text=t) for t in text_list]"""# 如果直接读取本地txt文档documents = SimpleDirectoryReader(input_files='xxx.txt').load_data()"""# 如果documents长度为0,则在日志中记录if len(documents) == 0:logger.warning('documents list length::: 0')logger.info('documents build successfully!')return documents, qa_data_mp

考虑到实际RAG应用场景中,很多文档的长度过长,一方面难以直接放入LLM的上下文中,另一方面在可能导致检索过程忽略一些相关的内容导致参考信息质量不够,一般会采用对文本进行chunk的操作,将一段长文本切分成多个小块,这些小块在LlamaIndex中表示为Node节点。上述过程代码如下,我们将documents传入到下面定义的函数中,通过SimpleNodeParser对文本进行切块,并设置chunk的size为1024

from llama_index.core.node_parser import SimpleNodeParserdef get_nodes_from_documents_by_chunk(documents):# 对文本进行chunknode_paeser = SimpleNodeParser.from_defaults(chunk_size=1024)# [node1,node2,...] 每个node的结构为{'Node_ID':xxx, 'Text':xxx}nodes = node_paeser.get_nodes_from_documents(documents=documents)# 如果nodes长度为0,则在日志中记录if len(nodes) == 0:logger.warning('nodes list length::: 0')logger.info('nodes build successfully!')return nodes

3.2 构建向量索引

在RAG的retrieval模块中,一般采用的检索方式有两种:

  • 基于文本匹配的检索(例如,bm25算法)
  • 基于向量相似度的检索(embedding+relevance computing,例如bge等模型)

本文对前面构建的node节点中的文本进行向量化表征(基于BAAI/bge-base-zh-v1.5),然后构建每个node的索引,这里的embedding模型我们在第一节环境准备过程中已经下载至本地目录
此外,在构建索引后,可以使用StorageContext将index存储到本地空间,后续调用get_vector_index时可以先判断本地是否有存储过storage_context,如果有则直接加载即可(通过load_index_from_storage),如果没有则通过传入的nodes参数再次构建向量索引

from llama_index.core import Settings, VectorStoreIndex, StorageContext, load_index_from_storage
from llama_index.embeddings.huggingface import HuggingFaceEmbedding# 设置embedding model(bge-base-zh-v1.5)
Settings.embed_model = HuggingFaceEmbedding(model_name = "../../../model/bge-base-zh-v1.5"
)def get_vector_index(nodes=None, documents=None, store_path='../store/', use_store=True):# 如果已经进行过持久化数据库的存储,则直接加载if use_store:storage_context = StorageContext.from_defaults(persist_dir=store_path)index = load_index_from_storage(storage_context)# 如果没有存储,则直接构建vectore-store-indexelse:# 构建向量索引vector-indexif nodes is not None:index = VectorStoreIndex(nodes, # 构建的节点,如果没有进行chunk,则直接传入documents即可embed_model=Settings.embed_model # embedding model设置)else:index = VectorStoreIndex(documents, # 没有进行chunk,则直接传入documentsembed_model=Settings.embed_model # embedding model设置)#进行持久化存储index.storage_context.persist(store_path)  logger.info('vector-index build successfully!\nindex stores in the path:::{}'.format(store_path))return index

构建好的index保存到本地’…/store/'目录下:

在这里插入图片描述

3.3 检索增强

接下来,我们在代码中设置将要使用的LLMs,本文选择通义千问的Qwen2-7B-Instruct模型,在第一节中也已经下载至本地,通过HuggingFaceLLM设置。(其他大模型的使用方式也是类似,如果是OpenAI的大模型则不使用该方式,此类教程很多,本文不在赘述)

下面的代码中,首先使用HuggingFaceLLM设置通义千问大模型;同时根据通义千问的官方文档中的LlamaIndex使用demo,完成messages_to_prompts和completion_to_prompt两个函数的设置(新起一个utils.py用于存放着两个函数)

from llama_index.core.llms import ChatMessage
from llama_index.llms.huggingface import HuggingFaceLLM
from utils import messages_to_prompt, completion_to_prompt# 设置llm(Qwen2-7B-Instruct)
# 设置llm(Qwen2-7B-Instruct)
Settings.llm = HuggingFaceLLM(model_name="../../../model/Qwen2-7B-Instruct",tokenizer_name="../../../model/Qwen2-7B-Instruct",context_window=30000,max_new_tokens=2000,generate_kwargs={"temperature": 0.7,"top_k": 50, "top_p": 0.95},messages_to_prompt=messages_to_prompt,completion_to_prompt=completion_to_prompt,device_map="auto"
)def get_llm_answer_with_rag(query, index, use_rag=True):if use_rag:query_engine = index.as_query_engine()resp = query_engine.query(query).responselogger.info('use rag, query:::{}, resp:::{}'.format(query, resp))else:resp = Settings.llm.chat(messages=[ChatMessage(content=query)])logger.info('not use rag, query:::{}, resp:::{}'.format(query, resp))return resp
# utils.py
def messages_to_prompt(messages):prompt = ""for message in messages:if message.role == "system":prompt += f"<|im_start|>system\n{message.content}<|im_end|>\n"elif message.role == "user":prompt += f"<|im_start|>user\n{message.content}<|im_end|>\n"elif message.role == "assistant":prompt += f"<|im_start|>assistant\n{message.content}<|im_end|>\n"if not prompt.startswith("<|im_start|>system"):prompt = "<|im_start|>system\n" + promptprompt = prompt + "<|im_start|>assistant\n"return promptdef completion_to_prompt(completion):return f"<|im_start|>system\n<|im_end|>\n<|im_start|>user\n{completion}<|im_end|>\n<|im_start|>assistant\n"

3.4 main函数

最后,我们在main函数里面讲前面的整个链路打通,同时我们也从cmrc-eval-zh.jsonl中读取qa对

from collections import defaultdictdef main():documents_path = '../data/cmrc-eval-zh.jsonl'store_path = '../store/'# 加载文档documents, qa_data_mp = get_documents_qa_data(documents_path)# 进行chunknodes = get_nodes_from_documents_by_chunk(documents)# 构建向量索引index = get_vector_index(nodes=nodes, store_path=store_path, use_store=False)# 获取检索增强的llm回复for qa_data in qa_data_mp:query = qa_data["question"]reference_answer = qa_data["reference_answer"]llm_resp = get_llm_answer_with_rag(query, index, use_rag=True)print("query::: {}".format(query))print("reference answer::: {}".format(reference_answer))print("answer::: {}".format(llm_resp))print("*"*100)if __name__ == "__main__":main()

运行后结果如下:

在这里插入图片描述

参考

  1. LlamaIndex官方文档
  2. Qwen官方文档
  3. 本项目Github链接

这篇关于【大模型LLMs】RAG实战:基于LlamaIndex快速构建RAG链路(Qwen2-7B-Instruct+BGE Embedding)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1091540

相关文章

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring AI Alibaba接入大模型时的依赖问题小结

《SpringAIAlibaba接入大模型时的依赖问题小结》文章介绍了如何在pom.xml文件中配置SpringAIAlibaba依赖,并提供了一个示例pom.xml文件,同时,建议将Maven仓... 目录(一)pom.XML文件:(二)application.yml配置文件(一)pom.xml文件:首

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper

Java实战之自助进行多张图片合成拼接

《Java实战之自助进行多张图片合成拼接》在当今数字化时代,图像处理技术在各个领域都发挥着至关重要的作用,本文为大家详细介绍了如何使用Java实现多张图片合成拼接,需要的可以了解下... 目录前言一、图片合成需求描述二、图片合成设计与实现1、编程语言2、基础数据准备3、图片合成流程4、图片合成实现三、总结前

如何在本地部署 DeepSeek Janus Pro 文生图大模型

《如何在本地部署DeepSeekJanusPro文生图大模型》DeepSeekJanusPro模型在本地成功部署,支持图片理解和文生图功能,通过Gradio界面进行交互,展示了其强大的多模态处... 目录什么是 Janus Pro1. 安装 conda2. 创建 python 虚拟环境3. 克隆 janus

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee

nginx-rtmp-module构建流媒体直播服务器实战指南

《nginx-rtmp-module构建流媒体直播服务器实战指南》本文主要介绍了nginx-rtmp-module构建流媒体直播服务器实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. RTMP协议介绍与应用RTMP协议的原理RTMP协议的应用RTMP与现代流媒体技术的关系2