AI 도서 번역을 위한 정밀 번역 및 언어 식별 구축 방법
요약
AI 도서 번역 서비스 LectuLibre가 겪은 언어 식별 오류 문제를 해결하기 위해 구축한 정밀 언어 탐지 파이프라인을 소개합니다. fastText, langdetect, cld3 등 다양한 탐지기를 결합한 다수결 투표 방식을 통해 언어 탐지 정확도를 85%에서 98%로 향상시켰습니다.
핵심 포인트
- 단일 모델의 한계를 극복하기 위해 다중 탐지기 결합 방식 채택
- fastText, langdetect, cld3를 활용한 투표 기반 언어 식별 구현
- 도서 수준의 탐지와 청크 단위 검증을 통한 2단계 접근 방식
- 커스텀 미세 조정(fine-tuned) fastText 모델 활용
AI 기반 도서 번역을 위한 정밀 번역 및 언어 식별 (precise translation and language identification) 문제를 어떻게 해결했는지에 대하여.
문제점: 쓰레기가 들어가면 쓰레기가 나온다 (Garbage In, Garbage Out)
우리가 AI 도서 번역 서비스인 LectuLibre를 처음 출시했을 때, 가장 어려운 부분은 문학적 품질을 위한 LLM 프롬프트 엔지니어링 (prompt engineering)일 것이라고 생각했습니다. 하지만 우리는 더 근본적인 장애물을 빠르게 발견했습니다. 업로드된 도서의 출발어 (source language)가 잘못 식별되면, 아무리 뛰어난 프롬프트 엔지니어링도 번역을 살려낼 수 없다는 사실이었습니다.
사용자들은 전 세계의 EPUB 및 PDF 파일을 업로드합니다. 어떤 파일들은 언어를 지정하는 메타데이터 (metadata)를 포함하고 있지만, 많은 파일이 그렇지 않습니다. 어떤 것들은 다국어 도서이거나, 서문이 다른 언어로 작성되어 있기도 합니다. Python의 langdetect 라이브러리를 사용한 우리의 초기 언어 탐지 (language detection) 방식은 실제 업로드된 파일에 대해 약 85%의 정확도만을 보였습니다. 그 15%의 오류율은 완전히 엉망이 된 번역, 사용자의 불만, 그리고 낭비되는 LLM API 크레딧을 의미했습니다.
우리는 훨씬 더 강력한 것, 즉 내부적으로 **정밀 번역 및 언어 식별 (precise translation and language identification)**이라 부르는 것이 필요했습니다. 우리가 이를 어떻게 구축했는지 소개합니다.
언어 탐지 파이프라인: 정확도 85%에서 98%로
우리의 첫 번째 본능은 높은 정확도로 알려진 fastText의 사전 학습된 언어 식별 (language identification) 모델과 같은 더 무거운 모델을 사용하는 것이었습니다. 하지만 도서 발췌본으로 테스트했을 때 새로운 문제에 직면했습니다. 다른 언어로 된 책 안에 포함된 짧은 문단이나 대화(예: 영어 소설 속의 프랑스어 구절)가 청크 (chunk) 단위의 탐지를 방해한다는 점이었습니다.
우리는 두 단계의 접근 방식이 필요하다는 것을 깨달았습니다. 즉, 신뢰도 점수 (confidence scoring)를 포함한 도서 수준의 언어 탐지와, 번역 전 청크 단위의 검증이 필요했습니다.
투표를 통한 다중 탐지기 결합
우리는 여러 탐지기를 실행하고 다수결을 선택하며, 가능한 경우 사용자가 지정한 언어로 대체(fallback)하는 LanguageDetector 클래스를 생성했습니다. 우리가 사용하는 탐지기는 다음과 같습니다:
- 공식
lid.176.bin모델을 사용하는 fastText (요청마다 로드하지 않고 한 번만 로드함) - 가볍고 긴 텍스트에 적합한 langdetect
- 짧은 문구(snippet)에서 잘 작동하는 Google의 cld3 (Compact Language Detector 3)
- 다국어 EPUB에서 우리가 직접 큐레이션한 10,000개의 도서 발췌문을 사용하여 미세 조정(fine-tuned)한 커스텀 fastText 모델 (곧 오픈 소스로 공개할 예정)
특정 텍스트에 대해, 우리는 각 탐지기(detector)로부터 상위 1위(top-1) 예측값을 가져오며, 그중 최소 3개가 일치하면 해당 결과를 신뢰합니다. 만약 결과가 동률(tie)일 경우, 전체 신뢰도(모델의 확률값)가 가장 높은 결과로 대체(fallback)합니다. 사용자가 업로드 중에 소스 언어를 명시적으로 설정한 경우 이를 존중하지만, 그럼에도 탐지를 실행하며 불일치가 심할 경우 경고를 보냅니다.
다음은 우리 탐지기의 단순화된 버전입니다:
import fasttext
import langdetect
import cld3
...
프로덕션 환경에서는 요청당 오버헤드를 피하기 위해 시작 시점에 fastText 모델을 로드합니다. 또한 후속 작업의 속도를 높이기 위해 도서별로 탐지 결과(처음 10,000자의 해시값과 무작위 샘플링 기반)를 캐싱(cache)합니다.
다국어 도서 처리하기
여러 언어가 포함된 도서(예: 대조 번역이 포함된 언어 교재)의 경우, 도서 수준에서의 탐지를 요구하지 않습니다. 대신, 먼저 지배적인 언어(페이지의 80% 이상을 차지하는 언어)를 식별하려고 시도합니다. 사용자가 번역을 요청하면, 소스 언어와 일치하는 청크(chunk)만 번역하고 나머지는 그대로 둡니다. 이를 위해서는 청크별 언어 식별이 필요합니다. 우리의 파이프라인은 각 청크(약 1,000 토큰)를 LLM에 보내기 전에 탐지를 실행하며, 대상 언어가 명확히 아닌 청크는 건너뜁니다. 이는 LLM이 외국어 구절 때문에 멈추는 것을 방지합니다.
LLM을 위한 청킹(Chunking): 비용을 들이지 않고 문맥 보존하기
대규모 언어 모델 (Large language models)은 번역에 탁월하지만, 토큰 제한 (token limits)과 토큰당 비용 문제가 있습니다. 책은 분량이 매우 길기 때문에, 전체를 한꺼번에 보내면 컨텍스트 윈도우 (context windows)를 초과하거나 API 예산을 초과하게 됩니다. 우리는 토큰 낭비를 최소화하면서도 정확한 번역을 위해 충분한 문맥을 유지할 수 있는 청킹 (chunking) 전략이 필요했습니다.
오버랩(Overlap)을 활용한 동적 청킹 (Dynamic Chunking)
우리는 토큰 수를 계산하기 위해 TikToken (OpenAI의 토크나이저)을 사용합니다 (유사한 방식인 Claude의 토크나이저에도 적용 가능합니다). 시스템 프롬프트와 응답을 위한 여유 공간을 확보하기 위해 최대 청크 크기를 3,500 토큰으로 설정했습니다. 또한 문맥의 연속성을 제공하기 위해 이전 청크와 200 토큰의 오버랩 (overlap)을 유지합니다. 이는 문단 경계에 걸쳐 있는 문장들을 처리할 때 매우 중요합니다.
import tiktoken
def chunk_text(text, max_tokens=3500, overlap=200):
...
그 다음, 각 청크를 소스 언어와 대상 언어를 포함한 시스템 프롬프트, 그리고 서식(마크다운, 특수 문자)을 보존하라는 지침과 함께 LLM에 전달합니다. 비용 효율성을 위해 Anthropic의 Claude 3 Haiku를 사용하지만, 모델이 임계값 미만의 신뢰도 점수를 반환하는 복잡한 구절의 경우 Claude Sonnet으로 전환(fallback)합니다.
속도 제한(Rate Limiting)을 적용한 비동기 번역 파이프라인
책 한 권 전체를 번역하려면 50개 이상의 청크가 필요할 수 있습니다. 우리는 이를 asyncio를 사용하여 병렬로 실행하지만, API 속도 제한 (rate limits)을 준수해야 합니다 (Anthropic의 경우 우리 등급에서는 초당 5회 요청이 제한입니다). 우리는 토큰 버킷 (token bucket) 속도 제한기와 세마포어 (semaphore)를 구현했습니다.
import asyncio
from aiolimiter import AsyncLimiter
...
또한 대규모 도서를 청크 그룹 단위로 배치(batch)하여 소수의 워커 (workers)로 처리합니다. 이를 통해 API 사용을 원활하게 유지하고 429 에러를 방지합니다.
품질 관리 및 폴백 (Quality Control and Fallback)
훌륭한 언어 감지 (Language detection) 및 청킹 (Chunking) 기술을 갖추더라도, LLM은 때때로 직역에 치중하여 문학적 뉘앙스를 놓치는 번역을 생성할 수 있습니다. 학술 서적의 경우에는 괜찮을 수 있지만, 소설의 경우에는 유창성 (Fluency)을 개선할 방법이 필요했습니다. 이에 따라 사용자가 "문학적 (literary)" 번역 모드를 선택한 경우에만, 각 번역된 청크를 두 번째 LLM 호출(더 높은 온도 (Temperature)를 설정한 Claude Haiku와 같은 더 작은 모델)을 통해 언어를 "다듬는 (polish)" 후처리 단계를 추가했습니다. 이는 비용을 증가시키지만 가독성 (Readability)을 크게 향상시킵니다. 저희는 약 30%의 사용자가 문학 모드를 선택한다는 것을 확인했습니다.
결과 및 교훈 (Results and Lessons Learned)
멀티 모델 언어 감지기를 배포한 후, 5,000개의 도서 발췌문 테스트 세트에 대한 언어 식별 정확도가 85%에서 98.2%로 급증했습니다. 오식별은 드문 예외 사례(예: 방언으로 된 매우 짧은 텍스트)로 줄어들었습니다. 실제 운영 환경에서는 잘못된 언어 감지 오류가 거의 제로에 가깝게 떨어졌으며, 번역이 엉망이라는 사용자 불만도 사라졌습니다.
중첩 (Overlap)을 활용한 청킹 전략은 불완전한 문장으로 인한 API 재시도 횟수를 40% 감소시켰으며, 문학적 후처리 다듬기는 소설 분야에서 (A/B 테스트 결과) 평균 사용자 가독성 점수를 15% 향상시켰습니다.
또한, 더 나은 언어 감지 덕분에 잘못된 번역에 크레딧을 낭비하지 않게 되었고, 동적 청킹 (Dynamic chunking)을 통해 API 호출당 더 많은 텍스트를 담을 수 있게 되어 API 비용을 약 20% 절감했습니다.
개발자를 위한 핵심 요약 (Key Takeaways for Developers)
- 단일 감지기를 신뢰하지 마세요. 언어 식별 (Language ID)에는 앙상블 방법 (Ensemble methods)이 훨씬 더 신뢰할 수 있습니다.
- 번역 전 항상 청크별 검증을 수행하세요. 혼합 언어 콘텐츠를 잡아내기 위함입니다.
- LLM 번역을 위한 청킹 시 중첩은 필수입니다. 저희 테스트 결과, 200 토큰의 컨텍스트 (Context)가 큰 차이를 만들었습니다.
- 속도 제한 (Rate limiting)과 비동기 (Async) 처리는 필수입니다. 이는 취약한 파이프라인을 견고한 파이프라인으로 바꿔줍니다.
남겨진 과제 (Open Questions)
우리는 여전히 다음과 같은 질문들을 탐구하고 있습니다: 고대 언어나 매우 전문적인 전문 용어(jargon)를 어떻게 처리할 것인가? 문장 경계(sentence boundaries)를 기반으로 청크 크기(chunk size)를 동적으로 조정할 방법이 있는가? 우리는 커뮤니티의 의견을 듣고 싶습니다. 특히 기술 매뉴얼이나 시(poetry)의 번역을 다뤄본 분들의 의견을 기다립니다.
LectuLibre의 번역 파이프라인은 계속해서 진화하고 있으며, 우리는 언어 식별 앙상블(language detection ensemble)과 큐레이션된 데이터셋(curated dataset)을 오픈 소스로 공개할 계획입니다. 유사한 것을 구축하고 계신다면, 연락해 주세요!
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기