☰
AI大模型驱动的地质勘探语料清洗与标注实战
2026/9/29 2:17:28 网站建设 项目流程

简介:一份面向地质勘探研究人员、工程师与技术管理者的PDF方案文档,聚焦于如何用AI大模型构建高质量地质语料库,解决勘探数据分散、噪声多、标注难等痛点。文档系统覆盖项目背景、数据源选择与收集、语料清洗原则与算法、标注类别与流程、人员培训管理、数据存储及模型训练应用等完整环节,尤其对地质层位、矿体特征、结构特征等标注类别和去重算法进行了细化说明,具有较强的工程参考价值。资源为PDF格式,仅1个文件,压缩包大小约940KB,包含完整章节目录与结构化内容,便于按模块阅读与复用。方案不仅给出技术实现细节,还纳入实施计划、风险管理与未来展望,适合用于团队内部方案设计、课题立项参考,以及智能勘探系统建设的初始框架搭建。目前已有79人学习下载,适合关注地质勘探智能化转型的从业者参考。

1. 地勘报告文本是座富矿,但不清洗标注就挖不动

我接手第一个地质类知识库项目时,被一份钻孔编录表卡了三天。上面的岩性描述写着“灰岩夹页岩”,同一份报告后面又出现“石灰岩与泥岩互层”——说的是同一段地层。这类问题在历史地勘资料里到处都是,直接把原始文本喂给AI大模型,检索结果和训练数据都会跑偏。用AI大模型做地质勘探语料清洗和标注,真正的价值不是“模型能读懂地质报告”,而是把散乱文本变成结构化的术语、实体和关系,让下游的知识库和训练集有靠谱的输入。

这个方向适合两类人:手里攒着几千份老报告、想建行业知识库的地质工程师,以及要给行业大模型做训练数据的数据团队。它不会帮你产生新的地质认识,但能把手头报告变成机器能用的资产。下面从语料摸底开始,把完整方案拆开讲。

2. 语料摸底与模型选型:先知道手上有多少脏数据,再决定花多少钱

2.1 地质勘探文本的4类来源与脏数据画像

做清洗和标注之前,我一般先花两三天时间把语料来源盘清楚。地质勘探数据不是单一格式,常见有四类来源,每一类的脏数据长相完全不同:钻孔编录是表格转文本,处理重点是去制表符错位和补深度单位;勘查报告和岩矿鉴定报告是长文本,处理重点是术语不统一和OCR噪声;野外记录本口语化严重,简写、错别字、地名误差多;测井和采样台账则是单位混用、日期格式混乱、坐标精度缺失。

语料来源典型文件最常见的脏数据
钻孔编录编录表、柱状图描述岩性名称混用、深度区间缺单位、地层代号误写
勘查报告地质勘查报告、岩矿鉴定报告术语不一、表格转文本错乱、缩写无上下文
野外记录路线记录、野外记录本口语化描述、简写、地名错别字
测井与采样台账测井解释、采样登记表单位混用、坐标精度缺失、日期格式不统一

这张画像直接决定清洗策略。同一个模型不会用一套提示词同时处理“表格错位”和“口语简写”,前者靠格式修复,后者靠术语扩展和上下文推断。如果你跳过摸底直接写提示词,后面八成要反复返工。

2.2 抽样评估和摸底脚本:先定质量基线,再谈清洗效果

摸清来源之后,每类语料抽5到10份人工精修一遍,当成黄金样本。清洗效果怎么算合格,不能凭感觉,要有基线:术语统一率、单位规范率、符号正确率、噪声占比,四个维度各打一个分数。基线不是用来考核模型的,是用来防止你改提示词时把效果改回去的。

抽样前先用脚本把语料规模统计出来,顺便抓一遍常见脏信号。下面这段是我常用的摸底脚本,按来源分类统计文件数、总字符数和几个典型信号:

# 语料摸底:统计各来源文件数、字数和常见脏信号 import glob import re from collections import Counter sources = { "钻孔编录": glob.glob("data/drill/*"), "勘查报告": glob.glob("data/report/*"), "野外记录": glob.glob("data/field/*"), } for name, files in sources.items(): if not files: print(name, "没有文件,跳过") continue total_chars = 0 signals = Counter() for fp in files: # 老报告常是GBK或GB18030编码,先用errors忽略,后面单文件复检 text = open(fp, encoding="utf-8", errors="ignore").read() total_chars += len(text) signals["全角引号"] += len(re.findall(r"[“”‘’]", text)) signals["数字后无单位"] += len(re.findall(r"\d{2,3}(?=[\s,,;;))])", text)) signals["制表符"] += text.count("\t") signals["空行过多"] += text.count("\n\n\n") print(f"{name}: 文件{len(files)}个, 总字符{total_chars}") for k, v in signals.items(): print(f" {k}: {v}")

