SiameseUIE保姆级教程:如何导出抽取结果为JSON/CSV结构化数据
1. 为什么你需要这篇教程
你刚拿到一个预装好的SiameseUIE模型镜像,SSH登录后跑通了test.py,看到终端里漂亮地列出了“人物:李白,杜甫”“地点:碎叶城,成都”——但接下来呢?
这些结果只是打印在屏幕上,没法存档、没法分析、更没法导入Excel做统计。你可能正面临这些真实困扰:
- 需要把100篇新闻稿里的人物和地点批量抽出来,生成表格给编辑部用;
- 要把抽取结果喂给下游系统,但对方只认JSON格式;
- 想对比不同文本的实体分布,却只能靠肉眼数“李白”出现了几次;
- 测试脚本输出太“友好”,反而掩盖了原始结构,你根本不知道数据长什么样。
别担心。这篇教程不讲原理、不调参数、不碰环境配置——它只做一件事:手把手带你把屏幕上一闪而过的抽取结果,变成可保存、可传输、可编程处理的JSON和CSV文件。全程基于你已有的镜像环境,无需安装新包、不改PyTorch版本、不碰模型权重,5分钟内完成。
你不需要懂BERT,不需要会写推理代码,甚至不需要打开IDE。只要你会复制粘贴命令,就能让SiameseUIE真正变成你手边的结构化数据生产工具。
2. 理解当前输出的本质:它不是“结果”,而是“展示”
先破除一个关键误解:test.py默认输出的“ 分词器+模型加载成功!”和带横线分隔的文本,并不是原始数据,而是经过格式化的人类可读展示层。就像餐厅菜单上写的“宫保鸡丁(辣度中等,配花生)”,它告诉你菜是什么,但不会给你生鸡肉、干辣椒和花生米。
我们来拆解一段典型输出:
========== 1. 例子1:历史人物+多地点 ========== 文本:李白出生在碎叶城,杜甫在成都修建了杜甫草堂,王维隐居在终南山。 抽取结果: - 人物:李白,杜甫,王维 - 地点:碎叶城,成都,终南山 ----------------------------------------这段文字里藏着三类信息:
- 元信息:例子编号(1)、场景标签(历史人物+多地点)、原始文本(李白出生在…)
- 结构化内容:人物列表
["李白", "杜甫", "王维"]、地点列表["碎叶城", "成都", "终南山"] - 展示包装:等号分隔线、中文冒号、短横线列表、空行
而我们要做的,就是绕过包装,直接拿到结构化内容,并按标准格式组织。
好消息是:test.py内部早已把结构化数据算好了,只是没往外吐。我们的任务,是找到那个“开关”。
3. 核心操作:修改test.py,让结构化数据流出来
重要前提:请确保你已按README操作,能正常运行
python test.py并看到预期输出。若尚未成功,请先返回README完成基础验证。
3.1 定位数据生成位置
打开test.py文件(路径:nlp_structbert_siamese-uie_chinese-base/test.py),用任意编辑器(如nano或vim)查看。快速滚动,找到类似这样的代码块(通常在文件中后部,函数名为extract_pure_entities或run_inference附近):
# 示例伪代码(实际文件中结构类似) results = model.predict(text, schema, custom_entities) print(f"- 人物:{', '.join(results['人物'])}") print(f"- 地点:{', '.join(results['地点'])}")这里results变量就是我们要的“金矿”。它是一个Python字典,结构大致为:
{ "人物": ["李白", "杜甫", "王维"], "地点": ["碎叶城", "成都", "终南山"] }但注意:test.py默认只把它转成字符串打印,没做任何序列化。我们的目标,就是让这个字典变成JSON或CSV。
3.2 添加JSON导出功能(3步搞定)
在test.py文件末尾(所有代码之后),新增以下完整代码段:
# ========== 新增:JSON导出功能 ========== import json import os def save_results_to_json(results_list, output_file="extraction_results.json"): """ 将抽取结果列表保存为JSON文件 results_list: 由test_examples循环生成的每个结果字典组成的列表 output_file: 输出文件名,默认extraction_results.json """ # 构建标准JSON结构:包含元信息和实体结果 json_output = { "metadata": { "model": "SiameseUIE", "schema": ["人物", "地点"], "total_examples": len(results_list), "generated_at": "auto" }, "results": results_list } with open(output_file, "w", encoding="utf-8") as f: json.dump(json_output, f, ensure_ascii=False, indent=2) print(f"\n JSON导出完成!结果已保存至:{os.path.abspath(output_file)}") print(f" 文件大小:{os.path.getsize(output_file)} 字节") # 在原有测试循环结束后,调用此函数 # (请将此行插入到原test.py中for循环的下方,紧挨着最后一行print之后) # 假设原循环是:for example in test_examples: ... # 则在此处添加: # save_results_to_json(all_results) # all_results是你自己定义的结果列表名关键操作:你需要找到原文件中遍历test_examples的for循环,并在循环外、文件末尾,添加一行调用代码。具体找法:
- 在
test.py中搜索关键词for example in test_examples:或for i, example in enumerate(test_examples): - 找到该循环的结束位置(通常是最后一个
print语句之后,或if __name__ == "__main__":块结尾前) - 在此处插入调用行(替换
all_results为你实际使用的变量名,常见为results_list或final_results):
# 在原文件中找到类似这行的位置(示例,非原代码): # print("-" * 40) # 在它下面,添加: save_results_to_json(final_results) # ← 这行必须加,且变量名要匹配修改完成后,保存文件(Ctrl+O→Enter→Ctrl+Xin nano)。
3.3 一键运行,获取JSON文件
回到终端,执行与之前完全相同的命令:
cd .. cd nlp_structbert_siamese-uie_chinese-base python test.py你会看到和以前一样的屏幕输出,但在最后,多出两行:
JSON导出完成!结果已保存至:/root/nlp_structbert_siamese-uie_chinese-base/extraction_results.json 文件大小:2846 字节用ls -l extraction_results.json确认文件存在,用head -n 20 extraction_results.json查看开头内容,结构清晰:
{ "metadata": { "model": "SiameseUIE", "schema": ["人物", "地点"], "total_examples": 5, "generated_at": "auto" }, "results": [ { "example_id": 1, "text": "李白出生在碎叶城,杜甫在成都修建了杜甫草堂,王维隐居在终南山。", "entities": { "人物": ["李白", "杜甫", "王维"], "地点": ["碎叶城", "成都", "终南山"] } }, ... ] }4. 进阶操作:导出为CSV(兼容Excel和数据分析)
JSON适合程序处理,但如果你需要发给同事、做简单统计、或导入Power BI,CSV是更通用的选择。我们继续改造test.py。
4.1 在同一文件中追加CSV导出函数
仍在test.py末尾,在刚才添加的JSON函数之后,再添加以下代码:
# ========== 新增:CSV导出功能 ========== import csv def save_results_to_csv(results_list, output_file="extraction_results.csv"): """ 将抽取结果保存为CSV文件(扁平化结构,每行一个实体) 每行包含:例子ID、原始文本、实体类型、实体值 """ if not results_list: print(" 警告:无结果可导出,CSV文件未生成。") return with open(output_file, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) # 写入表头 writer.writerow(["example_id", "text", "entity_type", "entity_value"]) # 遍历每个例子 for result in results_list: example_id = result.get("example_id", "unknown") text = result.get("text", "") # 遍历每种实体类型 entities = result.get("entities", {}) for entity_type, entity_list in entities.items(): if isinstance(entity_list, list): for entity in entity_list: writer.writerow([example_id, text[:50] + "..." if len(text) > 50 else text, entity_type, entity]) print(f"\n CSV导出完成!结果已保存至:{os.path.abspath(output_file)}") print(f" 总行数:{sum(len(r.get('entities', {}).get(t, [])) for r in results_list for t in ['人物', '地点'])} 行")4.2 同时调用JSON和CSV导出
找到你之前添加的save_results_to_json(...)调用行,在它下方,再加一行:
save_results_to_csv(final_results) # 变量名需与JSON调用一致4.3 验证CSV效果
再次运行python test.py,你会看到:
CSV导出完成!结果已保存至:/root/nlp_structbert_siamese-uie_chinese-base/extraction_results.csv 总行数:15 行用head -n 10 extraction_results.csv查看:
example_id,text,entity_type,entity_value 1,"李白出生在碎叶城,杜甫在成都修建了杜甫草堂,王维隐居在终南山。",人物,李白 1,"李白出生在碎叶城,杜甫在成都修建了杜甫草堂,王维隐居在终南山。",人物,杜甫 1,"李白出生在碎叶城,杜甫在成都修建了杜甫草堂,王维隐居在终南山。",人物,王维 1,"李白出生在碎叶城,杜甫在成都修建了杜甫草堂,王维隐居在终南山。",地点,碎叶城 ...这个CSV可直接双击用Excel打开,也可用Python的pandas读取:
import pandas as pd df = pd.read_csv("extraction_results.csv") print(df.groupby(["entity_type", "entity_value"]).size())5. 实战技巧:3个高频需求的定制方案
5.1 需求:只导出“人物”实体,不要地点
修改save_results_to_csv函数中的循环部分(找到for entity_type, entity_list in entities.items():这一行),在其上方添加过滤条件:
# 在for循环前插入 for entity_type, entity_list in entities.items(): if entity_type != "人物": # ← 只保留人物 continue # 后续保持不变...5.2 需求:导出时保留原始文本全貌(不截断)
找到CSV函数中text[:50] + "..."这一行,直接改为:
text # 删除截断逻辑,保留全文注意:如果文本含逗号或换行符,Excel打开可能错位。此时建议坚持用JSON,或用pandas.to_csv(quotechar='"', quoting=csv.QUOTE_ALL)替代原生csv模块。
5.3 需求:每次运行生成带时间戳的文件名,避免覆盖
修改两个导出函数的output_file参数,默认值改为动态生成:
from datetime import datetime timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") output_file = f"extraction_results_{timestamp}.json" # CSV同理6. 故障排查:当导出不工作时,检查这4点
| 现象 | 最可能原因 | 快速解决 |
|---|---|---|
运行python test.py报错NameError: name 'save_results_to_json' is not defined | 函数定义位置错误,或调用行在函数定义之前 | 确保def save_results_to_json(...):在文件顶部(或至少在调用行之前);检查缩进是否为4空格 |
JSON文件生成了,但内容为空{}或只有{"metadata":{...},"results":[]} | results_list变量名不匹配,或未在循环中正确收集结果 | 在for循环内添加print("DEBUG:", type(results)),确认results字典结构;检查save_results_to_json(...)调用时传入的变量名是否与循环中append的列表名一致 |
| CSV文件有表头,但无数据行 | results_list为空,或entities字段名与实际不符(如代码中是"entity"而非"entities") | 用print(json.dumps(results_list[0], ensure_ascii=False, indent=2))打印第一个结果,确认键名;检查result.get("entities", {})中的键名是否拼写正确 |
导出文件生成在/tmp或其他奇怪路径 | output_file参数用了相对路径,但当前工作目录不是模型目录 | 在test.py开头添加import os; print("当前目录:", os.getcwd()),确认路径;或在save_函数中用os.path.join(os.getcwd(), output_file)确保路径明确 |
7. 总结:从屏幕到数据,只需一次修改
你现在已经掌握了SiameseUIE镜像最实用的“临门一脚”:
把模型的智能,转化为你可用的数据资产。
回顾整个过程,你只做了三件小事:
- 在
test.py末尾粘贴了2个函数(JSON + CSV导出); - 在循环结束后加了2行调用代码;
- 运行一次
python test.py,就获得了两个标准格式的文件。
没有环境冲突,不升级包,不重装模型——这正是受限云实例环境下最优雅的工程实践:最小改动,最大产出。
下一步,你可以:
- 把
extraction_results.json丢给前端工程师,让他直接渲染成可视化图表; - 用Python脚本读取CSV,自动统计“李白”在100篇文章中出现频次;
- 将导出逻辑封装成独立脚本
export.py,通过python export.py --input my_texts.txt --format csv一键调用。
技术的价值,不在于它多酷炫,而在于它多好用。现在,SiameseUIE对你而言,已不只是一个能“打印结果”的模型,而是一个随时待命的结构化数据工厂。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。