【AI大模型应用开发】【基础】6.LLM的提示词工程应用-金融行业动态方向评估项目

一.金融行业动态方向评估任务介绍

1. 项目背景介绍

当前金融领域数据大量激增,如何从繁杂的数据中获取有效的信息,进 而帮助投资者或者研究者减少决策失误带来的损失,成为金融数据分析 方法研究的热门话题

随着科技的进步,人工智能技术在各行业中的应用越来越广泛,而金融领域也不例外。人工智能技术的应用可以为金融企业提供更高效、精准的服务,也可以帮助投资者更好的地进行投资决策
AI在金融行业的应用:

  • 风险评估: 通过AI技术识别出不同金融风险事件类型、反欺诈 行为、信用评分低等风险,提高贷款的准确性和风险控制能力

  • 投资决策:通过AI技术对历史数据、 财务报表等信息分析,为投资者提供精准的投资决 策支持

  • 客户服务:通过AI技术,实现智能客服等功能,进而为客户提供便捷而又准确的答案
    AI应用方式:

  • 传统应用方式:

    • 需要专业的AI技术加持如:NLP、CV、 机器学习等知识
    • 专业的算法人员去分析数据、训练模型从实现预测分析
    • 非专业人员来说要想实现上述应用,可谓是举步维艰
  • 大模型应用方式:

    • 伴随着ChatGPT等大模型问世,使得非专业人员实现上述应用成为可能
    • 不需要特别专业的算法知识,就可以利用大模型来实现金融领域的应用
    • 设计合理的Prompt来挖掘大模型的涌现能力

因此,本项目主要基于大模型来直接实现在金融领域相关任务的应用,重点在于如何对大模型设计prompt,从而激发大模型的"涌现能力",进而给出准确的答案

2. 项目任务与方法介绍

业务场景:

  • **金融文本分类:**如:帮助机构识别和评估不同类型的风险,包括信用风险、市场风险、操 作风险等
  • 金融文本信息抽取 : 如:自动识别和提取金融文本中的关键信息,如实体名称、日期、 金额等,可以大大提高数据处理的效率,减少人工处理的工作量
  • 金融文本匹配: 如:信息检索和过滤,匹配相关的金融信息文本,提供准确的搜索结果
    方法选择:


注意:本项目主要以金融领域的文本进行分析,但是该思想同样适用于其他场景数据

3. Few-shot 、Zero-shot 思想回顾

Zero-Shot

Zero-shot(Zero-shot Learning) 是指在训练阶段不存在与测试阶段完全相同的类别 ,但是模型可以使用训练过的知识来推广到测试集中的新类别上 。这种能力被称为"零样本"学习,因为模型在训练时从未见过测试集中的新类别.

举例说明:

有一天,小明和爸爸一块去动物园,看到了马,然后爸爸告诉他,这就是马;之后,又看到了老虎,告诉 他:"看,这种身上有条纹的动物就是老虎。";最后,又带他去看了熊猫,对他说:"你看这熊猫是黑 白色的。"然后,爸爸给小明提了个问题,让他在动物园里找一种他从没见过的动物,叫斑马,并告诉了 小明有关于斑马的信息:"斑马有着马的轮廓,身上有像老虎一样的条纹,而且它像熊猫一样是黑白色 的。"最后,小明根据爸爸的提示,在动物园里找到了斑马(意料之中的结局。。。)
上述例子中展示了一个人类的推理过程,就是利用过去的知识(马,老虎,熊猫和斑马的描述),在脑海中推理出新对象的具体形态,从而能对新对象进行辨认。(如下图所示)ZSL就是希望能够模仿人类的这个推理过程,使得计算机具有识别新事物的能力

模型在zero-shot情况下如果需要准确识别出斑马,需要知道的信息是马的样本、老虎的样本、熊猫的样本和样本的标签,以及关于前三种动物和斑马的描述
训练和预测基本流程:

Few-Shot

Few-shot 学习 ( Few-shot Learning) 是指少样本学习,当模型在学习了一定类别的大量数据后,对于新的类别, 只需要少量的样本就能快速学习(目的:让机器自己学会学习)

