news 2026/10/13 11:55:49

命名实体识别NER全攻略:序列标注、BIOES与BiLSTM-CRF实战笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
命名实体识别NER全攻略:序列标注、BIOES与BiLSTM-CRF实战笔记

2021年秋招,我在准备NLP算法岗的面试题时,几乎每轮都会碰到同一个话题:NER。命名实体识别作为NLP基础任务模型里的典型代表,它考察的其实不只是背几个概念,而是一个人能否把数据、标签、模型、评估和部署串成一条完整链路。这篇文章把我当时整理的NER知识体系、手写思路和踩坑记录完整放出来,适合正在备战秋招、或者刚接触序列标注任务的读者。哪怕你只是临时补课,这里也有能直接上手的框架。

1. 秋招面试里的NER:为什么它总被拿来当考题

1.1 实体识别是信息抽取的地基

先对齐概念。NER要从一段文本中抽取出人名、地名、机构名、时间、金额等实体,同时判断类别。比如,文本“王小甲在云澜市的蓝山科技担任算法工程师”里,“王小甲”是人名,“云澜市”是地名,“蓝山科技”是机构名。听起来简单,但落地应用非常广,搜索、问答、知识图谱、舆情分析、文本结构化,第一层几乎都要靠NER先找到关键对象。实体抽错了,后面所有信息抽取、关系分类都是空中楼阁。

这也就是为什么在NLP基础任务模型里,NER和分词、词性标注一样,是最能看出基本功的题。秋招面试官不会指望你上来就做出多贵的模型,但如果你连实体边界、标签体系都讲不清楚,后面基本没法聊。尤其是很多业务场景里,NER不是最后一环,只是前置引擎,它做得稳不稳直接决定下游效果。

1.2 面试现场的高频问法

我自己感受,秋招里NER题常以三种面目出现。

第一种是概念题:“说说NER的难点。”如果只回“边界模糊、嵌套实体、样本不均衡”,面试官通常不会太满意。我会往下拆:中文没有天然空格,分词结果可能把实体切碎,所以很多方案选择基于字标注;实体边界本身也有歧义,比如“云澜市第一中学”里,到底把“第一中学”标成机构,还是把整个“云澜市第一中学”标成机构,这需要标注规范定义;实体还有嵌套,例如“某大学电子系”里既有大学又有院系,一套扁平BIO标签表达不了。把这些串成自己的理解,比背十条目录强很多。

第二种是方案设计题:“给你一批数据,怎么做实体识别系统?”这种题没有标准答案,但你要把数据清洗、标注、模型选型、后处理、评估全串起来。对方想看的是你如何权衡成本和收益,而不是无脑上BERT。比如说,数据只有几百条,那第一反应不应该是重训大模型,而是先看看规则和词典能不能解决一部分;如果数据有几万条,再考虑BiLSTM-CRF或BERT-CRF。

第三种是代码题,比如“写一个函数把BIO标签解码成实体列表”“手写维特比”。这种题考察的是你能不能把概念落到代码。后面我会给一个可以直接用的版本。

1.3 一次模拟面试暴露的问题

我有一个很深的记忆。当时模拟面,对方抛出“实体类别有十种,其中一类占样本80%,另一类只占1%,怎么做?”我的第一反应是“给少样本类别加权重”,对方接着问“那你会不会让模型过度拟合少数类?”那一瞬间我意识到,面试官要的不是一个操作,而是一套诊断流程:先看类别分布,看错误类型,看是召回不够还是精确率不够,然后才决定要用重采样、加权损失还是阈值调整。

这个复盘帮我养成了习惯:任何模型改动前,先明确“当前指标差在哪一类实体、哪一种边界情况”,而不是直接换一个新的网络结构。后来在实际项目里,这个习惯帮我省了很多无用功。

2. 建模前先想清楚:从跑通到能用,方案怎么选

2.1 把NER问题定义为序列标注

在写模型之前,要先决定输出表示。最主流的方式是为每个token打一个标签。中文场景更常用“字”而不是“词”,因为分词工具本身可能把实体边界切破。假设预分词结果是“蓝山/科技”,而真实实体是“蓝山科技”,那实体就被切开了,后面再还原特别费劲。基于字标注可以绕开这个问题。

标签体系我常聊两个:

  • BIO:B表示实体第一个字,I表示实体内部其他字,O表示非实体。类别跟在B/I后面。
  • BIOES:在BIO基础上增加E和S,分别表示实体结尾和单字实体。

举例,文本“王小甲在云澜市”,两种标注分别如下:

BIO:王/B-PER,小/I-PER,甲/I-PER,在/O,云/B-LOC,澜/I-LOC,市/I-LOC
BIOES:王/B-PER,小/I-PER,甲/E-PER,在/O,云/B-LOC,澜/I-LOC,市/E-LOC

