AI 프롬프트
데이터/SQL

영수증 테스트의 합성데이터 와 Golden Dataset 데이터 및 평가 검증

중년개발자
중년개발자

@loxo

1일 전

4

합성 데이터로 다양한 경우를 넓게 만들고, 실제 영수증으로 만든 Golden Dataset을 최종 평가 기준으로 사용하는 것입니다.

다국어 영수증 AI 검증 데이터 설계 및 샘플

1. 목적

이 문서는 Gemini Vision API를 이용한 다국어 영수증 분석 기능을 검증하기 위한 테스트 데이터 설계 방법과 샘플 데이터를 정의한다.

지원 대상 언어는 다음과 같다.

  • 한국어
  • 영어
  • 일본어
  • 다국어 혼합 영수증

검증 대상 기능은 다음과 같다.

  1. 영수증 문서 판별
  2. 언어 판별
  3. 상호 및 거래정보 추출
  4. 상품 항목 추출
  5. 수량, 단가, 금액 추출
  6. 할인, 세금, 포인트 추출
  7. 결제수단 추출
  8. 항목 합계 계산
  9. 최종 결제금액 검증
  10. 불확실한 정보에 대한 보수적 처리
  11. 이미지에 없는 정보의 생성 방지
  12. JSON 스키마 준수 여부 검증

2. 관련 용어

2.1 Synthetic Data

실제 데이터의 구조와 특징을 모방하여 인위적으로 생성한 데이터다.

한국어로는 일반적으로 다음과 같이 표현한다.

  • 합성 데이터
  • 인공 생성 데이터
  • 모의 데이터

영수증 이미지가 충분하지 않은 개발 초기 단계에서 다양한 영수증 상황을 만들어 테스트할 때 사용한다.

예시:

  • 한국어 편의점 영수증
  • 영어 식당 영수증
  • 일본어 편의점 영수증
  • 할인과 포인트가 함께 있는 영수증
  • 세율이 여러 개인 영수증
  • 일부가 잘린 영수증
  • 숫자가 흐린 영수증

2.2 Data Augmentation

기존 영수증 이미지를 변형하여 데이터 수를 늘리는 방법이다.

대표적인 변형 방법은 다음과 같다.

  • 이미지 회전
  • 원근 왜곡
  • 밝기 조절
  • 명암 조절
  • 그림자 추가
  • 반사광 추가
  • 흐림 효과
  • 압축 노이즈
  • 종이 구김
  • 일부 영역 가림
  • 이미지 일부 잘라내기
  • 배경 변경

원본 영수증 한 장으로 여러 촬영 환경을 재현할 수 있다.

2.3 Ground Truth

영수증 이미지에서 추출되어야 하는 정확한 정답 데이터다.

예를 들어 영수증에 다음 내용이 있다고 가정한다.

text
아메리카노 2 3,000 6,000 할인 1,000 합계 5,000

Ground Truth는 다음과 같다.

json
{ "items": [ { "name": "아메리카노", "quantity": 2, "unit_price": 3000, "amount": 6000 } ], "order_discount_total": 1000, "total_amount": 5000 }

2.4 Golden Dataset

사람이 직접 영수증 이미지와 추출 결과를 비교하여 정답임을 확인한 고품질 데이터셋이다.

Golden Dataset은 다음 용도로 사용한다.

  • 프롬프트 변경 전후 성능 비교
  • Gemini 모델 버전 변경 영향 확인
  • 회귀 테스트
  • 운영 배포 전 품질 검증
  • 오류 유형 분석
  • 모델별 성능 비교

합성 데이터는 초기 개발에 유용하지만 최종 품질 검증에는 실제 영수증 기반 Golden Dataset이 필요하다.

2.5 Edge Case Dataset

일반적인 영수증이 아니라 모델이 실패하기 쉬운 예외 상황을 모은 데이터셋이다.

예시:

  • 영수증 일부가 잘림
  • 숫자 한 자리가 흐림
  • 상품명이 두 줄로 출력됨
  • 수량과 단가가 없음
  • 세금이 총액에 포함됨
  • 세금이 총액에 포함되지 않음
  • 상품별 할인과 전체 할인이 함께 있음
  • 복합 결제
  • 영수증 두 장이 한 이미지에 존재
  • 동일 영수증 이미지가 중복됨
  • 영어와 일본어가 혼합됨
  • 달러 기호만 있고 국가가 불명확함
  • 날짜가 03/04/2026처럼 모호함

2.6 Adversarial Test Data

AI가 잘못 추측하도록 유도하는 데이터를 말한다.

예시:

  • 이미지에 $만 있고 국가 정보가 없음
  • 연도 없이 07/16만 표시됨
  • 상품명 옆 숫자가 수량인지 상품 코드인지 불명확함
  • 합계는 있지만 상품 목록이 잘림
  • 포인트 적립과 포인트 사용이 함께 있음
  • 세금이 포함되었는데 별도로 더하면 합계가 맞지 않음
  • 동일 금액이 Total과 Paid에 각각 반복됨

이 데이터는 AI가 정보를 만들어 내지 않는지 검증하는 데 중요하다.


3. 권장 데이터 구성

초기 검증 데이터는 다음 비율로 구성하는 것이 좋다.

분류권장 비율
일반적인 정상 영수증40%
이미지 품질 저하15%
계산 및 할인 예외15%
세금 예외10%
다국어 및 혼합 언어10%
문서가 아닌 이미지5%
공격적·추측 방지 검증5%

언어별 데이터는 다음과 같이 균형을 맞출 수 있다.

언어권장 비율
한국어40%
영어25%
일본어25%
혼합 언어10%

