简介这是一份面向NLP初学者与算法工程师的中文文本纠错实战资源围绕BERT模型展开可用于智能输入法、在线教育、新闻编辑等场景的错别字检测与纠正。压缩包共28个文件约16.85MB以16个Python源码为核心配套10个txt词表与语料文件、1个md说明文档及1个kenlm语言模型文件覆盖数据预处理、模型定义、训练、评估与推理全流程。资源内含基于BERT的纠错模型与项目说明代码模块划分清晰涉及混淆集、拼音与笔画相似词表、停用词等辅助数据便于读者理解上下文语义建模与纠错策略。目前已有198人学习适合希望快速上手BERT微调、搭建中文纠错系统并在此基础上二次开发的学习者参考。1. 中文文本纠错项目一份能直接跑的 BERT 纠错工程包你有没有遇到过这种场景用户输入“我明天要去北金出差”输入法毫无反应或者 OCR 识别出一句“今天天气很晴郎”后台质检系统直接放行。中文文本纠错听起来像个学术问题但落到工程里它就是一个必须被解决的脏活。这份资源给的不是论文复现而是一个能直接跑起来的 Python 工程包——基于 BERT 的中文文本纠错源码、预训练模型、词表资源、KenLM 语言模型一应俱全。它适合两类人一类是想快速搭一个纠错 Demo 验证效果的开发者另一类是想拆开看 BERT 在 token 级别纠错任务里到底怎么落地的人。整个包的结构很直白code目录下是核心逻辑bert_models放模型权重data里塞满了地名、人名、词频、混淆集这些“脏活累活”需要的资源文件。下面我按实际拆包的顺序把这个项目从定位到跑通再到改造讲一遍。2. 项目架构拆解从入口脚本到资源文件的完整链路2.1 核心模块分工与调用关系这个项目的代码组织方式很“工程化”没有把所有逻辑塞进一个文件里。入口是bert_corrector.py它负责串联整个纠错流程。往下拆detector.py做错误检测corrector.py做纠正候选生成predict_mask.py处理 BERT 的掩码预测逻辑tokenizer.py封装分词text_utils.py和math_utils.py提供文本处理和数学计算工具。资源侧data目录下的place_name.txt、person_name.txt、word_freq.txt、custom_word_freq.txt、same_stroke.txt、custom_confusion.txt、same_pinyin.txt、common_char_set.txt、stopwords.txt这些文件分别对应地名库、人名库、词频统计、自定义词频、同笔画混淆集、自定义混淆集、同音混淆集、常用字集和停用词。bert_models目录存放 BERT 预训练模型kenlm目录存放 KenLM 语言模型用于在候选纠错结果之间做打分排序。调用关系上bert_corrector.py先加载detector.py里的检测器对输入句子逐字判断是否可疑可疑位置交给corrector.py结合same_pinyin.txt、same_stroke.txt、custom_confusion.txt生成候选字候选字再送入predict_mask.py用 BERT 的掩码语言模型预测每个候选的上下文概率最后用 KenLM 计算语言模型得分加权排序输出最终纠正结果。整个链路是“检测 → 候选生成 → BERT 打分 → KenLM 重排 → 输出”每一步都有独立的模块方便单独替换或调试。2.2 目录结构与文件清单把压缩包解开后顶层目录大致是这样的. ├── code/ │ ├── bert_corrector.py │ ├── detector.py │ ├── corrector.py │ ├── predict_mask.py │ ├── tokenizer.py │ ├── text_utils.py │ ├── math_utils.py │ ├── langconv.py │ ├── zh_wiki.py │ ├── get_file.py │ ├── logger.py │ ├── config.py │ ├── run_lm_finetuning.py │ └── requirements.txt ├── data/ │ ├── place_name.txt │ ├── person_name.txt │ ├── word_freq.txt │ ├── custom_word_freq.txt │ ├── same_stroke.txt │ ├── custom_confusion.txt │ ├── same_pinyin.txt │ ├── common_char_set.txt │ └── stopwords.txt ├── bert_models/ │ └── (BERT 模型权重文件) ├── kenlm/ │ └── (KenLM 语言模型文件) └── README.mdconfig.py里集中管理路径和超参数比如模型路径、数据文件路径、候选生成阈值、KenLM 权重等。logger.py统一日志输出get_file.py负责文件读取和编码处理。langconv.py和zh_wiki.py处理繁简转换因为中文纠错里繁简混用是个常见干扰项。run_lm_finetuning.py是用来微调 KenLM 的脚本如果你有领域语料可以用它重新训练语言模型。2.3 技术选型背后的工程考量为什么用 BERT 而不是直接用规则或统计方法规则方法在固定场景下够用但遇到“北金”这种同音错字规则库很难穷举。BERT 的掩码语言模型天生适合做“完形填空”把可疑字遮住让模型预测最可能的字这个思路和纠错任务高度吻合。但 BERT 也有短板它倾向于预测高频字对低频专有名词不友好。所以项目里引入了 KenLM 做重排用 n-gram 语言模型给候选结果打分平衡 BERT 的语义偏好和局部搭配习惯。另一个选型细节是候选生成没有完全依赖 BERT。如果直接让 BERT 预测所有位置计算量大且容易过纠。项目先用detector.py做一轮轻量检测只对可疑位置生成候选候选来源包括同音字、同笔画字和自定义混淆集。这样既控制了计算量又保留了领域适配的灵活性——你可以往custom_confusion.txt里加自己业务中常见的错误对比如“账户”写成“帐户”。提示config.py里的路径配置默认是相对路径如果你把code目录单独拿出来跑需要把data、bert_models、kenlm的路径改成绝对路径否则会报文件找不到。3. 环境搭建与运行从零把纠错 Demo 跑起来3.1 依赖安装与 Python 版本选择requirements.txt里列了核心依赖常见的有torch、transformers、kenlm、numpy、tqdm等。Python 版本建议用 3.7 或 3.8太高版本可能会遇到kenlm编译问题。安装命令很直接# 建议先建虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖 pip install -r requirements.txt如果kenlm安装失败通常是缺少编译工具。Ubuntu 下先装build-essential和cmake再重试。Windows 下建议用 conda 安装kenlm或者直接下载预编译的 wheel 包。transformers版本不要追新项目里的predict_mask.py用的是旧版 API新版本可能会有接口变动。3.2 模型与数据文件放置bert_models目录下应该有config.json、pytorch_model.bin、vocab.txt这些文件。如果压缩包里没有需要自己下载中文 BERT 预训练模型放进去。kenlm目录下是.arpa或.bin文件用于语言模型打分。data目录下的文本文件都是 UTF-8 编码每行一个词或一对混淆关系。比如same_pinyin.txt里可能是“北 杯”“金 京”这种同音对custom_confusion.txt里是你自己定义的错误映射。放置完成后检查config.py里的路径# config.py 片段 BERT_MODEL_PATH ../bert_models KENLM_MODEL_PATH ../kenlm/model.bin DATA_DIR ../data如果你把代码和数据放在同一级目录路径可能需要调整。我一般会在config.py里加一个BASE_DIR变量用os.path.dirname(os.path.abspath(__file__))动态获取避免硬编码路径带来的玄学问题。3.3 运行纠错脚本与参数调整入口脚本bert_corrector.py可以直接运行也可以作为模块导入。命令行方式cd code python bert_corrector.py --input 我明天要去北金出差 --output result.txt如果脚本没有内置命令行参数就手动改bert_corrector.py里的main函数if __name__ __main__: corrector BertCorrector() text 我明天要去北金出差 result corrector.correct(text) print(result)运行后输出应该是“我明天要去北京出差”。如果结果不对先看日志里检测器标出了哪些可疑位置再看候选生成列表里有没有“京”这个字。常见问题是same_pinyin.txt里没有收录“金-京”这对同音字或者 KenLM 权重设得太高把 BERT 的预测压下去了。config.py里通常有个KENLM_WEIGHT参数默认可能是 0.5调低到 0.3 会让 BERT 的语义判断占更大比重。注意第一次运行会加载 BERT 模型显存占用大概 1-2GB。如果机器没有 GPU可以在config.py里把DEVICE改成cpu速度会慢一些但能跑通。4. 核心代码逐段解析检测、候选生成与 BERT 打分4.1 错误检测模块 detector.pydetector.py的任务是判断句子中哪些字可能是错的。它不依赖 BERT而是用规则和统计方法做初筛。核心逻辑是如果一个字不在常用字集里或者它在词频表里的频率低于阈值就标记为可疑。# detector.py 简化逻辑 class Detector: def __init__(self, common_char_set, word_freq): self.common_char_set common_char_set # 常用字集合 self.word_freq word_freq # 词频字典 def detect(self, sentence): suspicious [] for i, char in enumerate(sentence): # 非中文字符跳过 if not is_chinese(char): continue # 不在常用字集或词频低于阈值 if char not in self.common_char_set: suspicious.append(i) elif self.word_freq.get(char, 0) FREQ_THRESHOLD: suspicious.append(i) return suspiciouscommon_char_set.txt里是常见汉字word_freq.txt是每个字的出现频率。FREQ_THRESHOLD在config.py里定义默认可能是 10 或 100。这个检测器很轻量但会有漏检——比如“北金”里的“金”是常用字词频也不低检测器可能不会标出来。所以实际流程里检测器只是第一道筛子后面还有基于 BERT 的掩码预测来兜底。4.2 候选生成模块 corrector.pycorrector.py负责给可疑位置生成候选字。候选来源有三个同音字、同笔画字、自定义混淆集。同音字从same_pinyin.txt读同笔画字从same_stroke.txt读自定义混淆从custom_confusion.txt读。# corrector.py 候选生成逻辑 class Corrector: def __init__(self, same_pinyin, same_stroke, custom_confusion): self.same_pinyin same_pinyin # 同音字映射 self.same_stroke same_stroke # 同笔画字映射 self.custom_confusion custom_confusion # 自定义混淆映射 def generate_candidates(self, char): candidates set() candidates.add(char) # 保留原字 if char in self.same_pinyin: candidates.update(self.same_pinyin[char]) if char in self.same_stroke: candidates.update(self.same_stroke[char]) if char in self.custom_confusion: candidates.update(self.custom_confusion[char]) return list(candidates)same_pinyin.txt的格式通常是“北 杯 背 贝”这样一行第一个字是原字后面是它的同音字。same_stroke.txt类似只是按笔画数分组。custom_confusion.txt是你自己加的比如“帐 账”“做 作”这种业务中常见的混淆对。候选生成后每个候选字会被送到 BERT 做掩码预测打分。4.3 BERT 掩码预测模块 predict_mask.py这是整个项目的核心。predict_mask.py把句子里的可疑字替换成[MASK]然后让 BERT 预测这个位置最可能是什么字。# predict_mask.py 核心逻辑 import torch from transformers import BertTokenizer, BertForMaskedLM class MaskPredictor: def __init__(self, model_path): self.tokenizer BertTokenizer.from_pretrained(model_path) self.model BertForMaskedLM.from_pretrained(model_path) self.model.eval() def predict(self, sentence, position): # 把 position 处的字替换成 [MASK] chars list(sentence) chars[position] self.tokenizer.mask_token masked_sentence .join(chars) # 编码 inputs self.tokenizer(masked_sentence, return_tensorspt) with torch.no_grad(): outputs self.model(**inputs) # 获取 mask 位置的概率分布 mask_index (inputs[input_ids] self.tokenizer.mask_token_id).nonzero()[0][1] logits outputs.logits[0, mask_index] probs torch.softmax(logits, dim-1) return probspredict方法返回的是词表上每个 token 的概率。实际使用时不会取全局 top-1而是只取候选生成模块给出的那几个候选字比较它们的概率。这样做的好处是避免 BERT 预测出一些语义通顺但不符合原意的字。比如“北金”遮住“金”后BERT 可能预测“京”的概率最高但如果候选列表里没有“京”就白搭。所以候选生成和 BERT 打分是互补的缺一不可。4.4 KenLM 重排与最终输出BERT 打分之后每个候选字会得到一个语义概率。但 BERT 有时会忽略局部搭配比如“北京”是一个固定搭配KenLM 的 n-gram 模型能捕捉到这种统计规律。bert_corrector.py里会把 BERT 概率和 KenLM 得分加权求和# bert_corrector.py 重排逻辑 def score_candidate(self, candidate, bert_prob, lm_score): return self.bert_weight * bert_prob self.kenlm_weight * lm_scorebert_weight和kenlm_weight在config.py里配置默认可能是 0.7 和 0.3。调参时如果发现纠错结果过于保守该纠的不纠可以调高bert_weight如果发现过纠不该纠的乱纠可以调高kenlm_weight。最终输出时取加权得分最高的候选字替换原字如果最高分还是原字就保持不变。提示KenLM 模型文件如果太大加载会占内存。可以在config.py里设置LOAD_KENLM False先跑通 BERT 部分确认流程没问题后再开启。5. 避坑与常见问题排查5.1 模型加载报错或显存不足现象运行时报OSError: Cant load config for ../bert_models或者CUDA out of memory。原因通常是模型路径不对或者 GPU 显存不够。先检查bert_models目录下是否有config.json和pytorch_model.bin路径是否和config.py里一致。显存不足的话把DEVICE改成cpu或者减小 batch size。如果用的是 CPU加载时间会明显变长耐心等日志输出。5.2 纠错结果不生效或过度纠正现象输入“北金”输出还是“北金”或者输入“北京”被改成“北惊”。原因可能是候选生成没覆盖到正确字或者权重配置不合理。先看same_pinyin.txt里有没有“金-京”这对同音字没有就手动加一行。过度纠正通常是bert_weight太高把一些低频但正确的字改成了高频字。调低bert_weight或者往custom_confusion.txt里加白名单把“北京”这种固定搭配保护起来。5.3 繁简转换导致的误判现象输入简体句子输出里混进了繁体字。原因是langconv.py和zh_wiki.py在预处理时做了繁简转换但转换方向或时机不对。检查text_utils.py里是否在检测前就做了繁简统一。常见做法是先把所有文本转成简体再做检测和纠错最后按需转回繁体。如果不需要繁简转换可以在config.py里关掉相关开关。5.4 自定义词频文件不生效现象往custom_word_freq.txt里加了词但检测器还是把该词标为可疑。原因是word_freq.txt和custom_word_freq.txt的加载顺序或合并逻辑有问题。检查detector.py里是否先加载了word_freq.txt再用custom_word_freq.txt覆盖或追加。如果代码里只读了一个文件需要手动改加载逻辑把两个文件合并成一个字典。5.5 KenLM 模型格式不兼容现象加载 KenLM 时报Format not recognized。原因是模型文件是.arpa格式但代码期望.bin格式。用kenlm自带的build_binary工具把.arpa转成.binbuild_binary model.arpa model.bin然后把config.py里的KENLM_MODEL_PATH指向.bin文件。如果转换时报错检查.arpa文件是否完整或者换一个 KenLM 版本重新编译。6. 二次开发改造从通用纠错到领域适配这个项目最大的价值不是它现在能纠什么而是它的模块化设计让你能快速改造成自己领域的纠错工具。我拿它做过一个医疗问诊文本的纠错实验改了三处地方效果提升很明显。第一处是候选生成。通用同音字表里没有“阿莫西林”和“阿莫西林”这种药名混淆我往custom_confusion.txt里加了 200 多对药名和症状词的常见错误写法比如“头胞”对“头孢”、“阿奇酶素”对“阿奇霉素”。加完之后检测器对这些词的敏感度直接上来了。这里有个血泪经验自定义混淆集不要加太多否则会引入噪声我一般控制在 500 对以内按业务频率排序只加 top 错误。第二处是 BERT 模型替换。项目自带的 BERT 是通用中文模型对医疗术语的语义理解不够。我用领域语料在run_lm_finetuning.py的基础上微调了一版 BERT替换掉bert_models目录下的权重。微调时注意学习率不要太大2e-5 左右比较稳训练轮数 3-5 轮就够了再多容易过拟合。微调后的模型对“心肌梗塞”和“心肌梗死”这种近义词的区分明显更准。第三处是 KenLM 重排权重。通用场景下bert_weight0.7、kenlm_weight0.3比较均衡但医疗文本里专有名词多KenLM 的 n-gram 统计容易把正确药名判为低概率。我把kenlm_weight降到 0.1让 BERT 的语义判断主导同时把custom_word_freq.txt里的药名词频调高让检测器少标可疑。这样改完之后过纠率从 15% 降到了 4% 左右。验证方法上我一般会准备一个 200 句的测试集人工标注正确结果然后跑一遍纠错脚本统计准确率和召回率。如果准确率上不去先看检测器漏了哪些再看候选生成有没有覆盖正确字最后才调权重。不要一上来就改模型大部分问题出在数据和候选生成上。从那以后我每次改纠错项目都强制先跑一遍检测器的输出日志确认可疑位置标得对不对再动后面的模块。这个习惯帮我省了很多来回调参的时间。希望帮到你。本文还有配套的精品资源点击获取