【AI-Ollama进阶】信息提取(Information Extraction)

写在前面 / 链接存档

Ollama应用实践:信息提取(Information Extraction)

需求

  • 现在有很多个电子发票PDF文件, 使用自动化工具帮我们批量自动从发票PDF提取出格式化信息。如从发票

  • 提取出 DICT_DATA

1
2
3
4
5
6
7
DICT_DATA = {
"开票日期": "2023年01月06日",
"应税货物(或服务)名称": "*信息技术服务*技术服务费",
"价税合计(大写)": "",
"税率": "6%",
"备注": "230106163474406331"
}

准备工作

第一步:安装ollama

第二步:下载LLM

  • ollama软件目前支持多种大模型, 如阿里的(qwen、qwen2)、meta的(llama3)
  • 以llama3为例,根据自己电脑显存性能, 选择适宜的版本。如果不知道选什么,那就试着安装,不合适不能用再删除即可
  • 打开电脑命令行cmd(mac是terminal), 网络是连网状态,执行模型下载(安装)命令
1
ollama run llama3
  • 等待 llama3:8b 下载完成

第三步:安装Python包

  • 在python中调用ollama服务,需要Pythonollama
  • 打开电脑命令行cmd(mac是terminal), 网络是连网状态,执行安装命令
1
pip3 install ollama

第四步:启动ollama服务

  • 在Python中调用本地ollama服务,需要先启动本地ollama服务, 打开电脑命令行cmd(mac是terminal), 执行
1
ollama serve

实验

第零步:代码结构

1
2
3
4
5
6
7
project
|
- 代码.ipynb #代码
- prompt.txt #提示模板
- data
|--- 1.pdf #实验的发票
- result.csv #结果

第一步:读取pdf

  • Python代码
1
2
3
4
5
6
import cntext as ct
#cntext版本为2.1.3,非开源, #需联系大邓372335839获取

text = ct.read_pdf('data/1.pdf')
print(ct.__version__)
text
  • Run
1
' 北京增值税电子普通发票发票代码: \n发票号码: 69453658\n开票日期: 2023年01月06日\n校 验 码: \n购\n买\n方名        称: 哈尔滨所以然信息技术有限公司\n密\n码\n区030898/5<32>*/0*440/63+79*08\n纳税人识别号: 91230109MABT7KBC4M /<54<1*6+49<-*+*>7<-8*04<+01\n地 址、电 话: 68+160026-45904*2<+3+15503>2\n开户行及账号: 98*2/*-*480145+-19*0917-1*61\n货物或应税劳务、服务名称 规格型号 单 位 数 量 单 价 金 额 税率 税 额\n*信息技术服务*技术服务费 1248.113208 248.11 6% 14.89\n合      计 ¥248.11 ¥14.89\n价税合计(大写)\n  贰佰陆拾叁元整             (小写)¥263.00\n销\n售\n方名        称: \n备\n注230106163474406331\n纳税人识别号: 91110108MA01WFY0X6\n地 址、电 话: \n开户行及账号: \n  收款人: 复核: 开票人: 销售方:(章)'

第二步:提取信息

  • 使用ollama服务中的大模型 llama3:8b , 需要大模型提示信息及数据
  • 提示信息prompt
1
2
prompt = open('prompt.txt', encoding='utf-8').read()
print(prompt)
  • Run
1
2
3
4
5
6
发票文本内容
---
{TEXT}
---

以 JSON 格式回答。 JSON 应包含如下信息, 依次为"开票日期", "应税货物(或服务)名称", "价税合计(大写)", "税率", "备注";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%%time

import ollama
import cntext as ct
#cntext版本为2.1.3,非开源, 需联系大邓372335839获取

#读取发票pdf
content = ct.read_pdf('data/1.pdf')
#读取prompt
prompt = open('prompt.txt', encoding='utf-8').read()

response = ollama.chat(model='llama3:8b', messages=[
{'role': 'system','content': prompt},
{'role': 'user','content': content},
])

result = response['message']['content']
result = eval(result.split('```\n')[1].split('\n```')[0])
result
  • Run
1
2
3
4
5
6
7
8
CPU times: user 20.5 ms, sys: 2.34 ms, total: 22.9 ms
Wall time: 3.58 s

{'开票日期': '2023年01月06日',
'应税货物(或服务)名称': '*信息技术服务*技术服务费',
'价税合计(大写)': '贰佰陆拾叁元整',
'税率': '6%',
'备注': '230106163474406331'}
  • 提取TEXT中的关键信息,返回DICT_DATA, DICT_DATAdict数据格式,所含关键词依次为”开票日期”, “应税货物(或服务)名称”,“价税合计(大写)”, “税率”, “备注”;
  • 结果只显示DICT_DATA
  • TEXT: {text}
