DeepSeek-OCR 2の使い方

Ashley Innocent

Ashley Innocent

27 1月 2026

DeepSeek-OCR 2の使い方

ドキュメント処理は長らくAIの最も実用的なアプリケーションの一つでしたが、ほとんどのOCRソリューションは精度と効率の間で不快なトレードオフを強いられています。Tesseractのような従来のシステムは、広範な前処理を必要とします。クラウドAPIはページごとに課金され、レイテンシが追加されます。現代の視覚言語モデルでさえ、高解像度のドキュメント画像から生じるトークン爆発に苦戦しています。

DeepSeek-OCR 2は、この状況を完全に変えます。バージョン1の「Contexts Optical Compression」アプローチを基盤とし、新リリースでは「Visual Causal Flow」を導入しています。これは、単に文字を認識するのではなく、視覚的な関係とコンテキストを理解することで、人間が実際にドキュメントを読む方法で処理するアーキテクチャです。その結果、画像をわずか64トークンに圧縮しながら97%の精度を達成し、単一GPUで1日あたり20万ページ以上のスループットを可能にするモデルが誕生しました。

このガイドでは、基本的な設定から本番環境へのデプロイまで、すべてを網羅しています。すぐにコピー&ペーストして実行できるコードも含まれています。

💡
DeepSeek-OCR 2を試す準備はできましたか? Apidogをダウンロードして、APIを視覚的に試しましょう。Apidogを使えば、定型コードを書くことなく、ドキュメント画像をアップロードし、OCRリクエストを設定し、Markdown出力を確認し、異なる解像度モードでの結果を比較できます。

DeepSeek-OCR 2とは?

DeepSeek-OCR 2は、ドキュメントの理解とテキスト抽出のために特別に設計されたオープンソースの視覚言語モデルです。2026年1月にDeepSeek AIによってリリースされたこのモデルは、元のDeepSeek-OCRを基盤とし、新しい「Visual Causal Flow」アーキテクチャを採用しています。このアーキテクチャは、ドキュメント内の視覚要素がどのように因果的に関連し合っているかをモデル化します。例えば、テーブルのヘッダーがその下のセルをどのように解釈すべきかを決定したり、図のキャプションがその上のグラフを説明したりすることを理解します。

モデルは主に2つのコンポーネントで構成されています。

  1. DeepEncoder: ローカルの詳細抽出(SAMベース、80Mパラメータ)とグローバルなレイアウト理解(CLIPベース、300Mパラメータ)を組み合わせたデュアルビジョントランスフォーマー
  2. DeepSeek3B-MoE デコーダー: 圧縮された視覚表現から構造化された出力(Markdown、LaTeX、JSON)を生成する、専門家混合(mixture-of-experts)言語モデル。

DeepSeek-OCR 2の特長:

主要機能とアーキテクチャ

Visual Causal Flow

バージョン2の目玉機能は「Visual Causal Flow」です。これは、単なるOCRを超えてドキュメントを理解するための新しいアプローチです。ページを文字の平坦なグリッドとして扱うのではなく、モデルは視覚要素間の因果関係を学習します。

DeepEncoderアーキテクチャ

DeepEncoderは魔法が起こる場所です。これは、管理可能なトークン数を維持しながら高解像度画像を処理します。

Input Image (1024×1024)
    ↓
SAM-base Block (80M params)
    - Windowed attention for local detail
    - Extracts fine-grained features
    ↓
CLIP-large Block (300M params)
    - Global attention for layout
    - Understands document structure
    ↓
Convolution Block
    - 16× token reduction
    - 4,096 patches → 256 tokens
    ↓
Output: Compressed Vision Tokens

圧縮と精度のトレードオフ

圧縮率ビジョントークン精度
1,02499%+
10×25697%
16×16092%
20×128約60%

ほとんどのアプリケーションにとって最適なのは10倍の圧縮率であり、これにより97%の精度を維持しつつ、本番環境でのデプロイを実用的にする高いスループットが可能になります。

インストールとセットアップ

