로컬 모델의 현실
Qwen2.5-7B로 魔法少女ノ魔女裁判 챕터 1을 번역했다.
- 어색하다
- 하지만 플레이는 가능하다
- GPU는 불타고 있다
챕터 2 시작. 문제 발생.
싫어받다 (X)
미움받다 (O)로컬 7B 모델은 문맥을 이해 못 한다.
무슨 상황에서 나온말인지 구분을 못 한다.
다른 Unity 게임도 하고 싶었다
魔法少女ノ魔女裁判은 Naninovel 엔진이다. 다른 Unity 게임들도 번역하고 싶었다.
일반 Unity 게임은 텍스트가 MonoBehaviour에 직렬화되어 있다. UnityPy로는 안 읽힌다.
AssetsTools.NET이라는 구원자
UABE라는 도구의 핵심 라이브러리. TypeTree로 모든 Unity 버전 지원.
문제: C# DLL이다.
나는 Python을 쓴다.
해결: pythonnet
pip install pythonnet
# .NET을 Python에서 쓴다.
import clr
from System import Activator
from System.Reflection import Assembly
# AssetsTools.NET 로드
assembly = Assembly.LoadFrom("AssetsTools.NET.dll")
manager_type = assembly.GetType("AssetsTools.NET.AssetsManager")
manager = Activator.CreateInstance(manager_type)
# TypeTree DB 로드
manager.LoadClassPackage("classdata.tpk")
성공. 일반 Unity 게임도 텍스트 추출 가능.
Python에서 .NET DLL 호출하는 날이 올 줄이야.
API 모델 조사 (지갑을 열 시간)
로컬 7B로는 한계다. 돈을 쓰기로 했다.
1. Claude API (Anthropic)
- 장점: 번역 품질 최상
- 단점: 비싸다
- 특이사항: Prompt Caching으로 90% 할인
2. GPT-4 (OpenAI)
- 장점: 번역 품질 좋음
- 단점: Claude보다 비쌈
- 특이사항: Caching 없음 (돈 많이 나감)
3. DeepL API
- 장점: 번역 전문
- 단점: 커스텀 규칙 적용 어려움
- 특이사항: "魔法少女" 같은 고유명사 처리 애매
4. Google Translate
- 장점: 무료
- 단점: 품질 낮음 (이미 경험함)
- 특이사항: Ruby 태그 때문에 폭사
Claude API 선택 (Prompt Caching의 힘)
Prompt Caching이 결정적이었다.
system = [{
"type": "text",
"text": open("translation_rules_ja_ko.yaml").read(), # 5000토큰
"cache_control": {"type": "ephemeral"} # 5분 TTL
}]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
system=system,
messages=[{"role": "user", "content": japanese_text}]
)
비용:
- 첫 호출: $1.00 (전체 토큰)
- 캐시 히트: $0.10 (90% 절감!)
번역 규칙 5,000토큰을 매번 보낸다. 하지만 캐싱 덕분에 많이 비용 절감이 된다
번역 엔진 추상화
여러 API를 쉽게 바꿀 수 있게 했다.
# core/translation_engines.py
class BaseTranslator(ABC):
@abstractmethod
def translate(self, text, source_lang, target_lang):
pass
class ClaudeTranslator(BaseTranslator):
def translate(self, text, source_lang, target_lang):
# Claude API
pass
class GPT4Translator(BaseTranslator):
def translate(self, text, source_lang, target_lang):
# GPT-4 API
pass
class DeepLTranslator(BaseTranslator):
def translate(self, text, source_lang, target_lang):
# DeepL API
pass
GUI에서 엔진 선택 가능. 드롭다운 하나면 끝.
Translation Memory (중복은 NO)
같은 문장 두 번 번역하면 바보다.
# core/translation_memory.py
import sqlite3
import hashlib
class TranslationMemory:
def get(self, original_text, language_pair):
key = hashlib.md5(f"{original_text}:{language_pair}".encode()).hexdigest()
self.cursor.execute("SELECT translated FROM cache WHERE key=?", (key,))
result = self.cursor.fetchone()
return result[0] if result else None
def save(self, original_text, translated_text, language_pair):
key = hashlib.md5(f"{original_text}:{language_pair}".encode()).hexdigest()
self.cursor.execute(
"INSERT OR REPLACE INTO cache VALUES (?, ?, ?, ?)",
(key, original_text, translated_text, language_pair)
)
실제 비용:
- Prompt Caching: 90% 절감
- Translation Memory: 80-90% 중복 방지
- 최종: 원래 비용의 1-2%
지갑이 살았다.
품질 비교 (돈값을 하는가?)
魔法少女ノ魔女裁判 챕터 1을 다시 번역했다.
| 모델 | 품질 | 속도 | 비용 |
|---|---|---|---|
| Qwen 7B | 어색함 | 1.5시간 | 무료 (전기세 제외) |
| Claude | 자연스러움 | 5분 | $0.50 (캐싱 후) |
| GPT-4 | 좋음 | 7분 | $2.50 |
| DeepL | 괜찮음 | 3분 | $1.00 |
Claude 압도적 승리.
챕터 1 번역에 1.5시간 걸리던 게 5분으로 줄었다. GPU 팬도 조용해졌다.
배운 것
로컬 모델은 한계가 있다
7B로는 자연스러운 번역 어렵다. 문맥 이해가 약하다.Prompt Caching은 사기다
5,000토큰 규칙을 매번 보내도 90% 절감. 이게 기술이다.Translation Memory는 필수
같은 문장 두 번 번역하는건 바보짓. 80-90% 추가 절감.엔진 추상화의 중요성
오늘은 Claude, 내일은 GPT-4. 드롭다운 하나로 끝.돈을 쓸 때와 아낄 때를 구분하자
품질이 중요하면 돈 쓰기. 최적화로 90% 아끼기.
다음 편: 코드 2,600줄이라는 재앙
'개발관련' 카테고리의 다른 글
| Unity 게임 번역기 개발기 #6: 최적화와 크로스플랫폼 (0) | 2025.10.19 |
|---|---|
| Unity 게임 번역기 개발기 #5: RPG 메이커? 그것도 된다 (0) | 2025.10.18 |
| Unity 게임 번역기 개발기 #4: 개발은 디테일이다 (1) | 2025.10.12 |
| Unity 게임 번역기 개발기 #3: 코드가 괴물이 되다 (0) | 2025.10.11 |
| Unity 게임 번역기 개발기 #1: 이게 시작이었다 (0) | 2025.10.09 |