4. 데이터 파일 구조

권장 디렉터리 구조는 다음과 같다.

text
receipt-ai-dataset/ ├── README.md ├── dataset.jsonl ├── images/ │ ├── ko/ │ │ ├── receipt-ko-001.jpg │ │ ├── receipt-ko-002.jpg │ │ └── receipt-ko-003.jpg │ ├── en/ │ │ ├── receipt-en-001.jpg │ │ └── receipt-en-002.jpg │ ├── ja/ │ │ ├── receipt-ja-001.jpg │ │ └── receipt-ja-002.jpg │ ├── mixed/ │ │ └── receipt-mixed-001.jpg │ └── negative/ │ └── not-receipt-001.jpg ├── expected/ │ ├── receipt-ko-001.json │ ├── receipt-en-001.json │ └── receipt-ja-001.json └── results/ ├── gemini-response/ └── comparison/

5. 검증 데이터 레코드 형식

한 개의 테스트 데이터는 다음 구조를 사용한다.

json
{ "test_case_id": "receipt-ko-001", "name": "한국어 일반 카드 영수증", "description": "상품 두 개와 부가세, 카드 결제가 포함된 정상 영수증", "category": "normal", "language": "ko", "image_path": "images/ko/receipt-ko-001.jpg", "synthetic": true, "input_text_representation": null, "expected_output": {}, "validation_rules": [], "tags": [], "difficulty": "easy" }

주요 필드 설명

필드설명
test_case_id테스트 케이스 고유 ID
name사람이 확인하기 쉬운 테스트 이름
description검증 목적
categorynormal, edge_case, adversarial, negative 등
languageko, en, ja, mixed
image_path영수증 이미지 경로
synthetic합성 데이터 여부
input_text_representation이미지 생성 전 원본 영수증 텍스트
expected_output모델이 반환해야 하는 정답
validation_rules테스트별 추가 검증 규칙
tags할인, 세금, 포인트 등의 특징
difficultyeasy, medium, hard

6. 샘플 검증 데이터

샘플 1. 한국어 일반 영수증

테스트 목적

  • 한국어 판별
  • 상품명, 수량, 단가, 금액 추출
  • 부가세 포함 금액 처리
  • 카드 결제수단 추출
  • 합계 검증

영수증 원문 표현

text
좋은카페 강남점 사업자번호 123-45-67890 2026-07-16 14:32 영수증번호 000123 아메리카노 2 3,000 6,000 치즈케이크 1 5,500 5,500 합계 11,500 공급가액 10,455 부가세 1,045 신용카드 11,500 승인번호 12345678

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "ko", "detected_languages": [ "ko" ], "receipts": [ { "receipt_index": 1, "language": { "primary_language": "ko", "detected_languages": [ "ko" ] }, "merchant": { "merchant_name": "좋은카페", "branch_name": "강남점", "business_number": "123-45-67890" }, "transaction": { "receipt_number": "000123", "transaction_date": "2026-07-16", "transaction_time": "14:32:00", "raw_transaction_datetime": "2026-07-16 14:32", "currency": "KRW", "currency_raw": null }, "items": [ { "line_number": 1, "name": "아메리카노", "normalized_name": "아메리카노", "quantity": 2, "unit": null, "unit_price": 3000, "amount": 6000, "discount_amount": null, "calculated_expected_amount": 6000, "calculation_status": "matched" }, { "line_number": 2, "name": "치즈케이크", "normalized_name": "치즈케이크", "quantity": 1, "unit": null, "unit_price": 5500, "amount": 5500, "discount_amount": null, "calculated_expected_amount": 5500, "calculation_status": "matched" } ], "amounts": { "subtotal": null, "items_amount_sum": 11500, "supply_amount": 10455, "tax": 1045, "total_amount": 11500, "paid_amount": 11500, "calculated_total": 11500, "difference": 0 }, "payment": { "payment_method": "credit_card", "payment_method_raw": "신용카드", "approval_number": "12345678", "payment_amount": 11500 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true, "duplicate_adjustment_risk": false, "missing_item_amounts": false } } ] }

검증 규칙

json
[ "merchant.merchant_name은 좋은카페여야 한다", "items 배열의 길이는 2여야 한다", "items_amount_sum은 11500이어야 한다", "tax는 총액에 포함된 금액이므로 calculated_total에 다시 더하지 않아야 한다", "validation.status는 matched여야 한다" ]

샘플 2. 한국어 할인 및 포인트 사용 영수증

테스트 목적

  • 상품별 금액 계산
  • 주문 전체 할인
  • 포인트 사용
  • 할인 중복 차감 방지

영수증 원문 표현

text
스마트마트 우유 2 x 2,000 4,000 식빵 1 x 3,000 3,000 소계 7,000 회원할인 1,000 포인트사용 500 결제금액 5,500 체크카드 5,500 적립포인트 55P 보유포인트 2,100P

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "ko", "detected_languages": [ "ko" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "스마트마트" }, "items": [ { "line_number": 1, "name": "우유", "normalized_name": "우유", "quantity": 2, "unit_price": 2000, "amount": 4000, "calculated_expected_amount": 4000, "calculation_status": "matched" }, { "line_number": 2, "name": "식빵", "normalized_name": "식빵", "quantity": 1, "unit_price": 3000, "amount": 3000, "calculated_expected_amount": 3000, "calculation_status": "matched" } ], "amounts": { "subtotal": 7000, "items_amount_sum": 7000, "order_discount_total": 1000, "point_used": 500, "total_amount": 5500, "paid_amount": 5500, "calculated_total": 5500, "difference": 0 }, "payment": { "payment_method": "debit_card", "payment_method_raw": "체크카드", "payment_amount": 5500 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true, "duplicate_adjustment_risk": false, "missing_item_amounts": false }, "warnings": [] } ] }