为什么要跟面试官强调BIOES?因为E和S把边界信息显式建模,解码时实体收尾更明确,后处理可以少做边界尝试;代价是标签数变多,类别不均衡可能更明显。到底选哪种,没有绝对答案,要看数据和工程约束。

2.2 四类方案:规则、词典、统计、深度学习

这个问题我几乎每次都会被追问。可以先用一张表说清区别:

方案类型核心原理优点缺点适合场景
规则模板正则、词典、句法模式可解释、上线快、无需训练数据维护成本高、跨领域难迁移数字、日期、固定番号等强规则实体
统计模型CRF、HMM能建模标签依赖,结果稳定依赖特征工程,泛化能力一般中小规模数据、资源受限场景
深度学习BiLSTM-CRF、BERT-CRF自动学习上下文特征,效果好需要数据和算力,调试成本高大规模通用领域、复杂语义实体
混合方案规则优先层 + 模型兼顾精度与召回,容错性好两套系统维护成本大多数真实业务系统

在真实业务里,很多团队为求稳妥,会在模型前面挂一层“规则优先层”,把电话号码、身份证号、日期这种几乎没有歧义的实体先抽走,剩余交给模型。面试时如果能说出这种混合思路,会显得务实,而不是唯模型论。

2.3 为什么标准答案还是序列标注

2021年前后,很多前沿试验把NER建模成阅读理解,比如“找出句子中的人名”并输出一段span;生成式大模型出现后,也有人直接用生成方式做实体抽取。但秋招面试里的标准答案仍然是序列标注。原因是稳定、标签可控、部署成本低,而且训练数据在千条级别也能跑出可用模型。

阅读理解式更灵活,支持嵌套实体和复杂指令,但需要更大的模型和更强的推理,工程复杂度也更高。面试官如果问“这两种你怎么选”,你可以答:数据量、速度、可解释性决定。小数据、低延时场景用序列标注更划算,复杂语义场景可以探索抽取式或生成式方案。

3. BiLSTM-CRF:秋招必答的范式模型

3.1 两个模块各管什么

在经典方案里,BiLSTM负责看上下文,计算每个字到每个标签的发射分数。CRF层负责相邻标签的依赖关系,把发射分数加起来,再考虑转移矩阵。

举个例子,如果模型输出序列“B-PER I-LOC I-LOC”,CRF会压制这种不合法转移。转移矩阵是可学习的,形状是(标签数, 标签数),训练后可以学会“B-PER后面不能直接跟I-LOC”这类约束。所以即便BiLSTM在某几个位置给了I-LOC很高分,维特比搜索也会因为转移分数太低而绕开这条路径。

这里有一个很多人理解不了的细节:CRF不是简单地在输出层加规则,而是把“转移关系”也当成参数参与训练。所以模型学到的不是“谁不能跟谁”这种硬规则,而是“在数据中哪种转移更合理”的软约束。

3.2 训练目标:让真实标签路径得分尽可能高

给定句子x和标签序列y,模型的打分可以写成:

score(y|x) = 所有位置的发射分数之和 + 所有相邻位置的转移分数之和

训练目标是要让真实标签路径的条件概率P(y|x)尽可能大。由于所有可能标签序列的数量是指数级,计算分母时要通过动态规划把所有路径得分做log-sum-exp,也就是“配分函数”。预测时则用维特比算法找到得分最高的路径,等价于全局最优解。把这个逻辑讲清楚,面试官就知道你不只是在CNN上套softmax。

我可以拿生活场景打比方:发射分数是每门课的单科成绩,转移矩阵是选课顺序要求,CRF决定的是整条路径是否合法,而不是只看某一门课高不高。真正算绩点时要看完整课程计划,不能只看单科成绩。

3.3 从BiLSTM到BERT:编码器变了,约束没变

到了后期项目里,更常见的是BERT-CRF:把BERT输出的每个token向量接一层线性分类,再进CRF。预训练模型效果更好,是因为文本表示是上下文相关的,对OOV和罕见写法也更鲁棒。CRF层保持不变。

面试中问到“为什么BERT比BiLSTM强”,不要只说“用了Transformer”,最好提到大规模预训练语料带来的泛化能力,以及动态上下文编码。如果是垂直领域,BERT可能需要继续预训练,也就是做领域迁移训练。这个点能体现你真正调过模型。

3.4 面试官追问:CRF的反向传播难在哪

这个问题相对少,但一旦被问就是拉分项。难点在于配分函数对转移矩阵求梯度时,不能只对真实路径求梯度,还要对所有路径的期望做计算。不过实际工程中你不需要手推梯度,更关键的是理解CRF把标签间的条件依赖建模成了概率图结构。如果你能从“观察序列到隐状态序列”的角度解释,就已经合格了。

