본문 바로가기

텍스트 마이닝

BERT 사전학습 모형에 대한 미세조정학습 (3)

반응형

15.3 파이토치를 이용한 미세조정학습

del model
del trainer
torch.cuda.empty_cache()
from torch.utils.data import DataLoader

train_loader = DataLoader(train_dataset, shuffle=True, batch_size=8)
from transformers import BertModel

bert_model = BertModel.from_pretrained('bert-base-uncased')
# BERT를 포함한 신경망 모형
class MyModel(torch.nn.Module):
    def __init__(self, pretrained_model, token_size, num_labels): 
        super(MyModel, self).__init__()
        self.token_size = token_size
        self.num_labels = num_labels
        self.pretrained_model = pretrained_model

        # 분류기 정의
        self.classifier = torch.nn.Linear(self.token_size, self.num_labels)

    def forward(self, inputs):
        # BERT 모형에 입력을 넣고 출력을 받음
        outputs = self.pretrained_model(**inputs)
        # BERT 출력에서 CLS 토큰에 해당하는 부분만 가져옴
        bert_clf_token = outputs.last_hidden_state[:,0,:]
        
        return self.classifier(bert_clf_token)

# token_size는 BERT 토큰과 동일, bert_model.config.hidden_size로 알 수 있음
model = MyModel(bert_model, num_labels=2, token_size=bert_model.config.hidden_size)
from transformers import AdamW
import torch.nn.functional as F
import time

# GPU 가속을 사용할 수 있으면 device를 cuda로 설정하고, 아니면 cpu로 설정
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

model.to(device)  # 모형을 GPU로 복사
model.train()     # 학습모드로 전환

optim = AdamW(model.parameters(), lr=5e-5) # 옵티마이저를 트랜스포머가 제공하는 AdamW로 설정
criterion = torch.nn.CrossEntropyLoss()    # 멀티클래스이므로 크로스 엔트로피를 손실함수로 사용

start = time.time() # 시작시간 기록
num_epochs = 4      # 학습 epoch를 4회로 설정
for epoch in range(num_epochs):
    total_epoch_loss = 0  # epoch의 총 loss 초기화
    for step, batch in enumerate(train_loader):
        optim.zero_grad()     # 그래디언트 초기화
        # 배치에서 label을 제외한 입력만 추출하여  GPU로 복사
        inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'} 
        labels = batch['labels'].to(device) # 배치에서 라벨을 추출하여 GPU로 복사
        outputs = model(inputs) # 모형으로 결과 예측
        # 두 클래스에 대해 예측하고 각각 비교해야 하므로 labels에 대해 원핫인코딩을 적용한 후에 손실을 게산
        loss = criterion(outputs, F.one_hot(labels, num_classes=2).float()) # loss 계산

        if (step+1) % 100 == 0: # 100 배치마다 경과한 시간과 loss를 출력
            elapsed = time.time() - start
            print('Epoch %d, batch %d, elapsed time: %.2f, loss: %.4f' % (epoch+1, step+1, elapsed, loss))
        total_epoch_loss += loss
        loss.backward() # 그래디언트 계산
        optim.step()    # 가중치 업데이트
    avg_epoch_loss = total_epoch_loss / len(train_loader) # epoch의 평균 loss 계산
    print('Average loss for epoch %d: %.4f' % (epoch+1, avg_epoch_loss))



"""
Epoch 1, batch 100, elapsed time: 39.77, loss: 0.5169
Epoch 1, batch 200, elapsed time: 80.67, loss: 0.3300
Average loss for epoch 1: 0.5417
Epoch 2, batch 100, elapsed time: 121.59, loss: 0.5303
Epoch 2, batch 200, elapsed time: 162.57, loss: 0.1188
Average loss for epoch 2: 0.2969
Epoch 3, batch 100, elapsed time: 203.50, loss: 0.0132
Epoch 3, batch 200, elapsed time: 244.48, loss: 0.0130
Average loss for epoch 3: 0.1258
Epoch 4, batch 100, elapsed time: 285.65, loss: 0.0095
Epoch 4, batch 200, elapsed time: 327.03, loss: 0.0188
Average loss for epoch 4: 0.1103
"""
from datasets import load_metric

test_loader = DataLoader(test_dataset, batch_size=16)

metric= load_metric("accuracy")
model.eval()
for batch in test_loader:
    inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
    labels = batch['labels'].to(device)
    
    with torch.no_grad(): # 학습할 필요가 없으므로 그래디언트 계산을 끔
        outputs = model(inputs)
        #print(outputs)

    predictions = torch.argmax(outputs, dim=-1)
    metric.add_batch(predictions=predictions, references=labels)

metric.compute()

"""
{'accuracy': 0.8825}
"""

 

 

 

 

 

※ 해당 내용은 <파이썬 텍스트 마이닝 완벽 가이드>의 내용을 토대로 학습하며 정리한 내용입니다.

반응형