基于Ollama + Qwen2.5 的金融NLP三大任务实战:文本分类、语义匹配与信息抽取

前言

在大模型时代,利用LLM(大语言模型)进行自然语言处理任务变得越来越便捷。本文将通过三个金融领域的实际小项目(面向初学者乃至小白),详细介绍如何使用 Ollama 本地部署 Qwen2.5:7b 模型(模型仅用于演示代码,并非最佳模型),并基于 Few-shot / In-Context Learning 方法完成三大经典NLP任务:金融文本分类金融文本语义匹配金融文本信息抽取

三个项目均采用同一套方法论和代码架构,适合初学者快速上手。


一、项目整体架构

三个项目共享相同的设计模式,核心流程如下:

输入数据 → 构建Few-shot提示词(init_prompts) → 调用Ollama模型推理(inference) → 输出结果

共通的技术栈:

  • Ollama:本地模型部署与推理框架
  • Qwen2.5:7b:阿里通义千问2.5版本,70亿参数
  • Rich:Python终端美化输出库
  • Few-shot Learning:通过在Prompt中提供少量标注样例,让模型学会任务格式

二、项目一:金融文本分类

2.1 任务目标

将给定的金融文本自动归类到以下四个类别中:

  • 新闻报道:宏观经济、政策变动相关
  • 财务报告:公司财报、资产负债相关
  • 公司公告:并购、战略调整等公司层面公告
  • 分析师报告:行业分析、投资趋势研判

2.2 核心代码解析

(1)定义类别及样例
class_examples = {
    '新闻报道': '今日,股市经历了一轮震荡,受到宏观经济数据和全球贸易紧张局势的影响...',
    '财务报告': '本公司年度财务报告显示,去年公司实现了稳步增长的盈利...',
    '公司公告': '本公司高兴地宣布成功完成最新一轮并购交易...',
    '分析师报告': '最新的行业分析报告指出,科技公司的创新将成为未来增长的主要推动力...'
}

每个类别提供一个代表性样例,作为Few-shot的参考依据。

(2)初始化Prompt
def init_prompts():
    class_list = list(class_examples.keys())
    pre_history = [
        {"role": "system", "content": f"现在你是一个文本分类器,你需要按照要求将我给你的句子分类到:{class_list}类别中。"},
    ]
    for _type, example in class_examples.items():
        pre_history.append({"role": "user", "content": f'"{example}"是 {class_list} 里的什么类别?'})
        pre_history.append({"role": "assistant", "content": _type})
    return {'class_list': class_list, 'pre_history': pre_history}

这里采用了经典的 In-Context Learning 模式:

  1. 首先用System Prompt设定模型的角色——“你是一个文本分类器”
  2. 然后依次给出每个类别的样例,构造 User → Assistant 的问答对
  3. 模型从这些样例中学会分类规则
(3)推理函数
def inference(sentences: list, custom_settings: dict):
    for sentence in sentences:
        sentence_with_prompt = f'"{sentence}"是 {custom_settings["class_list"]} 里的什么类别?'
        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: {sentence}')
        print(f'>>> inference answer: {response}')

调用 ollama.chat() 时,将之前构造好的 pre_history(包含Few-shot样例)与当前待分类句子一起传入,模型便会按照样例的格式返回分类结果。

2.3 测试用例与预期效果

sentences = [
    "今日,央行发布公告宣布降低利率,以刺激经济增长...",           # → 新闻报道
    "本公司宣布成功收购一家在创新科技领域领先的公司...",           # → 公司公告
    "公司资产负债表显示,公司偿债能力强劲,现金流充足...",         # → 财务报告
    "最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长..." # → 分析师报告
]

模型能够根据Few-shot样例准确地完成分类。


三、项目二:金融文本语义匹配

3.1 任务目标

判断两个金融句子在语义上是否相似,输出"是"或"不是"。这在金融领域有广泛应用,例如:新闻去重、公告匹配、舆情聚合等。

3.2 核心代码解析

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

与分类任务不同,匹配任务需要同时提供正例(语义相似)负例(语义不相似),让模型理解"相似"与"不相似"的边界。

(2)初始化Prompt
def init_prompts():
    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}

注意System Prompt中明确要求模型"只需要回答是否相似,不要做多余的回答",这是为了减少模型输出噪声,提高结果的确定性。

(3)推理函数
def inference(sentence_pairs: list, custom_settings: dict):
    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: {sentence_pair}')
        print(f'>>> inference answer: {response}')

3.3 测试用例与预期效果

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

四、项目三:金融文本信息抽取

4.1 任务目标

