반응형
16.2 KoBERT 사전학습모형에 대한 파이토치 기반 미세조정학습
!pip install sentencepiece
!pip install 'git+https://github.com/SKTBrain/KoBERT.git#egg=kobert_tokenizer&subdirectory=kobert_hf'
del model
del trainer
torch.cuda.empty_cache()
from kobert_tokenizer import KoBERTTokenizer
tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1')
print(tokenizer.tokenize("안녕하세요. 반갑습니다."))
inputs = tokenizer("안녕하세요. 반갑습니다.")
print(inputs)
"""
['▁안', '녕', '하세요', '.', '▁반', '갑', '습니다', '.']
{'input_ids': [2, 3135, 5724, 7814, 54, 2207, 5345, 6701, 54, 3], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
"""
from transformers import BertModel
from torch.utils.data import DataLoader
# 토큰화
train_input = tokenizer(X_train, truncation=True, padding=True, return_tensors="pt")
val_input = tokenizer(X_val, truncation=True, padding=True, return_tensors="pt")
test_input = tokenizer(X_test, truncation=True, padding=True, return_tensors="pt")
# Dataset 생성
train_dataset = OurDataset(train_input, y_train)
val_dataset = OurDataset(val_input, y_val)
test_dataset = OurDataset(test_input, y_test)
# 데이터로더 생성
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=8)
val_loader = DataLoader(val_dataset, batch_size=16)
test_loader = DataLoader(test_dataset, batch_size=16)
# KoBERT 사전학습모형 로드
bert_model = BertModel.from_pretrained('skt/kobert-base-v1')
# 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, get_linear_schedule_with_warmup
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() # 학습모드로 전환
# 옵티마이저를 트랜스포머가 제공하는 AdamW로 설정
optim = AdamW(model.parameters(), lr=5e-5, weight_decay=0.01) # 가중치 감쇠 설정
criterion = torch.nn.CrossEntropyLoss() # 멀티클래스이므로 크로스 엔트로피를 손실함수로 사용
num_epochs = 2 # 학습 epoch를 3회로 설정
total_training_steps = num_epochs * len(train_loader)
# 학습 스케줄러 설정
scheduler = get_linear_schedule_with_warmup(optimizer=optim,
num_training_steps=total_training_steps,
num_warmup_steps=200)
start = time.time() # 시작시간 기록
train_loss = 0
eval_steps = 500
step = 0
for epoch in range(num_epochs):
#total_epoch_loss = 0 # epoch의 총 loss 초기화
for batch in train_loader:
model.train() # 학습모드로 전환
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 계산
train_loss += loss
loss.backward() # 그래디언트 계산
optim.step() # 가중치 업데이트
scheduler.step() # 스케줄러 업데이트
step += 1
if step % eval_steps == 0: # eval_steps 마다 경과한 시간과 loss를 출력
with torch.no_grad():
val_loss = 0
model.eval()
for batch in val_loader:
inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
labels = batch['labels'].to(device)
outputs = model(inputs)
loss = criterion(outputs, F.one_hot(labels, num_classes=2).float()) # loss 계산
val_loss += loss
avg_val_loss = val_loss / len(val_loader)
avg_train_loss = train_loss / eval_steps
elapsed = time.time() - start
print('Step %d, elapsed time: %.2f, train loss: %.4f, validation loss: %.4f'
% (step, elapsed, avg_train_loss, avg_val_loss))
train_loss = 0
"""
Step 500, elapsed time: 141.06, train loss: 0.4933, validation loss: 0.4198
Step 1000, elapsed time: 281.86, train loss: 0.3552, validation loss: 0.3710
Step 1500, elapsed time: 423.89, train loss: 0.2663, validation loss: 0.2829
Step 2000, elapsed time: 566.67, train loss: 0.2225, validation loss: 0.2967
"""
from datasets import load_metric
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.8693644758283542}
"""
※ 해당 내용은 <파이썬 텍스트 마이닝 완벽 가이드>의 내용을 토대로 학습하며 정리한 내용입니다.
반응형
'텍스트 마이닝' 카테고리의 다른 글
한국어 문서에 대한 BERT 활용 (1) (0) | 2023.08.05 |
---|---|
BERT 사전학습 모형에 대한 미세조정학습 (3) (0) | 2023.08.04 |
BERT 사전학습 모형에 대한 미세조정학습 (2) (0) | 2023.08.03 |
BERT 사전학습 모형에 대한 미세조정학습 (1) (0) | 2023.08.02 |
BERT의 이해와 간단한 활용 (2) (0) | 2023.08.01 |