命名实体识别

B站影视 欧美电影 2025-09-29 13:34 1

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

命名实体识别(NER)是自然语言处理(NLP)领域的一项基础技术,用于从非结构化文本中自动提取具有特定含义的实体,如人名、地点、组织和日期等。随着大语言模型(LLM)的兴起,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-PERS #技术分享ON, O, O, B-LOCATION, I-LOCATION]。

NER 的挑战包括:

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

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

规则-based 方法 :基于正则表达式和词典匹配,早期的 Gazetteer 系统使用预定义实体库。优点:解释性强,无需训练数据。缺点:泛化差,无法处理未知实体。理论基础:有限状态机(FSM),匹配模式如“[A-Z][a-z]+ [A-Z][a-z]+”识别人名。机器学习方法监督学习 :使用 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)捕捉长距离依赖。LLM-based 方法 :利用 GPT 等模型的零样本能力,通过提示(Prompt)指导生成结构化输出。理论:LLM 的上下文理解基于 Transformer 的自注意力,提示工程模拟少样本学习。优点:灵活,支持自定义实体类型,无需标注数据。缺点:幻觉(Hallucination)和 token 限制,需要 schema 验证。

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

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

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

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

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

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; }

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

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

服务层入队:

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

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

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, });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 || {}) };}

来源:墨码行者

相关推荐