从金融文本中抽取出结构化的实体信息,包括:日期、股票名称、开盘价、收盘价、成交量,并以JSON格式输出。

4.2 核心代码解析

(1)定义Schema和样例
schema = {
    '金融': ['日期', '股票名称', '开盘价', '收盘价', '成交量'],
}

ie_examples = {
    '金融': [
        {
            'content': '2023-01-10,股市震荡。股票古哥-D[EOOE]美股今日开盘价100美元,一度飙升...最终以102美元收盘,成交量达到520000。',
            'answers': {
                '日期': ['2023-01-10'],
                '股票名称': ['古哥-D[EOOE]美股'],
                '开盘价': ['100美元'],
                '收盘价': ['102美元'],
                '成交量': ['520000'],
            }
        }
    ]
}

信息抽取任务比前两个更复杂——需要定义实体类型(Schema),并指定输出格式为JSON。样例中的answers是标准的JSON格式,模型会学习这种输出模式。

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

这里定义了信息抽取的专用Prompt模板,关键点在于:

  • 明确要求JSON格式输出
  • 对于原文中不存在的字段,用"原文中未提及"填充
  • 多个值用逗号分隔
(3)初始化Prompt
def init_prompts():
    ie_pre_history = [
        {"role": "system", "content": "你是一个信息抽取助手。"},
    ]
    for _type, example_list in ie_examples.items():
        for example in example_list:
            sentence = example['content']
            properties_str = ', '.join(schema[_type])
            schema_str_list = f'"{_type}"({properties_str})'
            sentence_with_prompt = IE_PATTERN.format(sentence, schema_str_list)
            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}
(4)后处理清洗函数(关键创新点)
def clean_response(response: str):
    if '```json' in response:
        res = re.findall(r'```json(.*?)```', response, re.DOTALL)
        if len(res) and res[0]:
            response = res[0]
        response.replace('、', ',')
    try:
        return json.loads(response)
    except:
        return response

这是信息抽取任务独有的后处理步骤。由于模型输出有时会包含Markdown代码块标记(```json … ```),需要用正则将其中的JSON部分提取出来,再进行解析。这个细节处理在实际项目中非常重要。

(5)推理函数
def inference(sentences: list, custom_settings: dict):
    for sentence in sentences:
        cls_res = "金融"
        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)
        response = ollama.chat(
            model="qwen2.5:7b",
            messages=[*custom_settings['ie_pre_history'],
                      {"role": "user", "content": sentence_with_ie_prompt}]
        )
        res_content = response["message"]["content"]
        ie_res = clean_response(res_content)
        print(f'>>> sentence: {sentence}')
        print(f'>>> inference answer: {ie_res}')

4.3 测试用例与预期效果

sentences = [
    '2023-02-15,寓意吉祥的节日,股票佰笃[BD]美股开盘价10美元...最终以13美元收盘,成交量微幅增加至460,000...',
    '2023-04-05,市场迎来轻松氛围,股票盘古(0021)开盘价23元...最终以26美元收盘,成交量缩小至310,000...',
]

模型将输出结构化的JSON:

{
    "日期": ["2023-02-15"],
    "股票名称": ["佰笃[BD]美股"],
    "开盘价": ["10美元"],
    "收盘价": ["13美元"],
    "成交量": ["460,000"]
}

五、三个项目的方法论对比

维度 文本分类 语义匹配 信息抽取
任务类型 多分类 二分类(是/否) 结构化抽取
输出格式 类别标签 是/不是 JSON
Few-shot样例 每类1个样例 正例+负例共3组 带Schema的标注样例
Prompt复杂度 高(需定义Schema)
后处理 无需 无需 需要JSON清洗
System Prompt “你是文本分类器” “帮助我完成文本匹配” “你是信息抽取助手”

六、核心方法论:In-Context Learning(上下文学习)

三个项目都采用了相同的核心方法论——In-Context Learning,这是一种无需微调(Fine-tuning)即可让大模型适应特定任务的技术。

工作流程

┌──────────────────────────────────────────────────┐
│              构建Few-shot Prompt                   │
│                                                    │
│  ① System Prompt:定义模型角色                      │
│  ② User:提供样例输入                               │
│  ③ Assistant:提供样例输出(标准答案)                │
│  ④ ...(重复②③,提供多个样例)                      │
│  ⑤ User:提供真正的待处理输入                        │
└──────────────────────────────────────────────────┘
                         ↓
┌──────────────────────────────────────────────────┐
│            Ollama 调用 Qwen2.5:7b                 │
│                                                    │
│  模型从历史对话(pre_history)中学习任务模式          │
│  并按照样例格式输出结果                              │
└──────────────────────────────────────────────────┘
                         ↓
