青蛙小白
博客 / 2025/10

DeepSeek-OCR 本地部署:使用 vLLM 构建 OCR API 服务

2025/10/26 · — 字 · 阅读约 — 分钟 ·
目录

本文记录了使用 vLLM 部署 DeepSeek-OCR 的完整过程。

DeepSeek-OCR 简介

DeepSeek-OCR 是 DeepSeek 于 2025 年 10 月发布的开源 OCR 模型,主要特点包括:

  • 多分辨率支持:从 512x512 (Tiny) 到 1280x1280 (Large) 多种分辨率模式
  • 动态分辨率:支持 “Gundam” 模式,组合多个 640x640 patches 和 1024x1024 patch
  • Grounding 能力:可以定位和引用图像中的特定元素
  • 文档转换:将文档转换为 Markdown 格式,保留布局结构
  • 高性能:在 A100-40G GPU 上可达约 2500 tokens/秒

模型已在 2025 年 10 月 23 日被 vLLM 官方支持,可以直接使用 vLLM 部署为 OpenAI 兼容的 API 服务。

环境准备

硬件环境

  • GPU: NVIDIA RTX 4090 (24GB 显存)
  • 操作系统: Ubuntu 24.04 LTS
  • CUDA: 12.6

创建 Python 虚拟环境

推荐使用 uv 来管理 Python 环境:

# 创建项目目录
mkdir -p ~/projects/deepseek-ocr
cd ~/projects/deepseek-ocr

# 使用 uv 创建虚拟环境
uv venv --python 3.12

# 激活虚拟环境
source .venv/bin/activate

# 安装 vLLM
uv pip install vllm

注意:Python 虚拟环境包含硬编码的绝对路径,创建后不要移动目录位置。如果必须移动,需要删除并重新创建虚拟环境。

下载模型

由于 Hugging Face 在国内访问不稳定,推荐使用 hf-mirror.com 镜像加速下载:

# 设置镜像环境变量
export HF_ENDPOINT=https://hf-mirror.com

# 使用 hf 命令下载模型(推荐)
hf download deepseek-ai/DeepSeek-OCR --local-dir ./DeepSeek-OCR

模型文件约6GB,下载需要一些时间。下载完成后,模型文件位于 ~/projects/deepseek-ocr/DeepSeek-OCR 目录。

部署 vLLM 服务

启动命令

cd ~/projects/deepseek-ocr
source .venv/bin/activate

vllm serve ./DeepSeek-OCR \
    --served-model-name deepseek-ocr \
    --host 0.0.0.0 \
    --port 8100 \
    --trust-remote-code \
    --gpu-memory-utilization 0.35 \
    --max-model-len 2048 \
    --max-num-seqs 64 \
    --api-key <YOUR_API_KEY>

关键参数说明:

参数说明
--gpu-memory-utilization0.35GPU 显存使用比例,RTX 4090 设置 0.35 约使用 8.4GB
--max-model-len2048最大上下文长度,根据显存调整
--max-num-seqs64最大并发序列数
--api-key-API 认证密钥,客户端需在请求头中携带
--served-model-name-指定对外暴露的模型名称,客户端调用时使用此名称
--trust-remote-code-允许执行模型仓库中的自定义代码,某些模型需要此参数

配置 systemd 服务

为了实现开机自启和服务管理,创建 systemd 服务文件:

sudo vim /etc/systemd/system/deepseek-ocr.service

内容如下:

[Unit]
Description=DeepSeek-OCR vLLM Service
After=network.target

[Service]
Type=simple
User=your_username
WorkingDirectory=/home/$USER/projects/deepseek-ocr
Environment="PATH=/home/$USER/projects/deepseek-ocr/.venv/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/home/$USER/projects/deepseek-ocr/.venv/bin/vllm serve /home/$USER/projects/deepseek-ocr/DeepSeek-OCR \
    --served-model-name deepseek-ocr \
    --host 0.0.0.0 \
    --port 8100 \
    --trust-remote-code \
    --gpu-memory-utilization 0.35 \
    --max-model-len 2048 \
    --max-num-seqs 64 \
    --api-key <YOUR_API_KEY>
Restart=on-failure
RestartSec=10
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

注意:请将 your_username$USER 替换为实际的系统用户名。

启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable deepseek-ocr
sudo systemctl start deepseek-ocr

# 查看服务状态
sudo systemctl status deepseek-ocr