핵심 검증 조건

text
적립포인트 55P와 보유포인트 2,100P는 금액 계산에서 제외해야 한다. 계산식: 7,000 - 1,000 - 500 = 5,500

샘플 3. 영어 식당 영수증

테스트 목적

  • 영어 판별
  • Tax와 Tip 분리
  • 세금 별도 가산
  • 카드 결제 검증

영수증 원문 표현

text
SUNNY TABLE Date: July 16, 2026 Receipt No: A-1042 Burger 2 x $8.50 $17.00 Coffee 1 x $3.00 $3.00 Subtotal $20.00 Sales Tax $1.60 Tip $3.00 Total $24.60 VISA $24.60 Auth Code: 839201

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "en", "detected_languages": [ "en" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "SUNNY TABLE" }, "transaction": { "receipt_number": "A-1042", "transaction_date": "2026-07-16", "transaction_time": null, "raw_transaction_datetime": "July 16, 2026", "currency": "USD", "currency_raw": "$" }, "items": [ { "line_number": 1, "name": "Burger", "normalized_name": "Burger", "quantity": 2, "unit_price": 8.5, "amount": 17, "calculated_expected_amount": 17, "calculation_status": "matched" }, { "line_number": 2, "name": "Coffee", "normalized_name": "Coffee", "quantity": 1, "unit_price": 3, "amount": 3, "calculated_expected_amount": 3, "calculation_status": "matched" } ], "amounts": { "subtotal": 20, "items_amount_sum": 20, "tax": 1.6, "tip": 3, "total_amount": 24.6, "paid_amount": 24.6, "calculated_total": 24.6, "difference": 0 }, "tax_breakdown": [ { "label": "Sales Tax", "tax_rate": null, "taxable_amount": null, "tax_amount": 1.6, "included_in_total": false, "raw_text": "Sales Tax $1.60" } ], "payment": { "payment_method": "credit_card", "payment_method_raw": "VISA", "card_brand": "VISA", "authorization_code": "839201", "payment_amount": 24.6 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true } } ] }

샘플 4. 일본어 세율 혼합 영수증

테스트 목적

  • 일본어 판별
  • 일본 소비세 8%와 10% 구분
  • 세금 포함 금액 처리
  • 세금 중복 가산 방지

영수증 원문 표현

text
サンプルマート 東京店 2026年7月16日 18:40 おにぎり ※ 2点 150円 300円 弁当 1点 600円 600円 レジ袋 1点 10円 10円 合計 910円 10%対象 610円 内消費税 55円 8%対象 ※ 300円 内消費税 22円 現金 910円 お預り 1,000円 お釣り 90円

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "ja", "detected_languages": [ "ja" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "サンプルマート", "branch_name": "東京店" }, "transaction": { "transaction_date": "2026-07-16", "transaction_time": "18:40:00", "raw_transaction_datetime": "2026年7月16日 18:40", "currency": "JPY", "currency_raw": "円" }, "items": [ { "line_number": 1, "name": "おにぎり", "normalized_name": "おにぎり", "quantity": 2, "unit": "点", "unit_price": 150, "amount": 300, "tax_rate": 8, "tax_type": "tax_included", "calculated_expected_amount": 300, "calculation_status": "matched" }, { "line_number": 2, "name": "弁当", "normalized_name": "弁当", "quantity": 1, "unit": "点", "unit_price": 600, "amount": 600, "tax_rate": 10, "tax_type": "tax_included", "calculated_expected_amount": 600, "calculation_status": "matched" }, { "line_number": 3, "name": "レジ袋", "normalized_name": "レジ袋", "quantity": 1, "unit": "点", "unit_price": 10, "amount": 10, "tax_rate": 10, "tax_type": "tax_included", "calculated_expected_amount": 10, "calculation_status": "matched" } ], "amounts": { "items_amount_sum": 910, "tax": 77, "received_amount": 1000, "change_amount": 90, "total_amount": 910, "paid_amount": 910, "calculated_total": 910, "difference": 0 }, "tax_breakdown": [ { "label": "10%対象 内消費税", "tax_rate": 10, "taxable_amount": 610, "tax_amount": 55, "included_in_total": true, "raw_text": "10%対象 610円 内消費税55円" }, { "label": "8%対象 内消費税", "tax_rate": 8, "taxable_amount": 300, "tax_amount": 22, "included_in_total": true, "raw_text": "8%対象 300円 内消費税22円" } ], "payment": { "payment_method": "cash", "payment_method_raw": "現金", "payment_amount": 910 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true, "duplicate_adjustment_risk": false } } ] }

핵심 검증 조건

text
내소비세 55엔과 22엔은 총액 910엔에 이미 포함되어 있다. 잘못된 계산: 910 + 55 + 22 = 987 올바른 계산: calculated_total = 910

샘플 5. 한국어와 영어 혼합 영수증

테스트 목적

  • mixed 언어 판별
  • 브랜드와 상품명 원문 보존
  • 영어 결제수단 인식

영수증 원문 표현

