来自工业界的知识库 RAG 服务(四),FinGLM 竞赛冠军项目详解

背景介绍

在 前一篇文章 中介绍过智谱组织的一个金融大模型 RAG 比赛 FinGLM 以及 ChatGLM反卷总局 团队的项目,这篇文章继续介绍下获得冠军的馒头科技的技术方案。

建议不了解比赛背景信息的可以先查看 来自工业界的知识库 RAG 服务(三),FinGLM 竞赛获奖项目详解,方便更好地理解技术方案的设计。

项目方案详解

方案设计

项目设计的整体流程如下所示:
请添加图片描述

从当前的流程来看,项目的几个核心预处理模块包含:

  1. 问题分类,不同类型的问题需要采取的方案完全不同,在选择处理方案时需要先确定是什么类型的问题,方便进行有针对性的处理;
  2. SQL 生成,部分问题需要依赖 SQL 从数据库中的数据处理得到结果,因此需要具备根据问题生成 SQL 语句的能力;
  3. 关键词抽取,就是流程图中的意图识别模块,项目依赖关键词进行内容召回,因此需要具备准确提取关键词的能力;

上面的几个核心功能都是基于微调 ChatGLM-6B 模型来实现的。

微调模型预处理

上面提到的几个预处理的功能都是通过微调大模型实现的。实际问题回答流程基本就是调用微调后的大模型进行回答,差异不是很大。下面以问题分类为例查看其中的预处理流程,实际的调用如下所示:

def _get_classify_prompt(self, question) -> str:
    classify_prompt = '''
    请问“{}”是属于下面哪个类别的问题?
    A: 公司基本信息,包含股票简称, 公司名称, 外文名称, 法定代表人, 注册地址, 办公地址, 公司网址网站, 电子信箱等.
    B: 公司员工信息,包含员工人数, 员工专业, 员工类别, 员工教育程度等.
    C: 财务报表相关内容, 包含资产负债表, 现金流量表, 利润表 中存在的字段, 包括费用, 资产,金额,收入等.
    D: 计算题,无法从年报中直接获得,需要根据计算公式获得, 包括增长率, 率, 比率, 比重,占比等.
    E: 统计题,需要从题目获取检索条件,在数据集/数据库中进行检索、过滤、排序后获得结果.
    F: 开放性问题,包括介绍情况,介绍方法,分析情况,分析影响,什么是XXX.
    你只需要回答字母编号, 不要回答字母编号及选项文本外的其他内容.
    '''.format(question)
    return classify_prompt

# 加载Classify训练权重后,来强化问题的分类能力,返回问题的类型字母编号
def classify(self, question: str):
    classify_prompt = self._get_classify_prompt(question)
    response, _ = self.model.chat(
        self.tokenizer,
        classify_prompt,
        history=[],
        max_length=cfg.CLASSIFY_PTUNING_PRE_SEQ_LEN,
        top_p=1, do_sample=False,
        temperature=0.001)
    return response

可以看到基于微调模型的方案进行问题分类,实现过程相对简单,只需要构造对应的 prompt 即可。

关键词抽取和 SQL 生成的流程也是类似的,在 SQL 生成中额外在 prompt 使用 Few Shot 机制优化了效果,有兴趣可以查看其中的细节内容。

文本召回与回答

在经过前面的预处理环节后,接下来就可以基于根据问题分类的类型召回对应的数据源,并根据召回的信息使用大模型生成最终的结果。

表格数据召回与回答

表格数据的召回与回答的流程如下所示:

请添加图片描述

这种情况下就是基于提取的关键词与数据表进行逐行比较,选择最接近的数据进行返回,最后构造出数据提供给大模型进行回答。

公式类问题数据召回与回答

公式类问题数据召回和回答的流程如下所示:

请添加图片描述

因为 ChatGLM-6B 实际进行数学计算时表现不佳,因此需要进行外部计算,根据匹配的关键词确定实际需要采取的计算公式,根据公式确定需要获取原始数据。使用原始的数据进行 Python 计算,之后将得到的结果提供给大模型输出最终的结果。

