零微调、纯本地、面向初学者!Ollama + Qwen2.5 实战金融NLP三大任务:文本分类、语义匹配与信息抽取
基于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 模式:
- 首先用System Prompt设定模型的角色——“你是一个文本分类器”
- 然后依次给出每个类别的样例,构造
User → Assistant的问答对 - 模型从这些样例中学会分类规则
(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样例设计原则
- 代表性强:样例要覆盖目标场景的典型情况
- 格式一致:所有样例的输入输出格式必须保持统一
- 数量适中:通常2-4个样例即可,过多会增加token消耗
- 正负均衡:对于匹配类任务,正例和负例都要提供
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小项目:
- 金融文本分类:将文本归类到新闻报道、财务报告、公司公告、分析师报告四个类别
- 金融文本语义匹配:判断两个句子语义是否相似
- 金融文本信息抽取:从文本中抽取出日期、股票名称、股价等结构化信息
三个项目的共同特点是:
- 采用 In-Context Learning,无需微调
- 使用统一的代码架构(
init_prompts+inference) - 完全本地运行,数据安全可控
- 代码简洁,适合学习和二次开发
这些项目的实践表明,大模型配合Few-shot学习可以在金融NLP领域快速落地,为更复杂的应用场景(如智能投研、舆情监控、自动化报告生成等)打下坚实基础。
关键词:Ollama、Qwen2.5、金融NLP、文本分类、语义匹配、信息抽取、Few-shot Learning、In-Context Learning、大模型应用
本文是面向初学者的文章,这三个小项目都是初学者乃至小白都能快速上手的项目,无需深入大模型的内部架构和具体大模型原理,只需本地调用训练好的大模型即可,非常简单
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)