A reproduction of the Deepseek-OCR model including training
Python
211
10 commits
updated Nov 21, 2025
A reproduction of the Deepseek-OCR model based on the VILA codebase. DeepOCR explores context optical compression through vision-text token compression, achieving competitive OCR performance with minimal vision tokens.
Deepseek-OCR: Contexts Optical Compression
arXiv Paper
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DeepOCR β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β DeepEncoder (380M) β β
β β β β
β β ββββββββββββββββ βββββββββββ ββββββββββ β β
β β β SAM-base βββββ Conv ββββ CLIP β β β
β β β (80M) β β 16Γ β β (300M) β β β
β β β Window Attn β βCompress β β Global β β β
β β ββββββββββββββββ βββββββββββ ββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β Linear Projector (2048 β LLM dim) β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β Qwen 2-7B β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Clone the repository
git clone https://github.com/pkulium/DeepOCR
cd DeepOCR
# Set up environment
./environment_setup.sh deeporc
conda activate deeporc
# Install additional dependencies for OCR
pip install safetensors einops easydict mupdf
Download required checkpoints:
# SAM and CLIP checkpoints (combined in one file)
# Place at: checkpoints/sam_clip_ckpt/model_cache/model-00001-of-000001.safetensors
huggingface-cli download pkulium/sam_clip_ckpt
# Base LLM (Qwen2-7B-Instruct)
huggingface-cli download Efficient-Large-Model/Qwen2-VL-7B-Instruct
Trains the vision-to-text projector while freezing vision encoder and LLM:
bash scripts/NVILA-Lite/align_ocr.sh \
Efficient-Large-Model/Qwen2-VL-7B-Instruct \
llava_15_mix \
runs/train/ocr-qwen2-vl-8b-align
Key parameters:
Full model training with OCR data:
bash scripts/NVILA-Lite/pretrain_ocr.sh \
runs/train/ocr-qwen2-vl-8b-align/model \
olmOCR-mix-pretrain \
runs/train/ocr-qwen2-vl-8b-pretrain
Key parameters:
The model requires three types of data across two training stages:
Stage 1: Initialize Projector
Stage 2: Model Pretrain
allenai/olmOCR-mix-1025bash scripts/eval/all.sh
python llava/eval/omini_doc_bench.py \
--model-path <model_path> \
--input-folder <input_images> \
--output-folder <output_markdown> \
--text "Free OCR."
Available prompts:
"<image>\nFree OCR." - Plain text extraction"<image>\n<|grounding|>Convert the document to markdown." - With layout"<image>\nParse the figure." - Chart/figure parsing"<image>\nDescribe this image in detail." - General descriptionvila-eval \
--model-name NVILA-8B-OCR \
--model-path runs/train/ocr-qwen2-vl-8b-pretrain/model \
--conv-mode auto \
--tags-include local
llava/model/multimodal_encoder/sam_clip/)Core Components:
deepencoder.py: Implements SAM and CLIP vision towers
build_sam_vit_b(): SAM-base with 768-dim, 12 layers, window attentionbuild_clip_l(): CLIP-large with 1024-dim, 24 layers, global attentionMlpProjector: Token compression modulemodeling_sam_clip.py: Main SAMCLIP wrapper
[CLIP_cls, CLIP_patches, SAM_features]Token Flow:
Input (1024Γ1024) β SAM (4096 tokens) β Conv16Γ (256 tokens)
β
CLIP (256 tokens) β Concat β 2048-dim features
image_process.py)# Dynamic resolution preprocessing
def dynamic_preprocess(image, min_num=2, max_num=6, image_size=640):
"""
Splits image into tiles based on aspect ratio
Returns: List of tile images + crop ratio
"""
Processing modes:
base_projector.py)class MultimodalProjector:
def __init__(self):
self.layers = nn.Linear(2048, llm_hidden_size)
self.image_newline = nn.Parameter(...) # Token separator
self.view_seperator = nn.Parameter(...) # View separator
Token formatting:
[Local_Tiles] + [Image_Newline] + [Global_View] + [View_Separator]
config.py)Key settings:
BASE_SIZE = 1024 # Global view size
IMAGE_SIZE = 640 # Tile size
CROP_MODE = True # Enable dynamic tiling
MIN_CROPS = 2 # Min tiles per dimension
MAX_CROPS = 6 # Max tiles per dimension
MAX_CONCURRENCY = 100 # Batch processing limit
First, download the model from Hugging Face:
huggingface-cli download pkulium/easy_deepocr --local-dir ./easy_deepocr_sam_clip
Then use the model:
vila-infer \
--model-path ./easy_deepocr_sam_clip \
--conv-mode auto \
--text "Free OCR." \
--media "./assets/test.png"
import llava
# Load model
model = llava.load("./easy_deepocr_sam_clip")
prompt = [
Image("document.pdf"),
"<|grounding|>Convert the document to markdown."
]
response = model.generate_content(prompt)
prompt = [Image("chart.png"), "Parse the figure."]
response = model.generate_content(prompt)
# Returns: HTML table or structured data