text
COFFEE LAB 강남점 Order No. K1024 2026.07.16 09:20 아이스 라떼 1 5,500 Blueberry Muffin 1 4,000 Total 9,500 Mastercard 9,500

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "mixed", "detected_languages": [ "ko", "en" ], "receipts": [ { "receipt_index": 1, "language": { "primary_language": "mixed", "detected_languages": [ "ko", "en" ] }, "merchant": { "merchant_name": "COFFEE LAB", "branch_name": "강남점" }, "transaction": { "order_number": "K1024", "transaction_date": "2026-07-16", "transaction_time": "09:20:00", "raw_transaction_datetime": "2026.07.16 09:20", "currency": null, "currency_raw": null }, "items": [ { "line_number": 1, "name": "아이스 라떼", "normalized_name": "아이스 라떼", "quantity": 1, "unit_price": null, "amount": 5500, "calculation_status": "not_calculable" }, { "line_number": 2, "name": "Blueberry Muffin", "normalized_name": "Blueberry Muffin", "quantity": 1, "unit_price": null, "amount": 4000, "calculation_status": "not_calculable" } ], "amounts": { "items_amount_sum": 9500, "total_amount": 9500, "paid_amount": 9500, "calculated_total": 9500, "difference": 0 }, "payment": { "payment_method": "credit_card", "payment_method_raw": "Mastercard", "card_brand": "Mastercard", "payment_amount": 9500 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true } } ] }

핵심 검증 조건

text
문서에 원, ₩, KRW 등의 통화 근거가 없으므로 언어와 지점명만 보고 currency를 KRW로 추측해서는 안 된다.

샘플 6. 모호한 달러 통화

테스트 목적

  • 통화 추측 방지
  • $ 기호만으로 USD를 확정하지 않는지 검증

영수증 원문 표현

text
GREEN MARKET Milk $4.50 Bread $3.20 Total $7.70 Card $7.70

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "en", "detected_languages": [ "en" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "GREEN MARKET", "country": null }, "transaction": { "currency": null, "currency_raw": "$" }, "items": [ { "line_number": 1, "name": "Milk", "quantity": null, "unit_price": null, "amount": 4.5, "calculation_status": "not_calculable" }, { "line_number": 2, "name": "Bread", "quantity": null, "unit_price": null, "amount": 3.2, "calculation_status": "not_calculable" } ], "amounts": { "items_amount_sum": 7.7, "total_amount": 7.7, "paid_amount": 7.7, "calculated_total": 7.7, "difference": 0 }, "payment": { "payment_method": "credit_card", "payment_method_raw": "Card", "payment_amount": 7.7 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true }, "warnings": [ "통화 기호 $가 확인되지만 국가 또는 ISO 통화 코드가 없어 통화를 확정할 수 없음" ] } ] }

샘플 7. 날짜가 모호한 영수증

테스트 목적

  • MM/DD와 DD/MM 추측 방지
  • 원문 날짜 보존
  • 정규화 날짜 null 처리

영수증 원문 표현

text
CITY SHOP Date: 03/04/2026 Notebook 5.00 Total 5.00

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "en", "detected_languages": [ "en" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "CITY SHOP" }, "transaction": { "transaction_date": null, "transaction_time": null, "raw_transaction_datetime": "03/04/2026", "currency": null, "currency_raw": null }, "items": [ { "line_number": 1, "name": "Notebook", "amount": 5, "calculation_status": "not_calculable" } ], "amounts": { "items_amount_sum": 5, "total_amount": 5, "calculated_total": 5, "difference": 0 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": false }, "warnings": [ "날짜 03/04/2026의 월과 일 순서를 확정할 근거가 부족함" ], "unreadable_fields": [ { "field": "transaction.transaction_date", "raw_text": "03/04/2026", "reason": "월과 일 순서가 모호함" } ] } ] }

샘플 8. 연도가 없는 일본어 영수증

테스트 목적

  • 현재 연도 자동 삽입 방지
  • 불완전 날짜 처리

영수증 원문 표현

text
コンビニ東京 7月16日 08:10 水 120円 合計 120円 現金 120円

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "ja", "detected_languages": [ "ja" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "コンビニ東京" }, "transaction": { "transaction_date": null, "transaction_time": "08:10:00", "raw_transaction_datetime": "7月16日 08:10", "currency": "JPY", "currency_raw": "円" }, "items": [ { "line_number": 1, "name": "水", "amount": 120, "calculation_status": "not_calculable" } ], "amounts": { "items_amount_sum": 120, "total_amount": 120, "paid_amount": 120, "calculated_total": 120, "difference": 0 }, "payment": { "payment_method": "cash", "payment_method_raw": "現金", "payment_amount": 120 }, "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true }, "warnings": [ "거래 날짜에 연도가 표시되지 않아 transaction_date를 정규화하지 않음" ] } ] }

샘플 9. 복합 결제 영수증

테스트 목적

  • 카드와 포인트 복합 결제
  • 결제 상세 합산
  • 포인트를 할인과 결제수단으로 중복 처리하지 않는지 검증

영수증 원문 표현

text
테크스토어 USB 케이블 15,000 충전기 25,000 총액 40,000 포인트 결제 5,000 신용카드 결제 35,000 결제완료 40,000

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "ko", "detected_languages": [ "ko" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "테크스토어" }, "items": [ { "line_number": 1, "name": "USB 케이블", "amount": 15000, "calculation_status": "not_calculable" }, { "line_number": 2, "name": "충전기", "amount": 25000, "calculation_status": "not_calculable" } ], "amounts": { "items_amount_sum": 40000, "point_used": null, "total_amount": 40000, "paid_amount": 40000, "calculated_total": 40000, "difference": 0 }, "payment": { "payment_method": "mixed", "payment_method_raw": "포인트 결제 + 신용카드 결제", "payment_amount": 40000 }, "payment_details": [ { "payment_method": "points", "payment_method_raw": "포인트 결제", "amount": 5000, "raw_text": "포인트 결제 5,000" }, { "payment_method": "credit_card", "payment_method_raw": "신용카드 결제", "amount": 35000, "raw_text": "신용카드 결제 35,000" } ], "validation": { "status": "matched", "items_sum_verified": true, "total_verified": true, "payment_verified": true, "duplicate_adjustment_risk": false }, "warnings": [ "포인트 5,000은 할인 항목이 아니라 복합 결제수단으로 처리됨" ] } ] }

