MGeo微调指南:如何在特定场景提升精度
地址匹配不是简单的字符串比对,而是地理语义的深度对齐。当你面对“杭州余杭区文一西路1288号”和“杭州市余杭区未来科技城文一西路1288号”这样一对地址时,通用文本相似度模型往往只看到“多出几个字”,而MGeo却能识别出“余杭区”与“未来科技城”的行政隶属关系、“文一西路”的道路一致性,以及“1288号”的门牌精确对应——这种能力,正是它被广泛用于物流面单清洗、政务地址标准化、地图POI去重等关键业务的原因。
但现实中的业务场景远比标准测试集复杂:医院系统里常出现“浙一医院”“浙江大学医学院附属第一医院”“浙大一院”三种写法;校园场景中,“清华园路”“清华大学东南门”“五道口清华校区”指向同一片区域;电商订单中,“广东省深圳市南山区科技园科发路8号”可能被简写为“深圳南山科技园8号”。这些领域特有表达,会让开箱即用的MGeo模型在实际部署中出现精度波动——相似度分数忽高忽低,边界案例误判率上升。
问题不在于模型本身,而在于它训练于通用中文地址语料,未适配你的具体业务语境。幸运的是,MGeo不仅是一个推理工具,更是一套可微调的地址理解框架。本文将跳过基础部署(你已熟悉python /root/推理.py),聚焦真正影响落地效果的核心动作:如何在你自己的数据上安全、高效、可复现地完成微调,让MGeo从“好用”变成“刚刚好”。
微调前的关键认知:什么该调,什么不该碰
不要碰模型主干结构,但必须调整输入范式
MGeo的底层是经过千万级地址对预训练的双塔+交互式编码器,其空间感知注意力机制和多粒度字段建模能力已高度成熟。直接修改Transformer层数、隐藏层维度或注意力头数,不仅不会提升精度,反而极易导致梯度爆炸或语义坍塌。我们真正需要干预的,是模型“如何看待输入”。
观察原始推理脚本中的输入构造方式:
inputs = tokenizer(addr1, addr2, padding=True, truncation=True, max_length=128, return_tensors="pt")这是标准的句子对分类格式:[CLS] 地址A [SEP] 地址B [SEP]。但在真实业务中,地址常附带非地理信息干扰项。例如物流订单中的“【顺丰】浙江省杭州市西湖区文三路398号”,括号内是承运商标识;政务系统中的“杭州市上城区望江街道海潮路268号(原望江路268号)”,括号内是历史名称。这些内容会稀释模型对核心地理字段的关注。
正确做法:在送入tokenizer前,增加轻量级预处理函数,剥离非地理噪声:
import re def clean_address(address: str) -> str: """移除地址中常见的非地理干扰字符""" # 移除物流标识、订单号、括号内备注、特殊符号 address = re.sub(r'【.*?】|〖.*?〗|\[.*?\]|\(.*?\)|\{.*?\}', '', address) address = re.sub(r'[\u4e00-\u9fff]+[::]\s*', '', address) # 移除"类型:"前缀 address = re.sub(r'[^\u4e00-\u9fff0-9a-zA-Z\u3000-\u303f\uff00-\uffef\s]', '', address) # 保留中英文数字空格 return re.sub(r'\s+', ' ', address).strip() # 使用示例 addr1_clean = clean_address("【京东】杭州市滨江区江南大道1234号") # 输出: "杭州市滨江区江南大道1234号"这个函数不改变地址本体,仅做“减法”,却能让模型更聚焦于省市区路号等关键地理要素。
阈值不是固定值,而是业务杠杆
原始文档建议0.85为判定阈值,但这只是通用场景的经验值。在不同业务中,它的意义截然不同:
- POI去重场景:宁可漏掉10个重复点,也不能把两个不同商户错标为同一地点 → 需提高阈值至0.92+
- 订单地址纠错场景:用户手输错误常见(如“深证市”“广州市天河去”),需容忍一定拼写误差 → 阈值可降至0.78
- 政务地址归一化场景:要求100%覆盖所有变体,允许后续人工复核 → 阈值设为0.65,召回优先
正确做法:将阈值从硬编码改为配置化,并与业务指标强绑定:
# config.py THRESHOLD_CONFIG = { "poi_deduplication": 0.92, "logistics_correction": 0.78, "gov_standardization": 0.65 } # 在推理逻辑中动态加载 from config import THRESHOLD_CONFIG business_type = "logistics_correction" threshold = THRESHOLD_CONFIG[business_type] is_match = score > threshold这一步虽不涉及模型参数更新,却是微调效果落地的“最后一公里”。
构建高质量微调数据集:少而精胜过多而杂
微调效果70%取决于数据质量,而非数据规模。MGeo已在亿级地址对上预训练,你不需要百万样本,但必须确保每一条都精准反映业务痛点。
数据采集三原则:真实、成对、有标签
- 真实:必须来自你的真实业务日志,而非爬取或合成。例如,从近3个月物流订单中提取被人工标记为“同一地址”的订单对,比用规则生成的10万对更有价值。
- 成对:每条样本必须是
(addr_a, addr_b, label)三元组,其中label为0(不匹配)或1(匹配)。避免单地址标注,MGeo是句子对模型。 - 有标签:标签必须由业务方确认,而非算法自动生成。曾有团队用模糊匹配结果作为伪标签,导致模型学会“抄作业”,在真实场景中全面失效。
标注策略:聚焦“模型当前犯错的边界案例”
不要平均分配正负样本。重点收集两类数据:
- 高置信度误判样本:模型输出0.91但业务确认为不同地址(如“北京朝阳区酒仙桥路10号” vs “北京市朝阳区酒仙桥路10号院”)
- 低置信度难分样本:模型输出0.45~0.55,人工判断需反复斟酌(如“上海浦东新区张江路188号” vs “上海市浦东新区张江高科技园区188号”)
这类样本占比应达总数据集的60%以上。它们才是模型真正的“知识盲区”。
示例:一个高质量微调样本集结构
假设你服务于高校信息化系统,核心挑战是识别“校内不同命名体系下的同一建筑”。你构建了如下200条样本(含120条正样本,80条负样本):
| addr_a | addr_b | label | 业务说明 |
|---|---|---|---|
| 浙江大学紫金港校区东1A教学楼 | 浙大紫金港东1A楼 | 1 | 同一建筑,“浙大”“浙大”缩写一致 |
| 浙江大学玉泉校区教七楼 | 浙大玉泉第七教学楼 | 1 | “教七楼”是师生常用简称 |
| 浙江大学西溪校区艺术楼 | 浙大西溪校区逸夫艺术楼 | 0 | “艺术楼”指代不明,逸夫楼是独立建筑 |
| 杭州师范大学仓前校区诚园1号楼 | 杭师大仓前诚园1栋 | 1 | “栋”与“号楼”在校园语境中完全等价 |
注意:所有地址均经clean_address()预处理,且正负样本比例接近1.5:1(符合地址匹配任务中正样本略多的业务规律)。
实战微调流程:从零开始的端到端操作
本节提供可在镜像内直接执行的完整微调脚本,无需额外环境配置。所有命令均基于镜像预置的py37testmaas环境。
第一步:准备数据与目录结构
在Jupyter工作区创建标准微调目录:
mkdir -p /root/workspace/mgeo_finetune/{data,models,logs}将标注好的CSV文件(train.csv,dev.csv)放入/root/workspace/mgeo_finetune/data/。CSV格式如下:
addr_a,addr_b,label "浙江大学紫金港校区东1A教学楼","浙大紫金港东1A楼",1 "浙江大学玉泉校区教七楼","浙大玉泉第七教学楼",1 ...第二步:编写微调脚本finetune.py
将以下代码保存为/root/workspace/mgeo_finetune/finetune.py:
# finetune.py import os import torch from datasets import Dataset, DatasetDict from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer, DataCollatorWithPadding ) import pandas as pd # =================== 配置区 =================== MODEL_NAME = "/models/mgeo-chinese-address-v1" DATA_DIR = "/root/workspace/mgeo_finetune/data" OUTPUT_DIR = "/root/workspace/mgeo_finetune/models/fine_tuned_v1" LOG_DIR = "/root/workspace/mgeo_finetune/logs" # 微调超参(已针对地址任务优化) TRAINING_ARGS = TrainingArguments( output_dir=OUTPUT_DIR, num_train_epochs=3, # 地址任务收敛快,3轮足够 per_device_train_batch_size=16, # 4090D显存充足 per_device_eval_batch_size=32, warmup_ratio=0.1, # 快速进入稳定训练 learning_rate=2e-5, # 比通用NLP任务略低,保护预训练知识 weight_decay=0.01, logging_dir=LOG_DIR, logging_steps=10, evaluation_strategy="steps", eval_steps=50, save_strategy="steps", save_steps=50, load_best_model_at_end=True, metric_for_best_model="eval_accuracy", greater_is_better=True, report_to="none", # 禁用wandb,简化依赖 fp16=True, # 启用混合精度,加速训练 seed=42 ) # =================== 数据加载与预处理 =================== def load_and_preprocess_data(): def clean_address(address: str) -> str: import re address = re.sub(r'【.*?】|〖.*?〗|\[.*?\]|\(.*?\)|\{.*?\}', '', address) address = re.sub(r'[\u4e00-\u9fff]+[::]\s*', '', address) address = re.sub(r'[^\u4e00-\u9fff0-9a-zA-Z\u3000-\u303f\uff00-\uffef\s]', '', address) return re.sub(r'\s+', ' ', address).strip() train_df = pd.read_csv(os.path.join(DATA_DIR, "train.csv")) dev_df = pd.read_csv(os.path.join(DATA_DIR, "dev.csv")) # 应用清洗 train_df["addr_a"] = train_df["addr_a"].apply(clean_address) train_df["addr_b"] = train_df["addr_b"].apply(clean_address) dev_df["addr_a"] = dev_df["addr_a"].apply(clean_address) dev_df["addr_b"] = dev_df["addr_b"].apply(clean_address) # 转换为Hugging Face Dataset train_dataset = Dataset.from_pandas(train_df) dev_dataset = Dataset.from_pandas(dev_df) return DatasetDict({"train": train_dataset, "validation": dev_dataset}) # =================== 模型与分词器 =================== tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) model = AutoModelForSequenceClassification.from_pretrained( MODEL_NAME, num_labels=2, ignore_mismatched_sizes=True # 兼容微调时的权重加载 ) # =================== 数据预处理函数 =================== def tokenize_function(examples): return tokenizer( examples["addr_a"], examples["addr_b"], truncation=True, max_length=128, padding=True ) # =================== 主训练流程 =================== if __name__ == "__main__": print(" 开始MGeo微调...") # 加载数据 raw_datasets = load_and_preprocess_data() tokenized_datasets = raw_datasets.map( tokenize_function, batched=True, remove_columns=["addr_a", "addr_b", "label"] ) # 数据整理器 data_collator = DataCollatorWithPadding(tokenizer=tokenizer) # 计算准确率指标 def compute_metrics(eval_pred): predictions, labels = eval_pred preds = predictions.argmax(-1) return {"accuracy": (preds == labels).mean()} # 初始化Trainer trainer = Trainer( model=model, args=TRAINING_ARGS, train_dataset=tokenized_datasets["train"], eval_dataset=tokenized_datasets["validation"], tokenizer=tokenizer, data_collator=data_collator, compute_metrics=compute_metrics, ) # 执行训练 trainer.train() # 保存最终模型 trainer.save_model(OUTPUT_DIR) print(f" 微调完成!模型已保存至 {OUTPUT_DIR}")第三步:执行微调并监控过程
在镜像终端中执行:
cd /root/workspace/mgeo_finetune conda activate py37testmaas python finetune.py训练过程约需25分钟(4090D)。关键监控点:
- Loss下降曲线:应在前100步内快速下降,3轮后稳定在0.1以下
- 验证集Accuracy:应从初始的0.75+提升至0.92+(你的业务数据决定上限)
- GPU显存占用:稳定在18GB左右,无OOM风险
若Loss震荡剧烈或Accuracy停滞,检查train.csv中是否存在大量标签错误样本——微调不是魔法,它只会强化你给它的信号。
微调后效果验证:用业务指标说话
微调不是为了在验证集上刷分,而是解决真实业务问题。我们设计三类验证实验,全部基于你自己的业务数据。
实验一:边界案例重测(最直接)
选取微调前模型在你业务中误判率最高的20个地址对,重新用微调后模型计算相似度:
| 地址对 | 原模型得分 | 微调后得分 | 业务真实标签 | 判定改善 |
|---|---|---|---|---|
| “浙大紫金港东1A楼” vs “浙江大学紫金港校区东1A教学楼” | 0.83 | 0.96 | 1 | 从误判为“否”变为正确“是” |
| “杭州师范大学仓前校区诚园1号楼” vs “杭师大仓前诚园1栋” | 0.79 | 0.94 | 1 | |
| “上海交通大学闵行校区东川路800号” vs “上海交大东川路校区800号” | 0.81 | 0.95 | 1 |
若20个案例中15个以上得到改善,微调即成功。
实验二:线上AB测试(最可信)
将微调后模型部署为新服务端点,与原模型并行运行。随机抽取1%生产流量,对比两者的匹配结果:
| 指标 | 原模型 | 微调后模型 | 提升 |
|---|---|---|---|
| 召回率(Top10中含正确地址) | 82.3% | 94.7% | +12.4pp |
| 人工复核率(需人工确认的匹配对占比) | 35.6% | 18.2% | -17.4pp |
| 平均响应时间 | 128ms | 131ms | +3ms(可忽略) |
注意:响应时间微增是正常现象,因微调后模型对输入更敏感,但业务价值提升远超此成本。
实验三:业务闭环验证(最深刻)
选择一个具体业务动作,验证微调是否带来可衡量的业务收益。例如:
- 物流场景:统计微调前后一周内,因地址匹配错误导致的“派件失败”工单数。若从日均12单降至3单,即证明微调直接降低运营成本。
- 政务场景:统计地址标准化系统中,人工干预率(需工作人员手动修正的地址数占比)。若从41%降至19%,说明自动化水平实质性提升。
这才是微调的终极目标——让技术进步转化为业务语言。
工程化部署:让微调成果稳定服务生产
微调完成只是起点,如何让新模型安全、可靠、可持续地服务业务,才是工程落地的关键。
方案一:无缝替换原模型(推荐新手)
将微调后的模型直接覆盖原模型路径:
# 备份原模型 mv /models/mgeo-chinese-address-v1 /models/mgeo-chinese-address-v1-backup # 复制微调模型 cp -r /root/workspace/mgeo_finetune/models/fine_tuned_v1 /models/mgeo-chinese-address-v1 # 重启推理服务(若使用API) # 或直接运行原推理脚本 python /root/推理.py优点:零代码修改,立即生效。缺点:无法回滚,需确保微调效果绝对可靠。
方案二:双模型热切换(推荐生产环境)
修改推理.py,支持动态加载模型:
# 在推理脚本开头添加 import argparse parser = argparse.ArgumentParser() parser.add_argument("--model-path", default="/models/mgeo-chinese-address-v1", type=str) args = parser.parse_args() # 加载模型时使用 model = AutoModelForSequenceClassification.from_pretrained(args.model_path)启动时指定模型:
# 使用原模型 python /root/推理.py --model-path "/models/mgeo-chinese-address-v1" # 使用微调模型 python /root/推理.py --model-path "/root/workspace/mgeo_finetune/models/fine_tuned_v1"配合Nginx或负载均衡器,可实现灰度发布与秒级回滚。
方案三:构建微调流水线(推荐长期迭代)
为持续优化,建立自动化微调Pipeline:
graph LR A[每日新增业务日志] --> B[自动抽样难例] B --> C[触发人工标注任务] C --> D[标注完成通知] D --> E[自动启动微调任务] E --> F[微调完成自动AB测试] F --> G{达标?} G -->|是| H[自动上线新模型] G -->|否| I[邮件告警,人工介入]这需要少量DevOps投入,但一旦建成,你的MGeo模型将随业务演进而持续进化,而非成为静态资产。
总结:微调不是终点,而是地址智能的新起点
本文没有教你如何从零训练一个地址模型,而是聚焦一个务实问题:当开箱即用的MGeo在你的具体业务中表现不够完美时,如何用最小成本、最短路径、最可控方式,让它变得“刚刚好”。
我们梳理了四个不可跳过的环节:
- 认知先行:明确微调的边界——不碰模型主干,但必须优化输入范式与业务阈值;
- 数据为王:用真实、成对、有标签的边界案例构建高质量小样本集;
- 端到端实战:提供可在镜像内直接运行的微调脚本与监控要点;
- 效果验证:用业务指标(而非单纯准确率)定义成功,通过AB测试与闭环验证确认价值;
- 工程落地:给出从简单替换到自动化流水线的多种部署方案。
MGeo的价值,从来不只是一个开源模型,而是一套可演进的地址理解基础设施。微调,是你掌握这套设施主动权的第一步。当你能自主决定模型“学什么”“怎么学”“学到什么程度”,地址匹配就不再是一个黑盒调用,而成为你业务逻辑中可解释、可优化、可传承的核心能力。
下一步,你可以尝试:
- 将微调扩展到多语言地址(如中英混合地址“Beijing University of Posts and Telecommunications” vs “北京邮电大学”)
- 结合GIS坐标,构建“文本相似度+空间距离”的联合打分模型
- 探索Prompt Tuning等轻量微调方法,在资源受限设备上部署
地址,是物理世界的数字锚点。而MGeo微调,就是为你自己的业务世界,校准这个锚点的过程。
1. 微调前的关键认知:什么该调,什么不该碰
1.1 不要碰模型主干结构,但必须调整输入范式
MGeo的底层是经过千万级地址对预训练的双塔+交互式编码器,其空间感知注意力机制和多粒度字段建模能力已高度成熟。直接修改Transformer层数、隐藏层维度或注意力头数,不仅不会提升精度,反而极易导致梯度爆炸或语义坍塌。我们真正需要干预的,是模型“如何看待输入”。
观察原始推理脚本中的输入构造方式:
inputs = tokenizer(addr1, addr2, padding=True, truncation=True, max_length=128, return_tensors="pt")这是标准的句子对分类格式:[CLS] 地址A [SEP] 地址B [SEP]。但在真实业务中,地址常附带非地理信息干扰项。例如物流订单中的“【顺丰】浙江省杭州市西湖区文三路398号”,括号内是承运商标识;政务系统中的“杭州市上城区望江街道海潮路268号(原望江路268号)”,括号内是历史名称。这些内容会稀释模型对核心地理字段的关注。
正确做法:在送入tokenizer前,增加轻量级预处理函数,剥离非地理噪声:
import re def clean_address(address: str) -> str: """移除地址中常见的非地理干扰字符""" # 移除物流标识、订单号、括号内备注、特殊符号 address = re.sub(r'【.*?】|〖.*?〗|\[.*?\]|\(.*?\)|\{.*?\}', '', address) address = re.sub(r'[\u4e00-\u9fff]+[::]\s*', '', address) # 移除"类型:"前缀 address = re.sub(r'[^\u4e00-\u9fff0-9a-zA-Z\u3000-\u303f\uff00-\uffef\s]', '', address) # 保留中英文数字空格 return re.sub(r'\s+', ' ', address).strip() # 使用示例 addr1_clean = clean_address("【京东】杭州市滨江区江南大道1234号") # 输出: "杭州市滨江区江南大道1234号"这个函数不改变地址本体,仅做“减法”,却能让模型更聚焦于省市区路号等关键地理要素。
1.2 阈值不是固定值,而是业务杠杆
原始文档建议0.85为判定阈值,但这只是通用场景的经验值。在不同业务中,它的意义截然不同:
- POI去重场景:宁可漏掉10个重复点,也不能把两个不同商户错标为同一地点 → 需提高阈值至0.92+
- 订单地址纠错场景:用户手输错误常见(如“深证市”“广州市天河去”),需容忍一定拼写误差 → 阈值可降至0.78
- 政务地址归一化场景:要求100%覆盖所有变体,允许后续人工复核 → 阈值设为0.65,召回优先
正确做法:将阈值从硬编码改为配置化,并与业务指标强绑定:
# config.py THRESHOLD_CONFIG = { "poi_deduplication": 0.92, "logistics_correction": 0.78, "gov_standardization": 0.65 } # 在推理逻辑中动态加载 from config import THRESHOLD_CONFIG business_type = "logistics_correction" threshold = THRESHOLD_CONFIG[business_type] is_match = score > threshold这一步虽不涉及模型参数更新,却是微调效果落地的“最后一公里”。
2. 构建高质量微调数据集:少而精胜过多而杂
2.1 数据采集三原则:真实、成对、有标签
- 真实:必须来自你的真实业务日志,而非爬取或合成。例如,从近3个月物流订单中提取被人工标记为“同一地址”的订单对,比用规则生成的10万对更有价值。
- 成对:每条样本必须是
(addr_a, addr_b, label)三元组,其中label为0(不匹配)或1(匹配)。避免单地址标注,MGeo是句子对模型。 - 有标签:标签必须由业务方确认,而非算法自动生成。曾有团队用模糊匹配结果作为伪标签,导致模型学会“抄作业”,在真实场景中全面失效。
2.2 标注策略:聚焦“模型当前犯错的边界案例”
不要平均分配正负样本。重点收集两类数据:
- 高置信度误判样本:模型输出0.91但业务确认为不同地址(如“北京朝阳区酒仙桥路10号” vs “北京市朝阳区酒仙桥路10号院”)
- 低置信度难分样本:模型输出0.45~0.55,人工判断需反复斟酌(如“上海浦东新区张江路188号” vs “上海市浦东新区张江高科技园区188号”)
这类样本占比应达总数据集的60%以上。它们才是模型真正的“知识盲区”。
2.3 示例:一个高质量微调样本集结构
假设你服务于高校信息化系统,核心挑战是识别“校内不同命名体系下的同一建筑”。你构建了如下200条样本(含120条正样本,80条负样本):
| addr_a | addr_b | label | 业务说明 |
|---|---|---|---|
| 浙江大学紫金港校区东1A教学楼 | 浙大紫金港东1A楼 | 1 | 同一建筑,“浙大”“浙大”缩写一致 |
| 浙江大学玉泉校区教七楼 | 浙大玉泉第七教学楼 | 1 | “教七楼”是师生常用简称 |
| 浙江大学西溪校区艺术楼 | 浙大西溪校区逸夫艺术楼 | 0 | “艺术楼”指代不明,逸夫楼是独立建筑 |
| 杭州师范大学仓前校区诚园1号楼 | 杭师大仓前诚园1栋 | 1 | “栋”与“号楼”在校园语境中完全等价 |
注意:所有地址均经clean_address()预处理,且正负样本比例接近1.5:1(符合地址匹配任务中正样本略多的业务规律)。
3. 实战微调流程:从零开始的端到端操作
3.1 第一步:准备数据与目录结构
在Jupyter工作区创建标准微调目录:
mkdir -p /root/workspace/mgeo_finetune/{data,models,logs}将标注好的CSV文件(train.csv,dev.csv)放入/root/workspace/mgeo_finetune/data/。CSV格式如下:
addr_a,addr_b,label "浙江大学紫金港校区东1A教学楼","浙大紫金港东1A楼",1 "浙江大学玉泉校区教七楼","浙大玉泉第七教学楼",1 ...3.2 第二步:编写微调脚本finetune.py
将以下代码保存为/root/workspace/mgeo_finetune/finetune.py:
# finetune.py import os import torch from datasets import Dataset, DatasetDict from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer, DataCollatorWithPadding ) import pandas as pd # =================== 配置区 =================== MODEL_NAME = "/models/mgeo-chinese-address-v1" DATA_DIR = "/root/workspace/mgeo_finetune/data" OUTPUT_DIR = "/root/workspace/mgeo_finetune/models/fine_tuned_v1" LOG_DIR = "/root/workspace/mgeo_finetune/logs" # 微调超参(已针对地址任务优化) TRAINING_ARGS = TrainingArguments( output_dir=OUTPUT_DIR, num_train_epochs=3, # 地址任务收敛快,3轮足够 per_device_train_batch_size=16, # 4090D显存充足 per_device_eval_batch_size=32, warmup_ratio=0.1, # 快速进入稳定训练 learning_rate=2e-5, # 比通用NLP任务略低,保护预训练知识 weight_decay=0.01, logging_dir=LOG_DIR, logging_steps=10, evaluation_strategy="steps", eval_steps=50, save_strategy="steps", save_steps=50, load_best_model_at_end=True, metric_for_best_model="eval_accuracy", greater_is_better=True, report_to="none", # 禁用wandb,简化依赖 fp16=True, # 启用混合精度,加速训练 seed=42 ) # =================== 数据加载与预处理 =================== def load_and_preprocess_data(): def clean_address(address: str) -> str: import re address = re.sub(r'【.*?】|〖.*?〗|\[.*?\]|\(.*?\)|\{.*?\}', '', address) address = re.sub(r'[\u4e00-\u9fff]+[::]\s*', '', address) address = re.sub(r'[^\u4e00-\u9fff0-9a-zA-Z\u3000-\u303f\uff00-\uffef\s]', '', address) return re.sub(r'\s+', ' ', address).strip() train_df = pd.read_csv(os.path.join(DATA_DIR, "train.csv")) dev_df = pd.read_csv(os.path.join(DATA_DIR, "dev.csv")) # 应用清洗 train_df["addr_a"] = train_df["addr_a"].apply(clean_address) train_df["addr_b"] = train_df["addr_b"].apply(clean_address) dev_df["addr_a"] = dev_df["addr_a"].apply(clean_address) dev_df["addr_b"] = dev_df["addr_b"].apply(clean_address) # 转换为Hugging Face Dataset train_dataset = Dataset.from_pandas(train_df) dev_dataset = Dataset.from_pandas(dev_df) return DatasetDict({"train": train_dataset, "validation": dev_dataset}) # =================== 模型与分词器 =================== tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) model = AutoModelForSequenceClassification.from_pretrained( MODEL_NAME, num_labels=2, ignore_mismatched_sizes=True # 兼容微调时的权重加载 ) # =================== 数据预处理函数 =================== def tokenize_function(examples): return tokenizer( examples["addr_a"], examples["addr_b"], truncation=True, max_length=128, padding=True ) # =================== 主训练流程 =================== if __name__ == "__main__": print(" 开始MGeo微调...") # 加载数据 raw_datasets = load_and_preprocess_data() tokenized_datasets = raw_datasets.map( tokenize_function, batched=True, remove_columns=["addr_a", "addr_b", "label"] ) # 数据整理器 data_collator = DataCollatorWithPadding(tokenizer=tokenizer) # 计算准确率指标 def compute_metrics(eval_pred): predictions, labels = eval_pred preds = predictions.argmax(-1) return {"accuracy": (preds == labels).mean()} # 初始化Trainer trainer = Trainer( model=model, args=TRAINING_ARGS, train_dataset=tokenized_datasets["train"], eval_dataset=tokenized_datasets["validation"], tokenizer=tokenizer, data_collator=data_collator, compute_metrics=compute_metrics, ) # 执行训练 trainer.train() # 保存最终模型 trainer.save_model(OUTPUT_DIR) print(f" 微调完成!模型已保存至 {OUTPUT_DIR}")3.3 第三步:执行微调并监控过程
在镜像终端中执行:
cd /root/workspace/mgeo_finetune conda activate py37testmaas python finetune.py训练过程约需25分钟(4090D)。关键监控点:
- Loss下降曲线:应在前100步内快速下降,3轮后稳定在0.1以下
- 验证集Accuracy:应从初始的0.75+提升至0.92+(你的业务数据决定上限)
- GPU显存占用:稳定在18GB左右,无OOM风险
若Loss震荡剧烈或Accuracy停滞,检查train.csv中是否存在大量标签错误样本——微调不是魔法,它只会强化你给它的信号。
4. 微调后效果验证:用业务指标说话
4.1 实验一:边界案例重测(最直接)
选取微调前模型在你业务中误判率最高的20个地址对,重新用微调后模型计算相似度:
| 地址对 | 原模型得分 | 微调后得分 | 业务真实标签 | 判定改善 |
|---|---|---|---|---|
| “浙大紫金港东1A楼” vs “浙江大学紫金港校区东1A教学楼” | 0.83 | 0.96 | 1 | 从误判为 |