Transformers Roberta의 정의와 사용법(custom train)
RoBERTa란
우선 BERT란 pretrained-model로 2018년에 위대한 구글에서 개발한 언어 모델인데, NLP 전반적인 분야에 아주 좋은 성능을 보여주는 모델이라고 한다.
하지만 아직 BERT는 under-fit되어 있기에 여러 하이퍼 파라미터들을 조정하여 기존 bert보다 우월한 성능을 지닌 roberta가 개발되었다.
import pandas as pd
import numpy as np
from tqdm import tqdm
데이터셋 준비
KorNLI - 두 문장의 관계를 entailment/neutral/contradiction 으로 분류
https://github.com/kakaobrain/KorNLUDatasets/tree/master/KorNLI 에서 받아온다.
path = '/content/drive/MyDrive/KorNLUDatasets-master/KorNLI/'
train_snli = pd.read_csv(path + "snli_1.0_train.ko.tsv", sep='\t', quoting=3)
train_xnli = pd.read_csv(path + "multinli.train.ko.tsv", sep='\t', quoting=3)
# 결합 후 섞기
train_data = train_snli.append(train_xnli)
train = train_data.sample(frac=1).reset_index(drop=True)
def drop_na_and_duplciates(df):
df = df.dropna()
df = df.drop_duplicates()
df = df.reset_index(drop=True)
return df
# 전제, 가설에 존재하는 한글 단어가 아닌 다른 단어들은 전부 제거해줍니다.
# train['sentence1'] = train['sentence1'].str.replace('[^ㄱ-ㅎㅏ-ㅣ가-힣 0-9]', '')
# train['sentence2'] = train['sentence2'].str.replace('[^ㄱ-ㅎㅏ-ㅣ가-힣 0-9]', '')
# 결측값 및 중복 샘플 제거
train = drop_na_and_duplciates(train)
train.head()
sentence1 \ 0 전화를 많이 받으셨나요? 1 제노바인들은 농업과 무역에 가장 가치 있는 것으로 여겨졌던 동부 에게 제도를 장악했다. 2 그리고 베네딕트 자신도 그것을 잘 믿을 수 없었지만, 정말로 보스였다. 3 다른 의상을 입은 몇몇 사람들이 할로윈 파티에 함께 모인다. 4 미니 트램펄린에 앉아 물병을 공중에 던지는 여자. sentence2 gold_label 0 전화를 받은 적이 있습니까? entailment 1 제노바인들은 농업을 높이 평가했다. neutral 2 그는 그곳에 도착하기 위해 열심히 일했기 때문에 사장이었다. neutral 3 몇몇 사람들이 술을 마시고 있다 neutral 4 여자는 점프에 지쳤다. neutral
trains = train[:20000]
valid = train[20000:22000]
test = train[22000:24000]
print("premise 최대 길이:", trains['sentence1'].map(len).max())
print("hypothesis 최대 길이:", trains['sentence2'].map(len).max())
print("premise 최대 길이:", valid['sentence1'].map(len).max())
print("hypothesis 최대 길이:", valid['sentence2'].map(len).max())
print("premise 최대 길이:", test['sentence1'].map(len).max())
print("hypothesis 최대 길이:", test['sentence2'].map(len).max())
premise 최대 길이: 710 hypothesis 최대 길이: 163 premise 최대 길이: 412 hypothesis 최대 길이: 85 premise 최대 길이: 301 hypothesis 최대 길이: 109
max_seq_len = 250
MAX_LEN = 250
커스텀 학습
!pip install transformers
import torch
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as F
from transformers import TrainingArguments, Trainer
from transformers import AutoModel,AutoModelForSequenceClassification, AutoConfig, AutoTokenizer
from transformers import AdamW
from transformers import get_scheduler, get_cosine_with_hard_restarts_schedule_with_warmup
import warnings
warnings.filterwarnings('ignore')
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
BERT 파인 튜닝을 위한 입력데이터 변환
입력데이터를 (input_ids, attention_mask, 세그먼트 인코딩)으로 변환
input_ids는 토큰화 된 문장이다.
attention_mask는 pad 토큰이 있는 자리는 0, 나머지는 1을 반환한다다.
attention_mask의 길이는 항상 input_ids길이와 같아야 한다.
from transformers import AutoTokenizer
model_name = "klue/roberta-base"
tokenizer = AutoTokenizer.from_pretrained(model_name)
class BertSet(Dataset):
def __init__(self, dataset, labels, mode):
self.dataset = dataset
self.labels = labels
self.mode = mode
def __len__(self):
return len(self.dataset)
def __getitem__(self, idx):
sen1 = self.dataset.loc[idx, 'sentence1']
sen2 = self.dataset.loc[idx, 'sentence2']
item = tokenizer(sen1,
sen2,
return_tensors='pt',
max_length=MAX_LEN,
padding='max_length',
truncation=True,
add_special_tokens=True,
return_token_type_ids=True)
item['input_ids'] = item['input_ids'].squeeze(0)
item['attention_mask'] = item['attention_mask'].squeeze(0)
item['token_type_ids'] = item['token_type_ids'].squeeze(0)
if self.mode == 'train':
item['label'] = self.labels[idx]
return item
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y_train = le.fit_transform(trains['gold_label'])
y_valid = le.transform(valid['gold_label'])
trainset = BertSet(trains, y_train, 'train')
train_loader = DataLoader(trainset, batch_size=16, shuffle=False, num_workers=4)
valid = valid.reset_index(drop=True)
validset = BertSet(valid, y_valid, 'train')
valid_loader = DataLoader(validset, batch_size=16, shuffle=False, num_workers=4)
test = test.reset_index(drop=True)
testset = BertSet(test, None, 'test')
test_loader = DataLoader(testset, batch_size=16, shuffle=False, num_workers=4)
모델
class 수 만큼 num_labels를 지정
config = AutoConfig.from_pretrained(model_name)
config.num_labels = 3
model = AutoModelForSequenceClassification.from_pretrained(model_name, config=config).to(device)
num_epochs = 10
optimizer = AdamW(model.parameters(), lr= 1e-5)
lr_scheduler = get_cosine_with_hard_restarts_schedule_with_warmup(
optimizer=optimizer,
num_warmup_steps=1,
num_training_steps=num_epochs * len(train_loader),
)
def calc_accuracy(X,Y):
max_vals, max_indices = torch.max(X, 1)
train_acc = (max_indices == Y).sum().data.cpu().numpy()/max_indices.size()[0]
return train_acc
Train
for epoch in range(num_epochs):
train_acc = 0
train_losses = 0
model.train()
for batch_id, batch in enumerate(tqdm(train_loader)):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
token_type_ids = batch['token_type_ids'].to(device)
label = batch['label'].to(device)
outputs = model(input_ids, attention_mask, token_type_ids)
loss = F.cross_entropy(outputs[0], label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
lr_scheduler.step()
train_acc += calc_accuracy(outputs.logits, label)
train_losses += loss.item()
print("Epoch: {}, acc: {}, loss: {}".format(epoch+1, train_acc/(batch_id+1), train_losses/(batch_id+1)))
100%|██████████| 1250/1250 [14:42<00:00, 1.42it/s]
Epoch: 1, acc: 0.6602, loss: 0.750719735455513
100%|██████████| 1250/1250 [14:48<00:00, 1.41it/s]
Epoch: 2, acc: 0.8147, loss: 0.48823210109472276
100%|██████████| 1250/1250 [14:48<00:00, 1.41it/s]
Epoch: 3, acc: 0.87865, loss: 0.33945019143223765
100%|██████████| 1250/1250 [14:48<00:00, 1.41it/s]
Epoch: 4, acc: 0.92245, loss: 0.2307316340766847
100%|██████████| 1250/1250 [14:48<00:00, 1.41it/s]
Epoch: 5, acc: 0.9482, loss: 0.16217252725437284
100%|██████████| 1250/1250 [14:47<00:00, 1.41it/s]
Epoch: 6, acc: 0.96265, loss: 0.11964335745833814
100%|██████████| 1250/1250 [14:48<00:00, 1.41it/s]
Epoch: 7, acc: 0.9727, loss: 0.09230715118460357
100%|██████████| 1250/1250 [14:47<00:00, 1.41it/s]
Epoch: 8, acc: 0.97885, loss: 0.0730009714672342
100%|██████████| 1250/1250 [14:48<00:00, 1.41it/s]
Epoch: 9, acc: 0.9837, loss: 0.05952671880833805
100%|██████████| 1250/1250 [14:47<00:00, 1.41it/s]
Epoch: 10, acc: 0.98435, loss: 0.055092274013161656
Test
output_pred = []
model.eval()
for batch_id, batch in enumerate(test_loader):
with torch.no_grad():
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
token_type_ids = batch['token_type_ids'].to(device)
outputs = model(input_ids, attention_mask, token_type_ids)
logits = outputs[0]
logits = logits.detach().cpu().numpy()
result = np.argmax(logits, axis=-1)
output_pred.extend(result)
label_idx = dict(zip(list(le.classes_), le.transform(list(le.classes_))))
pred = [list(label_idx.keys())[_] for _ in output_pred]
pred[:5]
['contradiction', 'entailment', 'contradiction', 'entailment', 'contradiction']
Trainer를 이용한 학습
!pip install datasets
from transformers import Trainer, TrainingArguments, DataCollatorWithPadding, EarlyStoppingCallback
from datasets import load_metric
_collator = DataCollatorWithPadding(tokenizer=tokenizer)
_metric = load_metric('accuracy')
def METRIC(p):
logits, labels = p
output = _metric.compute(references=labels, predictions=np.argmax(logits, axis=-1))
return output
args = TrainingArguments(
'/content/',
do_eval=True,
do_train=True,
load_best_model_at_end = True,
save_strategy="epoch",
logging_strategy="epoch",
evaluation_strategy="epoch",
num_train_epochs = num_epochs,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
label_smoothing_factor=0.1
)
Trained_Model = Trainer(
model=model,
args=args,
data_collator=_collator,
train_dataset=trainset,
eval_dataset=validset,
tokenizer=tokenizer,
optimizers=(optimizer, lr_scheduler),
callbacks = [EarlyStoppingCallback(early_stopping_patience=3)],
compute_metrics=METRIC,
)
Trained_Model.train()
preds = Trained_Model.predict(testset)
output_pred = np.argmax(preds[0], axis=-1)
label_idx = dict(zip(list(le.classes_), le.transform(list(le.classes_))))
pred = [list(label_idx.keys())[_] for _ in output_pred]
pred[:5]
['contradiction', 'contradiction', 'contradiction', 'entailment', 'entailment']
댓글남기기