파인튜닝한 모델을 저장하고, 불러와서 사용해보자!
파인튜닝방법은 아래에서 볼 수 있다.
https://pleasestudy-alswldi.tistory.com/122
KoBERT로 감정분류 실습하기
https://velog.io/@danbibibi/KoBERT-fine-tuning-Sentiment-Analysis https://www.notion.so/KoBERT-f7dff49a989c4b9cbdce46afc906c3c1 2021년경 KoBERT모델의 기존 서버가 닫히고 huginggace hub로 이전되었다. 아래는 hugingface로 구현하는
pleasestudy-alswldi.tistory.com
🖥️모델저장
구글드라이브에 연동해서 모델을 저장한다.
#구글드라이브 연동
from google.colab import drive
drive.mount('/content/drive')
# 현재경로 /content/
import os
os.chdir('/content/drive/MyDrive/models/')
os.getcwd()
path = '/content/drive/MyDrive/models/'
torch.save(model, path + 'sentiment_analysis_model_epoch10.pt') # 전체 모델 저장
torch.save(model.state_dict(), 'sentiment_analysis_model_state_dict_epoch10.pt') # 모델 객체의 state_dict 저장
torch.save({
'model': model.state_dict(),
'optimizer': optimizer.state_dict()
}, 'sentiment_analysis_model_all_epoch10.tar') # 여러 가지 값 저장, 학습 중 진행 상황 저장을 위해 epoch, loss 값 등 일반 scalar값 저장 가능
👩💻모델 불러와서 사용하기
import os
from transformers import AdamW
os.chdir('/content/drive/MyDrive/models/')
model1 = torch.load('sentiment_analysis_model_epoch10.pt') # 전체 모델을 통째로 불러옴, 클래스 선언 필수
model1.load_state_dict(torch.load('sentiment_analysis_model_state_dict_epoch10.pt')) # state_dict를 불러 온 후, 모델에 저장
no_decay = ['bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
{'params': [p for n, p in model1.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01},
{'params': [p for n, p in model1.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
optimizer = AdamW(optimizer_grouped_parameters, lr=learning_rate)
checkpoint = torch.load('sentiment_analysis_model_all_epoch10.tar') # dict 불러오기
model1.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
load_state_dict()는 모델에 저장된 상태를 불러와서 모델에 적용하는 역할을 한다.
왜 이렇게 여러번 쓰였나?
-> 각각의 파일에서 모델의 상태를 다르게 저장했기 때문이다! 전체 모델을 저장한 파일에서 모델을 불러오고, 모델의 매개변수 또는 모델과 옵티마이저의 상태를 저장한 파일에서 각각 불러온다.
- torch.load('sentiment_analysis_model_epoch10.pt'): 저장된 'sentiment_analysis_model_epoch10.pt' 파일에서 모델을 불러온다.
주의 : 모델을 불러오기 위해서는 모델 클래스를 미리 정의하고 있어야 한다. - torch.load('sentiment_analysis_model_state_dict_epoch10.pt'): 'sentiment_analysis_model_state_dict_epoch10.pt' 파일에서 모델의 state_dict를 불러온다. (state_dict는 모델의 매개변수를 담고 있는 딕셔너리 -모델을 저장할 때 모델의 매개변수만을 저장하고 싶을 때 사용된다)
- torch.load('sentiment_analysis_model_all_epoch10.tar'): 'sentiment_analysis_model_all_epoch10.tar' 파일에서 모델과 옵티마이저의 상태를 모두 불러온다. (딕셔너리 형태로 모델과 옵티마이저의 상태를 함께 저장) 체크포인트를 저장하고 나중에 불러와서 계속 학습을 진행할 수도 있다!
전체 모델을 저장한 파일에서 모델을 불러올 때, 일반적으로 아래 두 가지 방법이 있다.
- 전체 모델을 불러오기: 전체 모델을 저장한 파일을 불러와서 모델을 통째로 불러오는 방법으로 이 경우 모델의 모든 구성 요소와 매개변수가 한 번에 불러온다.
- 상태 사전(state_dict)만 불러오기: 모델의 매개변수만을 저장한 파일에서는 모델의 구조를 다시 정의하지 않고 모델의 상태 사전만을 불러온다. ( 상태 사전 : 모델의 학습 가능한 매개변수와 해당 매개변수의 이름을 매핑한 딕셔너리)
각각의 방법은 서로 다른 용도로 사용되기 때문에 모델을 정확하게 복원하고 유지하기 위해 필요하다! 전체 모델을 저장하고 불러오는 방법은 모델의 구조와 매개변수를 모두 저장하고 불러오는 데 유용하며, 상태 사전을 저장하고 불러오는 방법은 모델의 학습 가능한 가중치만을 관리할 때 좋다.
따라서 두 가지 방법을 함께 사용하여 모델을 저장하고 복원하는 것이 일반적이고 올바른 방법이라고 한다!
불러온 모델을 사용해보자
프로젝트에서는 입력한 여러 문장에서 나타나는 세가지 감정을 뽑아내고 있다.
시간순서대로 뽑기위해 여러문장을 3부분으로 나누고, 각각 가장 높게 나온 확률클래스를 출력한다!
def find_most_frequent(lst):
# 감정 라벨
labels = ['우울', '기쁨', '화남', '슬픔', '편안', '걱정', '신남', '충만']
# 리스트를 3등분
chunk_size = len(lst) // 3 # 리스트의 길이를 3으로 나눈 몫을 등분 크기로 설정
remainder = len(lst) % 3 # 리스트를 3으로 나눈 나머지 계산
chunks = []
start = 0
# 남는 나머지가 있을 경우, 등분 크기를 1씩 늘려가며 등분
for i in range(3):
end = start + chunk_size + (1 if i < remainder else 0)
chunks.append(lst[start:end])
start = end
result = []
# 각 부분에서 확률값을 합산하여 가장 높은 라벨 찾기
for chunk in chunks:
# 각 라벨별로 확률값 합산
label_sum = [0] * len(chunk[0]) # 라벨의 개수에 따라 초기화
for probs in chunk:
for i, prob in enumerate(probs):
label_sum[i] += prob
# 가장 높은 합산값을 가진 라벨 찾기
most_common_label = label_sum.index(max(label_sum))
# 가장 높은 합산값을 가진 라벨을 감정으로 변환하여 결과에 추가
result.append(labels[most_common_label])
# 결과 출력
print(' '.join(result))
import torch.nn.functional as F
def predict(predict_sentence):
labels = ['우울', '기쁨', '화남', '슬픔', '편안', '걱정', '신남', '충만']
result = ''
data = [predict_sentence, '0']
dataset_another = [data]
another_test = BERTDataset(dataset_another, 0, 1, tokenizer, vocab, max_len, True, False)
test_dataloader = torch.utils.data.DataLoader(another_test, batch_size=batch_size, num_workers=5)
model1.eval()
for batch_id, (token_ids, valid_length, segment_ids, label) in enumerate(test_dataloader):
token_ids = token_ids.long().to(device)
segment_ids = segment_ids.long().to(device)
valid_length = valid_length
label = label.long().to(device)
out = model1(token_ids, valid_length, segment_ids)
# 소프트맥스 함수를 적용하여 확률값 계산
probabilities = F.softmax(out, dim=1)
for prob in probabilities:
print("확률값:", prob)
print("가장 높은 확률 클래스:", labels[torch.argmax(prob).item()])
return prob
문장을 분류하기 위해 kss를 사용했다.
import kss
from kobert_tokenizer import KoBERTTokenizer
tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1')
#질문 무한반복하기! 0 입력시 종료
while True:
sentence = input("하고싶은 말을 입력해주세요 : ")
if sentence == 0 :
break
result=[]
for sent in kss.split_sentences(sentence):
print(sent)
result.append(predict(sent))
find_most_frequent(result)
입력 예시)
오늘은 평범한 하루였지만, 마음에 다양한 감정이 교차했다. 아침에는 작업을 시작하려고 했는데 예상치 못한 문제로 인해 불편함을 느꼈다. 그래서 처음에는 조금 답답하고 힘들었다. 하지만 문제를 해결하고 나서는 안도감과 해방감이 들었다. 그 순간의 쾌감은 내 마음을 기쁘게 만들었다. 그러나 오후에는 갑작스러운 소식으로 인해 걱정이 앞섰다. 뜻밖의 일에 당황스러움과 불안함이 느껴졌다. 그래서 마음이 조금 불안해졌고, 슬픔이 스며들었다. 하지만 가족과 이야기하고 문제를 해결하는 과정에서 안정감과 희망을 느낄 수 있었다. 그로 인해 마음이 안정되었고, 행복함을 느낄 수 있었다. 하루가 끝나고 나서는 피곤함과 만족감이 함께 느껴졌다. 오늘의 감정은 파도처럼 번져가듯 변화했지만, 그것이 내게 새로운 경험이 되었다. 변화무쌍한 하루를 보내며 느낀 것은, 삶은 항상 다양한 감정으로 가득 차 있고, 그것이 삶의 풍요로움이라는 것이다.
출력 결과) 우울 걱정 충만
오늘은 평범한 하루였지만, 마음에 다양한 감정이 교차했다. 확률값: tensor([2.1910e-01, 1.9121e-04, 1.5869e-03, 6.6834e-01, 1.0779e-01, 1.5219e-03, 1.1415e-03, 3.2715e-04], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 슬픔
아침에는 작업을 시작하려고 했는데 예상치 못한 문제로 인해 불편함을 느꼈다. 확률값: tensor([9.4013e-02, 1.5340e-04, 6.2196e-04, 4.8124e-01, 6.1895e-04, 4.2151e-01, 1.7377e-03, 9.6548e-05], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 슬픔
그래서 처음에는 조금 답답하고 힘들었다. 확률값: tensor([9.9884e-01, 1.5884e-04, 1.4287e-04, 2.6035e-04, 1.4277e-04, 1.7328e-04, 1.1804e-04, 1.5984e-04], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 우울
하지만 문제를 해결하고 나서는 안도감과 해방감이 들었다. 확률값: tensor([3.1525e-05, 2.0341e-05, 4.4079e-05, 1.2623e-04, 9.9965e-01, 3.3301e-05, 6.5023e-05, 3.1512e-05], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 편안
그 순간의 쾌감은 내 마음을 기쁘게 만들었다. 확률값: tensor([1.8050e-05, 9.9981e-01, 1.8831e-05, 1.1434e-05, 2.1731e-05, 8.8051e-06, 5.7221e-05, 5.7102e-05], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 기쁨
그러나 오후에는 갑작스러운 소식으로 인해 걱정이 앞섰다. 확률값: tensor([1.7131e-04, 4.0686e-05, 1.8406e-04, 1.2127e-04, 7.8323e-05, 9.9931e-01, 3.9634e-05, 5.0493e-05], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 걱정
뜻밖의 일에 당황스러움과 불안함이 느껴졌다. 확률값: tensor([1.9405e-04, 3.2989e-05, 1.5628e-04, 1.4225e-04, 5.4804e-05, 9.9935e-01, 3.1562e-05, 3.4206e-05], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 걱정
그래서 마음이 조금 불안해졌고, 슬픔이 스며들었다. 확률값: tensor([1.1814e-04, 3.2288e-05, 1.2295e-04, 7.7069e-04, 7.9733e-05, 9.9881e-01, 2.2191e-05, 3.9661e-05], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 걱정
하지만 가족과 이야기하고 문제를 해결하는 과정에서 안정감과 희망을 느낄 수 있었다. 확률값: tensor([1.4117e-04, 2.3883e-05, 2.3189e-05, 5.8661e-05, 9.9959e-01, 2.6258e-05, 2.6456e-05, 1.1414e-04], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 편안
그로 인해 마음이 안정되었고, 행복함을 느낄 수 있었다. 확률값: tensor([2.2248e-05, 3.9982e-05, 1.2730e-05, 7.6719e-06, 2.4665e-05, 5.2909e-05, 2.7959e-05, 9.9981e-01], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 충만
하루가 끝나고 나서는 피곤함과 만족감이 함께 느껴졌다. 확률값: tensor([5.3315e-05, 3.5346e-05, 1.4953e-05, 5.1512e-06, 2.7131e-05, 4.5555e-05, 2.6579e-05, 9.9979e-01], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 충만
오늘의 감정은 파도처럼 번져가듯 변화했지만, 그것이 내게 새로운 경험이 되었다. 확률값: tensor([3.7849e-01, 9.2820e-04, 7.4004e-03, 7.1504e-03, 5.8511e-01, 6.9939e-04, 1.9887e-02, 3.3914e-04], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 편안
변화무쌍한 하루를 보내며 느낀 것은, 삶은 항상 다양한 감정으로 가득 차 있고, 그것이 삶의 풍요로움이라는 것이다. 확률값: tensor([2.3920e-02, 1.4874e-03, 4.5738e-03, 5.0912e-02, 7.2834e-01, 5.7492e-04, 1.8969e-01, 5.0508e-04], device='cuda:0', grad_fn=<UnbindBackward0>) 가장 높은 확률 클래스: 편안
덧)
최근 다시 실행해보니 아래와 같은 에러가 발생했다.
AttributeError: 'BertModel' object has no attribute 'attn_implementation'
transformer가 업데이트 되면서 발생한 문제 같다 .. 4.41에서 4.37로 다운그레이드 시키니 해결!
'AI' 카테고리의 다른 글
비지도학습 다변량 데이터 이상탐지 - IsolationForest (0) | 2024.08.21 |
---|---|
[논문 리뷰] BERT : Pre-training of Deep Bidirectional Transformers for Language Understanding (0) | 2024.05.21 |
생성형 인공지능 이미지 생성 GAN, diffusion (0) | 2024.05.04 |
KoBERT로 감정분류 실습하기 (1) | 2024.04.02 |
BERT, KoBERT 자연어 처리 모델 (0) | 2024.04.02 |