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

news2024/12/29 9:25:04

背景介绍

在 前一篇文章 中介绍过智谱组织的一个金融大模型 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.coloradmin.cn/o/1830861.html

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

相关文章

[YOLOv10涨点改进:注意力魔改 | 轻量级的 Mixed Local Channel Attention (MLCA),加强通道信息和空间信息提取能力]

本文属于原创独家改进:一种轻量级的Mixed Local Channel Attention (MLCA)模块,该模块考虑通道信息和空间信息,并结合局部信息和全局信息以提高网络的表达效果 1.YOLOv10介绍 论文:[https://arxiv.org/pdf/2405.14458] 代码: https://gitcode.com/THU-MIG/yolov10?utm_s…

基于R-Tree的地理空间数据分析加速

几年前,我正在做一个业余项目。我想创建一个 Web 应用程序,推荐当地的特色景点,例如咖啡馆、书店或隐藏的酒吧。我的想法是在地图上显示用户触手可及的所有兴趣点。我的数据集中有数十万个点,我必须巧妙地过滤用户给定范围内的数据…

DVWA - Brute Force

DVWA - Brute Force 等级:low ​ 直接上bp弱口令爆破,设置变量,攻击类型最后一个,payload为用户名、密码简单列表 ​ 直接run,长度排序下,不一样的就是正确的用户名和密码 ​ 另解: 看一下…

3DMAX网格插入插件使用方法讲解

3DMAX网格插入插件使用方法 3DMAX网格插入插件,在选择的面上安门窗、打螺丝、挖洞、插入眼耳口鼻及其它网格模型等可以分分钟搞定!它通过将面选择替换为库中的资源来加快建模过程。非常适合硬网格和有机建模! 【适用版本】 3dMax2013及更高版…

快速欧氏聚类与普通欧氏聚类比较

1、前言 文献《FEC: Fast Euclidean Clustering for Point Cloud Segmentation》介绍了一种快速欧氏聚类方法,大概原理可以参考如下图,具体原理可以参考参考文献。 2、时间效率比较:快速欧氏聚类VS普通欧氏聚类 网上搜集的快速欧式聚类,与自己手写的普通欧式聚类进行对比,…

网络知识:这些特殊的IP地址,具体的用途你都知道吗

目录 一、0.0.0.0 二、255.255.255.255 限制广播地址 三、127.0.0.1 本机地址 四、224.0.0.1 组播地址 五、169.254.x.x 六、10.x.x.x、172.16。x。x~172.31。x。x、192.168。x。x 私有地址 对于计算机网络来说,IP地址是非常重要的概念&#xff0c…

Objective-C 学习笔记 | 协议(property)

Objective-C 学习笔记 | 协议(property) Objective-C 学习笔记 | 协议(property) Objective-C 学习笔记 | 协议(property) iOS 应用经常会用 UITableView 实例来显示数据,但是它本身不包含数据…

采集罗克韦尔AB、西门子等PLC数据发布成HTTP接口

智能网关IGT-DSER集成了多种PLC的原厂协议,方便实现各种PLC的原厂协议转换为HTTP协议的服务端,通过网关的参数配置软件绑定JSON文件的字段与PLC寄存器地址,即可接收来自客户端的GET、PUT和POST命令,解析和打包JSON文件(JSON文件格…

去哪儿网PMO张璐受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 去哪儿网PMO张璐女士受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾,演讲议题为“数字化助力组织目标落地”。大会将于6月29-30日在北京举办,敬请关注! 议题简要 本次议题将分享去哪儿流程标准化&工具化…

我用chatgpt写了一款程序

众所周知,Chatgpt能够帮助人们写代码,前几天苏音试着完全用Chatgpt写一款Python程序 有一句话我很赞同,未来能代替人的不是AI,是会使用AI的人。 最终,写下来效果还不错,完全提升了我的办公效率。 开发前…

告密者斯诺登:永远不要信任 OpenAI 或其 ChatGPT 等产品|TodayAI

为什么 OpenAI 变得越来越难以信任 OpenAI,一家以开发先进人工智能技术而闻名的公司,正面临越来越多的信任危机。近期,一些令人不安的迹象使人们对这家公司的透明度和安全性产生了质疑。 首先,在 OpenAI 的旧金山办公室外&#…

顺安蜘蛛池四川官网下载

baidu搜索:如何联系八爪鱼SEO? baidu搜索:如何联系八爪鱼SEO? baidu搜索:如何联系八爪鱼SEO? 虽然影视泛目录很火,但超度站群版本自出现以来-直流量稳定,可惜这两年起站全靠域名。但话说回来,咱不能否认,只要用的域名好,做啥泛目录都有好…

湖北省小学毕业学籍照片采集流程及教师手机拍摄方法说明

随着教育信息化的不断推进,学籍管理也越来越规范和便捷。湖北省小学毕业学籍照片采集作为学籍管理的重要组成部分,对于确保学生信息的准确性和完整性具有重要意义。本文将详细介绍湖北省小学毕业学籍照片采集的流程,并提供教师使用手机拍摄照…

如何用优盘加密自己的电脑:人离后自动锁定

看电影的时候,看到有人展示,用优盘加密自己的电脑,人走开的时候拔下优盘,自动上锁。似乎很科幻,其实这样的软件非常多,不论是成品商业用的还是免费的,都非常多,很多版权管理比较强的…

结合gin框架在沙箱环境下实现电脑网站支付和当面支付

文章目录 配置支付宝开放平台编写代码测试电脑网站支付当面扫码支付 配置支付宝开放平台 支付宝开放平台 点击链接,扫码进入后,点击沙箱: 点击沙箱应用,可以看到APPID,接口加签方式选择系统默认密钥就行&#xff0…

前端JS必用工具【js-tool-big-box】学习,打开全屏和关闭全屏

这一小节,我们说一下 js-tool-big-box 工具库中,打开全屏和关闭全屏的方法学习。 我们知道,浏览器想打开全屏,按一下 F11 键就可以了,但一来这个功能不一定所有使用的用户都知道,二来在一些例如大屏的需求…

前端实现获取后端返回的文件流并下载

前端实现获取后端返回的文件流并下载 方法一:使用Axios实现文件流下载优点缺点 方法二:使用封装的Request工具实现文件流下载优点缺点 方法三:直接通过URL跳转下载优点缺点 结论 在前端开发中,有时需要从后端获取文件流&#xff0…

【解决方案】数据采集工作站数据传不上去?

数据采集工作站扮演着至关重要的角色,它们负责收集、处理和传输各种传感器和设备的数据。然而,有时会遇到数据传输失败的问题。本文将详细探讨数据采集工作站数据传不上去的可能原因及其解决方案。(更多了解采集器设备可前往苏州稳联&#xf…

芯片封测从入门到精通

文章目录 📑前言一、作者简介二、书籍亮点三、内容简介四、适读人群 📑前言 在科技日新月异的今天,芯片作为现代电子设备的核心部件,其性能与可靠性直接决定了整个产品的竞争力。而芯片封测,作为确保芯片性能与可靠性…

闭包、内存泄漏、垃圾回收详解

首先要说清楚这个话题&#xff0c;必须要先清楚什么是垃圾回收&#xff0c;要清楚什么是垃圾回收呢&#xff0c;必须要知道什么是垃圾&#xff0c;所谓的垃圾就是不再需要的内存&#xff0c;需要或者不需要是由人为来决定的 <!DOCTYPE html> <html lang"en"…