4. 秋招必须会写的代码与高频追问

4.1 从原始标签到实体列表的解码函数

面试手撕题里,最常见的是把BIO标签解码成实体列表。下面是一个极简实现:

def decode_bio(tokens, tags): entities = [] cur = None for token, tag in zip(tokens, tags): if tag.startswith("B-"): if cur: entities.append(cur) cur = {"type": tag[2:], "tokens": [token]} elif tag.startswith("I-"): if cur and cur["type"] == tag[2:]: cur["tokens"].append(token) else: if cur: entities.append(cur) cur = None else: # O 或非法标签 if cur: entities.append(cur) cur = None if cur: entities.append(cur) return entities

逻辑其实很简单:遇到B就开启新实体,遇到I且类型和当前实体一致就继续,遇到O或类型不一致就结束当前实体。生产环境里还要加一步合法性校验,比如不允许O后面直接跟I,不应该让后处理把不合法输出强行补成实体。面试时能写出这个函数,基本就能看出你了解序列标注任务的实际输出。

4.2 评估时的匹配口径

很多新手拿token-level准确率说“模型有98%”,但样本里绝大多数token是O,实体级F1很可能低到没法看。正确做法是把预测的标签序列转回实体列表,再做entity-level匹配。常见口径有这几种:

匹配方式判定标准什么时候用
strict类型和边界完全一致才算对大多数学术基准
type only只看类型,边界不同可以容忍关注某类实体是否被找到
boundary only只看边界,类型不同也算对关注定位是否有偏移
token-level逐token计算指标快速看趋势,不适合最终评价

这里要特别注意:不同口径算出来的F1差别很大。写项目报告或博客时,一定要写明用的是什么匹配方式,不然别人很难复现你的结论。

4.3 高频追问与答题思路

第一问:实体嵌套怎么处理?如果业务不需要嵌套,就把嵌套实体拆成单层,只保留最外层或最核心的实体;如果一定要支持嵌套,可以用分层标注,或者用抽取式方案,而不是硬套BIOES。

第二问:OOV和罕见实体怎么识别?我的思路是先保证基于字建模,再用上下文编码,这样未登录词也不会直接变成“UNK”。预训练模型对OOV更友好,但仍需注意测试集里可能出现训练时完全没见过的领域词,所以要留一部分数据专门看泛化。

第三问:类别严重不均衡怎么办?不要一上来就调loss。先分析是哪些实体漏召回,是数据量少,还是边界标注混乱。常见手段有重采样、类别权重、难例挖掘、数据增强。我通常会先给少样本实体加一些权重,然后观察验证集,避免过拟合少数类。

第四问:线上推理太慢怎么办?先看瓶颈在编码器还是CRF解码。BERT编码器很重,可以蒸馏成小模型,或者换成轻量编码器;CRF解码本身不慢,但如果标签多,可以限制候选标签,或者批量处理。另外,长文本要做合理的截断策略,不能简单一刀切。接口层可以加同文本缓存。

第五问:标注样本有噪声怎么办?一定要先做数据清洗。统计训练集里是否存在非法转移,比如O后面直接跟I,大概率是标注工具导出bug或人工误标。如果只是个别噪声,可以在损失里给这部分样本降权,或者直接删除。

5. 实操里的坑与排查清单

5.1 标注一致性是最容易被低估的坑

很多项目效果差,根本不是模型问题,而是标注标准不一致。同一个“云澜市第一中学”,有人把“云澜市第一中学”整体标成机构,有人只标“第一中学”,还有人把“云澜市”标成地名后就不再标机构。模型看到同一个句子有好几种标签,自然学不稳。

实操中我会先抽一批标注样本,人工检查标签长度分布和转移矩阵。如果出现大量“O到I”这种非法转移,说明标注工具或数据导出有bug。标签一致性甚至比模型选择更重要。这里有一个经验:新项目启动前,先让不同标注者对同一条数据各标一遍,算一下一致率,再开始大规模标注。

5.2 训练与评估指标里的隐性坑

除了匹配口径,还有一个容易被忽略的问题:很多数据集在评测时会忽略重复实体,或者把同一实体在句子中出现多次算一个。比如一个句子里两次出现“蓝山科技”,严格实体列表可能只算一个。如果评估脚本没做去重,F1就会偏低。

另一个坑是提前用测试集调参。反复跑测试集选模型,最后得到的分数虚高,上线后很容易翻车。秋招项目复盘时,宁可少拿一个点,也要诚实地把验证集和测试集分开。我自己的做法是只在最终阶段碰一次测试集,平时都用验证集做早停和选模型。

