news 2026/9/27 7:50:14

PaddleNLP Pipelines 文档智能节点实战:DocOCRProcessor 与 DocPrompter 全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PaddleNLP Pipelines 文档智能节点实战:DocOCRProcessor 与 DocPrompter 全解析
  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址:https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看免费下载

导读

本文围绕 PaddleNLP 仓库中slm/pipelines子项目下的 Document Intelligence 模块展开,系统讲解其两个核心节点——负责文档图像预处理与 OCR 的DocOCRProcessor,以及负责"文档提示问答"(DocPrompt)的DocPrompter。通过阅读本文,你将掌握这两个节点的完整 API 参数、输入输出约定、静态图推理调用链、YAML 流水线配置方法,以及它们与paddlenlp.taskflow.utils底层工具函数之间的协作关系,可直接用于搭建"文档问答"类应用。本文对应的 API 文档页为 document_intelligence.md,其中通过 mkdocstrings 指令引用了pipelines.pipelines.nodes.document.document_intelligence与pipelines.pipelines.nodes.document.document_preprocessor两个模块。

一、模块概览:一条"图像输入 → 结构化问答"的流水线

Document Intelligence 模块在 PaddleNLP Pipelines 中承担的角色是:将图片、扫描件等非结构化文档输入,经过 OCR 版面解析与视觉-语言联合建模,最终输出针对用户 Prompt 的答案。整个模块由两个节点串联而成:

节点类源码文件职责
DocOCRProcessordocument_preprocessor.py文档预处理:把图片路径/URL/Base64 字节流转换为 OCR 结果
DocPrompterdocument_intelligence.py文档理解:基于 OCR 结果与 Prompt 抽取答案

两个节点均继承自pipelines.nodes.base.BaseComponent,outgoing_edges = 1,即每个节点只有一条输出边,分别返回("output_1", 数据)形式的二元组。它们通过init.py 对外导出,并在 nodes/init.py 中随pipelines.nodes包统一注册,因此可以直接在 YAML 配置中以组件类型名引用。

从 mkdocs 配置 mkdocs.yml 可以看到,该模块被组织在 "Nodes module" 文档分组下,文档页即package/nodes/document_intelligence.md,使用 mkdocstrings 的 python handler(paths: [pipelines/pipelines])自动提取上述两个模块的签名与 docstring 生成 API 文档。

二、DocOCRProcessor:文档预处理节点

2.1 构造参数

DocOCRProcessor(use_gpu: bool = True, lang: str = "ch")
  • use_gpu:是否使用 GPU。源码中会先通过paddle.get_device()判断当前环境,若为 CPU 环境则强制回退为False("Falls back on CPU if no GPU is available")。
  • lang:OCR 模型处理语言,默认"ch",对应 PaddleOCR 的语言参数。

构造时内部实例化PaddleOCR(use_angle_cls=True, show_log=False, use_gpu=self._use_gpu, lang=self._lang),其中use_angle_cls=True表示启用方向分类,用于自动矫正旋转后的文本行;show_log=False关闭推理日志。也就是说该节点直接复用 PaddleOCR 完成检测 + 方向分类 + 识别。

2.2 输入校验与三种文档来源

节点入口run(meta: dict)首先调用_check_input_text(meta)对输入做严格校验。输入必须是 dict 或 dict 列表,且必须包含doc与prompt两个字段,否则抛出ValueError/TypeError并附带明确的错误信息。

doc字段支持三种形式(源码见 document_preprocessor.py 第 64-79 行):

  1. 本地图片路径:os.path.isfile判定为真时直接使用;
  2. 图片 URL:以http://或https://开头时,先调用download_file下载到当前目录,再以本地文件名作为doc;
  3. Base64 字节流:既非 URL 也非本地文件时,按 Base64 解码后经Image.open(BytesIO(...))转成 RGB 图像,保存为./tmp.jpg后交给后续流程。

prompt字段可以是单个字符串(自动包装成单元素列表)或全部由字符串组成的列表;也可以额外提供word_boxes字段,用于跳过 OCR、直接使用调用方已准备好的版面框信息(格式为[[word_str, [x1, y1, x2, y2]], ...])。

2.3 run 流程与输出

def run(self, meta: dict): example = self._check_input_text(meta)[0] if "word_boxes" in example.keys(): ocr_result = example["word_boxes"] example["ocr_type"] = "word_boxes" else: ocr_result = self._ocr.ocr(example["doc"], cls=True) example["ocr_type"] = "ppocr" # Compatible with paddleocr>=2.6.0.2 ocr_result = ocr_result[0] if len(ocr_result) == 1 else ocr_result example["ocr_result"] = ocr_result output = {"example": example} return output, "output_1"

关键点:

  • 若用户已提供word_boxes,则直接采用,ocr_type标记为"word_boxes",避免重复 OCR;否则调用self._ocr.ocr(doc, cls=True)执行完整 OCR,ocr_type标记为"ppocr"。
  • 兼容性处理:PaddleOCR 2.6.0.2 及以上版本返回结果嵌套层级有变化,源码用ocr_result = ocr_result[0] if len(ocr_result) == 1 else ocr_result做了适配。
  • 输出结构为{"example": {...}, "output_1": ...},其中example在原有doc、prompt基础上新增ocr_result与ocr_type两个字段,作为下游DocPrompter的输入。