逻辑说明:“数字后无单位”这条正则专门抓钻孔编录里最常见的问题,深度区间“12.5-14.0”后面没有“m”。全角引号和制表符则反映OCR或输入法时代留下的格式噪声。这些信号只是线索,真正的质量基线要人工看样本才能定。

参数说明:编码先用utf-8加errors忽略,跑完脚本后,把字符异常的文件单独挑出来用GB18030再解一次。正则里\d{2,3}限定两到三位数字,避免把年份、电话号码这类无关数字误伤,实际项目里要根据你的文本微调这段。

2.3 模型选型:API还是本地部署,取决于数据能不能出单位内网

模型选型其实是个约束问题,不是技术问题。语料量小、数据不敏感,直接调API,成本和效率最优;语料量大、单位有数据保密要求或者内网环境出不去,就本地部署开源模型。地质勘探文本有一个特点:清洗和标注这类结构化任务,7B到14B量级的模型就够用,没必要上70B,推理慢且成本高。

选型参数量硬件需求适合场景注意点
API调用商用大模型无,需要出网小批量、允许出网按token计费,清洗要控制输出长度
本地GGUF量化7B~14B16GB以上内存或8GB以上显存涉密数据、长期批量任务量化等级影响抽取稳定性

本地部署我一般用GGUF量化格式,配Ollama或llama.cpp这类推理服务。14B模型用Q4_K_M量化后体量压到10GB左右,一张常见显卡或者64GB内存的机器就能跑起来。量化等级别一味追求最小,Q4_K_M是清洗标注任务的常用平衡点,再低的量化等级会让抽取结果飘。

如果你要做清洗结果的实时审核界面,可以考虑用SSE流式输出把结果逐段渲染出来,配合前端abort中断超时请求。这个后面在参数部分细说。

3. 清洗流水线:拆成四段的方案与3个必调参数

3.1 清洗为什么不能一把梭:四段流水线的设计

很多人上来就写一个巨型提示词,把“去噪、补单位、统一术语”全塞进去,让模型一次搞定。结果模型自由发挥,把“泥质粉砂岩”改成“泥岩”,把“含砾砂岩”里的“砾”删掉,你还找不到是哪一步改的。清洗必须拆段,每一段只做一类事,段与段之间留中间产物。

我用的流水线分四段:提取去噪、格式规范化、术语统一、规则检核。提取去噪把乱码、页眉页脚、OCR错字清掉;格式规范化补单位、统一日期、修全角半角;术语统一靠同义词表和上下文判断;规则检核用正则和人审兜底。每一段产物都落盘,后面发现问题能回滚到任意一步重跑,不用整批返工。

# 清洗流水线:每段独立,段间产物落盘,方便回滚 import json STEP_QUEUE = [ "extract_denoise", "normalize_format", "unify_terms", "rule_check", ] def run_pipeline(raw_text: str, step_overrides: dict = None) -> dict: """step_overrides 用于某一段单独调整提示词参数""" current = {"raw": raw_text, "text": raw_text, "log": []} for step in STEP_QUEUE: if step == "extract_denoise": current["text"] = denoise_text(current["text"]) elif step == "normalize_format": current["text"] = llm_normalize(current["text"], overrides=step_overrides) elif step == "unify_terms": current["text"] = llm_unify(current["text"], glossary=GLOSSARY) elif step == "rule_check": current["issues"] = rule_check(current["text"]) current["log"].append({"step": step, "len": len(current["text"])}) return current

逻辑说明:流水线是串行的,后面每一步的输入是上一步的输出,任何一步出问题,只需要重跑从该步骤往后的链路。这里的denoise_text是规则函数,llm_normalize和llm_unify是调用大模型,后面会给出具体实现思路。

参数说明:step_overrides这个参数很实用,当你发现“格式规范化”这步总把数字改坏,可以单独给这步传一个更严的temperature或换一份提示词,不影响其他段。

3.2 提示词模板:JSON输出、few-shot和“禁止推测”

清洗的提示词核心是三件事:强制JSON输出、给1到2个示例、明确禁止补全缺失信息。地质文本里深度、倾角、坐标经常缺,模型看到“12.5-14.0”顺手补个“m”,看起来更规范,但污染了原始数据。所以提示词里必须写死“不要补全”。