Few-shot 学习的核心:

**用足够大的数据,来训练一个大模型,**训练的目的,不是让模型单纯的识别大象/老虎等。 而是让模型学会事物的异同,然后根据support set, 就可以知道quey是什么,尽管训练集中没有水獭类别

4. 项目环境配置

环境准备

  • python 版本: python 版本
  • 依赖包requirments:
    • protobuf>=3.19.5,<3.20.1
    • transformers>=4.26.1
    • icetk
    • cpm_kernels
    • streamlit==1.17.0

二.LLM实现金融文本分类

1. 分类任务介绍

下面几段文本来自某平台发布的金融领域文本:

复制代码
1."今日,央行发布公告宣布降低利率, 以刺激经济增长。这一降息举措将影响贷款利率,并在未来几个季度内对金融市场   产生影响。"
2."本公司宣布成功收购一家在创新科技领域领先的公司,这一战略性收购将有助于公司拓展技术能力和加速产品研发。"
3."公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩张提供了坚实的财务基础。"
4."最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,投资者应该关注这一领域的投资机会"

目的是期望模型能够帮助我们识别出这4段话中每一句话描述的是一个什么类型的报告

期望模型输出的结果为: '**新闻报道', '公司公告', '财务公告', '分析师报告'**

2. Prompt设计

对于大模型来讲,prompt的设计非常重要,一个 `明确` 的 prompt能够帮助我们更好从大模型中获得我们想要的结果

设计要点:

  • 向模型解释什么叫作**「文本分类任务」**

  • 需要让模型按照我们****指定的格式输出
    为了让模型知道什么叫做**「文本分类」**,借用In Context Learning (ICL)的方式,先给模型展示几个正确的例子:

  • User : "今日,股市经历了一轮震荡,受到宏观经济数据和全球贸易紧张局势的影响。投资者密切关注美联储可能的政策调整, 以适应市场的不确定性。" 是'新闻报道', '公司公告', '财务公告 '分析师报告'里的什么类别?

  • Bot : 新闻报道

  • User : "本公司年度财务报告显示,去年公司实现了稳步增长的盈利,同时资产负债表呈现强劲的状况。经济环境的稳定和 管理层的有效战略执行为公司的健康发展奠定了基础。"是'新闻报道', '公司公告', '财务公告 '分析师报告'里的什么 类别?

  • Bot : 财务报告

其中,`User` 代表我们输入给模型的句子,`Bot` 代表模型的回复内容, 注意:上述例子中 `Bot` 的部分也是由人工输入的,其目的是希望看到在看到类似 `User` 中的句子时,模型应当做出类似 `Bot` 的回答

3. 代码实现

这里使用的模型为**ChatGLM-6B**,参数参数较大(6B),下载到本地大概需要 12G+ 的磁盘空间,请确保磁盘有充足的空间。此外,加载模型大概需要13G 左右的显存,如果显存不够,可以进行模型量化加载以缩小模型成本

本次分类任务实现的主要过程:

  • **构造prompt:**对应init_prompts()函数
  • **实现模型预测:**对应inference()函数

代码如下:

python 复制代码
# ---*-coding:utf-8-*-
"""
利用 LLM 进行文本分类任务。
"""
# rich第三方库,用来美化终端输出结果
# from pprint import pprint
from rich import print
from rich.console import Console
import ollama

# 提供所有类别以及每个类别下的样例
class_examples = {
    '新闻报道': '今日,股市经历了一轮震荡,受到宏观经济数据和全球贸易紧张局势的影响。投资者密切关注美联储可能的政策调整,以适应市场的不确定性。',
    '财务报告': '本公司年度财务报告显示,去年公司实现了稳步增长的盈利,同时资产负债表呈现强劲的状况。经济环境的稳定和管理层的有效战略执行为公司的健康发展奠定了基础。',
    '公司公告': '本公司高兴地宣布成功完成最新一轮并购交易,收购了一家在人工智能领域领先的公司。这一战略举措将有助于扩大我们的业务领域,提高市场竞争力',
    '分析师报告': '最新的行业分析报告指出,科技公司的创新将成为未来增长的主要推动力。云计算、人工智能和数字化转型被认为是引领行业发展的关键因素,投资者应关注这些趋势'}