三、DocPrompter:文档提示问答节点

3.1 构造参数全解

DocPrompter( topn: int = 1, use_gpu: bool = True, task_path: str = None, model: str = "docprompt", device_id: int = 0, num_threads: int = None, lang: str = "ch", batch_size: int = 1, )

各参数含义(与源码 docstring 一致):

参数默认值说明
topn1返回前 N 个答案
use_gpuTrue是否使用全部可用 GPU,无 GPU 时自动回退 CPU
task_pathNone自定义模型参数目录;为None时默认下载到PPNLP_HOME/pipelines/document_intelligence/docprompt
model"docprompt"模型名称,当前URLS字典中仅注册了docprompt一项
device_id0GPU 设备 ID
num_threadsNone推理线程数;为None时取math.ceil(cpu_count() / 2)
lang"ch"语言,影响答案排序策略(详见后文sort_res)
batch_size1单次推理的样本数;文档注释建议推理内存占用远低于训练,可增大 batch 使整个文档只用一个 batch

3.2 模型获取与静态图加载

构造阶段依次完成四件事:

  1. 权重下载:通过download_file(self._task_path, "docprompt_params.tar", URLS["docprompt"][0], URLS["docprompt"][1])下载模型压缩包,并校验 MD5(8eae8148981731f230b328076c5a08bf)。
  2. 推理模型装配:_get_inference_model()拼接task_path/static/inference下的模型文件与参数文件路径(后缀来自PADDLE_INFERENCE_MODEL_SUFFIX与PADDLE_INFERENCE_WEIGHTS_SUFFIX),并用paddle.inference.Config构造推理配置。
  3. 运行环境配置:_prepare_static_mode()中,CPU 环境调用disable_gpu()并enable_mkldnn();GPU 环境调用enable_use_gpu(100, device_id)并删除embedding_eltwise_layernorm_fuse_pass融合 Pass;同时设置线程数、关闭 feed/fetch 旧接口、关闭 glog、开启显存优化、关闭 IR 优化,最终paddle.inference.create_predictor创建预测器。
  4. 模型侧组件初始化:加载ernie-layoutx-base-uncased分词器(AutoTokenizer.from_pretrained),并基于该分词器构造ImageReader(super_rel_pos=False, tokenizer=...)。

3.3 推理主流程 _run_model

run(example: dict)将单条输入包装为列表交给_run_model,返回{"results": results}。

_run_model对每个 example 依次执行:

  • 从 example 中取出ocr_result、doc(文档路径)、prompt、ocr_type;
  • OCR 结果为空时直接构造空答案占位:{"prompt": p, "result": [{"value": "", "prob": 0.0, "start": -1, "end": -1}]};
  • 否则调用self._reader.data_generator(ocr_result, doc_path, prompt, batch_size, ocr_type)生成批次数据;
  • 逐 batch 将输入copy_from_cpu写入 predictor 输入句柄、执行predictor.run()、将输出copy_to_cpu取回,得到unique_ids与seq_logits;
  • 借助find_answer_pos(result.seq_logits, feature)在序列 logits 上寻找候选答案区间,再经get_doc_pred(...)还原为具体答案文本;
  • 若某 prompt 没有任何候选答案,则同样返回空答案占位;否则用sort_res(example_query, preds, doc_tokens, boxes, lang)排序并截取前topn个。

最终每个 example 产出一组{"prompt": ..., "result": [...]},聚合后返回all_predictions_list。输出结果中每个 answer 包含value(答案文本)、prob(置信度)、start/end(在文档 token 序列中的起止位置,无效时为 -1)。

四、底层工具函数:paddlenlp.taskflow.utils 中的关键实现

DocPrompter复用了 utils.py(路径paddlenlp/taskflow/utils.py)中的一批函数,理解它们才能完整把握推理链路:

4.1 ImageReader:OCR 结果 → 模型特征

ImageReader类(utils.py 第 1701 行起)负责把 OCR 输出转换成布局感知的语言模型输入:

  • ppocr2example:将 PaddleOCR 输出(每行含四点坐标与文本)转成Bbox(left, top, width, height)线段,按版面排序后生成doc_tokens、doc_boxes、ori_boxes(原始尺寸坐标)、doc_segment_ids,并把图像转 Base64 供视觉分支使用;
  • box2example:处理word_boxes格式输入;
  • example2feature:将 example 切分为max_seq_len=512(ImageReader默认,另有doc_stride=128滑窗)、max_key_len=16的 features,同时维护token_to_orig_map、token_is_max_context等对齐信息;
  • data_generator:按ocr_type选择转换入口,随后按 batch_size 打包并yield供 predictor 消费。图像统一ResizeImage(target_size=224)、按 ImageNet 均方差归一化,并做PadBatch(pad_to_stride=32)。