CLEAN_SYSTEM_PROMPT = """你是一个地质文本清洗助手。请只清理格式和术语问题,不要补全缺失信息,不要删除原文中的数字,不要改写地层描述。 要求: 1. 保留所有原始信息,包括缺失的深度区间(原文缺单位就保持原样并标注missing_unit)。 2. 把等价术语统一为靠前术语:{glossary} 3. 输出JSON:{{"cleaned_text": "...", "changes": [{{"original": "...", "replaced": "...", "reason": "..."}}]}} 4. 如果原文不属于地质文本,输出{{"cleaned_text": "", "changes": [], "not_geology": true}} """

逻辑说明:changes数组是把改动原因列出来,这是让清洗从黑匣子变成白盒的关键。人工抽检时直接看changes就能判断模型是改对了还是改错了,不用整篇比对。

参数说明:{glossary}是术语统一表,比如“灰岩=石灰岩”,但要注意不是所有同义词都能直接替换,我后面在避坑章节会展开讲。few-shot示例要包含一个负例,比如“原文:ZK301 12.5-14.0;输出:cleaned_text保持12.5-14.0不变,不要补m”。

3.3 3个必调参数:temperature、top_p、max_tokens

清洗标注这类抽取生成任务,三个参数必须调,不调就等着翻车。

temperature设置为0或0.1。温度越高,输出越随机,术语被改错、数字被替换的概率直线上升。top_p设置在0.1到0.3之间,进一步压缩采样空间。这两个参数配合,能有效减少“同一个文本跑两遍结果不一样”的玄学问题。

max_tokens要限制。清洗是压缩任务,清洗后的文本应该比原文短或相当。如果max_tokens给太大,模型会把一段话扩写成小作文,还把原文信息改得面目全非。我一般设1024到2048,再通过规则检查“清洗后文本长度是否超过原文1.2倍”,超了就报警。

# 本地模型调用:用OpenAI兼容接口,关闭流式 import requests def llm_normalize(text: str, overrides: dict = None, max_len: int = 800) -> dict: payload = { "model": "你的本地模型名,按实际替换", "messages": [ {"role": "system", "content": CLEAN_SYSTEM_PROMPT}, {"role": "user", "content": text[:max_len]}, ], "temperature": 0, "top_p": 0.1, "max_tokens": 1024, "stream": False, } if overrides: payload.update(overrides) resp = requests.post("http://127.0.0.1:11434/v1/chat/completions", json=payload, timeout=120) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]

逻辑说明:这里用OpenAI兼容接口,本地推理服务基本都支持,不绑定特定厂商。max_len把超长文本截断到800字符再清洗,避免模型注意力在长文本里分散、漏看中间内容。

参数说明:timeout设120秒,本地量化模型在纯CPU上跑,单条文本可能要一两分钟。如果你在审核界面做实时渲染,可以把stream设为True,按行解析SSE数据流,前端用abort中断;批量任务则关闭流式,简化日志和重试逻辑。

3.4 规则检核:清洗完不能全信模型

大模型清洗完必须过一道规则检核,用正则和词典兜底。规则不能替代模型,但能拦截大部分“新增数字”“术语改错”这类问题。

# 规则检核:找模型可能改错的地方 import re def rule_check(cleaned_text: str) -> list: issues = [] # 1. 深度区间后面必须有单位,没有就报警 issues += re.findall(r"\d{2,3}(?:\.\d+)?-\d{2,3}(?:\.\d+)?(?!\s*m\b)", cleaned_text) # 2. 术语白名单之外出现“疑似新词” # 实际项目里维护一个术语表,清洗后全量比对 return issues

逻辑说明:第一条正则专门盯“深度区间缺m”,第二条在白名单之外找异常词。规则检核的产出是issues列表,不是直接改文本。把这些报警喂给人工抽检,比让模型自己检查自己靠谱得多。

注意:规则检核要和日志配套使用。每一段清洗产物的落盘文件里都带版本号和时间戳,这样报警出来能快速定位是哪个步骤、哪一批数据引入的问题,直接回滚重跑那一段,不用从头再来。

4. 标注实现:实体schema、预标注与人工审核闭环

4.1 地质实体与关系的schema:先定类型,再谈抽取

清洗完的文本只能算半成品,知识库和训练集要的是结构化标注。地质语料标注不是给句子贴个分类,而是要抽实体、定关系、判属性。最容易翻车的是schema没定清楚就让标注员或大模型上手,结果标出来的东西五花八门,返工成本极高。

