Hugging Face Transformers 中的 BROS 模型:面向文档关键信息抽取(KIE)的文本与布局联合预训练语言模型
2026/9/11 16:36:53 网站建设 项目流程

Hugging Face Transformers 中的 BROS 模型:面向文档关键信息抽取(KIE)的文本与布局联合预训练语言模型

【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers

导读

BROS(BERT Relying On Spatially)是一个仅编码器(encoder-only)的 Transformer 预训练语言模型,专门为文档图像上的关键信息抽取(Key Information Extraction, KIE)设计:它接收「文本 token 序列 + 每个 token 的边界框(bounding box)」作为输入,输出一系列隐藏状态,并通过相对空间位置编码与区域掩码语言建模(Area-Masked Language Modeling, AMLM)两大核心机制,在不依赖显式视觉特征的前提下理解二维空间中的文档语义。本文以 Transformers 仓库中的 BROS 官方文档为主体,结合 模型实现、配置定义、处理器 与 测试用例 源码,系统讲解 BROS 的架构设计、预训练目标、三种下游任务头(Token 分类、SPADE-EE 实体抽取、SPADE-EL 实体链接)、边界框预处理与box_first_token_mask构造方法,以及完整的代码级使用示例,帮助读者在发票、收据、表单等文档理解任务中直接落地 BROS。


一、BROS 是什么:从论文到仓库实现

BROS 模型由 Teakgyu Hong、Donghyun Kim、Mingi Ji、Wonseok Hwang、Daehyun Nam、Sungrae Park 在论文《BROS: A Pre-trained Language Model Focusing on Text and Layout for Better Key Information Extraction from Documents》中提出,仓库文档记录其论文发布于 2021-08-10,并在 2023-09-15 由 jinho8345 贡献到 Hugging Face Transformers。模型代码由BrosModelBrosForTokenClassificationBrosSpadeEEForTokenClassificationBrosSpadeELForTokenClassification四部分组成,全部实现在 modeling_bros.py 中,此外仓库还提供了 checkpoint 转换脚本,用于将原始 clovaai/bros 的权重转换到 Transformers 格式。

1.1 核心思想:编码相对空间信息

BROS 全称BERT Relying On Spatiality。与传统 LayoutLM 系列直接把绝对坐标拼接进 embedding 的做法不同,BROS 在自注意力计算内部编码 token 之间的相对空间关系:每个 token 与其余 token 构成两两相对坐标对,再通过正弦位置编码(sinusoid embedding)与线性投影生成空间偏置项,直接叠加到注意力分数上。

从源码可以看到这一机制的完整链路(modeling_bros.py):

  • BrosPositionalEmbedding1D(L67-L85):参考 Transformer-XL 的相对位置编码思路,对一维坐标序列计算正弦/余弦嵌入;
  • BrosPositionalEmbedding2D(L88-L104):将边界框的 8 个坐标分量交替送入 x/y 一维编码器,偶数维走 x、奇数维走 y,拼接出二维空间位置嵌入;
  • BrosBboxEmbeddings(L107-L119):计算bbox的转置差分bbox_t[None, :, :, :] - bbox_t[:, None, :, :],即得到 token i 与 token j 的相对坐标差,再经正弦编码与线性投影(bbox_projection)产出最终的空间偏置;
  • BrosSelfAttention.forward(L231-L237):通过torch.einsum("bnid,bijd->bnij", (query_layer, bbox_pos_emb))计算空间偏置分数,并叠加到原始注意力分数上:attention_scores = attention_scores + bbox_pos_scores

由此,BROS 的注意力分数同时包含「语义相关性」与「空间相对位置」,模型可以学会「同一列/同一行的 token 更相关」这类二维布局语义。

1.2 两个预训练目标:TMLM 与 AMLM

BROS 使用两个目标进行预训练:

  • TMLM(Token-Masked Language Modeling):与 BERT 相同的 token 掩码语言建模。随机掩码部分 token,模型利用空间信息与其他未掩码 token 预测被掩码 token。
  • AMLM(Area-Masked Language Modeling):TMLM 的 2D 版本。区别在于 AMLM 掩码的单位是**文本块(区域)**而非单个 token——整块区域的文本被掩码,模型再基于周围文本与布局进行预测。这种掩码策略迫使模型学习区块级(如「发票号」这类字段区域)的语义结构,与文档 KIE 任务中「按区域抽取字段」的需求天然对齐。