前提条件

方法1:vLLMのインストール(推奨)

vLLMは、本番環境のデプロイメントで最高のパフォーマンスを提供します。

# Create virtual environment
python -m venv deepseek-ocr-env
source deepseek-ocr-env/bin/activate

# Install vLLM with CUDA support
pip install vllm>=0.8.5

# Install flash attention for optimal performance
pip install flash-attn==2.7.3 --no-build-isolation

方法2:Transformersのインストール

開発および実験用:

pip install transformers>=4.40.0
pip install torch>=2.6.0 torchvision>=0.21.0
pip install accelerate
pip install flash-attn==2.7.3 --no-build-isolation

方法3:Docker(本番環境)

FROM nvidia/cuda:11.8-devel-ubuntu22.04

RUN pip install vllm>=0.8.5 flash-attn==2.7.3

# Pre-download model
RUN python -c "from vllm import LLM; LLM(model='deepseek-ai/DeepSeek-OCR-2')"

EXPOSE 8000
CMD ["vllm", "serve", "deepseek-ai/DeepSeek-OCR-2", "--port", "8000"]

インストールの検証

import torch
print(f"PyTorch version: {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
print(f"GPU: {torch.cuda.get_device_name(0)}")

import vllm
print(f"vLLM version: {vllm.__version__}")

Pythonコードの例

vLLMを使った基本的なOCR

ドキュメント画像からテキストを抽出する最も簡単な方法を以下に示します。

from vllm import LLM, SamplingParams
from vllm.model_executor.models.deepseek_ocr import NGramPerReqLogitsProcessor
from PIL import Image

# Initialize the model
llm = LLM(
    model="deepseek-ai/DeepSeek-OCR-2",
    enable_prefix_caching=False,
    mm_processor_cache_gb=0,
    logits_processors=[NGramPerReqLogitsProcessor],
    trust_remote_code=True,
)

# Load your document image
image = Image.open("document.png").convert("RGB")

# Prepare the prompt - "Free OCR." triggers standard extraction
prompt = "<image>\nFree OCR."

model_input = [{
    "prompt": prompt,
    "multi_modal_data": {"image": image}
}]

# Configure sampling parameters
sampling_params = SamplingParams(
    temperature=0.0,  # Deterministic for OCR
    max_tokens=8192,
    extra_args={
        "ngram_size": 30,
        "window_size": 90,
        "whitelist_token_ids": {128821, 128822},  # <td>, </td> for tables
    },
    skip_special_tokens=False,
)

# Generate output
outputs = llm.generate(model_input, sampling_params)

# Extract the markdown text
markdown_text = outputs[0].outputs[0].text
print(markdown_text)

複数のドキュメントの一括処理

複数のドキュメントを単一のバッチで効率的に処理します。

from vllm import LLM, SamplingParams
from vllm.model_executor.models.deepseek_ocr import NGramPerReqLogitsProcessor
from PIL import Image
from pathlib import Path

def batch_ocr(image_paths: list[str], llm: LLM) -> list[str]:
    """Process multiple images in a single batch."""

    # Load all images
    images = [Image.open(p).convert("RGB") for p in image_paths]

    # Prepare batch input
    prompt = "<image>\nFree OCR."
    model_inputs = [
        {"prompt": prompt, "multi_modal_data": {"image": img}}
        for img in images
    ]

    sampling_params = SamplingParams(
        temperature=0.0,
        max_tokens=8192,
        extra_args={
            "ngram_size": 30,
            "window_size": 90,
            "whitelist_token_ids": {128821, 128822},
        },
        skip_special_tokens=False,
    )

    # Generate all outputs in one call
    outputs = llm.generate(model_inputs, sampling_params)

    return [out.outputs[0].text for out in outputs]


# Usage
llm = LLM(
    model="deepseek-ai/DeepSeek-OCR-2",
    enable_prefix_caching=False,
    mm_processor_cache_gb=0,
    logits_processors=[NGramPerReqLogitsProcessor],
)

image_files = list(Path("documents/").glob("*.png"))
results = batch_ocr([str(f) for f in image_files], llm)

for path, text in zip(image_files, results):
    print(f"--- {path.name} ---")
    print(text[:500])  # First 500 chars
    print()

Transformersを直接使用する

推論プロセスをより細かく制御するには:

import torch
from transformers import AutoModel, AutoTokenizer
from PIL import Image

# Set GPU
device = "cuda:0"

# Load model and tokenizer
model_name = "deepseek-ai/DeepSeek-OCR-2"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModel.from_pretrained(
    model_name,
    _attn_implementation="flash_attention_2",
    trust_remote_code=True,
    use_safetensors=True,
)
model = model.eval().to(device).to(torch.bfloat16)

# Load and preprocess image
image = Image.open("document.png").convert("RGB")

# Different prompts for different tasks
prompts = {
    "ocr": "<image>\nFree OCR.",
    "markdown": "<image>\n<|grounding|>Convert the document to markdown.",
    "table": "<image>\nExtract all tables as markdown.",
    "math": "<image>\nExtract mathematical expressions as LaTeX.",
}

# Process with your chosen prompt
prompt = prompts["markdown"]
inputs = tokenizer(prompt, return_tensors="pt").to(device)

# Add image to inputs (model-specific preprocessing)
with torch.no_grad():
    outputs = model.generate(
        **inputs,
        images=[image],
        max_new_tokens=4096,
        do_sample=False,
    )

result = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(result)

高スループットのための非同期処理

import asyncio
from vllm import AsyncLLMEngine, AsyncEngineArgs, SamplingParams
from vllm.model_executor.models.deepseek_ocr import NGramPerReqLogitsProcessor
from PIL import Image

async def process_document(engine, image_path: str, request_id: str):
    """Process a single document asynchronously."""
    image = Image.open(image_path).convert("RGB")

    prompt = "<image>\nFree OCR."
    sampling_params = SamplingParams(
        temperature=0.0,
        max_tokens=8192,
        extra_args={
            "ngram_size": 30,
            "window_size": 90,
            "whitelist_token_ids": {128821, 128822},
        },
    )

    results = []
    async for output in engine.generate(prompt, sampling_params, request_id):
        results.append(output)

    return results[-1].outputs[0].text

async def main():
    # Initialize async engine
    engine_args = AsyncEngineArgs(
        model="deepseek-ai/DeepSeek-OCR-2",
        enable_prefix_caching=False,
        mm_processor_cache_gb=0,
    )
    engine = AsyncLLMEngine.from_engine_args(engine_args)

    # Process multiple documents concurrently
    image_paths = ["doc1.png", "doc2.png", "doc3.png"]
    tasks = [
        process_document(engine, path, f"req_{i}")
        for i, path in enumerate(image_paths)
    ]

    results = await asyncio.gather(*tasks)

    for path, text in zip(image_paths, results):
        print(f"{path}: {len(text)} characters extracted")

asyncio.run(main())

本番環境でのvLLMの使用

OpenAI互換サーバーの起動

DeepSeek-OCR 2をAPIサーバーとしてデプロイします。

vllm serve deepseek-ai/DeepSeek-OCR-2 \
    --host 0.0.0.0 \
    --port 8000 \
    --logits_processors vllm.model_executor.models.deepseek_ocr:NGramPerReqLogitsProcessor \
    --no-enable-prefix-caching \
    --mm-processor-cache-gb 0 \
    --max-model-len 16384 \
    --gpu-memory-utilization 0.9

OpenAI SDKを使用したサーバー呼び出し

from openai import OpenAI
import base64

# Initialize client pointing to local server
client = OpenAI(
    api_key="EMPTY",  # Not required for local server
    base_url="http://localhost:8000/v1",
    timeout=3600,
)

def encode_image(image_path: str) -> str:
    """Encode image to base64."""
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

def ocr_document(image_path: str) -> str:
    """Extract text from document using OCR API."""
    base64_image = encode_image(image_path)

    response = client.chat.completions.create(
        model="deepseek-ai/DeepSeek-OCR-2",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{base64_image}"
                        }
                    },
                    {
                        "type": "text",
                        "text": "Free OCR."
                    }
                ]
            }
        ],
        max_tokens=8192,
        temperature=0.0,
        extra_body={
            "skip_special_tokens": False,
            "vllm_xargs": {
                "ngram_size": 30,
                "window_size": 90,
                "whitelist_token_ids": [128821, 128822],
            },
        },
    )

    return response.choices[0].message.content

