PaddleOCR-VL

PaddleOCR-VL

이 문서는 Baidu의 컴팩트한 문서 파싱 비전-언어 모델인 PaddleOCR-VL을 SGLang으로 서빙하고 호출하는 방법을 설명해요. NaViT 스타일 동적 해상도 SigLIP 비전 인코더가 ERNIE-4.5-0.3B 언어 백본을 공급하는 구조로, 총 0.9B 파라미터이며 Apache 2.0 라이선스로 배포돼요. 원문 페이지에는 하드웨어 플랫폼과 페이지 해상도를 골라 명령을 자동 생성해 주는 Playground가 포함되어 있어요.

출처: 문서

본문

배포

SGLang 설치

모든 방법과 하드웨어 플랫폼은 공식 SGLang 설치 가이드를 참고하세요. 아래 두 경로는 명령 패널의 Python / Docker 토글과 일치해요.

Python (pip / uv):

pip install --upgrade pip
pip install uv
uv pip install --prerelease=allow sglang

그런 다음 해당 환경에서 아래 명령 패널의 Python 출력을 실행하세요.

Docker:

docker pull lmsysorg/sglang:dev

이미지 실행 방법은 Install → Method 3: Using Docker를 참고하세요. 내부 sglang serve ...을 아래 명령 생성기가 만드는 것으로 대체하세요.

릴리스와 하드웨어를 골라 실행 명령을 생성하세요. 모델은 0.9B이고 단일 GPU이므로 플랫폼별 서빙 레시피가 하나예요; 실제로 비용을 움직이는 축은 페이지 해상도(Page Resolution) 로, 페이지 하나가 몇 개의 이미지 토큰으로 환산되는지 제한해요.

Playground

Playground를 사용해 선택한 배포 셀 위에 텐서 병렬화를 얹으세요. 이 크기에서 TP는 용량 노브가 아닌 지연 시간 노브예요 — 가중치가 GPU 하나에 맞아요.

1. 모델 소개

PaddleOCR-VL은 Baidu의 컴팩트 문서 파싱 비전-언어 모델이에요: NaViT 스타일 동적 해상도 SigLIP 비전 인코더가 ERNIE-4.5-0.3B 언어 백본을 공급하고, 총 0.9B 파라미터이며 Apache 2.0으로 출시됐어요. 109개 언어에 걸쳐 텍스트, 표, 수식, 차트, 도장을 인식하며, 단일 GPU가 편안하게 서빙할 만큼 작아요.

SGLang은 PaddleOCR 문서 파싱 파이프라인의 인식 단계인 이 모델을 서빙해요 — 파이프라인 자체는 아니에요. 레이아웃·영역 감지, 크롭핑, 읽기 순서, markdown 또는 JSON 조립은 PaddleOCR에 남아 있고, PaddleOCR이 영역당 한 번 모델 엔드포인트를 호출해요. 이런 분리가 모델이 지시 따르기보다 프롬프트 조건화되는 이유예요: 호출자가 크롭이 표인지, 수식인지, 차트인지 결정하고(§3.1), SGLang이 답하는 백엔드예요.

세 릴리스 모두 동일한 config.json(같은 PaddleOCRVLForConditionalGeneration 아키텍처, 같은 타워와 백본 차원)을 공유하므로, 하나의 SGLang 레시피가 모든 변형을 서빙하고 모델 경로만 바뀌어요.

변형 총 파라미터 용도
PaddleOCR-VL-1.6 0.9B 최신. 표, 한자, 도장에 최고. 1.5의 drop-in.
PaddleOCR-VL-1.5 0.9B 이전 세대; 그 출력에 대해 보정했다면 고정하세요.
PaddleOCR-VL 0.9B 원래 0.9B 릴리스.

권장 생성: 페이지당 max_tokens 예산을 둔 greedy 디코딩(temperature=0) — 모델 카드는 단일 영역에 512를, 참조 서버는 전체 페이지에 더 많은 값을 사용해요. 정보용이며 라이브러리 코드에 하드코딩하지 마세요.

리소스: Hugging Face · PaddleOCR on GitHub

