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

news2024/9/19 10:46:43

【大模型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_dataset

cmrc = 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, SimpleDirectoryReader

def 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 SimpleNodeParser

def get_nodes_from_documents_by_chunk(documents):
    # 对文本进行chunk
    node_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-index
    else:
        # 构建向量索引vector-index
        if nodes is not None:
            index = VectorStoreIndex(
                nodes, # 构建的节点,如果没有进行chunk,则直接传入documents即可
                embed_model=Settings.embed_model # embedding model设置
            )
        else:
            index = VectorStoreIndex(
                documents, # 没有进行chunk,则直接传入documents
                embed_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).response
        logger.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" + prompt

    prompt = prompt + "<|im_start|>assistant\n"

    return prompt

def 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 defaultdict

def main():
    documents_path = '../data/cmrc-eval-zh.jsonl'
    store_path = '../store/'
    # 加载文档
    documents, qa_data_mp = get_documents_qa_data(documents_path)
    # 进行chunk
    nodes = 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链接

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

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

相关文章

【GitLab】使用 Docker 安装 GitLab 1:配置 SSH 端口

使用 Docker 安装 GitLab 要求修改ssh端口 GitLab 使用 SSH 通过 SSH 与 Git 交互。默认情况下,GitLab 使用端口22。 要在使用 GitLab Docker 映像时使用其他端口,您可以执行以下操作之一: 更改服务器的 SSH 端口(推荐)。 更改 GitLab Shell SSH 端口。 更改服务器的 SSH …

电测量数据交换DLMS∕COSEM组件第62部分:COSEM接口类(4)

1.7COSEM服务器模型 COSEM服务器被构建为3层体系结构如图3所示。 图4示例说明如何用COSEM服务器模型构建一台组合式计量设备。 1.8COSEM逻辑设备 1.8.1概述 COSEM逻辑设备包含一组COSEM对象,每个物理设备均应包含一个“Management logical device”。对COSEM逻辑设备…

MPLS相关实验

一、实验拓扑图以及实验要求 1、实验拓扑图 2、实验要求 合理利用IP地址进行分配R3、R4、R5、R6运行ospf在R2、R3、R4、R5、R6上运行MPLSR1上使用静态&#xff0c;R7上运行rip协议&#xff0c;R8上运行ospf协议全网可达 二、实验分析 合理利用IP地址进行分配R3、R4、R5、R6…

将自己的网站改造成可安装的PWA

概述 本文是一篇水文&#xff0c;感兴趣的读者可以看看。分享一下怎么讲自己的网站改造成可安装的PWA。 PWA简介 渐进式 Web 应用&#xff08;Progressive Web App&#xff0c;PWA&#xff09;是一个使用 web 平台技术构建的应用程序&#xff0c;但它提供的用户体验就像一个…

死锁问题分析和解决——资源回收时

1.描述问题 在完成线程池核心功能功能时&#xff0c;没有遇到太大的问题&#xff08;Any,Result,Semfore的设计&#xff09;&#xff0c;在做线程池资源回收时&#xff0c;遇到了死锁的问题 1、在ThreadPool的资源回收&#xff0c;等待线程池所有线程退出时&#xff…

2024年【浙江省安全员-C证】复审模拟考试及浙江省安全员-C证作业模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 浙江省安全员-C证复审模拟考试参考答案及浙江省安全员-C证考试试题解析是安全生产模拟考试一点通题库老师及浙江省安全员-C证操作证已考过的学员汇总&#xff0c;相对有效帮助浙江省安全员-C证作业模拟考试学员顺利通…

C++进阶(14)类型转换、IO流

文章目录 一、类型转换C语言隐式类型转换强制类型转换 C类型转换的情况类型转换的函数&#xff08;4个&#xff09; 二、IO流1、缓冲区2、提高输入输出效率3、文件操作文件权限写操作 --- ofstream文本方式写入二进制方式写入 读操作 --- ifstream文本方式读取二进制方式读取 其…

如何使用DEV-C++做游戏?

我的B站视频做过关于python的小游戏开发&#xff0c;但很多小伙伴希望通过C做一些有趣的动画或游戏&#xff0c;该怎么实现呢&#xff1f; B站&#xff1a;bigbigli_大李 大家好&#xff0c;我是大李。 今天主要跟大家谈谈如何通过C做一些小游戏开发&#xff0c;这里我们就 使用…

JavaScript - Api学习 Day02(事件监听、流、委托)