# Usage
result = ocr_document("invoice.png")
print(result)

URLでの使用

response = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-OCR-2",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://example.com/document.png"
                    }
                },
                {
                    "type": "text",
                    "text": "Free OCR."
                }
            ]
        }
    ],
    max_tokens=8192,
    temperature=0.0,
)

Apidogでのテスト

OCR APIを効果的にテストするには、入力ドキュメントと抽出された出力の両方を視覚化する必要があります。Apidogは、DeepSeek-OCR 2を試すための直感的なインターフェースを提供します。

OCRエンドポイントのセットアップ

ステップ1:新しいリクエストの作成

  1. Apidogを開き、新しいプロジェクトを作成します。
  2. http://localhost:8000/v1/chat/completionsにPOSTリクエストを追加します。

ステップ2:ヘッダーの設定

Content-Type: application/json

ステップ3:リクエストボディの設定

{
  "model": "deepseek-ai/DeepSeek-OCR-2",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "image_url",
          "image_url": {
            "url": "data:image/png;base64,{{base64_image}}"
          }
        },
        {
          "type": "text",
          "text": "Free OCR."
        }
      ]
    }
  ],
  "max_tokens": 8192,
  "temperature": 0,
  "extra_body": {
    "skip_special_tokens": false,
    "vllm_xargs": {
      "ngram_size": 30,
      "window_size": 90,
      "whitelist_token_ids": [128821, 128822]
    }
  }
}