핵심 검증 조건

text
포인트 결제 5,000은 상품 총액에서 차감하는 할인으로 처리하면 안 된다. 결제 검증: 5,000 + 35,000 = 40,000

샘플 10. 일부가 잘린 영수증

테스트 목적

  • 보이지 않는 상품 생성 방지
  • 계산 데이터 부족 처리
  • partially_readable 판정

영수증 원문 표현

text
[영수증 상단과 상품 목록이 잘림] 할인 1,000 부가세 900 결제금액 9,900 카드 9,900

기대 결과

json
{ "document_status": "partially_readable", "receipt_count": 1, "primary_language": "ko", "detected_languages": [ "ko" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": null }, "items": [], "amounts": { "items_amount_sum": null, "order_discount_total": 1000, "tax": 900, "total_amount": 9900, "paid_amount": 9900, "calculated_total": null, "difference": null }, "payment": { "payment_method": "credit_card", "payment_method_raw": "카드", "payment_amount": 9900 }, "validation": { "status": "insufficient_data", "items_sum_verified": false, "total_verified": false, "payment_verified": true, "duplicate_adjustment_risk": false, "missing_item_amounts": true, "reason": "상품 목록과 소계가 이미지에서 잘려 최종 금액 계산이 불가능함" }, "warnings": [ "영수증 상단과 상품 영역이 이미지에 포함되지 않음" ], "unreadable_fields": [ { "field": "items", "raw_text": null, "reason": "상품 목록 영역이 잘림" }, { "field": "merchant.merchant_name", "raw_text": null, "reason": "상호 영역이 잘림" } ] } ] }

샘플 11. 계산 불일치 영수증

테스트 목적

  • 합계를 맞추기 위해 원본 금액을 변경하지 않는지 검증
  • mismatch 판정

영수증 원문 표현

text
TEST SHOP Item A 2 x 5.00 10.00 Item B 1 x 3.00 3.00 Total 14.00 Cash 14.00

기대 결과

json
{ "document_status": "readable", "receipt_count": 1, "primary_language": "en", "detected_languages": [ "en" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "TEST SHOP" }, "items": [ { "line_number": 1, "name": "Item A", "quantity": 2, "unit_price": 5, "amount": 10, "calculated_expected_amount": 10, "calculation_status": "matched" }, { "line_number": 2, "name": "Item B", "quantity": 1, "unit_price": 3, "amount": 3, "calculated_expected_amount": 3, "calculation_status": "matched" } ], "amounts": { "items_amount_sum": 13, "total_amount": 14, "paid_amount": 14, "calculated_total": 13, "difference": -1 }, "payment": { "payment_method": "cash", "payment_method_raw": "Cash", "payment_amount": 14 }, "validation": { "status": "mismatch", "items_sum_verified": true, "total_verified": false, "payment_verified": true, "duplicate_adjustment_risk": false, "missing_item_amounts": false, "reason": "상품 금액 합계는 13.00이지만 인쇄된 Total은 14.00임" }, "warnings": [ "상품 합계와 인쇄된 총액 사이에 1.00 차이가 있음" ] } ] }

샘플 12. 영수증이 아닌 이미지

테스트 목적

  • 영수증이 아닌 문서에서 정보를 억지로 추출하지 않는지 검증

입력 이미지 설명

text
카페 메뉴판 사진 아메리카노 3,000 카페라떼 4,000 바닐라라떼 4,500

기대 결과

json
{ "document_status": "not_a_receipt", "receipt_count": 0, "primary_language": "ko", "detected_languages": [ "ko" ], "overall_confidence": 0.98, "warnings": [ "가격 정보는 존재하지만 거래 일시, 합계 또는 결제정보가 없는 메뉴판으로 판단됨" ], "receipts": [] }

샘플 13. 숫자 일부가 흐린 영수증

테스트 목적

  • 불확실한 숫자 추측 방지
  • 일부 숫자를 0으로 대체하지 않는지 검증

영수증 원문 표현

text
MART Orange Juice 4.?0 Bread 2.50 Total 7.20

첫 번째 상품 금액의 가운데 숫자가 흐려서 판독할 수 없다고 가정한다.

기대 결과

json
{ "document_status": "partially_readable", "receipt_count": 1, "primary_language": "en", "detected_languages": [ "en" ], "receipts": [ { "receipt_index": 1, "merchant": { "merchant_name": "MART" }, "items": [ { "line_number": 1, "name": "Orange Juice", "amount": null, "raw_text": "Orange Juice 4.?0", "calculated_expected_amount": null, "calculation_status": "not_calculable", "confidence": 0.42, "reason": "금액의 가운데 숫자를 판독할 수 없음" }, { "line_number": 2, "name": "Bread", "amount": 2.5, "raw_text": "Bread 2.50", "calculation_status": "not_calculable", "confidence": 0.98 } ], "amounts": { "items_amount_sum": 2.5, "total_amount": 7.2, "calculated_total": null, "difference": null }, "validation": { "status": "insufficient_data", "items_sum_verified": false, "total_verified": false, "payment_verified": false, "missing_item_amounts": true, "reason": "일부 상품 금액을 판독할 수 없어 전체 계산이 불가능함" }, "unreadable_fields": [ { "field": "items[0].amount", "raw_text": "4.?0", "reason": "숫자 한 자리가 흐림" } ] } ] }

