# Chandra OCR 2 — 사내 Document Intelligence 서비스 기술 문서 > **작성일**: 2026-04-15 > **작성자**: AI Engineering Team > **서비스 서버**: DGX H200 (10.150.6.159) > **상태**: 운영 중 ----- ## 1. 개요 Chandra OCR 2는 Datalab에서 개발한 최신 OCR(Optical Character Recognition) 모델로, 문서 이미지를 구조화된 HTML/Markdown/JSON으로 변환하면서 레이아웃 정보를 보존합니다. olmOCR 벤치마크에서 85.9% 점수로 오픈소스 OCR 모델 중 최고 성능(SOTA)을 기록하고 있으며, 90개 이상의 언어를 지원합니다. 사내에서는 기존 DotsOCR(Qwen3-VL-235B 기반) 파이프라인을 Chandra OCR 2로 교체하여, 동일한 FastAPI 인터페이스(`POST /process-file/`)를 유지하면서 더 높은 정확도와 효율적인 리소스 사용을 실현하고 있습니다. ### 1.1 기존 시스템 대비 개선점 |항목 |기존 (DotsOCR) |현재 (Chandra OCR 2) | |-----------|---------------------------------|---------------------------| |추론 모델 |Qwen3-VL-235B (MoE, ~235B params)|Chandra OCR 2 (5B params) | |모델 크기 |~130GB+ |~10.6GB | |GPU 요구사항 |멀티 GPU 필수 |단일 GPU (24GB+) | |출력 형식 |레이아웃 JSON (커스텀 프롬프트) |HTML → Markdown/JSON (네이티브)| |olmOCR 벤치마크|64.6% (Qwen3-VL-8B 기준) |85.9% | |다국어 지원 |40+ 언어 |90+ 언어 | |한국어 정확도 |82.3% |88.7% | ----- ## 2. 모델 소개 ### 2.1 Chandra OCR 2란? Chandra OCR 2는 복잡한 문서 — 손글씨, 테이블, 수식, 양식 등 — 을 높은 정확도로 인식하여 구조화된 디지털 포맷으로 변환하는 OCR 모델입니다. 단순한 텍스트 추출이 아니라, 문서의 공간적 레이아웃(바운딩 박스), 요소 유형(텍스트, 테이블, 수식 등), 읽기 순서를 함께 보존합니다. ### 2.2 모델 아키텍처 Chandra OCR 2는 Alibaba의 Qwen 3.5 비전-언어 모델을 기반(base model)으로 하여, 문서 특화 태스크에 파인튜닝한 모델입니다. ``` ┌─────────────────────────────────────────────┐ │ Chandra OCR 2 (5B) │ │ │ │ ┌─────────────┐ ┌────────────────────┐ │ │ │ Vision │ │ Language Model │ │ │ │ Encoder │───▶│ (Qwen 3.5 기반) │ │ │ │ │ │ │ │ │ │ 이미지 입력 │ │ HTML/Markdown 출력 │ │ │ └─────────────┘ └────────────────────┘ │ │ │ │ Fine-tuned on document-specific tasks │ └─────────────────────────────────────────────┘ ``` **주요 구성 요소:** - **Vision Encoder**: 문서 이미지를 시각 토큰으로 인코딩하는 비전 인코더 - **Language Model**: Qwen 3.5 아키텍처 기반 텍스트 생성 모델 (5B 파라미터) - **Projection Layer**: 비전 토큰을 언어 모델 입력으로 변환하는 투사 레이어 ### 2.3 학습 방식 Chandra OCR 2의 학습은 “강력한 범용 기반 모델에서 시작하여, 태스크 특화 데이터로 집중 파인튜닝”하는 방식을 따릅니다. **기반 모델 (Base Model)** Qwen 3.5는 Alibaba의 Qwen 팀이 개발한 비전-언어 모델로, 이미지와 텍스트를 함께 이해하고 추론할 수 있는 모델입니다. 이 모델이 Chandra OCR 2의 “인식 백본(perception backbone)“을 제공합니다. **파인튜닝 (Fine-tuning)** Datalab은 이 기반 모델을 다양한 카테고리의 대규모 문서 데이터셋으로 파인튜닝했습니다: - 학술 논문, 기술 보고서 - 손글씨 양식 및 메모 - 복잡한 테이블 (병합 셀, 다중 헤더) - 수학 수식 (인라인/블록) - 다국어 문서 (90+ 언어) - 스캔된 문서 및 팩스 - 다단 레이아웃 (신문, 교과서) - 양식 (체크박스, 라디오 버튼) - 차트, 다이어그램 파인튜닝의 핵심은 단순 문자 인식이 아니라 **레이아웃 인식(layout-aware) OCR**에 초점을 맞춘 것입니다. 모델이 “문서가 어떻게 생겼는지”를 내재화하여, 콘텐츠의 존재뿐 아니라 위치와 공간적 관계까지 이해합니다. ### 2.4 모델 스펙 |항목 |값 | |--------|--------------------------------| |모델명 |datalab-to/chandra-ocr-2 | |파라미터 수 |5B (BF16) | |모델 크기 |~10.6GB | |기반 모델 |Qwen 3.5 (Vision-Language Model)| |아키텍처 |qwen3_5 (Transformers) | |최대 출력 토큰|12,384 | |정규화 좌표 |0-1000 (bbox) | |라이선스 |OpenRAIL-M (연구/개인/스타트업 무료) | ### 2.5 벤치마크 성능 **olmOCR Benchmark (영문 문서)** |모델 |ArXiv |Tables |Multi-column|Overall | |-------------|--------|--------|------------|--------| |**Chandra 2**|**90.2**|**89.9**|83.5 |**85.9**| |dots.ocr 1.5 |85.9 |90.7 |85.3 |83.9 | |Chandra 1 |82.2 |88.0 |81.2 |83.1 | |olmOCR 2 |83.0 |84.9 |83.7 |82.4 | |GPT-4o |53.5 |70.0 |69.3 |69.9 | |Qwen 3 VL 8B |70.2 |45.6 |62.1 |64.6 | **다국어 벤치마크 (주요 언어)** |언어 |Chandra 2|Gemini 2.5 Flash|GPT-5 Mini| |-----------|---------|----------------|----------| |한국어 (ko) |81.5% |84.8% |78.4% | |일본어 (ja) |86.9% |80.0% |76.1% | |중국어 (zh) |88.7% |70.0% |70.4% | |영어 (en) |94.8% |88.3% |93.8% | |평균 (43개 언어)|**77.8%**|67.6% |60.5% | ### 2.6 지원 문서 유형 |유형 |설명 | |--------|--------------------| |손글씨 |필기체, 메모, 의사 처방전 등 | |테이블 |병합 셀, 다중 헤더, 재무제표 | |수식 |인라인/블록 수식 → LaTeX 변환| |양식 |체크박스, 라디오 버튼, 입력 필드 | |다단 레이아웃 |신문, 교과서, 학술 논문 | |차트/다이어그램|데이터 추출, Mermaid 변환 | |화학식 |SMILES 표기 변환 | ----- ## 3. 시스템 아키텍처 ### 3.1 전체 구성도 ``` 외부 클라이언트 │ POST /process-file/ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ DGX H200 서버 (10.150.6.159) │ │ │ │ ┌──────────────────────┐ │ │ │ FastAPI 서비스 │ ← 호스트 실행 (uvicorn :10001) │ │ │ - 파일 업로드/검증 │ │ │ │ - 복호화 요청 │──→ 복호화 서버 (10.150.6.47:9001) │ │ │ - Office→PDF 변환 │──→ LibreOffice (libre1~4) │ │ │ - 이미지 전처리 │ │ │ │ - Chandra OCR 요청 │ │ │ │ - 결과 파싱/변환 │ │ │ └──────────┬───────────┘ │ │ │ http://localhost:10002 │ │ ▼ │ │ ┌──────────────────────┐ │ │ │ vLLM 서버 (Docker) │ ← GPU #3 (H200 141GB) │ │ │ - chandra-ocr-2 모델 │ │ │ │ - OpenAI-compatible │ │ │ │ - 컨테이너 내부 :8000 │ │ │ │ - 호스트 :10002 │ │ │ └──────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### 3.2 처리 파이프라인 ``` 1. 파일 업로드 └→ MIME 타입 검증, 크기 제한 (10MB) 2. 복호화 └→ 10.150.6.47:9001 복호화 서버 호출 └→ 이미지 파일은 복호화 생략 3. 파일 변환 └→ Office 파일 (docx, pptx 등) → LibreOffice 컨테이너로 PDF 변환 └→ PDF → PyMuPDF로 페이지별 이미지 추출 4. 이미지 전처리 └→ 해상도 리사이징 (min: 100,352px / max: 1,003,520px) 5. Chandra OCR 추론 └→ vLLM 서버에 OCR Layout 프롬프트 + 이미지 전송 └→ HTML 형식 응답 수신 (data-bbox, data-label 포함) 6. 출력 파싱 └→ HTML → Markdown 변환 (수식, 테이블 보존) └→ HTML → JSON 레이아웃 블록 추출 (bbox, category, text) └→ HTML → 레이아웃 시각화 이미지 생성 7. 응답 반환 └→ full_markdown, filtered_markdown, page_markdowns, page_processed_images, json, total_pages ``` ### 3.3 구성 요소별 상세 |구성 요소 |기술 스택 |포트 |역할 | |-----------|-----------------------------|----------------------|---------------------| |FastAPI |Python 3.12, uvicorn, aiohttp|10001 |API 서버, 파일 처리 오케스트레이션| |vLLM |vllm-openai:v0.17.0, Docker |10002 (외부) / 8000 (내부)|Chandra OCR 2 모델 서빙 | |LibreOffice|libre1~4 Docker 컨테이너 |- |Office → PDF 변환 | |복호화 서버 |별도 서버 (10.150.6.47) |9001 |암호화 문서 복호화 | ----- ## 4. Chandra OCR 2의 추론 방식 ### 4.1 프롬프트 구조 Chandra OCR 2는 자체 네이티브 프롬프트를 사용하여 HTML 형식의 레이아웃 출력을 생성합니다. 기존 DotsOCR의 레이아웃 JSON 프롬프트와는 근본적으로 다른 방식입니다. **OCR Layout 프롬프트 (핵심)** 모델에게 이미지를 HTML 레이아웃 블록으로 OCR하도록 지시합니다. 각 블록은 `
` 태그에 `data-bbox`(바운딩 박스, 0-1000 정규화)와 `data-label`(레이블)을 포함합니다. **지원 레이블 목록:** |레이블 |설명 | |-------------------------|-----------------| |Text |일반 텍스트 단락 | |Section-Header |섹션/챕터 제목 | |Table |테이블 (HTML 구조 보존) | |Image / Figure |이미지, 도표, 차트 | |Caption |이미지/테이블 캡션 | |Footnote |각주 | |Equation-Block |수식 블록 (LaTeX) | |List-Group |목록 (순서/비순서) | |Form |양식 (체크박스, 라디오 버튼)| |Code-Block |코드 블록 | |Page-Header / Page-Footer|머리글/바닥글 | |Table-Of-Contents |목차 | |Complex-Block |복합 레이아웃 | |Chemical-Block |화학식 (SMILES) | |Diagram |다이어그램 (Mermaid) | |Bibliography |참고문헌 | ### 4.2 모델 출력 형식 Chandra OCR 2는 HTML을 출력하며, 각 레이아웃 블록이 `
` 태그로 구분됩니다: ```html

