大模型生成RAG评估数据集并计算hit_rate 和 mrr

news2024/10/6 22:33:06

文章目录

    • 背景
    • 简介
    • 代码实现
    • 公开
    • 参考资料

背景

最近在做RAG评估的实验,需要一个RAG问答对的评估数据集。在网上没有找到好用的,于是便打算自己构建一个数据集。

简介

本文使用大模型自动生成RAG 问答数据集。使用BM25关键词作为检索器,然后在问答数据集上评估该检索器的效果。
输入是一篇文本,使用llamaindex加载该文本,使用prompt让大模型针对输入的文本生成提问。
步骤如下:

  1. llamaindex 加载数据;
  2. 利用 chatglm3-6B 构建CustomLLM;
  3. 使用prompt和chatglm,结合文本生成对应的问题,构建RAG问答数据集;
  4. 使用BM25Retriever,构建基于关键词的检索器;
  5. 评估BM25Retriever在数据集上的hite_ratemrr结果;

由于在构建问答对时,让大模型结合文本生成对应的问题。笔者在测试时,发现关键词检索比向量检索效果要好

代码实现

导入包

from typing import List, Any

from llama_index.core import SimpleDirectoryReader

from llama_index.core.node_parser import SentenceWindowNodeParser
from llama_index.legacy.llms import (
    CustomLLM, CompletionResponse, CompletionResponseGen, LLMMetadata)
from llama_index.legacy.schema import NodeWithScore, QueryBundle, Node
from llama_index.core.base.base_retriever import BaseRetriever
from llama_index.legacy.retrievers import BM25Retriever
from llama_index.core.evaluation import RetrieverEvaluator
from llama_index.core.evaluation import (
    generate_question_context_pairs,
    EmbeddingQAFinetuneDataset,
)

加载数据,使用llamaindex网站的paul_graham_essay.txt

# Load data
documents = SimpleDirectoryReader(
    input_files=["data/paul_graham_essay.txt"]
).load_data()

# create the sentence window node parser w/ default settings
node_parser = SentenceWindowNodeParser.from_defaults(
    window_size=3,
    window_metadata_key="window",
    original_text_metadata_key="original_text",
)

# Extract nodes from documents
nodes = node_parser.get_nodes_from_documents(documents)

# by default, the node ids are set to random uuids. To ensure same id's per run, we manually set them.
for idx, node in enumerate(nodes):
    node.id_ = f"node_{idx}"

大模型加载
chatglm3-6B 使用half,显存占用12G

from modelscope import snapshot_download
from modelscope import AutoTokenizer, AutoModel

model_name = "chatglm3-6b"
model_path = snapshot_download('ZhipuAI/chatglm3-6b')
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).half().cuda()
model = model.eval()

本地自定义大模型

# set context window size
context_window = 2048
# set number of output tokens
num_output = 256


class ChatGML(CustomLLM):
    @property
    def metadata(self) -> LLMMetadata:
        """Get LLM metadata."""
        return LLMMetadata(
            context_window=context_window,
            num_output=num_output,
            model_name=model_name,
        )

    def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse:
        prompt_length = len(prompt)

        # only return newly generated tokens
        text, _ = model.chat(tokenizer, prompt, history=[])
        return CompletionResponse(text=text)

    def stream_complete(
            self, prompt: str, **kwargs: Any
    ) -> CompletionResponseGen:
        raise NotImplementedError()


llm_model = ChatGML()

生成RAG测试数据集

