命名实体识别

命名实体识别(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-Z][a-z]+ [A-Z][a-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 提升了灵活性.

相关推荐
shark_chili2 小时前
网卡数据包处理全攻略:DMA、中断、NAPI机制深度解析
后端
JarvanMo2 小时前
4 个让我重新思考我的开发环境配置的开源工具
前端
蜚鸣3 小时前
JavaScript中国手机号校验
前端
秋田君3 小时前
Electron 安装踩坑实录
前端·javascript·electron
excel3 小时前
《深入理解单页应用(SPA):原理、实现与SPA/MPA对比全解析》
前端
RoyLin3 小时前
微任务与宏任务
前端·后端·node.js
IT_陈寒3 小时前
Redis 性能提升秘籍:这5个被低估的命令让你的QPS飙升200%
前端·人工智能·后端
多看书少吃饭3 小时前
前端实现抽烟识别:从算法到可视化
前端·算法
excel3 小时前
合并路由与微前端框架的对比解析
前端