Google TranslateGemma - 55개 언어 번역 모델과 EOS 이슈 해결

2026. 1. 22. 09:33·알쓸신잡

얼마 전 Google에서 TranslateGemma라는 모델이 오픈소스로 공개됐다. Gemma 3를 기반으로 한 번역 특화 모델인데, 55개 언어를 지원하고 텍스트뿐만 아니라 이미지 속 텍스트까지 추출해서 번역할 수 있다는 점이 인상적이었다. 게다가 4B, 12B, 27B 등 다양한 크기로 제공되어서 개인 노트북이나 클라우드 환경에서도 충분히 사용할 수 있다.

 

"이거 한번 써봐야겠다!" 싶어서 공식 문서대로 테스트해 보기로 했다. 그런데 이 모델을 돌려보다가 이상한 문제를 발견했다. 짧은 문장에 대한 번역을 생성하는데 응답 시간이 비정상적으로 오래 걸리는 것이다. 처음에는 "GPU가 느린가? 모델 자체가 무거운가?" 싶었는데, raw 로그를 확인해 보니 내부적으로 <end_of_turn> 토큰을 끝없이 생성하고 있었다. 알고 보니 EOS 토큰 설정을 명시하지 않으면 이런 일이 발생한다.

이 글에서는 TranslateGemma가 무엇인지, 그리고 이 모델에서 발견한 EOS 토큰 성능 이슈에 대해 정리해봤다. 왜 이런 문제가 발생하고, 어떻게 해결할 수 있는지 살펴보자.

TranslateGemma란?

TranslateGemma는 Google에서 개발한 경량 오픈소스 번역 모델 제품군이다. Gemma 3 모델을 기반으로 번역 태스크에 특화된 파인튜닝을 거쳤다.

주요 특징

특징 설명
기반 모델 Gemma 3 (Google의 최신 오픈소스 LLM)
모델 크기 4B, 12B, 27B 파라미터
지원 언어 55개 언어 (ISO 639-1 코드 지원)
입력 형식 텍스트, 이미지 (896x896)
컨텍스트 최대 2K 토큰
배포 환경 노트북, 데스크톱, 개인 클라우드

일반적인 번역 모델과 달리, TranslateGemma는 이미지에서 텍스트를 추출해서 번역할 수 있다는 점이 특별하다. 예를 들어 체코어로 된 도로 표지판 사진을 입력하면, 텍스트를 인식해서 독일어로 번역해 준다.

사용 예시

공식 문서에 나온 코드를 그대로 따라해봤다:

import torch
from transformers import AutoModelForImageTextToText, AutoProcessor

model_id = "google/translategemma-27b-it"
processor = AutoProcessor.from_pretrained(model_id)
model = AutoModelForImageTextToText.from_pretrained(model_id, device_map="auto")

# ---- 텍스트 번역 ----
messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "source_lang_code": "cs",      # 체코어
                "target_lang_code": "de-DE",   # 독일어
                "text": "V nejhorším případě i k prasknutí čočky.",
            }
        ],
    }
]

inputs = processor.apply_chat_template(
    messages, tokenize=True, add_generation_prompt=True,
    return_dict=True, return_tensors="pt"
).to(model.device, dtype=torch.bfloat16)

input_len = len(inputs['input_ids'][0])

with torch.inference_mode():
    generation = model.generate(**inputs, do_sample=False)

generation = generation[0][input_len:]
decoded = processor.decode(generation, skip_special_tokens=True)
print(decoded)
# 출력: "Im schlimmsten Fall kann es auch zum Platzen der Linse kommen."

 

이미지에서 텍스트 추출 + 번역도 가능하다:

# ---- 이미지 속 텍스트 추출 및 번역 ----
messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "source_lang_code": "cs",
                "target_lang_code": "de-DE",
                "url": "https://c7.alamy.com/comp/2YAX36N/traffic-signs-in-czech-republic-pedestrian-zone-2YAX36N.jpg",
            },
        ],
    }
]

inputs = processor.apply_chat_template(
    messages, tokenize=True, add_generation_prompt=True,
    return_dict=True, return_tensors="pt"
).to(model.device, dtype=torch.bfloat16)