实体类型不要超过6类,关系不要超过5类。类型越多,标注一致性越差,大模型和人工都记不住边界。我常用的地质文本标注schema长这样:

{ "entities": { "stratum": {"desc": "地层/岩性描述", "props": ["name", "age", "thickness"]}, "lithology": {"desc": "岩石名称", "props": ["name", "color", "texture"]}, "structure": {"desc": "褶皱/断层/节理", "props": ["type", "attitude"]}, "mineral": {"desc": "矿物/矿产", "props": ["name", "occurrence"]}, "depth_interval": {"desc": "深度区间", "props": ["start", "end", "unit"]} }, "relations": { "overlies": "整合/不整合覆盖下伏地层", "intrudes": "侵入体与围岩关系", "contains": "地层包含岩性或矿物", "crossed_by": "被断层/岩脉切穿" } }

逻辑说明:深度区间单独作为一个实体类型,因为它在钻孔编录里出现频率极高,边界清晰,下游做剖面图、三维建模都要用。关系只保留最稳定的四种,像“形成于”“属于”这类模糊关系先不标,等数据多了再说。

参数说明:props里的属性按需增减。thickness不是每个stratum都有,没有就留空,不要为了让模型“完整”而逼它编造。

4.2 标注工具选型:文本标注用Label Studio,影像点云用CVAT

标注工具选型要看数据形态。文本实体和关系标注,我一般用Label Studio,它支持导入预标注JSON、自定义标签体系、多人协作和审核流。注意Label Studio直接用官方镜像或者打包好的安装方式就行,别自己从源码编译,前端依赖一大堆,编译半天可能只是版本对不上。

如果你的语料里有遥感影像、3D点云这类空间数据,比如要做地质体边界勾画或点云分类,那应该用CVAT或者专门的点云标注工具,它们的核心能力是画框、画多边形、做实例分割。目标检测常用的LabelImg也是图像标注工具,但不适合文本实体标注,别混用。

注意:文本标注、图像标注、点云标注是三条不同的工具链,不要试图用一套工具通吃。一个项目里同时有文本报告和影像数据,就分开建两个标注项目,互不干扰。

4.3 大模型预标注:低置信度进人工池,高置信度直接过初审

标注的核心思路是“大模型预标注,人工审核”。让大模型先把实体抽出来,低置信度的样本进人工池,高置信度的直接过初审。这样能把标注员的工作量集中在模型拿不准的地方。

# 预标注:用大模型抽实体,输出带置信度的标注结果 def preannotate_with_llm(text: str, schema: dict) -> list: prompt = f"""你是地质文本标注助手。请从文本中抽取实体,输出JSON数组。 实体类型:{list(schema['entities'].keys())} 只输出实体,不要输出关系。格式: [{{"type": "stratum", "start": 0, "end": 4, "text": "灰岩", "props": {{"thickness": "12.5m"}}}}] """ # 调用方式和清洗一致,省略重复的requests细节 result = llm_chat(prompt, temperature=0, max_tokens=2048) return json.loads(result) def to_labelstudio(pre_results: list) -> list: """转成Label Studio的prediction格式,带score字段""" return [{ "result": [ {"id": i, "from_name": "label", "to_name": "text", "type": "labels", "value": { "start": r["start"], "end": r["end"], "text": r["text"], "labels": [r["type"]]}} ], "score": 0.9 if r.get("props") else 0.6 } for i, r in enumerate(pre_results)]

逻辑说明:preannotate_with_llm让模型只输出实体数组,不输出关系,关系标注留给人工,因为关系判断上下文跨度大,模型容易漏。to_labelstudio把结果转成Label Studio的prediction格式,带score字段。

参数说明:score阈值刚开始设0.7,低于0.7的进人工池。跑两轮后统计人工修正率,如果修正率很低,可以把阈值放宽到0.6,减少无意义的人工审核量。

4.4 一致性管理:用Kappa值和争议样本闭环

标注质量不能靠感觉,要用一致性指标量化。常见做法是让两个人各标50条相同的文本,算Cohen's Kappa。Kappa值低于0.8,说明schema定义或者标注指南有歧义,要回头改,否则下游训练出来的模型也会学偏。

另一个容易被忽略的环节是争议样本回灌。大模型预标注和人工审核不一致的样本,不要改完就扔,收集起来,这些就是最天然的few-shot示例。下次再跑prompt时把几个典型争议样本塞进去,模型会明显收敛。这个闭环跑起来之后,标注不一致率会逐步下降,而不是靠运气。