7. JSONL 형식의 간단한 샘플 데이터

실제 테스트 자동화에서는 한 줄에 하나의 JSON 객체가 있는 JSONL 형식을 사용할 수 있다.

파일명 예시:

text
receipt-evaluation-dataset.jsonl
jsonl
{"test_case_id":"receipt-ko-001","name":"한국어 일반 카드 영수증","category":"normal","language":"ko","synthetic":true,"image_path":"images/ko/receipt-ko-001.jpg","expected":{"document_status":"readable","receipt_count":1,"primary_language":"ko","merchant_name":"좋은카페","item_count":2,"items_amount_sum":11500,"total_amount":11500,"payment_method":"credit_card","validation_status":"matched"},"tags":["korean","card","tax_included"],"difficulty":"easy"} {"test_case_id":"receipt-ko-002","name":"한국어 할인 포인트 영수증","category":"calculation","language":"ko","synthetic":true,"image_path":"images/ko/receipt-ko-002.jpg","expected":{"document_status":"readable","receipt_count":1,"item_count":2,"items_amount_sum":7000,"order_discount_total":1000,"point_used":500,"total_amount":5500,"validation_status":"matched"},"tags":["discount","points","debit_card"],"difficulty":"medium"} {"test_case_id":"receipt-en-001","name":"영어 세금 팁 영수증","category":"calculation","language":"en","synthetic":true,"image_path":"images/en/receipt-en-001.jpg","expected":{"document_status":"readable","receipt_count":1,"currency":"USD","items_amount_sum":20.0,"tax":1.6,"tip":3.0,"total_amount":24.6,"validation_status":"matched"},"tags":["sales_tax","tip","visa"],"difficulty":"medium"} {"test_case_id":"receipt-ja-001","name":"일본어 복수세율 영수증","category":"tax","language":"ja","synthetic":true,"image_path":"images/ja/receipt-ja-001.jpg","expected":{"document_status":"readable","receipt_count":1,"currency":"JPY","items_amount_sum":910,"tax":77,"total_amount":910,"tax_included":true,"validation_status":"matched"},"tags":["japanese","tax_8","tax_10","cash"],"difficulty":"hard"} {"test_case_id":"receipt-mixed-001","name":"한영 혼합 영수증","category":"multilingual","language":"mixed","synthetic":true,"image_path":"images/mixed/receipt-mixed-001.jpg","expected":{"document_status":"readable","receipt_count":1,"primary_language":"mixed","detected_languages":["ko","en"],"currency":null,"items_amount_sum":9500,"total_amount":9500},"tags":["mixed_language","currency_unknown"],"difficulty":"medium"} {"test_case_id":"receipt-en-002","name":"달러 통화 모호성","category":"adversarial","language":"en","synthetic":true,"image_path":"images/en/receipt-en-002.jpg","expected":{"document_status":"readable","currency":null,"currency_raw":"$","items_amount_sum":7.7,"total_amount":7.7},"tags":["currency_ambiguity","no_country"],"difficulty":"hard"} {"test_case_id":"receipt-en-003","name":"날짜 순서 모호성","category":"adversarial","language":"en","synthetic":true,"image_path":"images/en/receipt-en-003.jpg","expected":{"transaction_date":null,"raw_transaction_datetime":"03/04/2026","total_amount":5.0},"tags":["ambiguous_date"],"difficulty":"hard"} {"test_case_id":"receipt-ja-002","name":"연도 없는 일본어 영수증","category":"edge_case","language":"ja","synthetic":true,"image_path":"images/ja/receipt-ja-002.jpg","expected":{"transaction_date":null,"transaction_time":"08:10:00","currency":"JPY","total_amount":120},"tags":["missing_year"],"difficulty":"medium"} {"test_case_id":"receipt-ko-003","name":"복합 결제 영수증","category":"payment","language":"ko","synthetic":true,"image_path":"images/ko/receipt-ko-003.jpg","expected":{"payment_method":"mixed","point_payment":5000,"card_payment":35000,"paid_amount":40000,"point_used":null},"tags":["mixed_payment","points","card"],"difficulty":"hard"} {"test_case_id":"receipt-ko-004","name":"상단이 잘린 영수증","category":"image_quality","language":"ko","synthetic":true,"image_path":"images/ko/receipt-ko-004.jpg","expected":{"document_status":"partially_readable","item_count":0,"items_amount_sum":null,"total_amount":9900,"validation_status":"insufficient_data"},"tags":["cropped","missing_items"],"difficulty":"hard"} {"test_case_id":"receipt-en-004","name":"합계 불일치 영수증","category":"calculation","language":"en","synthetic":true,"image_path":"images/en/receipt-en-004.jpg","expected":{"items_amount_sum":13.0,"total_amount":14.0,"calculated_total":13.0,"difference":-1.0,"validation_status":"mismatch"},"tags":["mismatch"],"difficulty":"medium"} {"test_case_id":"not-receipt-001","name":"카페 메뉴판","category":"negative","language":"ko","synthetic":true,"image_path":"images/negative/not-receipt-001.jpg","expected":{"document_status":"not_a_receipt","receipt_count":0},"tags":["negative","menu"],"difficulty":"easy"}

8. 검증 항목과 평가 기준

8.1 문서 판별 정확도

text
document_accuracy = 정확하게 판별한 문서 수 / 전체 문서 수

검증 대상:

  • readable
  • partially_readable
  • unreadable
  • not_a_receipt
  • multiple_receipts
  • duplicate_images

8.2 필드 정확도

각 필드별로 일치 여부를 측정한다.

