命名实体识别

命名实体识别(NER)是自然语言处理(NLP)领域的一项基础技术,用于从非结构化文本中自动提取具有特定含义的实体,如人名、地点、组织和日期等。随着大语言模型(LLM)的兴起,NER 不仅在传统搜索系统中发挥作用,还广泛应用于知识库构建、知识图谱生成和智能问答。

NER 的基本理论

命名实体识别(NER)最早源于信息抽取(Information Extraction)任务,旨在解决文本中"命名实体"的标注问题。根据 MITRE 标准,NER 通常识别三大类实体:

  • 实体类:包括 PERSON(人名)、LOCATION(地点)、ORGANIZATION(组织)。
  • 时间类:如 DATE(日期)、TIME(时间)。
  • 数值类:如 MONEY(货币)、PERCENT(百分比)。

理论上,NER 是序列标注问题:给定句子序列 S = w1, w2, ..., wn,输出标签序列 L = l1, l2, ..., ln,其中 li 是 BIO 方案(Begin、Inside、Outside)下的标签。例如,在句子"John Doe lives in New York"中,标签为 B-PERSON, I-PERSON, O, O, B-LOCATION, I-LOCATION

NER 的挑战包括:

  • 歧义性:同一词在不同上下文有不同含义(如"Apple"可指水果或公司)。
  • 边界检测:准确确定实体的起始和结束位置。
  • 多语言支持:中文无词界限,需要字符级处理。

NER 算法的演进

NER 算法从规则-based 到深度学习,再到 LLM-based,经历了显著演进。

  1. 规则-based 方法

    • 基于正则表达式和词典匹配,早期的 Gazetteer 系统使用预定义实体库。
    • 优点:解释性强,无需训练数据。
    • 缺点:泛化差,无法处理未知实体。
    • 理论基础:有限状态机(FSM),匹配模式如"A-Za-z+ A-Za-z+"识别人名。
  2. 机器学习方法

    • 监督学习 :使用 CRF(Conditional Random Fields)或 HMM(Hidden Markov Model)建模序列概率。
      • CRF 最大化 P(L|S) = (1/Z) exp(∑ λk fk(L,S)),其中 fk 是特征函数(如词性、上下文)。
    • 深度学习 :BiLSTM-CRF 结合双向 LSTM 捕捉上下文,CRF 优化全局标签。
      • BERT 等 Transformer 模型通过预训练嵌入提升准确率,F1 分数可达 90%以上。
    • 理论:端到端学习,注意力机制(Attention)捕捉长距离依赖。
  3. LLM-based 方法

    • 利用 GPT 等模型的零样本能力,通过提示(Prompt)指导生成结构化输出。
    • 理论:LLM 的上下文理解基于 Transformer 的自注意力,提示工程模拟少样本学习。
    • 优点:灵活,支持自定义实体类型,无需标注数据。
    • 缺点:幻觉(Hallucination)和 token 限制,需要 schema 验证。

在知识库中,NER 扩展到关系提取(Relation Extraction),使用联合模型(如 LLM 的多步生成)识别实体间关系,如"subject-relation-object"三元组。

NER 在知识库系统中的应用

知识库(如企业文档系统)使用 NER 自动化元数据提取:

  • 实体索引:提取实体构建倒排索引,提升搜索精度。
  • 知识图谱:实体作为节点,关系作为边,支持推理查询(如"谁是 Apple CEO?")。
  • 挑战:处理多模态数据(文本+图像),结合 OCR 或多模态 LLM。
  • 益处:提高信息检索的召回率和准确率,理论上基于信息论的熵减少。

基于 Nest.js 和 LLM 的 NER 实现示例

在实践层面,我们使用 Nest.js 构建 API,结合 BullMQ 异步队列和 Vercel AI SDK 调用 LLM。以下辅以关键代码展示实现。

DTO 定义与验证

NerTaskDto 定义输入参数,确保类型安全:

typescript 复制代码
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNotEmpty, Length, IsOptional, IsNumber, Min, Max } from 'class-validator';

export class NerTaskDto {
    @ApiProperty({ description: '文本', example: '示例文本' })
    @IsString() @IsNotEmpty() @Length(1, 10000)
    text: string;

    @ApiProperty({ description: '模型名称', required: false })
    @IsOptional() @IsString()
    model?: string;

    @ApiProperty({ description: '温度', required: false })
    @IsOptional() @IsNumber() @Min(0) @Max(2)
    temperature?: number;

    @ApiProperty({ description: '最大 token', required: false })
    @IsOptional() @IsNumber() @Min(1) @Max(50000)
    maxTokens?: number;
}

任务创建与控制器

控制器处理请求,创建队列任务:

typescript 复制代码
@Post('ner')
async ner(@Body() dto: NerTaskDto) {
    return this.ok(await this.knowledgeService.createNerTask(dto));
}

服务层入队:

typescript 复制代码
async createNerTask(dto: NerTaskDto) {
    const job = await this.knowledgeQueue.add('ner', { dto });
    return { taskId: job.id };
}

NER 执行

使用 Zod 定义 schema 和提示调用 LLM:

typescript 复制代码
const NerSchema = z.object({
    entities: z.array(z.object({ text: z.string(), label: z.enum(["PERSON", "LOCATION", ...]), ... })),
    relations: z.array(z.object({ subject: z.string(), relation: z.string(), ... })),
});

const result = await generateObject({
    model: this.llmService.createChatModel(dto.model),
    prompt: const prompt = `对以下中文或英文文本执行命名实体识别(NER)和关系提取。任务要求:
    1. 识别实体,类型包括 PERSON(人名)、LOCATION(地点)、ORGANIZATION(组织)、DATE(日期)或 OTHER(其他)。
    2. 为每个实体提供起始索引(start)、结束索引(end)和置信度分数(0-1)。
    3. 提取实体之间的关系,指定主语(subject)、关系类型(relation,如 "lives in"、"works at")、宾语(object)和置信度分数(0-1)。
    4. 以 JSON 格式返回结果,严格遵循以下 schema:
    {
        "entities": [
            { "text": string, "label": "PERSON" | "LOCATION" | "ORGANIZATION" | "DATE" | "OTHER", "start": number, "end": number, "confidence": number }
        ],
        "relations": [
            { "subject": string, "relation": string, "object": string, "confidence": number }
        ]
    }
    文本: "${dto.text}"
    示例输出:
    {
        "entities": [
            { "text": "John Doe", "label": "PERSON", "start": 0, "end": 8, "confidence": 0.95 },
            { "text": "New York", "label": "LOCATION", "start": 10, "end": 18, "confidence": 0.98 }
        ],
        "relations": [
            { "subject": "John Doe", "relation": "lives in", "object": "New York", "confidence": 0.90 }
        ]
    }
    如果文本中没有可识别的实体或关系,返回 {"entities": [], "relations": []}。`,
    schema: NerSchema,
    temperature: dto.temperature || 0.3,
});

任务查询

getTask 处理状态:

typescript 复制代码
async getTask(id: string) {
    const job = await this.knowledgeQueue.getJob(id);
    if (job?.stacktrace?.length > 0) return this.halt(job.failedReason);
    if (!job?.returnvalue && job?.finishedOn) await job.retry('completed');
    return { ...this.camelcase(job.returnvalue || {}) };
}

总结

NER 是知识库智能化的基石,其理论演进从规则到 LLM 提升了灵活性.

相关推荐
狗头大军之江苏分军6 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术7 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong7 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7047 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc7 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae8 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
计算机魔术师8 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen9 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒9 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
分支预测失败9 小时前
RISC-V 时间子系统深度专题:mtime 访问路径、SBI 定时器与 Linux tickless 协同
后端