5. 避坑:地质语料清洗标注里容易翻车的5个场景

5.1 五个典型踩坑记录

坑1:模型把缺失深度“脑补”出来了。 现象:原文写“12.5-14.0”,清洗后变成“12.5m-14.0m”,看起来更规范,但原始文本并没有单位。原因:模型在“修复”数据,而不是清洗数据,提示词里没写禁止推测。解决:系统提示词里加“不要补全缺失信息”,规则检核里加“清洗后新增单位或数字则报警”。

坑2:术语统一改错了方向。 现象:“灰岩”和“灰质页岩”被统一成“石灰岩”,把两类不同的东西合并了。原因:同义词表只映射了“灰岩→石灰岩”,没考虑“灰质”作为修饰语的场景。解决:术语统一表要经过地质人员审核,统一完再抽30条人工目检。

坑3:地层代号被当成化学元素改掉。 现象:“C2ym”(二叠系下统栖霞组一段代号)被模型改成“C 2ym”,或者直接标注成碳元素。原因:地层代号短,和化学符号撞车,模型缺地质领域知识。解决:把地层代号白名单写进提示词,明确说明“以下代码是地层代号,不是化学式”。

坑4:本地部署并发一高就超时。 现象:批量清洗跑到200条,接口开始大量超时,队列积压,最后发现模型服务挂了。原因:GGUF量化模型在CPU上推理慢,并发线程开太多,内存带宽被打满。解决:并发降到2到4,加任务队列,超时重试用指数退避;审核界面用流式输出配abort,挂死的请求主动取消。

坑5:标注一致性差,同一实体前后不一。 现象:同一个“断层F1”,前文标成structure,后文标成geological_body。原因:schema里这两个类型边界模糊,标注员各凭理解。解决:缩小schema,把模糊类型合并成“geological_structure”,标注指南里给正反例。

5.2 通用排查套路:先复现、再比对、后隔离

遇到清洗或标注异常,我的排查顺序是固定的。先在单个样本上复现,看是流程问题还是模型问题;再把清洗前后文本做diff,定位改动位置;最后按段落隔离,只重跑出问题的段落,不整篇重来。

# 排查:对比清洗前后文本差异,快速定位模型改错的段落 diff -u data/cleaned/z301.txt data/original/z301.txt \ | grep -E "^[+-]" | grep -E "[0-9]|m" | head -50

逻辑说明:diff里出现“+”号开头的新增数字或单位,基本就是模型在脑补。grep的[0-9]|m把数字和单位相关的改动过滤出来,优先看这类高嫌疑改动。

参数说明:head -50限制输出行数,避免刷屏。如果改动集中在某一段,把该段从源文件单独切出来,调整提示词或参数只对这一条重跑,验证没问题再放回批量流程。

6. 进阶:黄金测试集和增量微调才是长期资产

前面几章讲的是一条能跑通的流水线,但项目能不能长期用下去,取决于两件事:黄金测试集和增量微调。我第一次做地勘语料清洗时吃了亏,prompt一改,整个流程要全部回归,手里没有可对比的数据,只能人工抽看,效率极低。后来养成的习惯是每个项目开跑前,人工精标30到50条文本作为黄金集,条目不用多,但要覆盖每类语料和每种脏数据。每次换模型、改提示词、调参数,都在黄金集上跑一遍,把一致性分数记下来。

# 黄金集回归:计算清洗结果和人工精标的字符级一致率 import difflib def consistency_score(gold_text: str, model_text: str) -> float: return difflib.SequenceMatcher(None, gold_text, model_text).ratio()

逻辑说明:字符级一致率比较粗糙,但成本低、可每天跑。真正要看的还有实体级一致率——拿清洗后的文本去跑一遍预标注,和黄金集的人工标注比对,算实体级别的精确率和召回率。两套指标配合,才能判断清洗和标注各自有没有退化。

参数说明:分数低于阈值就报警,不要直接回滚,先看差异集中在哪个类型,多半是提示词或同义词表改坏了。增量微调的前提是攒够经人工复核的清洗标注结果,五千条以上就有价值。用GGUF量化一个7B小模型,本地就能跑,微调目标是让它在“地质文本抽取”任务上更稳,把通用大模型的调用量降下来。但别指望微调一步到位,黄金测试集是唯一裁判,分数没涨就回滚。

做这类项目,我的习惯是先把“怎么判断做对了”定下来,再让模型上场。清洗和标注的每一步都留日志、留中间产物,即使翻车也有后悔药。这个习惯让我少踩了很多坑,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询