- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
LUKE(Language Understanding with Knowledge-based Embeddings)是一种能够同时建模词元(word)与实体(entity)的 Transformer 预训练模型,其核心创新在于将文本中的实体(如人名、地名、机构名)作为独立的"实体序列"与词序列并行编码。本文基于 PaddleNLP 仓库中的 luke/modeling.py 及其配套的 configuration.py、tokenizer.py 源码,系统讲解 LUKE 在 PaddleNLP 中的完整实现:包括LukeModel基础模型的双塔结构与实体感知注意力机制、LukeConfig的全部配置项,以及面向掩码语言建模、实体分类、实体对分类、实体片段(span)分类和问答五大任务的模型头部实现。读完本文,你将掌握 LUKE 的架构原理,并能直接使用 PaddleNLP 加载luke-base/luke-large预训练权重完成各类实体相关任务的推理与开发。
一、LUKE 在 PaddleNLP 中的定位与模块总览
LUKE 模块位于仓库的paddlenlp/transformers/luke/目录下,由四个文件组成:
| 文件 | 职责 |
|---|---|
| modeling.py | 模型结构:基础模型与五个下游任务模型 |
| configuration.py | 配置类LukeConfig与预训练权重注册表 |
| tokenizer.py | LukeTokenizer,负责文本与实体的联合编码 |
| init.py | 模块导出入口 |
其中 modeling.py 通过 paddlenlp/transformers/init.py 中的from .luke.modeling import *被导出,因此用户可以直接从paddlenlp.transformers顶层命名空间导入全部 LUKE 模型类。
modeling.py 对外暴露的完整 API 集合(见 modeling.py 的__all__):
LukeModel:不带任务头的基础 Transformer 模型,输出词与实体的隐状态及池化结果;LukePretrainedModel:预训练模型的抽象基类,承载权重下载与加载逻辑;LukeForMaskedLM:掩码语言建模(MLM)模型,同时预测被掩码的词与实体;LukeForEntityClassification:实体分类模型(如 Open Entity 任务);LukeForEntityPairClassification:实体对关系分类模型(如 TACRED 任务);LukeForEntitySpanClassification:实体片段分类模型(如命名实体识别 NER);LukeForQuestionAnswering:抽取式问答模型。
二、LukeConfig:实体感知模型的配置体系
所有 LUKE 模型均由 LukeConfig 配置类驱动。它继承自PretrainedConfig,除常规 Transformer 配置外,还引入了两个 LUKE 特有的超参数:entity_vocab_size(实体词表大小)与entity_emb_size(实体嵌入维度)。
2.1 预训练模型注册表
configuration.py 中定义了LUKE_PRETRAINED_INIT_CONFIGURATION,注册了两个官方预训练规模:
| 配置项 | luke-base | luke-large |
|---|---|---|
| hidden_size | 768 | 1024 |
| num_hidden_layers | 12 | 24 |
| num_attention_heads | 12 | 16 |
| intermediate_size | 3072 | 4096 |
| max_position_embeddings | 514 | 514 |
| vocab_size | 50267 | 50267 |
| hidden_act | gelu | gelu |
| hidden_dropout_prob / attention_probs_dropout_prob | 0.1 | 0.1 |
| type_vocab_size | 1 | 1 |
| pad_token_id | 1 | 1 |
| initializer_range | 0.02 | 0.02 |
同时 LUKE_PRETRAINED_RESOURCE_FILES_MAP 注册了luke-base与luke-large的model_state.pdparams权重资源地址,LukePretrainedModel正是通过这两个注册表实现from_pretrained的自动下载与加载。
2.2 构造参数与默认值
LukeConfig.__init__的完整参数(均带默认值)如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
vocab_size | 50267 | 词元(token)词表大小,决定 token embedding 矩阵维度 |
hidden_size | 768 | 嵌入层、编码层与池化层的隐藏维度 |
num_hidden_layers | 12 | Transformer 编码器层数 |
num_attention_heads | 12 | 每层注意力头数 |
intermediate_size | 3072 | 前馈网络中间维度(先由 hidden_size 升维、再降回) |
hidden_act | "gelu" | 前馈层激活函数,支持 gelu、relu 等 Paddle 支持的激活 |
hidden_dropout_prob | 0.1 | 嵌入层与编码器中全连接层的 dropout 概率 |
attention_probs_dropout_prob | 0.1 | 注意力概率矩阵的 dropout 概率 |
max_position_embeddings | 514 | 位置编码最大长度 |
type_vocab_size | 1 | token_type_ids 的词表大小 |
entity_vocab_size | 500000 | 实体词表大小,决定实体嵌入矩阵维度 |
entity_emb_size | 256 | 实体嵌入维度(远小于 hidden_size,通过投影对齐) |
initializer_range | 0.02 | 权重正态初始化标准差 |
pad_token_id | 1 | 词序列的填充 token id |
entity_pad_token_id | 0 | 实体序列的填充 token id |
cls_token_id | 101 | 分类标记 token id |
从配置可以看出 LUKE 的一个关键设计:实体嵌入维度entity_emb_size=256远小于词隐藏维度hidden_size=768。这使实体词表可以做得非常大(50 万),同时保持参数量可控——实体嵌入经投影层映射到隐藏空间后再参与注意力计算,具体见下文EntityEmbeddings。
三、LukeModel:双塔输入与实体感知注意力
LukeModel(modeling.py)是全部 LUKE 任务模型的基础,被@register_base_model装饰,base_model_prefix = "luke"。
3.1 整体结构
LukeModel.__init__由五个子模块组成:
LukeEmbeddings:词元嵌入(直接复用RobertaEmbeddings);EntityEmbeddings:实体嵌入(LUKE 特有);LukeEncoder:堆叠num_hidden_layers层LukeLayer;LukePooler:取[CLS](即<s>)位置的隐状态做 tanh 池化;- 另保存
pad_token_id与entity_pad_token_id用于自动构造注意力掩码。
LukePretrainedModel._init_weights(modeling.py)定义了初始化钩子:nn.Linear与nn.Embedding权重按initializer_range标准差做正态初始化,nn.LayerNorm的 epsilon 固定为1e-6。
3.2 双塔输入:词序列与实体序列
LukeModel.forward同时接受两类输入(modeling.py):
- 词侧:
input_ids、token_type_ids、position_ids、attention_mask; - 实体侧:
entity_ids、entity_position_ids、entity_token_type_ids、entity_attention_mask。
实体侧参数均为可选。当entity_ids为None时,模型退化为普通的 RoBERTa 风格编码器;当提供实体输入时,模型将实体序列拼接到词序列之后(paddle.concat)进行联合编码。
LukeEmbeddings(词嵌入)
LukeEmbeddings(modeling.py)直接继承RobertaEmbeddings,其 docstring 明确指出"Same as BertEmbeddings with a tiny tweak for positional embeddings indexing",即词侧嵌入与 RoBERTa 完全一致,仅保留位置嵌入索引的小调整。
EntityEmbeddings(实体嵌入)
EntityEmbeddings(modeling.py)是 LUKE 区别于普通 Transformer 的核心组件之一,其 forward 计算分为四步:
- 实体嵌入查找:
entity_embeddings(entity_ids)得到[batch, entity_len, entity_emb_size]的向量;当entity_emb_size != hidden_size时,通过无偏置的entity_embedding_dense线性层投影到hidden_size; - 位置嵌入:对
position_ids.clip(min=0)做位置嵌入查找,随后用掩码(position_ids != -1)过滤掉值为-1(表示该实体对应的词位置为空)的嵌入,再对实体覆盖的多个词位置求平均(除以掩码和,最小值截断为1e-7)。这一步实现了 LUKE 论文中"实体位置嵌入 = 其提及(mention)覆盖的所有词位置嵌入的平均"的设计; - 段嵌入:
token_type_embeddings(token_type_ids),未提供时默认为全 0; - 融合:三者相加后经过 LayerNorm 与 dropout。
3.3 LukeSelfAttention:四类注意力查询的实体感知机制
LUKE 注意力的最大特色体现在LukeSelfAttention(modeling.py)中。当实体存在时,模型不再使用单一 Query 投影,而是使用四套独立的 Query 线性层:
query(w2w):词对词注意力;w2e_query:词对实体注意力;e2w_query:实体对词注意力;e2e_query:实体对实体注意力。
Key 与 Value 则共享同一投影(对词+实体拼接后的整体计算),再按位置切分为词部分与实体部分。前向过程(modeling.py)依次计算w2w_attention_scores、w2e_attention_scores、e2w_attention_scores、e2e_attention_scores四个分数矩阵,然后按行、列拼接成完整的注意力分数矩阵:词行由 w2w 与 w2e 拼接,实体行由 e2w 与 e2e 拼接。
随后除以sqrt(attention_head_size)缩放、叠加注意力掩码、softmax 归一化、dropout,再与共享的 Value 矩阵相乘得到上下文向量,最后按word_size位置切分回词隐状态与实体隐状态。当entity_hidden_states为None时则退化为标准多头注意力。
3.4 层结构与编码器堆叠
LukeAttention(modeling.py):由LukeSelfAttention与LukeSelfOutput(dense + LayerNorm + dropout 的残差结构)组成,将词/实体输出拼接后过残差,再按word_size切分;LukeIntermediate/LukeOutput(modeling.py):前馈网络,hidden_size → intermediate_size → hidden_size,中间经过get_activation(config.hidden_act)激活,外层带残差与 LayerNorm;LukeLayer(modeling.py):将注意力层与前馈层串成单层,同样对词/实体输出分别切分;LukeEncoder(modeling.py):nn.LayerList堆叠多层LukeLayer,逐层传递词与实体隐状态。
3.5 掩码构造与前向输出
LukeModel.forward在 modeling.py 中完成掩码与嵌入的组装:
- 未提供
attention_mask时,自动根据input_ids == pad_token_id生成-1e4的惩罚掩码并 unsqueeze 到 4 维;若提供 2 维掩码则自动unsqueeze([1,2])并转换为(1.0 - mask) * -1e4; - 实体侧同理:未提供
entity_attention_mask时按entity_ids == entity_pad_token_id生成;最终attention_mask与entity_attention_mask在最后一维拼接,形成覆盖词+实体全长度的联合掩码; - 实体 token_type_ids 默认全 0。
返回值为三元组(sequence_output, entity_hidden_state, pooled_output):
word_hidden_state:[batch_size, sequence_length, hidden_size]的最后一层词隐状态;entity_hidden_state:[batch_size, entity_length, hidden_size]的实体隐状态;pooled_output:取序列第一个 token(<s>)隐状态经LukePooler(dense + tanh)池化,形状[batch_size, hidden_size]。
四、五个下游任务模型
所有任务模型都复用LukeModel作为骨干,再在顶部叠加不同的任务头。
4.1 LukeForMaskedLM:词与实体的联合掩码预测
LukeForMaskedLM(modeling.py)同时预测被掩码的词与实体,包含两个头部:
LukeLMHead(modeling.py):词侧 MLM 头,dense → 激活 → LayerNorm 后与decoder_weight(默认复用word_embeddings.weight做权重绑定,即tied结构)做转置矩阵乘并加decoder_bias,实现权重共享的掩码词预测;EntityPredictionHead(modeling.py):实体侧预测头,先由EntityPredictionHeadTransform(dense 到entity_emb_size+ 激活 + LayerNorm)变换,再由decoder线性层映射回实体词表大小entity_vocab_size。
返回二元组(logits, entity_logits):词侧形状[batch_size, sequence_length, vocab_size],实体侧形状[batch_size, entity_length, entity_vocab_size]。
4.2 LukeForEntityClassification:实体级分类
LukeForEntityClassification(modeling.py)面向 Open Entity 这类对单个实体分类的任务。它取实体隐状态的第一个实体 tokenoutputs[1][:, 0, :]作为特征,经 dropout 后送入nn.Linear(hidden_size, num_labels)分类器,输出[batch_size, num_labels]的 logits。
4.3 LukeForEntityPairClassification:实体对关系分类
LukeForEntityPairClassification(modeling.py)面向 TACRED 这类对实体对进行关系分类的任务。它将前两个实体 token 的隐状态outputs[1][:, 0, :]与outputs[1][:, 1, :]拼接成hidden_size * 2维向量,经 dropout 后送入nn.Linear(hidden_size * 2, num_labels, bias_attr=False)(注意此处不使用偏置),输出[batch_size, num_labels]。
4.4 LukeForEntitySpanClassification:实体片段分类
LukeForEntitySpanClassification(modeling.py)面向命名实体识别等实体片段分类任务。它的输入额外增加entity_start_positions与entity_end_positions两个参数,前向逻辑为:
- 通过
paddle_gather(模块内自实现的 gather 工具函数,modeling.py,用gather_nd模拟任意维度 gather)从词隐状态中按 start/end 位置收集起始与结束词的隐状态; - 将
start_states、end_states与实体隐状态outputs[1]三者拼接成hidden_size * 3维特征; - 经 dropout 后送入
nn.Linear(hidden_size * 3, num_labels),输出[batch_size, num_entities, num_labels]。
4.5 LukeForQuestionAnswering:抽取式问答
LukeForQuestionAnswering(modeling.py)是五类模型中唯一不直接使用实体隐状态的模型:它取词隐状态的前input_ids.shape[1]个位置,经nn.Linear(hidden_size, 2)输出 2 维 logits 后paddle.split拆分为start_logits与end_logits,两者形状均为[batch_size, sequence_length],分别表示答案跨度起始与结束位置的概率分布。
五、LukeTokenizer:实体标注的编码配套
LukeTokenizer(tokenizer.py)继承自RobertaBPETokenizer(RoBERTa 风格的 byte-level BPE),并额外引入了实体词表entity_file(entity_vocab.json)。它加载三类资源文件:vocab.json、merges.txt与entity_vocab.json,特殊 token 与 RoBERTa 一致(<s>、</s>、<pad>、<unk>、<mask>)。
其__call__方法(tokenizer.py)在标准词编码参数之外,新增了以下 LUKE 专有参数:
| 参数 | 说明 |
|---|---|
entity_spans/entity_spans_pair | 字符级别的实体跨度列表,如[(0, 7)]表示第 0~7 个字符是实体 |
entities/entities_pair | 实体的字符串表示(如 Wikipedia 实体标题"Los Angeles"或特殊实体[MASK]);若只给entity_spans不给定实体,则自动用[MASK]实体填充 |
max_mention_length | 实体提及覆盖的最大词位置数,默认 30,决定entity_position_ids的长度 |
task | 构造器中的任务类型参数,可选"entity_classification"、"entity_pair_classification"等,用于约束实体数量 |
返回的字典除input_ids、attention_mask等常规字段外,还会在传入entity_spans时额外包含entity_ids、entity_position_ids、entity_segment_ids、entity_attention_mask等实体侧字段(tokenizer.py)。tokenizer 的 docstring 给出了一个直观示例:对"Beyoncé lives in Los Angeles"并指定两个实体跨度,会输出input_ids与entity_ids两组 id。
六、快速上手:完整可运行的推理示例
综合 modeling.py 中各个类的 docstring 示例,加载luke-base的完整流程如下:
import paddle from paddlenlp.transformers import LukeModel, LukeTokenizer # 1. 加载预训练模型与配套 tokenizer(首次运行自动下载权重) tokenizer = LukeTokenizer.from_pretrained('luke-base') model = LukeModel.from_pretrained('luke-base') # 2. 构造文本与实体跨度(字符级别的 start/end 位置) text = "Beyoncé lives in Los Angeles." entity_spans = [(0, 7)] # "Beyoncé" 为实体 inputs = tokenizer(text, entity_spans=entity_spans, add_prefix_space=True) # 3. 转换为 Tensor 并前向 inputs = {k: paddle.to_tensor([v]) for (k, v) in inputs.items()} word_hidden_state, entity_hidden_state, pooled_output = model(**inputs) print(word_hidden_state.shape, entity_hidden_state.shape, pooled_output.shape)五个任务模型的使用方式与之完全一致,仅需替换模型类与输出处理:
from paddlenlp.transformers import LukeForMaskedLM model = LukeForMaskedLM.from_pretrained('luke-base') logits, entity_logits = model(**inputs) # 词 MLM logits + 实体 MLM logitsfrom paddlenlp.transformers import LukeForEntityClassification model = LukeForEntityClassification.from_pretrained('luke-base', num_labels=2) logits = model(**inputs) # [batch_size, num_labels]from paddlenlp.transformers import LukeForEntityPairClassification model = LukeForEntityPairClassification.from_pretrained('luke-base', num_labels=2) # 实体对任务需提供两个实体跨度 inputs = tokenizer(text, entity_spans=[(0, 7), (17, 28)], add_prefix_space=True) inputs = {k: paddle.to_tensor([v]) for (k, v) in inputs.items()} logits = model(**inputs) # [batch_size, num_labels]from paddlenlp.transformers import LukeForEntitySpanClassification model = LukeForEntitySpanClassification.from_pretrained('luke-base', num_labels=2) inputs = tokenizer(text, entity_spans=[(0, 7)], add_prefix_space=True) inputs = {k: paddle.to_tensor([v]) for (k, v) in inputs.items()} inputs['entity_start_positions'] = paddle.to_tensor([[1]], dtype='int64') inputs['entity_end_positions'] = paddle.to_tensor([[2]], dtype='int64') logits = model(**inputs) # [batch_size, num_entities, num_labels]from paddlenlp.transformers import LukeForQuestionAnswering model = LukeForQuestionAnswering.from_pretrained('luke-base') start_logits, end_logits = model(**inputs) # 均为 [batch_size, sequence_length]需要说明的适用前提:所有示例的默认entity_spans均为单实体,实体对分类等任务请按上文所示提供相应数量的跨度;add_prefix_space=True与 RoBERTa 系列 tokenizer 的惯例一致,用于在句首补充空格标记。
七、测试与验证
仓库在 tests/transformers/luke/test_modeling.py 中提供了完整的模型测试,可用于验证上述 API 的正确性:
LukeModelTester(test_modeling.py)构造小规模随机配置的模型与输入;- 测试断言覆盖了
LukeModel输出的[batch_size, seq_length, hidden_size]与[batch_size, hidden_size]形状(对应前向三元组中的词隐状态与池化输出); - 五个任务模型的输出形状均被逐一验证:
LukeForMaskedLM的词/实体 logits、LukeForQuestionAnswering的 start/end logits、LukeForEntityClassification的[batch_size, num_labels]、LukeForEntityPairClassification的[batch_size, num_labels]、LukeForEntitySpanClassification的[batch_size, 2, num_labels]; test_forward_signature与test_model_from_pretrained则分别检查前向方法参数签名与预训练权重加载。
配套的 tests/transformers/luke/test_tokenizer.py 覆盖 tokenizer 的实体编码逻辑。若需在本地复现测试,可参照仓库 tests/README.md 的测试环境说明运行对应用例。
八、小结:从源码理解 LUKE 的设计要点
纵观 modeling.py,LUKE 在 PaddleNLP 中的实现有四个值得记住的设计要点:
- 双序列输入:词序列与实体序列并行编码,实体序列经
EntityEmbeddings(含平均化的位置嵌入)映射到与词相同的隐藏空间; - 实体感知注意力:
LukeSelfAttention使用 w2w / w2e / e2w / e2e 四套 Query 投影,Key/Value 共享,将实体信息显式注入每一层的注意力计算; - 大实体词表 + 低维实体嵌入:
entity_vocab_size达 50 万而entity_emb_size仅 256,通过投影层与隐藏维度对齐,兼顾表示能力与参数量; - 任务头多样化:同一骨干之上,通过取实体首 token(实体分类)、实体对拼接(实体对分类)、词隐状态按 span 收集拼接(实体片段分类)、词隐状态投影(问答)、双头联合预测(MLM),覆盖了实体相关的典型 NLP 任务。
如需进一步了解 LUKE 与 PaddleNLP 其他预训练模型的差异,可参考 paddlenlp/transformers/README.md 与 docs/zh/source/paddlenlp.transformers.luke.configuration.rst 对应的配置类文档;各任务模型的完整参数与返回类型说明,可直接阅读 modeling.py 中各类的 docstring。
- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
相关推荐
Graphite 如何使用网格系统绘制像素级对齐的作品?
Graphite 如何使用网格系统绘制像素级对齐的作品? 如果你想在 Graphite 里画出边缘干净、坐标精确的作品(例如像素画风格图形或等轴测插画),需要用
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP深入解析 Transformers 中的 LUKE 模型:实体感知自注意力与知识增强的实体表示
深入解析 Transformers 中的 LUKE 模型:实体感知自注意力与知识增强的实体表示 LUKE(Language Understanding with
人工智能深度学习机器学习预训练微调NLP计算机视觉语音多模态PaddleNLP 中 Luke 模型预训练权重汇总与实体级 NLP 任务使用指南
PaddleNLP 中 Luke 模型预训练权重汇总与实体级 NLP 任务使用指南 导读 本文以 docs/zh/model_zoo/transformers/
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考