1
2
3
4
5
6
7
8
9
10
11
import ollama

response = ollama.chat(model='llama3:8b', messages=[
{
'role': 'user',
'content': f'提取TEXT中的关键信息,返回DICT_DATA, DICT_DATA为dict数据格式,所含关键词依次为"开票日期", "应税货物(或服务)名称", "价税合计(大写)", "税率", "备注"; 结果只显示DICT_DATA。TEXT: {text1}',
},
])

result = response['message']['content']
result
  • 从运行结果看出, 大模型从发票PDF中准确提取到我们需要的信息,耗时大概10s
  • 需要注意,有时候大模型还会返回
1
'Here is the extracted key information in dictionary format:\n\n```\n{\n    "开票日期": "2023年01月06日",\n      "应税货物(或服务)名称": "*信息技术服务*技术服务费",\n    "价税合计(大写)": "贰佰陆拾叁元整",\n    "税率": "6%",\n    "备注": "230106163474406331"\n}\n```\n\nLet me know if you have any further requests! 😊'
  • DICT_DATA变为真正的字典数据
1
2
3
4
5
6
7
import re

result = response['message']['content']
result = [r for r in re.split('```|DICT_DATA = ', result) if '{' in r][0]

print(type(eval(result)))
print(eval(result))

第三步:封装成函数extract_info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import ollama
import re

def extract_info(text, prompt, max_retries=3):
for attempt in range(max_retries + 1):
try:
response = ollama.chat(
model='llama3:8b',
messages=[
{'role': 'system', 'content': prompt},
{'role': 'user', 'content': text}
]
)

result = response['message']['content']
result = eval(result.split('```\n')[1].split('\n```')[0])
return result

except Exception as e:
if attempt < max_retries:
print(f"An error occurred: {e}. Retrying ({attempt + 1}/{max_retries + 1})...")
else:
raise e

#读取prompt
prompt = open('prompt.txt', encoding='utf-8').read()
result = extract_info(text, prompt)
result

第四步:批量提取

  • 假设data文件夹内有成百上千的发票(实际上只有一张发票)
  • data文件夹进行批量信息提取,结果存储为csv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
%%time

import os
import ollama
#cntext版本为2.1.3,非开源, 需联系大邓372335839获取
import cntext as ct
import pandas as pd


def extract_info(text, prompt, max_retries=3):
for attempt in range(max_retries + 1):
try:
response = ollama.chat(
model='llama3:8b',
messages=[
{'role': 'system', 'content': prompt},
{'role': 'user', 'content': text}
]
)

result = response['message']['content']
result = eval(result.split('```\n')[1].split('\n```')[0])
return result

except Exception as e:
if attempt < max_retries:
print(f"An error occurred: {e}. Retrying ({attempt + 1}/{max_retries + 1})...")
else:
raise e


# 当前代码所在的代码文件与data文件夹处于同一个文件夹内
# 获取data内所有pdf的路径
pdf_files = [f'data/{file}' for file in os.listdir('data') if '.pdf' in file]
# 读取prompt
prompt = open('prompt.txt', encoding='utf-8').read()

dict_datas = []
for pdf_file in pdf_files:
text = ct.read_pdf(pdf_file)
dict_data = extract_info(text, prompt)
dict_datas.append(dict_data)

df = pd.DataFrame(dict_datas)
df.to_csv('result.csv', index=False)
df
1
2
CPU times: user 32 ms, sys: 2.17 ms, total: 15.2 ms
Wall time: 3.8 s

讨论 / 总结

本文只使用了一张发票进行实验, 实际上准确率没有这么高, 识别错误字段集中在销售方纳税识别号(案例没有展示销售方纳税识别号的识别)。原因主要是ct.read_pdf读入pdf时,文本比较杂乱。对大模型的语义理解有一定的挑战。目前大模型已经支持文本、图片、音频、视频、网址, 所以各位看官,不用等太久,就可克服此问题。

大模型会对每个输入,给出正确概率最大的回答,因此大模型提取数据时存在一定的错误识别风险。为降低该风险,尽量选择特别特殊、显眼,例如三张发票的价税合计(大写), 因为信息是特殊的中文大写数字, 在所有文本中是最醒目最特别的文本信息,这样大模型处理这类信息时会给这类信息尽可能高的权重,增大回答的准确率。

精选内容

  • Copyrights © 2024-2025 brocademaple
  • 访问人数: | 浏览次数:

      请我喝杯咖啡吧~

      支付宝
      微信