8G显存跑OCR?DeepSeek-OCR单卡部署全解
显存约束下的模型选型:从Tiny到Gundam的尺寸阶梯
在单张 8GB 显存显卡上运行视觉语言大模型,过去常被认为是不切实际的幻想。8GB 能干什么?跑一个 7B 级别的对话模型都得精打细算,更别提让模型“看”一张高分辨率截图,再把版面里的每个字、每张表、每个标题都结构化地读出来。视觉 token 一旦膨胀,KV Cache 瞬间吃光显存,OOM 是常态,成功反而是意外。
但一个被反复验证的工程原则是:瓶颈往往不在硬件,而在选型。DeepSeek-OCR 给出了另一种答案——它以“上下文光学压缩”为核心,不去追求把视觉信息平铺直叙地塞进上下文,而是用更经济的方式承载图像特征;同时,官方模型卡列出了从 Tiny 到 Gundam 的多种尺寸配置。这意味着一件事:8GB 能不能跑,不是“能”或“不能”的判断题,而是“选哪一档”的选择题。
这一章,我们就来解决这道选择题。
像选螺丝刀一样选模型尺寸
你不会用大号撬棍去修笔记本电脑的屏幕排线,也不会用精密螺丝刀去拆一堵承重墙。OCR 任务的复杂度跨度极大——识别一行验证码,和解析一篇多栏排版的学术论文,根本是两个量级的挑战。官方模型卡为 DeepSeek-OCR 家族提供了从 Tiny 到 Gundam 的多个尺寸档位,正是为了让你按需取用,而不是永远盯着最大的那个。
作为本文主角的 deepseek-ai/DeepSeek-OCR 是一个 3.3B 参数的多语言视觉语言模型。家族中还有 3.4B 的 DeepSeek-OCR-2、290.9B 的 DeepSeek-V4-Flash 系列,以及 Gundam 级别的 DeepSeek-V4-Pro(1598.8B)。这个跨度本身就是一张地图:3.3B 级别是单卡消费级显卡的现实选择,百亿级往上就开始考验你的钱包和机架了。
这里有两点必须提前说清楚。第一,参数量不是免费午餐,MoE 架构虽然能稀疏激活、降低计算量,但推理时全部专家权重必须完整驻留显存——加载多少,显存就占多少,不存在“只加载部分专家”的捷径。第二,如果你只是想把文档转成 Markdown、抽出干净的文本,3.3B 档位往往已经够用;Gundam 级别的模型属于“想要但未必需要”的范畴,它的存在更多是帮你建立坐标系:你处于这个刻度的哪一格?
显存预算:被低估的“开销三件套”
选定了参数量档位,下一个问题是量化。Q4_K_M 量化把权重压到了非常经济的水平:以 DeepSeek-OCR(3.3B)为例,GGUF 格式的 Q4_K_M 权重仅约 1.9GB。但只看权重文件大小,恰恰是新手最容易踩的坑——显存占用从来不是“权重 + 一点点”那么简单。
一次推理的显存占用包含三部分:模型权重、系统开销、KV Cache。系统开销里是 CUDA 上下文、推理框架的中间缓冲区、算子临时空间,这些不会显示在模型下载页面上,却在每次启动时实实在在地咬掉一块显存。KV Cache 则随上下文长度线性增长,你输入越长的文档,它占得越多。
下表是 DeepSeek-OCR(Q4_K_M)的显存预算,数据基于官方规格:
| 上下文 | KV Cache | 总占用(权重+KV+系统) |
|---|---|---|
| 4K | 0.2GB | 3.6GB |
| 8K | 0.5GB | 3.9GB |
| 16K | 1GB | 4.4GB |
| 32K | 2GB | 5.4GB |
注意这些数字背后的结构:权重 1.9GB,系统开销 1.5GB——后者几乎赶上了半个模型的体量。这就是为什么“我下载的模型才 2GB,怎么显存就爆了”会成为部署区最高频的求助帖。而 8GB 显卡放在这张表里是什么位置?规格明确表示,Q4_K_M 量化后约 1.9GB,8GB 以上显卡均可流畅运行。哪怕你把上下文开到 32K,总占用也不过 5.4GB,对 8GB 显卡来说依然有充足余量。
门槛也是存在的。如果你的显卡只有 4GB 档位,那 4K 上下文(总占用 3.6GB)就已经是极限场景,稍有不慎就会触碰显存天花板。8GB 的意义在于:你不必在上下文长度上委曲求全。
决策思路:三步锁定你的档位
把上面的信息收拢成可操作的决策流程,只需要三步。
第一步,评估任务精度需求。 如果你处理的是清晰的印刷体、单栏文档、标准表格,3.3B 档位完全能胜任“文档转 Markdown”这类常规任务;如果涉及手写体、复杂版面、多语言混排,才需要考虑是否上探更大的档位——但注意,每次上探都要同步审视你的显存预算是否跟得上。
第二步,确认上下文长度。 你的输入是单页告示牌,还是几十页的 PDF?上下文光学压缩让模型能更经济地利用视觉信息,但 KV Cache 的成本依然实打实。4K 上下文的 KV Cache 只有 0.2 GB,32K 则到 2GB——对 8GB 显卡来说差异不大,但如果你只有 4GB 档位,这个差异就会决定部署成败。
第三步,对照显存预算表,选出最优解。 这一步可以直接用下面这个脚本做快速判断:
#!/usr/bin/env bash
# 根据显存预算表快速判断候选档位
# 使用整型比较:总占用(GB)×10,避免浮点运算
GPU_MEM_GB=8 # 你的显卡显存
CTX_K=${1:-4} # 目标上下文长度(K),默认 4K
case $CTX_K in
4) TOTAL_X10=36 ;; # 总占用 3.6GB
8) TOTAL_X10=39 ;; # 总占用 3.9GB
16) TOTAL_X10=44 ;; # 总占用 4.4GB
32) TOTAL_X10=54 ;; # 总占用 3.4 GB
*) echo "不支持的上下文长度"; exit 1 ;;
esac
if (( TOTAL_X10 <= GPU_MEM_GB * 10 )); then
echo "✅ 8GB 显卡可运行 ${CTX_K}K 上下文的 DeepSeek-OCR (3.3B, Q4_K_M)"
else
echo "❌ 8GB 显卡装不下 ${CTX_K}K 上下文,请降低上下文或选择更小档位"
fi
比如你有一张 8GB 显卡,任务是最普通的单页文档转 Markdown,上下文 4K 足够——那么 bash check.sh 4 会告诉你“可运行”,推荐的候选就是 DeepSeek-OCR 的 Q4_K_M 档位。整个过程只需要几秒钟,却能帮你避免数小时的无效部署调试。
还有一个隐藏的坑要在选型阶段就排掉:格式与框架的匹配。GGUF 格式走 llama.cpp/Ollama/MLX,safetensors 走 vLLM/SGLang,二者不互通。你选定了 3.3B 的 Q4_K_M,就意味着默认使用了 GGUF 生态;如果后续想换到 vLLM,需要先做格式转换或改用其他量化方案。选型不只是选模型,也是选后端。
选型定了,显存预算清楚了,下一步就是理解 DeepSeek-OCR 那个最核心的“上下文光学压缩”到底在做什么——这决定了你在配置生成参数,尤其是 max_tokens 和图像分辨率时,每一个选择背后的依据。
架构特性:上下文光学压缩如何降低显存占用
上一章我们确定了模型和后端:3.3B 的 Q4_K_M 量化权重约 1.9GB,走 GGUF 生态。这个数字本身并不震撼——真正让 8GB 显卡”无压力”跑 OCR 的,是 DeepSeek-OCR 在架构层面做的一件关键事:上下文光学压缩。
要理解它,先想一个日常场景:你请一位助理去会议室看一眼白板,然后回来复述。平庸的助理会背下一整块白板的每一处涂改痕迹;聪明的助理只记结论和关键数据。前者费脑费力,后者轻装上阵。视觉语言模型处理图像时也面临同样的选择——是把整张图切成成百上千个 patch 逐一”读”进去,还是先把图像内容提炼成一份精炼的”光学摘要”再进入语言模型?
DeepSeek-OCR 选择了后者。官方描述这个模型”专注于上下文光学压缩”,通俗讲:视觉编码器输出的不是海量图像 patch token,而是一段高度压缩的光学上下文。这段上下文保留了布局、文字、结构这些 OCR 真正需要的信息,同时把无关的像素级细节挡在门外。
这跟你有什么关系?看显存公式就明白了。推理时显存占用主要由三块构成:模型权重、系统开销、KV Cache。权重是固定成本——MoE 模型的专家权重必须完整驻留显存,稀疏激活只降低计算量、不降低显存占用,1.9GB 就是 1.9GB,省不掉。能省的是 KV Cache。而 KV Cache 的大小,直接取决于序列长度。
序列越长,KV Cache 越大。传统视觉模型把 1024×1024 的图像切分成 256 个 patch,每个 patch 映射成多个 token,一张图轻松产生上千个视觉 token;再接上 OCR 输出的几百个文本 token,序列轻轻松松突破 2K。这个数字每涨一倍,KV Cache 就跟着涨一倍。
DeepSeek-OCR 的光学压缩做的就是”减法”:把上千视觉 token 压缩到远小于传统方案的规模。token 少了,序列短了,KV Cache 的显存占用就降下来了。我们用权威数据对照:这份模型在 4K 上下文下,KV Cache 仅约 0.2 GB,总占用约 3.6GB;即便拉到 32K 上下文,KV Cache 也不过约 2GB,总占用约 3.5 GB——一张 8GB 显卡照样从容。作为对比,如果你用非压缩架构的视觉语言模型处理类似任务,图像 token 产生的 KV Cache 开销会高出一个量级。
这就是”小显存能跑”的根本逻辑:压缩发生在输入端,收益体现在显存端。光学压缩减少了进入语言模型的 token 数量,KV Cache 随之缩水,省出的显存被释放给了更长的上下文或更大的 batch。
概念上验证这件事并不难。读论文 arXiv 2510.18234 时,重点看”上下文光学压缩”章节里关于 token 数量的实验对比;实操层面,你可以用 llama.cpp 加载 Q4_K_M 量化后的 GGUF 模型,分别以 4K 和 16K 上下文运行同一个 OCR 任务,观察显存总占用的差异:
# 4K 上下文:总占用约 3.6GB
llama-cli -m DeepSeek-OCR-Q4_K_M.gguf \
-ngl 99 -c 4096 \
--image doc.jpg \
-p "将图片内容转换为 Markdown"
# 16K 上下文:总占用约 4.4GB
# 上下文变长 4 倍,总占用增幅不到 3.4 GB
llama-cli -m DeepSeek-OCR-Q4_K_M.gguf \
-ngl 99 -c 16384 \
--image doc.jpg \
-p "将图片内容转换为 Markdown"
这个对照实验会让你直观感受到 KV Cache 在总占用中的分量——以及光学压缩替你在无形中省下了多少。后面的部署章节我们还会深入调节 -c 和 KV Cache 量化参数的细节,但此刻请先记住这个结论:你能在 8GB 显卡上跑 OCR,不是因为你显卡大,而是因为模型把进嘴的”食物”先嚼碎了。
理解了压缩机制,下一步就是实战:到底怎么配置生成参数,才能让这套压缩机制发挥最大价值?max_tokens 设多少才不至于截断长文档?输入图像的分辨率对 token 消耗有什么影响?下一章,我们进入部署实操。
部署前置:vLLM集成与Flash Attention 2加速组件
上一章得到一个结论:能在 8GB 显卡上跑 OCR,靠的不是显卡大,而是模型把“食物”先嚼碎了。但嚼碎的食物要变成一顿饭,还需要一条可靠的运输线——这条运输线,就是推理引擎。
DeepSeek-OCR(3.3B)的官方模型卡里有两句话,很容易被当成宣传语略过:已集成 vLLM 推理框架、支持 Flash Attention 2 加速。但这两句话恰恰是 8GB 部署的第一块基石。翻译一下:官方已经把运输线铺到了你的灶台门口。你不需要自己写推理引擎,不需要从零维护 KV Cache 管理、采样器、批处理调度——这些重活,官方选好的框架已经替你干完了。
没有这个集成是什么光景?新模型发布后,社区要苦等 vLLM 适配、等 llama.cpp 提交 PR,等不及的人只能 fork 源码自己改。而“已集成”三个字,直接省掉了这段最耗时的等待。
那为什么偏偏是 vLLM?
因为它为 8GB 显存这种受限环境做了三件实事。
第一件是 PagedAttention。KV Cache 是推理时最容易被浪费的显存:它动态增长,天然产生碎片。vLLM 把它按固定大小的“页”管理,像操作系统虚拟内存一样按需分配。DeepSeek-OCR 处理长文档时,视觉 token 会把 KV Cache 撑得很大;页式管理能把碎片吃掉的显存省回来。
第二件是连续批处理。OCR 服务化后,请求长短不一、稀疏到达。vLLM 会把陆续到达的请求动态塞进同一个 batch,先到的先跑,后到的填空,不用傻等“凑满一车再发车”。这是把这套部署变成 API 时实打实的吞吐收益。
第三件是量化选择的自由度。vLLM 对低位量化的支持比较成熟,可以走 safetensors + bitsandbytes(NF4/FP4)动态量化,也可以加载 GGUF 权重文件。但格式配对有清晰边界,请记住:
- GGUF → llama.cpp / Ollama / MLX
- safetensors → vLLM / SGLang
Q4_K_M 属于 GGUF 量化的命名体系,bitsandbytes 的 NF4/FP4 是 transformers 生态的动态量化,二者不互通,不要混用。vLLM 官方文档确实确认支持加载 GGUF 格式,但官方对它的定位是有限支持,不建议当作主路径——如果手里是 GGUF 权重,优先给 llama.cpp 或 Ollama;如果要用 vLLM,就准备好 safetensors 权重。
框架说完,另一半是 Flash Attention 2。
注意力算子慢,瓶颈往往不在算术,而在显存带宽。传统注意力函数的运行方式是:把 Q、K、V 从显存搬到计算单元,算出中间结果写回显存,下一步再读出来——每一步都在“储藏室”和“灶台”之间跑腿。
想象厨师炒菜:酱油在储藏室,醋在储藏室,盐也在储藏室。每放一种调料就跑一趟,大半时间耗在脚上。Flash Attention 2 的做法,是一次性把调料搬到灶台,切好码好,炒菜过程中绝不回储藏室——把长序列注意力切成小块,在片上 SRAM 里就地完成,中间结果不写回显存。
对视觉语言模型来说,图像切出的 token 数量多,注意力序列比纯文本长得多,省下的跑腿尤其可观。模型卡写明“支持 Flash Attention 2 加速”,说明官方已经把这些加速机制内置好了。你不需要手动编译 flash-attn 包,启动 vLLM 后,它会自动选择合适的注意力内核;FA2 可用就优先调度,不可用就自动回退,不影响正确性,只是速度差异。
光说不练不行,现在动手。
先检查环境,三个命令:
nvidia-smi # 检查 GPU 驱动与显存
nvcc --version # 检查 CUDA 工具链版本
python3 --version # 检查 Python 环境
注意 nvidia-smi 显示的是驱动支持的 CUDA 版本,nvcc 显示的是本地开发工具链版本,两者可能不同。差距过大时,vLLM 往往会在 import 阶段报出诡异的链接错误——解决方案是升级驱动,或安装与驱动匹配的 CUDA Toolkit。
然后建一个干净的虚拟环境,安装 vLLM:
python3 -m venv ocr-venv
source ocr-venv/bin/activate
pip install vllm
pip 会自动拉齐 torch、transformers 等依赖。装完做一次最小验证:
python -c "from vllm import LLM; print('vLLM 就绪')"
这一步正常输出,说明驱动、CUDA、PyTorch、vLLM 四者已经自洽——这是后续一切部署的地基。
最后回到显存账本。按权威显存预算,Q4_K_M 权重约 1.9GB,系统开销约 1.5GB,KV Cache 在 4K 上下文约 0.2 GB,三者合计约 3.6GB。系统开销这 1.5GB 已经涵盖了 CUDA context、运行时缓冲区等固定支出,不需要你自行累加。上下文拉到 8K,总占用约 3.9GB;16K 时约 4.4GB。对 8GB 显卡来说,4K 到 8K 是余量很足的工作区间;执意跑 32K 超长文档,就得紧盯显存水位了。
基础设施就位,引擎可以点火了。下一章,我们真正加载 DeepSeek-OCR 模型,把之前一直悬着的 max_tokens、输入图像分辨率、上下文长度这些参数放到真实部署场景里逐一敲定——从“环境准备好了”走到“服务真正跑起来了”。
单机启动:以vLLM加载DeepSeek-OCR服务
环境搭好了,显存预算也摸清了,现在到点火环节。
DeepSeek-OCR 的官方仓库(github.com/deepseek-ai/DeepSeek-OCR)已经完成了 vLLM 集成,README 里给出了标准的启动方式。这意味着我们不需要自己写一套推理脚本,也不用手工拼装多模态前后处理——把 3.3B 的权重交给 vLLM,它就能按标准推理方式把服务拉起来。
打个比方:模型权重是已经腌好的食材,vLLM serve 就是后厨开张。你不需要教厨师怎么切菜,只需要告诉他“客人来了,做菜”。
一条命令,服务起来
在安装了 vLLM 的机器上,执行:
vllm serve deepseek-ai/DeepSeek-OCR \
--max-model-len 8192
第一次运行时,vLLM 会从 Hugging Face 自动下载模型权重,所以请保持网络畅通。下载完成后,权重会缓存在本地,后续启动就快多了。
--max-model-len 8192 是把上下文长度上限设为 8K。这个数字不是拍脑袋定的——上一章的 KV Cache 预算表显示,8K 上下文对应的 KV Cache 约 0.5 GB,在显存规划里属于可控区间。如果你确定自己的任务用不到这么长,可以降到 4096,把显存余量再撑大一点。
如果显存不算宽裕,也可以加上这个参数:
vllm serve deepseek-ai/DeepSeek-OCR \
--max-model-len 8192 \
--gpu-memory-utilization 0.9
--gpu-memory-utilization 0.9 表示 vLLM 最多使用 90% 的显存,剩下的留给系统开销和临时缓冲。注意,它只是一个“安全围栏”,并不能凭空变出显存——权重放不下的时候,调低这个比例只会让服务直接启动失败。
这里要诚实地说一句:vLLM 加载的是官方仓库里的 safetensors 权重,默认按 FP16 精度驻留显存,大约 7GB。加上约 1.5GB 的系统开销和 KV Cache,4 GB 显存起步比较从容,6.7GB 会更宽松。如果你的显卡正好在 8GB 档位,别急——Q4_K_M 量化权重大约只有 1.9GB,那是下一章的主角,这条线我们最后再收。
参数落地:上下文、分辨率、max_tokens
上一章悬着的几个参数,现在可以逐一敲定。
上下文长度已经通过 --max-model-len 8192 定死。vLLM 内部用 PagedAttention 管理 KV Cache,类似于“按桌分配餐具”:客人来一桌,餐具开一桌,而不是开张前就在仓库里囤满几万套。这种按需分配让显存利用率高了不少,但边界仍然存在——序列越长,KV Cache 越大,8K 是个兼顾深度和余量的选择。
输入图像分辨率是 OCR 场景里最容易忽略的变量。图像编码成视觉 token 之后会进入上下文,高分辨率照片产生的视觉 token 数量远多于普通截图,KV Cache 也随之膨胀。实际部署时,不用把图片无限放大,能看清小字即可;如果识别结果缺字,再逐步提高分辨率,而不是一步到位。
max_tokens 是请求参数,不是启动参数。OCR 输出的文本可能很长,尤其是整页文档转 Markdown 的时候;在请求里显式设置一个合理上限,避免输出被截断。
用 HTTP 请求验证服务
服务启动成功后,先确认端点还活着:
curl http://localhost:8000/v1/models
返回的 JSON 里能看到 deepseek-ai/DeepSeek-OCR 这个模型 ID,说明后端已经就绪。现在发一个真正的 OCR 请求:
curl -s http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-ai/DeepSeek-OCR",
"messages": [
{
"role": "user",
"content": [
{"type": "image_url",
"image_url": {"url": "data:image/png;base64,<图片的Base64>"}},
{"type": "text", "text": "把这张图片完整转成 Markdown,保留表格和代码块"}
]
}
],
"max_tokens": 2048
}'
响应里 choices[0].message.content 就是模型输出的 Markdown 文本。如果你不想手工拼 Base64,用 Python 会更顺手:
import base64
import requests
with open("invoice.png", "rb") as f:
b64 = base64.b64encode(f.read).decode
resp = requests.post(
"http://localhost:8000/v1/chat/completions",
json={
"model": "deepseek-ai/DeepSeek-OCR",
"messages": [{
"role": "user",
"content": [
{"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{b64}"}},
{"type": "text", "text": "提取这份发票的全部字段"},
],
}],
"max_tokens": 2048,
},
)
print(resp.json["choices"][0]["message"]["content"])
到这里,你已经走通了“下载权重 → 启动服务 → HTTP 调用 → 拿到 Markdown”的完整闭环。vLLM 提供的是 OpenAI 兼容接口,意味着现有调用 OpenAI 的客户端代码,只需要改一下 base_url 就能接过来。
但有一个事实没法回避:这一章的 vLLM 路径跑的是 FP16 权重,对显存的胃口不算小。如果你手里的卡恰好是 8GB,又想把 DeepSeek-OCR 塞进去流畅跑起来,那就需要换个思路——把权重从约 7GB 的 FP16 换成约 1.9GB 的 Q4_K_M 量化版,并把它交给 GGUF 生态的工具链。下一章,我们就来解决这个问题。
任务实测:Free OCR 与文档转 Markdown 两种调用方式
模型部署上线,只是拿到了“能跑”的资格。真正决定它能不能成为生产力工具的,是面对具体任务时,模型的输出是否稳定、格式是否顺手。DeepSeek-OCR 在官方模型卡里,将 Free OCR 与文档转 Markdown 列为关键功能——这两条路,恰好覆盖了现实中绝大多数 OCR 场景。
先做一个类比。Free OCR 像速记员:眼睛扫过画面,把文字一行行念出来,不做排版干预,你要的是“内容本身”。文档转 Markdown 更像排版编辑:读完一页文档,还要按标题层级分章、把表格还原成表格、把列表还原成列表,你要的是“结构完整、可直接复用的文稿”。同一个模型,两种工况,起点相同,出口不同。
两种模式的分工
无论哪种模式,模型都会先解读图像内容,差异集中在输出形态与提示词设计上:
- Free OCR 输出去格式化的纯文本,适合路牌、聊天截图、票据这类“文字散落在画面中”的输入。下游等着做检索、翻译、关键词抽取。
- 文档转 Markdown 输出带
#、-、|等标记的结构化文本,适合论文页、合同页、报表页。下游可以直接落盘为.md文件,进入文档库或知识库。
所以两者不是模型能力上的差别,而是使用方式上的分流。请求的 prompt 一换,输出的形态就跟着换了。
准备两张测试图
本次实测需要两张图片,一张自由文本图,一张排版文档图:
- 自由文本图:截取一段聊天记录,或拍一张路牌照片,画面里有文字即可;
- 文档图:找一个含标题、正文、表格的 PDF 页面,导出成 PNG。
图片不需要过大,能看清文字即可——过大的图只会让请求体膨胀,对识别增益有限。
用 llama.cpp 起服务
上一章我们已经把权重换成了约 1.9GB 的 Q4_K_M GGUF,并用 llama.cpp 验证了加载。这里在此基础上启动本地服务:
# 注意 mmproj 参数取决于模型的 GGUF 打包方式:
# 若视觉权重与语言模型拆分为两个文件,必须用 --mmproj 指定视觉投影文件;
# 若为单文件打包(已含视觉权重),则省略该参数。
./llama-server \
-m ./models/DeepSeek-OCR-Q4_K_M.gguf \
--mmproj ./models/mmproj-f16.gguf \
-ngl 99 \
--ctx-size 4096 \
--port 8080
-ngl 99 告诉 llama.cpp 把尽可能多的层放到 GPU 上;--ctx-size 4096 对应 4K 上下文。这个配置下,模型总占用约 3.6GB,一张 8GB 显卡装得下,还能留给后续批量任务足够余量;如果用的是 4.6 GB 卡,4K 上下文运行后剩余 4.4 GB,跑长文档也基本没有压力。
Free OCR:把图片文字“念”出来
服务起来后,先用 Free OCR 模式发请求。把测试图片转成 base64,随请求一起交给 /v1/chat/completions:
IMAGE_BASE64=$(base64 -w0 ./test_free_ocr.png)
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d "{
\"model\": \"deepseek-ocr\",
\"messages\": [{
\"role\": \"user\",
\"content\": [
{\"type\": \"image_url\", \"image_url\": {\"url\": \"data:image/png;base64,${IMAGE_BASE64}\"}},
{\"type\": \"text\", \"text\": \"请识别图片中的所有文字。\"}
]
}]
}"
回包里的 choices[0].message.content 就是模型念出的文字。注意它的形态:没有标题标记,没有列表符号,干干净净的一段连续文本。
这类输出的下游接法很直接——把这段文本交给全文检索引擎、翻译接口或 RAG 管线。Free OCR 的定位,就是低摩擦地把图变成字。
文档转 Markdown:把版面还原成结构
接下来换一张文档图,核心差异在 prompt:明确要求“转成 Markdown,保留结构”。
IMAGE_BASE64=$(base64 -w0 ./test_document.png)
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d "{
\"model\": \"deepseek-ocr\",
\"messages\": [{
\"role\": \"user\",
\"content\": [
{\"type\": \"image_url\", \"image_url\": {\"url\": \"data:image/png;base64,${IMAGE_BASE64}\"}},
{\"type\": \"text\", \"text\": \"请将图片中的文档内容转换为 Markdown 格式,保留标题层级、列表与表格结构。\"}
]
}]
}"
这次的 content 字段会带着明显的结构痕迹:标题前有 #,列表项前有 -,表格被还原为 Markdown 语法。直接把这段输出写入文件,就是一份可编辑、可入知识库的 .md 文档:
curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d "{...请求体同上,prompt 换成文档转 Markdown...}" \
| jq -r '.choices[0].message.content' > output.md
需要注意的是,文档转 Markdown 模式下,模型还原的是版面结构。如果原文档表格线条清晰、分辨率足够,表格还原度通常不错;如果表格线太淡,就需要在图像预处理阶段做一次对比度增强。
按场景选模式,也可以双模式串联
两种模式的取舍,一张表能说清:
| 维度 | Free OCR | 文档转 Markdown |
|---|---|---|
| 最佳输入 | 自然场景文字、截图 | 排版严谨的文档页 |
| 输出形态 | 纯文本 | 带结构的 Markdown |
| 典型下游 | 全文检索、翻译、摘要 | 文档入库、知识库构建 |
| Prompt 要点 | 指令简短,只要“识别文字” | 必须声明输出格式 |
但实际项目中未必是二选一。比如一个合同库场景,可以先用 Free OCR 把全库页面快速过一遍,抽出纯文本建索引;用户命中某一页后,再调文档转 Markdown,把那页重新渲染成可编辑文稿。同一份权重,两种模式各司其职——一个负责“找得到”,一个负责“用得好”。
给读者的检查清单也很直接:准备两张图、跑两个 curl、对比两次 content 字段的形态差异。如果 Markdown 模式下表格经常乱掉,先检查原图表格清晰度;如果 Free OCR 模式偶发漏字,优先确认上下文是否给足——4K 上下文总占用约 3.6GB,12GB 显卡剩余 8.4GB;即便拉到 32K 上下文,总占用也只要 5.4GB,12GB 显卡仍剩余 6.6GB。显存通常不是瓶颈,真正值得调的是图片质量和 prompt 表述。
DeepSeek-OCR 通过上下文光学压缩和多尺寸配置,把视觉语言模型的部署门槛拉到了消费级显卡能接住的水平;再加上 vLLM 与 Flash Attention 2 的支持,单卡部署不再是实验室专利。如果你正为显存而苦恼,不妨沿着本文的路径——选型、量化、启动、实测——让这个 3.3B 的模型真正跑在自己手里的硬件上,而不是止步于别人的评测报告。更多细节,请以官方模型卡与 GitHub 仓库为准。