论文摘要指出:BROS 通过「文本 + 布局的有效组合」这一回归本质的思路,在 FUNSD、SROIE*、CORD、SciTSR 四个 KIE 基准上,在不依赖视觉特征的情况下取得了与以往方法相当或更好的结果,并揭示了 KIE 的两大现实挑战——(1) 错误文本排序带来的误差最小化,(2) 从少量下游样本中高效学习。

注意:以上论文结论来自仓库文档对论文摘要的引用,属于论文声明内容;具体数值请以论文原文为准,仓库源码本身不包含基准测试数据。


二、模型家族:三种下游任务头

BROS 在仓库中提供了 4 个类,围绕 KIE 的三个子任务展开(modeling_bros.py 中均有完整实现,并在 test_modeling_bros.py 中有对应测试覆盖)。

2.1 BrosModel:骨干编码器

BrosModel是 BROS 的基础模型,由三部分组成(modeling_bros.py L509-L513):

  • BrosTextEmbeddings:文本 embedding(word / position / token_type 三路求和 + LayerNorm + Dropout),结构与 BERT 一致;
  • BrosBboxEmbeddings:相对边界框位置编码模块(见上文);
  • BrosEncoder:堆叠num_hidden_layersBrosLayer,每层包含带空间偏置的BrosSelfAttention与 FFN。

forward接受input_idsbboxattention_masktoken_type_idsposition_idsinputs_embeds等输入。其中:

  • bbox是必填项,不传会直接抛出ValueError("You have to specify bbox")(L563-L564);
  • bbox每个 token 只有 4 个坐标(x0, y0, x1, y1),forward内部会通过bbox[:, :, [0, 1, 2, 1, 2, 3, 0, 3]]自动扩展为 8 个坐标(左上、右上、右下、左下四角),用于二维位置编码(L594-L595);
  • 坐标会乘以config.bbox_scale(默认 100.0)后再送入位置编码(L596)。

2.2 BrosForTokenClassification:经典序列标注

BrosForTokenClassificationBrosModel之上加一个简单的线性分类层(modeling_bros.py L620-L708),为每个 token 预测标签,对应文档 NER/序列标注任务。它假设输入 token 已被完美串行化(perfectly serialized)——即按正确的二维阅读顺序排好,这对于存在于 2D 空间的文档文本是很有挑战性的前置条件。

损失计算时(L692-L701),若提供了bbox_first_token_mask,则只对每个边界框的首 token计算交叉熵损失,从而避免同一框内因分词产生的子 token 重复计损。

2.3 BrosSpadeEEForTokenClassification:SPADE 实体抽取

BrosSpadeEEForTokenClassification(EE = Entity Extraction)采用 SPADE 的两阶段解码思想(modeling_bros.py L720-L854):

  • initial_token_classifier:预测每个实体的首 token(两层 MLP 结构,源码 L736-L741);
  • subsequent_token_classifier:基于BrosRelationExtractor,预测实体内部「下一个 token」的链接关系(L744)。

前向时,initial_token_logitssubsequent_token_logits分别输出,损失为两者之和(L846:loss = initial_token_loss + subsequent_token_loss)。由于它是「从一个 token 预测下一个连接 token」,对文本排序错误具有更强的鲁棒性——这正是它与BrosForTokenClassification的本质区别:后者依赖完美串行化,前者通过 token 到 token 的链接逐步构建实体。

2.4 BrosSpadeELForTokenClassification:SPADE 实体链接

BrosSpadeELForTokenClassification(EL = Entity Linking)在BrosModel之上放置一个entity_linker(同样是BrosRelationExtractor,modeling_bros.py L876),用于实体间关系预测:当两个实体共享某种关系时,预测从一个实体的某个 token 指向另一个实体某个 token 的链接,即完成文档关系抽取(如「供应商」→「发票号」)。