# Prompt to generate questions
qa_generate_prompt_tmpl = """\
Context information is below.

---------------------
{context_str}
---------------------

Given the context information and not prior knowledge.
generate only questions based on the below query.

You are a Professor. Your task is to setup \
{num_questions_per_chunk} questions for an upcoming \
quiz/examination. The questions should be diverse in nature \
across the document. The questions should not contain options, not start with Q1/ Q2. \
Restrict the questions to the context information provided.\
"""
# The questions should be solely based on the provided context information, and please pose them in Chinese.\
qa_dataset = generate_question_context_pairs(
    nodes,
    llm=llm_model,
    num_questions_per_chunk=2,
    qa_generate_prompt_tmpl=qa_generate_prompt_tmpl
)
qa_dataset.save_json("pg_eval_dataset.json")
# qa_dataset = EmbeddingQAFinetuneDataset.from_json("pg_eval_dataset.json")
import pandas as pd

def display_results(eval_results):
    """
        计算hit_rate和mrr的平均值
    """

    metric_dicts = []
    for eval_result in eval_results:
        metric_dict = eval_result.metric_vals_dict
        metric_dicts.append(metric_dict)

    full_df = pd.DataFrame(metric_dicts)

    hit_rate = full_df["hit_rate"].mean()
    mrr = full_df["mrr"].mean()

    metric_df = pd.DataFrame(
        {"hit_rate": [hit_rate], "mrr": [mrr]}
    )
    return metric_df
class JieRetriever(BM25Retriever, BaseRetriever):
    def _get_scored_nodes(self, query: str):
        tokenized_query = self._tokenizer(query)
        doc_scores = self.bm25.get_scores(tokenized_query)
        nodes = []
        for i, node in enumerate(self._nodes):
            node_new = Node.from_dict(node.to_dict())
            node_with_score = NodeWithScore(node=node_new, score=doc_scores[i])
            nodes.append(node_with_score)
        return nodes
    
    def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
        if query_bundle.custom_embedding_strs or query_bundle.embedding:
            logger.warning("BM25Retriever does not support embeddings, skipping...")

        scored_nodes = self._get_scored_nodes(query_bundle.query_str)

        # Sort and get top_k nodes, score range => 0..1, closer to 1 means more relevant
        nodes = sorted(scored_nodes, key=lambda x: x.score or 0.0, reverse=True)
        return nodes[:self._similarity_top_k]
retriever = JieRetriever.from_defaults(
# retriever = BM25Retrieve r.from_defaults(
                            nodes=nodes,
                            similarity_top_k=10
                        )

现在llamaindex在使用BM25Retrieve会报错,故笔者创建了JieRetriever,具体请点击查看链接

from llama_index.core.base.base_retriever import BaseRetriever

retriever_evaluator = RetrieverEvaluator.from_metric_names(
            ["mrr", "hit_rate"], retriever=retriever
        )
eval_results = await retriever_evaluator.aevaluate_dataset(qa_dataset)
for idx, item in enumerate(eval_results):
    if idx == 15:
        break
    d = item.metric_vals_dict
    mrr, hit_rate = d['mrr'], d['hit_rate']
    if mrr != 1 or hit_rate != 1:
        print(mrr, hit_rate, item.expected_ids, item.retrieved_ids)

下图展示了hit_rate 和 mrr 的计算:
在这里插入图片描述

结合下述结果,分析一下 hit_rate 和 mrr:

0.5 1.0 ['node_2'] ['node_71', 'node_2', 'node_0', 'node_199', 'node_126', 'node_419', 'node_446', 'node_218', 'node_1', 'node_70']
  • ['node_2'] 是 label
  • ['node_71', 'node_2', 'node_0', 'node_199', 'node_126', 'node_419', 'node_446', 'node_218', 'node_1', 'node_70'] 是检索器召回的候选列表;
  • mrr : 0.5;'node_2' 在候选列表的第二个位置,故mrr为 二分之一。在第几位就是几分之一;
  • hit_rate:代表label是否在候选集中,在就是1,不在就是0;
def display_results(eval_results):
    """
    	计算平均 hit_rate 和 mrr
    """

    metric_dicts = []
    for eval_result in eval_results:
        metric_dict = eval_result.metric_vals_dict
        metric_dicts.append(metric_dict)

    full_df = pd.DataFrame(metric_dicts)

    hit_rate = full_df["hit_rate"].mean()
    mrr = full_df["mrr"].mean()

    metric_df = pd.DataFrame(
        {"hit_rate": [hit_rate], "mrr": [mrr]}
    )
    return metric_df