CUDA OOM during training
# Reduce batch size or enable gradient checkpointing
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 16 \
--gradient_checkpointing True
NCCL timeout in multi-GPU training
export NCCL_TIMEOUT=1800
export NCCL_IB_TIMEOUT=22
Position_ids buffer device mismatch
deepencoder.py by reinitializing position_ids after checkpoint loadingDistributed training hangs
modeling_sam_clip.py fix)This reproduction is based on the VILA codebase and has some adaptations:
If you find our work helpful, please consider citing it:
@misc{DeepOCR,
title = {DeepOCR},
year = {2025},
howpublished = {\url{https://github.com/pkulium/DeepOCR}},
note = {Accessed: 2025-11-04}
}
Note: This is a research reproduction. For production deployment, consider the official Deepseek-OCR implementation.
10 commits
Python
96.4%
Shell
1.9%
Cuda
1.5%
A reproduction of the Deepseek-OCR model including training
Python
211
10 commits
updated Nov 21, 2025
A reproduction of the Deepseek-OCR model based on the VILA codebase. DeepOCR explores context optical compression through vision-text token compression, achieving competitive OCR performance with minimal vision tokens.
Deepseek-OCR: Contexts Optical Compression
arXiv Paper
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DeepOCR β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β DeepEncoder (380M) β β
β β β β
β β ββββββββββββββββ βββββββββββ ββββββββββ β β
β β β SAM-base βββββ Conv ββββ CLIP β β β
β β β (80M) β β 16Γ β β (300M) β β β
β β β Window Attn β βCompress β β Global β β β
β β ββββββββββββββββ βββββββββββ ββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β Linear Projector (2048 β LLM dim) β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β Qwen 2-7B β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Clone the repository
git clone https://github.com/pkulium/DeepOCR
cd DeepOCR
# Set up environment
./environment_setup.sh deeporc
conda activate deeporc
# Install additional dependencies for OCR
pip install safetensors einops easydict mupdf
Download required checkpoints:
# SAM and CLIP checkpoints (combined in one file)
# Place at: checkpoints/sam_clip_ckpt/model_cache/model-00001-of-000001.safetensors
huggingface-cli download pkulium/sam_clip_ckpt
# Base LLM (Qwen2-7B-Instruct)
huggingface-cli download Efficient-Large-Model/Qwen2-VL-7B-Instruct
Trains the vision-to-text projector while freezing vision encoder and LLM:
bash scripts/NVILA-Lite/align_ocr.sh \
Efficient-Large-Model/Qwen2-VL-7B-Instruct \
llava_15_mix \
runs/train/ocr-qwen2-vl-8b-align
Key parameters:
Full model training with OCR data:
bash scripts/NVILA-Lite/pretrain_ocr.sh \
runs/train/ocr-qwen2-vl-8b-align/model \
olmOCR-mix-pretrain \
runs/train/ocr-qwen2-vl-8b-pretrain
Key parameters:
The model requires three types of data across two training stages:
Stage 1: Initialize Projector
Stage 2: Model Pretrain
allenai/olmOCR-mix-1025bash scripts/eval/all.sh
python llava/eval/omini_doc_bench.py \
--model-path <model_path> \
--input-folder <input_images> \
--output-folder <output_markdown> \
--text "Free OCR."
Available prompts:
"<image>\nFree OCR." - Plain text extraction"<image>\n<|grounding|>Convert the document to markdown." - With layout"<image>\nParse the figure." - Chart/figure parsing"<image>\nDescribe this image in detail." - General descriptionvila-eval \
--model-name NVILA-8B-OCR \
--model-path runs/train/ocr-qwen2-vl-8b-pretrain/model \
--conv-mode auto \
--tags-include local
llava/model/multimodal_encoder/sam_clip/)Core Components:
deepencoder.py: Implements SAM and CLIP vision towers
build_sam_vit_b(): SAM-base with 768-dim, 12 layers, window attentionbuild_clip_l(): CLIP-large with 1024-dim, 24 layers, global attentionMlpProjector: Token compression modulemodeling_sam_clip.py: Main SAMCLIP wrapper
[CLIP_cls, CLIP_patches, SAM_features]Token Flow:
Input (1024Γ1024) β SAM (4096 tokens) β Conv16Γ (256 tokens)
β
CLIP (256 tokens) β Concat β 2048-dim features
image_process.py)# Dynamic resolution preprocessing
def dynamic_preprocess(image, min_num=2, max_num=6, image_size=640):
"""
Splits image into tiles based on aspect ratio
Returns: List of tile images + crop ratio
"""
Processing modes:
base_projector.py)class MultimodalProjector:
def __init__(self):
self.layers = nn.Linear(2048, llm_hidden_size)
self.image_newline = nn.Parameter(...) # Token separator
self.view_seperator = nn.Parameter(...) # View separator
Token formatting:
[Local_Tiles] + [Image_Newline] + [Global_View] + [View_Separator]
config.py)Key settings:
BASE_SIZE = 1024 # Global view size
IMAGE_SIZE = 640 # Tile size
CROP_MODE = True # Enable dynamic tiling
MIN_CROPS = 2 # Min tiles per dimension
MAX_CROPS = 6 # Max tiles per dimension
MAX_CONCURRENCY = 100 # Batch processing limit
First, download the model from Hugging Face:
huggingface-cli download pkulium/easy_deepocr --local-dir ./easy_deepocr_sam_clip
Then use the model:
vila-infer \
--model-path ./easy_deepocr_sam_clip \
--conv-mode auto \
--text "Free OCR." \
--media "./assets/test.png"
import llava
# Load model
model = llava.load("./easy_deepocr_sam_clip")
prompt = [
Image("document.pdf"),
"<|grounding|>Convert the document to markdown."
]
response = model.generate_content(prompt)
prompt = [Image("chart.png"), "Parse the figure."]
response = model.generate_content(prompt)
# Returns: HTML table or structured data


CUDA OOM during training
# Reduce batch size or enable gradient checkpointing
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 16 \
--gradient_checkpointing True
NCCL timeout in multi-GPU training
export NCCL_TIMEOUT=1800
export NCCL_IB_TIMEOUT=22
Position_ids buffer device mismatch
deepencoder.py by reinitializing position_ids after checkpoint loadingDistributed training hangs
modeling_sam_clip.py fix)This reproduction is based on the VILA codebase and has some adaptations:
If you find our work helpful, please consider citing it:
@misc{DeepOCR,
title = {DeepOCR},
year = {2025},
howpublished = {\url{https://github.com/pkulium/DeepOCR}},
note = {Accessed: 2025-11-04}
}
Note: This is a research reproduction. For production deployment, consider the official Deepseek-OCR implementation.
10 commits
Python
96.4%
Shell
1.9%
Cuda
1.5%