이제 노래 가사도 크롤링했고 모델도 학습했으니 노래 가사의 감정을 분석하는 일만 남았습니다. 크롤링한 곡들 중 일부 곡들을 골라서 노래 가사의 감정 분석을 해보았습니다.
import torch
from torch import nn
from torch.utils.data import Dataset
import gluonnlp as nlp
import numpy as np
from kobert.utils import get_tokenizer
from kobert.pytorch_kobert import get_pytorch_kobert_model
import pandas as pd
import re
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))
bertmodel, vocab = get_pytorch_kobert_model()
class BERTDataset(Dataset):
def __init__(self, dataset, sent_idx, label_idx, bert_tokenizer, max_len,
pad, pair):
transform = nlp.data.BERTSentenceTransform(
bert_tokenizer, max_seq_length=max_len, pad=pad, pair=pair)
self.sentences = [transform([i[sent_idx]]) for i in dataset]
self.labels = [np.int32(i[label_idx]) for i in dataset]
def __getitem__(self, i):
return (self.sentences[i] + (self.labels[i],))
def __len__(self):
return (len(self.labels))
class BERTClassifier(nn.Module):
def __init__(self,
bert,
hidden_size=768,
num_classes=6,
dr_rate=None,
params=None):
super(BERTClassifier, self).__init__()
self.bert = bert
self.dr_rate = dr_rate
self.classifier = nn.Linear(hidden_size, num_classes)
if dr_rate:
self.dropout = nn.Dropout(p=dr_rate)
def gen_attention_mask(self, token_ids, valid_length):
attention_mask = torch.zeros_like(token_ids)
for i, v in enumerate(valid_length):
attention_mask[i][:v] = 1
return attention_mask.float()
def forward(self, token_ids, valid_length, segment_ids):
attention_mask = self.gen_attention_mask(token_ids, valid_length)
_, pooler = self.bert(input_ids=token_ids, token_type_ids=segment_ids.long(),
attention_mask=attention_mask.float().to(token_ids.device))
if self.dr_rate:
out = self.dropout(pooler)
return self.classifier(out)
max_len = 512
batch_size = 32
warmup_ratio = 0.1
num_epochs = 5
max_grad_norm = 1
log_interval = 100
learning_rate = 5e-5
model = torch.load('C:/projects/NLP/KoBERT/Model/model.pt')
model.load_state_dict(torch.load('C:/projects/NLP/KoBERT/Model/model_state_dict.pt'))
tokenizer = get_tokenizer()
tok = nlp.data.BERTSPTokenizer(tokenizer, vocab, lower=False)
pred = []
# 예측 함수
def testModel(a, s, ly):
cate = ['기쁨', '분노', '상처', '슬픔', '불안', '당황']
ly = re.sub('[a-zA-z]', '', ly)
ly = re.sub("\n", ' ', ly)
ly = re.sub('[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]', '', ly)
transform = nlp.data.BERTSentenceTransform(tok, max_len, pad=True, pair=False)
tokenized = transform(ly)
model.eval()
result = model(torch.tensor([tokenized[0]]).to(device), [tokenized[1]], torch.tensor(tokenized[2]).to(device))
idx = result.argmax().cpu().item()
pred.append(cate[idx])
print(a+"의 "+s+"은(는) "+cate[idx]+"로 분류되었습니다.\n")
testData = pd.read_csv('C:/projects/NLP/KoBERT/노래 가사/lyric.csv')
for artist, songname, lyric in zip(testData['artist'], testData['song_name'], testData['lyric']):
testModel(artist, songname, lyric)
성능에 그렇게 큰 기대를 하지는 않았는데도 불구하고 상당히 아쉬운 성능이 나오는 건 같습니다. 성능이 부족한 이유를 제 나름으로 생각해보자면 일단 노래 가사에는 여러 감정이 복합적으로 표현되다 보니 한가지로 표현하기 힘든 것 같고 전처리 과정도 부족했던 것 같습니다. 그리고 모델 학습에서는 대화문을 이용했는데 감정 분석에는 노래 가사를 사용하다 보니 그로 인한 차이도 발생하는 것 같습니다.
다른 분들이 이런 프로젝트를 하는 것을 보고 저도 NLP를 처음 입문하기에 좋아 보여서 도전해보았는데 꽤나 재미있는 프로젝트였던 것 같습니다. 저는 이 프로젝트를 응용해서 졸업작품에도 적용했었는데(할 일들을 하느라 글을 늦게 올렸지만 프로젝트는 11월 쯤에 진행했었습니다.) 졸업작품에 적용할 때는 팀원들과 함께 집단지성을 발휘해서 성능이 좀 더 잘 나왔던 것 같습니다. 만약 NLP를 해보고 싶지만 어려워 보여서 고민이 되는 분이 계신다면 저처럼 쉬운 프로젝트를 통해 시작을 해보시기를 추천드립니다.
PS. 이번 부스트캠프 AI Tech 5기에 NLP 포지션으로 지원을 할 예정인데 합격한다면 제대로 공부를 한 뒤 나중에 한번 다시해보면 재미있을 것 같습니다.
PS2. 저는 부스트캠프 4기에서 1차 합격을 해서 이번에는 2차 코딩테스트만 준비하면되는데 알고리즘은 역시 어렵네요...
PS3. 사건의 지평선은 몇 번들어도 좋네요🎵