实际公式链的构造实现如下所示:

def get_step_questions(question, keywords, real_comp, year):
    new_question = question
    step_questions = []
    question_keywords = []
    variable_names = []
    step_years = []
    formula = None
    question_formula = None

    # 增长率问题链构造

    if '增长率' in question:
        if keywords == '增长率':
            keywords = new_question
        question_keywords = [keywords.replace('增长率', '')] * 2 + [keywords]
        variable_names = ['A', 'B', 'C']
        formula = '(A-B)/B'
        question_formula = '根据公式,=(-上年)/上年'
        for formula_key, formula_value in growth_formula():
            if formula_key in new_question.replace('的', ''):
                question_formula = '根据公式,{}={},'.format(formula_key, formula_value)
        step_years = [year, str(int(year)-1), year]
        step_questions.append(new_question.replace('增长率', ''))
        step_questions.append(new_question.replace('增长率', '').replace(year, str(int(year)-1)))
        step_questions.append(new_question)
    else:
        # 常规问题的问题链构造

        formulas = get_formulas()
        for k, v in formulas:
            if k in new_question:
                variable_names = get_keywords_of_formula(v)
                formula = v
                for name in variable_names:
                    if '人数' in question or '数量' in question or '人员' in question:
                        step_questions.append('{}年{}{}有多少人?如果已知信息没有提供, 你应该回答为0人。'.format(year, real_comp, name))
                    else:
                        step_questions.append('{}年{}的{}是多少元?'.format(year, real_comp, name))

                    question_keywords.append(name)
                    step_years.append(year)
                question_formula = '根据公式,{}={}'.format(k, v)
                break
    return step_questions, question_keywords, variable_names, step_years, formula, question_formula

增长率是一个特殊情况,因为增长率需要获取前一年的数据,因此需要特殊处理。除此以外,其他就是根据实际的公式确定计算所需的字段,之后就可以生成对应的问题。

实际计算的过程就是依次调用上面生成的公式链,通过先获取所需的原始数据,然后提取所需的字段的数值。处理完之后调用 Python 提供的 eval() 计算得到最终结果。实现过程如下所示:

for step_question, step_keyword, step_year in zip( step_questions, step_keywords, step_years):

    background = "已知{}{}年的资料如下:\n".format(
        real_comp, step_year
    )

    # 从表格中召回所需的原始数据

    matched_table_rows = recall_pdf_tables(
        step_keyword,
        [step_year],
        pdf_table,
        min_match_number=3,
        top_k=5,
    )

    table_text = table_to_text(
        real_comp,
        ori_question,
        matched_table_rows,
        with_year=False,
    )
    if table_text != "":
        background += table_text

    question_for_model = prompt_util.get_prompt_single_question(
        ori_question, real_comp, step_year
    ).format(background, step_question)
    # 调用大模型获取用于计算的基础数据

    step_answer = model(question_for_model)
    variable_value = type2.get_variable_value_from_answer(
        step_answer
    )
    if variable_value is not None:
        step_answers.append(step_answer)
        variable_values.append(variable_value)

# 将公式中的原始内容替换为计算得到的数据,执行数学计算

if len(step_questions) == len(variable_values):
    for name, value in zip(variable_names, variable_values):
        formula = formula.replace(name, value)

    result = eval(formula)

文本类问题数据召回与回答

文本类问题需要召回文本内容,并将召回的文本内容提供给大模型得到最终的结果,主要流程如下所示:

请添加图片描述

这种类型的问题比较类似常规的 RAG 系统中面临的问题,但是与常规的 RAG 系统基于向量检索不同,此项目使用的 BM25 检索,基于关键字进行检索。

项目检索是基于 fastbm25 实现,实现简化后如下所示:

text_pages = load_pdf_pages(key)
text_lines = list(itertools.chain(*[page.split('\n') for page in text_pages]))
text_lines = [line for line in text_lines if len(line) > 0]

# 基于原始文本初始化 fastbm25

model = fastbm25(text_lines)

# 使用问题中提取的关键词和原始问题分别进行检索

result_keywords = model.top_k_sentence(keywords, k=3)
result_question = model.top_k_sentence(anoy_question, k=3)

# 合并检索结果

top_match_indexes = [t[1] for t in result_question + result_keywords]
block_line_indexes = merge_idx(top_match_indexes, len(text_lines), 0, 30)

总结

本文主要整理了 FinGLM 获得冠军的团队提供的方案,从处理流程来看,亮点不是特别突出,最终表现良好与其精细的问题分类以及有针对性的微调模型应该有不少的关系。

结合目前梳理的几个获奖项目,存在如下一些值得参考的经验:

  1. 特定场景的大模型微调可以大幅提升效果,而且所需的数据量不大,可以在实际的场景下进行尝试;
  2. 关键词在需要精准匹配的情况下效果良好,可以与向量检索结合使用;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM学习记录(六)————串口的发送接收

文章目录 前言一、串口结构体及库函数二、实现串口发送(库函数)1.程序设计2.代码 三.串口接收1.串口接收(普通)2.串口中断接收3. 串口发送字符串函数4.串口实现printf(重定向)5. 串口实现scanf(…

五大维度大比拼:ChatGPT比较文心一言,你的AI助手选择指南

文章目录 一、评估AI助手的五个关键维度二、ChatGPT和文心一言的比较 评估AI助手的五个关键维度,以及ChatGPT和文心一言的比较如下: 一、评估AI助手的五个关键维度 界面友好性 : 评估标准:用户界面是否直观易用,是否…

详解 HBase 的架构和基本原理

一、基本架构 StoreFile:保存实际数据的物理文件,StoreFile 以 HFile 的格式 (KV) 存储在 HDFS 上。每个 Store 会有一个或多个 StoreFile(HFile),数据在每个 StoreFile 中都是有序的MemStore:写缓存&#…

Samba 服务器的搭建以及windows server 2008客户端的使用实验报告

一、 实验目的 通过 Samba 服务器的搭建,基本了解搭建服务器的基本步骤,理解 Samba 服务器的实现文件共享的功能,如何配置 Samba服务器配置文件等。 二、 实验环境 准备一台安装 centOS7系统的 Linux 虚拟机作为 Samba 服务器 server,准备…

手机ip地址怎么换成成都的

随着互联网的快速发展,我们越来越依赖于网络进行各种操作。而在某些情况下,为了更好地享受网络服务或保护个人隐私,我们可能需要改变手机的IP地址。本文将详细介绍如何将手机IP地址换成成都的,同时提醒大家在操作过程中需要注意的…

如何学习创建和使用 Java 归档(JAR)文件

1. 简介 JAR(Java ARchive)文件是一种用于打包多个Java类、资源文件和元数据的压缩文件格式。它在Java开发和发布过程中扮演着重要角色。通过使用JAR文件,开发者可以将应用程序的所有组件打包在一个文件中,方便分发和部署。 2. …

二次元资源汇总

获取更多资源,请关注公众号:阿宇的编程之旅,回复‘书签’获取 动漫网站 动漫世界 网站名称:动漫世界网址:nav.acgsq.com介绍:中国最大最权威的正版动漫网站,提供漫画、动画、资讯、论坛等全方…

一些激活函数

一些激活函数 摘要激活函数分类sigmoidTanhSoftsignSoftmaxReLUSoftplusNoisy ReLULeaky ReLUPReluELUSELUSwishGELUGLUGEGLUMishMaxout 摘要 本篇博客对一些激活函数进行总结,以便加深理解和记忆 激活函数分类 饱和激活函数:sigmoid、tanh… 非饱和激…

短链接生成器排名前三!长链接转化成短链接工具有哪些?

在现今的网络营销环境中,短链接的应用越来越广泛。它不仅能简化长链接,提高分享效果,还能提升企业品牌形象和用户体验。于是,市场上涌现出众多短链接生成工具。本文将为您揭秘短链接生成器排名前三的产品,帮您找到最适…

ABB工业喷涂机器人保养,轻松搞定!

小伙伴都知道机器人在长时间的使用下,难免遇到一些机械手故障。一旦发生了机器人故障,会影响整个生产线的作业,那么怎么才能做到防止机器人的故障率发生呢?定期的保养与维护显得尤为重要,一个好的维修保养服务商也很重…

yml配置文件快速上手

yml配置文件快速上手 springboot中,有三种文件可以作为配置文件 xml文件(不推荐,臃肿)application.propertis文件(层次不够分明)yml文件(推荐,层次分明,语法简洁) yml文件的基本语…

【递归、搜索与回溯】记忆化搜索

一、经验总结 以斐波那契数为例引入今天的主角:记忆化搜索和动态规划 题目链接 509. 斐波那契数 - 力扣(LeetCode) 题目描述 算法原理 编写代码 //解法二:递归->记忆化搜索 class Solution {int mem[31]; //备忘录 public…

揭秘未来:用线性回归模型预测一切的秘密武器!

线性回归模型 1. 引言2. 理论基础2.1 线性回归模型的定义与原理原理与关键假设模型参数估计 2.2 模型评估指标2.2.1 残差分析2.2.2 拟合优度指标2.2.3 统计检验 3. 应用场景3.1. 金融领域中的应用3.2. 医疗健康领域中的应用3.3. 其他领域的应用 4. 实例分析4.1、数据集选择4.2、…

目标检测算法YOLOv10简介

YOLOv10由Ao Wang等人于2024年提出,论文名为:《YOLOv10: Real-Time End-to-End Object Detection》,论文见:https://arxiv.org/pdf/2405.14458 ;源码见: https://github.com/THU-MIG/yolov10 以下内容主要来自论文&a…

Open To Buy(OTB)计划:零售业者的库存管理利器

在当今快速变化的服装市场中,如何高效、精准地进行商品管理成为了服装企业竞争的关键。OTB(Open-to-Buy)作为一种有效的商品管理方法,在企业管理中扮演着至关重要的角色。它基于预算、商品计划以及市场需求等多维度因素&#xff0…

《优化接口设计的思路》系列:第1篇—什么是接口缓存

一、缓存的定义: 缓存是一种存储数据的技术,用于提高数据访问的速度和效率。缓存通常存储在内存中,因为内存访问速度远快于磁盘和网络。数据接口通常会使用缓存技术,以降低对后端数据存储和处理的压力,提高系统性能。…

CSAPP -lecture01

##01COURSE OVERVIEW int or not intergers ,float and not reals that you need to understand what the system dose ,what make it run wll,what make it run poorly .in order to be able to do that kind of optimization

期货到底难在哪里?

第一难:使用杠杠,杠杠放大的其实是你性格、天赋和技能上的弱点,同时相应缩小你这三个方面的优点;第二难:双向交易。如果只能做多,理论上你每次交易将有50%的概率盈利。现在既能做多又能做空,只剩…

Semantic Kernel 直接调用本地大模型与阿里云灵积 DashScope

本文主要介绍如何在无需网关,无需配置 HttpClient 的情况下,使用 Semantic Kernel 直接调用本地大模型与阿里云灵积 DashScope 等 OpenAI 接口兼容的大模型服务。 1. 背景 一直以来,我们都在探索如何更好地利用大型语言模型(LLM&…

如何快速搭建产业数字化生态链?

如何快速搭建产业数字化生态链?这是当下许多企业都在思索的关键问题。 首先,要明确自身的核之心优势与定位,找到在数字化生态中的独特价值。 加强与产业链上下游企业的合作与协同,打破信息壁垒,实现资源共享与互补。 注…