異なるドキュメントタイプのテスト

一般的なドキュメントタイプ用に保存されたリクエストを作成します。

  1. 請求書抽出 - 構造化データ抽出をテストします。
  2. 学術論文 - LaTeX数式の処理をテストします。
  3. 手書きメモ - 手書き認識をテストします。
  4. 多段組レイアウト - 読み取り順序の推論をテストします。

解像度モードの比較

環境変数を設定して、異なるモードを迅速にテストします。

モード解像度トークンユースケース
tiny512×51264クイックプレビュー
small640×640100シンプルなドキュメント
base1024×1024256標準的なドキュメント
large1280×1280400密なテキスト
gundam動的可変複雑なレイアウト

解像度モードと圧縮

DeepSeek-OCR 2は5つの解像度モードをサポートしており、それぞれ異なるユースケースに最適化されています。

Tinyモード(64トークン)

最適な用途:高速なテキスト検出、シンプルなフォーム、低解像度入力

# Configure for tiny mode
os.environ["DEEPSEEK_OCR_MODE"] = "tiny"  # 512×512

Smallモード(100トークン)

最適な用途:クリーンなデジタルドキュメント、単段組テキスト

Baseモード(256トークン)- デフォルト

最適な用途:ほとんどの標準的なドキュメント、請求書、手紙

Largeモード(400トークン)

最適な用途:密な学術論文、法的文書

Gundamモード(動的)

最適な用途:複雑な複数ページドキュメントで様々なレイアウトを持つもの

# Gundam mode combines multiple views
# - n × 640×640 local tiles for detail
# - 1 × 1024×1024 global view for structure

適切なモードの選択

def select_mode(document_type: str, page_count: int) -> str:
    """Select optimal resolution mode based on document characteristics."""

    if document_type == "simple_form":
        return "tiny"
    elif document_type == "digital_document" and page_count == 1:
        return "small"
    elif document_type == "academic_paper":
        return "large"
    elif document_type == "mixed_layout" or page_count > 1:
        return "gundam"
    else:
        return "base"  # Default

