Hugging Face Transformers 中的 LightOnOcr:轻量级端到端 OCR 与文档理解视觉语言模型全解析
【免费下载链接】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
LightOnOcr是 Transformers 于 2026-01-14 合入的一个紧凑型端到端视觉语言模型(VLM),专为 OCR(光学字符识别)与文档理解设计:它用基于 Pixtral 的 Vision Transformer 编码器提取版面感知的图像特征,再交给一个由高质量开放 VLM 蒸馏而来的轻量 Qwen3 文本解码器生成结构化文本。本文基于官方模型文档 model_doc/lighton_ocr.md 与仓库源码,完整覆盖其使用方式、配置结构、Processor 工作原理与模型前向流程,并给出可运行示例与验证路径。
1. 模型概览:架构与定位
官方文档给出的模型定位如下:
LightOnOcris a compact, end-to-end vision–language model for Optical Character Recognition (OCR) and document understanding. It achieves state-of-the-art accuracy in its weight class while being several times faster and cheaper than larger general-purpose VLMs.
从源码结构看,LightOnOcr 是一个典型的"视觉编码器 + 多模态投影器 + 文本解码器"三件套。模型定义文件 中LightOnOcrModel的构造函数清晰地展示了这一组合:
class LightOnOcrModel(LightOnOcrPreTrainedModel): def __init__(self, config: LightOnOcrConfig): super().__init__(config) self.vision_encoder = AutoModel.from_config(config.vision_config) # Pixtral 视觉编码器 self.vision_projection = LightOnOcrMultiModalProjector(config) # 多模态投影器 self.language_model = AutoModel.from_config(config.text_config) # Qwen3 语言模型 self.post_init()三个关键组件与文档描述一一对应:
| 组件 | 实现 | 默认模型族 | 说明 |
|---|---|---|---|
vision_encoder | Pixtral 视觉编码器 | pixtral | 24 层 ViT,patch_size=14,hidden_size=1024 |
vision_projection | LightOnOcrMultiModalProjector | — | RMSNorm + PatchMerger + 两层线性映射(GELU 激活) |
language_model | Qwen3 解码器 | qwen3 | 28 层,GQA(16 头 / 8 KV 头),vocab_size=151936 |
模型通过model_type = "lighton_ocr"注册,可在 Auto 映射 中被AutoModel等自动类识别,并映射到image-text-to-text管线(见 测试中的 pipeline_model_mapping)。
2. 快速上手:官方 Usage 示例
以下是官方文档给出的完整用法,核心路径是Processor.apply_chat_template→model.generate→processor.decode三步:
from transformers import LightOnOcrForConditionalGeneration, LightOnOcrProcessor # 加载模型与处理器(官方模型卡权重:lightonai/LightOnOCR-1B-1025) model = LightOnOcrForConditionalGeneration.from_pretrained("lightonai/LightOnOCR-1025".replace("LightOnOCR", "LightOnOCR"), device_map="auto") model = LightOnOcrForConditionalGeneration.from_pretrained("lightonai/LightOnOCR-1B-1025", device_map="auto") processor = LightOnOcrProcessor.from_pretrained("lightonai/LightOnOCR-1B-1025") # 一张收据图片 URL(SROIE 数据集样例) url = "https://huggingface.co/datasets/hf-internal-testing/fixtures_ocr/resolve/main/SROIE-receipt.jpeg" # 以对话形式构造输入:user 消息中只放图片 conversation = [{"role": "user", "content": [{"type": "image", "url": url}]}] inputs = processor.apply_chat_template( conversation, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) output_ids = model.generate(**inputs, max_new_tokens=1024) # 切掉 prompt 部分,只保留新生成的 token generated_ids = output_ids[0, inputs["input_ids"].shape[1]:] output_text = processor.decode(generated_ids, skip_special_tokens=True) print(output_text)几点实操说明(结合源码与测试确认):
- 输入张量键名:Processor 输出的模型输入名为
input_ids、attention_mask、pixel_values、image_sizes四件套(模型输入名校验测试)。其中image_sizes记录每张图缩放后的 (height, width),模型用它来切分每张图的视觉 token。 - 生成参数:官方集成测试 test_lightonocr_ocr_integration 使用
max_new_tokens=50, do_sample=False, num_beams=1做贪心解码,并把输出与期望文本做SequenceMatcher相似度比对(要求 >95%),期望输出形如"Document No : TD01167104\n\nDate : 25/12/2018 8:13:39 PM..."——说明模型直接产出带换行、保留版式的纯文本。 - 可纯文本生成:test_model_can_generate_without_images 验证了不提供图片时
model.generate(input_ids=...)同样可用,因此它本质上仍是一个可自回归的 LM。 - 精度提示:集成测试在加载后使用
dtype=torch.bfloat16做前向,文档示例未显式指定精度,按 Hub 权重的 dtype 默认加载即可。
3. LightOnOcrConfig:配置结构与默认参数
LightOnOcrConfig定义于 configuration_lighton_ocr.py,是一个"复合配置"(sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}),顶层只保留少量共享字段:
| 字段 | 默认值 | 含义 |
|---|---|---|
spatial_merge_size | 2 | 空间合并倍数:每2×2=4个视觉 patch 合并为 1 个图像 token,图像 token 数降为 1/4 |
image_token_id | 151655 | 文本序列中标记图像占位符的 token id(<img>) |
tie_word_embeddings | True | lm_head与embed_tokens共享权重 |
vision_config | None(自动补全为 pixtral) | 视觉编码器配置 |
text_config | None(自动补全为 qwen3) | 文本解码器配置 |
__post_init__的逻辑值得注意:当vision_config/text_config未提供时,会自动实例化一套完整的默认配置(源码 L70-L98),这正是"1B"规格参数的来源:
- 视觉侧(pixtral):
hidden_size=1024、num_hidden_layers=24、num_attention_heads=16、head_dim=64、patch_size=14、rope_theta=10000、hidden_act="silu"; - 文本侧(qwen3):
hidden_size=1024、num_hidden_layers=28、num_attention_heads=16、num_key_value_heads=8(GQA)、head_dim=128、intermediate_size=3072、max_position_embeddings=40960、rope_theta=1000000、vocab_size=151936。
如果你要基于该模型微调或自定义尺寸,只需传入一个text_config/vision_config字典(会按model_type自动映射到对应 Config 类),例如测试中就用小尺寸配置快速构建模型(测试配置示例)。
文档中[[autodoc]]引用的完整 API 面为:LightOnOcrConfig、LightOnOcrProcessor(含__call__)、LightOnOcrModel(含forward、get_image_features)、LightOnOcrForConditionalGeneration(含forward、get_image_features),全部由 modular_lighton_ocr.py 生成,configuration_lighton_ocr.py等文件头部的注释表明 CI 会强制 modular 与生成文件保持一致。
4. LightOnOcrProcessor:图像 token 的展开机制
这是 LightOnOcr 使用中最容易踩坑的部分——一张图在文本序列里到底占多少个 token。答案在 processing_lighton_ocr.py 中:
def __init__(self, image_processor=None, tokenizer=None, patch_size: int = 14, spatial_merge_size: int = 2, ...): self.patch_size = patch_size self.spatial_merge_size = spatial_merge_size # 有效 patch 尺寸 = 14 × 2 = 28 self.effective_patch_size = patch_size * spatial_merge_size # 特殊 token 直接取自 tokenizer 属性 self.image_token = tokenizer.image_token # "<img>" self.image_break_token = tokenizer.image_break_token # "<im_start>" self.image_end_token = tokenizer.image_end_token # "<im_end>"- 有效 patch 为 28×28:视觉编码器的
patch_size=14与spatial_merge_size=2相乘,得到每个图像 token 对应原图 28×28 像素区域。 - token 展开规则:
replace_image_token(L132-L136)把单张图的占位符替换为num_height_tokens × num_width_tokens个<img>token,其中num_*_tokens = 图像尺寸 // effective_patch_size。例如一张 112×112 的图会产生(112/28)² = 16个图像 token——这与 测试中的 token 计数推导num_patches // spatial_merge_size**2完全一致。 - 缩放对齐:
_get_num_multimodal_tokens(L138-L174)在预计算占位 token 数时,复用 Pixtral 的get_resize_output_image_size:先按size["longest_edge"]等比缩小,再把边长向下取整到 patch 的整数倍,最后除以effective_patch_size得到 token 数。这保证了"处理器算出的占位 token 数"与"视觉编码器实际输出的特征数"严格相等。 - 默认参数:
LightOnOcrProcessorKwargs._defaults规定文本侧padding=False、输出默认return_tensors="pt"。 - 特殊 token 的具体取值(
<img>/<im_start>/<im_end>)由 测试 明确断言。
验证:test_processor_image_token_expansion验证了单图场景下<img>token 会被展开为多个(>1);test_processor_batch_processing验证了批量多图输入下pixel_values与input_ids的 batch 维度对齐。
5. LightOnOcrModel 前向流程:视觉特征如何注入文本序列
LightOnOcrModel.forward(modeling_lighton_ocr.py L211-L254)的流程可以分为四步:
第一步:文本嵌入。input_ids通过get_input_embeddings()转为inputs_embeds;若调用方直接传了inputs_embeds则跳过(二者必须恰好给一个,否则抛ValueError)。
第二步:提取并投影图像特征。若pixel_values非空,调用get_image_features(L168-L183):
def get_image_features(self, pixel_values, image_sizes, **kwargs): image_outputs = self.vision_encoder(pixel_values, image_sizes=image_sizes, return_dict=True) image_features = image_outputs.last_hidden_state image_features = self.vision_projection(image_features.squeeze(0), image_sizes) # 按有效 patch 尺寸把特征切回"每张图一段" downsample_ratio = self.config.vision_config.patch_size * self.config.spatial_merge_size split_sizes = [(h // downsample_ratio) * (w // downsample_ratio) for h, w in image_sizes] image_features = torch.split(image_features, split_sizes) image_outputs.pooler_output = image_features return image_outputs投影器LightOnOcrMultiModalProjector(L97-L113)内部为:RMSNorm(vision_hidden)→PatchMerger(用unfold做 2×2 空间合并后接一个无偏置线性层,把4×hidden压回hidden)→Linear(1024→1024, bias=False)→GELU→Linear(1024→1024, bias=False)。最终pooler_output是一个list,每个元素是一张图的 token 特征(shape 为[num_tokens_of_image_i, text_hidden_size])。
第三步:占位符对齐校验。get_placeholder_mask(L185-L207)统计input_ids中等于image_token_id(默认 151655)的位置数,并与图像特征总数比对,不一致时抛出:
Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}对应的测试test_mismatching_num_image_tokens专门验证了三种情形:少给一张图(报错)、一条 prompt 里两张图但只给一个图像特征(报错)、两张图配两段文本(正常通过)——即同一 prompt 可以包含多张图。
第四步:masked_scatter注入。把图像特征cat成一维后,通过inputs_embeds.masked_scatter(special_image_mask, image_features)原样填入所有<img>位置,然后整个嵌入序列送入language_model,输出LightOnOcrModelOutputWithPast(额外携带image_hidden_states字段,见 输出 dataclass)。
6. LightOnOcrForConditionalGeneration:生成头与权重绑定
生成模型类 在LightOnOcrModel之上只加了 lm_head 与GenerationMixin:
class LightOnOcrForConditionalGeneration(LightOnOcrPreTrainedModel, GenerationMixin): _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"} def __init__(self, config): super().__init__(config) self.model = LightOnOcrModel(config) self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False) self.post_init()要点:
lm_head为无偏置线性层,输出维度vocab_size=151936;由于tie_word_embeddings=True,它默认与embed_tokens共享权重,因此"1B"级权重非常紧凑。forward支持labels(用self.loss_function计算 next-token 预测 loss,可用于微调/SFT)、logits_to_keep(只计算末尾部分位置的 logits 以省显存)等标准参数,签名见 forward 定义。- 基类 LightOnOcrPreTrainedModel 声明了能力位:
input_modalities = ("image", "text")、supports_gradient_checkpointing = True、_supports_flash_attn / _supports_sdpa = True、_can_compile_fullgraph = True——即支持 FlashAttention-2 与 SDPA 注意力后端、梯度检查点以及torch.compile全图编译。 - 一个值得留意的实现细节:测试文件注释指出 "LightOnOcr uses a PixtralVisionModel, which merges batch_size and num_patches in index 1, with index 0 hardcoded to 1",因此该测试类跳过了图像特征输出 shape 的通用断言(
skip_test_image_features_output_shape = True),并因多模态占位 mask 依赖数据而关闭了 torch.export(测试 L228-L232)。
7. 测试与验证路径
| 关注点 | 测试 | 说明 |
|---|---|---|
| OCR 端到端效果 | test_lightonocr_ocr_integration | 用 Hub 上的lightonai/LightOnOCR-1B-1025+ SROIE 收据图贪心解码 50 token,与期望文本做 95% 相似度断言 |
| 图像/文本 token 数不一致 | test_mismatching_num_image_tokens | 覆盖单图缺失、多图缺特征、多图匹配三种情况 |
| 不同 spatial_merge_size | test_spatial_merge_size | 1/2/4 均可构建模型,投影器参数随之变化 |
| 变尺寸图像 | test_forward_pass_with_image_sizes | 同 batch 内不同图像尺寸的前向 |
| 投影器维度 | test_vision_projection | 输出最后一维等于text_config.hidden_size |
| Processor 行为 | test_processing_lighton_ocr.py | token 展开、batch 处理、特殊 token、image_sizes输出等 |
运行方式:transformers的模型测试均为标准 pytest/unittest 套件,集成测试(标记@slow)需要联网拉取 Hub 权重,快测(如LightOnOcrForConditionalGenerationModelTest)则用随机初始化的迷你配置本地跑通。
8. 小结与适用边界
- 适用场景:文档/票据/扫描件类页面的结构化文本抽取(版式感知的 OCR),以及需要低成本多模态理解的其他图像问答任务;从源码结构看它也保留了纯文本自回归能力。
- 关键约束:
- 输入图像会被缩放到
longest_edge以内并对齐到 28 的整数倍,超长页面的 token 数会随分辨率增长,max_new_tokens需要按页面长度调大(官方示例取 1024); - 处理器展开的
<img>token 数必须与视觉特征数严格一致,手动构造输入时务必带上image_sizes且不要改动占位 token 数量,否则触发 "Image features and image tokens do not match"; - 官方示例使用
apply_chat_template的对话式输入格式([{"role": "user", "content": [{"type": "image", "url": ...}]}]),图片 URL 或本地 PIL 图均可。
- 输入图像会被缩放到
- 代码入口索引:配置 configuration_lighton_ocr.py、建模 modeling_lighton_ocr.py、处理器 processing_lighton_ocr.py、modular 源 modular_lighton_ocr.py、测试 tests/models/lighton_ocr/。
【免费下载链接】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),仅供参考