PaddleOCR v5 REC 파이프라인 재구축 회고
— Dict 불일치부터 Eval 노이즈까지, 왜 우리는 처음부터 다시 학습했는가
1. 개요
본 문서는 PaddleOCR v5 기반 REC(텍스트 인식) 모델을 커스터마이징·추가학습하는 과정에서 발생한
다수의 오류와 구조적 문제를 분석하고,
왜 기존 학습을 이어가지 않고 “처음부터 재학습”을 선택했는지,
그리고 어떻게 문제를 해결했는지를 정리한 기술 기록이다.
2. 문제의 시작 — “추가 학습이면 충분할 줄 알았다”
초기 목표는 다음과 같았다.
- 기존 PP-OCRv5 Korean REC 모델 기반
- 코드/한글/기호 인식에 특화된 추가 학습
- 기존 학습 결과를 최대한 활용 (fine-tuning)
그러나 결과적으로 fine-tuning이 불가능한 구조적 상태였음이 뒤늦게 드러났다.
3. 오류 원인 분류 (Root Cause Analysis)
[A] Dict(문자 사전) 불일치 — 가장 치명적인 원인
증상
- 추론 시 IndexError: list index out of range
- WordBox 추출 실패
- 학습은 되는데 추론에서 기본 모델로 fallback
원인
- 과거 학습 dict ≠ 추가 학습 dict
- dict 크기:
- 기존 pretrained: 약 18,000+
- 현재 학습: 639
- Head / Embedding 레이어 shape 불일치
Head FC: [120, 641] (현재) vs [120, 18385] (pretrained) Embedding: 645 vs 18389
➡️ Pretrained weight의 80~90%가 로드 불가
✅ 해결
- Dict를 **단일 소스(asset/rec_dict/character_dict.txt)**로 고정
- 학습 / Export / 서버 추론 모두 동일 dict 사용
- Dict 변경 시 재학습 강제 정책 도입
[B] Inference Config 자동 패치 & 덮어쓰기 문제
증상
- inference.yml / json에 Global 섹션 누락
- PaddleX pipeline에서 KeyError: 'Global'
- 커스텀 모델 로드 실패 후 기본 v5 모델로 자동 fallback
원인
- 학습 산출물과 무관한 템플릿 inference.yml을 런타임에 덮어씀
- 여러 dict 경로(dict_v2, 내부 dict 등) 혼용
✅ 해결
- 런타임 패치 / 템플릿 덮어쓰기 완전 제거
- Export 결과를 있는 그대로 신뢰
- 커스텀 모델 로드 실패 시:
- ❌ fallback
- ✅ 서버 시작 실패 (문제 은폐 방지)
[C] Pretrained 모델 URL 404 & “의미 없는 Pretrained”
증상
- 학습 시작 시 pretrained URL 404
- 로컬 pretrained 로드 시 warning 수백 개
- accuracy 초반 0.00% 장기 유지
원인
- 공식 URL 변경 (404)
- 설령 다운로드에 성공해도:
- Dict 크기 차이로 Head/Embedding 대부분 불일치
- 실제 매칭률 10~20% 수준
✅ 해결
- Pretrained 완전 제거
- Global.pretrained_model: null
- train from scratch 명시적 선택
- Epoch 수 증가 (40 → 80)
➡️ 결과적으로 성능이 더 안정적으로 상승
[D] Eval 라벨 노이즈 — “학습이 안 되는 것처럼 보이던 진짜 이유”
증상
- acc가 특정 epoch 이후 정체 또는 하락
- best_accuracy 갱신이 불규칙
- 학습 로그상 loss는 정상 감소
원인
Eval 라벨(rec_gt_test.txt)에 OCR 대상이 아닌 텍스트 다수 포함
실제 검출 결과:
- 전체 8,000줄 중 312줄 (3.9%)
- 포함 패턴:
- .json({ message: ... })
- return res.status(404)...
- error, null, undefined
- “유효하지 않은 …”
➡️ 모델 성능 문제가 아니라 평가 기준이 오염됨
✅ 해결
- 학습 중단 없이 eval 라벨 패턴 스캔
- Train 라벨은 유지 (42,000 / 100% 정상)
- Eval 성능 해석 시 노이즈 감안
- 이후 best_accuracy:
- ~0.10 → 0.14 이상으로 정상 상승
[E] DataLoader NoneType 에러 (일시적)
증상
- Maximum retry count (10) reached
- TypeError: object of type 'NoneType' has no len()
원인
- 특정 샘플 I/O 지연 또는 일시적 읽기 실패
- 파일 자체는 정상 (후속 스캔 100% 통과)
해결
- 전체 데이터 스캔 도구 추가
- bad sample 자동 격리 구조 마련
- 동일 에러 재발 없음
4. 왜 “처음부터 재학습”을 선택했는가
단순 추가학습이 불가능했던 이유 요약
항목상태
| Dict | ❌ 불일치 |
| Pretrained | ❌ 의미 없음 |
| Inference config | ❌ 덮어쓰기 |
| Eval 지표 | ❌ 오염 |
| 구조 신뢰성 | ❌ 낮음 |
➡️ 이어가는 학습은 시간 낭비 + 오류 증폭 가능성
5. 재학습 전략 (최종 안정 구조)
- PaddleOCR v5 최신 구조
- REC 전용 50,000 샘플 재생성
- Dict 639자 고정 & 해시 관리
- Pretrained 없이 학습
- Epoch 80
- GPU 학습 / CPU 추론 분리
- Eval 노이즈 모니터링 도구 상시 유지
6. 얻은 교훈 (중요)
- Dict는 모델의 “스키마”다
- 바뀌면 다른 모델이다
- Eval acc가 낮다고 바로 모델을 의심하지 말 것
- Fallback은 편하지만 문제를 숨긴다
- Pretrained는 “있다고 좋은 것”이 아니다
- 구조를 신뢰할 수 없으면 과감히 처음으로 돌아가라
7. 현재 상태 요약
- 학습 정상 진행 중
- best_accuracy 지속 갱신
- 구조/지표 모두 일관성 확보
- 재현 가능 파이프라인 완성
'3. 자습 & 메모(실전, 실습, 프로젝트) > 3-2 메모(실전, 프로젝트)' 카테고리의 다른 글
| [개인 공부] 강화 학습에 대한 스터디 노트 (0) | 2026.03.02 |
|---|---|
| [MEMO] 실사용 모드 (0) | 2026.02.08 |
| [PaddleOCR] 학습용 REC 데이터셋 생성 스크립트(업데이트 버전) (0) | 2026.01.04 |
| [PaddleOCR] 학습용 REC 데이터셋 생성 스크립트 (0) | 2026.01.02 |
| [MEMO] PaddleOCR 코드 문법 인식 모델 학습 (0) | 2026.01.01 |