Transformers Library는 Pipline라는 모듈을 제공해 zero-shot 학습 추론 가능.
해당 Library는 많은 모델이 있다.
Pipeline
감성분석: pipeline('sentiment-analysis')
import transformers
from transformers import pipeline #pipeline: 모듈, 이를 통해 zero-shot 학습 추론 가능
sentiment = pipeline('sentiment-analysis')
print(sentiment(["I like Olympic games as it's very exciting."]))
print(sentiment(["I'm against to hold Olympic games in Tokyo in terms of preventing the covid19 to be spread."]))
질의응답: question-answering pipeline from Transformers Library
question-answering을 파이프라인에 변수로 저장
from transformers import pipeline
qa = pipeline("question-answering")
olympic_wiki_text = """
The 2020 Summer Olympics (Japanese: 2020年夏季オリンピック, Hepburn: Nisen Nijū-nen Kaki Orinpikku), officially the Games of the XXXII Olympiad (第三十二回オリンピック競技大会, Dai Sanjūni-kai Orinpikku Kyōgi Taikai) and branded as Tokyo 2020 (東京2020), is an ongoing international multi-sport event being held from 23 July to 8 August 2021 in Tokyo, Japan, with some preliminary events that began on 21 July.
The 2020 Games introduced new competitions and re-introduced competitions that once were held but were subsequently removed. New ones include 3x3 basketball, freestyle BMX and mixed gender team events in a number of existing sports, as well as the return of madison cycling for men and an introduction of the same event for women. New IOC policies also allow the host organizing committee to add new sports to the Olympic program for just one Games. The disciplines added by the Japanese Olympic Committee are baseball and softball, karate, sport climbing, surfing, and skateboarding, the last four of which make their Olympic debuts.[7]
Bermuda, the Philippines, and Qatar won their first-ever Olympic gold medals.[8][9][10] San Marino, Turkmenistan, and Burkina Faso won their first-ever Olympic medals.[11][12][13]
"""
print(qa(question="What caused Tokyo Olympic postponed?", context=olympic_wiki_text))
DistillBERT FIne-Tuning & Evaluation
Transformer 기반 모델은 unlabled Text를 가지고 지도 학습을 완료함.
Fine-Tuning: 사전학습 모델의 파라미터를 초깃값으로 사용하지만, 주어진 Task와 Dataset으로 학습을 진행해 모델의 파라미터를 업데이트한다.
IMBD Dataset:영화 리뷰 코멘트의 긍정/부정 감정을 판단하기 위한 Sentiment Analysis Dataset
torchtext: Pytorch의 자연어 전처리용 Library
04. IMDB Dataset
from torchtext.datasets import IMDB
train_iter = IMDB(split='train')
test_iter = IMDB(split='test')
#train,test를 list로 변환
train_lists = list(train_iter)
test_lists = list(test_iter)
# 1000개씩 Random Sampling
train_lists_small = random.sample(train_lists, 1000)
test_lists_small = random.sample(test_lists, 1000)
05. Label Encoding
원 Dataset에는 긍정 Label : 2, 부정 Label : 1로 구성됨
긍정:1, 부정:0으로 변환하자.
#Train
train_texts = []
train_labels = []
for label, text in train_lists_small:
train_labels.append(1 if label == 2 else 0)
train_texts.append(text)
#Test
test_texts = []
test_labels = []
for label, text in test_lists_small:
test_labels.append(1 if label == 2 else 0)
test_texts.append(text)
06. Train/Valid/Test 분리
Train Data를 Train/Valid로 분리
from sklearn.model_selection import train_test_split
train_texts, val_texts, train_labels, val_labels = train_test_split(train_texts, train_labels, test_size=.2)
07. Tokenization & Encoding
from transformers import DistilBertTokenizerFast
tokenizer = DistilBertTokenizerFast.from_pretrained('distilbert-base-uncased')
#distilbert-base-uncased모델이 상대적으로 가볍기에 토크나이저 모델로 사용
# 토크나이징을 통한 인코딩
#truncation = True: 모델의 디폴트 max_length를 넘는 부분을 더 이상 받지 않는다
#padding = True: 데이터의 크기나 모양을 일치시키기
train_encodings = tokenizer(train_texts, truncation=True, padding=True)
val_encodings = tokenizer(val_texts, truncation=True, padding=True)
test_encodings = tokenizer(test_texts, truncation=True, padding=True)
# 위의 결과를 디코딩하여 출력:Encoding은 텍스트를 숫자로 바꾸고, Decoding은 숫자를 텍스트로 바꾼다.
print(tokenizer.decode(train_encodings["input_ids"][0][:5]))
08. Dataset Class 생성
Torch.utils.data.Dataset 상속하는 IMDbDataset Class 생성
# __getitem__()은 대괄호 [] 안에 표기된 인덱스와 결합된 객체를 얻어 오는 파이썬 매서드(메서드는 클래스 내에 포함되어 있는 함수)
# 인덱싱하거나 슬라이싱할 때 호출되는 메서드. 이 메서드를 정의하면 해당 클래스의 객체를 리스트와 유사하게 사용 가능.
import torch
# Dataset 클래스를 상속하는 IMDBDataset 클래스를 정의
class IMDbDataset(torch.utils.data.Dataset):
def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labels
def __getitem__(self, idx):
# self.encoding에 담긴 키(key)와 키값(value)을 items()로 추출
item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()}
# 주어진 idx에 해당하는 텍스트 데이터를 encodings에서 추출하고, 해당 텍스트 데이터를 PyTorch 텐서로 변환하여 딕셔너리 형태로 반환.
# 이때, 딕셔너리는 모델의 입력으로 사용되는 데이터 포맷에 따라 'input_ids', 'attention_mask', 'labels' 등을 포함.
# labels는 해당 리뷰의 긍정 또는 부정 레이블 담고있음.
# self.lables[idx]에 담긴 데이터를 torch.tensor()를 통해 파이토치 텐서 변환
# attention_mask: 패딩 위치를 나타냄.
# input_ids: 단어 ID
item['labels'] = torch.tensor(self.labels[idx])
return item
# 자신을 가르키는 매개변수 self 포함
def __len__(self):
return len(self.labels)
train_dataset = IMDbDataset(train_encodings, train_labels)
val_dataset = IMDbDataset(val_encodings, val_labels)
test_dataset = IMDbDataset(test_encodings, test_labels)
09. 사전학습 모델 불러오기
from transformers import DistilBertForSequenceClassification
# distilbert 모델 불러오기
model = DistilBertForSequenceClassification.from_pretrained("distilbert-base-uncased")
10. TrainingArguments 설정
TrainingArguments 불러와 하이퍼파라미터를 정의한다.
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir='./results', # 출력 디렉토리 경로
num_train_epochs=8, # 학습 에포크 수
per_device_train_batch_size=16, # 학습시 (디바이스 별) 미니 배치 수
per_device_eval_batch_size=64, # 평가시 (디바이스 별) 미니 배치 수
warmup_steps=500, # 학습률 스케줄링용 웜업 스텝 수
weight_decay=0.01, # 가중치 감쇄 강도
logging_dir='./logs', # 로그 디렉토리 경로
logging_steps=10,
)
11. GPU 설정
Torch.tensors 타입의 데이터는 model.to(device)를 통해 GPU로 전달.
import torch
# GPU 사용이 가능한 경우 텐서 타입 데이터를 GPU로 전달
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
12. Trainer Class 사용한 Fine-Tuning
Trainer Class를 인스턴스화하고 Fine-Tuning
Fine Tuning 이전 도출
# 파인튜닝 이전의 Positive/Negative 판단
# 입력 문장 토크나이징 결과(input_tokens)에 담긴 input_ids를 모델에 투입
# 그리고 모델 출력 결과를 GPU로 전송하며 값은 변수 outputs에 저장
# model = DistilBertForSequenceClassification.from_pretrained("distilbert-base-uncased")
input_tokens = tokenizer(["I feel fantastic", "My life is going something wrong", "I have not figured out what the chosen title has to do with the movie."], truncation=True, padding=True)
outputs = model(torch.tensor(input_tokens['input_ids']).to(device))
# output의 결과는 logit으로 나온다. Rogistic Regression에서 사용.
# 레이블 딕셔너리 생성
label_dict = {1:'positive', 0:'negative'}
# outputs 변수에 담긴 logits 값을 행 단위, 즉 입력 문장 단위로 가장 큰 값 위치(인덱스) 추출
# 그 결과값(인덱스)을 cpu로 넘기고 넘파이 타입으로 변경 후, 인덱스에 매칭되는 레이블 출력
print([label_dict[i] for i in torch.argmax(outputs['logits'], axis=1).cpu().numpy()])
model의 output은 logit 형태로 출력된다. logit은 log odds로 Logistic Regression에서 파라미터 추정 시 사용한다.
또한, 머신러닝 및 딥러닝에서 각 클래스 또는 카테고리에 대한 예측값을 나타내는 용어로 'logit'을 사용하기도 한다.
Fine Tuning 이후 도출
Fine Tuning은 Train.train을 사용해 사용한다.
from transformers import Trainer
trainer = Trainer(
model=model, # 사전학습 모델 인스턴스화
args=training_args, # TrainingArguments에 정의한 하이퍼 파라미터값 가져오기
train_dataset=train_dataset, # 학습 데이터세트
eval_dataset=val_dataset # evaluation 데이터세트
)
trainer.train()
# Trainer.train()에 의한 파인튜팅 이후 판별
input_tokens = tokenizer(["I feel fantastic", "My life is going something wrong", "I have not figured out what the chosen title has to do with the movie."], truncation=True, padding=True)
outputs = model(torch.tensor(input_tokens['input_ids']).to(device))
# 영어 원문의 1과 0값과 달리 일반적인 용례에 따라 변경
label_dict = {1:'positive', 0:'negative'}
print([label_dict[i] for i in torch.argmax(outputs['logits'], axis=1).cpu().numpy()])
13. Pytorch Fine-Tuning
- Pre-trained model & Tokenizer 불러오기
- DataLoader 객체 생성
- Pytorch의 DataLoader를 사용해 batch 단위로 데이터를 읽고 모델에 전달한다.
- Optimization 함수 정의
- 모델을 Training 모드로 전환
- Epoch 횟수만큼 루프 반복
- Gradient Initialize(기울기 초기화)
- Model을 사용한 추론
- Loss 계산
- Back Propagation
- 가중치 업데이트
학습 전후의 추론 결과 위해 다음과 같은 test_inference 함수 생성
def test_inference(model, tokenizer):
input_tokens = tokenizer(["I feel fantastic", "My life is going something wrong", "I have not figured out what the chosen title has to do with the movie."], truncation=True, padding=True)
outputs = model(torch.tensor(input_tokens['input_ids']).to(device))
# 영어 원문의 1과 0값 배정고 달리 일반적인 용례에 따라 변경
label_dict = {1:'positive', 0:'negative'}
return [label_dict[i] for i in torch.argmax(outputs['logits'], axis=1).cpu().numpy()]
from torch.utils.data import DataLoader
from transformers import DistilBertForSequenceClassification, AdamW
from transformers import DistilBertTokenizerFast
#1) 사전학습 모델과 토크나이저 불러오기
tokenizer = DistilBertTokenizerFast.from_pretrained('distilbert-base-uncased')
model = DistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased')
model.to(device)
# 파인튜닝 이전 모델을 사용하여 test_inference 함수 실행
print(test_inference(model, tokenizer))
#2)DataLoader 인스턴스화
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
#3)최적화 함수 정의
optim = AdamW(model.parameters(), lr=5e-5)
#4)모델을 학습(train) 모드로 전환
model.train()
losses = []
#5)에포크 횟수(epochs)만큼 루프 반복
for epoch in range(8):
print(f'epoch:{epoch}')
for batch in train_loader:
#6)최적화 함수의 기울기(그래디언트) 초기화
optim.zero_grad()
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
#7) 모델을 사용한 추론
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
#8) 손실 계산
loss = outputs[0]
losses.append(loss)
#9) 오차역전파
loss.backward()
#10) 가중치(weight) 업데이트
optim.step()
# 모델을 eval 모드로 전환
model.eval()
# eval 모드를 사용하여 test_inference 함수 실행
print(test_inference(model, tokenizer))
- model.eval(): Model을 훈련모드로 설정하면 모델이 가중치를 업데이트하고 학습 중 사용되는 기능들 활성화한다.
- Dropout, Batch Normalization와 같은 Regularization 활성화
- Model이 학습 데이터를 처리할 때 동적 Gradient 계산 수행
- 즉, 모델이 학습 데이터로부터 가중치를 조정하고 손실을 최소화하려 함.
- model.eval(): Model을 평가모드로 설정하면 가중치를 업데이트하지 않고, 학습 중 사용되는 교제 기법들이 비활성화된다.
- 모델은 입력 데이터를 처리하고 예측을 생성하기만 하며, 학습을 수행하지 않음.
- 평가 모드는 모델의 성능을 평가하거나 추론을 수행할 때 사용.
optimizer.zero_grad() #Gradient 초기화 해 이전 미니배치의 Gradient의 영향을 없앤다.
outputs = model(input)
loss = MSE
loss.backward() #Back propagation을 통해 Gradient 계산
optimizer.step() #Gradient와 learning rate 바탕으로 Parameter update
해당 게시글은 101가지 문제로 배우는 딥러닝 허깅페이스 트랜스포머 with 파이토치/조슈아 K.케이지/임선집 번역 책을 바탕으로 작성된 글입니다.