text
field_accuracy = 정답과 일치한 필드 수 / 검증 대상 필드 수

주요 필드:

  • merchant_name
  • transaction_date
  • transaction_time
  • currency
  • item name
  • quantity
  • unit_price
  • amount
  • total_amount
  • paid_amount
  • payment_method

8.3 상품 항목 정확도

상품명은 OCR 특성상 완전 일치와 정규화 일치를 구분하는 것이 좋다.

text
exact_name_match: 원문 상품명과 완전히 일치 normalized_name_match: 공백 및 허용된 문자 정규화 후 일치

상품 개수에 대한 평가도 별도로 수행한다.

text
item_count_accuracy = 정확한 상품 개수를 반환한 테스트 수 / 전체 테스트 수

8.4 금액 정확도

금액은 문자열이 아니라 정규화된 숫자로 비교한다.

text
expected = 11500 actual = 11500 result = pass

소수 통화는 작은 부동소수점 오차를 고려할 수 있다.

text
absolute_error = abs(expected - actual) pass 조건: absolute_error <= 0.001

단, 통화의 최소 단위를 임의로 반올림해서는 안 된다.

8.5 계산 검증 정확도

다음 값을 비교한다.

  • items_amount_sum
  • calculated_total
  • total_amount
  • difference
  • validation.status

예시:

text
items_amount_sum = 7000 order_discount_total = 1000 point_used = 500 calculated_total = 5500 total_amount = 5500 validation.status = matched

8.6 Hallucination Rate

이미지에 존재하지 않는 값을 AI가 생성한 비율이다.

한국어로는 환각률 또는 허위 생성률이라고 표현할 수 있다.

text
hallucination_rate = 근거 없이 생성된 필드 수 / 모델이 값으로 반환한 전체 필드 수

영수증 분석에서는 정확도뿐 아니라 Hallucination Rate가 매우 중요하다.

특히 다음 필드를 검사한다.

  • currency
  • country
  • transaction_date
  • quantity
  • unit_price
  • tax_rate
  • merchant_name
  • payment_method
  • unreadable number

8.7 Null Precision

null로 반환해야 할 필드를 올바르게 null로 반환했는지 측정한다.

text
null_precision = 정답이 null이고 모델도 null로 반환한 필드 수 / 정답이 null인 전체 필드 수

이 지표는 모델이 추측하지 않는지를 평가한다.

8.8 JSON Schema 통과율

text
schema_pass_rate = JSON Schema 검증을 통과한 응답 수 / 전체 응답 수

검증 항목:

  • 유효한 JSON 여부
  • 필수 필드 존재 여부
  • 숫자 타입
  • 문자열 타입
  • boolean 타입
  • enum 값 준수
  • JSON 외부 문장 존재 여부

9. 권장 품질 기준

초기 개발 단계의 목표 예시는 다음과 같다.

지표초기 목표운영 권장 목표
JSON 파싱 성공률98% 이상99.9% 이상
문서 판별 정확도95% 이상99% 이상
상호명 정확도90% 이상97% 이상
상품명 정확도85% 이상95% 이상
상품 금액 정확도95% 이상99% 이상
총액 정확도98% 이상99.5% 이상
계산 상태 정확도95% 이상99% 이상
통화 정확도95% 이상99% 이상
Hallucination Rate2% 이하0.5% 이하
JSON Schema 통과율98% 이상99.9% 이상

10. 실제 운영 데이터로 발전시키는 방법

1단계: 합성 텍스트 데이터

먼저 영수증 원문과 기대 JSON을 작성한다.

text
영수증 텍스트 기대 JSON 작성 계산 결과 검증

2단계: 합성 이미지 생성

작성한 영수증 텍스트를 실제 영수증처럼 이미지로 만든다.

변형 요소:

  • 영수증 글꼴
  • 감열지 배경
  • 종이 너비
  • 줄 간격
  • 좌우 정렬
  • 로고
  • 구분선
  • QR 코드 형태
  • 바코드 형태

QR 코드와 바코드에는 실제 개인정보나 결제정보를 넣지 않는다.

3단계: 이미지 증강

동일 이미지를 여러 촬영 환경으로 변형한다.

text
원본 이미지 ├── 3도 회전 ├── 8도 회전 ├── 약한 흐림 ├── 강한 그림자 ├── 부분 반사광 ├── 원근 왜곡 ├── 하단 잘림 └── JPEG 압축

4단계: Gemini Vision API 실행

각 이미지에 동일한 시스템 프롬프트를 적용한다.

저장 대상:

  • 모델 이름
  • 모델 버전
  • 프롬프트 버전
  • 이미지 ID
  • 원본 응답
  • 파싱 결과
  • 처리 시간
  • 오류 여부

5단계: 자동 비교

모델 결과와 expected_output을 비교한다.

text
Expected JSON Field-by-field comparator Actual Gemini JSON

6단계: 실제 영수증 추가

합성 데이터만으로는 실제 촬영 환경을 완전히 재현할 수 없다.

운영 전에는 개인정보를 제거한 실제 영수증을 추가한다.

필수 처리:

  • 카드번호 마스킹
  • 전화번호 마스킹
  • 승인번호 마스킹
  • 회원번호 마스킹
  • 개인 이름 제거
  • 위치정보 검토

7단계: Golden Dataset 확정

사람이 실제 이미지와 기대 결과를 검수한다.

권장 검수 방식:

text
1차 라벨러 2차 검수자 불일치 협의 Golden Dataset 확정

11. 데이터 사용 시 주의사항

  1. 합성 데이터만으로 운영 품질을 판단하지 않는다.
  2. 정상 영수증만 생성하지 말고 실패하기 쉬운 데이터를 충분히 포함한다.
  3. 한국어, 영어, 일본어 데이터 양을 균형 있게 구성한다.
  4. 통화와 언어를 항상 연결하지 않는다.
  5. 날짜 형식의 지역별 차이를 포함한다.
  6. 세금 포함과 세금 별도 영수증을 모두 포함한다.
  7. 적립 포인트와 사용 포인트를 구분하는 데이터를 포함한다.
  8. 복합 결제 데이터를 포함한다.
  9. 일부가 잘리거나 흐린 영수증을 포함한다.
  10. 영수증이 아닌 이미지도 포함한다.
  11. 실제 개인정보를 합성 데이터에 넣지 않는다.
  12. 테스트 데이터와 운영 학습 데이터를 분리한다.
  13. 동일한 영수증이 학습 데이터와 평가 데이터에 동시에 들어가지 않도록 한다.
  14. 프롬프트를 수정한 후에는 전체 Golden Dataset으로 회귀 테스트한다.
  15. 모델 버전을 변경할 때도 동일 데이터셋으로 비교한다.

12. 권장 데이터 분리

데이터는 다음과 같이 분리한다.

text
전체 데이터 100% ├── 개발용 데이터 70% ├── 검증용 데이터 15% └── 최종 테스트 데이터 15%

프롬프트 기반 Gemini Vision API에서는 모델을 직접 학습하지 않더라도 다음과 같이 분리하는 것이 좋다.

text
prompt-development-set/ 프롬프트를 작성하고 수정할 때 사용 validation-set/ 프롬프트 후보를 비교할 때 사용 holdout-test-set/ 최종 성능 평가에만 사용

Holdout Test Set은 프롬프트 작성 과정에서 반복적으로 확인하지 않는 것이 좋다.

반복해서 확인하면 테스트 데이터에 맞춘 프롬프트가 되어 실제 성능을 과대평가할 수 있다.


13. 권장 테스트 케이스 목록

text
NORMAL-001 한국어 일반 현금 영수증 NORMAL-002 한국어 일반 카드 영수증 NORMAL-003 영어 일반 카드 영수증 NORMAL-004 일본어 일반 현금 영수증 LANG-001 한국어와 영어 혼합 LANG-002 일본어와 영어 혼합 LANG-003 숫자와 기호만 많은 영수증 TAX-001 한국 부가세 포함 TAX-002 영어 Sales Tax 별도 TAX-003 영어 VAT 포함 TAX-004 일본 소비세 8%와 10% TAX-005 과세와 면세 혼합 DISCOUNT-001 상품별 할인 DISCOUNT-002 전체 주문 할인 DISCOUNT-003 쿠폰 할인 DISCOUNT-004 포인트 사용 DISCOUNT-005 적립 포인트만 존재 DISCOUNT-006 할인과 포인트 동시 사용 PAYMENT-001 현금 결제 PAYMENT-002 신용카드 결제 PAYMENT-003 체크카드 결제 PAYMENT-004 모바일 결제 PAYMENT-005 포인트와 카드 복합 결제 PAYMENT-006 현금과 카드 복합 결제 IMAGE-001 약한 회전 IMAGE-002 강한 원근 왜곡 IMAGE-003 그림자 IMAGE-004 반사광 IMAGE-005 상단 잘림 IMAGE-006 하단 잘림 IMAGE-007 숫자 일부 흐림 IMAGE-008 구겨진 영수증 EDGE-001 수량 없음 EDGE-002 단가 없음 EDGE-003 상품명 두 줄 EDGE-004 동일 상품 여러 줄 EDGE-005 마이너스 금액 EDGE-006 취소 및 재결제 EDGE-007 거스름돈 존재 EDGE-008 Total과 Paid 반복 EDGE-009 동일 영수증 중복 이미지 EDGE-010 한 이미지에 영수증 두 장 ADVERSARIAL-001 통화 기호 $만 존재 ADVERSARIAL-002 통화 기호 ¥만 존재 ADVERSARIAL-003 날짜 03/04/2026 ADVERSARIAL-004 연도 없는 날짜 ADVERSARIAL-005 흐린 숫자 ADVERSARIAL-006 합계 불일치 ADVERSARIAL-007 세금 포함 여부 불명확 ADVERSARIAL-008 포인트 적립과 사용 혼재 NEGATIVE-001 메뉴판 NEGATIVE-002 가격표 NEGATIVE-003 명함 NEGATIVE-004 카드 전표가 아닌 광고지 NEGATIVE-005 일반 문서

14. 결론

영수증 데이터가 부족할 때 사용할 수 있는 핵심 방법은 다음 두 가지다.

text
Synthetic Data 실제 영수증과 유사한 데이터를 인위적으로 생성 Data Augmentation 기존 영수증 이미지를 다양한 촬영 환경으로 변형

최종 검증에는 다음 데이터가 필요하다.

text
Golden Dataset 사람이 이미지와 정답을 직접 검증한 고품질 평가 데이터

권장 진행 순서는 다음과 같다.

text
합성 텍스트 영수증 정답 JSON 작성 영수증 이미지 생성 회전·흐림·그림자 데이터 증강 Gemini Vision API 실행 정답 JSON과 자동 비교 실제 영수증 추가 사람 검수 Golden Dataset 완성 프롬프트 및 모델 회귀 테스트
#Synthetic Data#Golden Dataset#OCR#AI Evaluation#Data Augmentation

댓글 0

Ctrl + Enter를 눌러 등록할 수 있습니다
※ AI 다듬기는 내용을 정제하는 보조 기능이며, 최종 내용은 사용자가 확인해야 합니다.