BrosRelationExtractor(L406-L437)是 SPADE 两个头共用的核心组件,其内部包含querykey两个线性层以及一个可学习的dummy_node(哑节点,用于支持「无关系/指向空」的预测),输出形状为(n_relations, batch, seq, seq)的关系分数矩阵,推理时计算 query 与 key(拼接 dummy node 后)的矩阵乘。

2.5 三个分类头的选择建议

模型类任务对串行化错误的鲁棒性输出
BrosForTokenClassification每 token 打标签(经典序列标注)低(依赖完美串行化)logits
BrosSpadeEEForTokenClassification实体抽取(首 token + 后续 token 链接)高(逐 token 链接构建实体)initial_token_logits+subsequent_token_logits
BrosSpadeELForTokenClassification实体间关系/链接预测关系分数矩阵logits

三、输入准备:边界框归一化与 box_first_token_mask

3.1 边界框的获取与归一化

BrosModel.forward需要input_idsbbox两个核心输入(modeling_bros.py L527-L564)。每个边界框采用(x0, y0, x1, y1)格式,即左上角与右下角。边界框的获取依赖外部 OCR 系统,Transformers 仓库本身不提供 OCR 能力;文档要求坐标满足归一化约束:

  • x坐标用文档图像宽度归一化;
  • y坐标用文档图像高度归一化;

归一化后坐标落在0 ~ 1区间。原文档给出的归一化函数如下(注意原文档代码中width/height为外部传入变量,实际使用时请以函数入参doc_width/doc_height为准):

def expand_and_normalize_bbox(bboxes, doc_width, doc_height): # here, bboxes are numpy array # Normalize bbox -> 0 ~ 1 bboxes[:, [0, 2]] = bboxes[:, [0, 2]] / doc_width bboxes[:, [1, 3]] = bboxes[:, [1, 3]] / doc_height

从实现看,归一化后的坐标会被config.bbox_scale(默认 100.0)放大(modeling_bros.py L596),因此输入坐标并不强制为 0~1,只要全序列尺度一致即可,但遵循文档的 0~1 归一化约定是与预训练 checkpoint 保持行为一致的最稳妥做法。

3.2 构造 box_first_token_mask

对于BrosForTokenClassificationBrosSpadeEEForTokenClassificationBrosSpadeELForTokenClassification损失计算需要第三个关键输入box_first_token_mask(modeling_bros.py 中三个 forward 均有该参数,L643、L755、L887)。它的作用是把每个边界框内除首 token 之外的子 token 排除在损失之外——因为一个词(一个框)可能被分词器切成多个子 token,只有框首 token 携带该词的完整语义标签。

文档给出了标准的构造方法:对每个词单独encode(不加特殊 token),累加各词 token 数得到每个框的起止索引,再截断到max_seq_length范围内,把「框首 token 位置」置为 True:

def make_box_first_token_mask(bboxes, words, tokenizer, max_seq_length=512): box_first_token_mask = np.zeros(max_seq_length, dtype=np.bool_) # encode(tokenize) each word from words (list[str]) input_ids_list: list[list[int]] = [tokenizer.encode(e, add_special_tokens=False) for e in words] # get the length of each box tokens_length_list: list[int] = [len(l) for l in input_ids_list] box_end_token_indices = np.array(list(itertools.accumulate(tokens_length_list))) box_start_token_indices = box_end_token_indices - np.array(tokens_length_list) # filter out the indices that are out of max_seq_length box_end_token_indices = box_end_token_indices[box_end_token_indices < max_seq_length - 1] if len(box_start_token_indices) > len(box_end_token_indices): box_start_token_indices = box_start_token_indices[: len(box_end_token_indices)] # set box_start_token_indices to True box_first_token_mask[box_start_token_indices] = True return box_first_token_mask

该 mask 在三个头中的用法与源码位置:

  • BrosForTokenClassification:损失只统计被 mask 选中的 token(L695-L699);
  • BrosSpadeEEForTokenClassificationinitial_token_loss只在框首 token 上计算(L831-L838),subsequent_token_loss通过subsequent_token_mask(即 attention_mask)统计(L840-L844);
  • BrosSpadeELForTokenClassification:用 mask 过滤「非框首 token 不能作为关系起点/终点」,并对自环(self-token)做掩码(L943-L956)。

