本文记录了使用 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-utilization | 0.35 | GPU 显存使用比例,RTX 4090 设置 0.35 约使用 8.4GB |
--max-model-len | 2048 | 最大上下文长度,根据显存调整 |
--max-num-seqs | 64 | 最大并发序列数 |
--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 的精度、速度和显存占用:
| 模式 | 分辨率 | 适用场景 | 特点 |
|---|---|---|---|
| tiny | 512x512 | 简单文档、快速预览 | 最快、显存最小、细节丢失多 |
| small | 640x640 | 一般文档 | 速度和精度平衡 |
| base | 1024x1024 | 标准文档 | 默认模式,精度较好 |
| large | 1280x1280 | 复杂表格、小字体 | 精度最高,显存占用大 |
| 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 模式的工作方式比较特殊:
- 把大图裁剪成多个 640x640 的小块(patches)—— 保留局部细节
- 再生成一个 1024x1024 的全局缩略图 —— 保留整体布局
- 组合这些 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 = 6gundam 模式的实现原理
官方脚本在 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 兼容 API | Python 脚本 |
| 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 接口。调用时需要注意以下几点:
- 图片位置:图片必须放在
content数组的第一个位置 - 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 服务。主要优势包括:
- 部署简单:vLLM 原生支持,无需额外适配
- API 兼容:OpenAI 兼容接口,易于集成到现有系统
- 性能优秀:支持批量处理和流式输出
- 输出结构化:直接输出 Markdown 格式,便于后续处理
对于有文档数字化需求的场景,DeepSeek-OCR 是一个值得考虑的选择。RTX 4090 单卡即可流畅运行,显存占用可控,适合中小规模的本地部署。