TT Lab
블로그

한국어

시작하기
블로그

블로그

한국어 문서 OCR 비교 1: PP-OCRv5, PaddleOCR-VL-1.6, Unlimited-OCR-2 를 같은 60쪽으로 재 보니

상태: 실측 (2026-10-29 부터 10-30, 24GB 소비자용 GPU 한 장). 숫자는 모두 직접 잰 원자료(쪽마다 출력 원문, 측정 로그, 채점 스크립트)에서 가져왔습니다. 논문 수치는 원문을 열어 확인한 표·절 번호와 함께 적었고, 재지 못한 것은 「미실측」, 원문에서 찾지 못한 것은 「미확인」으로 표시합니다. 설치하며 겪은 일과 보안 메모는 2편에 따로 모았습니다.

목차

1. 무엇을 왜 쟀나

OCR(광학 문자 인식)은 사진이나 스캔한 쪽에서 글자를 뽑아내는 일입니다. 오래된 방식은 두 단계입니다. 먼저 쪽에서 글줄이 있는 자리를 찾고(검출), 찾은 줄을 하나씩 잘라 글자로 읽습니다(인식). 요즘은 이미지를 통째로 보고 마크다운을 바로 써 내는 비전 언어 모델 (VLM, 그림을 입력으로 받는 언어 모델)이 문서 OCR 의 순위표 위쪽을 차지합니다.

이번에 고른 엔진은 셋입니다. 셋 다 같은 회사(Baidu) 계열에서 나왔지만 쪽을 읽는 방식이 서로 다릅니다.

엔진 한 줄 우리가 쓴 판
PP-OCRv5 한국어 고전 검출+인식 파이프라인. 작은 전용 모델 둘 PaddleOCR 3.7.0, 검출 PP-OCRv5_server_det, 인식 korean_PP-OCRv5_mobile_rec
PaddleOCR-VL-1.6 레이아웃 모델이 쪽을 요소로 자르고, 0.9B VLM 이 요소마다 읽는다 PaddleOCR 3.7.0, PaddleOCR-VL-1.6
Unlimited-OCR-2 이미지를 시각 토큰으로 압축하고, 3B MoE 디코더가 여러 쪽을 한 번에 읽는다 baidu/Unlimited-OCR-2(2026-10-27 공개), Transformers 4.52.1

왜 직접 재야 했는가. 문서 OCR 의 대표 벤치마크에 한국어가 없기 때문입니다.

그래서 질문은 셋입니다. 한국어 문서에서 셋 중 무엇이 가장 정확한가. 얼마나 빠르고 메모리는 얼마나 드는가. 그리고 틀릴 때는 어떻게 틀리는가.

2. 세 엔진은 어떻게 다르게 읽나

세 엔진은 쪽을 이렇게 읽는다 왼쪽에서 오른쪽으로. 회색 글자는 우리가 쓴 설정 PP-OCRv5 한국어: 고전 파이프라인 글줄 검출 server_det 줄마다 인식 korean mobile_rec 위에서 아래로 줄 글자 잇기 작은 전용 모델 둘. 쪽 구조는 모른다 PaddleOCR-VL-1.6: 레이아웃 + 작은 VLM 레이아웃 PP-DocLayoutV3 요소마다 잘라 0.9B VLM 읽는 순서대로 마크다운 VLM 은 잘린 요소 하나만 본다 Unlimited-OCR-2: 광학 압축 + 긴 디코딩 DeepEncoder 16배 압축 3B MoE 디코더 활성 약 0.5B 여러 쪽을 한 번에 출력 SR-SWA: 단계마다 약 512토큰만 참조 Unlimited-OCR-2 의 한 쪽은 Gundam(타일) 모드, 여러 쪽은 Base 모드(쪽당 시각 토큰 273개)로 읽는다
그림: 세 엔진이 쪽을 읽는 방식

PP-OCRv5 한국어는 쪽 구조를 모릅니다. 검출기가 글줄 상자를 찾고, 인식기가 줄마다 글자를 냅니다. 출력은 줄 글자를 위에서 아래로 이은 것입니다. PaddleOCR 3.0 기술 보고서(arXiv 2507.05595, §2.1)에 따르면 이 계열은 전체 0.07B, 100MB 미만입니다(보고서의 기본 모델 기준이고, 한국어 인식 모델의 크기는 미확인입니다).

PaddleOCR-VL 은 일을 둘로 나눕니다(1.0 논문 arXiv 2510.14528, §2.1). 레이아웃 모델(1.5판부터 PP-DocLayoutV3)이 「어디에 무엇이 어떤 순서로 있는가」를 정하면, 0.9B VLM(NaViT 방식 동적 해상도 인코더 + ERNIE-4.5-0.3B)이 잘린 요소 하나의 내용만 읽습니다. VLM 이 쪽 전체를 한 번에 보지는 않습니다. 1.6판(arXiv 2606.03264)은 구조를 그대로 두고 데이터와 후학습만 바꿨습니다. 모델이 잘 틀리던 표본(체크포인트 8개와 왜곡 16종에서 예측이 갈리는 것, 고서·희귀 글자)을 골라 보강하고 CPT, SFT, GRPO 강화학습을 차례로 거쳤습니다(§3, §4).

Unlimited-OCR-2 의 뼈대는 DeepSeek-OCR(arXiv 2510.18234)의 「광학 압축」입니다. 인코더(DeepEncoder)가 1024×1024 이미지를 256개 안팎의 시각 토큰으로 16배 줄이고, 3B MoE(전문가 혼합, 토큰마다 일부 전문가만 쓴다) 디코더가 글을 씁니다. 2편 보고서(HF 공개 PDF, p.5-7)가 더한 것이 SR-SWA(희소 참조 슬라이딩 창 어텐션)입니다. 디코더가 다음 토큰을 낼 때마다 모든 쪽의 시각 토큰을 다 보지 않고, 그 쪽의 블록 몇 개와 직전 출력만 봅니다. 우리가 받은 설정값으로 계산하면 원격 12블록×32 + 로컬 128 = 512토큰이고, 모델 카드의 「512 tokens」와 맞습니다. 쪽이 늘어도 한 걸음의 계산량이 늘지 않게 하려는 장치입니다.

두 VLM 의 학습 데이터에도 흥미로운 고리가 있습니다. Unlimited-OCR-2 의 의사 정답(사람 대신 다른 모델이 만든 정답)은 MinerU2.5-Pro 와 PaddleOCR-VL-1.6 에서 뽑았습니다(2편 보고서 p.8). 이번 비교의 두 VLM 은 선생과 학생 관계이기도 합니다.

3. 시험 문서와 채점

정답을 아는 문서를 만들었다

실제 스캔 문서는 정답 글자를 사람이 일일이 쳐야 합니다. 그래서 반대로 했습니다. 정답 글자를 먼저 정하고 그 글자로 쪽을 그렸습니다. 본문은 TT Lab 의 기술 공부 노트에서 한글 비중이 40% 넘는 문단을 뽑았고, A4 크기(150dpi, 1240×1754 화소)에 Apple SD Gothic Neo 글꼴 하나로 그렸습니다. 60쪽은 이렇게 나눴습니다.

묶음 쪽 만든 방법 정답 글자 수(정규화 뒤)
일반 본문 8 한 단 본문, 26px 글자 9,833
흐린 사본 8 일반 본문 8쪽을 가우스 흐림 → 1.5도 회전 → 0.6배 축소 → JPEG 품질 35 9,833
한국어 표 4 머리글이 한국어인 6열 23행 격자 표 2,285
긴 문서 40 서로 다른 문단 40쪽, 24px 글자 48,878

흐린 사본은 일반 본문과 글자가 똑같습니다. 그래서 같은 엔진의 두 묶음 차이가 곧 「화질이 나빠져서 생긴 오류」입니다.

채점: 글자 오류율(CER) 두 가지

CER(Character Error Rate)은 정답을 출력으로 바꾸는 데 필요한 최소 편집(글자 넣기·빼기·바꾸기) 수를 정답 글자 수로 나눈 값입니다. 1%면 100글자에 하나꼴로 틀렸다는 뜻입니다. 편집거리는 Myers(1999)의 비트 병렬 알고리즘으로 구했습니다.

엔진마다 출력 꼴이 다르므로 먼저 정규화합니다. 유니코드 NFKC 로 맞추고, 마크다운 기호(#, *, | 등)와 HTML 태그, 모델이 붙이는 좌표 표식을 지우고, 공백과 줄바꿈을 모두 뺍니다. 띄어쓰기와 줄 나눔은 레이아웃에 따라 달라지므로 글자 순서만 비교합니다.

그리고 CER 을 두 번 냅니다.

둘을 따로 둔 이유는 엔진마다 문장 부호를 다루는 습관이 크게 달라서입니다. 한 숫자로 묶으면 「문장 부호를 자주 빼먹는 엔진」과 「한글을 잘못 읽는 엔진」이 같은 점수로 보입니다. 검색 색인처럼 글자만 맞으면 되는 용도와, 사람이 그대로 읽을 문서를 만드는 용도는 서로 다른 숫자를 봐야 합니다.

채점 함정: 좌표를 지우지 않으면 CER 이 16%

Unlimited-OCR-2 의 출력에는 블록마다 좌표가 붙습니다. 예를 들면 이런 꼴입니다.

<BLOCK>text[71, 96, 900, 160]<FORK>우리에게 의미는 「언젠가 휴대폰 안에서 …

처음 채점기는 <…> 태그만 지우고 text[71, 96, 900, 160] 는 남겼습니다. 그 결과 일반 본문 CER 중앙값이 16.3%(쪽마다 13.9%에서 17.5%)로 나왔습니다. 좌표 정규식 한 줄을 더하자 0.80% 가 됐습니다. 흐린 사본은 16.95% → 1.99%, 표는 8.38% → 0.17%입니다(같은 출력, 같은 정답으로 다시 계산). 20배 차이가 출력 꼴 하나에서 나온 셈입니다. VLM 출력을 채점할 때는 먼저 출력 원문 몇 쪽을 눈으로 읽고 정규화 규칙을 맞추는 것이 순서입니다.

4. 결과: 정확도, 속도, 메모리

엔진 묶음 쪽 전체 CER(중앙) 전체 CER(최대) 글자만 CER(중앙) 초/쪽 최대 GPU 메모리
PP-OCRv5 한국어 일반 본문 8 1.52% 2.37% 0.33% 0.36 2.27 GiB
흐린 사본 8 4.47% 4.89% 1.23% 0.20 2.27 GiB
한국어 표 4 0.00% 0.00% 0.00% 0.30 2.27 GiB
긴 문서(쪽마다, 40쪽) 40 1.41% 0.49% 0.34 2.27 GiB
PaddleOCR-VL-1.6 일반 본문 8 0.48% 1.71% 0.28% 13.81 5.80 GiB
흐린 사본 8 1.51% 2.68% 0.83% 14.04 5.80 GiB
한국어 표 4 0.00% 0.00% 0.00% 8.59 5.90 GiB
긴 문서(쪽마다, 40쪽) 40 0.53% 0.37% 14.39 5.80 GiB
Unlimited-OCR-2 일반 본문 8 0.80% 0.98% 0.75% 31.07 6.85 GiB
흐린 사본 8 1.99% 3.25% 1.76% 30.91 6.85 GiB
한국어 표 4 0.17% 0.71% 0.19% 21.52 6.85 GiB
긴 문서(10쪽 한 번에) 10 2.72% 2.01% 38.07 7.59 GiB
긴 문서(20쪽 한 번에) 20 2.59% 2.00% 41.21 8.81 GiB

읽는 법. 중앙값은 쪽마다 낸 CER 을 늘어놓은 가운데 값이고, 8쪽처럼 짝수면 위쪽 값(다섯 번째)을 썼습니다. 긴 문서는 한 덩어리로 채점해 값이 하나뿐입니다. 시간은 예열 한 쪽을 빼고 잰 평균이고, 긴 문서는 전체 시간을 쪽 수로 나눴습니다. 메모리는 각 프레임워크의 할당기가 기록한 최대값(PyTorch·Paddle 의 max_memory_allocated)이라 nvidia-smi 에 보이는 값보다 작습니다. 13개 묶음 모두 실패한 쪽은 없었습니다.

글자 오류율: 문장 부호를 빼면 순위가 바뀐다 ● 전체 CER(문장 부호 포함) ○ 글자만 CER(한글·영문·숫자) 일반 본문 8쪽(쪽마다 중앙값) PP-OCRv5 한국어 1.52 / 0.33 PaddleOCR-VL-1.6 0.48 / 0.28 Unlimited-OCR-2 0.80 / 0.75 흐린 사본 8쪽(쪽마다 중앙값) PP-OCRv5 한국어 4.47 / 1.23 PaddleOCR-VL-1.6 1.51 / 0.83 Unlimited-OCR-2 1.99 / 1.76 한국어 표 4쪽(쪽마다 중앙값) PP-OCRv5 한국어 0.00 / 0.00 PaddleOCR-VL-1.6 0.00 / 0.00 Unlimited-OCR-2 0.17 / 0.19 긴 문서 앞 10쪽(한 덩어리) PP-OCRv5 한국어 1.53 / 0.50 PaddleOCR-VL-1.6 0.58 / 0.39 Unlimited-OCR-2 2.72 / 2.01 0% 1% 2% 3% 4% 5% 오른쪽 숫자: 전체 / 글자만(%). 표는 세 엔진 모두 거의 0 긴 문서는 Unlimited-OCR-2 만 10쪽을 한 번에, 나머지는 쪽마다 읽음
그림: 글자 오류율: 전체 CER 과 글자만 CER

그림에서 짚을 곳은 셋입니다.

PaddleOCR-VL-1.6 이 네 묶음 모두에서 가장 정확했습니다. 일반 본문 전체 CER 0.48%는 1,230글자쯤인 쪽에서 여섯 글자 안팎을 틀린 정도입니다. 표는 PP-OCRv5 와 함께 4쪽 모두 0%였습니다.

문장 부호를 빼면 순위가 바뀝니다. 전체 CER 로는 Unlimited-OCR-2(0.80%)가 PP-OCRv5(1.52%)보다 낫습니다. 그런데 글자만 세면 PP-OCRv5 가 0.33%, Unlimited-OCR-2 가 0.75%로 뒤집힙니다. 흐린 사본에서는 더 뚜렷합니다. 전체 CER 은 PP-OCRv5 가 4.47%로 셋 중 가장 나쁘지만 글자만 CER 은 1.23%로 Unlimited-OCR-2(1.76%)보다 좋습니다. PP-OCRv5 의 오류는 대부분 문장 부호이고, Unlimited-OCR-2 의 오류는 대부분 글자입니다. 그림에서 두 점 사이 막대가 길면 문장 부호 오류가 많다는 뜻입니다. Unlimited-OCR-2 는 막대가 거의 없습니다.

여러 쪽을 한 번에 읽으면 Unlimited-OCR-2 의 오류가 세 배쯤 늘었습니다. 같은 긴 문서 앞 10쪽을 놓고 비교하면 PaddleOCR-VL-1.6 은 0.58%, PP-OCRv5 는 1.53%인데 Unlimited-OCR-2 는 2.72%입니다(앞 10쪽 값은 40쪽 출력을 정답 10쪽 끝에 맞춰 잘라 다시 계산했고, 40쪽 전체로 같은 방법을 쓰면 표의 0.53%·1.41%가 그대로 나옵니다). 한 쪽씩 읽을 때(일반 본문 0.80%)와의 차이는 해상도 모드에서 옵니다. 한 쪽은 타일을 나눠 자세히 보는 Gundam 모드로, 여러 쪽은 쪽마다 시각 토큰 273개인 Base 모드로 읽습니다. 5절에서 실제 예를 봅니다.

속도와 메모리: 같은 GPU, 배치 1, 서빙 엔진 없이 위 세 줄은 일반 본문 8쪽 평균, 아래 두 줄은 긴 문서 묶음 쪽당 걸린 시간 PP-OCRv5 한국어 0.36초 PaddleOCR-VL-1.6 13.81초 Unlimited-OCR-2 31.07초 UOCR-2, 10쪽 한 번에 38.07초 UOCR-2, 20쪽 한 번에 41.21초 최대 GPU 메모리(할당기 기준) PP-OCRv5 한국어 2.27 GiB PaddleOCR-VL-1.6 5.80 GiB Unlimited-OCR-2 6.85 GiB UOCR-2, 10쪽 한 번에 7.59 GiB UOCR-2, 20쪽 한 번에 8.81 GiB Unlimited-OCR-2 는 모델 카드가 「빠른 시험용」이라 부르는 Transformers 경로로 쟀다. 저자가 권하는 SGLang 서빙은 미실측
그림: 속도와 GPU 메모리

속도는 자릿수가 다릅니다. 일반 본문 한 쪽에 PP-OCRv5 는 0.36초, PaddleOCR-VL-1.6 은 13.81초(약 38배), Unlimited-OCR-2 는 31.07초(약 86배)가 걸렸습니다. 한 시간으로 단순 환산하면 1만 쪽, 약 260쪽, 약 116쪽입니다. 흐린 사본에서 PP-OCRv5 가 더 빠른 것(0.20초)은 이미지가 0.6배로 작아서입니다. 메모리는 PP-OCRv5 2.27 GiB, PaddleOCR-VL-1.6 5.80 GiB, Unlimited-OCR-2 는 가중치만 6.32 GiB 이고 한 쪽 6.85 GiB, 10쪽 7.59 GiB, 20쪽 8.81 GiB 로 쪽 수에 따라 늘었습니다. 24GB 카드라면 셋 모두 여유가 있습니다.

다만 VLM 둘의 시간은 우리 실행 방식의 시간이지 모델의 한계가 아닙니다. 6절에서 이유를 봅니다.

5. 실제 출력 비교

숫자 뒤에 있는 실제 출력을 봅니다. 아래 두 그림은 같은 2쪽의 같은 자리입니다. 위는 엔진이 받은 이미지를 화소 그대로 옮긴 조각이고, 아래는 세 엔진의 출력을 정답 위치에 맞춰 놓은 것입니다. 이 쪽은 오류 유형이 고루 모여 있어서 골랐습니다(일반 본문 2쪽의 CER 은 PaddleOCR-VL-1.6 0.09%, Unlimited-OCR-2 0.26%, PP-OCRv5 1.57%로 묶음 중앙값보다 조금 좋은 쪽입니다).

같은 쪽, 세 엔진의 실제 출력: 일반 본문 2쪽 위: 엔진이 받은 이미지 조각. 아래: 각 엔진의 출력 굵은 밑줄 = 틀리게 읽은 글자, 취소선 = 빠뜨린 글자 드문 받침: 「쟀」 PP-OCRv5 한국어 2026-10-13 에 졌습니다(5절 끝: 틀린 곳 1 PaddleOCR-VL-1.6 2026-10-13 에 잤습니다(5절 끝: 틀린 곳 1 Unlimited-OCR-2 2026-10-13 에 잤습니다(5절 끝: 틀린 곳 1 낫표 「 」 와 쉼표 PP-OCRv5 한국어 0.60 으로 '아마 탁구대,라고 했을 책상이, 틀린 곳 2 PaddleOCR-VL-1.6 0.60 으로 「아마 탁구대」라고 했을 책상이, 맞음 Unlimited-OCR-2 0.60 으로 「아마 탁구대」라고 했을 책상이, 맞음 뜻이 뒤집히는 「없 → 있」 PP-OCRv5 한국어 메시지도 없습니다. 맞음 PaddleOCR-VL-1.6 메시지도 없습니다. 맞음 Unlimited-OCR-2 메시지도 있습니다. 틀린 곳 1 띄어쓰기는 채점에서 빼므로 정답의 띄어쓰기로 보여 준다
그림: 같은 쪽, 세 엔진의 실제 출력: 일반 본문 2쪽
같은 쪽, 세 엔진의 실제 출력: 흐린 사본 2쪽 위: 엔진이 받은 이미지 조각. 아래: 각 엔진의 출력 굵은 밑줄 = 틀리게 읽은 글자, 취소선 = 빠뜨린 글자 드문 받침: 「쟀」 PP-OCRv5 한국어 2026-10-13 에 겠습니다(5절 끝: 틀린 곳 1 PaddleOCR-VL-1.6 2026-10-13 에 졌습니다(5절 끝: 틀린 곳 1 Unlimited-OCR-2 2026-10-13 에 겠습니다(5절 끝: 틀린 곳 1 낫표 「 」 와 쉼표 PP-OCRv5 한국어 0.60 으로 「아마 탁구대,라고 했을 책상이, 틀린 곳 3 PaddleOCR-VL-1.6 0.60 으로 「아마 탁구대,라고 했을 책상이, 틀린 곳 1 Unlimited-OCR-2 0.60 으로 「아마 탁구대」라고 했을 책상이, 맞음 뜻이 뒤집히는 「없 → 있」 PP-OCRv5 한국어 메시지도 없습니다. 틀린 곳 1 PaddleOCR-VL-1.6 메시지도 없습니다. 맞음 Unlimited-OCR-2 메시지도 있습니다. 틀린 곳 1 흐린 사본에서만: 「둘 → 돌」 PP-OCRv5 한국어 공개 가중치 둘(라켓 찾기 맞음 PaddleOCR-VL-1.6 공개 가중치 돌(라켓 찾기 틀린 곳 1 Unlimited-OCR-2 공개 가중치 돌(라켓 찾기 틀린 곳 1 띄어쓰기는 채점에서 빼므로 정답의 띄어쓰기로 보여 준다
그림: 같은 쪽, 세 엔진의 실제 출력: 흐린 사본 2쪽

드문 받침: 「쟀」은 셋 다 한 번도 못 읽었다

「쟀습니다」(쟀다, 재다의 과거형)는 일반 본문 8쪽에 7번 나옵니다. 세 엔진 모두 7번 다 틀렸습니다. 깨끗한 일반 본문에서도 그렇습니다.

「쟀」을 무엇으로 읽었나 일반 본문(7번) 흐린 사본(7번)
PP-OCRv5 한국어 졌 7 괜 4, 겠 3
PaddleOCR-VL-1.6 잤 6, 챙 1 졌 2, 채 2, 잤 1, 짧 1, 챗 1
Unlimited-OCR-2 잤 2, 잿 2, 썼 1, 겠 1, 잦 1 겠 3, 쪼 2, 켜 1, 챙 1

PP-OCRv5 는 깨끗한 쪽에서 언제나 「졌」으로 읽었습니다. 인식기는 정해진 글자 사전 안에서만 답을 고르는데, 「쟀」이 그 사전에 없으면 이렇게 가장 닮은 글자로 일관되게 바뀝니다(사전에 실제로 없는지는 미확인입니다). VLM 둘은 답이 흩어집니다. 비슷한 모양의 글자 가운데 언어 모델이 더 자주 본 쪽으로 기우는 것으로 보입니다. 일상 문서에 드물게 나오는 받침이라 세 엔진의 학습 데이터에 적었을 가능성이 큽니다.

비슷한 글자가 더 있습니다. 「힙」(읽힙니다, 밝힙니다, 힙 메모리)은 일반 본문에 5번 나오는데 Unlimited-OCR-2 는 5번 모두 「힘」이나 「립」으로 읽었고, PaddleOCR-VL-1.6 은 다 맞혔으며, PP-OCRv5 는 한 번 틀렸습니다. 「씁니다」의 「씁」은 Unlimited-OCR-2 만 「씽」으로 읽었습니다 (일반 본문 3번 중 2번, 흐린 사본 3번 모두). 반대로 「랠리」의 「랠」은 PP-OCRv5(앨)와 PaddleOCR-VL-1.6(램, 랄)이 틀리고 Unlimited-OCR-2 가 깨끗한 쪽에서 맞혔습니다. 드문 글자에서는 어느 엔진도 안전하지 않고, 엔진마다 약한 글자가 다릅니다.

뜻이 뒤집히는 오류: 「없습니다 → 있습니다」

Unlimited-OCR-2 는 「없」을 「있」으로 읽었습니다. 일반 본문에 「없」이 14번 나오는데 그중 2번이고, 흐린 사본에서도 같은 2번입니다. 그림의 예는 「왜 그런지 알려 주는 메시지도 없습니다」가 「메시지도 있습니다」가 된 것입니다. CER 로는 1,150글자 중 한 글자, 0.09%포인트에 불과하지만 문장의 뜻은 정반대가 됩니다. 두 Paddle 엔진은 14번 모두 맞혔습니다.

이 오류는 글자 모양만으로는 설명이 어렵습니다. 「없」과 「있」은 받침 모양이 꽤 다릅니다. 시각 근거가 약할 때 디코더의 언어 지식이 더 흔한 쪽을 채우는 오류로 보입니다. PP-OCRv6 논문(arXiv 2606.13108)도 VLM 계열 OCR 의 이런 「환각」을 문제로 듭니다. 계약서·검사 결과처럼 부정어 하나가 중요한 문서라면 VLM 출력은 사람이 검수하는 흐름을 전제로 해야 합니다.

문장 부호: PP-OCRv5 는 낫표를 바꾸고, 흐리면 지운다

PP-OCRv5 의 전체 CER 을 끌어올린 것은 거의 모두 문장 부호입니다. 오류 유형을 세어 보면(정규화 글자를 정렬해 다른 곳을 센 것) 일반 본문에서 「 를 ' 로 바꾼 것이 46번, 」 를 ] 로 바꾼 것이 42번이었습니다. 흐린 사본에서는 바꾸는 대신 지웠습니다. 마침표 삭제 60번, 「 삭제 39번, 쉼표 삭제 38번, 쉼표를 마침표로 바꾼 것 36번, 」 삭제 30번, 가운뎃점 · 삭제 28번입니다. 작고 흐린 점은 검출 단계에서 글자로 잡히지 않는 것으로 보입니다.

VLM 둘도 문장 부호를 틀리지만 횟수가 훨씬 적습니다. PaddleOCR-VL-1.6 은 흐린 사본에서 「 를 ‘ 나 ' 로 바꾼 것이 18번, Unlimited-OCR-2 는 흐린 사본에서 가운뎃점 삭제 18번, 「 를 ' 로 바꾼 것 11번이 가장 많았습니다.

채점기 쪽의 작은 흠도 하나 밝혀 둡니다. 정답의 줄표 – 를 세 엔진 모두 하이픈 - 으로 읽었는데, 정규화가 하이픈을 마크다운 기호로 보고 지우므로 「줄표 삭제」로 묶음마다 4번씩 세었습니다. 엄밀히는 줄표를 하이픈으로 바꾼 것이고, 세 엔진에 똑같이 들어가므로 순위는 바뀌지 않습니다.

여러 쪽 모드의 해상도 저하

Unlimited-OCR-2 가 긴 문서 10쪽을 한 번에 읽은 출력에는 한 쪽씩 읽을 때 보기 어려운 오류가 섞입니다. 「늘어납니다 → 놀아납니다」, 「체감 → 채갑」, 「멘션 → 댄선」, 「가벼운 → 바뀌온」, 「댓글 → 덧글」(3번), 「랠리 → 뭘리」 같은 것들입니다. 한 글자가 아니라 두세 글자가 함께 무너지고, 모양이 닮은 다른 낱말로 바뀝니다. 같은 쪽을 쪽마다 읽은 PaddleOCR-VL-1.6 의 앞 10쪽 CER 은 0.58%였습니다.

원인은 논문이 이미 짚었습니다. DeepSeek-OCR 표 3에서 획이 빽빽한 중국어는 같은 Base 모드에서 편집거리가 영어의 약 4배(0.205 대 0.054)이고, 토큰을 늘린 Gundam 모드에서 0.097로 좋아집니다. Unlimited-OCR 1편 보고서(arXiv 2606.23050, §5.4)도 40쪽이 넘는 문서의 오류 대부분이 Base 모드에서 작은 글자를 못 읽어서 생겼다고 적습니다. 한글도 받침이 있는 네모꼴 글자라 중국어 쪽에 가깝습니다. 우리 결과는 그 설명과 맞습니다. 쪽당 시각 토큰이 273개로 줄면 24px 한글의 받침이 뭉개집니다.

6. 왜 이렇게 느린가, 그리고 이 숫자로 말하면 안 되는 것

Unlimited-OCR-2 의 한 쪽 30초는 논문의 속도와 거리가 멉니다. 하지만 두 숫자는 종류가 다릅니다.

논문 숫자는 동시 처리량입니다. Unlimited-OCR 1편의 5,580 TPS(초당 토큰, §5.2)는 동시 요청 512개의 합계이고, 2편 표 4의 1,315 TPS 는 배치 16입니다. 요청 하나로 나누면 각각 약 11, 약 82 토큰/초입니다. 2편 그림 6(b)의 단계당 약 3.8ms 는 배치 3에서 잰 값입니다. 세 수치 모두 하드웨어가 밝혀져 있지 않습니다(미확인). 우리는 배치 1에서 초당 37.9토큰, 한 걸음 26.4ms 였습니다(일반 본문 8쪽의 출력 9,426토큰을 248.6초로 나눈 값).

하드웨어 몫은 크지 않습니다. 배치 1 디코딩은 매 걸음 가중치를 메모리에서 읽어야 하므로 메모리 대역폭에 묶입니다. 모델 카드가 시험한 A800 80GB 의 대역폭은 1,935 GB/s(공식 사양)이고 우리 GPU 는 약 936 GB/s 로, 차이는 2.1배쯤입니다. 활성 파라미터 약 0.57B 를 bf16(2바이트)으로 읽으면 한 걸음에 약 1.14 GB 이니, 우리 GPU 로도 이론상 한 걸음 약 1.2ms 가 하한입니다(추정). 실측 26ms 는 그 20배가 넘습니다. 병목은 하드웨어가 아니라 실행 경로입니다.

Transformers 경로는 「빠른 시험용」입니다. 모델 카드가 그렇게 부르고, 서빙에는 SGLang 을 권합니다. 우리가 받은 원격 코드(revision 5632329a)를 읽어 보면 이유가 보입니다. 토큰을 하나씩 내는 파이썬 반복문이고 CUDA graph 가 없습니다. MoE 층마다 tokens_per_expert.cpu() 로 GPU 와 CPU 가 동기화하고, 디코딩 어텐션은 고른 블록만 계산하지 않고 캐시 전체를 fp32 로 복사해 밀집 행렬곱을 한 뒤 마스크로 가립니다. 결과는 희소 어텐션과 같지만 계산은 밀집입니다. 그래서 20쪽 묶음이 10쪽 묶음의 2.16배 걸렸습니다(출력 토큰은 1.96배). 한 걸음이 27.7ms 에서 30.6ms 로 약 10% 길어진 몫입니다.

SR-SWA 가 약속하는 것은 「한 걸음이 일정하다」입니다. 총시간은 출력 토큰 수에 비례하므로 쪽 수에 선형으로 늘어나는 것이 정상입니다. SR-SWA 가 막는 것은 전체 어텐션의 제곱 증가이고, 2편 그림 6(b)에서 그 차이가 크게 벌어지는 곳은 50쪽에서 100쪽 이상입니다(100쪽에서 전체 어텐션 약 26.7ms 대 SR-SWA 약 3.9ms, 그림을 눈으로 읽은 값). 우리 10·20쪽 측정으로는 SR-SWA 의 이득을 검증할 수 없습니다.

PaddleOCR-VL 도 같은 사정입니다. 우리는 vLLM 이나 FastDeploy 서버 없이 Paddle 동적 그래프로 한 쪽씩 순서대로 읽었습니다. 1.0 논문 표 13은 A100 한 장에 512개 PDF 를 몰아 넣어 FastDeploy 1.62 쪽/초, vLLM 1.35 쪽/초를 보고합니다. 부록 표 A2·1.5판 표 A8에는 보급형 소비자 GPU 의 0.36에서 0.54 쪽/초도 있습니다. 우리 0.07 쪽/초(13.81초)와는 열 배 넘게 차이가 납니다. 이것도 묶음 처리량이라 바로 견줄 수는 없습니다.

그래서 이 결과로 말하면 안 되는 것들이 있습니다.

7. 무엇을 고를까

우리 실측이 닿는 범위 안에서만 권합니다. 한국어 한 단 본문, 격자 표, 24GB GPU 한 장, 배치 1 기준입니다.

하려는 일 고를 것 근거와 주의
많은 쪽을 빨리, 검색 색인용 PP-OCRv5 한국어 쪽당 0.36초, 2.27 GiB. 글자만 CER 0.33%(흐린 사본 1.23%). 낫표·마침표는 바뀌거나 사라진다
사람이 읽을 결과, 흐린 스캔 PaddleOCR-VL-1.6 네 묶음 모두 가장 정확(일반 0.48%, 흐린 1.51%). 우리 경로로 쪽당 14초, 대량이면 vLLM·FastDeploy 서빙을 따로 재 볼 것
쪽을 넘는 표·문단, 긴 책 우리 실측으로는 판단 보류 Unlimited-OCR-2 는 글자 정확도에서 셋 중 가장 약했고(여러 쪽 모드 2.6%대), 구조 장점은 재지 않았다. SGLang 경로로 따로 잴 것
부정어·숫자가 중요한 문서 어느 엔진이든 검수 전제 「쟀」은 셋 다 7번 모두 틀렸고, VLM 은 「없 → 있」처럼 뜻을 뒤집을 수 있다

고전 파이프라인과 VLM 을 섞는 길도 있습니다. 먼저 PP-OCRv5 로 전부 읽어 색인을 만들고, 사람이 읽을 쪽이나 낮은 신뢰도의 쪽만 PaddleOCR-VL-1.6 으로 다시 읽는 식입니다. 이 조합의 효과는 미실측입니다.

8. 한계

티티 한마디. 「쟀습니다」를 세 엔진이 스물한 번 읽어서 스물한 번 다 틀렸어요. 탁구공 속도를 쟀다고 쓰려다 잤다고 쓰면 곤란하잖아요! 그래서 OCR 결과는 숫자 하나로 고르지 않고, 무엇을 어떻게 틀리는지까지 보고 골라요.

참고

원문은 모두 2026-10-30 에 열어 확인했습니다. arXiv 는 HTML 판이라 쪽 번호 대신 절·표 번호를 적었습니다.

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다