mlx-vlm 中 DeepSeek-OCR 实战:SAM+Qwen2 双编码器、动态分辨率与视觉定位解析
发布时间:2026/9/17 20:58:55来源:尧图网络
mlx-vlm 中 DeepSeek-OCR 实战SAMQwen2 双编码器、动态分辨率与视觉定位解析【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm本文基于 mlx-vlm 仓库中 DeepSeek-OCR 模块的官方文档mlx_vlm/models/deepseekocr/README.md展开系统讲解该模型「SAM 编码器 Qwen2 编码器 DeepSeek-V2 语言模型」的混合视觉架构、全套 Prompt 协议文档转 Markdown、通用 OCR、文本定位以及动态分辨率切片的原理与控制方法。读完本文后你可以直接用 CLI 或 Python 在 Mac 上运行 DeepSeek-OCR 完成文档解析与文本框定位并理解cropping、min_patches、max_patches等参数在源码层面如何决定视觉 token 数量。一、模型架构SAM 提细节、Qwen2 提语义、DeepSeek-V2 出文本DeepSeek-OCR 是一个面向文档理解、文本抽取与视觉定位visual grounding的 OCR 模型。其整体结构由文档架构图给出┌─────────────────────────────────────────────────────────────────┐ │ Dynamic Resolution │ ├─────────────────────────────────────────────────────────────────┤ Local Patches │ 768×768 → SAM (12×12×896) → Qwen2 (144×896) → Proj (144×1024) │ (1-6 patches) │ │ ├─────────────────────────────────────────────────────────────────┤ Global View │ 1024×1024 → SAM (16×16×896) → Qwen2 (256×896) → Proj (256×1024)│ (1 image) │ │ └─────────────────────────────────────────────────────────────────┘ ↓ [local_patches, global_view, view_separator] ↓ Language Model (DeepSeek-V2)要点是「局部切片 全局视图」双路特征局部路径Local Patches按动态分辨率策略将大图切成 1~6 个 768×768 的切片每个切片依次经过 SAM 编码器得到 12×12 的 token 网格再送入 Qwen2 视觉编码器最后由投影层Proj映射到 1024 维全局路径Global View整图缩放为 1024×1024 得到 16×16 的 token 网格走同样的 SAM → Qwen2 → Proj 管线用于捕捉版面整体上下文两路特征拼接[local_patches, global_view, view_separator]中间用一个可学习的 view separator token 分隔视图最终作为视觉 token 注入 DeepSeek-V2 语言模型。结合仓库源码可以进一步印证这条调用链Model 类 在初始化时同时构建SAMEncodersam.py、VisionModelvision.py、LanguageModellanguage.py与MlpProjector在 get_input_embeddings 中先对局部切片执行sam_model → vision_model → projector得到local_features再对全局视图执行同样流程得到global_features最后在第 253~256 行以mx.concatenate([local_features, global_features, self.view_separator[None, :]], axis0)完成文档所述的拼接顺序。这与架构图中的[local_patches, global_view, view_separator]完全一致从 config.py 的默认配置看语言侧TextConfig是一份典型的 DeepSeek-V2 MoE 配置64 个路由专家 2 个共享专家、每 token 激活 6 个专家num_experts_per_tok、MLA 压缩秩kv_lora_rank512、30 层L9-L39视觉侧SAMViTConfig定义为 12 层、patch_size 16、window_size 14并在第 2、5、8、11 层使用全局注意力global_attn_indexes(2, 5, 8, 11)L85-L94VisionConfig则是 24 层、hidden 1024 的 Qwen2 风格 ViT 编码器权重加载时Model.sanitize 负责把上游 checkpoint 的键名映射到本仓库结构VisionModel.sanitize 还处理了 PyTorch 与 MLX 的 Conv2d 权重布局差异[C, kH, kW, in]转置以及位置编码的尺寸插值这是该模型能在 MLX 上直接推理的工程细节之一。二、Prompt 协议与特殊 TokenDeepSeek-OCR 的能力由 Prompt 中的特殊 token 控制。完整协议如下1. 文档转 Markdown将文档图像转为结构化 Markdownimage |grounding|Convert the document to markdown.2. 通用 OCR抽取图像中全部文本image |grounding|OCR this image.3. Free OCR不保留版式只抽取文字、不保留版面结构image Free OCR.4. 图表解析解析并描述文档中的图表image Parse the figure.5. 图像描述image Describe this image in detail.6. 文本定位Grounding定位指定文本并返回边界框坐标image Locate |ref|your text here|/ref| in the image.输出格式为|/ref||det|[[x1, y1, x2, y2]]|/det|坐标归一化到 0~1000 区间使用时需按实际图像尺寸换算。全部特殊 token 汇总Token作用imagePrompt 中的图像占位符\|grounding\|启用 grounding / 结构化输出模式\|ref\|...\|/ref\|标记需要在图中定位的文本\|det\|...\|/det\|边界框输出格式\|User\|用户轮次标记\|Assistant\|助手轮次标记源码层面可以确认这些 token 的注入位置DeepseekOCRProcessor 在__init__中依次向 tokenizer 追加 grounding 相关 token|ref|、|/ref|、|det|、|/det|、|grounding|L203-L207与对话 token|User|、|Assistant|。处理器同时要求 Prompt 中image占位符数量与传入图像数量严格一致tokenize_with_images 中的断言这是写自定义 Prompt 时最容易踩的坑。三、CLI 快速使用模型权重使用 Hugging Face 上的mlx-community/DeepSeek-OCR-bf16。以下命令均来自官方文档。文档转 Markdownmlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image document.png \ --prompt |grounding|Convert the document to markdown. \ --max-tokens 2000通用 OCRmlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image receipt.jpg \ --prompt |grounding|OCR this image. \ --max-tokens 1000Free OCRmlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image text_image.png \ --prompt Free OCR. \ --max-tokens 500文本定位mlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image table.jpeg \ --prompt Locate |ref|Total assets|/ref| in the image. \ --max-tokens 100CLI 参数由 mlx_vlm/generate/dispatch.py 统一解析其中与本文动态分辨率控制直接相关的是--processor-kwargs它是一个 JSON 字典最终在第 1511~1512 行被合并进处理器调用参数kwargs.update(args.processor_kwargs)帮助文本本身就给出了 DeepSeek-OCR 的示例--processor-kwargs {cropping: false, max_patches: 3}L566-L572。四、Python 脚本实战1. 基础 OCRfrom mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template # 加载模型 model, processor load(mlx-community/DeepSeek-OCR-bf16) # OCR prompt prompt |grounding|OCR this image. formatted_prompt apply_chat_template(processor, model.config, prompt, num_images1) result generate( modelmodel, processorprocessor, imagedocument.png, promptformatted_prompt, max_tokens1000, temperature0.0, ) print(result.text)注意传入generate的 Prompt 必须先用apply_chat_template套用聊天模板模板负责插入image占位符与轮次标记使占位符数量与图像数量匹配对应上文处理器中的断言检查。2. 文档转 Markdown含 PDF 页from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template model, processor load(mlx-community/DeepSeek-OCR-bf16) prompt |grounding|Convert the document to markdown. formatted_prompt apply_chat_template(processor, model.config, prompt, num_images1) result generate( modelmodel, processorprocessor, imagepaper.pdf, # 也支持 PDF 页面 promptformatted_prompt, max_tokens2000, temperature0.0, ) print(result.text)3. 文本定位与边界框解析from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template model, processor load(mlx-community/DeepSeek-OCR-bf16) text_to_find Total liabilities prompt fLocate |ref|{text_to_find}|/ref| in the image. formatted_prompt apply_chat_template(processor, model.config, prompt, num_images1) result generate( modelmodel, processorprocessor, imagefinancial_table.png, promptformatted_prompt, max_tokens100, temperature0.0, ) # 输出格式: |/ref||det|[[x1, y1, x2, y2]]|/det| import re match re.search(r\[\[(\d),\s*(\d),\s*(\d),\s*(\d)\]\], result.text) if match: x1, y1, x2, y2 map(int, match.groups()) # 坐标归一化到 0-1000 print(fBounding box: ({x1}, {y1}) to ({x2}, {y2}))4. 批量处理from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template from pathlib import Path model, processor load(mlx-community/DeepSeek-OCR-bf16) prompt |grounding|OCR this image. formatted_prompt apply_chat_template(processor, model.config, prompt, num_images1) image_dir Path(documents/) for image_path in image_dir.glob(*.png): result generate( modelmodel, processorprocessor, imagestr(image_path), promptformatted_prompt, max_tokens1000, ) print(f\n--- {image_path.name} ---) print(result.text)处理器本身也支持批量输入DeepseekOCRProcessor.call在text为列表时逐条调用process_one再通过_collate_batch做左填充并把所有局部切片与全局视图堆叠为[patches, global_images]两个张量供模型侧按images_spatial_crop逐图取用。五、动态分辨率切多少块、产生多少视觉 Token这是 DeepSeek-OCR 区别于固定分辨率 VLM 的核心机制。默认配置下全局视图1×1024×1024 → 256 个视觉 token局部切片(1-6)×768×768 → 每块 144 个视觉 token视图分隔符1 个 token工作流程为分析图像宽高比确定最优切片网格局部切片768×768按网格布局捕捉细节全局视图1024×1024捕捉整体上下文特征按[local_patches, global_view, view_separator]拼接。Token 计算规则每个局部切片144 token来自 SAM 12×12 特征全局视图256 token来自 SAM 16×16 特征视图分隔符1 token合计(num_patches × 144) 256 1官方给出的典型宽高比切片示例图像尺寸宽高比网格切片数总 Token800×6004:33×261121600×8003:42×3611211200×4003:13×13689400×12001:31×336891000×10001:11×11401从源码结构看网格选择由 dynamic_preprocess 完成它枚举i×j候选网格用find_closest_aspect_ratio挑选与图像宽高比最接近的一个再把整图按该网格裁切成若干方块若图像两边都不超过 640 像素则直接跳过切片crop_ratio [1, 1]见 tokenize_with_images即退化为「全局视图 单块」的低成本路径。全局视图则由ImageOps.pad填充到base_size默认 1024保持长宽比。需要说明的一个实现细节本仓库 v1 处理器的切片分支以image_size640计算局部查询数num_queries ceil((640 // 16) / 4) 10对应每块 10×10100 个图像 tokentoken 统计也按 100/块累加见 L419-L425而文档架构图与 Token 表描述的是上游 768×768、12×12144 token 的约定。两者差异不影响用法只影响精确的 token 预算核算本文以官方文档表格为准。控制动态分辨率可通过cropping、min_patches、max_patches参数控制切片数量from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template model, processor load(mlx-community/DeepSeek-OCR-bf16) prompt |grounding|OCR this image. formatted_prompt apply_chat_template(processor, model.config, prompt, num_images1) # 默认动态分辨率1-6 个切片 result generate( modelmodel, processorprocessor, imagedocument.png, promptformatted_prompt, max_tokens1000, # croppingTrue, min_patches1, max_patches6默认值 ) # 仅全局视图更快257 token result generate( modelmodel, processorprocessor, imagedocument.png, promptformatted_prompt, max_tokens1000, croppingFalse, ) # 限制切片数平衡速度与细节 result generate( modelmodel, processorprocessor, imagedocument.png, promptformatted_prompt, max_tokens1000, croppingTrue, min_patches1, max_patches3, )各配置的 Token 预算配置切片数TokencroppingFalse0257max_patches11401max_patches31-3401-689max_patches6默认1-6401-1121对应的 CLI 写法经由--processor-kwargs透传# 默认动态分辨率1-6 个切片 mlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image document.png \ --prompt |grounding|OCR this image. \ --max-tokens 1000 # 仅全局视图更快257 token mlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image document.png \ --prompt |grounding|OCR this image. \ --max-tokens 1000 \ --processor-kwargs {cropping: false} # 最多限制为 3 个切片 mlx_vlm.generate \ --model mlx-community/DeepSeek-OCR-bf16 \ --image document.png \ --prompt |grounding|OCR this image. \ --max-tokens 1000 \ --processor-kwargs {cropping: true, max_patches: 3}从源码结构看这些参数最终都落在处理器调用链上DeepseekOCRProcessor.call的签名即包含cropping: bool True、base_size: int 1024、image_size: int 640cropping直接决定tokenize_with_images走「全局视图 动态切片」还是「纯全局视图」分支该分支下图像 token 序列只有 256 个image占位符 1 个分隔符即文档所述 257 token。六、实践建议与已知局限官方文档给出的 Tips最佳 OCR 结果使用|grounding|前缀启用结构化输出模式表格模型会自动以 HTML 表格格式输出温度 0.0OCR 任务推荐temperature0.0保证输出确定性max_tokens文本较多的文档请提高到 2000整页文档定位坐标归一化到 0~1000 区间需按图像尺寸等比换算后使用。已知局限来自文档 Limitations 一节定位|ref|...|/ref|对部分查询可能返回整图坐标最适合文档类图像表单、表格、票据、论文自由问答类 Prompt 的可靠性低于结构化 Prompt所有查询建议以句号结尾有助于提升表现。七、模块文件索引文件说明README.md本文所基于的官方文档config.pyText/Vision/SAM/Projector 四份配置含 DeepSeek-V2 MoE 参数deepseekocr.py顶层 Model双路特征编码、拼接与权重键名映射sam.pySAM ViT 编码器窗口/全局混合注意力vision.pyQwen2 风格视觉编码器与 Conv 权重布局转换language.pyDeepSeek-V2 语言模型MoE MLAprocessing_deepseekocr.py图像处理、动态切片、特殊 token 与批量拼合dispatch.pyCLI 参数解析--processor-kwargs透传入口【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网