def init_prompts():
    """
    初始化前置prompt,便于模型做 incontext learning
    """
    class_list = list(class_examples.keys())
    pre_history = [
        {
            "role": "system",
            "content": f"现在你是一个文本分类器,你需要按照要求将我给你的句子分类到:{class_list}类别中。"
        },
    ]

    for _type, exmpale in class_examples.items():
        # print(f'"{exmpale}"是 {class_list} 里的什么类别?')
        pre_history.append({"role": "user", "content": f'"{exmpale}"是 {class_list} 里的什么类别?'})
        pre_history.append({"role": "assistant", "content": _type})

    return {'class_list': class_list, 'pre_history': pre_history}


def inference(sentences: list, custom_settings: dict):
    """
    推理函数
    Args:
        sentences (List[str]): 待推理的句子。
        custom_settings (dict): 初始设定,包含人为给定的 few-shot example。
    """

    for sentence in sentences:
        with console.status("[bold bright_green] Model Inference..."):
            sentence_with_prompt = f""{sentence}"是 {custom_settings['class_list']} 里的什么类别?"
            # print(f'sentence_with_prompt--》{sentence_with_prompt}')
            # break
            response = ollama.chat(
                model='qwen2.5:7b',
                messages=[
                    *custom_settings['pre_history'],
                    {"role": 'user', "content": sentence_with_prompt}
                ])
            response = response["message"]["content"]
        print(f'>>> [bold bright_red]sentence: {sentence}')
        print(f'>>> [bold bright_green]inference answer: {response}')
        print('*' * 80)


if __name__ == '__main__':
    console = Console()

    sentences = [
        "今日,央行发布公告宣布降低利率,以刺激经济增长。这一降息举措将影响贷款利率,并在未来几个季度内对金融市场产生影响。",
        "本公司宣布成功收购一家在创新科技领域领先的公司,这一战略性收购将有助于公司拓展技术能力和加速产品研发。",
        "公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩张提供了坚实的财务基础。",
        "最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,投资者应该关注这一领域的投资机会",
    ]

    custom_settings = init_prompts()
    # print(custom_settings)
    #
    inference(sentences, custom_settings)

4. 结果展示

python 复制代码
>>> sentence: 
今日,央行发布公告宣布降低利率,以刺激经济增长。这一降息举措将影响贷款利率,并
在未来几个季度内对金融市场产生影响。
>>> inference answer: 新闻报道
*******************************************************************************

>>> sentence: 
本公司宣布成功收购一家在创新科技领域领先的公司,这一战略性收购将有助于公司拓展
技术能力和加速产品研发。
>>> inference answer: 公司公告
*******************************************************************************

>>> sentence: 
公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩张提供了坚实的
财务基础。
>>> inference answer: 财务报告
*******************************************************************************

>>> sentence: 
最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,投资者应该关注
这一领域的投资机会
>>> inference answer: 分析师报告
*******************************************************************************

三.LLM实现金融文本信息抽取

1. 信息抽取任务介绍

首先,定义信息抽取的Schema:

python 复制代码
# 定义不同实体信息
schema = {'金融' :  [' 日期', '股票名称', '开盘价', '收盘价', '成交量']}

下面几段文本来自某平台发布的股票信息:

python 复制代码
1.'2023-02-15,寓意吉祥的节日,股票佰笃[BD]美股开盘价10美元,虽然经历了波动,但最终以13美元收盘,成交量微幅   增加至460,000,投资者情绪较为平稳。'
2.'2023-04-05,市场迎来轻松氛围,股票盘古(0021)开盘价23元,尽管经历了波动,但最终以26美元收盘,成交量缩小至   310,000,投资者保持观望态度。'

期望模型输出的结果为: 抽取出这2段话中的关键实体信息

2. Prompt设计

对于大模型来讲,prompt 的设计非常重要,一个 `明确` 的 prompt 能够帮助我们更好从大模型中获得我们想要的结果