2. 설정 팁

  • 페이지 해상도가 주요 비용 노브예요. 비전 타워와 prefill 모두 페이지의 패치 수에 따라 확장돼요. max_pixels는 28x28 단위로 표현되며(패치 크기 14에 2x2 병합), 따라서 max_pixels / 784가 페이지당 이미지 토큰 예산이에요. 체크포인트의 자체 기본값은 1280 토큰이고, Deploy 패널의 Page Resolution 선택기가 해당 --mm-process-config 값을 생성해요. 깨끗한 born-digital PDF에는 낮추고, 밀집 스캔과 작은 인쇄에는 높이세요.
  • 프롬프트가 작업을 선택해요. PaddleOCR-VL은 지시 따르기보다 프롬프트 조건화돼요 — §3.1의 정확한 작업 문자열을 사용하세요. 자유 형식 질문은 채팅 모델처럼 동작하지 않아요.
  • --trust-remote-code를 끄세요. 체크포인트는 자체 configuration_paddleocr_vl.py / processing_paddleocr_vl.py를 제공하지만, transformers 5.12는 paddleocr_vl을 네이티브로 지원해요 — 그리고 번들된 원격 이미지 프로세서는 두 구현 중 더 느린 쪽이에요(1080p 페이지당 87.4 ms 대 39.1 ms 측정). 플래그를 전달하면 SGLang이 원격 복사본에 고정돼요. 없이 서빙하면 확인한 모든 페이지에서 byte-identical OCR 출력을 냈고 32-way 동시성에서 초당 요청이 약 5% 더 많았어요.
  • 전처리는 자동으로 병렬화돼요. 전체 해상도 페이지는 리사이즈, 정규화, 패치화에 수십 밀리초의 CPU가 들며, 이는 GPU가 포화되기 한참 전에 처리량을 제한하므로 이 모델은 기본적으로 여러 worker에서 이미지 프로세서를 실행해요. --mm-processor-worker-num이 그 수를 재정의해요. 측정에서 기본값을 넘게 높여도 도움이 되지 않았어요.
  • 반복 페이지에는 radix cache를 켜 두세요. 고유 스캔에 대한 문서 전체 배치 OCR과 달리, 같은 페이지에 대해 다시 물어보는 워크로드(한 이미지에 다른 작업 프롬프트)는 이미지 prefix를 재사용해요. 모든 요청이 다른 페이지를 실을 때만 --disable-radix-cache를 추가하세요.
  • 포화 처리량 플래그가 제 몫을 해요. 페이지는 약 2700 토큰이므로 기본 8192 토큰 prefill 예산은 그중 세 개만 forward에 담아요. 16384로 올리고 decode를 같은 배치에 탑승시키면(--enable-mixed-chunk, --num-continuous-decode-steps 2) 32-way 동시성에서 초당 요청 +11%, 대기 TTFT -23%를 측정했고 단일 스트림 지연은 동일했어요. H200에서 측정; 더 작은 카드에서는 --chunked-prefill-size를 맞을 때까지 낮추세요.
  • 이 모델에서는 prefill CUDA graph가 켜져 있어요. SGLang은 보통 모든 멀티모달 아키텍처에서 breakable prefill 그래프를 끄지만, PaddleOCR-VL은 allowlist에 다시 등록되어 텍스트 전용 프롬프트에서 단일 스트림 TTFT가 16.1 ms → 11.5 ms로 단축됐어요. 이미지 포함 배치는 그래프 리플레이에서 거부되고 eager로 실행되므로, 이는 혼합 및 텍스트 트래픽을 돕지 순수 페이지 파싱은 돕지 않아요. 플래그 불필요.
  • 텐서 병렬화는 선택 사항이에요. BF16에서 가중치가 2 GB 미만이에요; TP>1은 계층당 컬렉티브 비용에 비전 인코더·prefill 임계 경로만 단축해요. 채택 전에 측정하세요.
  • 컨텍스트 길이. 백본은 131072 위치를 광고하지만, 파싱된 페이지는 거의 수천 토큰을 넘지 않아요. 레시피는 --context-length 16384로 고정해 KV 풀을 작게 유지하고 동시성을 높게 유지해요; 한 요청에 여러 페이지를 배치하는 경우에만 올리세요.

H200 하나에서 측정

1080p 페이지(약 2700 이미지 토큰) 입력, 128 토큰 출력, prefix cache 비활성, 중앙값 TTFT:

