KoBERT를 통한 노래 가사 감정분석(3)-노래 가사의 감정 분석하기

README·2022년 12월 29일
0

개요

이제 노래 가사도 크롤링했고 모델도 학습했으니 노래 가사의 감정을 분석하는 일만 남았습니다. 크롤링한 곡들 중 일부 곡들을 골라서 노래 가사의 감정 분석을 해보았습니다.

가사 전처리하기

  1. 크롤링한 모든 곡들을 처리하기에는 너무 많기때문에 제가 좋아하는 곡 몇곡만 골라봤습니다.
  2. 노래 가사의 감정 분석을 하기 전에 가사의 전처리를 수행해주어야 합니다. 영어가 너무 많은 경우 영어를 제거해주어야 하고 의미 없는 가사들(예,아,오,음 처럼 별 의미 없는 가사) 역시도 제거해 주는 것이 좋습니다.
  3. 저는 영어를 제거해주는 것보다는 영어를 해석하는 것이 가사의 감정 분석에 좀 더 도움이 될 것 같아서 구글에서 가사 번역본을 찾아 이용했습니다. (이 경우를 테스트해보기 위해서 해외 곡인 STAR WALKIN을 선택했습니다.)

노래 가사 감정 분석하기

  1. 학습이 완료된 모델을 불러옵니다
  2. 감정을 분석할 가사를 불러옵니다.
  3. 가사에서 영어, 특수문자 등을 삭제합니다.
  4. 가사를 BERT에서 활용할 수 있는 형태로 변환해주고 토큰화 시켜줍니다.
  5. 가사의 감정분석 결과를 출력합니다.

소스코드

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)

감정 결과

10곡을 뽑아서 감정 분석을 해본 결과인데... 대부분이 분노로 나오네요...

모델 성능...

성능에 그렇게 큰 기대를 하지는 않았는데도 불구하고 상당히 아쉬운 성능이 나오는 건 같습니다. 성능이 부족한 이유를 제 나름으로 생각해보자면 일단 노래 가사에는 여러 감정이 복합적으로 표현되다 보니 한가지로 표현하기 힘든 것 같고 전처리 과정도 부족했던 것 같습니다. 그리고 모델 학습에서는 대화문을 이용했는데 감정 분석에는 노래 가사를 사용하다 보니 그로 인한 차이도 발생하는 것 같습니다.

프로젝트 정리

프로젝트 순서

  1. 노래 가사 크롤링
  2. 학습 데이터 수집 및 전처리
  3. 모델 학습
  4. 노래 가사 전처리
  5. 학습된 모델을 통한 노래 가사의 감정 분석

아쉬운 점

  1. 학습 데이터로 사용한 대화문과 노래 가사의 차이가 분명히 있다 보니 성능이 잘 나오지 않는 것 같습니다.
  2. 노래 가사의 전처리가 다소 부족했던 것 같습니다. 전처리 과정을 완벽하게 처리하면 더 좋은 성능이 나올 수도 있을 것 같은데 이 부분은 추후 자연어처리를 좀 더 공부해보고 다시 시도해봐야 할 것 같습니다.
  3. KoBERT 모델에 대한 이해도 부족으로 인해 모델의 성능을 완벽하게 이용하지 못 한 것 같습니다.
  4. 노래 가사에는 여러 감정이 표현되다 보니 하나의 감정으로 나타내기에는 어려움이 있는 것 같습니다.

배운 점

  1. KoBERT라는 모델을 들어보기만 하고 사용해 본 적은 없었는데 이번에 KoBERT 모델을 통해 프로젝트를 진행하며 어렴풋이나마 알게 된 것 같습니다.
  2. 크롤링을 하면서 원하는 부분의 정보를 얻기 위해서 프론트엔드 코드에 대해 찾아보게 되었고 조금이라도 더 알게 된 것 같습니다.
  3. 자연어처리가 어떤 것이고 어떤 과정을 통해 수행하는지 배웠습니다. 이후 좀 더 공부를 하면서 자세한 과정을 알아보고 싶습니다.

총평

다른 분들이 이런 프로젝트를 하는 것을 보고 저도 NLP를 처음 입문하기에 좋아 보여서 도전해보았는데 꽤나 재미있는 프로젝트였던 것 같습니다. 저는 이 프로젝트를 응용해서 졸업작품에도 적용했었는데(할 일들을 하느라 글을 늦게 올렸지만 프로젝트는 11월 쯤에 진행했었습니다.) 졸업작품에 적용할 때는 팀원들과 함께 집단지성을 발휘해서 성능이 좀 더 잘 나왔던 것 같습니다. 만약 NLP를 해보고 싶지만 어려워 보여서 고민이 되는 분이 계신다면 저처럼 쉬운 프로젝트를 통해 시작을 해보시기를 추천드립니다.

PS. 이번 부스트캠프 AI Tech 5기에 NLP 포지션으로 지원을 할 예정인데 합격한다면 제대로 공부를 한 뒤 나중에 한번 다시해보면 재미있을 것 같습니다.
PS2. 저는 부스트캠프 4기에서 1차 합격을 해서 이번에는 2차 코딩테스트만 준비하면되는데 알고리즘은 역시 어렵네요...
PS3. 사건의 지평선은 몇 번들어도 좋네요🎵

profile
INTP 개발자 지망생

0개의 댓글