设计要点:

  • 向模型解释什么叫作**「信息抽取任务」**

  • 让模型按照我们指定的格式**(json)输出**
    为了让模型知道什么叫做**「信息抽取**」,借用Incontext Learning的方式,先给模型展示几个正确的例子:

  • User : '2023-01-10,股市震荡。股票古哥-DEOOE美股今日开盘价100美元,一度飙升至105美元,随后回落至98美元,最 终以102美元收盘,成交量达到520000。'。提取上述句子中"金融 " (' 日期', '股票名称', '开盘价', '收盘价', '成交量 ')类型的实体,并按照JSON格式输出,上述句子中没有的信息用'原文中未提及'来表示,多个值之间用','分隔。

  • Bot : {' 日期' : '2023-01-10', '股票名称' : '古哥-D\[EOOE美股'], '开盘价' : '100美元', '收盘价' : '102美元 ', 成交量' : '520000'}

其中,`User`代表我们输入给模型的句子`Bot` 代表模型的回复内容, 注意:上述例子中 `Bot` 的部分也是由人工输入的,其目的是希望看到在看到类似 `User` 中的句子时,模型应当做出类似 `Bot` 的回答

3. 代码实现

这里使用的模型为ChatGLM-6B,参数参数较大(6B),下载到本地大概需要 12G+ 的磁盘空间,请确保磁盘有充足的空间。此外,加载模型大概需要13G左右的显存,如果显存不够,可以进行模型量化加载以缩小模型成本

本次抽取任务实现的主要过程:

  • **构造prompt:**对应init_prompts()函数
  • **模型结果后处理:**对应clean_response()函数
  • 进**行信息抽取:**对应inference()函数

代码如下:

python 复制代码
# coding:utf-8
import json
import ollama
import re

# from rich import print

# 定义不同类型下的实体类型
schema = {
    '金融': ['日期', '股票名称', '开盘价', '收盘价', '成交量']
}

IE_PATTERN = "{}\n\n提取上述句子中{}的实体,并按照JSON格式输出,上述句子中不存在的信息用['原文中未提及']来表示,多个值之间用','分隔。"

# 提供一些例子供模型参考
ie_examples = {
    '金融': [
        {
            'content': '2023-01-10,股市震荡。股票古哥-D[EOOE]美股今日开盘价100美元,一度飙升至105美元,随后回落至98美元,最终以102美元收盘,成交量达到520000。',
            'answers': {
                '日期': ['2023-01-10'],
                '股票名称': ['古哥-D[EOOE]美股'],
                '开盘价': ['100美元'],
                '收盘价': ['102美元'],
                '成交量': ['520000'],
            }
        }
    ]
}


def init_prompts():
    """
    初始化前置prompt,便于模型做 incontext learning。
    """

    ie_pre_history = [{"role": "system", "content": "你是一个信息抽取助手。"}, ]

    for _type, example_list in ie_examples.items():
        # print(f'_type-->{_type}')
        for example in example_list:
            # print(f'example-->{example}')
            sentence = example['content']
            # print(f'sentence--》{sentence}')
            properties_str = ', '.join(schema[_type])
            # print(f'properties_str--》{properties_str}')
            schema_str_list = f'"{_type}"({properties_str})'
            # print(f'schema_str_list-->{schema_str_list}')
            # print('*'*80)
            sentence_with_prompt = IE_PATTERN.format(sentence, schema_str_list)
            # print(f'sentence_with_prompt-->{sentence_with_prompt}')
            ie_pre_history.append({"role": "user", "content": f'{sentence_with_prompt}'})
            ie_pre_history.append(
                {"role": "assistant", "content": f"{json.dumps(example['answers'], ensure_ascii=False)}"})

    return {'ie_pre_history': ie_pre_history}


def clean_response(response: str):
    """
    后处理模型输出
    Args:
        response (str): _description_
    """
    # response1='```json["name":lucy]```abc```json["name":lucy]```'
    if '```json' in response:
        res = re.findall(r'```json(.*?)```', response, re.DOTALL)
        # print(f'res--》{res}')
        if len(res) and res[0]:
            response = res[0]
        response.replace('、', ',')
    try:
        return json.loads(response)
    except:
        return response


