Jeju-Standard Korean Translator 소개
제주 방언(제주어, Jejueo)은 한반도 표준어와 음운, 어휘, 어미 체계가 크게 달라 같은 한국어 화자라도 알아듣기 어려운 경우가 많습니다. 옛 한국어의 흔적인 아래아(ᆞ) 같은 음절이 살아 있고, 종결 어미와 보조 용언의 쓰임이 독특해서 단순한 사투리를 넘어 별도의 언어 변종으로 다뤄지기도 합니다. 유네스코는 2010년 세계 위기 언어 지도(UNESCO Atlas of the World's Languages in Danger)에서 제주어를 "아주 심각하게 위기에 처한(critically endangered)" 언어로 분류했는데, 주로 노년층 화자에게만 온전히 남아 있어 세대가 바뀌면 사라질 위험이 크다는 이유에서였습니다. 이러한 배경에서 제주 방언을 자동으로 표준어로 옮기거나, 반대로 표준어를 제주 방언으로 생성하는 번역 모델은 단순한 편의 도구를 넘어 저자원 방언 보존(Low-Resource Dialect Preservation) 의 기초 인프라가 됩니다.
Jeju-Standard Korean Translator는 바로 이 문제를 겨냥한 88.79M 파라미터의 소형 언어 모델입니다. PCN R&S LLM 팀이 공개한 이 모델은 Qwen3 계열의 디코더 전용(Decoder-only) 아키텍처를 채택했으며, 다른 대형 모델을 미세조정한 것이 아니라 처음부터 학습(From-scratch Pretraining) 한 점이 특징입니다. 약 147만 쌍의 제주 방언과 표준어 평행 코퍼스(Parallel Corpus)를 H100 GPU 한 장으로 4시간 동안 학습해, 단일 체크포인트 하나로 방언에서 표준어 , 표준어에서 방언 두 방향을 모두 처리합니다.
흥미로운 점은 이렇게 작은 모델이 번역이라는 좁은 과제에서는 충분히 쓸 만한 품질을 낸다는 것입니다. 방언에서 표준어로 옮기는 방향에서 BLEU 77.67, 표준어에서 방언으로 생성하는 방향에서 BLEU 60.97을 기록했습니다. 모델 가중치는 178MB의 safetensors 파일 하나로, 별도의 커스텀 코드 없이 Qwen3ForCausalLM 클래스로 등록되어 Hugging Face Transformers와 vLLM에서 그대로 불러올 수 있습니다. 소비자용 GPU에서도 부담 없이 돌아가기 때문에, 방언 NLP 연구의 재현 가능한 베이스라인으로 활용하기에 적합합니다.
핵심 하이라이트
- 처음부터 학습(From-scratch): 부모 체크포인트 없이 H100 한 장으로 약 4시간 만에 학습했습니다.
- 하나의 모델, 두 방향: 프리픽스 토큰
<d2s>/<s2d>로 번역 방향을 전환합니다. - 공개된 평가 지표: 36,930쌍의 보류된(held-out) 테스트셋에서 BLEU 77.67(방언에서 표준어) / 60.97(표준어에서 방언) 을 달성했습니다.
- HF / vLLM 호환:
Qwen3ForCausalLM으로 등록되어 별도 커스텀 코드가 필요 없습니다. - 작은 용량: 178MB safetensors로, 소비자용 GPU에서도 충분히 구동됩니다.
단일 체크포인트로 두 방향을 처리하는 방법
이 모델의 가장 실용적인 설계 결정은 번역 방향을 프리픽스 제어 토큰(Prefix Control Token) 으로 다루는 것입니다. 보통 양방향 번역은 모델을 두 개 만들거나 방향별로 분리된 헤드를 두지만, 이 모델은 입력 맨 앞에 방향 태그 하나를 붙이는 것만으로 같은 가중치가 두 과제를 모두 수행하도록 했습니다. 학습 시에는 엄격한 4토큰 프롬프트 규칙을 따릅니다. 항상 <bos> 로 시작하고, 방향 태그를 붙인 뒤, 원문 텍스트, 그리고 <sep> 을 넣으면 모델이 <eos> 가 나올 때까지 번역 결과를 생성합니다.
<bos><d2s>{ 제주 방언 텍스트 }<sep> # 방언 → 표준
<bos><s2d>{ 표준 한국어 텍스트 }<sep> # 표준 → 방언
유효한 프롬프트는 반드시 <bos> 다음에 <d2s> / <s2d> / <copy> 중 정확히 하나가 와야 합니다. 둘 중 하나라도 빠지면 모델은 정의되지 않은 동작을 보입니다.
모델 아키텍처
모델은 Qwen3 스타일을 따르는 디코더 전용 트랜스포머입니다. Pre-LN RMSNorm, SwiGLU 활성화, 회전 위치 임베딩(Rotary Position Embedding, RoPE), 그룹 쿼리 어텐션(Grouped-Query Attention, GQA)를 사용하며, 여기에 헤드별 QK-Norm(per-head QK-Norm)을 추가해 학습 안정성을 높였습니다.
| 항목 | 값 |
|---|---|
| 아키텍처 | 디코더 전용 트랜스포머 (Qwen3 스타일: Pre-LN RMSNorm, SwiGLU, RoPE, GQA, 헤드별 QK-Norm) |
| HF 클래스 | Qwen3ForCausalLM |
| 파라미터 수 | 88.79 M |
| 히든 사이즈 | 640 |
| 레이어 수 | 18 |
| 어텐션 헤드 | 쿼리 10개 / 키-밸류 2개 (GQA 5:1), head_dim 64 |
| FFN 중간 차원 | 1,760 (SwiGLU) |
| 어휘 크기 | 16,000 (커스텀 SentencePiece BPE) |
| 최대 시퀀스 길이 | 1,024 |
| RoPE θ | 500,000 |
| 임베딩 공유(Tied) | 적용 |
| 정밀도 | bfloat16 |
| 토크나이저 | SentencePiece BPE, byte-fallback, NFC 정규화 (아래아 ᆞ 같은 고어 음절 보존) |
토크나이저는 SentencePiece BPE를 사용하되, NFC 유니코드 정규화 과정에서 아래아(ᆞ)처럼 제주 방언에 남아 있는 고어 음절을 보존하도록 설계했습니다. 방언의 음운적 특징을 토큰화 단계에서부터 지키려는 의도로, 저자원 방언 모델링에서 자주 간과되는 부분입니다.
학습 데이터와 전처리
학습 데이터는 AIHUB가 공개한 제주 방언 코퍼스에서 가져왔습니다. 이 데이터는 Saltlux / PCN이 2020년에 구축한 것으로, 40개 주제에 걸친 대화 전사(transcript)에 화자와 주제 메타데이터가 풍부하게 달려 있습니다. 두 개의 릴리스를 합친 뒤 중복 제거와 필터링을 거쳐 최종적으로 약 147만 쌍을 확보했습니다.
| 출처 | 쌍 수 | 비고 |
|---|---|---|
| AIHUB 제주 방언 (주석본, 40개 주제) | 1,318,497 | 화자 / 주제 메타데이터가 풍부한 대화 전사 |
| AIHUB 제주 방언 (추가 분할) | 223,965 | 동일 코퍼스 계열의 이전 릴리스 |
| 전체 (중복 제거 + 필터 후) | 1,477,173 | 학습 94.99% / 검증 2.50% / 테스트 2.50% |
전처리 파이프라인은 다음 네 단계로 구성됩니다.
- 정규화(Normalize): NFC 유니코드 정규화(아래아
ᆞ같은 고어 음절 보존), 따옴표 표준화, 공백 정리를 수행합니다. - 중복 제거(Dedup):
(방언_정규화, 표준_정규화)쌍 단위로 정확히 일치하는 중복을 제거하되, 대화의 순서는 보존합니다. - 필터(Filter): 3자 미만의 짧은 쌍, 길이 비율이 0.7을 넘는 쌍, 방언과 표준어가 동일한 쌍을 걸러냅니다. 단, 동일한 쌍은 복사 과제(copy-task) 신호로 10%만 남깁니다.
- 그룹 분할(Group split): 30개 단위로 묶어 분할(seed=20260417)하여, 같은 대화 세션이 학습/검증/테스트 경계를 넘나들지 않도록 했습니다.
데이터 누수를 막기 위해 같은 대화 세션을 통째로 한쪽에만 배정한 그룹 분할 방식이 눈에 띕니다. 한편 화자 분포는 여성 76%, 남성 24%로 여성에 치우쳐 있고, 연령은 20대(50%), 50대(24%), 60대 이상(14%) 순으로 분포합니다. 이 편향은 뒤의 한계 부분에서 다시 짚어보겠습니다.
학습 절차
학습은 NVIDIA H100 NVL 96GB 한 장에서 약 4시간 동안 진행되었습니다. 최적화는 fused AdamW를 사용했고, 코사인 스케줄에 선형 워밍업을 결합했습니다.
| 항목 | 값 |
|---|---|
| 하드웨어 | 1 × NVIDIA H100 NVL 96 GB |
| 학습 시간 | 약 4시간 |
| 옵티마이저 | AdamW (fused), β₁=0.9, β₂=0.95, ε=1e-8, weight_decay=0.1 (norm / 임베딩 제외) |
| LR 스케줄 | 코사인 + 선형 워밍업 |
| 최대 LR / 최소 LR | 4 × 10⁻⁴ / 4 × 10⁻⁵ |
| 워밍업 스텝 | 700 |
| 스텝당 유효 토큰 | 약 65K (block_tokens 16,384 × grad_accum 4) |
| 총 스텝 | 21,040 (상한); 조기 종료로 3,000 스텝(약 3 에폭)에서 최적 |
| 조기 종료 | 검증 평균 CHRF 기준 patience 10 |
| Grad clip | 1.0 |
| 정밀도 | bfloat16, torch.compile 미사용 (varlen flash-attn) |
| 손실 | 타깃 토큰에만 교차 엔트로피 (원문 / 방향 토큰은 -100 으로 마스킹) |
| 랜덤 시드 | 학습 42, 데이터 분할 20260417 |
각 쌍은 [bos][dir_tag][src_tokens...][sep][tgt_tokens...][eos] 형태로 인코딩됩니다. 여러 쌍을 하나의 학습 샘플로 패킹(Packing) 한 뒤, FlashAttention의 cu_seqlens varlen 커널로 처리해 짧은 문장이 대부분인 대화 데이터를 효율적으로 학습합니다. 또한 5%의 자기 복사(self-copy) 보조 과제(방언에서 방언, 표준어에서 표준어로 <copy> 태그를 통해 그대로 복사)를 섞어 모델이 입력을 그대로 보존해야 하는 상황을 익히도록 했습니다. 최종 학습 코퍼스는 패킹된 시퀀스 2,876,856개, 전체 토큰 69.0M개, 이 중 지도 학습 대상이 되는 타깃 토큰은 31.6M개였습니다.
평가 결과
평가는 sacreBLEU 기반의 코퍼스 단위 BLEU, CHRF++(문자 순서 6, 단어 순서 2, β=2, eps 스무딩), 그리고 정규화된 정확 일치(Exact Match)로 측정했습니다. 디코딩은 빔 서치(beam=4)를 사용했습니다.
| 방향 | BLEU | CHRF++ | Exact Match |
|---|---|---|---|
방언 → 표준 (<d2s>) |
77.67 | 84.19 | 51.0 % |
표준 → 방언 (<s2d>) |
60.97 | 70.02 | 30.0 % |
두 방향의 점수 차이가 흥미롭습니다. 방언을 표준어로 옮기는 <d2s> 방향이 일관되게 더 쉬운데, 모델 저자는 그 이유를 "방언을 생성하려면 더 넓은 어휘와 형태론적 범위가 필요한 반면, 방언을 표준어로 정규화하는 것은 다대일(many-to-one) 매핑에 가깝기 때문" 이라고 설명합니다. 즉, 여러 방언 표현이 하나의 표준 표현으로 수렴하는 정규화는 상대적으로 다루기 쉽지만, 반대로 표준어에서 다양한 방언 변종을 만들어내는 생성은 본질적으로 더 어려운 과제라는 것입니다.
실제 번역 예시를 보면 모델이 단순한 어휘 치환을 넘어 어미와 어순까지 자연스럽게 옮기는 것을 확인할 수 있습니다.
| 방향 | 입력 | 출력 |
|---|---|---|
<d2s> |
거~ 거~ 걸 말입니까 보말입니까 세상에 원 | 거~ 거~ 걸 말이예요 고둥이예요 세상에 원 |
<d2s> |
글로 죽 가당 보믄 큰큰헌 소낭이 나옵니다게. | 그리로 쭉 가다 보면 큰 소나무가 나옵니다. |
<s2d> |
제주도에는 수많은 관광지가 있습니다. | 제주도엔 하영헌 관광지가 잇수다. |
마지막 예시에서 표준어의 "수많은" 이 제주 방언의 "하영헌" 으로, "있습니다" 가 "잇수다" 로 바뀌는 것처럼, 표준어에서 방언으로의 생성에서도 제주 특유의 어휘와 종결 어미를 살려내는 모습을 보여줍니다.
사용 방법
모델은 표준 Qwen3ForCausalLM 이므로 Transformers에서 별도 코드 없이 불러올 수 있습니다. 프롬프트를 직접 조립해야 한다는 점만 주의하면 됩니다.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
REPO = "postcn/Jeju-Standard_Korean_Translator"
device = "cuda" if torch.cuda.is_available() else "cpu"
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForCausalLM.from_pretrained(REPO, dtype=torch.bfloat16).to(device).eval()
BOS = tok.convert_tokens_to_ids("<bos>")
SEP = tok.convert_tokens_to_ids("<sep>")
EOS = tok.convert_tokens_to_ids("<eos>")
def translate(text: str, direction: str = "<d2s>") -> str:
"""direction = '<d2s>' (방언→표준) or '<s2d>' (표준→방언)"""
dir_id = tok.convert_tokens_to_ids(direction)
ids = [BOS, dir_id] + tok.encode(text, add_special_tokens=False) + [SEP]
inp = torch.tensor([ids], device=model.device)
out = model.generate(
inp,
max_new_tokens=96,
do_sample=False,
num_beams=4,
eos_token_id=EOS,
pad_token_id=tok.pad_token_id,
)
gen = out[0, inp.shape[1]:].tolist()
if EOS in gen:
gen = gen[:gen.index(EOS)]
return tok.decode(gen, skip_special_tokens=True).strip()
# Jeju → Standard
print(translate("글로 죽 가당 보믄 큰큰헌 소낭이 나옵니다게.", "<d2s>"))
# Standard → Jeju
print(translate("저기로 쭉 가다 보면 큰 소나무가 나옵니다.", "<s2d>"))
대량 추론이나 서빙이 필요하다면 vLLM으로도 바로 띄울 수 있습니다.
vllm serve postcn/Jeju-Standard_Korean_Translator \
--host 0.0.0.0 --port 8001 \
--max-model-len 1024 \
--dtype bfloat16
이후에는 OpenAI 호환 클라이언트로 호출하되, 프롬프트에 방향 태그를 직접 넣고 특수 토큰을 stop 조건으로 지정합니다.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8001/v1", api_key="sk-dummy")
resp = client.completions.create(
model="postcn/Jeju-Standard_Korean_Translator",
prompt="<bos><s2d>제주도에는 수많은 관광지가 있습니다.<sep>",
max_tokens=64,
temperature=0.0,
stop=["<eos>", "<bos>", "<sep>", "<d2s>", "<s2d>"],
)
print(resp.choices[0].text)
모델 저자는 "그리디 또는 빔=4 디코딩이 가장 좋은 BLEU를 낸다" 고 권하며, 번역 결과가 잘 정의된 과제이므로 샘플링(temperature > 0)은 거의 도움이 되지 않는다고 덧붙입니다.
특수 토큰
프롬프트를 직접 다룰 때 알아두면 좋은 특수 토큰 구성입니다.
| ID | 토큰 | 용도 |
|---|---|---|
| 0 | <pad> |
패딩 |
| 1 | <unk> |
미상 토큰 |
| 2 | <bos> |
시퀀스 시작 (항상 첫 번째) |
| 3 | <eos> |
생성 종료 |
| 4 | <d2s> |
방향 태그: 방언 → 표준 |
| 5 | <s2d> |
방향 태그: 표준 → 방언 |
| 6 | <copy> |
자기 복사 보조 과제 (학습 전용) |
| 7 | <sep> |
원문과 타깃 사이 구분자 |
한계와 편향
작고 특화된 모델인 만큼, 실제로 활용하기 전에 알아둬야 할 한계가 분명합니다. 모델 저자는 이를 솔직하게 정리해 두었습니다.
- 도메인 편향: 학습 데이터가 AIHUB의 대화 전사 중심이라, 모델은 공식 문서나 뉴스, 기술 텍스트를 본 적이 없습니다. 이 분포를 벗어난 텍스트를 번역하면 품질이 떨어집니다.
- 화자 편향: 코퍼스가 여성 76%에 20대와 50대 화자에 치우쳐 있어, 고령의 남성 화자나 희귀한 지역 하위 방언의 표현은 과소 대표될 수 있습니다.
- 모델 용량: 88M 파라미터는 그 크기에 대한 친칠라 최적(Chinchilla-optimal) 토큰 수에 한참 못 미칩니다. 번역이라는 좁은 과제이기에 작동하는 것이며, 개방형 언어 모델링으로는 일반화되지 않습니다.
- 긴 입력에서의 환각:
max_position_embeddings가 1,024이고 학습 시퀀스의 평균 길이가 약 24토큰에 불과해, 평균보다 훨씬 긴 입력에서는 성능이 저하될 수 있습니다. - 안전 정렬 부재: 이 모델은 지시나 안전성으로 튜닝된 어시스턴트가 아니라 베이스 번역 모델입니다. 출력은 가공되지 않은 번역으로 취급하고, 민감한 용도라면 반드시 사람이 검토해야 합니다.
- 형태론적 보존: 자체 프로브 결과, 모델은 방언 특유의 어미를 약 74~78% 비율로 보존하며, 실패는 주로
<s2d>방향에서의 과도한 표준화로 나타납니다.
이러한 한계는 이 모델이 만능 번역기가 아니라, 특정 도메인(일상 대화)과 특정 코퍼스(AIHUB)에 최적화된 베이스라인임을 분명히 합니다. 오히려 저자는 이 모델을 역번역(back-translation), 화자 조건부 생성, 방언 인식 음성 인식(ASR) 후처리 같은 후속 연구의 재현 가능한 출발점으로 제안하고 있습니다.
Jeju-Standard Korean Translator의 라이선스
Jeju-Standard Korean Translator 모델은 Apache License 2.0으로 배포되고 있어, 연구 목적은 물론 상업적 용도로도 자유롭게 사용 및 수정이 가능합니다.
다만 학습 데이터는 AIHUB 제주 방언 코퍼스에서 가져온 것이므로, 특히 상업적 배포 시에는 사용자가 AIHUB의 데이터 이용 약관을 별도로 확인하고 준수해야 합니다. 이번 공개는 학습된 모델 가중치만 배포하며, 원본 데이터는 포함하지 않습니다.
Jeju-Standard Korean Translator 모델 카드
더 읽어보기
이 글은 GPT 모델로 정리한 글을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 덧글로 알려주시기를 부탁드립니다. ![]()
파이토치 한국 사용자 모임
이 정리한 이 글이 유용하셨나요? 회원으로 가입하시면 주요 글들을 이메일
로 보내드립니다! (기본은 Weekly지만 Daily로 변경도 가능합니다.)
아래
쪽에 좋아요
를 눌러주시면 새로운 소식들을 정리하고 공유하는데 힘이 됩니다~ ![]()