반도체 시장 분석 보고서

HBM 수급 전망

글로벌 반도체 시장은 2024년 기준...

......
``` ### 4.3 출력 파싱 파이프라인 모델의 HTML 출력은 세 가지 형태로 변환됩니다: ``` Chandra HTML 출력 │ ├─→ parse_markdown() → Markdown 텍스트 │ - 수식: $...$ / $$...$$ │ - 테이블: HTML 유지 │ - 이미지: 설명 텍스트 │ ├─→ parse_chunks() → JSON 배열 │ - bbox: 실제 이미지 좌표 (0-1000 → 픽셀) │ - category: 레이블 │ - text: HTML 내용 │ └─→ draw_layout() → 시각화 이미지 - 레이블별 색상 구분 - 바운딩 박스 오버레이 ``` ----- ## 5. 배포 구성 ### 5.1 서버 환경 |항목 |값 | |--------------|------------------------------| |서버 |DGX H200 | |IP |10.150.6.159 | |GPU |NVIDIA H200 × 8 (141GB VRAM 각)| |Chandra 할당 GPU|#3 | |OS |Ubuntu | |Docker |24.0+ | |Docker Compose|v2 | ### 5.2 모델 가중치 ``` 경로: ~/projects/models/chandra-ocr-2/ 파일: ├── config.json ├── generation_config.json ├── model.safetensors # ~10.6GB ├── preprocessor_config.json ├── processor_config.json ├── tokenizer.json ├── tokenizer_config.json ├── chat_template.jinja └── video_preprocessor_config.json ``` ### 5.3 Docker Compose 구성 **vLLM 서버 (GPU)** ```yaml vllm-server: image: vllm/vllm-openai:v0.17.0 container_name: chandra-vllm environment: - NVIDIA_VISIBLE_DEVICES=3 - CUDA_VISIBLE_DEVICES=3 - HF_HUB_OFFLINE=1 - TRANSFORMERS_OFFLINE=1 command: > --model /models/chandra-ocr-2 --served-model-name chandra --dtype bfloat16 --max-model-len 32768 --max-num-seqs 64 --max-num-batched-tokens 8192 --enable-prefix-caching --gpu-memory-utilization 0.90 --trust-remote-code deploy: resources: reservations: devices: - driver: nvidia device_ids: ["3"] capabilities: [gpu] ``` **주요 파라미터 설명:** |파라미터 |값 |설명 | |------------------------|-----|--------------------------| |`max-model-len` |32768|입력+출력 합산 최대 토큰 수 | |`max-num-seqs` |64 |동시 처리 가능한 시퀀스 수 | |`max-num-batched-tokens`|8192 |배치당 최대 토큰 수 | |`gpu-memory-utilization`|0.90 |GPU 메모리 사용률 (90%) | |`enable-prefix-caching` |true |OCR 프롬프트가 동일하므로 캐싱으로 성능 향상| ### 5.4 FastAPI 서비스 현재 호스트의 가상환경에서 직접 실행 중: ```bash nohup bash -c 'uvicorn main:app --host 0.0.0.0 --port 10001 \ --workers 20 --log-level info | tee -a uvicorn.log' > nohup.out 2>&1 & ``` 컨테이너화 버전도 준비되어 있으며, `chandra-fastapi:latest` 이미지로 전환 가능합니다. ----- ## 6. API 사용 가이드 ### 6.1 엔드포인트 ``` POST http://10.150.6.159:10001/process-file/ ``` ### 6.2 요청 파라미터 |파라미터 |타입 |기본값 |설명 | |----------|----|-----|----------| |`file` |File|(필수) |업로드할 문서 파일| |`highqual`|bool|false|고해상도 처리 모드| ### 6.3 지원 파일 형식 |구분 |확장자 | |----------|-----------------------| |PDF |.pdf | |Word |.doc, .docx | |PowerPoint|.ppt, .pptx | |Excel |.xls, .xlsx | |이미지 |.jpg, .jpeg, .png, .bmp| ### 6.4 호출 예시 **Python:** ```python import requests import json API_URL = "http://10.150.6.159:10001/process-file/" with open("document.pdf", "rb") as f: response = requests.post(API_URL, files={"file": f}) result = response.json() # 전체 마크다운 print(result["full_markdown"]) # 페이지별 마크다운 for i, page_md in enumerate(result["page_markdowns"]): print(f"=== Page {i+1} ===") print(page_md) # 레이아웃 JSON print(json.dumps(result["json"], indent=2, ensure_ascii=False)) ``` **curl:** ```bash curl -X POST http://10.150.6.159:10001/process-file/ \ -F "file=@document.pdf" ``` **고해상도 모드:** ```bash curl -X POST "http://10.150.6.159:10001/process-file/?highqual=true" \ -F "file=@document.pdf" ``` ### 6.5 응답 필드 |필드 |타입 |설명 | |-----------------------|-----------------|--------------------------------------| |`job_id` |string |요청별 고유 식별자 (UUID) | |`filename` |string |업로드된 원본 파일명 | |`status` |string |처리 상태 (`"processed"`) | |`total_pages` |int |총 페이지 수 | |`full_markdown` |string |Base64 이미지 포함 전체 마크다운. 문서 원본 재현용 | |`filtered_markdown` |string |이미지 제외 텍스트 전용 마크다운. RAG/LLM 입력에 적합 | |`page_markdowns` |list[string] |페이지별 분리된 마크다운 배열 | |`page_processed_images`|list[string] |레이아웃 시각화 이미지 (Base64 PNG). OCR 품질 검증용 | |`json` |list[dict] / dict|구조화된 레이아웃 데이터 (bbox + category + text)| **JSON 필드 상세:** ```json { "bbox": [64, 66, 850, 105], "category": "Section-Header", "text": "

HBM 수급 전망

" } ``` - `bbox`: 바운딩 박스 `[x1, y1, x2, y2]` (픽셀 단위) - `category`: 레이아웃 블록 유형 - `text`: 해당 블록의 HTML 내용 ### 6.6 에러 응답 |HTTP 코드|상황 | |-------|------------------| |400 |지원하지 않는 파일 형식 | |413 |파일 크기 초과 (10MB 제한)| |500 |내부 서버 오류 | ----- ## 7. 운영 가이드 ### 7.1 서비스 상태 확인 ```bash # API 서버 상태 curl http://10.150.6.159:10001/ # vLLM 서버 상태 curl http://localhost:10002/health # 모델 정보 curl http://localhost:10002/v1/models # Docker 상태 docker ps | grep chandra # GPU 사용량 nvidia-smi ``` ### 7.2 로그 확인 ```bash # vLLM 로그 docker compose logs -f vllm-server docker compose logs --tail 100 vllm-server # FastAPI 로그 (호스트 실행 시) tail -f uvicorn.log # FastAPI 로그 (컨테이너 실행 시) docker compose logs -f fastapi ``` ### 7.3 서비스 재시작 ```bash # vLLM 서버 재시작 docker compose down vllm-server docker compose up -d vllm-server # FastAPI 재시작 (호스트 실행 시) kill $(lsof -t -i :10001) nohup bash -c 'uvicorn main:app --host 0.0.0.0 --port 10001 \ --workers 20 --log-level info | tee -a uvicorn.log' > nohup.out 2>&1 & ``` ### 7.4 트러블슈팅 |증상 |원인 |해결 | |----------------------------|-------------------|-------------------------------------------| |`context length is only N` |`max-model-len` 부족 |docker-compose.yml에서 값 증가 후 재기동 | |`'category' KeyError` |DotsOCR 프롬프트 사용 중 |Chandra 네이티브 프롬프트로 교체 확인 | |`Connection refused` (10002)|vLLM 모델 로딩 중 |로그 확인, 로딩 완료 대기 (1~3분) | |GPU OOM |동시 요청 과다 |`max-num-seqs`, `gpu-memory-utilization` 조정| |한글 깨짐 |이미지 해상도 부족 |`highqual=true` 또는 `max_pixels` 증가 | |Office 변환 실패 |LibreOffice 컨테이너 문제|`docker ps`로 libre1~4 상태 확인 | ----- ## 8. 폐쇄망 배포 절차 사내 폐쇄망 환경에서의 배포는 외부 PC에서 빌드 후 전송하는 방식으로 수행합니다. ### 8.1 전송 파일 목록 |파일 |용량 |설명 | |----------------------------|-------|---------------------------| |`vllm-openai-v0.17.0.tar.gz`|~4-5GB |vLLM 서버 Docker 이미지 | |`chandra-fastapi.tar.gz` |~1GB |FastAPI 서비스 Docker 이미지 (선택)| |`chandra-ocr-2-model/` |~10.6GB|모델 가중치 | |`docker-compose.yml` |- |서비스 구성 파일 | |소스 코드 (*.py) |- |FastAPI 애플리케이션 코드 | ### 8.2 배포 순서 ``` [외부 PC (인터넷 가능)] 1. 모델 가중치 다운로드 (huggingface_hub) 2. Docker 이미지 Pull/Build 3. docker save | gzip → tar.gz 4. USB/외장HDD로 전송 [폐쇄망 서버] 5. docker load < *.tar.gz 6. 모델 파일 배치 7. docker-compose.yml 배치 8. docker compose up -d 9. 동작 확인 ``` ### 8.3 폐쇄망 필수 환경변수 ```bash HF_HUB_OFFLINE=1 # HuggingFace Hub 외부 접속 차단 TRANSFORMERS_OFFLINE=1 # Transformers 외부 접속 차단 ``` ----- ## 9. 소스 코드 구조 ``` ~/projects/chandra/scripts/ ├── main.py # FastAPI 앱 (엔드포인트 정의) ├── config_chandra.py # 설정 (vLLM 주소, 프롬프트, 허용 타입 등) ├── tool_chandra.py # 문서 처리 핵심 로직 (프롬프트, 파싱, 시각화) ├── utils_chandra.py # 유틸리티 (이미지 처리, PDF 변환, LibreOffice 연동) ├── inference_chandra.py # vLLM 비동기 추론 모듈 ├── docker-compose.yml # Docker 서비스 구성 ├── Dockerfile # FastAPI 컨테이너 빌드용 └── requirements.txt # Python 의존성 ``` |파일 |역할 | |----------------------|-------------------------------------------------------------------| |`main.py` |FastAPI 앱. `POST /process-file/` 엔드포인트 정의. 기존 DotsOCR과 동일한 인터페이스 유지| |`config_chandra.py` |vLLM 서버 주소, 모델명, 토큰 제한, 이미지 리사이징 설정, 복호화 서버 주소 등 | |`tool_chandra.py` |Chandra 네이티브 프롬프트 정의, HTML→Markdown/JSON 파싱, 레이아웃 시각화, 복호화, 재시도 로직 | |`utils_chandra.py` |이미지 리사이징, PDF→이미지 변환, LibreOffice 연동, 마크다운 문법 정제 | |`inference_chandra.py`|vLLM OpenAI-compatible API 비동기 호출 | ----- ## 10. 참고 자료 |항목 |링크 | |---------------------------|-----------------------------------------------| |Chandra GitHub |https://github.com/datalab-to/chandra | |Chandra OCR 2 (HuggingFace)|https://huggingface.co/datalab-to/chandra-ocr-2| |vLLM 공식 문서 |https://docs.vllm.ai | |Qwen 3.5 모델 |https://github.com/QwenLM/Qwen3 | |olmOCR Benchmark |https://github.com/allenai/olmocr | |Datalab Playground |https://www.datalab.to/playground |