# 查看日志
journalctl -u deepseek-ocr -f

分辨率模式配置

DeepSeek-OCR 支持多种分辨率模式,用于控制图像输入分辨率,影响 OCR 的精度、速度和显存占用:

模式分辨率适用场景特点
tiny512x512简单文档、快速预览最快、显存最小、细节丢失多
small640x640一般文档速度和精度平衡
base1024x1024标准文档默认模式,精度较好
large1280x1280复杂表格、小字体精度最高,显存占用大
gundam动态裁剪大尺寸图片、高分辨率扫描件组合多个 patches,保留全局+局部细节

vLLM serve 部署的默认模式

vLLM 通过读取模型目录中的 processor_config.json 来处理图像。查看该文件可以看到默认配置:

{
  "candidate_resolutions": [[1024, 1024]],
  "patch_size": 16,
  "downsample_ratio": 4,
  ...
}

这意味着 vLLM 部署时默认使用 base 模式(1024x1024)

切换分辨率模式

如果需要使用其他分辨率,可以直接修改本地模型目录中的 processor_config.json

# 编辑配置文件
vim ~/projects/deepseek-ocr/DeepSeek-OCR/processor_config.json

根据需要修改 candidate_resolutions 的值:

目标模式修改值
tiny"candidate_resolutions": [[512, 512]]
small"candidate_resolutions": [[640, 640]]
base"candidate_resolutions": [[1024, 1024]]
large"candidate_resolutions": [[1280, 1280]]

修改后重启 vLLM 服务使配置生效:

sudo systemctl restart deepseek-ocr

注意:分辨率越高,显存占用越大。如果切换到 large 模式,可能需要相应调整 --gpu-memory-utilization 参数。

gundam 模式与两种 vLLM 部署方式

gundam 模式的工作方式比较特殊:

  1. 把大图裁剪成多个 640x640 的小块(patches)—— 保留局部细节
  2. 再生成一个 1024x1024 的全局缩略图 —— 保留整体布局
  3. 组合这些 patches 一起送入模型

这种动态裁剪需要自定义的图像预处理逻辑。这里需要区分两种 vLLM 部署方式:

方式一:vllm serve(本文采用的部署方式)

vllm serve ./DeepSeek-OCR --served-model-name deepseek-ocr ...

这种方式部署的是 OpenAI 兼容的 API 服务,图像预处理由 vLLM 内部根据 processor_config.json 自动完成,不支持 gundam 模式。只能通过修改配置文件切换 tiny/small/base/large 模式。

方式二:官方推理脚本(支持 gundam)

官方仓库提供的 run_dpsk_ocr_image.py 虽然底层也使用 vLLM(AsyncLLMEngine),但它自己实现了 DeepseekOCRProcessor 来处理图像预处理,包括裁剪逻辑。

查看其 config.py 可以看到默认就是 gundam 模式:

# Gundam 模式配置
BASE_SIZE = 1024
IMAGE_SIZE = 640
CROP_MODE = True  # 启用裁剪
MIN_CROPS = 2
MAX_CROPS = 6

gundam 模式的实现原理

官方脚本在 image_process.py 中实现了图像预处理逻辑。核心是在 Python 层面完成裁剪,然后再把处理后的图像块传给 vLLM 引擎。

首先,在 tokenize_with_images 方法中判断是否需要裁剪:

if image.size[0] <= 640 and image.size[1] <= 640:
    crop_ratio = [1, 1]  # 小图不裁剪
else:
    if cropping:  # CROP_MODE = True 时启用
        images_crop_raw, crop_ratio = dynamic_preprocess(
            image, image_size=IMAGE_SIZE)  # 本地裁剪
    else:
        crop_ratio = [1, 1]

然后,dynamic_preprocess 函数负责实际的裁剪工作:

def dynamic_preprocess(image, min_num=MIN_CROPS, max_num=MAX_CROPS,
                       image_size=640, use_thumbnail=False):
    orig_width, orig_height = image.size
    aspect_ratio = orig_width / orig_height

    # 根据图片宽高比,计算最优网格布局(如 2x3 = 6 个 tiles)
    target_ratios = set(
        (i, j) for n in range(min_num, max_num + 1)
        for i in range(1, n + 1) for j in range(1, n + 1)
        if i * j <= max_num and i * j >= min_num)

    # 调整大小后裁剪成多个 640x640 的块
    resized_img = image.resize((target_width, target_height))
    for i in range(blocks):
        box = (...)  # 计算每个 tile 的坐标
        split_img = resized_img.crop(box)
        processed_images.append(split_img)

    return processed_images, target_aspect_ratio

