如果你想在翻譯任務(wù)上測試一個(gè)新的體系結(jié)構(gòu),比如在自定義數(shù)據(jù)集上訓(xùn)練一個(gè)新的標(biāo)記,那么處理起來會很麻煩,所以在本文中,我將介紹添加新標(biāo)記的預(yù)處理步驟,并介紹如何進(jìn)行模型微調(diào)。
因?yàn)镠uggingface Hub有很多預(yù)訓(xùn)練過的模型,可以很容易地找到預(yù)訓(xùn)練標(biāo)記器。但是我們要添加一個(gè)標(biāo)記可能就會有些棘手,下面我們來完整的介紹如何實(shí)現(xiàn)它,首先加載和預(yù)處理數(shù)據(jù)集。
加載數(shù)據(jù)集
我們使用WMT16數(shù)據(jù)集及其羅馬尼亞語-英語子集。load_dataset()函數(shù)將從Huggingface下載并加載任何可用的數(shù)據(jù)集。
import datasets
dataset = datasets.load_dataset("stas/wmt16-en-ro-pre-processed", cache_dir="./wmt16-en_ro")

在上圖1中可以看到數(shù)據(jù)集內(nèi)容。我們需要將其“壓平”,這樣可以更好的訪問數(shù)據(jù),讓后將其保存到硬盤中。
def flatten(batch):
batch['en'] = batch['translation']['en']
batch['ro'] = batch['translation']['ro']
return batch
# Map the 'flatten' function
train = dataset['train'].map( flatten )
test = dataset['test'].map( flatten )
validation = dataset['validation'].map( flatten )
# Save to disk
train.save_to_disk("./dataset/train")
test.save_to_disk("./dataset/test")
validation.save_to_disk("./dataset/validation")
下圖2可以看到,已經(jīng)從數(shù)據(jù)集中刪除了“translation”維度。