설정 TTFT, 1 stream 32 concurrent에서 req/s
--trust-remote-code 사용 (원격 이미지 프로세서) 219 ms 10.9
위 레시피 (네이티브 이미지 프로세서) 114 ms 11.3

포화에서 처리량은 비전 타워에 묶이며, 비전 타워는 페이지의 모든 패치에 대해 전체 attention을 실행해요 — 따라서 지연 시간보다 Page Resolution 선택기가 그걸 움직이는 레버예요.

3. 고급 사용법

3.1 작업 프롬프트

PaddleOCR-VL은 작은 고정 프롬프트 집합을 통해 능력을 노출해요. 프롬프트를 텍스트 부분으로, 페이지를 같은 사용자 턴의 이미지 부분으로 보내세요.

프롬프트 작업
OCR: 일반 텍스트 인식.
Table Recognition: 표 구조와 셀 내용.
Formula Recognition: 수학 표현식.
Chart Recognition: 차트 내용.
Spotting: 위치가 있는 텍스트. 고해상도 설정이 유리해요.
Seal Recognition: 도장과 스탬프 (1.6).

구조화 작업은 HTML이 아닌 모델 자체 마크업으로 답해요: Table Recognition:은 OTSL 스타일 셀 토큰(셀당 <fcel>, 행당 <nl>)을 반환하므로, HTML이나 markdown을 원하는 호출자는 직접 변환해요.

표 인식 출력:

<fcel>Methods<fcel>R<fcel>P<fcel>F<fcel>FPS<nl><fcel>SegLink [26]<fcel>70.0<fcel>86.0<fcel>77.0<fcel>8.9<nl><fcel>PixelLink [4]<fcel>73.2<fcel>83.0<fcel>77.8<fcel>-<nl><fcel>TextSnake [18]<fcel>73.9<fcel>83.2<fcel>78.3<fcel>1.1<nl>
... (one <fcel> per cell, one <nl> per row, to the end of the table)

OCR 요청 (Python):

from openai import OpenAI

client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")

response = client.chat.completions.create(
    model="PaddlePaddle/PaddleOCR-VL-1.6",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "OCR:"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/release/2.9/doc/imgs_en/img_12.jpg"
                    },
                },
            ],
        }
    ],
    max_tokens=2048,
)

print(response.choices[0].message.content)

출력 예시:

ACKNOWLEDGEMENTS
We would like to thank all the designers and contributors who have been involved in the
production of this book; their contributions have been indispensable to its creation. We would
also like to express our gratitude to all the producers for their invaluable opinions and
assistance throughout this project. And to the many others whose names are not credited but
have made specific input in this book, we thank you for your continuous support.

3.2 다중 페이지 문서 파싱

모델은 요청당 한 페이지를 파싱해요. 각 페이지를 이미지로 렌더링한 다음 페이지를 동시에 펼치세요 — SGLang이 진행 중 요청의 비전 인코더를 단일 forward로 배치하므로, 이렇게 작은 모델에서 동시성이 GPU를 바쁘게 유지하는 것이에요.

동시 페이지 파싱 (Python):

import base64
from concurrent.futures import ThreadPoolExecutor

import pymupdf
from openai import OpenAI

client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")


def render(page, dpi=200):
    pixmap = page.get_pixmap(dpi=dpi)
    return base64.b64encode(pixmap.tobytes("png")).decode("ascii")


def parse(page_png_b64):
    response = client.chat.completions.create(
        model="PaddlePaddle/PaddleOCR-VL-1.6",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "OCR:"},
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{page_png_b64}"
                        },
                    },
                ],
            }
        ],
        max_tokens=2048,
    )
    return response.choices[0].message.content


document = pymupdf.open("your_document.pdf")
pages = [render(page) for page in document]

with ThreadPoolExecutor(max_workers=16) as pool:
    for index, text in enumerate(pool.map(parse, pages)):
        print(f"--- page {index + 1} ---")
        print(text)

출력 예시:

--- page 1 ---
(a) Total-Text
(b) Total-Text
(c) CTW1500
(d) CTW1500

Figure 8. Visual experimental results. The blue contours are boundary proposals, and the
green contours are final detection boundaries.
Table 6. Experimental results on CTW-1500.
Methods
Ext
R
P
F
FPS
TextSnake [18]
Syn
85.3
67.9
75.6
-
... (page continues)
--- page 2 ---
... (one block per page, in page order)