Hugging Face ๊ฐ ๋ฌด์์ธ์ง, ๊ทธ๋ฆฌ๊ณ Hugging Face ๋ฅผ ์ฌ์ฉํด์ translation ํ๋ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํ๋ค.
ํ๊น ํ์ด์ค๋ ๋ค์ํ ํธ๋์คํฌ๋จธ ๋ชจ๋ธ (transformer.models)๊ณผ ํ์ต ์คํฌ๋ฆฝํธ(transformer.Trainer)๋ฅผ ์ ๊ณตํ๋ ๋ชจ๋์ด๋ค. ํ๊น ํ์ด์ค๋ ํธ๋์คํฌ๋จธ ๋ชจ๋ธ์ ์ฌ์ฉํ ๋ layer, model ๋ฑ์ ์ ์ธํ๊ฑฐ๋ ํ์ต ์คํฌ๋ฆฝํธ๋ฅผ ๊ตฌํํด์ผ ํ๋ ์๊ณ ๋ฅผ ๋์ด์ค๋ค.
๐คTransformers๋ SOTA pretrained model๋ค์ ์ฝ๊ฒ ๋ค์ด๋ฐ๊ณ ํ์ตํ ์ ์๊ฒ API๋ค๊ณผ tool๋ค์ ์ ๊ณตํ๋ค. pretrained model ์ ์ฌ์ฉํ๋ฉด ๊ณ์ฐ ๋น์ฉ๋ ์ค์ผ ์ ์๊ณ , ์๋ฒ๋ฅผ ๋ ์ฐ๋ carbon footprint๋ ์ค์ผ ์ ์๊ณ , ์ฒ์๋ถํฐ ๋ชจ๋ธ์ ํ์ตํด์ผ ํ๋ ๊ฒ์ ๋นํด ์๊ฐ๋ ์์๋ ์ค์ผ ์ ์๋ค.
๐ค Transformers๋ PyTorch, TensorFlow, ๊ทธ๋ฆฌ๊ณ JAX ๊ฐ์ ํ๋ ์ ์ํฌ ๊ฐ ์ํธ ์ด์ฉ์ฑ์ ์ง์ํ๋ค. ๋ชจ๋ธ์ ๋ค๋ฅธ ํ๋ ์์ํฌ ์ฌ์ฉ์๋ ์ ์ฐํ๊ฒ ์๋ํ๋ค.
translation์ sequence-to-sequence task ์ด๋ค. ์ฆ ํ sequence์ธ ๋ฌธ์ฅ์์ ๋ค๋ฅธ sequence๋ฅผ ์ถ๋ ฅํ๋ค๋ ๊ฒ์ด๋ค. ๋ ์ ํน์ ๊ทธ ์ด์์ ์ธ์ด ์๋ค์ ์ถฉ๋ถํ ๊ฐ๊ณ ์์ผ๋ฉด ์๋ก์ด ๋ฒ์ญ ๋ชจ๋ธ์ ์ฒ์๋ถํฐ ๋ง๋ค ์ ์๋ค. ํ์ง๋ง ์ด๋ฏธ ๋ง๋ ๋ฒ์ญ ๋ชจ๋ธ์ ๊ฐ์ ธ์์ fine-tune ํ๋ ๊ฒ์ด ๋ ๋น ๋ฅด๋ค. ์๋์์๋ Marian model ์ ๊ฐ์ ธ์์ fine-tune ํ๋ ์์๋ฅผ ๋ณด์ฌ์ค๋ค.
Fine-tuning ์ ํ๊ฑฐ๋ ์ฒ์๋ถํฐ ๋ชจ๋ธ์ ํ๋ จํ๊ธฐ ์ํด์ ๋จผ์ ๋ฐ์ดํฐ๊ฐ ํ์ํ๋ค. ๋ฐ์ดํฐ๋ HuggingFace Hub ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํด์ ์ฌ์ฉํ ์๋ ์๊ณ ๋๋ ๋ด๊ฐ ๊ฐ์ง custom ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ์ฌ์ฉํ ์ ์๋ค. ์ฌ๊ธฐ์์๋ KDE4 dataset์ ๋ก๋ํด์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณด์ฌ์ค๋ค.
load_dataset() ์ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ฐ๋๋ค.
from datasets import load_dataset
raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
๋ค๋ฅธ ์ธ์ด๋ฅผ ๋ฐ๊ณ ์ถ์ผ๋ฉด lang1, lang2 ์ code๋ฅผ ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ค. ์ด ๋ฐ์ดํฐ์์ ๊ฐ๋ฅํ ๋ค๋ฅธ ์ธ์ด๋ค์ ์ด dataset card ๋งํฌ์์ ํ์ธํด๋ณด๊ธฐ!
๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ฐ์ raw_datasets ์ ๋ค์๊ณผ ๊ฐ์ ํ์์ด๋ค.
DatasetDict({
train: Dataset({
features: ['id', 'translation'],
num_rows: 210173
})
})
ํ๋์ ๋์ ๋๋ฆฌ ์์ 210,173 pair ๋ฌธ์ฅ์ด ํต์งธ๋ก ๋ค์ด์๋๋ฐ, validation ์ผ๋ก ๋ฐ๋ก ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋ฐ์ดํฐ๋ฅผ split ํด์ผํ๋ค. ์ด๋ train_test_split() ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
split_datasets
DatasetDict({
train: Dataset({
features: ['id', 'translation'],
num_rows: 189155
})
test: Dataset({
features: ['id', 'translation'],
num_rows: 21018
})
})
'test' key ์ด๋ฆ์ 'validation'์ผ๋ก ๋ฐ๊ฟ์ฃผ๊ธฐ ์ํด ์๋์ ๊ฐ์ด ํด์ค๋ค.
split_datasets["validation"] = split_datasets.pop("test")
์ด์ dataset ์์ ํ๋๋ง ํ์ธํด๋ณด๋ฉด,
split_datasets["train"][1]["translation"]
{'en': 'Default to expanded threads',
'fr': 'Par dรฉfaut, dรฉvelopper les fils de discussion'}
์ด๋ ๊ฒ ํ์ธํ ์ ์๋ค! ์ด์ ํ ์์ด ๋ ๋ ๋ฌธ์ฅ์ด ๋ค์ด๊ฐ dictionary ๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค.
text ๋ ์ ๋ถ sets of token ID ๋ก ๋ณํ๋์ด์ผ ๋ชจ๋ธ์ด ์์๋ค์ ์ ์๋ค. ์ด๋ฅผ ์ํด input ๊ณผ target ๋ ๋ค tokenize ํ ํ์๊ฐ ์๋ค. ์ด๋ฅผ ์ํด tokenizer object ๋ฅผ ๋ง๋ ๋ค. ์์์ ๋งํ ๋ฏ Marian pretrained model ์ ์ฌ์ฉํ ๊ฒ์ด๋ค. ๋ค๋ฅธ ์ธ์ด์์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ Helsinki-NLP/opus-mt-{src}-{tgt} ์ ์ธ์ด์์ ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ค. ๋๋, ๋ค๋ฅธ ๋ชจ๋ธ์ ์ฌ์ฉํ๊ณ ์ถ์ผ๋ฉด model_checkpoint ์ HuggingFace Hub ์ ์๋ ๋ค๋ฅธ ๋ชจ๋ธ์ ์ง์ ํด์ฃผ๊ฑฐ๋, ์ง์ ์ ์ฅํ pretrained model ์ ์ง์ ํด์ค ์๋ ์๋ค.
from transformers import AutoTokenizer
model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="pt")
๋ฐ์ดํฐ๋ฅผ ์ค๋นํ๊ธฐ ์ํด์ ๊ธฐ์ตํ ํ๊ฐ์ง๊ฐ ์๋ค. tokenizer ๊ฐ output language ๋ก target ๋ฌธ์ฅ์ ์ฒ๋ฆฌํ๋ ์ง ํ์ธํด์ผํ๋ ๊ฒ! ์ด๋ฅผ ์ํด์ tokenizer์ text_target ์ target ์ ๋๊ฒจ์ฃผ๋ฉด ๋๋ค.
์ด๋ป๊ฒ ๋์ํ๋์ง ์ง์ ํ ๋ฌธ์ฅ์ ํด๋ณด์~!
en_sentence = split_datasets["train"][1]["translation"]["en"]
fr_sentence = split_datasets["train"][1]["translation"]["fr"]
inputs = tokenizer(en_sentence, text_target=fr_sentence)
inputs
{'input_ids': [47591, 12, 9842, 19634, 9, 0], 'attention_mask': [1, 1, 1, 1, 1, 1], 'labels': [577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]}
output ์ผ๋ก input_ids ์๋ input๋ฌธ์ฅ(์์ด)์ id, labels ์๋ target ๋ฌธ์ฅ(ํ๋์ค์ด)์ id ๊ฐ ๋ค์ด์๋ค. ๋ง์ฝ, label ์ tokenize ํ๋ ๊ฒ์ ๊น๋จน์ผ๋ฉด input tokenizer ๋ก tokenize ํ๊ฒ ๋๋๋ฐ, Marian model์ ๊ฒฝ์ฐ ๊ทธ๋ฌ๋ฉด ์ด์ํ๊ฒ ์ฒ๋ฆฌ๊ฐ ๋๋ค. ๋ค์์ ๋ณด์.
wrong_targets = tokenizer(fr_sentence)
print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
print(tokenizer.convert_ids_to_tokens(inputs["labels"]))
['โPar', 'โdรฉ', 'f', 'aut', ',', 'โdรฉ', 've', 'lop', 'per', 'โles', 'โfil', 's', 'โde', 'โdiscussion', '</s>']
['โPar', 'โdรฉfaut', ',', 'โdรฉvelopper', 'โles', 'โfils', 'โde', 'โdiscussion', '</s>']
์ถ๋ ฅ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ํ๋์ค์ด ๋ฌธ์ฅ์ English tokenizer๋ก ์ฒ๋ฆฌํ๋ฉด tokenizer๊ฐ ํ๋์ค ๋จ์ด๋ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ๋ ๋ง์ token ์ ๋ง๋ค์ด๋ธ๋ค.
inputs
๋ input IDs, attention mask, ๋ฑ์ key๋ก ๊ฐ์ง๊ณ ์๋ ๋์
๋๋ฆฌ์ด๋ค. ๋ง์ง๋ง์ผ๋ก ์ฐ๋ฆฌ๊ฐ ๊ฐ์ง ๋ฐ์ดํฐ์ ์ ์ฉํ preprocessing function ์ ์ ์ํ๋ค.
max_length = 128
def preprocessing_function(examples):
inputs = [ex["en"] for ex in examples["translation"]]
targets = [ex["fr"] for ex in examples["translation"]]
model_inputs = tokenizer(inputs, text_target=targets, max_length=max_length, truncation=True)
return model_inputs
์ง๊ธ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ๋ฌธ์ฅ์ ์งง๊ธฐ ๋๋ฌธ์ input๊ณผ output ๋ ๋ค ๊ฐ์ maximum length ๋ก 128์ ์ค๋ค.
๋ค์๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ์ preprocessing์ ์ ์ฉํ๋ค.
tokenized_datasets = split_datasets.map(
preprocess_function,
batched=True,
remove_columns=split_datases["train"].column_names,
๊ทธ๋ผ ์ด์ ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ๋ฅผ ๋ค ํ๊ณ pretrained model์ fine-tuneํ ์ค๋น๊ฐ ๋์๋ค~
Traner
APIํ์ตํ๊ธฐ ์ํด์ Seq2SeqTrainer
๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค. Seq2SeqTrainer
๋ Trainer
์ subclass ๋ก, input ์ผ๋ก output ์ ์์ธกํ ์ ์๋ generate()
method ๋ฅผ ์ฌ์ฉํด์ evaluation ์ ์ ์ ํ ์ฌ์ฉํ ์ ์๋ค.
๋จผ์ fine-tune ํ ๋ชจ๋ธ์ด ํ์ํ๋ค. ์ฐ๋ฆฌ๋ AutoModel
API ๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค.
from transformers import AutoModelForSeq2SeqLM
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
dynamic batching ์ ์ํด padding ์ ์ฒ๋ฆฌํ๋ ค๋ฉด data collator ๊ฐ ํ์ํ๋ค. ์ฌ๊ธฐ์์๋ inputs ๋ฟ๋ง ์๋๋ผ labels ๋ maximum length ๋ก pad ๋์ด์ผ ํ๋ค. ๋ํ ํจ๋ฉ๋ ๊ฐ์ Loss ๊ณ์ฐ์์ ๋ฌด์๋๋๋ก ํ๊ธฐ ์ํด labels ๋ฅผ ์ฑ์ฐ๋๋ฐ ์ฌ์ฉ๋๋ ํจ๋ฉ ๊ฐ์ tokenizer์ ํจ๋ฉ ๊ฐ์ด ์๋๋ผ -100 ์ด์ด์ผ ํ๋ค.
์ด๋ DataCollatorForSeq2Seq
๋ฅผ ์ฌ์ฉํด์ ํ ์ ์๋ค. DataCollatorWithPadding
๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก input ์ ์ฒ๋ฆฌ์ ์ฌ์ฉ๋๋ tokenizer๊ณผ ํจ๊ป ๋ชจ๋ธ๋ ์ฌ์ฉํ๋ค. ๋ชจ๋ธ๋ ์
๋ ฅ๋ฐ๋ ์ด์ ๋ data collator ๊ฐ ์์ ๋ถ๋ถ์ ํน์ ํ ํฐ์ด ์๋, label ์ shift ๋ ๋ฒ์ ์ธ decoder input ID ๋ ์ค๋นํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ด shift ๋ ์ํคํ
์ณ๋ง๋ค ์ฝ๊ฐ ๋ค๋ฅด๊ฒ ์ํ๋๊ธฐ ๋๋ฌธ์ DataCollatorForSeq2Seq
๋ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์์์ผ ํ๋ค.
from transformers import DataCollatorForSeq2Seq
data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
๋ช๊ฐ์ง ๋ฌธ์ฅ์ผ๋ก ํ ์คํธ ํ๊ธฐ ์ํด tokenized training set ์ ์์ ๋ฌธ์ฅ์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์จ๋ค.
batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
batch.keys()
dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
-100 ์ ํตํด Labels ์ด ๋ฐฐ์น์ Maximum length ๋ก ํจ๋ฉ ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
batch["labels"]
tensor([[ 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0, -100,
-100, -100, -100, -100, -100, -100],
[ 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817,
550, 7032, 5821, 7907, 12649, 0]])
๋ํ decoder input ID ๋ฅผ ๋ณด์์ ๋ label ์ shifted version ์ธ ๊ฒ์ ์ ์ ์๋ค.
batch["decoder_input_ids"]
tensor([[59513, 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0,
59513, 59513, 59513, 59513, 59513, 59513],
[59513, 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124,
817, 550, 7032, 5821, 7907, 12649]])
๋ค์์ ๋ฐ์ดํฐ์ ์ฒซ๋ฒ์งธ, ๋๋ฒ์งธ ๋ฌธ์ฅ์ ๋ํ label ์ด๋ค.
for i in range(1, 3):
print(tokenized_datasets["train"][i]["labels"])
[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
์ด data_collator ๋ Seq2Seq2Trainer ๋ก ๋ณด๋ด์ง๊ฒ ๋ ๊ฒ์ด๋ค. ๋ค์์ผ๋ก Metric ์ ๋ณด์!
Seq2SeqTrainer ๊ฐ superclass ์ธ Trainer ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ evaluation ๋๋ prediction ์ค์ generate() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ด๋ค. ํ์ตํ๋ ๋์ ๋ชจ๋ธ์ ํ์ต ์๋๋ฅผ ๋์ด๊ธฐ ์ํด ์์ธกํ๋ ค๋ ํ ํฐ ์ดํ์ ์กด์ฌํ๋ ํ ํฐ์ ์ฌ์ฉํ์ง ์๋๋ก ํ๋ ค๊ณ attention masking ๊ณผ ํจ๊ป decoder_input_ids ๋ฅผ ์ฌ์ฉํ๋ค. Inference ํ ๋์๋ label ์ด ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ๋์ผํ ์ค์ ์ผ๋ก ๋ชจ๋ธ์ ํ๊ฐํ๋ ๊ฒ์ด ์ข๋ค.
๋ฒ์ญ์ ์์ด์ ์ ํต์ ์ธ ํ๊ฐ metric ์ 2002๋ ์ Kishore Papineni et al.์ ์ํด ์๊ฐ๋ BLEU score ๋ก ์๋ ค์ ธ์๋ค. BLEU score ๋ ๋ฒ์ญ๋ ๋ฌธ์ฅ์ด ์ค์ label ๊ณผ ์ผ๋ง๋ ๊ฐ๊น์ด์ง ํ๊ฐํ๋ค. ๋ชจ๋ธ์ ๊ฒฐ๊ณผ๋ฅผ ์ผ๋ง๋ ๋ช ๋ฃํ์ง ๋๋ ๋ฌธ๋ฒ์ ์ผ๋ก ์ ์ ํ์ง๋ ํ๊ฐํ์ง ์๋๋ค. ํ์ง๋ง ์ถ๋ ฅ๋ ๊ฒฐ๊ณผ์ ์๋ ๋จ์ด๊ฐ target ์๋ ๋ํ๋๋์ง ํ๊ฐํ๊ธฐ ์ํด ํต๊ณ์ ์ธ ๋ฐฉ๋ฒ์ ์ด๋ค. ์ค์ ๋ฌธ์ฅ์์๋ ๋ฐ๋ณต์ด ์๋๋ฐ ๋ชจ๋ธ์ ๊ฒฐ๊ณผ์๋ ๊ฐ์ ๋จ์ด๊ฐ ๋ฐ๋ณต๋๋ ๊ฒฝ์ฐ ํจ๋ํฐ๋ฅผ ์ฃผ๊ณ , ์ค์ ๋ฌธ์ฅ๋ณด๋ค ์งง์ ๋ฌธ์ฅ์ ๋ง๋ค์ด ๋ด๋ ๊ฒฝ์ฐ์๋ ํจ๋ํฐ๋ฅผ ์ค๋ค.
BLEU score ์ ์ฝ์ ์ค ํ๊ฐ์ง๋ ์ด๋ฏธ ํ ํฐํ ๋ ๋ฌธ์ฅ์ ๋์์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ tokenizer ๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ธ ๊ฐ์ ์ ์๋ฅผ ๋น๊ตํ๊ธฐ ์ด๋ ต๋ค๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ์ค๋๋ ๋ฒ์ญ ๋ชจ๋ธ์ ๋ฒค์น๋งํนํ๋๋ฐ ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ metric ์ ํ ํฐํ ๋จ๊ณ๋ฅผ ํ์คํํ์ฌ ์ด๋ฌํ ์ฝ์ ์ ํด๊ฒฐํ๋ SacreBLEU ๋ค. ์ด metric ์ ์ฌ์ฉํ๋ ค๋ฉด SacreBLEU ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด์ผ ํ๋ค.
!python3 -m pip install sacrebleu
๊ทธ ๋ค์์ load_metric() ์ ํตํด ๋ก๋ํ ์ ์๋ค.
from datasets import load_metric
metric = load_metric("sacrebleu")
์ด metric ์ ํ
์คํธ๋ฅผ input ๊ณผ target ์ผ๋ก ์ฌ์ฉํ๋ค. ๊ฐ์ ๋ฌธ์ฅ์ ๋ํด ๊ฐ๋ฅํ ๋ฒ์ญ ๊ฒฐ๊ณผ๊ฐ ์ฌ๋ฌ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ ๊ฐ์ ๊ฐ๋ฅํ ๋์์ ์
๋ ฅ๋ฐ๋๋ก ์ค๊ณ๋์๋ค. NLP์์ ์ฌ๋ฌ ๋ฌธ์ฅ์ label๋ก ์ ๊ณตํ๋ ๋ฐ์ดํฐ์
๋ค๋ ๋ง์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ predictions
๋ ๋ฌธ์ฅ ๋ฆฌ์คํธ์ฌ์ผ ํ์ง๋ง refenrences
๋ ๋ฌธ์ ๋ฆฌ์คํธ์ ๋ฆฌ์คํธ ์ด์ด์ผ ํ๋ค.
์์๋ฅผ ๋ณด์.
predictions = [
"This plugin lets you translate web pages between several languages automatically."
]
references = [
[
"This plugin allows you to automatically translate web pages between several languages."
]
]
metric.compute(predictions=predictions, references=references)
{'score': 46.750469682990165,
'counts': [11, 6, 4, 3],
'totals': [12, 11, 10, 9],
'precisions': [91.67, 54.54, 40.0, 33.33],
'bp': 0.9200444146293233,
'sys_len': 12,
'ref_len': 13}
BLEU score ๋ก 46.75 ๋ฅผ ๋ณด์ฌ์ฃผ๋๋ฐ ๊ฝค ๊ด์ฐฎ์ ๊ฒฐ๊ณผ๋ค. ๋ฐ๋ฉด์ ์๋์๋ ๋ฐ๋ณต๋๊ณ ์งง์ ๋ฌธ์ฅ์ผ๋ก ํ๊ฐํ์ ๋ ๋์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์์์ด๋ค.
predictions = ["This This This This"]
references = [
[
"This plugin allows you to automatically translate web pages between several languages."
]
]
metric.compute(predictions=predictions, references=references)
{'score': 1.683602693167689,
'counts': [1, 0, 0, 0],
'totals': [4, 3, 2, 1],
'precisions': [25.0, 16.67, 12.5, 12.5],
'bp': 0.10539922456186433,
'sys_len': 4,
'ref_len': 13}
predictions = ["This plugin"]
references = [
[
"This plugin allows you to automatically translate web pages between several languages."
]
]
metric.compute(predictions=predictions, references=references)
{'score': 0.0,
'counts': [2, 1, 0, 0],
'totals': [2, 1, 0, 0],
'precisions': [100.0, 100.0, 0.0, 0.0],
'bp': 0.004086771438464067,
'sys_len': 2,
'ref_len': 13}
score ๋ ํด์๋ก ์ข์ ๊ฒ์ด๋ค.
๋ชจ๋ธ ์ถ๋ ฅ์์ metric ์ด ์ฌ์ฉํ ์ ์๋ ํ
์คํธ๋ก ๋ณํํ๊ธฐ ์ํด tokenizer.batch_decode()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค. label ์์๋ ๋ชจ๋ -100์ ์ ๊ฑฐํ๋ฉด ๋๋ค. (ํ ํฌ๋์ด์ ๋ ํจ๋ฉ ํ ํฐ์ ๋ํด ์๋์ผ๋ก ๋์ผํ ์์
์ ์ํํจ)
import numpy as np
def compute_metrics(eval_preds):
preds, labels = eval_preds
# In case the model returns more than the prediction logits
if ininstance(preds, tuple):
preds = preds[0]
decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
# Replace -100s in the labels as we can't decode them
labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
decoded_labels = tokenizer.batch_decode(labels, skip_spacial_tokens=True)
# Some simple post-processing
decoded_preds = [pred.strip() for pred in decoded_preds]
decoded_labels = [[label.strip()] for label in decoded_labels]
result = metric.compute(predictions=decoded_preds, references=decoded_labels)
return {"bleu": result["score"]}
๋ชจ๋ธ์ ๋ฏธ์ธ์กฐ์ ํ๊ธฐ ์ํด ๋จผ์ Seq2SeqTrainingArguments ๋ฅผ ์ ์ํ๋ค.
from transformers import Seq2SeqTrainingArguments
args = Seq2SeqTrainingArguments(
f"marian-finetuned-kde4-en-to-fr",
evaluation_strategy="no",
save_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=32,
per_device_eval_batch_size=64,
weight_decay=0.01,
save_total_limit=3,
num_train_epochs=3,
predict_with_generate=True,
fp16=True,
push_to_hub=True,
)
์ผ๋ฐ์ ์ธ hyperparameter (learning rate, num of epochs, batch size, weight decay) ๋ฅผ ์ ์ธํ๊ณ , ์ฌ๊ธฐ์์๋ ์กฐ๊ธ ๋ณํ๋ฅผ ์ค๋ค.
regular evaluation ์ ์ค์ ํ์ง ์๋๋ค. training ํ๊ธฐ ์ ์ด๋ ํ์ evaluate ํ๋ค.
fp16=True
๋ก ์ค์ ํด์ GPU ๋ก training ํ๋ ์๋๋ฅผ ๋์ธ๋ค.
predict_with_generate=True
์ผ๋ก ํ๋ค.
push_to_hub=True
๋ฅผ ์ฌ์ฉํด์ ๊ฐ epoch ๊ฐ ๋๋ ๋ Hub ์ ๋ชจ๋ธ์ ์
๋ก๋ํ๋ค.
hub_model_id
์ ์ฌ์ฉํ๋ฉด ํธ์ฌํ๋ ค๋ ์ ์ฅ์์ ์ ์ฒด ์ด๋ฆ์ ์ง์ ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ๋ชจ๋ธ์ huggingface-course ์ ํธ์ฌํ ๋, hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"
๋ฅผ Seq2SeqTrainingArguments ์ ์ถ๊ฐํ๋ฉด ๋๋ค.
๋ง์ง๋ง์ผ๋ก ๋ชจ๋ ๊ฑธ Seq2SeqTrainer ์ ์ ๋ฌํ๋ค.
from transformers import Seq2SeqTrainer
trainer = Seq2SeqTrainer(
model,
args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
ํ์ตํ๊ธฐ ์ ์ ๋จผ์ ์ด๊ธฐ ๋ชจ๋ธ์ด ์ป๋ ์ ์๋ฅผ ๋ณด๊ณ , fine-tuning ์ผ๋ก ํน์ ๋ ์ ํ๋๋ ๊ฒ์ ์๋์ง ํ์ธํด๋ณธ๋ค. ์๋ ๋ช ๋ น์ ์ข ์ค๋ ๊ฑธ๋ฆฐ๋ค.
trainer.evaluate(max_length=max_target_length)
{'eval_loss': 1.6964408159255981,
'eval_bleu': 39.26865061007616,
'eval_runtime': 965.8884,
'eval_samples_per_second': 21.76,
'eval_steps_per_second': 0.341}
BLEU score 39 ๋ ๋์์ง ์๋ค. ์ฐ๋ฆฌ๊ฐ ์ ํํ ๋ชจ๋ธ์ด ์ด๋ฏธ ์์ด-ํ๋์ค์ด ๋ฌธ์ฅ์ ์ ๋ฒ์ญํ๋ค๋ ๊ฒ์ด๋ค.
๋ค์์ผ๋ก ๋ณธ๊ฒฉ์ ์ผ๋ก ํ์ต์ ํ๋ค. ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ๊ฒ์ด๋ค.
trainer.train()
ํ์ต์ด ์งํ๋๋ ๋์ ๋ชจ๋ธ์ด ์ ์ฅ๋ ๋๋ง๋ค (์ฌ๊ธฐ์ epoch ๋ง๋ค) ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ชจ๋ธ์ด ํ๋ธ์ ์ ๋ก๋๋๋ค. ์ด๋ฐ ๋ฐฉ์์ผ๋ก ๋ค๋ฅธ ๋จธ์ ์์ ํ์ต์ ๋ค์ ์์ํ ์๋ ์๋ค.
ํ์ต์ด ์๋ฃ๋๋ฉด ๋ค์ ํ๊ฐํด๋ณด์.
trainer.evaluate(max_length=max_target_length)
{'eval_loss': 0.8558505773544312,
'eval_bleu': 52.94161337775576,
'eval_runtime': 714.2576,
'eval_samples_per_second': 29.426,
'eval_steps_per_second': 0.461,
'epoch': 3.0}
๊ฑฐ์ 14 ์ ์ด ์ฌ๋๋ค!
๋ง์ง๋ง์ผ๋ก push_to_hub()
๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ์ต์ ๋ฒ์ ์ ๋ชจ๋ธ์ ์
๋ก๋ํ๋์ง ํ์ธํ๋ค. Trainer
๋ ๋ชจ๋ ํ๊ฐ ๊ฒฐ๊ณผ๊ฐ ํฌํจ๋ ๋ชจ๋ธ ์นด๋์ ์ด์์ ์์ฑํด์ ์
๋ก๋ํ๋ค. ์ด ๋ชจ๋ธ ์นด๋์๋ Model Hub ๊ฐ inference ๋ฐ๋ชจ์ฉ ์์ ฏ์ ์ ํํ๋๋ฐ ๋์์ด ๋๋ ๋ฉํ๋ฐ์ดํฐ๊ฐ ํฌํจ๋์ด์๋ค. ์ผ๋ฐ์ ์ผ๋ก ๋ชจ๋ธ ํด๋์ค์์ ์ฌ๋ฐ๋ฅธ ์์ ฏ์ ์ ์ถํ ์ ์์ผ๋ฏ๋ก ์๋ฌด๊ฒ๋ ํ ํ์๊ฐ ์์ง๋ง, ์ด ๊ฒฝ์ฐ ๋์ผํ ๋ชจ๋ธ ํด๋์ค๋ฅผ ๋ชจ๋ ์ข
๋ฅ์ sequence-to-sequence ๋ฌธ์ ์ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ๋ฒ์ญ๋ชจ๋ธ์ด๋ผ๊ณ ์ง์ ํ๋ค.
trainer.push_to_hub(tags="tanslation", commit_message="Training complete")
์ ๋ช ๋ น์ ์๋์ ๊ฐ์ด ๋ฐฉ๊ธ ํ ์ปค๋ฐ์ URL ์ ๋ฐํํ๋ค.
'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
์ด์ ๋ชจ๋ธ์ ํ ์คํธํ๊ธฐ ์ํด Model Hub ์ inference widget ์ ์ฌ์ฉํ ์ ์๊ณ ๊ณต์ ํ ์๋ ์๋ค. ๋๋์ด ์ฑ๊ณต์ ์ผ๋ก ๋ฒ์ญ ํ ์คํฌ๋ฅผ ํ๋ ๋ชจ๋ธ์ fine-tune ์๋ฃํ๋ค!
ํ์ต ๋ฃจํ์ ๋ํด ์ข ๋ ์์ธํ ์๊ณ ์ถ๋ค๋ฉด ์ด์ ๐คAccelerate๋ฅผ ์ฌ์ฉํ์ฌ ๋์ผํ ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์๋์์ ๋ณด์ฌ์ค๋ค.
์ด์ full training loop ๋ฅผ ๋ณด๊ณ , ํ์ํ ๋ ์ํ๋ ๋ถ๋ถ๋ง ์ปค์คํ ํด์ ์ฌ์ฉํ ์ ์๊ฒ ๊ณต๋ถํด๋ณด์.
๋จผ์ ๋ฐ์ดํฐ์
์ torch ๋ก ํฌ๋ฉงํ ๋ค์, ๋ฐ์ดํฐ ์
์ ๊ฐ์ง๊ณ DataLoaders
๋ฅผ ๋ง๋ ๋ค.
from torch.utils.data import DataLoader
tokenized_datasets.set_format("torch")
train_dataloader = DataLoader(
tokenized_datasets["train"],
shuffle=True,
collate_fn=data_collator,
batch_size=8,
)
eval_dataloader = DataLoader(
tokenized_datasets[], collate_fn=data_collator, batch_size=8
)
๋ค์์ผ๋ก ๋ชจ๋ธ์ reinstantiate ํด์ pretrained model ์์ ์์ํ๋ค.
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
๊ทธ๋ฐ ๋ค์ optmizer ๋ ๋ง๋ ๋ค.
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=2e-5)
์ด๋ ๊ฒ ๋ง๋ค๋ฉด accelerator.prepare()
๋ฉ์๋์ ๋ณด๋ผ ์ ์๋ค.
from accelerate import Accelerator
accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
model, optimizer, train_dataloader, eval_dataloader
)
์ด์ train_dataloader
๋ฅผ accelerator.prepare()
๋ก ๋ณด๋ด๊ณ , ์ด ๊ธธ์ด๋ฅผ training step ์ ์๋ฅผ ๊ณ์ฐํ ๋ ์ฌ์ฉํ ์ ์๋ค.
from transformers import get_scheduler
num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps,
)
์ฐ๋ฆฌ๊ฐ ๋ง๋ ๋ชจ๋ธ์ Hub ์ ์ฌ๋ฆฌ๋ ค๋ฉด Repository ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค. ๋ก๊ทธ์ธ ํ ์ํ๊ฐ ์๋๋ผ๋ฉด ์ผ๋จ Hugging Face Hub ์ ๋ก๊ทธ์ธ ๋ถํฐ ํ๋ค. ๊ทธ๋ฆฌ๊ณ model ID ๋ก ๋ ํฌ์งํ ๋ฆฌ์ ์ด๋ฆ์ ์ ํด์ค๋ค.
from huggingface_hub import Repository, get_full_repo_name
model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
์ด์ full training loop ๋ฅผ ์ธ ์ค๋น๊ฐ ๋์๋ค. evaluation ๋ถ๋ถ์ ๊ฐ๋จํ๊ฒ ํ๊ธฐ ์ํด, predictions ์ labels ๋ฅผ metric
์ด ์ฌ์ฉํ๊ธฐ ์ฝ๊ฒ ๋ณํํ๋ postprocess()
ํจ์์ด๋ค.
def postprocess(predictions, labels):
predictions = predictions.cpu().numpy()
labels = labels.cpu().numpy()
decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
# Replace -100 in the labels as we can't decode them.
labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
# Some simple post-processing
decoded_preds = [pred.strip() for pred in decoded_preds]
decoded_labels = [[label.strip()] for label in decoded_labels]
return decoded_preds, decoded_labels
from tqdm.auto import tqdm
import torch
progress_bar = tqdm(range(num_training_steps))
for epoch in range(num_train_epochs):
# Training
model.train()
for batch in train_dataloader:
outputs = model(**batch)
loss = outputs.loss
accelerator.backward(loss)
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
# Evaluation
model.eval()
for batch in tqdm(eval_dataloader):
with torch.no_grad():
generated_tokens = accelerator.unwrap_model(model).generate(
batch["input_ids"],
attention_mask=batch["attention_mask"],
max_length=128,
)
labels = batch["labels"]
# Necessary to pad predictions and labels for being gathered
generated_tokens = accelerator.pad_across_processes(
generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
)
labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)
predictions_gathered = accelerator.gather(generated_tokens)
labels_gathered = accelerator.gather(labels)
decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
metric.add_batch(predictions=decoded_preds, references=decoded_labels)
results = metric.compute()
print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")
# Save and upload
accelerator.wait_for_everyone()
unwrapped_model = accelerator.unwrap_model(model)
unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
if accelerator.is_main_process:
tokenizer.save_pretrained(output_dir)
repo.push_to_hub(
commit_message=f"Training in progress epoch {epoch}", blocking=False
)
epoch 0, BLEU score: 53.47
epoch 1, BLEU score: 54.24
epoch 2, BLEU score: 54.44
pipeline ์ผ๋ก fine-tuned ๋ชจ๋ธ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ model identifier ๋ง ์ ์ง์ ํด์ฃผ๋ฉด ๋๋ค.
from transformers import pipeline
# Replace this with your own checkpoint
model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
[{'translation_text': 'Par dรฉfaut, dรฉvelopper les fils de discussion'}]
์ด์ pretrained model ์ด fine-tune ํ corpus ์ ๋ง๊ฒ ์์ธกํ๋ค. ์์ด ๋จ์ด "threads" ๋ก ๊ทธ๋ฅ ๋์ง ์๊ณ , ํ๋์ค์ด ๋จ์ด๋ก ๋ฒ์ญํ๋ค.
domain adaptation ์ ๋ ๋ค๋ฅธ ์์ด๋ค.
translator(
"Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
์ฝ๋์ ํ๋ฆฐ๋ถ๋ถ์ด ์๋๋ฐ ์์ง ์์ ์ํจ. ์ ํํ๊ฒ๋ ์๋ ๋ ํผ ์ฐธ๊ณ ํ๊ธฐ...
[reference]
https://huggingface.co/learn/nlp-course/chapter7/4?fw=pt#translation