with torch.inference_mode():
    generation = model.generate(**inputs, do_sample=False)

generation = generation[0][input_len:]
decoded = processor.decode(generation, skip_special_tokens=True)
print(decoded)
# 이미지 속 체코어 표지판 텍스트를 독일어로 번역

코드가 꽤 직관적이다. source_lang_code와 target_lang_code만 지정하면 되고, 이미지 번역도 type: "image"로 URL만 바꿔주면 된다.


문제의 발단: 왜 이렇게 느리지?

위의 코드를 실행해 봤다. 간단한 체코어 문장 하나를 독일어로 번역하는 건데, 응답 시간이 비정상적으로 오래 걸리는 것이다. (Blackwell Rtx 6000 pro server edition vram 96GB에서 테스트를 진행했다. )

예상:

  • 응답: "Im schlimmsten Fall kann es auch zum Platzen der Linse kommen."
  • 소요 시간: ~1초

실제:

  • 응답: "Im schlimmsten Fall kann es auch zum Platzen der Linse kommen." (정상적으로 보임!)
  • 소요 시간: 5~7초 (왜 이렇게 느리지?)

겉으로는 정상적인 번역 결과가 나오는데 시간만 엄청 오래 걸린다. "27B 모델이 원래 이렇게 느린가? GPU가 문제인가?" 싶어서 raw token ids를 찍어봤더니...

# 디코딩 전 raw token ids 확인
print(generation)
# tensor([..., 12847, 106, 106, 106, 106, 106, ..., 106])
# 106 = <end_of_turn> 토큰이 끝없이 반복됨!

print(f"생성된 토큰 수: {len(generation)}")
# 생성된 토큰 수: 256 (실제 번역은 20토큰인데!)

 

내부적으로 <end_of_turn> 토큰을 계속 생성하고 있었다! processor.decode(..., skip_special_tokens=True)가 이 토큰들을 텍스트로 변환하지 않아서 출력에는 안 보이는 것뿐, 실제로는 기본 max_new_tokens 설정에 도달할 때까지 불필요한 토큰을 계속 생성하고 있었던 거다.

처음에는 "내 코드가 잘못됐나?" 생각했는데, 여러 TranslateGemma 변형 모델(4B, 12B, 27B)과 다른 Gemma-3 IT 모델들에서도 똑같은 문제가 발생했다. 이건 분명 뭔가 잘못된 거다.


EOS vs end_of_turn: 뭐가 다른 건데?

이 문제를 이해하려면 먼저 EOS 토큰이 뭔지 알아야 한다.

EOS 토큰이란?

EOS(End of Sequence) 토큰은 언어 모델이 텍스트 생성을 멈춰야 할 시점을 알려주는 특수 토큰이다. 사람으로 치면 문장 끝에 찍는 마침표 같은 거라고 보면 된다.

Transformer 모델의 생성 메커니즘은 간단하다:

  1. 모델이 다음 토큰 예측
  2. 예측한 토큰이 EOS 토큰이면 → 생성 중단
  3. 아니면 → 계속 생성 (max_new_tokens 한계까지)

쉽게 말해, EOS 토큰이 없으면 모델은 "언제 멈춰야 하는지" 모른다는 것이다.

Gemma의 두 가지 EOS 토큰

Gemma 모델은 두 가지 버전이 있다:

  • PT (Pre-trained): 사전 학습만 된 베이스 모델
  • IT (Instruct-tuned): 대화형으로 파인튜닝된 모델

문제는 이 두 모델이 서로 다른 EOS 토큰을 사용한다는 것이다.

모델 타입 EOS 토큰 토큰 ID
PT (Pre-trained) <eos> 1
IT (Instruct-tuned) <end_of_turn> 106

IT 모델은 대화 턴이 끝날 때마다 <end_of_turn> 토큰을 사용한다. 이게 학습 데이터에 포함되어 있고, 모델도 이 토큰을 보고 "아, 내 차례 끝났구나" 하고 멈추도록 학습되었다.

하지만 HuggingFace의 모델 설정 파일(generation_config.json)에는 이 정보가 없었다.


