很多人做中文NLP项目的第一步往往不是纠结用什么模型而是先栽在“分词”上。英文有天然的空格边界中文没有而分词结果的每一个错误都会顺着流水线传染给词性标注、命名实体识别最终污染上层一切应用——文本分类、信息抽取、知识图谱构建几乎没有哪个任务能逃过这一劫。所以我才坚持把分词、词性标注、命名实体识别这三件事放在一起讲它们不是三个彼此独立的“入门练习”而是同一套地基工程拆开看各有各的难点合起来才是完整的中文文本结构化方案。这篇文章算是我的一个阶段复盘。做自然语言处理的这些年我在小到几万条评论语料、大到上亿级别的行业报告里反复处理过这三类任务中间踩过的坑、总结出的选型和调参经验比教科书里写得更实在。如果你是刚接触NLP的学生或者正在做文本挖掘、知识库、智能问答之类项目的工程师这篇文章应该能帮你把三件事的顺序、原理和工具取舍一次理顺。1. 分词没那么简单歧义与未登录词两大难题1.1 从一句话到一串词中文为什么没有天然边界对中文来说分词是英文里不存在的特殊问题。英文句子每个单词之间都有空格计算机用空格切分就能拿到接近合理的token串中文是连续的汉字序列词和词之间没有分隔符算法必须先判断在哪里断开才会得到有意义的词汇单元。问题在于这个“判断”并不总是唯一的。比如“研究生命起源”这句既可以理解成“研究/生命/起源”也可以理解成“研究生/命/起源”上下文稍微一变合法切分就完全不同。再比如经典的“乒乓球拍卖完了”——“乒乓球拍/卖完了”和“乒乓球/拍卖/完了”在脱离语境时都成立。这种场景在真实语料里大量出现有时候连人都需要靠更大的上下文才能判断机器就更不用说。所以我一直跟团队强调一个概念分词是中文NLP的上游水位水位一高下游全淹。后来我们做词性标注和NER时遇到的大量诡异结果追根溯源一半以上都是分词阶段埋下的雷。1.2 三类主流分词算法词典匹配、统计模型与混合式分词算法演进到今天大体可以分成三条路线。初学时不一定要把每条路都吃透但至少要明白它们各自的思路和边界。第一条是词典最大匹配。核心逻辑很简单准备一个足够大的词典从前向后正向最大匹配或从后向前逆向最大匹配尝试切出最长的词。正向最大匹配处理“我们研究生命起源”时会先从“我”开始找最长词大概率得到“我们/研究/生命/起源”但在“南京市长江大桥”这种句子上就很容易翻车正向最大匹配会切出“南京市/长江大桥”而“南京市长江大桥”这种歧义结构逆向匹配可能又给出另一种结果。所以实践中常用双向最大匹配加消歧规则谁切出来的词数量更少、单字更少就倾向谁。第二条是统计模型。不再依赖词典而是根据大规模语料统计来判断切分概率。最简单的N-gram模型会计算“研究/生命”和“研究生/命”这两组词序列在语料里出现的概率谁概率高选谁。再进一步就是用隐马尔可夫模型HMM把分词视为按字标注问题——每个字可以标注为词首、词中、词尾或单字词用维特比算法求解整个序列最可能的标注结果。统计模型的优势是能处理一部分词典里没有的组合但对低频片段容易产生偏差。第三条是混合式也是现在大多数开源中文分词工具的默认方案。先靠词典做召回主体再用统计模型处理未登录词和歧义片段。比如广泛使用的中文分词库jieba底层就是词典最大匹配加HMM的混合结构。这种方案的工程性和准确性比较平衡也是我早期项目里最常用的起点。1.3 分词最大的两个坑歧义与未登录词前面提过的“武汉市长江大桥”“研究生命起源”属于交集型歧义和组合型歧义这类问题靠上下文能解决一部分但靠单句分词器很难根治。更让项目头疼的是另一个问题未登录词。未登录词指的是词典里原本不存在的新词、人名、地名、产品名、专业缩写。比如“某某直播间的弹幕里全是新梗我只想问‘这是什么意思’”这里的网络新词大概率不在常用词典里分词器就把它拆成“这/是/什么/意思”语义倒不丢但如果你要做关键词提取或实体识别这个新词就彻底漏掉了。处理未登录词的常见思路有两个一个是在分词阶段给模型提供更强的上下文表征让它在训练时见过类似模式后能泛化另一个是工程上最直接的办法——自定义词典。无论是jieba还是其他工具都支持在词典里追加领域词汇加上词频和词性信息。这个操作在快速原型阶段几乎是必做的后面我会专门讲自定义词典怎么维护才不会帮倒忙。2. 词性标注为每个词标好“身份标签”2.1 词性标注为什么重要它不只是语法课作业词性标注Part-of-Speech TaggingPOS Tagging的任务是给句子里的每个词打上标签指出它是名词、动词、形容词、副词还是其他类别。对初学者来说这看起来像是中学语文课的“词性判断”但放到NLP里它解决的是很实际的歧义和结构化问题。比如“打”这个字在不同语境里可以是动词“打篮球”、量词“一打啤酒”甚至是介词性用法“打去年开始”。如果电脑只知道字面不知道它在句子里承担的角色后续的句法分析、语义角色判断、关键词权重计算都会出现偏差。我做情感分析项目时就有过一个很直观的体验形容词和副词的组合往往决定了句子情绪的强烈程度比如“这部电影非常好看”里“非常”是副词强化“好看”的形容词程度。如果词性标注不可靠这类权重就无法准确建模。另外词性标注也是句法分析、命名实体识别和关系抽取等上层结构预测的重要输入。可以说它是让计算机从“认识词”走向“理解句子”的桥梁。2.2 主流词性标注方法与常用词性体系词性标注的方法演进和分词非常相似。规则时代的做法是查词典加手工规则如果词典里“领导”可以标名词也可以标动词就根据上下文“领导我们”“公司领导”之类的模式判断。规则法在小领域能凑合用但规则相互冲突后维护成本极高很快就退居辅助地位。随后是统计机器学习时代主流工具用HMM或条件随机场CRF来做序列标注。HMM的思路是词性序列是隐含状态句子里的词是观测值利用转移概率前一个词性到后一个词性的概率和发射概率某个词性生成某个词的概率通过维特比算法找最可能的词性序列。CRF则更进一步不假设严格的独立性可以直接把上下文特征组合成特征函数在兼顾左右信息的基础上做全局最优标注。在那个年代CRF几乎就是词性标注的优选方案一直到深度学习出现。深度学习方法现在基本有两种形态一是双向LSTMBiLSTM编码词序列再对每个位置做分类二是用BERT这类预训练模型把整句话编码成上下文相关的向量再在上面接一个分类头。预训练模型的优势很明显它天然注意词所在的具体上下文对兼类词的消歧能力比传统模型强一大截。至于词性体系中英文各不相同。英文里最常见的是宾州树库Penn Treebank的约45个标签集中文领域则流传多种标注规范核心类别大致包括名词、动词、形容词、代词、数词、量词、副词、介词、连词、助词、语气词等但细分粒度差别很大。做中文项目的时候我强烈建议在一开始就固定标签体系不要乱改否则后续下游任务的适配会被反复折腾。2.3 词性标注实践中容易踩的坑第一个坑是分词错误连带标签错误。这个词性问题表面上在标注层根子却在分词层——词边界一旦切错标注模型根本无从正确分类。所以排查标注错误时我一般先看原始切分对不对而不是急着调标注模型的参数。第二个坑是领域词性偏移。通用语料里“安装”大概率是动词但在软件工程文档里“安装包”“安装步骤”里的“安装”却在组合成名词性结构。如果模型没有见过来自该领域的训练数据标出来的结果会显得非常“教科书化”但不贴合业务。这时候需要补充领域标注数据或者用词典强行指定领域词的优先词性。第三个坑和工程习惯有关不要丢掉词性标注的原始输出。有些项目组为了省存储只保留分词结果词性标完就丢弃。等到做特征分析或多轮迭代时又要重新跑一遍全量语料白白浪费算力和时间。我会在管线上把标注结果直接输出成结构化字段比如JSON或TSV长期保留以便后续各种分析任务随时取用。3. 命名实体识别从词典到深度序列标注的进阶之路3.1 NER 到底要解决什么样的问题命名实体识别Named Entity RecognitionNER解决的是更具体的一件事从文本中找出人名、地名、机构名、时间、金额、产品名等专有名词并标注其类别。它和分词、词性标注最大的区别在于——前两者处理的是“每一个字/词”的角色而NER关心的是“连续片段”的边界和类型。比如“张三在北京市朝阳区人民法院工作”这句话识别结果应该是“张三/人名”“北京市朝阳区人民法院/机构名”。你不但要知道“北京市朝阳区人民法院”从哪里开始到哪里结束还得给它归对类别。这一步做不好后面的关系抽取无从谈起。我早年用规则做文本抽取时最深的体会是人名的识别远比想象中复杂。中文姓氏并不仅是常见百家姓“令狐”“欧阳”这类复姓也频繁出现人名又常和历史人物、网络昵称混在一起词典根本拦不住。这类问题一旦出现规则的破洞就一个接一个所以后来我才坚决转向序列标注方案。3.2 三条技术路线规则、特征工程、深度学习规则与词典路线是NER的起点。词典匹配能解决固定词表内的实体如药品名、车型、赛事名正则表达式可以稳定抓取邮箱、日期、金额、身份证号这类模式高度固定的字段。对纯垂直领域、文本格式相对规整的场景规则方案甚至可以上线。但它有个致命问题覆盖率完全取决于词表规模遇到未收录的新实体就失效且维护成本随时间陡增。传统机器学习路线更准确地说是“特征工程CRF”路线。做法是把NER建模为序列标注问题给每个字或词标上B、I、O之类的标签B表示实体开始BeginI表示实体内部InsideO表示非实体Outside。输入则是一串人工设计的特征如前一个词、后一个词、是否数字、是否大写、词性标签、词在词典中是否出现等。CRF模型可以利用标签之间的依赖约束来保证输出序列合法如I标签不能出现在B之前。这种方案在BERT普及前的很多年里都是工业界的主力直到今天当标注数据很少时它依然是非常务实的选择。深度学习路线里BiLSTM-CRF是最经典的组合BiLSTM在字级别编码双向上下文输出每个位置属于各实体类别的得分CRF层再对这些得分做序列级约束保证标签转移路径合理。后来预训练模型的兴起基本取代了BiLSTM的编码部分直接用BERT等模型提取上下文特征再接CRF或线性分类层。效果提升非常明显尤其在长文本、歧义性强、实体类型多的场景里F1值能比传统方案高出很大一截。3.3 B、I、O标注的具体写法和注意事项这里的BIO标注格式是整个NER实践的基础值得单独列一下。举个例子“张三在北京某科技公司工作。”人名的词边界较清晰地表示为张 B-PER 三 I-PER 在 O 北 B-LOC 京 I-LOC 某 O 科 B-ORG 技 I-ORG 公 I-ORG 司 I-ORG 工 O 作 O我见过不少刚入门的同学在这个环节吃亏。一个常见错误是全篇只有O和B没有I于是实体稍长一点模型就无从判断内部关系另一个错误是不同标注员对实体边界理解不一致同一段文本标注出完全不同的结果导致训练集噪声很大。所以在标注开始之前标注规范的制定和一致性校验先于模型构建这一步偷懒后面模型调参再努力也补不回来。3.4 NER工程化的典型坑与应对经验第一类坑是标签不平衡。绝大多数文本里O类标签占比远高于实体标签模型很容易学会“猜O”而得到很高的准确率但F1却很平庸。常见的应对方式是给损失函数里的实体类别加权或者对O类样本做下采样。我通常还会单独监控每一类实体的F1而不只看总体指标因为“总体不错”很可能是被数量占比大的一类实体拉高其他类别实际崩了。第二类坑是嵌套实体。比如“中国工商银行北京市分行”整体是机构名而其中“北京市”又是地名。扁平BIO标注无法同时表达两层实体这也是很多“NER模型效果不错但总差一口气”的原因之一。常规做法是分层标注或采用阅读理解式的抽取方案比如把“找出文中的机构名”当作问题去问答。如果项目规模不大我也接受先取最长实体通过后处理规则把嵌套的低层实体再抽取一轮。4. 工具选型中文NLP工具箱里的实战对比与取舍4.1 不要什么都自己造轮子但要清楚轮子的边界处理中文NLP基础任务成熟的开源工具已经足够覆盖大部分场景。真正需要自己动手写模型的场景非常有限——要么是极其特殊的领域和实体类型要么是对推理性能有苛刻要求。多数情况下选一个合适的基础工具并做好配置比从零训练模型性价比高得多。我的经验是把工具分成几类。第一类是轻型分词组件安装简单、速度极快适合做清洗、统计、关键词抽取这类不追求精细结构的任务第二类是一站式中文NLP工具包一次调用就能给出分词、词性、NER、依存句法等多种结果适合快速搭建完整文本分析管线第三类是深度学习框架加预训练模型适合标注数据充足、需要追求领域内SOTA效果的项目。下面这张表是我自己整理的一个粗略参考参数指标依版本和任务会有浮动工具类型典型代表优势需要注意的点轻型分词库jieba安装简单、速度快、自定义词典方便NER和词性标注能力弱长文本质量一般一站式中文NLP工具链HanLP、LTP、LAC多任务统一输出接口清晰部分任务依赖较大模型离线初次加载慢跨语言工业级框架spaCy、Stanza生态完整训练/推理流程标准化中文预训练模型往往需要额外适配预训练微调方案BERT家族效果上限高、多任务迁移强需要GPU资源、需要标注数据、推理成本高4.2 落地选型时优先看三个维度面对这么多选项怎么定夺我一般只看三件事。第一是实时性需求。如果任务是离线批量处理几百万条文本哪怕每条花几十毫秒也没问题如果要做在线接口延迟就必须压到几十毫秒内。此时轻量分词库或者量化后的模型是合理选择大模型直接部署很危险。第二是领域适配成本。你做金融公告、法律文书还是客服对话差异极大。如果只是想先把流程跑通那用通用工具可以但如果实体类型高度垂直比如“案号”“法条”“税种”几乎必然要引入自定义词典或领域微调。这时候选型时就得考虑该工具是否支持便捷的词典扩展和模型重训。第三是数据与算力现状。有标注数据、有GPU那自然可以走预训练微调如果两者都没有传统的CRF或现成工具反而更可靠。一个反面教材是我早期做某个文本抽检项目时一开始就上了BERT结果因为标注数据太少、领域噪声太大效果还没用词典加规则好白白忙了两周。后来老老实实回到“规则CRF”的组合先把流程跑起来攒够数据再升级反而稳妥。4.3 自定义词典的实战配置与一个经典小坑自定义词典是让通用工具快速适配领域的最短路径。以jieba为例词典文件的每一行大致是“词语 词频 词性”例如鸿蒙应用开发 10 nz 数据中台 20 n 私域运营 15 vn加载方式也很简单把文件放在路径里调用时传入即可。需要注意的词频数值会直接影响分词器在歧义路径里的选择。词频太低新词可能还是拼不过现有组合词频设得太高又可能把不该合并的词绑在一起。我踩过的一个经典小坑是这样的在某个商品评论项目里我把“不美丽”作为自定义词加了进去想把它当作情感短语识别。结果词频设太高导致所有“不/美丽/的/包装”这类句子都被强制切分成“不美丽/的/包装”。表面看切对了但后续词性标注和情感模型需要的是“不”作为否定词修饰“美丽”的独立结构词被合并后反而丢失了这种语言学关系标注结果变差。后来我调整策略自定义词典只用来加真正的实体和专有术语不轻易加短语结构。另外一个习惯是每次修改自定义词典后都要在同一份评测集上重新跑一遍分词和下游任务对比F1变化。不要凭直觉判断“加了词一定更好”因为分词器的行为是全局的一个词加入可能影响一大片相邻句子的切分。5. 三件事如何协作完整流水线与一次真实项目的复盘5.1 流水线顺序从字符到结构化字段在实际项目中分词、词性标注、NER极少彼此独立而是按固定顺序串成一条流水线原始文本 → 清洗与分句 → 分词 → 词性标注 → 命名实体识别 → 结构化输出。这样排列是有原因的。分词为词性标注提供基本的词单元词性标注又为NER提供丰富的上下文特征比如“机构名”通常由名词短语构成后面常跟“有限公司”“集团”这类词。虽然现在也有联合模型但流水线的价值在于每个环节都可以单独调试、单独替换、单独统计错误。我在一次线上问题排查中顺着结果一路向上查最终定位到分词环节把某个产品名切碎了才导致下游NER把实体一分为二。如果用的是端到端黑盒模型类似问题定位起来会困难得多。5.2 联合模型与流水线的取舍既然流水线错误会传导那为什么不直接做“联合模型”一次输出所有结构这种想法很自然实际学术界也的确有大量把分词、词性标注、NER放在一个模型里联合建模的工作。联合模型的优势是共享表征、避免错误级联但它的工程代价也很明显调试不直观、某个子任务效果不好时很难单独修。我的取舍原则是如果你在做一个长期维护的系统且团队成员能接受分环节排查流水线足够可靠如果你追求极致的整体精度且有充足标注数据与算力再去考虑端到端联合方案。还有一种折中策略——用预训练模型给每个环节都提一层上下文向量各环节共享同一套向量但不共用标签层。这样既保留了一定独立性又让上游信息更丰富。5.3 从零搭一套基础任务管线的步骤参考如果是面对一批全新的业务文本我的标准动作大致是这样的先把文本做清洗和分句去掉无意义的HTML标签、多余空白、特殊符号再按句号、问号、感叹号切分句子。接着选一个基础工具先快速输出全量数据的分词和词性结果存成带字段的表格文件方便后续按条件筛选。然后挑出几百条典型文本人工检查分词和词性结果找出最常出现的错误模式针对这些模式去更新自定义词典或规则。之后再用BIO标注一小批高质量语料去微调NER模型或者至少先用现成NER模型跑一遍并做规则后处理。最后输出结构化字段比如提取出“机构名关键词时间”这样的三元组交给上层业务去消费。这套流程看似朴素但每一步都是为下一步服务的。清洗做得不好后续所有字段都会带噪声分句做得不好跨句实体就被硬生生截断评测集不建立你连自己改没改好都不知道。我见过太多人兴致勃勃地装了一堆工具库却把数据清洗和评测集这两件事完全跳过最后模型F1始终上不去还以为是算法不够先进。5.4 一个值得长期复用的习惯先建评测集再调任何参数这三个任务都属于序列标注的范畴效果好坏要用精确率Precision、召回率Recall和F1来评价。我可以分享一个发生过很多次的经历某次觉得分词效果很差直接开调自定义词典加了一堆术语结果分词表面看着顺眼很多但下游NER的F1反而下降了。原因正如同前面说的——新词典在改变切分路径的同时破坏了原本稳定的特征分布。从那之后我给自己定了一条规矩任何一次词典或模型改动都要在固定的评测集上记录F1变化不准凭感觉上线。评测集不需要很大几百条覆盖常见模式、边界情况的句子就够关键是固定不变、多次复用。这个习惯看起来笨但在长期项目里节省的时间远超当初建评测集的半小时。如果你刚开始接触这三个任务我也想补一句朴素建议不要急着追新模型。先用现成工具在目标文本上跑一遍人工看看错误长什么样再做有针对性的修正。很多问题根本不在模型能力而在于词典、清洗和标注规范这些“琐事”上。把地基打稳上层建筑才立得起来。我至今还保留着第一个项目的分词评测集有时候翻出来看看反而比看最新论文更能提醒自己基础任务的重要性。