5.3 部署阶段的性能优化思路

实际落地场景经常是CPU推理,BERT-CRF在高并发下会比较吃力。我的做法是:第一,优先上小模型,字向量加BiLSTM-CRF往往已经能解决90%的问题;第二,如果必须用BERT,考虑蒸馏成6层,或者用动态长度截断减少计算量;第三,长文本要有截断策略,比如保留首尾,因为很多关键实体出现在开头和结尾;第四,接口层加缓存,同一段文本不重复推理,这个提升往往很直接。

在一次压力测试里,同样一批文本,加了缓存之后吞吐量提升了大约三成。数字其实不重要,重要的是你有没有这种工程意识。秋招面试官很吃这一套。

5.4 排查清单:当F1上不去时

项目调优时,我会按这个顺序排查:

  • 看坏案例分布:是哪一类实体差,是边界错还是类型错。
  • 看预测结果有没有非法转移:如果后处理能修复,先修后处理再考虑换模型。
  • 看训练集和验证集的标签分布是否差距很大:分布不一致要先做数据修正。
  • 看预测实体的长度分布:如果模型只会抽单字,可能是边界标签学得不好,BIOES会有帮助。
  • 试一次最简单的规则基底:规则把强规则实体抽走后,模型接受的任务变简单,指标往往能涨一点。

这个清单现在还在用。大多数情况下,动手改模型之前先过滤一轮数据问题,能省下很多调参时间。

我个人到现在都记得,秋招那阵子在纸上画了无数遍BIOES和转移矩阵之后,又花两周把NER从标注到部署完整做了一遍。那两周最大的体会是:模型结构只是NER最表面的部分,数据、标签、评估、后处理才是真正花时间的地方。面试也一样,你不需要把Transformer复现得毫厘不差,但需要能清楚说出“我要建模什么、标签怎么设计、算对还是算错”。如果正在准备秋招,我的建议是别只看论文,拿一份真实语料,用BIOES标两百条,跑一个最基础的模型,把实体级F1算明白,遇到难题时你自然会有底气。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 11:53:46

学习平台前后端分离实践:SpringBoot+Vue+MyBatis架构详解

1. 为什么我把智能学习平台拆成前后端分离两套工程去年上半年我接手了一个学习类项目,需求说简单也简单:管理员能上传课件和习题,学生能在线刷题、看学习记录,老师能批改作业、发布通知。但真做起设计时我发现,如果还用…

作者头像 李华
网站建设 2026/10/13 11:52:50

litellm LLM网关搭建指南:统一接入层的原理、部署配置与避坑实践

做 AI 应用这段时间,我最大的感受不是模型不够多,而是“接模型”这件事本身变成了一个工程问题。今天要聊的 litellm,就是一套开源的 LLM 统一接入层,业内常叫它 LLM 网关。它把各家模型服务商五花八门的接口格式收敛成一套通用协…

作者头像 李华
网站建设 2026/10/13 11:52:19

Spring Cache + Redis 实战:注解使用与穿透击穿雪崩解决方案

做后端开发的人,迟早会跟缓存打交道。“第 4-4 课:Spring Boot 中使用 Cache 缓存的使用”讲的就是 Spring 官方这套缓存抽象。说直白点,Spring Cache 就是给那些读多写少、短时间不变化的数据找个更快的存放地点,让高并发请求别一…

作者头像 李华
网站建设 2026/10/13 11:52:09

Ollama Mac版安装与M1/M2本地大模型推理实战指南

简介:这份安装包面向搭载苹果M1/M2芯片的Mac用户,提供与Apple Silicon原生兼容的Ollama运行环境,可用于在本地部署并调用DeepSeek-R1等大语言模型,解决旧版本在ARM架构下无法安装或运行不流畅的适配问题,也适合刚接触本…

作者头像 李华
网站建设 2026/10/13 11:51:11

NPU体系结构详解:从MAC阵列到带宽墙的AI芯片优化

1. 为什么专门把NPU从“计算机体系结构”里拎出来体系结构课上讲CPU,绕不开流水线、分支预测、乱序执行、Cache层级;讲GPU,绕不开SIMT、线程块、warp调度。但一旦讲到NPU,你会发现话题突然拐向了另一个方向:二维乘加阵…

作者头像 李华
网站建设 2026/10/13 11:50:55

Smartbi报表设置全指南:从数据源到权限发布的完整配置链路

简介:这份Smartbi报表设置文档面向使用Smartbi进行报表开发的数据分析与报表人员,聚焦从模板引用到预览发布的完整设置流程,适合需要快速上手或查漏补缺的初中级使用者。资源包内含1个docx文档,压缩包约1.05MB,以图文步…

作者头像 李华