四、BrosConfig:关键配置项

BrosConfig继承自PreTrainedConfig,定义在 configuration_bros.py,model_type = "bros"。除 BERT 风格的标准超参数(vocab_size=30522hidden_size=768num_hidden_layers=12num_attention_heads=12intermediate_size=3072hidden_act="gelu"max_position_embeddings=512type_vocab_size=2pad_token_id=0等)外,BROS 特有配置如下:

配置项默认值说明
dim_bbox8边界框坐标维度,即每个 token 的 8 个坐标值(x0, y1, x1, y0, x1, y1, x0, y1 四角展开)
bbox_scale100.0边界框坐标的缩放系数,前向时scaled_bbox = bbox * bbox_scale(modeling_bros.py L596)
n_relations1SPADE-EE / SPADE-EL 头的关系数量
classifier_dropout_prob0.1分类头 dropout 概率

此外,__post_init__会自动派生三个内部维度(configuration_bros.py L70-L74):

  • dim_bbox_sinusoid_emb_2d = hidden_size // 4(= 192);
  • dim_bbox_sinusoid_emb_1d = dim_bbox_sinusoid_emb_2d // dim_bbox(= 24);
  • dim_bbox_projection = hidden_size // num_attention_heads(= 64,即注意力头维度,空间嵌入投影到与 attention head 相同的维度以便相加)。

配置类自带的标准用法(与BrosModel配合):

>>> from transformers import BrosConfig, BrosModel >>> # Initializing a BROS jinho8345/bros-base-uncased style configuration >>> configuration = BrosConfig() >>> # Initializing a model from the jinho8345/bros-base-uncased style configuration >>> model = BrosModel(configuration) >>> # Accessing the model configuration >>> configuration = model.config

五、BrosProcessor:文本预处理入口

BrosProcessor定义在 processing_bros.py,继承ProcessorMixin,本质是对 tokenizer 的轻量封装:构造时必须传入tokenizer,否则抛出ValueError("You need to specify a tokenizer.")。默认的文本处理参数为add_special_tokens=Truepadding=Falsestride=0return_overflowing_tokens=False等。

注意:BrosProcessor 只负责文本侧(tokenize)处理,边界框 bbox 需要用户自行组织。官方使用示例中通过torch.tensor(...).repeat(...)手工构造与 token 序列等长的 bbox 张量。


六、完整使用示例

6.1 从预训练 checkpoint 加载并使用 BrosModel

以下代码来自BrosModel.forward的 docstring 示例(modeling_bros.py L545-L559),可直接运行:

>>> import torch >>> from transformers import BrosProcessor, BrosModel >>> processor = BrosProcessor.from_pretrained("jinho8345/bros-base-uncased") >>> model = BrosModel.from_pretrained("jinho8345/bros-base-uncased") >>> encoding = processor("Hello, my dog is cute", add_special_tokens=False, return_tensors="pt") >>> bbox = torch.tensor([[[0, 0, 1, 1]]]).repeat(1, encoding["input_ids"].shape[-1], 1) >>> encoding["bbox"] = bbox >>> outputs = model(**encoding) >>> last_hidden_states = outputs.last_hidden_state

6.2 使用三种分类头

三个分类头的加载方式与输入组织完全一致,区别仅在输出字段:

>>> import torch >>> from transformers import BrosProcessor, BrosForTokenClassification >>> processor = BrosProcessor.from_pretrained("jinho8345/bros-base-uncased") >>> model = BrosForTokenClassification.from_pretrained("jinho8345/bros-base-uncased") >>> encoding = processor("Hello, my dog is cute", add_special_tokens=False, return_tensors="pt") >>> bbox = torch.tensor([[[0, 0, 1, 1]]]).repeat(1, encoding["input_ids"].shape[-1], 1) >>> encoding["bbox"] = bbox >>> outputs = model(**encoding)