PDFおよびドキュメントの処理

PDFから画像への変換

import fitz  # PyMuPDF
from PIL import Image
import io

def pdf_to_images(pdf_path: str, dpi: int = 150) -> list[Image.Image]:
    """Convert PDF pages to PIL Images."""
    doc = fitz.open(pdf_path)
    images = []

    for page_num in range(len(doc)):
        page = doc[page_num]
        # Render at specified DPI
        mat = fitz.Matrix(dpi / 72, dpi / 72)
        pix = page.get_pixmap(matrix=mat)

        # Convert to PIL Image
        img_data = pix.tobytes("png")
        img = Image.open(io.BytesIO(img_data))
        images.append(img)

    doc.close()
    return images

# Usage
images = pdf_to_images("report.pdf", dpi=200)
print(f"Extracted {len(images)} pages")

完全なPDF処理パイプライン

from vllm import LLM, SamplingParams
from vllm.model_executor.models.deepseek_ocr import NGramPerReqLogitsProcessor
from pathlib import Path
import fitz
from PIL import Image
import io

class PDFProcessor:
    def __init__(self, model_name: str = "deepseek-ai/DeepSeek-OCR-2"):
        self.llm = LLM(
            model=model_name,
            enable_prefix_caching=False,
            mm_processor_cache_gb=0,
            logits_processors=[NGramPerReqLogitsProcessor],
        )
        self.sampling_params = SamplingParams(
            temperature=0.0,
            max_tokens=8192,
            extra_args={
                "ngram_size": 30,
                "window_size": 90,
                "whitelist_token_ids": {128821, 128822},
            },
            skip_special_tokens=False,
        )

    def process_pdf(self, pdf_path: str, dpi: int = 150) -> str:
        """Process entire PDF and return combined markdown."""
        doc = fitz.open(pdf_path)
        all_text = []

        for page_num in range(len(doc)):
            # Convert page to image
            page = doc[page_num]
            mat = fitz.Matrix(dpi / 72, dpi / 72)
            pix = page.get_pixmap(matrix=mat)
            img = Image.open(io.BytesIO(pix.tobytes("png")))

            # OCR the page
            prompt = "<image>\nFree OCR."
            model_input = [{
                "prompt": prompt,
                "multi_modal_data": {"image": img}
            }]

            output = self.llm.generate(model_input, self.sampling_params)
            page_text = output[0].outputs[0].text

            all_text.append(f"## Page {page_num + 1}\n\n{page_text}")

        doc.close()
        return "\n\n---\n\n".join(all_text)

# Usage
processor = PDFProcessor()
markdown = processor.process_pdf("annual_report.pdf")

# Save to file
Path("output.md").write_text(markdown)

ベンチマーク性能

精度ベンチマーク

ベンチマークDeepSeek-OCR 2GOT-OCR2.0MinerU2.0
OmniDocBench94.2%91.8%89.5%
1ページあたりのトークン数100-2562566,000+
Fox(10倍圧縮)97%
Fox(20倍圧縮)60%

スループット性能

ハードウェア1日あたりのページ数1時間あたりのページ数
A100-40G(シングル)200,000+約8,300
A100-40G × 2033M+約1.4M
RTX 4090約80,000約3,300
RTX 3090約50,000約2,100

ドキュメントタイプ別の実測精度

ドキュメントタイプ精度注記
デジタルPDF98%+最高の性能
スキャンされたドキュメント95%+良質なスキャン
財務報告書92%複雑なテーブル
手書きメモ85%判読性による
歴史的文書80%品質劣化

ベストプラクティスと最適化

画像の前処理

from PIL import Image, ImageEnhance, ImageFilter

def preprocess_document(image: Image.Image) -> Image.Image:
    """Preprocess document image for optimal OCR."""

    # Convert to RGB if necessary
    if image.mode != "RGB":
        image = image.convert("RGB")

    # Resize if too small (minimum 512px on shortest side)
    min_dim = min(image.size)
    if min_dim < 512:
        scale = 512 / min_dim
        new_size = (int(image.width * scale), int(image.height * scale))
        image = image.resize(new_size, Image.Resampling.LANCZOS)

    # Enhance contrast for scanned documents
    enhancer = ImageEnhance.Contrast(image)
    image = enhancer.enhance(1.2)

    # Sharpen slightly
    image = image.filter(ImageFilter.SHARPEN)

    return image