4.2 答案定位与解码

  • viterbi_decode(logits)(utils.py 第 2419 行起):在OIB标签体系下做维特比解码,得到 BIO 序列;
  • find_bio_pos(label):从 BIO 标签中抽取连续实体区间;
  • find_answer_pos(logits, feature):综合维特比路径、token_to_orig_map(start/end 必须能映射回原文 token)与token_is_max_context(必须是最大上下文切片)三重约束,产出合法候选区间列表。

4.3 答案文本还原

get_doc_pred(...)(utils.py 第 2278 行起)对 logits 做 softmax,结合 feature 的 token 对齐信息把候选区间映射回原始文档 token 序列,生成包含value、prob、start、end的结构化答案。

4.4 空间语义排序

sort_res(prompt, ans_list, context, boxes, lang)(utils.py 第 2497 行起)是中文场景下的关键排序逻辑:

  • 答案互不相同时按prob降序排列;
  • 存在重复答案时,先对 prompt 与上下文做最长公共子序列匹配(longestCommonSequence,中文按字符、英文按空格分词),定位 prompt 在版面中的中心坐标;
  • 再计算每个候选答案框中心与 prompt 中心的欧氏距离(calEuclidean),按空间邻近度排序,使得"与提问位置最近的答案"排在前面——这正是文档类问答"答案就在提问附近"这一先验的建模方式。

五、流水线组合:docprompt.yaml 完整配置

仓库提供了开箱即用的文档问答流水线配置 docprompt.yaml:

version: '1.1.0' components: - name: PreProcessor params: use_gpu: True lang: ch type: DocOCRProcessor - name: Reader params: topn: 1 use_gpu: True task_path: model: docprompt device_id: 0 num_threads: lang: ch batch_size: 1 type: DocPrompter pipelines: - name: query_documents nodes: - name: PreProcessor inputs: [Query] - name: Reader inputs: [PreProcessor]

该配置展示了两个节点的标准串联方式:DocOCRProcessor命名为PreProcessor,DocPrompter命名为Reader,组成名为query_documents的流水线。YAML 中的组件参数与两个类的构造函数一一对应,task_path、num_threads留空即使用源码中的默认行为(自动下载权重、自动计算线程数)。

加载方式与 Pipelines 框架保持一致,即通过Pipeline.load_from_yaml(Path(PIPELINE_YAML_PATH), pipeline_name=QUERY_PIPELINE_NAME)读取(参考 base.py 中的load_from_yaml与run实现)。默认配置路径由 config.py 通过环境变量PIPELINE_YAML_PATH指定,可用export PIPELINE_YAML_PATH=rest_api/pipeline/docprompt.yaml切换到文档问答场景。

六、REST API 集成:query_documents 端点

在 controller/search.py 中,query_documents端点(第 162 行起)直接对接该流水线:

@router.post("/query_documents", response_model=DocumentResponse, response_model_exclude_none=True) def query_documents(request: DocumentRequest): result = {} result["meta"] = request.meta params = request.params or {} res = PIPELINE.run(meta=request.meta, params=params, debug=request.debug) result["results"] = res["results"] return result

服务启动时通过PIPELINE = Pipeline.load_from_yaml(...)一次性构建流水线(search.py 第 58 行),随后每次请求调用PIPELINE.run(meta=...),把用户提交的meta(含doc与prompt)传入DocOCRProcessor,经DocPrompter推理后返回results。这意味着文档智能能力可以不经修改直接暴露为 HTTP 服务。

七、输入输出约定速查

DocOCRProcessor 输入(dict 或 dict 列表):

{"doc": "/path/to/image.jpg", "prompt": "金额是多少?"} {"doc": "https://example.com/doc.png", "prompt": ["发票号码", "开票日期"]} {"doc": "<base64 字节流>", "prompt": "公司名称", "word_boxes": [["text", [x1, y1, x2, y2]], ...]}

DocPrompter 输出(单条 example):

[ { "prompt": "金额是多少?", "result": [ {"value": "12,000.00", "prob": 0.987, "start": 34, "end": 36} ] } ]

无答案时会得到{"value": "", "prob": 0.0, "start": -1, "end": -1}占位,保证结构稳定、便于下游统一处理。

结语

Document Intelligence 模块将 PaddleOCR 的版面识别能力与 Ernie-LayoutX 的视觉-语言联合建模能力封装为两个可复用的 Pipelines 节点。开发者在实战中只需遵循"doc+prompt"的输入约定,即可在 docprompt.yaml 基础上按需调整topn、batch_size、lang等参数,快速搭建面向扫描件、截图与拍照文档的问答服务;如需深入定制,则可从 document_intelligence.py、document_preprocessor.py 以及 utils.py 中的ImageReader、sort_res等实现入手进行二次开发。

  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址:https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看免费下载

相关推荐

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 7:42:16

“高并发”对于Python爬虫有多重要?反封控的底层逻辑在这!

很多人做Python爬虫时&#xff0c;往往会忽视动态代理的基础变量——并发。为什么&#xff1f;打个比方&#xff0c;同样是爬10万条数据&#xff0c;串行跑可能得花8小时以上&#xff1b;但如果并发数拉到500&#xff0c;即使网络条件不变&#xff0c;时间可能只要十几分钟。更…

作者头像 李华