지난 글의 결정 트리에서 첫 갈래가 「본문 글자가 파일 안에 들어 있는가」였다. 아니오로 빠지는 문서 — 스캔본, 팩스, 사진으로 찍은 계약서, 이미지로 내보낸 도면 — 는 글자를 꺼내 오는 것이 아니라 만들어 내야 한다. 그 일을 하는 것이 OCR이다.
만들어 낸다는 말이 중요하다. 앞 글의 텍스트 추출은 틀리면 순서가 어긋나거나 글자가 빠지는 정도였다. OCR은 틀리면 다른 글자가 들어간다. 그리고 그 글자는 원문에 없었다는 표시를 달고 오지 않는다.
OCR은 한 덩어리가 아니라 파이프라인이다
「OCR을 돌린다」고 하면 모델 하나를 부르는 것처럼 들리지만, 실제로는 단계가 여럿이고 각 단계가 다른 방식으로 틀린다.
앞 단계의 실수는 뒤에서 못 고친다. 기울기가 0.5도쯤 남아 있으면 글자 줄을 찾는 단계가 흔들리고, 줄이 흔들리면 인식 단계에 잘못 잘린 이미지가 들어간다. 이 상태에서 인식 모델을 더 좋은 것으로 바꿔도 별로 안 오른다. 그래서 인식률이 안 나올 때 가장 먼저 볼 것은 모델이 아니라 전처리 결과 이미지다.
표 선을 글자로 보는 실수가 특히 크다. 격자선이 있는 표에서 세로줄을 「1」이나 「l」로 읽으면 그 줄 전체의 셀 대응이 한 칸씩 밀린다. 숫자가 다른 항목으로 옮겨 붙는다는 뜻이다.
후처리는 있는 낱말만 되살린다. 사전과 규칙으로 「환볼」을 「환불」로 고치는 것은 잘된다. 그런데 「7일」이 「7밀」이 되거나 금액의 자릿수가 바뀐 것은 못 고친다 — 어느 쪽이 맞는지 판단할 근거가 사전에 없기 때문이다. 사전에 없는 숫자와 고유명사가 끝까지 남는 오류다.
정확도 98%가 청크 단위로 뜻하는 것
OCR 제품이 말하는 정확도는 대개 글자 단위다. 98%라고 하면 100자 중 두 자가 틀린다는 뜻이고, 그 숫자만 보면 꽤 괜찮아 보인다. 그런데 우리가 다루는 단위는 글자가 아니라 청크다.
100자짜리 청크가 한 글자도 안 틀릴 확률은 이렇게 계산한다.
| 글자 정확도 | 100자당 평균 오류 | 100자 청크가 무결일 확률 |
|---|---|---|
| 92% | 8자 | 0.02% |
| 98% | 2자 | 약 13% |
| 99% | 1자 | 약 37% |
| 99.5% | 0.5자 | 약 61% |
98%짜리 파이프라인에서는 청크 열 개 중 아홉 개에 오류가 하나 이상 들어 있다. 그리고 400자짜리 청크를 쓴다면 무결 확률은 0.3%로 떨어진다.
이 계산이 알려 주는 것은 「OCR 문서에서는 오류가 예외가 아니라 기본값」이라는 사실이다. 오류를 없애는 쪽으로 설계하면 안 되고, 오류가 있다는 전제로 설계해야 한다.
같은 오류가 층마다 다르게 드러난다
오류 두 개가 든 청크가 검색과 답변을 어떻게 지나가는지 보면, 어디를 재야 하는지가 나온다.
키워드 검색이 가장 크게 무너진다. BM25는 글자가 일치해야 점수를 준다. 「7일」을 찾는데 색인에 「7밀」이 들어 있으면 그 청크는 아예 후보에 안 든다. 하이브리드 검색에서 키워드 쪽이 강했던 문서 종류 — 조항 번호, 금액, 제품 코드 — 가 하필 OCR이 가장 자주 틀리는 자리이기도 하다.
벡터 검색은 견딘다. 오타가 몇 개 있어도 문장 전체의 뜻은 남으므로 청크는 여전히 검색된다. 좋아 보이지만 실은 이쪽이 함정이다. 검색 지표가 멀쩡하게 나오니 문제를 늦게 안다.
답변에서 틀린 채로 나간다. 모델은 근거 문서에 적힌 것을 옮긴다. 「7밀」이 근거에 있으면 그것을 옮기거나, 「7일」로 알아서 고쳐 적는다. 후자가 더 무섭다 — 이번엔 맞았지만 금액에서 같은 일이 일어나면 조용히 틀린 숫자가 나간다.
그래서 OCR을 쓰는 저장소에서는 검색 지표만으로 판단하면 안 된다. RAG 평가의 recall은 벡터 검색 덕에 잘 나오는데 답변만 틀리는 상태가 실제로 생긴다. 인식 품질을 따로 재는 지표가 하나 더 있어야 한다.
전통 OCR과 비전 모델
요즘은 쪽 이미지를 그대로 모델에 넣어 마크다운을 받는 방식도 쓴다. 둘은 틀리는 방식이 다르다.
| 전통 OCR | 비전 모델 | |
|---|---|---|
| 나오는 것 | 글자 + 좌표 + 신뢰도 | 마크다운 텍스트 |
| 표 | 격자선에 의존, 자주 밀린다 | 구조를 꽤 잘 잡는다 |
| 틀리는 방식 | 비슷한 모양의 글자로 바뀐다 | 그럴듯한 다른 문장을 적는다 |
| 신뢰도 | 글자마다 점수가 나온다 | 없다 |
| 좌표 | 남는다 | 대개 안 남는다 |
| 비용 | 쪽당 매우 낮음 | 쪽당 수십~수백 배 |
신뢰도와 좌표가 남는지가 실무에서 큰 차이다. 신뢰도가 있으면 「이 청크는 인식이 흔들렸다」를 표시할 수 있고, 좌표가 있으면 답변 옆에 원본 이미지의 그 자리를 띄워 사람이 눈으로 확인하게 할 수 있다. 계약서·의무기록처럼 확인이 필요한 문서에서는 이 둘이 정확도보다 중요할 때가 많다.
비전 모델은 표가 답의 근거인 쪽에서 값을 한다. 다만 없는 글자를 지어낼 수 있으므로, 숫자가 든 표에서는 전통 OCR 결과와 대조해 두 결과가 다른 셀만 사람이 보는 방식이 안전하다.
둘 중 하나를 고르는 것이 아니라 문서 종류로 나누는 것이 보통이다. 대량의 평범한 스캔본은 전통 OCR로, 표가 빽빽한 소수 문서는 비전 모델로 돌린다.
신뢰도 점수를 버리지 않는다
전통 OCR은 글자마다 신뢰도를 내주는데, 대부분의 파이프라인이 이것을 텍스트만 꺼내면서 버린다. 남겨 두면 할 수 있는 일이 셋이다.
청크 메타데이터로 저장한다. 청크 안 글자들의 평균 신뢰도와 최저 신뢰도를 함께 넣어 둔다. 나중에 「이 답의 근거가 얼마나 믿을 만한가」를 답할 근거가 된다.
낮은 청크를 검수 대기열로 보낸다. 평균이 기준 아래인 청크만 모으면 대개 전체의 몇 퍼센트다. 그것만 사람이 보거나 다시 스캔한다. 만 쪽을 다 볼 수는 없어도 300쪽은 볼 수 있다.
답변에 표시한다. 근거 청크의 신뢰도가 낮으면 「이 근거는 스캔 인식 결과이며 확인이 필요합니다」를 함께 낸다. 틀릴 수 있다는 사실을 숨기지 않는 것이 틀린 답을 확신 있게 내는 것보다 낫다.
한국어에서 특히 갈리는 자리
한글은 라틴 문자보다 OCR이 어렵다. 한국어 처리에서 다룬 이유와 겹치는 자리가 있다.
받침이 작고 빽빽하다. 「갈」과 「길」, 「몸」과 「음」처럼 획 하나가 다른 글자쌍이 많고, 해상도가 낮으면 그 획이 뭉개진다. 스캔 해상도 300dpi가 실무 하한으로 통하는 이유다.
옛 문서는 세로쓰기와 한자가 섞인다. 줄 검출 단계가 가로쓰기를 전제하고 있으면 세로쓰기 쪽에서 통째로 실패한다. 문서 집합에 옛 문서가 섞여 있으면 방향 판별을 앞에 하나 붙여야 한다.
도장과 필기가 활자를 덮는다. 계약서에서 흔한데, 덮인 자리의 글자는 되살릴 방법이 없다. 이 경우는 인식 결과를 고치려 들기보다 그 자리를 「판독 불가」로 표시해 두는 편이 낫다.
파이프라인을 설계할 때 챙길 것
원본 이미지를 지우지 않는다. OCR 결과만 남기면 나중에 더 좋은 모델이 나와도 다시 못 돌린다. 원본을 두면 재처리가 그냥 일이지만, 없으면 재스캔이라 사실상 불가능하다.
쪽 번호와 좌표를 청크에 남긴다. 답변에서 「이 문서 43쪽」까지 가리킬 수 있고, 검수할 때 그 자리를 바로 열 수 있다.
재처리를 전제로 색인을 만든다. 문서 하나만 다시 돌려 그 문서의 청크만 갈아 끼울 수 있어야 한다. 저장소 전체를 다시 만들어야만 고칠 수 있는 구조면 아무도 안 고치게 된다.
인식 품질 회귀 세트를 둔다. 사람이 정답을 적어 둔 쪽 50장이면 충분하다. 전처리 설정을 바꿀 때마다 이것으로 재면, 어느 문서 종류가 나빠졌는지가 바로 보인다.
여기까지가 글자를 되살리는 이야기다. 그런데 앞의 결정 트리에서 마지막 갈래로 남겨 둔 것이 하나 있다 — 표가 답의 근거가 되는 문서다. 표는 글자를 다 살려도 그것만으로는 안 되는 자리다.
정리
- OCR은 글자를 꺼내는 것이 아니라 만들어 낸다. 틀리면 다른 글자가 들어간다
- 앞 단계의 실수는 뒤에서 못 고친다. 인식률이 안 나오면 전처리 결과부터 본다
- 글자 정확도 98%는 100자 청크의 87%에 오류가 있다는 뜻이다. 오류는 예외가 아니라 기본값이다
- 키워드 검색은 크게 무너지고, 벡터 검색은 견디고, 답변은 틀린 채로 나간다
- 신뢰도와 좌표를 버리지 않으면 검수 대기열과 근거 표시가 가능해진다
- 원본 이미지를 남기고, 문서 하나만 다시 돌릴 수 있게 만든다
읽어주셔서 감사합니다. 😊