整个流程可以概括为:

原始大图 → Python 本地裁剪成多个 640x640 tiles → 每个 tile 单独编码 → 传给 vLLM 引擎

这就是为什么 vllm serve 命令不支持 gundam 模式——它缺少这层 Python 预处理逻辑,图像直接由 vLLM 内部按 processor_config.json 处理。

两种方式对比:

特性vllm serve 命令官方推理脚本
部署形式OpenAI 兼容 APIPython 脚本
gundam 模式不支持支持(默认)
tiny/small/base/large修改配置文件修改 config.py
易用性高(标准 API)中(需要改代码集成)
适用场景通用 API 服务高分辨率文档处理

如果文档分辨率较高、需要识别小字体细节,建议使用官方推理脚本的 gundam 模式。如果追求部署简单和 API 兼容性,使用本文的 vllm serve 方式配合 base 或 large 模式即可满足大部分需求。

推荐配置:large 模式 + 应用端预处理

对于 vllm serve 部署方式,推荐将 processor_config.json 修改为 large 模式(1280x1280),同时在应用端对超大图片进行等比缩放,避免超出模型处理能力。

# 修改模型配置为 large 模式
vim ~/projects/deepseek-ocr/DeepSeek-OCR/processor_config.json
# 将 "candidate_resolutions": [[1024, 1024]] 改为 "candidate_resolutions": [[1280, 1280]]

# 重启服务
sudo systemctl restart deepseek-ocr

调用 OCR API

DeepSeek-OCR 通过 vLLM 提供 OpenAI 兼容的 API 接口。调用时需要注意以下几点:

  1. 图片位置:图片必须放在 content 数组的第一个位置
  2. Prompt 格式:使用特定的 grounding prompt 格式

Python 调用示例

import base64
from openai import OpenAI

# 初始化客户端
client = OpenAI(
    api_key="<YOUR_API_KEY>",  # 使用启动服务时设置的 API key
    base_url="http://localhost:8100/v1"
)

def ocr_image(image_path: str) -> str:
    """对图片进行 OCR 识别,返回 Markdown 格式文本"""

    # 读取并编码图片
    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode("utf-8")

    # 根据文件扩展名确定 MIME 类型
    ext = image_path.lower().split(".")[-1]
    mime_type = {
        "png": "image/png",
        "jpg": "image/jpeg",
        "jpeg": "image/jpeg",
        "gif": "image/gif",
        "webp": "image/webp"
    }.get(ext, "image/png")

    # 调用 API
    response = client.chat.completions.create(
        model="deepseek-ocr",
        messages=[
            {
                "role": "user",
                "content": [
                    # 图片必须放在第一个位置
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:{mime_type};base64,{image_data}"
                        }
                    },
                    # 使用 grounding prompt
                    {
                        "type": "text",
                        "text": "<|grounding|>Convert the document to markdown."
                    }
                ]
            }
        ],
        max_tokens=2048
    )

    return response.choices[0].message.content

# 使用示例
if __name__ == "__main__":
    result = ocr_image("./test_document.png")
    print(result)

cURL 调用示例

# 将图片转换为 base64
IMAGE_BASE64=$(base64 -w 0 test_document.png)

curl -X POST http://localhost:8100/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -d '{
    "model": "deepseek-ocr",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "image_url",
            "image_url": {
              "url": "data:image/png;base64,'$IMAGE_BASE64'"
            }
          },
          {
            "type": "text",
            "text": "<|grounding|>Convert the document to markdown."
          }
        ]
      }
    ],
    "max_tokens": 2048
  }'

总结

DeepSeek-OCR 是一个功能强大的开源 OCR 模型,通过 vLLM 部署可以快速构建本地 OCR API 服务。主要优势包括:

  1. 部署简单:vLLM 原生支持,无需额外适配
  2. API 兼容:OpenAI 兼容接口,易于集成到现有系统
  3. 性能优秀:支持批量处理和流式输出
  4. 输出结构化:直接输出 Markdown 格式,便于后续处理

对于有文档数字化需求的场景,DeepSeek-OCR 是一个值得考虑的选择。RTX 4090 单卡即可流畅运行,显存占用可控,适合中小规模的本地部署。

参考资料

TAGS # vllm
评论