def inference(
        sentences: list,
        custom_settings: dict
):
    """
    推理函数
    Args:
        sentences (List[str]): 待抽取的句子。
        custom_settings (dict): 初始设定,包含人为给定的 few-shot example。
    """
    for sentence in sentences:
        cls_res = "金融"
        if cls_res not in schema:
            print(f'The type model inferenced {cls_res} which is not in schema dict, exited.')
            exit()
        properties_str = ', '.join(schema[cls_res])
        schema_str_list = f'"{cls_res}"({properties_str})'
        sentence_with_ie_prompt = IE_PATTERN.format(sentence, schema_str_list)
        # print(f'sentence_with_ie_prompt-->{sentence_with_ie_prompt}')
        # 使用 Ollama 调用 Qwen2.5:7b 模型
        messages = [*custom_settings['ie_pre_history'],
                    {"role": "user", "content": sentence_with_ie_prompt}]
        response = ollama.chat(
            model="qwen2.5:7b",
            messages=messages
        )

        res_content = response["message"]["content"]
        # print(f'res_content-->{res_content}')

        ie_res = clean_response(res_content)
        print(f'sentence: {sentence}')
        print(f'inference answer: {ie_res}')


if __name__ == '__main__':
    # 初始化句子和自定义设置
    sentences = [
        '2023-02-15,寓意吉祥的节日,股票佰笃[BD]美股开盘价10美元,虽然经历了波动,但最终以13美元收盘,成交量微幅增加至460,000,投资者情绪较为平稳。',
        '2023-04-05,市场迎来轻松氛围,股票盘古(0021)开盘价23元,尽管经历了波动,但最终以26美元收盘,成交量缩小至310,000,投资者保持观望态度。',
    ]

    # 初始化自定义设置
    custom_settings = init_prompts()
    # print(custom_settings)

    # # 开始推理
    inference(
        sentences,
        custom_settings
    )

4. 结果展示

python 复制代码
sentence: 2023-02-15,寓意吉祥的节日,股票佰笃[BD]美股开盘价10美元,虽然经历了波动,但最终以13美元收盘,成交量微幅增加至460,000,投资者情绪较为平稳。
inference answer: {'日期': ['2023-02-15'], '股票名称': ['佰笃[BD]美股'], '开盘价': ['10美元'], '收盘价': ['13美元'], '成交量': ['460,000']}
sentence: 2023-04-05,市场迎来轻松氛围,股票盘古(0021)开盘价23元,尽管经历了波动,但最终以26美元收盘,成交量缩小至310,000,投资者保持观望态度。
inference answer: {'日期': ['2023-04-05'], '股票名称': ['盘古(0021)'], '开盘价': ['23元'], '收盘价': ['26美元'], '成交量': ['310,000']}

四.LLM实现金融信息匹配

1. 文本匹配任务介绍

首先,构造几个短文本对:

python 复制代码
1. ('股票市场今日大涨,投资者乐观。', '持续上涨的市场让投资者感到满意。'),
2. ('油价大幅下跌,能源公司面临挑战。', '未来智能城市的建设趋势愈发明显。'),                                     3. ('利率上升,影响房地产市场。', '高利率对房地产有一定冲击。'),

期望模型能够帮我们识别出这 3 对句子中**,哪几对描述的是相似的语言**

期望模型输出的结果为**: '相似', '不相似', ' 相似'**

2. Prompt设计

对于大模型来讲,prompt 的设计非常重要,一个 `明确` 的 prompt 能够帮助我们更好从大模型中获得我们想 要的结果

设计要点:

  • 向模型解释什么叫作**「文本匹配任务」**
  • 让模型按照我们指定的格式输出

为了让模型知道什么叫做**「文本匹配」,借用Incontext Learning** 的方式,先给模型展示几个正确的例子:

  • User : 句子一 : 公司ABC发布了季度财报,显示盈利增长。\n句子二 : 财报披露,公司ABC利润上升
  • Bot : 是
  • User : 句子一 : 黄金价格下跌,投资者抛售。\n句子二 : 外汇市场交易额创下新高
  • Bot : 不是