標(biāo)記器
標(biāo)記器提供了訓(xùn)練標(biāo)記器所需的所有工作。它由四個(gè)基本組成部分:(但這四個(gè)部分不是所有的都是必要的)
Models:標(biāo)記器將如何分解每個(gè)單詞。例如,給定單詞“playing”:i) BPE模型將其分解為“play”+“ing”兩個(gè)標(biāo)記,ii) WordLevel將其視為一個(gè)標(biāo)記。
Normalizers:需要在文本上發(fā)生的一些轉(zhuǎn)換。有一些過濾器可以更改Unicode、小寫字母或刪除內(nèi)容。
Pre-Tokenizers:為操作文本提供更大靈活性處理的函數(shù)。例如,如何處理數(shù)字。數(shù)字100應(yīng)該被認(rèn)為是“100”還是“1”、“0”、“0”?
Post-Processors:后處理具體情況取決于預(yù)訓(xùn)練模型的選擇。例如,將 [BOS](句首)或 [EOS](句尾)標(biāo)記添加到 BERT 輸入。
下面的代碼使用BPE模型、小寫Normalizers和空白Pre-Tokenizers。然后用默認(rèn)值初始化訓(xùn)練器對象,主要包括
1、詞匯量大小使用50265以與BART的英語標(biāo)記器一致
2、特殊標(biāo)記,如<s>和<pad>,
3、初始詞匯量,這是每個(gè)模型啟動(dòng)過程的預(yù)定義列表。
from tokenizers import normalizers, pre_tokenizers, Tokenizer, models, trainers
# Build a tokenizer
bpe_tokenizer = Tokenizer(models.BPE())
bpe_tokenizer.normalizer = normalizers.Lowercase()
bpe_tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
trainer = trainers.BpeTrainer(
vocab_size=50265,
special_tokens=["<s>", "<pad>", "</s>", "<unk>", "<mask>"],
initial_alphabet=pre_tokenizers.ByteLevel.alphabet(),
)
使用Huggingface的最后一步是連接Trainer和BPE模型,并傳遞數(shù)據(jù)集。根據(jù)數(shù)據(jù)的來源,可以使用不同的訓(xùn)練函數(shù)。我們將使用train_from_iterator()。
def batch_iterator():
batch_length = 1000
for i in range(0, len(train), batch_length):
yield train[i : i + batch_length]["ro"]
bpe_tokenizer.train_from_iterator( batch_iterator(), length=len(train), trainer=trainer )
bpe_tokenizer.save("./ro_tokenizer.json")
BART微調(diào)
現(xiàn)在可以使用使用新的標(biāo)記器了。
from transformers import AutoTokenizer, PreTrainedTokenizerFast
en_tokenizer = AutoTokenizer.from_pretrained( "facebook/bart-base" );
ro_tokenizer = PreTrainedTokenizerFast.from_pretrained( "./ro_tokenizer.json" );
ro_tokenizer.pad_token = en_tokenizer.pad_token
def tokenize_dataset(sample):
input = en_tokenizer(sample['en'], padding='max_length', max_length=120, truncation=True)
label = ro_tokenizer(sample['ro'], padding='max_length', max_length=120, truncation=True)
input["decoder_input_ids"] = label["input_ids"]
input["decoder_attention_mask"] = label["attention_mask"]
input["labels"] = label["input_ids"]
return input
train_tokenized = train.map(tokenize_dataset, batched=True)
test_tokenized = test.map(tokenize_dataset, batched=True)
validation_tokenized = validation.map(tokenize_dataset, batched=True)
上面代碼的第5行,為羅馬尼亞語的標(biāo)記器設(shè)置填充標(biāo)記是非常必要的。因?yàn)樗鼘⒃诘?行使用,標(biāo)記器使用填充可以使所有輸入都具有相同的大小。
下面就是訓(xùn)練的過程:
from transformers import BartForConditionalGeneration
from transformers import Seq2SeqTrainingArguments, Seq2SeqTrainer
model = BartForConditionalGeneration.from_pretrained( "facebook/bart-base" )
training_args = Seq2SeqTrainingArguments(
output_dir="./",
evaluation_strategy="steps",
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
predict_with_generate=True,
logging_steps=2, # set to 1000 for full training
save_steps=64, # set to 500 for full training
eval_steps=64, # set to 8000 for full training
warmup_steps=1, # set to 2000 for full training
max_steps=128, # delete for full training
overwrite_output_dir=True,
save_total_limit=3,
fp16=False, # True if GPU
)
trainer = Seq2SeqTrainer(
model=model,
args=training_args,
train_dataset=train_tokenized,
eval_dataset=validation_tokenized,
)
trainer.train()
過程也非常簡單,加載bart基礎(chǔ)模型(第4行),設(shè)置訓(xùn)練參數(shù)(第6行),使用Trainer對象綁定所有內(nèi)容(第22行),并啟動(dòng)流程(第29行)。上述超參數(shù)都是測試目的,所以如果要得到最好的結(jié)果還需要進(jìn)行超參數(shù)的設(shè)置,我們使用這些參數(shù)是可以運(yùn)行的。
推理
推理過程也很簡單,加載經(jīng)過微調(diào)的模型并使用generate()方法進(jìn)行轉(zhuǎn)換就可以了,但是需要注意的是對源 (En) 和目標(biāo) (RO) 序列使用適當(dāng)?shù)姆衷~器。
總結(jié)
雖然在使用自然語言處理(NLP)時(shí),標(biāo)記化似乎是一個(gè)基本操作,但它是一個(gè)不應(yīng)忽視的關(guān)鍵步驟。HuggingFace的出現(xiàn)可以方便的讓我們使用,這使得我們很容易忘記標(biāo)記化的基本原理,而僅僅依賴預(yù)先訓(xùn)練好的模型。但是當(dāng)我們希望自己訓(xùn)練新模型時(shí),了解標(biāo)記化過程及其對下游任務(wù)的影響是必不可少的,所以熟悉和掌握這個(gè)基本的操作是非常有必要的。
本文代碼:https://github.com/AlaFalaki/tutorial_notebooks/blob/main/translation/hf_bart_translation.ipynb