BrosForTokenClassification替换为BrosSpadeEEForTokenClassification可得到initial_token_logitssubsequent_token_logitsBrosSpadeOutput结构,见 modeling_bros.py L49-L64);替换为BrosSpadeELForTokenClassification则输出实体链接的关系分数矩阵。训练时,分别传入labels(Token 分类 / EL)或initial_token_labels+subsequent_token_labels(EE)即可自动计算损失。

6.3 真实文档的完整数据管线

将以上要素组合,一个真实的 BROS 文档 KIE 数据管线为:

  1. OCR:用外部 OCR 系统(如 Tesseract、PaddleOCR 等)识别文档图像,得到words(词文本列表)与bboxes(每词边界框,像素坐标);
  2. 归一化:用expand_and_normalize_bbox按图像宽高把坐标缩放到 0~1;
  3. 序列化:按阅读顺序(通常按 y 行、x 列排序)把词串成序列;
  4. 分词:用 tokenizer 把每个词编码为子 token,拼接成input_ids,同时记录每词的 token 起止索引;
  5. 构造 mask:用make_box_first_token_mask生成box_first_token_mask
  6. 组织 bbox:把每个词的边界框按 token 展开对齐到序列长度(一个词的所有子 token 共享同一 bbox);
  7. 前向/训练model(input_ids, bbox=..., attention_mask=..., bbox_first_token_mask=..., labels=...)

七、源码级验证:测试覆盖与 checkpoint 转换

7.1 测试覆盖

test_modeling_bros.py 提供了完整的模型测试套件,BrosModelTester对 bbox 的合法性做了约束(保证 x1 > x0、y1 > y0,见 L91-L102),并默认启用bbox_first_token_masktoken_type_idslabels等输入;测试覆盖BrosModelBrosForTokenClassificationBrosSpadeEEForTokenClassificationBrosSpadeELForTokenClassification四个类的前向与损失计算,可作为自定义微调时的输入格式参照。

7.2 checkpoint 转换

convert_bros_to_pytorch.py 演示了从原始 clovaai/bros 仓库权重转换到 Transformers 格式的关键步骤:键名重命名(如embeddings.bbox_projection.weightbbox_embeddings.bbox_projection.weight)、剔除无需加载的键(如embeddings.bbox_sinusoid_emb.inv_freq,因为inv_freq作为 buffer 由模型自行初始化),随后load_state_dict并验证输出一致性。如果你需要把自训练的原始 BROS 权重接入 Transformers 生态,可参考该脚本。


八、适用场景与局限

8.1 适用场景

  • 文档 NER / 序列标注:发票、收据、表单、证件等扫描件的字段抽取(BrosForTokenClassification);
  • 实体抽取:对文本排序错误鲁棒的端到端实体识别(BrosSpadeEEForTokenClassification);
  • 实体链接 / 关系抽取:字段之间关系的预测,如「开票方」与「发票号」的关联(BrosSpadeELForTokenClassification);
  • 文档理解研究:作为文本 + 布局联合建模的基线模型,对比 LayoutLM 等视觉-文本多模态方案。

8.2 局限与前提

  • 依赖外部 OCR:bbox 必须由外部 OCR 系统提供,模型本身不做版面分析或文字识别;
  • 序列化假设BrosForTokenClassification依赖 token 的完美串行化,实际使用时需配合稳健的阅读顺序排序;
  • 输入约束:坐标需按文档宽高归一化,bbox 为必填输入;序列长度受max_position_embeddings(默认 512)限制;
  • 论文结论边界:FUNSD / SROIE / CORD / SciTSR 上的性能结论来自论文原文声明,仓库源码不内置这些基准的复现数据。

参考资源(仓库内)

  • 模型实现:四类模型完整 PyTorch 实现与空间位置编码细节
  • 配置定义:BrosConfig及 BROS 特有超参数
  • 处理器:BrosProcessor文本预处理
  • checkpoint 转换脚本:原始权重到 Transformers 格式的转换参考
  • 模型测试:四个模型类的输入输出与损失计算验证
  • 英文官方文档:与本文同源的其他语言版本

【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询