┌──────────────────────────────────────────────────┐
│              后处理 & 结果展示                      │
│                                                    │
│  分类/匹配:直接输出                                │
│  信息抽取:JSON清洗 + 解析                          │
└──────────────────────────────────────────────────┘

代码架构统一性

三个项目的代码结构高度统一,都遵循以下模式:

# 第一步:定义数据和样例
examples / schema = { ... }

# 第二步:构建Few-shot Prompt
def init_prompts():
    # System Prompt + Few-shot Examples
    return custom_settings

# 第三步:模型推理
def inference(data, custom_settings):
    for item in data:
        response = ollama.chat(model='qwen2.5:7b', messages=[...])
        # 处理并输出结果

# 第四步:主程序入口
if __name__ == '__main__':
    custom_settings = init_prompts()
    inference(test_data, custom_settings)

七、技术栈详解

7.1 Ollama

Ollama 是一个让你在本地运行大模型的工具,支持多种开源模型。在本项目中:

# 安装Ollama后,拉取模型
ollama pull qwen2.5:7b

# Python调用
import ollama
response = ollama.chat(model='qwen2.5:7b', messages=[...])

优点:

  • 完全本地运行,数据不出本机
  • API简洁,与OpenAI格式类似
  • 支持多种开源模型

7.2 Qwen2.5:7b

阿里通义千问2.5版本,70亿参数,在中文任务上表现优异。7B的参数量在消费级硬件上即可流畅运行,非常适合本地开发和实验。

7.3 Rich库

from rich import print
from rich.console import Console

console = Console()
with console.status("[bold bright_green] Model Inference..."):
    # 显示推理状态动画
    ...
print(f'>>> [bold bright_red]sentence: {sentence}')

Rich库让终端输出更加美观,支持彩色文字、状态动画、进度条等,提升开发调试体验。


八、实战要点与经验总结

8.1 Few-shot样例设计原则

  1. 代表性强:样例要覆盖目标场景的典型情况
  2. 格式一致:所有样例的输入输出格式必须保持统一
  3. 数量适中:通常2-4个样例即可,过多会增加token消耗
  4. 正负均衡:对于匹配类任务,正例和负例都要提供

8.2 Prompt调优技巧

  • 角色设定要明确:System Prompt中清晰定义模型角色和任务边界
  • 约束输出格式:如"只需要回答是否相似"、“按照JSON格式输出”
  • 处理异常输出:信息抽取任务中,模型可能输出带Markdown标记的内容,需要后处理清洗

8.3 常见问题与解决方案

问题 原因 解决方案
分类结果不稳定 Few-shot样例不够典型 增加更有代表性的样例
JSON解析失败 模型输出包含额外文本 使用正则提取clean_response
匹配结果模糊 Prompt约束不够明确 加强System Prompt中的输出限制
推理速度慢 模型较大或硬件不足 可换用qwen2.5:3b等更小模型

九、完整代码获取与运行

环境准备

# 1. 安装Ollama
brew install ollama    # macOS
# 或访问 https://ollama.com 下载

# 2. 拉取模型
ollama pull qwen2.5:7b

# 3. 安装Python依赖
pip install ollama rich

运行方式

将三个项目的代码分别保存为 .py 文件,直接运行即可:

python 金融文本分类.py
python 金融文本匹配.py
python 金融文本信息抽取.py

十、总结

本文详细介绍了基于 Ollama + Qwen2.5:7b 的三个金融NLP小项目:

  1. 金融文本分类:将文本归类到新闻报道、财务报告、公司公告、分析师报告四个类别
  2. 金融文本语义匹配:判断两个句子语义是否相似
  3. 金融文本信息抽取:从文本中抽取出日期、股票名称、股价等结构化信息

三个项目的共同特点是:

  • 采用 In-Context Learning,无需微调
  • 使用统一的代码架构(init_prompts + inference
  • 完全本地运行,数据安全可控
  • 代码简洁,适合学习和二次开发

这些项目的实践表明,大模型配合Few-shot学习可以在金融NLP领域快速落地,为更复杂的应用场景(如智能投研、舆情监控、自动化报告生成等)打下坚实基础。


关键词:Ollama、Qwen2.5、金融NLP、文本分类、语义匹配、信息抽取、Few-shot Learning、In-Context Learning、大模型应用


本文是面向初学者的文章,这三个小项目都是初学者乃至小白都能快速上手的项目,无需深入大模型的内部架构和具体大模型原理,只需本地调用训练好的大模型即可,非常简单

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