其中,`User` 代表我们输入给模型的句子`Bot`代表模型的回复内容,注意:上述例子中 `Bot` 的部分也是由人工输入的,其目的是希望看到在看到类似 `User` 中的句子时,模型应当做出类似 `Bot` 的回答

3. 代码实现

这里使用的模型为ChatGLM-6B,参数参数较大(6B),下载到本地大概需要12G+ 的磁盘空间,请确保磁盘有充足的空间。此外,加载模型大概需要 13G 左右的显存,如显存不够,可以进行模型量化加载以缩小模型成本。

本次文本匹配任务实现的主要过程:

  • **构造prompt:**对应init_prompts()函数
  • 进行文本匹配:对应inference()函数

代码如下:

python 复制代码
# !/usr/bin/env python3
"""
利用 LLM 进行文本匹配任务。
"""
from rich import print
import ollama

# 提供相似,不相似的语义匹配例子
examples = {
    '是': [
        ('公司ABC发布了季度财报,显示盈利增长。', '财报披露,公司ABC利润上升。'),
    ],
    '不是': [
        ('黄金价格下跌,投资者抛售。', '外汇市场交易额创下新高。'),
        ('央行降息,刺激经济增长。', '新能源技术的创新。')
    ]
}


def init_prompts():
    """
    初始化前置prompt,便于模型做 incontext learning
    """
    pre_history = [
        {
            "role": "system",
            "content": "现在你需要帮助我完成文本匹配任务,当我给你两个句子时,你需要回答我这两句话语义是否相似。只需要回答是否相似,不要做多余的回答。"
        },
    ]
    for key, sentence_pairs in examples.items():
        for sentence_pair in sentence_pairs:
            sentence1, sentence2 = sentence_pair
            pre_history.append(
                {"role": "user", "content": f'句子一: {sentence1}\n句子二: {sentence2}\n上面两句话是相似的语义吗?'})
            pre_history.append({"role": "assistant", "content": key})

    return {'pre_history': pre_history}


def inference(
        sentence_pairs: list,
        custom_settings: dict
):
    """
    推理函数
    Args:
        model (transformers.AutoModel): Language Model 模型。
        sentence_pairs (List[str]): 待推理的句子对。
        custom_settings (dict): 初始设定,包含人为给定的 few-shot example。
    """
    for sentence_pair in sentence_pairs:
        sentence1, sentence2 = sentence_pair
        sentence_with_prompt = f'句子一: {sentence1}\n句子二: {sentence2}\n上面两句话是相似的语义吗?'
        response = ollama.chat(model="qwen2.5:7b",
                               messages=[*custom_settings["pre_history"],
                                         {"role": 'user', "content": sentence_with_prompt}])
        response = response["message"]["content"]
        print(f'sentence_pair:{sentence_pair}')
        print(f'inference answer: {response}')


if __name__ == '__main__':
    sentence_pairs = [
        ('股票市场今日大涨,投资者乐观。', '持续上涨的市场让投资者感到满意。'),
        ('油价大幅下跌,能源公司面临挑战。', '未来智能城市的建设趋势愈发明显。'),
        ('利率上升,影响房地产市场。', '高利率对房地产有一定冲击。'),
    ]

    custom_settings = init_prompts()
    # print(f'custom_settings-->{custom_settings}')
    inference(
        sentence_pairs,
        custom_settings
    )

4. 结果展示

python 复制代码
sentence_pair:('股票市场今日大涨,投资者乐观。', '持续上涨的市场让投资者感到满意。')
inference answer: 是

sentence_pair:('油价大幅下跌,能源公司面临挑战。', '未来智能城市的建设趋势愈发明显。')
inference answer: 不是

sentence_pair:('利率上升,影响房地产市场。', '高利率对房地产有一定冲击。')
inference answer: 是

【上一篇】【AI大模型应用开发】【基础】5.Prompt-Tuning方法入门&Prompt-Tuning方法进阶

【下一篇】