왜 이런 일이 발생했나?

1. 관련 이슈와 공식 입장

이 문제는 2024년부터 커뮤니티에서 계속 제기되었다:

이슈 날짜 핵심 내용
Issue #32110 2024.07 Gemma 템플릿이 assistant 응답 끝에 EOS를 안 붙임
gemma-2-9b-it Discussion #46 2024 EOS token ids 업데이트 요청
gemma-3-1b-it Discussion #5 2024 동일 이슈 수정
Issue #38182 2025 Gemma-3 IT 모델의 EOS가 <end_of_turn>(106)인데 tokenizer는 <eos>(1)
gemma-3-27b-it Discussion #47 2025 모델 업데이트 후 생성 멈추지 않음

그런데 중요한 점이 있다. Hugging Face maintainer @Rocketknight1의 공식 답변을 보면:

"This isn't really a bug" (Issue #38182)

"This is the correct behaviour" (Issue #32110)

즉, 이것은 버그가 아니라 의도된 동작이다. 모델은 학습 시 <end_of_turn>으로 종료를 배웠지만, <eos> 토큰도 emit할 수 있다. 하지만 사용자가 명시적으로 eos_token_id를 설정하지 않으면 기본값으로 동작하면서 성능 문제가 발생할 수 있다.

2. 왜 기본 설정이 최적이 아닌가?

"버그가 아니라면 왜 이런 문제가 생기는 걸까?" 몇 가지 관점에서 생각해 볼 수 있다.

관점 1: PT vs IT 모델의 EOS 토큰 차이

Gemma 기술 보고서에 따르면:

  • PT 모델: <eos>(1) 사용
  • IT 모델: <end_of_turn>(106) 사용

IT 모델은 대화형 학습 데이터로 파인튜닝되어 <end_of_turn>을 종료 신호로 배웠다. 하지만 generation_config.json의 기본 eos_token_id는 여전히 1로 설정되어 있다.

이는 범용성을 위한 선택일 수 있다. 모델이 다양한 상황에서 유연하게 동작하도록 하기 위해 기본값을 보수적으로 설정한 것이다.

관점 2: max_new_tokens로 제어하는 설계

Google은 내부 테스트에서 아마 이렇게 사용했을 것이다:

  • max_new_tokens를 명시적으로 설정
  • 또는 코드에서 eos_token_id=106을 직접 전달

즉, 사용자가 적절한 파라미터를 설정한다는 가정 하에 설계되었을 수 있다.

관점 3: TranslateGemma의 기본 설정

TranslateGemma는 번역 특화 모델이지만, Gemma-3 IT의 기본 설정을 그대로 상속받은 것으로 보인다. 번역 태스크에서는 출력 길이가 예측 가능하므로 max_new_tokens로 제어하는 것이 일반적이지만, 이를 명시하지 않으면 불필요한 토큰 생성이 발생한다.


성능 영향은 얼마나 될까?

버그는 아니지만, EOS 토큰 설정을 최적화하지 않으면 성능에 심각한 영향이 있다.

영향 분석

영향 항목 최적화 전/후 차이 설명
추론 속도 ⚠️ 5~10배 느려짐 EOS 설정 O: 2-20 토큰 생성 후 종료
EOS 설정 X: 기본 max_new_tokens까지 계속 생성
GPU 메모리 ⚠️ 불필요한 KV 캐시 증가 불필요한 토큰을 계속 생성하느라 메모리 낭비
사용자 경험 ⚠️ 응답 지연 겉으로는 정상이지만 응답 시간이 수 배 느려짐
학습(Fine-tuning) ⚠️ 학습 효율 저하 파인튜닝 시 EOS를 올바르게 설정하지 않으면 학습이 비효율적

특히 첫 사용자가 이 문제를 놓치기 쉽다. 공식 예시 코드에는 eos_token_id 설정이 명시되어 있지 않아서, 그대로 사용하면 성능 이슈가 발생한다.

실제 영향 사례

TranslateGemma에서 실제 측정한 결과다:

# EOS 최적 설정 (eos_token_id 명시)
# "V nejhorším případě i k prasknutí čočky." (체코어) → 독일어 번역

generation = model.generate(**inputs, do_sample=False, eos_token_id=[1, 106])
# 생성된 토큰 수: 18개 (실제 번역 결과만)
# 소요 시간: 1.2초
# Token IDs: [..., 12847, 106]  # 106에서 정상 종료
# 출력: "Im schlimmsten Fall kann es auch zum Platzen der Linse kommen."

# EOS 미설정 (기본값 사용 - 성능 저하)
generation = model.generate(**inputs, do_sample=False)
# 생성된 토큰 수: 256개 (기본 max_new_tokens까지 계속)
# 소요 시간: 6.8초
# Token IDs: [..., 12847, 106, 106, 106, ..., 106]  # 106이 238번 반복!
# 출력: "Im schlimmsten Fall kann es auch zum Platzen der Linse kommen." (동일)
# 겉으로는 정상 응답이지만 내부적으로 106 반복 생성!

 

추론 속도 5.6배 차이, 생성 토큰 14배 증가, GPU 시간/비용 낭비. 이거 프로덕션에서 쓰면 답 없다.


어떻게 해결하나?

해결 방법 1: generation_config.json 수정

가장 간단한 방법은 모델의 설정 파일을 직접 수정하는 것이다.

# 모델 다운로드 위치 확인
# 보통 ~/.cache/huggingface/hub/models--google--translategemma-27b-it/

 

generation_config.json 파일을 열어서 수정:

기존:

{
  "cache_implementation": "hybrid",
  "do_sample": true,
  "top_k": 64,
  "top_p": 0.95,
  "transformers_version": "4.57.3"
}

수정 후:

{
  "cache_implementation": "hybrid",
  "do_sample": true,
  "eos_token_id": [1, 106],
  "top_k": 64,
  "top_p": 0.95,
  "transformers_version": "4.57.3"
}

eos_token_id를 배열 형태 [1, 106]로 바꿔준다. 이렇게 하면 모델이 <eos>(1) 또는 <end_of_turn>(106) 둘 중 하나를 만나면 멈춘다.

해결 방법 2: 코드에서 직접 지정 (권장)

파일 수정이 번거롭다면 코드에서 직접 지정할 수도 있다.

import torch
from transformers import AutoModelForImageTextToText, AutoProcessor

model_id = "google/translategemma-27b-it"
processor = AutoProcessor.from_pretrained(model_id)
model = AutoModelForImageTextToText.from_pretrained(model_id, device_map="auto")

messages = [...]  # 번역 메시지

inputs = processor.apply_chat_template(
    messages, tokenize=True, add_generation_prompt=True,
    return_dict=True, return_tensors="pt"
).to(model.device, dtype=torch.bfloat16)

input_len = len(inputs['input_ids'][0])

# 방법 1: generate() 호출 시 직접 전달 (가장 간단)
with torch.inference_mode():
    generation = model.generate(
        **inputs,
        eos_token_id=[1, 106],  # 명시적 지정
        do_sample=False
    )

# 방법 2: model config 수정
model.config.eos_token_id = [1, 106]
with torch.inference_mode():
    generation = model.generate(**inputs, do_sample=False)

# 방법 3: IT 모델에서는 106만 사용 (더 안전)
model.config.eos_token_id = 106
with torch.inference_mode():
    generation = model.generate(**inputs, do_sample=False)

generation_config.json 수정만으로 충분한가?

대부분의 경우 Yes, 하지만 완벽하지는 않다.

설정 우선순위는 이렇다:

1순위: generate() 호출 시 직접 전달한 kwargs
2순위: generation_config.json
3순위: config.json (model.config)

 

즉, generate(eos_token_id=X)로 직접 전달하면 그게 최우선이고, 그다음이 generation_config.json이다.

주의사항:

상황 필요한 추가 조치
파인튜닝(Training) tokenizer_config.json도 수정 필요
vLLM/Ollama 등 3rd party 엔진 배열 [1, 106] 지원 안 할 수 있음 → 106만 사용
config.json에도 eos_token_id 있는 경우 둘 다 맞춰줘야 혼란 방지

특히 파인튜닝할 때는 주의해야 한다. generation_config.json만 수정하고 tokenizer_config.json은 안 건드리면, 학습 시에는 여전히 잘못된 EOS가 사용된다.

vLLM, Ollama 등에서 사용할 때

vLLM이나 Ollama 같은 추론 엔진은 배열 형태의 eos_token_id를 지원하지 않을 수 있다. 이 경우:

{
  "eos_token_id": 106
}

IT 모델에서는 106만 사용하는 게 안전하다. 어차피 대화형 모델에서는 <end_of_turn>이 실제 종료 시점이니까.


EOS 토큰과 학습(Fine-tuning)

이 문제가 특히 치명적인 건 파인튜닝할 때다.

왜 학습에서 문제가 되는가?

모델이 대화를 생성하는 법을 배울 때, 학습 데이터는 보통 이런 형태다:

<start_of_turn>user
What is Python?<end_of_turn>
<start_of_turn>model
Python is a high-level programming language...<end_of_turn>

이때 <end_of_turn> 토큰이 "내 답변이 끝났어!" 신호가 된다. 모델은 이 패턴을 보고 "아, 여기서 멈춰야 하는구나" 배운다.

하지만 EOS 토큰 설정이 잘못되면?

# 잘못된 설정
tokenizer.eos_token_id = 1  # <eos>

# 학습 데이터 토큰화
tokens = tokenizer("...Python is a language...<end_of_turn>")
# <end_of_turn>(106)이 일반 텍스트로 취급됨!
# EOS 토큰(1)이 문장 끝에 없음!

모델은 <end_of_turn>을 그냥 일반 텍스트로 보게 되고, "언제 멈춰야 하는지" 제대로 못 배운다. 결과적으로 파인튜닝 후 모델이 계속 횡설수설하게 된다.

올바른 파인튜닝 설정

from transformers import AutoTokenizer, AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("google/gemma-3-12b-it")
tokenizer = AutoTokenizer.from_pretrained("google/gemma-3-12b-it")

# 1. EOS 토큰을 end_of_turn으로 설정
tokenizer.eos_token = "<end_of_turn>"
tokenizer.eos_token_id = 106

# 2. 모델 config도 맞춰줌
model.config.eos_token_id = 106

# 3. generation_config도 맞춤
model.generation_config.eos_token_id = 106

# 이제 학습 시작
trainer = Trainer(
    model=model,
    tokenizer=tokenizer,
    ...
)
trainer.train()

이렇게 하면 학습 데이터 토큰화할 때 <end_of_turn>이 제대로 EOS 토큰으로 인식되고, 모델도 "여기서 멈춰야 해!" 배우게 된다.


Summary

이번 내용을 정리하면:

핵심 포인트

  1. Gemma IT 모델은 학습 시 <end_of_turn>(106)을 EOS 토큰으로 사용한다
  2. 하지만 기본 설정(generation_config.json)은 범용적으로 설정되어 있다
  3. 사용자가 명시적으로 eos_token_id를 설정하지 않으면 불필요한 토큰을 계속 생성한다
  4. 이는 버그가 아니라 의도된 동작이지만, 성능 최적화를 위해서는 설정이 필요하다
  5. 파인튜닝할 때는 특히 주의 - tokenizer와 config를 모두 올바르게 설정해야 한다

Hugging Face 공식 입장

Maintainer @Rocketknight1의 답변:

  • "This isn't really a bug" - 버그가 아닌 의도된 동작
  • "This is the correct behaviour" - 모델이 다양한 종료 방식을 학습했음
  • 사용자가 적절한 eos_token_id를 설정하면 최적 성능 확보

TranslateGemma 최적 설정

import torch
from transformers import AutoModelForImageTextToText, AutoProcessor

model_id = "google/translategemma-27b-it"
processor = AutoProcessor.from_pretrained(model_id)
model = AutoModelForImageTextToText.from_pretrained(model_id, device_map="auto")

# 추천: eos_token_id 명시적 설정
with torch.inference_mode():
    generation = model.generate(
        **inputs,
        eos_token_id=[1, 106],  # 또는 106만 사용
        do_sample=False
    )

앞으로 주의할 점

  • TranslateGemma 사용 시 eos_token_id=[1, 106] 또는 106을 명시
  • 성능 테스트 시 raw token IDs 확인하여 불필요한 생성 체크
  • 파인튜닝 전에 tokenizer, config, generation_config 모두 확인
  • vLLM/Ollama 같은 3rd party 엔진 쓸 때 배열 지원 여부 체크

나도 처음엔 "내가 뭘 잘못 쓴 건가?" 의심했는데, 알고 보니 설정 최적화가 필요한 부분이었다. Hugging Face maintainer의 답변을 보고 나서야 이것이 버그가 아니라 사용자가 이해하고 설정해야 하는 부분임을 알게 되었다. 혹시 비슷한 상황으로 고생하는 사람이 있다면 이 글이 도움이 되길 바란다!

 

260129 추가) 
google이 pr을 받아줬다~~~
올린지 꽤 됐었는데 따봉이 몇개 박히니까  받아줬네~~


Reference

Issues & Discussions:

  • Gemma-3 EOS token issue #38182 - Maintainer: "This isn't really a bug"
  • Gemma template won't end with eos_token #32110 - Maintainer: "This is the correct behaviour"
  • Ollama Gemma 3 end_of_turn issue #11276 - Ollama 특정 이슈 (다른 문제)

공식 문서:

  • TranslateGemma Model Card - Google 공식 모델 페이지
  • Gemma Tokenizer Documentation
  • Hugging Face Transformers Text Generation
  • Understanding EOS and PAD tokens in transformers

Related Issues:

  • Sample packing masks end_of_turn token
  • Gemma 2 chat template EOS token issue
  • Learning to generate EOS tokens
728x90

'알쓸신잡' 카테고리의 다른 글

PaddleOCR-VL-1.5-초경량 OCR의 새로운 강자  (0) 2026.02.02
STAX - 구글이 내놓은 LLM 평가 플랫폼  (0) 2026.01.26
옵시디언 새탭에서 파일 열기  (0) 2026.01.21
리눅스 파일경로 전체 복사  (0) 2026.01.16
macOS sshfs로 다른 시스템 마운트하기  (0) 2025.09.14
'알쓸신잡' 카테고리의 다른 글
  • PaddleOCR-VL-1.5-초경량 OCR의 새로운 강자
  • STAX - 구글이 내놓은 LLM 평가 플랫폼
  • 옵시디언 새탭에서 파일 열기
  • 리눅스 파일경로 전체 복사
창빵맨
창빵맨
  • 창빵맨
    Let's be Developers
    창빵맨
    로그인/로그아웃
  • 전체
    오늘
    어제
    • 분류 전체보기 (481)
      • 알쓸신잡 (88)
      • ML & DL (85)
        • Computer v.. (22)
        • NLP (22)
        • 파이썬 머신러닝 완.. (3)
        • 개념정리 (38)
      • 리눅스 (21)
      • 프로젝트 (29)
        • 산불 발생 예측 (6)
        • 음성비서 (12)
        • pdf 병합 프로그.. (0)
        • 수위 예측 (5)
        • 가짜 뉴스 분류 (5)
        • 전력사용량 예측 (1)
      • 코딩테스트 (217)
        • 프로그래머스[Pyt.. (17)
        • 프로그래머스[Fai.. (3)
        • 백준[Python] (160)
        • 이것이취업을위한코딩.. (18)
        • 파이썬 알고리즘 (19)
      • 데이터분석실습 (25)
        • 데이터 과학 기반의.. (18)
        • 헬로 데이터 과학 (7)
      • 메모장 (0)
      • 잡담 (4)
  • Blog

    • 🏠 Home

    ✏️글쓰기
    💻 관리

    Personal

    GITHUB
    Instagram
  • 공지사항

  • 인기 글

  • 태그

    dp
    나동빈
    백준
    이분탐색
    그리디
    파이썬
    이것이취업을위한코딩테스트다
    이코테
    BFS
    DFS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
상단으로

티스토리툴바