번역
번역 (Translation)
번역은 한 언어의 텍스트 시퀀스를 다른 언어로 변환하는 작업이에요. 입력에서 출력을 만들어 내는 강력한 프레임워크인 시퀀스-투-시퀀스(sequence-to-sequence) 문제로 다룰 수 있는 여러 작업 중 하나로, 번역이나 요약처럼 활용돼요. 번역 시스템은 서로 다른 언어의 텍스트 사이 번역에 흔히 쓰이지만, 음성이나 그 중간의 조합(예: 텍스트-투-스피치 또는 스피치-투-텍스트)에도 쓰일 수 있어요.
출처: 문서
본문
이 가이드에서는 다음을 배워요:
- OPUS Books 데이터셋의 영어-프랑스어 하위 집합에서 T5를 파인튜닝해서 영어 텍스트를 프랑스어로 번역하기.
- 파인튜닝한 모델을 추론(inference)에 사용하기.
이 작업과 호환되는 모든 아키텍처와 체크포인트를 보려면 task-page를 확인하는 걸 추천해요.
시작하기 전에 필요한 라이브러리를 모두 설치했는지 확인해요:
pip install transformers datasets evaluate sacrebleu
Hugging Face 계정에 로그인해서 모델을 커뮤니티에 업로드하고 공유하는 걸 권장해요. 로그인하라는 메시지가 나오면 토큰을 입력해서 로그인하세요:
>>> from huggingface_hub import notebook_login
>>> notebook_login()
OPUS Books 데이터셋 로드
먼저 🤗 Datasets 라이브러리에서 OPUS Books 데이터셋의 영어-프랑스어 하위 집합을 로드해요:
>>> from datasets import load_dataset
>>> books = load_dataset("opus_books", "en-fr")
train_test_split 메서드로 데이터셋을 훈련용과 테스트용으로 나눠요:
>>> books = books["train"].train_test_split(test_size=0.2)
그다음 예시 하나를 살펴봐요:
>>> books["train"][0]
{'id': '90560',
'translation': {'en': 'But this lofty plateau measured only a few fathoms, and soon we reentered Our Element.',
'fr': 'Mais ce plateau élevé ne mesurait que quelques toises, et bientôt nous fûmes rentrés dans notre élément.'}}
translation: 텍스트의 영어와 프랑스어 번역.
전처리
다음 단계는 영어-프랑스어 언어 쌍을 처리할 T5 토크나이저를 로드하는 거예요:
>>> from transformers import AutoTokenizer
>>> checkpoint = "google-t5/t5-small"
>>> tokenizer = AutoTokenizer.from_pretrained(checkpoint)
만들려는 전처리 함수는 다음을 해야 해요:
- 입력 앞에 프롬프트를 붙여서 T5가 이게 번역 작업임을 알게 해요. 여러 NLP 작업을 처리할 수 있는 일부 모델은 특정 작업을 위해 프롬프트가 필요해요.
text_target파라미터에 목표 언어(프랑스어)를 설정해서 토크나이저가 목표 텍스트를 올바르게 처리하도록 해요.text_target을 설정하지 않으면 토크나이저가 목표 텍스트를 영어로 처리해요.- 시퀀스가
max_length파라미터로 설정한 최대 길이를 넘지 않도록 자르는 것.
>>> source_lang = "en"
>>> target_lang = "fr"
>>> prefix = "translate English to French: "
>>> def preprocess_function(examples):
... inputs = [prefix + example[source_lang] for example in examples["translation"]]
... targets = [example[target_lang] for example in examples["translation"]]
... model_inputs = tokenizer(inputs, text_target=targets, max_length=128, truncation=True)
... return model_inputs
전처리 함수를 전체 데이터셋에 적용하려면 🤗 Datasets의 map 메서드를 사용해요. batched=True를 설정하면 데이터셋의 여러 요소를 한 번에 처리할 수 있어서 map 함수를 빠르게 할 수 있어요:
>>> tokenized_books = books.map(preprocess_function, batched=True)
이제 DataCollatorForSeq2Seq를 사용해 예시 배치를 만들어요. 콜레이션(collation) 과정에서 데이터셋 전체를 최대 길이로 패딩하는 것보다 배치 안의 문장들을 가장 긴 길이에 맞춰 동적으로 패딩하는 편이 더 효율적이에요.
>>> from transformers import DataCollatorForSeq2Seq
>>> data_collator = DataCollatorForSeq2Seq(tokenizer=tokenizer, model=checkpoint)
평가
학습 중에 메트릭을 포함하면 모델의 성능을 평가하는 데 종종 도움이 돼요. 🤗 Evaluate 라이브러리로 평가 방법을 빠르게 로드할 수 있어요. 이 작업에서는 SacreBLEU 메트릭을 로드해요 (메트릭을 로드하고 계산하는 방법은 🤗 Evaluate quick tour를 참고하세요):
>>> import evaluate
>>> metric = evaluate.load("sacrebleu")
그다음 예측값과 라벨을 compute에 넘겨 SacreBLEU 점수를 계산하는 함수를 만들어요:
>>> import numpy as np
>>> def postprocess_text(preds, labels):
... preds = [pred.strip() for pred in preds]
... labels = [[label.strip()] for label in labels]
... return preds, labels
>>> def compute_metrics(eval_preds):
... preds, labels = eval_preds
... if isinstance(preds, tuple):
... preds = preds[0]
... decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
... labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
... decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
... decoded_preds, decoded_labels = postprocess_text(decoded_preds, decoded_labels)
... result = metric.compute(predictions=decoded_preds, references=decoded_labels)
... result = {"bleu": result["score"]}
... prediction_lens = [np.count_nonzero(pred != tokenizer.pad_token_id) for pred in preds]
... result["gen_len"] = np.mean(prediction_lens)
... result = {k: round(v, 4) for k, v in result.items()}
... return result
이제 compute_metrics 함수를 쓸 준비가 됐어요. 학습을 설정할 때 다시 사용하게 될 거예요.
학습
Trainer로 모델을 파인튜닝하는 방법에 익숙하지 않다면 여기의 기본 튜토리얼을 확인해 보세요!
이제 모델 학습을 시작할 준비가 됐어요! T5를 AutoModelForSeq2SeqLM으로 로드해요:
>>> from transformers import AutoModelForSeq2SeqLM, Seq2SeqTrainingArguments, Seq2SeqTrainer
>>> model = AutoModelForSeq2SeqLM.from_pretrained(checkpoint)
이 시점에서 남은 단계는 세 가지뿐이에요:
- Seq2SeqTrainingArguments에서 학습 하이퍼파라미터를 정의해요. 유일하게 필수인 파라미터는 모델을 저장할 위치를 지정하는
output_dir이에요.push_to_hub=True로 설정하면 모델을 Hub에 푸시할 수 있어요 (모델을 업로드하려면 Hugging Face에 로그인해야 해요). 각 에폭이 끝날 때마다 Trainer가 SacreBLEU 메트릭을 평가하고 학습 체크포인트를 저장해요. - 학습 인수를 Seq2SeqTrainer에 모델, 데이터셋, 토크나이저, 데이터 콜레이터,
compute_metrics함수와 함께 전달해요. - train()을 호출해서 모델을 파인튜닝해요.
>>> training_args = Seq2SeqTrainingArguments(
... output_dir="my_awesome_opus_books_model",
... eval_strategy="epoch",
... learning_rate=2e-5,
... per_device_train_batch_size=16,
... per_device_eval_batch_size=16,
... weight_decay=0.01,
... save_total_limit=3,
... num_train_epochs=2,
... predict_with_generate=True,
... fp16=True, #change to bf16=True for XPU
... push_to_hub=True,
... )
>>> trainer = Seq2SeqTrainer(
... model=model,
... args=training_args,
... train_dataset=tokenized_books["train"],
... eval_dataset=tokenized_books["test"],
... processing_class=tokenizer,
... data_collator=data_collator,
... compute_metrics=compute_metrics,
... )
>>> trainer.train()
학습이 완료되면 push_to_hub() 메서드로 모델을 Hub에 공유해서 모두가 쓸 수 있게 해요:
>>> trainer.push_to_hub()
번역용으로 모델을 파인튜닝하는 더 심층적인 예시는 해당 PyTorch notebook을 참고하세요.
추론 (Inference)
좋아요, 이제 모델을 파인튜닝했으니 추론에 활용할 수 있어요!
다른 언어로 번역하고 싶은 텍스트를 하나 떠올려요. T5의 경우 작업에 따라 입력에 프롬프트를 붙여야 해요. 영어에서 프랑스어로 번역할 때는 아래처럼 입력에 프롬프트를 붙이세요:
>>> text = "translate English to French: Legumes share resources with nitrogen-fixing bacteria."
텍스트를 토크나이즈하고 input_ids를 PyTorch 텐서로 반환해요:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_opus_books_model")
>>> inputs = tokenizer(text, return_tensors="pt").input_ids
generate() 메서드를 사용해 번역을 만드세요. 다양한 텍스트 생성 전략과 생성을 제어하는 파라미터에 대한 자세한 내용은 Text Generation API를 확인해 보세요.
>>> from transformers import AutoModelForSeq2SeqLM
>>> model = AutoModelForSeq2SeqLM.from_pretrained("username/my_awesome_opus_books_model")
>>> outputs = model.generate(inputs, max_new_tokens=40, do_sample=True, top_k=30, top_p=0.95)
생성된 토큰 id를 다시 텍스트로 디코딩해요:
>>> tokenizer.decode(outputs[0], skip_special_tokens=True)
'Les lignées partagent des ressources avec des bactéries enfixant l'azote.'