プロンプトエンジニアリング

# Different prompts for different tasks
PROMPTS = {
    # Standard OCR - fastest, good for most cases
    "ocr": "<image>\nFree OCR.",

    # Markdown conversion - better structure preservation
    "markdown": "<image>\n<|grounding|>Convert the document to markdown.",

    # Table extraction - optimized for tabular data
    "table": "<image>\nExtract all tables in markdown format.",

    # Math extraction - for academic/scientific documents
    "math": "<image>\nExtract all text and mathematical expressions. Use LaTeX for math.",

    # Specific fields - for form extraction
    "fields": "<image>\nExtract the following fields: name, date, amount, signature.",
}

メモリの最適化

# For limited GPU memory
llm = LLM(
    model="deepseek-ai/DeepSeek-OCR-2",
    gpu_memory_utilization=0.8,  # Leave headroom
    max_model_len=8192,  # Reduce max context
    enable_chunked_prefill=True,  # Better memory efficiency
)

バッチ処理戦略

def optimal_batch_size(gpu_memory_gb: int, avg_image_size: tuple) -> int:
    """Calculate optimal batch size based on GPU memory."""

    # Approximate memory per image (in GB)
    pixels = avg_image_size[0] * avg_image_size[1]
    mem_per_image = (pixels * 4) / (1024**3)  # 4 bytes per pixel

    # Reserve 60% of GPU memory for model
    available = gpu_memory_gb * 0.4

    return max(1, int(available / mem_per_image))

# Example: A100-40G with 1024x1024 images
batch_size = optimal_batch_size(40, (1024, 1024))
print(f"Recommended batch size: {batch_size}")  # ~10

一般的な問題のトラブルシューティング

メモリ不足エラー

問題: CUDA out of memory

解決策:

# 1. Reduce batch size
sampling_params = SamplingParams(max_tokens=4096)  # Reduce from 8192

# 2. Use smaller resolution mode
os.environ["DEEPSEEK_OCR_MODE"] = "small"

# 3. Enable memory optimization
llm = LLM(
    model="deepseek-ai/DeepSeek-OCR-2",
    gpu_memory_utilization=0.7,
    enforce_eager=True,  # Disable CUDA graphs
)

不十分なテーブル抽出

問題: テーブルのずれやセルの欠落

解決策:

# Ensure whitelist tokens are set
sampling_params = SamplingParams(
    extra_args={
        "whitelist_token_ids": {128821, 128822},  # Critical for tables
    },
)

# Use higher resolution
os.environ["DEEPSEEK_OCR_MODE"] = "large"

推論速度の低下

問題: 処理に時間がかかりすぎる

解決策:

  1. Transformersの代わりにvLLMを使用する(2~3倍高速)
  2. Flash Attention 2を有効にする
  3. シーケンシャルではなくバッチ処理を使用する
  4. テンソルコアを備えたGPU(A100、H100)にデプロイする

判読不能な出力

問題: 出力に意味不明な文字や繰り返し文字が含まれている

解決策:

# Ensure logits processor is enabled
from vllm.model_executor.models.deepseek_ocr import NGramPerReqLogitsProcessor

llm = LLM(
    model="deepseek-ai/DeepSeek-OCR-2",
    logits_processors=[NGramPerReqLogitsProcessor],  # Required!
)

# Use temperature=0 for deterministic output
sampling_params = SamplingParams(temperature=0.0)

ドキュメントからテキストを抽出する準備はできましたか?Apidogをダウンロードして、視覚的なインターフェースでDeepSeek-OCR 2 APIコールをテストし、このガイドの本番環境パターンを使用して自信を持ってデプロイしてください。

button

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる