비전·음성·추천

DOMAIN / 41번째 글

차트와 표 읽기 — 그림 속 숫자를 값으로 되돌린다

차트 이미지와 복잡한 표에서 값을 뽑아낼 때 무엇이 왜 틀리는지 정리합니다. 축과 범례를 읽는 문제, 계층 머리를 가진 표가 한 줄로 펴질 때 생기는 손실, 그림에 바로 묻기와 값으로 되돌리기의 갈림, 그리고 상대 오차로 재는 평가입니다.

PALDYN Team15 MIN READ

지난 글에서 문서 하나에서 정해진 값을 뽑는 이야기를 했다. 거기서 "표에서 대부분의 시간이 간다"고 적고 넘어갔는데, 실제로 리포트 자동화를 붙여 보면 막히는 자리가 거의 늘 두 곳이다. 차트로 그려진 숫자와 머리가 여러 층인 표다.

둘은 겉보기에 다르지만 같은 문제를 공유한다. 원래 데이터가 있었고, 사람이 보기 좋게 배치하는 과정에서 그 데이터가 위치 정보 속으로 녹아들어 갔다. 막대의 높이, 셀이 놓인 행과 열, 병합된 머리 칸. 값을 되찾으려면 이 배치를 거꾸로 풀어야 한다.

차트는 이미 한 번 잃어버린 데이터다

막대그래프 하나를 만들 때 무슨 일이 일어났는지 생각해 보자. {"Q1": 62, "Q2": 88, "Q3": 104, "Q4": 141} 이라는 표가 있었고, 렌더러가 이걸 픽셀 높이로 바꿨다. 62는 69픽셀이 되었고, 축에 눈금 네 개가 붙었다.

모델이 이 그림에서 62를 되찾으려면 세 가지를 동시에 해야 한다. 축의 눈금 두 개를 읽어 픽셀과 값 사이의 비례 관계를 세우고, 막대의 밑변과 윗변 위치를 재고, 그 둘을 곱해야 한다. 사람이 눈대중으로 하는 일이지만 사람도 정확히는 못 한다 — 눈금이 0과 150뿐이면 62인지 65인지 구별할 수 없다.

차트를 읽는 두 갈래 — 그림에 바로 묻기와 값으로 되돌린 뒤 계산하기

그래서 실무에서 가장 먼저 물어야 하는 것은 모델이 아니라 데이터다. 그림을 만든 원본이 어디엔가 남아 있지 않은가. 사내 대시보드라면 그 뒤에 쿼리가 있고, 보고서 PPT라면 차트 개체 안에 원본 표가 들어 있으며(python-pptx로 chart.plots[0].categories와 series.values를 그대로 꺼낼 수 있다), 논문이라면 부록에 표가 있을 때가 많다. 그림을 읽는 것은 원본을 도저히 구할 수 없을 때의 마지막 수단이다.

무엇을 틀리는지는 종류가 정해져 있다

값이 틀리는 방식은 무작위가 아니다. 몇 가지로 모인다.

  • 눈금 사이 어림. 축 눈금이 성기면 그 사이 값을 못 정한다. 눈금이 촘촘하거나 막대에 값 라벨이 붙어 있으면 정확도가 확 올라간다.
  • 누적과 그룹의 혼동. 누적 막대에서 두 번째 구간의 값은 막대 전체 높이가 아니라 구간의 두께다. 그룹 막대와 누적 막대는 겉모습이 비슷하고, 여기를 뒤집으면 값이 두 배쯤 틀린다.
  • 범례 대응. 색이 다섯 개인 꺾은선에서 어느 선이 어느 계열인지는 범례의 색을 선의 색과 맞춰야 안다. 색이 비슷하거나 선이 겹치면 뒤바뀐다.
  • 로그 축과 잘린 축. y축이 0에서 시작하지 않거나 로그 눈금이면 "두 배 높으니 두 배 큰 값"이 성립하지 않는다. 사람도 자주 속는 자리다.
  • 파이 차트의 각도. 조각의 각도에서 비율을 되찾는 일은 막대 높이보다 훨씬 부정확하다. 값 라벨이 없는 파이 차트는 거의 못 읽는다고 보는 편이 낫다.

이 목록이 유용한 이유는 어떤 차트가 위험한지 미리 거를 수 있기 때문이다. 값 라벨이 붙은 단순 막대 차트는 잘 읽힌다. 값 라벨 없는 누적 막대에 로그 축이면 안 읽힌다. 파이프라인 앞단에서 차트 종류와 라벨 유무를 먼저 분류해 두면, 위험한 것만 사람에게 보낼 수 있다.

표는 한 줄로 펴는 순간 뜻을 잃는다

표는 차트와 문제의 성격이 다르다. 글자는 다 읽히는데 글자끼리의 관계가 사라진다.

계층 머리를 가진 표가 한 줄로 펴질 때 값이 어느 머리에 속하는지 사라진다

머리가 두 층인 표에서 12라는 셀은 "수도권"이자 "2025년"이자 "상반기"다. 이름 세 개가 붙는다. 그런데 이 표를 마크다운이나 CSV로 펴는 순간 머리 두 층이 서로 다른 줄로 갈라지고, 12가 어느 조합에 속했는지는 열의 자리 번호로만 남는다. 모델이 그 자리 번호를 세다가 한 칸만 밀리면 2025년 상반기 값이 2026년 값으로 보고된다.

여기에 병합 셀, 각주 기호가 붙은 머리, 소계 행, 단위가 머리에 적힌 경우("단위: 백만 원")가 겹치면 오류가 겹겹이 쌓인다.

고치는 방법은 단순하다. 자리 번호에 기대지 않는 모양으로 뽑는 것이다. 두 가지가 흔하다.

{"rows": [
  {"지역": "수도권", "연도": "2025", "반기": "상", "값": 12},
  {"지역": "수도권", "연도": "2025", "반기": "하", "값": 15},
  {"지역": "수도권", "연도": "2026", "반기": "상", "값": 18}
]}

이렇게 셀 하나를 한 줄로 펴고 머리를 전부 이름으로 붙이는 모양을 긴 형식(long format)이라고 한다. 셀 수만큼 줄이 늘어나 토큰은 더 들지만, 순서가 뒤섞여도 뜻이 안 변한다. 다른 하나는 표를 HTML로 뽑아 rowspan과 colspan을 그대로 살리는 방법이다. 원래 구조가 보존되고, 뒤에서 pandas의 read_html로 바로 받을 수 있다.

두 갈래 — 그림에 바로 묻기와 값으로 되돌리기

접근이 둘이고, 갈림이 뚜렷하다.

그림에 바로 묻기 값으로 되돌린 뒤 계산
하는 일 이미지 + 질문 → 답 이미지 → 표 → 코드로 계산
호출 수 1회 2회 이상
검산 불가능 중간 표를 사람이 볼 수 있다
계산 정확도 모델의 암산에 의존 코드가 하므로 정확
강한 자리 "가장 높은 분기는?" 같은 읽기 질문 합계·증감률·비교
약한 자리 여러 값을 조합하는 계산 표로 못 뽑히는 그림(산점도 등)

"가장 큰 값은?"처럼 한 번 보고 답하는 질문은 바로 묻는 편이 낫고, 값 두 개 이상을 조합하는 질문은 되돌리는 편이 낫다. 되돌리기를 표준으로 잡고, 표로 안 되는 그림에서만 바로 묻는 방식으로 떨어지는 구성이 실무에서 안정적이다.

되돌리기가 왜 나은지는 계산 부분에서 분명해진다. "4분기가 1분기의 몇 배인가"를 그림에 바로 물으면 모델은 141과 62를 읽는 일과 나누는 일을 한 번에 해야 하고, 둘 중 아무 데서나 틀릴 수 있다. 표로 뽑아 두면 나누기는 코드가 한다.

import json

table = json.loads(model_output)          # {"Q1": 62, ..., "Q4": 141}
ratio = table["Q4"] / table["Q1"]         # 2.274...
delta = (table["Q4"] - table["Q1"]) / table["Q1"] * 100
print(f"{ratio:.2f}배 · {delta:.1f}% 증가")

모델에게는 읽는 일만 시키고 세는 일은 시키지 않는다. 이게 차트·표 파이프라인에서 가장 값싸게 정확도를 올리는 방법이다.

평가는 정확 일치로 재지 않는다

차트에서 읽은 값은 눈금 사이 어림이 섞여 있으므로 정답과 소수점까지 같기를 요구하면 거의 다 오답이 된다. 그래서 이 분야의 표준 지표는 상대 오차 허용 정확도다. 예측값이 정답의 일정 비율 안에 들면 맞은 것으로 친다.

correct(y^,y)=1 ⁣[∣y^−y∣∣y∣≤τ]\text{correct}(\hat{y}, y) = \mathbb{1}\!\left[\frac{|\hat{y} - y|}{|y|} \le \tau\right]

ChartQA 계열 벤치마크가 쓰는 τ\tau 는 보통 0.050.05, 즉 5%다. 62를 60이라고 답하면 오차 3.2%이므로 맞은 것이고, 55라고 답하면 11.3%이므로 틀린 것이다. 이 임계값은 우리 쓰임새에 맞춰 다시 정해야 한다. 대략의 추세만 보는 요약이라면 10%도 넉넉하지만, 금액을 옮겨 적는 일이라면 5%도 못 쓴다 — 그런 자리는 애초에 차트를 읽을 게 아니라 원본을 찾아야 한다.

y=0y = 0 인 값이 섞이면 분모가 0이 되므로 예외를 정해 둔다. 보통 정답이 0이면 예측도 정확히 0일 때만 맞은 것으로 센다.

표 추출은 지표가 다르다. 셀 단위 정확도와 함께 구조 정확도를 본다. 셀 값은 다 맞았는데 행이 하나 밀렸다면 셀 정확도는 높게 나오지만 표로서는 못 쓴다. 실무에서는 문서 이해에서 쓴 것과 같은 방식으로, "이 표에서 뽑아야 하는 필드가 전부 맞았는가"를 표 하나 단위로 세는 완전 일치율을 함께 본다. 그 값이 자동화율의 상한이다.

뽑는 쪽보다 묻는 쪽을 고치는 편이 빠를 때가 있다

정확도가 안 나올 때 모델을 바꾸기 전에 볼 것이 몇 가지 있다.

해상도. 차트와 표는 잔글씨가 많아 입력 해상도에 특히 민감하다. 대부분의 비전 모델은 긴 변을 정해진 크기로 줄이는데, 그 과정에서 축 눈금 숫자가 뭉개진다. 큰 이미지를 타일로 잘라 넣는 방식을 지원하는 모델이라면 그 옵션을 켜고, 아니면 차트 영역만 잘라 확대해서 넣는다. 이것만으로 값 읽기 정확도가 눈에 띄게 올라가는 경우가 흔하다.

한 번에 하나만 묻기. "이 그림에서 모든 값을 뽑고 증감률도 계산해 줘"는 실패하기 좋은 요청이다. 값 뽑기와 계산을 나누면 어디서 틀렸는지도 보인다.

축과 범례를 먼저 물어보기. 값을 묻기 전에 "y축의 최솟값·최댓값·단위는?"을 먼저 뽑아 두면, 모델이 비례 관계를 명시적으로 세운 뒤에 값을 읽게 된다. 잘린 축과 로그 축을 여기서 걸러 낼 수 있다는 것이 더 큰 이득이다.

표 구조를 미리 알려 주기. 같은 양식의 보고서를 반복해서 처리한다면 열 이름과 단위를 프롬프트에 적어 준다. 모델이 구조를 추측할 필요가 없어진다.

정리

  • 차트와 표는 원본 데이터가 배치 속으로 녹아든 그림이다. 원본을 구할 수 있으면 그림을 읽지 않는다
  • 차트에서 틀리는 방식은 정해져 있다 — 눈금 사이 어림, 누적과 그룹의 혼동, 범례 대응, 잘린 축과 로그 축, 파이 차트의 각도
  • 표는 글자가 아니라 글자 사이의 관계를 잃는다. 머리를 이름으로 붙인 긴 형식이나 rowspan을 살린 HTML로 뽑으면 자리 번호에 안 기댄다
  • 값 두 개 이상을 조합하는 질문은 표로 되돌린 뒤 코드로 계산한다. 모델에게 읽는 일만 시킨다
  • 평가는 상대 오차 5% 허용이 기본이고, 임계값은 쓰임새에 맞춰 다시 정한다. 표는 완전 일치율을 함께 본다
  • 모델을 바꾸기 전에 해상도, 질문 쪼개기, 축·범례 먼저 묻기, 구조 알려 주기를 먼저 시도한다

읽어주셔서 감사합니다. 😊

LATEST

비전·음성·추천의 최신 글

비전·음성·추천2026.09.07

오프라인 강화학습 — 쌓인 로그만으로 정책을 배우기

실제 서비스에서 탐색은 곧 사용자에게 나쁜 행동을 해 보는 일입니다. 이미 쌓인 로그만으로 정책을 배우려 할 때 왜 Q값이 혼자 부풀어 오르는지, 그 부풀음을 누르는 세 갈래 대응, 행동 복제라는 기준선의 무게, 그리고 배포 전에 성능을 재는 일이 왜 가장 어려운지를 정리합니다.

18 MIN
비전·음성·추천2026.09.07

다국어 전이 — 라벨 없는 언어에서 모델이 동작하는 이유

영어 라벨만으로 학습한 분류기가 한국어 문장을 그대로 처리하는 일이 실제로 일어납니다. 여러 언어가 한 표현 공간에 겹쳐 놓이는 원리, 그 겹침이 무너지는 조건, 번역해서 학습할지 번역해서 추론할지 고르는 기준, 그리고 언어별로 나눠 재야 하는 이유를 정리합니다.

16 MIN
비전·음성·추천2026.09.07

정보 추출 — 글 한 덩이를 표 한 줄로 바꾸는 일

계약서와 이메일을 데이터베이스에 넣으려면 글에서 값을 뽑아 칸에 채워야 합니다. 개체명·관계·사건의 세 층위, 값을 정규화하는 일이 왜 절반인지, 근거 위치를 함께 남겨야 하는 이유, 규칙·전용 모델·언어 모델의 갈림길, 그리고 필드별로 재는 평가법을 정리합니다.

17 MIN