事件监听 一、事件监听1.1 什么是事件、事件监听1.1.1 事件监听的基本流程1.1.2 重点关注以下三个核心要素 1.2 Event handling models 事件处理模型1.2.1 DOM Level 版本&#xff08;1&#xff09;DOM Level 0&#xff08;2&#xff09;DOM Level 2 1.2.2 事件类型&#xff08…

iPad协议08算法

微信协议就是基于微信IPad协议的智能控制系统&#xff0c;利用人工智能AI技术、云计算技术、虚拟技术、边缘计算技术、大数据技术&#xff0c;打造出智能桌面系统RDS、 智能聊天系统ACS 、智能插 件系统PLUGIN 、云计算服务CCS 、任务管理系统TM、设备管理服务DM、 应用管理系统…

ECCV 2024亮点:APGCC技术刷新人群计数与定位的SOTA

摘要 在ECCV 2024会议上&#xff0c;一项名为APGCC的新技术以其卓越的性能引起了广泛关注。这项技术通过创新的方法&#xff0c;显著提高了人群计数和定位的准确性和鲁棒性&#xff0c;为监控、事件管理和城市规划等领域带来了新的解决方案。 正文&#xff1a; 随着城市化进…

ant design pro 技巧之实现列表页多标签

ant design pro 如何去保存颜色ant design pro v6 如何做好角色管理ant design 的 tree 如何作为角色中的权限选择之一ant design 的 tree 如何作为角色中的权限选择之二ant design pro access.ts 是如何控制多角色的权限的ant design pro 中用户的表单如何控制多个角色ant des…

全国大学生数学建模比赛——时间序列(详细解读)

全国大学生数学建模比赛中&#xff0c;时间序列分析是一种重要的方法。以下是对时间序列在该比赛中的详细解读&#xff1a; 一、时间序列的概念 时间序列是按时间顺序排列的一组数据。在数学建模中&#xff0c;时间序列数据通常反映了某个现象随时间的变化情况。例如&#xf…

编程中数据字典介绍

目录 第一章、快速了解数据字典1.1&#xff09;数据字典介绍1.2&#xff09;主动数据字典1.2.1&#xff09;主动数据字典对表字段的描述1.2.2&#xff09;主动数据字典对表索引的描述1.2.3&#xff09;主动数据字典对表外键的描述1.3&#xff09;被动数据字典1.4&#xff09;数…

golang实现windows获取加密盘符的总大小

golang实现windows获取加密盘符的总大小 package mainimport ("fmt""syscall""unsafe" )type PartitionStyle uint32const (IOCTL_DISK_GET_DRIVE_LAYOUT_EX 0x00070050FILE_DEVICE_MASS_STORAGE uint32 0x0000002dIOCTL_STOR…

【生物特征识别论文分享】基于深度学习的掌纹掌静脉识别

&#xff08;待更新&#xff09;基于深度学习的生物特征识别&#xff08;手掌静脉、手背静脉、手指静脉、掌纹、人脸等&#xff09;论文模型总结 。具体方法包括&#xff1a;基于特征表征、基于传统网络设计与优化、基于轻量级网络设计与优化、基于Transformer设计与优化、基于…

Isaac Sim仿真平台学习(2)基础知识

目录 0.前言 1.isaac sim的组建 1.Isaac Lab的资料 2.PhysX 3.RTX 4.Digital Twins 5.Replicator 6.Omnigraph 0.前言 难得更新阿&#xff0c;今天黑猴发布了没有去玩&#xff0c;跑来更新博客&#xff0c;本来想着按宇树的go2开发指南去试试RL的&#xff0c;但可惜没成…

39_WAF的概念、功能,ModSecurity部署配置、LAMP环境部署、Ubuntu搭建DVWA靶机测试、测试WAF防御、OWASP规则集的部署

一、WAF的概念 WAF&#xff08; Web Application Firewall &#xff09;&#xff0c;即Web应用防火墙 通过执行一系列HTTP/HTTPS&#xff08;应用层的协议&#xff09;的安全策略为Web应用提供保护的一种网络安全产品。增加攻击者的难度和成本&#xff0c;但不是100%安全。工…

XRAY~漏洞扫描工具

有人说&#xff1a;“所有的漏扫工具都是人写出来的&#xff0c;既然是工具&#xff0c;肯定没有人厉害&#xff0c;但是&#xff0c;漏扫可以大大减少你的工作量” 4、⭐ XRAY xray 是一款功能强大的安全评估工具&#xff0c;由多名经验丰富的一线安全从业者呕心打造而成&…

五、2 移位操作符赋值操作符

1、移位操作符 2、赋值操作符 “ ”赋值&#xff0c;“ ”判断是否相等 1&#xff09;连续赋值 2&#xff09;复合赋值符