display_results(eval_results)

在这里插入图片描述

公开

生成的评估数据集和相应示例代码,已上传到modelscope平台;

https://www.modelscope.cn/datasets/jieshenai/paul_graham_essay_rag/files

在这里插入图片描述

参考资料

  • https://www.llamaindex.ai/blog/boosting-rag-picking-the-best-embedding-reranker-models-42d079022e83

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

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

相关文章

网络编程核心概念解析:IP地址、端口号与网络字节序深度探讨

⭐小白苦学IT的博客主页 ⭐初学者必看:Linux操作系统入门 ⭐代码仓库:Linux代码仓库 ❤关注我一起讨论和学习Linux系统 本节重点 认识IP地址, 端口号, 网络字节序等网络编程中的基本概念; 1.前言 网络编程,作为现代信息社会中的一项核心技术&…

基于jsp+Spring boot+mybatis的图书管理系统设计和实现

基于jspSpring bootmybatis的图书管理系统设计和实现 博主介绍:多年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获…

kubesphere开启java服务

使用java:8作为基础镜像 1、创建持久化存储空间: 2、创建工作负载 (1)选择java镜像 (2)设置开启端口和启动命令(--spring.config.location为读取jar包外部的配置文件) (3&#xff…

Linux--进程(2)

目录 前言 1. 进程的状态 1.1 进程排队 1.2 运行,阻塞,挂起 2.Linux下具体的进程状态 2.1僵尸和孤儿 3.进程的优先级 4.Linux的调度与切换 前言 这篇继续来学习进程的其它知识 上篇文章:Linux--进程(1)-CS…

理解Three.js的相机

大家都知道我们生活中的相机,可以留下美好瞬间。那Three.js的相机是什么呢?Three.js创建的场景是三维的,而我们使用的显示器显然是二维的,相机就是抽象的定义了三维空间到二维显示器的投影方式。Three.js常见的相机有两类&#xf…

资源分享 | 解决你的算力烦恼,平台注册送算力

前言 最近趋动云在做活动,新用户注册即可送价值70元的算力金,做满新手任务最高可领300元的算力红包,趋动云中租卡的费用如下: 1张24G的显存的卡大概是2块钱一个小时,48G的是4块钱一个小时,300算力红包能用…

【Redis】详解 Redis

Redis是一种高性能的开源键值存储数据库,它支持各种数据结构,包括字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted se…

如何借助Idea创建多模块的SpringBoot项目

目录 1.1、前言1.2、开发环境1.3、项目多模块结构1.4、新建父工程1.5、创建子模块1.6、编辑父工程的pom.xml文件 1.1、前言 springmvc项目,一般会把项目分成多个包:controler、service、dao、utl等,但是随着项目的复杂性提高,想复用其他一个模…

蓝桥集训之斐波那契前n项和

蓝桥集训之斐波那契前n项和 核心思想&#xff1a;矩阵乘法 左边求和 右边求和 得到Sn fn2 – 1 因此只要求出fn2 即可 #include <iostream>#include <cstring>#include <algorithm>using namespace std;typedef long long LL;int n,m;int A[2][2] { …

论大数据服务化发展史

引言 一直想写一篇服务化相关的文章&#xff0c;那就别犹豫了现在就开始吧 正文 作为大数据基础架构工程师&#xff0c;业界也笑称“运维Boy”&#xff0c;日常工作就是在各个机器上部署以及维护服务&#xff0c;例如部署Hadoop、Kafka、Pulsar这些等等&#xff0c;用于给公…

使用python将作图并将局部放大

此程序主要特点&#xff1a; 1、使用python画实验结果图 2、想要对大图的局部进行放大 3、有两个子图 4、子图和原图的横坐标都使用标签而不是原始的数据 代码和注释如下&#xff1a; import pandas as pd import numpy as np import matplotlib.pyplot as plt import ope…

BCLinux-for-Euler配置本地yum源

稍微吐槽一句…… 在这片土地上&#xff0c;国产化软件的大潮正在滚滚而来&#xff0c;虽然都不是真正意义上的国产化&#xff0c;但是至少壳是国产的~~~ 之前使用的Centos7的系统&#xff0c;现在都要求统一换成BCLinux-for-Euler。说实话换了之后不太适应&#xff0c;好多用习…

COCO格式转YOLO格式训练

之前就转换过好几次&#xff0c;每次换设备训练&#xff0c;由于压缩包太大&#xff0c;u盘不够用。每次都要找教程从网上再下载一遍。因此这里记录一下&#xff0c;以免下次重新找教程。 在coco数据集中&#xff0c;coco2017train或coco2017val数据集中标注的目标(类别)位置在…

Spring 详细总结

文章目录 第一章 IOC容器第一节 Spring简介1、一家公司2、Spring旗下的众多项目3、Spring Framework①Spring Framework优良特性②Spring Framework五大功能模块 第二节 IOC容器概念1、普通容器①生活中的普通容器②程序中的普通容器 2、复杂容器①生活中的复杂容器②程序中的复…

传输层 --- UDP

目录 1. 传输层是什么呢&#xff1f; 2. 再谈端口号 2.1. 端口号是什么 2.2. 协议号是什么 2.3. 认识知名端口号 2.4. 端口号的相关问题 2.4.1. 一个进程可以绑定多个端口号吗&#xff1f; 2.4.2. 一个端口号可以被多个进程绑定吗&#xff1f; 2.4.3. 为什么不使用P…

数据结构进阶篇 之 【并归排序】(递归与非递归实现)详细讲解

都说贪小便宜吃大亏&#xff0c;但吃亏是福&#xff0c;那不就是贪小便宜吃大福了吗 一、并归排序 MergeSort 1.基本思想 2.实现原理 3.代码实现 4.归并排序的特性总结 二、非递归并归排序实现 三、完结撒❀ –❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀…

如何使用单片机 pwm 控制 mos 管?

目录 选择适合的硬件 连接电路 编写代码 参考示例 程序一 程序二 测试与调试 注意事项 使用单片机&#xff08;如常见的Arduino、STM32等&#xff09;通过PWM&#xff08;脉冲宽度调制&#xff09;控制MOS管&#xff08;金属氧化物半导体场效应管&#xff09;是一种常见…

Java中的集合(二)

一、回顾上期 上一篇讲到在Java中&#xff0c;集合和容器是非常重要的概念&#xff0c;用于存储和操作数据。在集合中&#xff0c;有单列集合和双列集合两种类型。我们在上一篇将单列集合中的list类讲完了&#xff0c;这一篇将会将集合中剩余部分介绍完&#xff0c;话不多说&am…

备战蓝桥杯---刷二分与前缀和题

刷点题~ 1.二分多路归并算法 对于每一个技能&#xff0c;我们把它看成一个等差数列&#xff0c;我们把所有可能都放到一个集合里&#xff0c;排个序&#xff0c;取前m个大即可&#xff0c;现在考虑优化&#xff0c;假如m不是很大&#xff0c;我们直接用优先队列即可&#xff0…

单细胞RNA测序(scRNA-seq)SRA数据下载及fastq-dumq数据拆分

单细胞RNA测序&#xff08;scRNA-seq&#xff09;入门可查看以下文章&#xff1a; 单细胞RNA测序&#xff08;scRNA-seq&#xff09;工作流程入门 单细胞RNA测序&#xff08;scRNA-seq&#xff09;细胞分离与扩增 1. NCBI查询scRNA-seq SRA数据 NCBI地址&#xff1a; https…