Search
Login
🎧

“내 10분의 1만이라도 아프다 행복해줘😭❤️” NLP로 이별 노래 플레이리스트 만들기

Created
2022/08/09
Editor

Intro. What is E-byul? (feat. NLP)

좋으니 그 사람 솔직히 견디기 버거워 네가 조금 더 힘들면 좋겠어 진짜 조금 내 십 분의 일만이라도 아프다 행복해줘 - 윤종신, ‘좋니’ (2017)
여러분들은 우울한 날에 어떤 식으로 시간을 보내시나요? 음악은 수많은 사람들을 위로하기도 하고, 단순히 말로 전달하기 힘든 표현을 건네곤 합니다. 우울증을 가지고 있는 사람들은 즐거운 음악보다는 슬픈 음악을 택하는 경향이 있습니다. 슬픈 음악을 듣는다고 해서 더 우울해지는 것이 아닌 오히려 자신의 감정 상태를 이해해준다고 느껴 편안함을 느끼는 것이죠.
대중 가요에는 수많은 이별 노래가 있습니다. 그리고 노래 가사에 나타나는 이별의 정서는 매우 섬세하고 복합적이죠. 그렇다면 수많은 이별 노래들 중에서도 우리가 듣고 싶은 종류의 가사가 담긴 노래들을 골라 들을 수는 없을까요? 자연어처리(NLP, Natural Language Processing)를 이용해 주어진 텍스트에 대한 특정 감정을 분류하는 ‘감정 분류’ 작업을 노래 가사에 적용한다면 가능합니다! 이번 포스트에서는 자연어처리를 이용하여 이별 가사들을 감정 분류하는 논문을 소개해보도록 하겠습니다.
Loading PDF…
본 논문에서는 이별 가사를 감정 분류하기 위해서 CBOW 학습을 통해 감정 사전을 구축하고, LSTM을 사용하여 가사 학습을 거친 후, 유사한 감정으로 가사를 분류하는 모델을 소개하고 있습니다. CBOW 는 Word2Vec 모델 중 하나이고, LSTM은 RNN 모델 중 하나입니다. 그럼 지금부터 CBOW 모델은 어떻게 구성되고 구현되는지, LSTM 모델은 어떤 과정을 거쳐 데이터를 학습하는지 알아보겠습니다!
deep daiv. 1st Digital Single <What is E-byul? (feat. NLP)> Tracklist

Track 1. CBOW로 감정 사전 구축하기

1) Word2Vec

저번 시간에는 ‘통계 기반 기법’을 통해 단어 분산 표현을 얻었습니다. 이번에는 한층 발전된 기법인 ‘추론 기반 기법’에 대해 알아보겠습니다. 여기에서 바로 Word2Vec이 등장합니다! 통계 기반과 추론 기반의 가장 큰 차이는 데이터를 다루는 데에 있습니다. 통계 기반은 단 한 번의 처리로 단어의 분산 표현을 얻는 반면 추론 기반은 데이터를 여러 개의 미니배치로 나누어 학습하죠. 말뭉치의 크기가 작으면 통계 기반도 괜찮지만 실제로 적용하는 데이터의 크기는 엄청나게 크기 때문에 통계 기반 기법으로는 한계가 있습니다.
Word2vec의 모델은 대표적으로 CBOWskip-gram으로 나뉘는데, CBOW는 맥락(주변 단어)으로부터 타겟 단어를 추측하고 skip-gram은 타겟 단어로부터 맥락(주변 단어)을 추측하는 방식입니다. 보통 skip-gram이 더 효율적으로 쓰이지만, 소개하는 논문에서는 CBOW를 이용하여 노래 가사의 단어 의미 파악과 이를 바탕으로 감정 사전을 구축하고 있기 때문에 CBOW에 대해 알아보는 것을 목표로 하겠습니다. *CBOW에 대한 대부분의 설명과 그림은 ‘밑바닥부터 시작하는 딥러닝2’ 교재 자료를 주로 참고하였습니다.

2) CBOW

위 그림을 보면 CBOW 모델이 입력층, 은닉층, 출력층으로 나누어져 있는 것을 알 수 있습니다. 각 층은 신경망 모델의 기본적인 구조인데요, CBOW의 특징은 여러 개의 입력이 있다는 점입니다. 주변 맥락의 크기를 어느 정도로 정할 것인지에서 입력의 개수가 달라지는데, 위의 예시에서는 윈도우의 크기(맥락)를 1로 설정했기 때문에 입력이 2개인 것입니다. You say goodbye and I say hello. 라는 문장에서 타깃이 say라는 단어라면 주변 단어인 you와 goodbye의 원핫벡터를 입력으로 사용하게 됩니다.
각 층 사이에는 가중치(W)가 존재합니다. 신경망 모델의 기본은 가중치편향에 있습니다. 신경망의 학습 원리는 순전파(정방향)로 입력과 가중치, 편향을 통해 손실함수를 구하고 이를 바탕으로 역전파(역방향)로 기울기를 구해 가중치를 갱신하는 것입니다. 이를 학습이라 하며 학습이 반복될수록 매개변수(가중치, 편향 등)들이 손실함수를 최소로 하도록 갱신되어, 학습을 진행할수록 신경망의 효율이 좋아지게 됩니다.
Softmax는 출력층에서 나온 점수를 확률로 변환해주는 함수입니다. Softmax 계층을 통해 구한 확률을 교차 엔트로피 오차 계층을 이용해 정답 레이블과 비교하는 것이죠. 이렇게 구해진 손실함수는 역전파를 통해 거슬러 올라가고 앞 계층의 매개변수를 갱신하게 됩니다. 이제 간단한 CBOW 모델을 구현해보겠습니다!
MatMul
SoftmaxWithLoss
class SimpleCBOW: def __init__(self, vocab_size, hidden_size): # 어휘 수, 은닉층의 뉴런 수 V, H = vocab_size, hidden_size # 가중치 초기화 # random.randn을 통해 임의의 가중치를 설정합니다. # astype('f') 는 넘파이 배열 데이터 타입, 32비트 부동소수점 수로 초기화 W_in = 0.01 * np.random.randn(V, H).astype('f') W_out = 0.01 * np.random.randn(H, V).astype('f') # 계층 생성 # MatMul 계층은 윈도우의 크기만큼 생성해야 합니다! self.in_layer0 = MatMul(W_in) self.in_layer1 = MatMul(W_in) self.out_layer = MatMul(W_out) self.loss_layer = SoftmaxWithLoss() # 모든 가중치와 기울기를 리스트에 추가 layers = [self.in_layer0, self.in_layer1, self.out_layer] self.params, self.grads = [], [] for layer in layers: self.params += layer.params self.grads += layer.grads # 인스턴스 변수에 단어의 분산 표현 저장 self.word_vecs = W_in # 순전파 def forward(self, contexts, target): h0 = self.in_layer0.forward(contexts[:, 0]) h1 = self.in_layer1.forward(contexts[:, 1]) h = (h0 + h1) * 0.5 score = self.out_layer.forward(h) loss = self.loss_layer.forward(score, target) return loss # 역전파 def backward(self, dout=1): ds = self.loss_layer.backward(dout) da = self.out_layer.backward(ds) da *= 0.5 self.in_layer1.backward(da) self.in_layer0.backward(da) return None
Python
복사
CBOW 모델의 학습은 일반적인 신경망의 학습과 비슷합니다. 학습 데이터를 신경망에 입력한 후, 기울기를 구하고 가중치 매개변수를 갱신해 나갑니다. Optimizer는 매개변수 갱신을 위한 알고리즘으로 SGD(확률적 경사 하강법), AdaGrad 등이 있습니다. 아래 코드에서는 Adam이라는 알고리즘을 사용하겠습니다!
import sys from common.trainer import Trainer from common.optimizer import Adam from simple_cbow import SimpleCBOW from common.util import preprocess, create_contexts_target, convert_one_hot window_size = 1 hidden_size = 5 batch_size = 3 max_epoch = 1000 text = 'You say goodbye and I say hello.' corpus, word_to_id, id_to_word = preprocess(text) vocab_size = len(word_to_id) contexts, target = create_contexts_target(corpus, window_size) target = convert_one_hot(target, vocab_size) contexts = convert_one_hot(contexts, vocab_size) model = SimpleCBOW(vocab_size, hidden_size) optimizer = Adam() trainer = Trainer(model, optimizer) trainer.fit(contexts, target, max_epoch, batch_size) trainer.plot() word_vecs = model.word_vecs for word_id, word in id_to_word.items(): print(word, word_vecs[word_id])
Python
복사
Colab을 통해 직접 시행해 본 결과입니다! 세로 축은 loss 값, 가로축은 iterations 로 학습을 진행할수록 loss 가 작아지는 것이 이상적입니다. 말뭉치가 작아서 그런지 만족스러운 결과는 아니지만 학습이 진행되었다는 것은 확인할 수 있었습니다!!
직접 해보고 싶으신 분들은 아래 링크를 참고하여 해보시면 좋을 것 같습니다!

3) CBOW로 감정 사전 구축하기

이제 논문에서 CBOW를 어떻게 활용하고 있는지에 대해 알아보겠습니다! Word2Vec를 이용하기 위해서는 전처리 과정이 필수입니다. 전처리에는 토큰화, 품사 태깅, 불용어 처리 과정 등이 있죠. 전처리를 마친 후에는 무엇을 할까요? 이젠 단어별 의미를 파악해야 합니다. 예를 들어 단순히 ‘사랑’이라는 단어는 무조건 행복만을 뜻하진 않습니다. 문맥에 따라 달라지죠. ‘아주 많이 사랑했던 나의 그대를 이젠 떠나 보내려해’, ‘다른 사랑 못 할 거 같아요’ 같은 문장들에서 사랑은 ‘이별’이라는 단어와 의미적으로 유사도가 더 높아야 한다는 것입니다. 이때 Word2Vec을 통한 임베딩 모델은 단어 간 거리 유사도를 판별하여 단어 간의 의미를 파악하게 됩니다.
지금까지 설명했던 CBOW를 이용해 단어를 학습하고, 이를 기반으로 감정 사전을 구축하게 됩니다. 위의 논문에서는 4가지 대표 감정을 ‘슬픔’, ‘부정’, ‘분노’, ‘무관심’으로 설정하고 유사도를 벡터화하였습니다. 아래 표는 논문에 소개되어 있는 표인데요, 4가지 감정에 대한 유사도가 높은 단어를 5개씩 추출한 결과입니다.
위의 표처럼 유사도가 높은 순서대로 , 유사도가 0.5 이상인 단어들만을 이용하여 대표 감정에 대한 감정 사전을 구축하는 것이죠. 이제 이를 바탕으로 LSTM을 이용하여 이별 가사의 감정을 분류하게 되는 것입니다!

Track 2. LSTM으로 감정 분류 학습하기

1) 데이터의 순서를 고려하여 학습하는 순환신경망 RNN(Recurrent Neural Network)

딥러닝 모델을 학습시키는 알고리즘은 사람의 신경망 원리와 구조를 모방하여 만들어진 ‘인공신경망(Artificial Neural Network)’입니다. 오늘 포스트에서 소개할 RNN(Recurrent Neural Network, 순환신경망)도 인공신경망의 한 종류인데요. 순환신경망은 ‘순차 데이터(Sequential Data)’ 즉 ‘순서’ 정보가 있는 데이터를 학습하는 것에 특화된 인공신경망입니다. ‘순차 데이터’는 시간 순서대로 장면이 진행되는 영화의 시퀀스처럼 순서 정보를 갖고 있어요.
텍스트데이터도 순차데이터에 해당해요. 문장을 읽는 ‘순서’가 있고, 어떤 단어의 의미는 그 단어의 앞뒤 순서에 있는 다른 단어들에 따라 즉 문장에서 위치하는 순서에 따라 달라지니까요. 텍스트데이터에서 순서 정보를 고려하지 않고 데이터 처리를 할 수도 있는데, RNN은 텍스트데이터의 순서 정보까지 모두 고려하여 학습을 하겠다는 것입니다.
RNN이 어떻게 텍스트데이터의 순서 정보를 고려해서 학습되는지 알아볼까요? RNN(순환신경망)은 이름처럼 모델 내부에 ‘순환’하는 구조를 가지고 있어, 과거에 학습한 값을 현재의 학습에 반영하는 과정을 계속해서 반복할 수 있어요. 바로 과거에 학습한 값의 가중치(Weight Value)를 현재의 학습과정에 반영하는 방법을 통해서요.
RNN 모델의 순환 구조는 시점(타임스탭) 단위로 cell을 펼쳐서 오른쪽과 같이 표현할 수도 있다. 사진출처 https://wikidocs.net/22886
RNN 알고리즘의 원리
* ‘[딥러닝] RNN 기초 (순환신경망 - Vanilla RNN)’ 영상의 설명과 이미지, 예제를 참고하였습니다. https://www.youtube.com/watch?v=PahF2hZM6cs
RNN 알고리즘이 작동하는 원리를 간단히 알아볼게요. 먼저 다음과 같은 문장에서 단어의 품사를 각각 분류하는 POS태깅(품사 태깅, Part-Of-Speech Tagging) 작업을 한다고 생각해봅시다.
I, work, at, google (난 구글에서 일해.) 대명사, 동사, 전치사, 명사
품사분류기 RNN 모델 예시. 사진 출처 https://www.youtube.com/watch?v=PahF2hZM6cs
타임스탭 t에서 RNN 모델 구현 과정 a. 입력층 : xt 투입 b. 은닉층 : xtht-1에 대한 활성화 함수 처리 → yt , ht 산출 (가중치와 편향값은 매개변수) c. 출력층 : yt에 대한 활성화 함수 처리 → 예측값 산출
그 다음 시점에서 ‘work’를 처리할 때는 ‘I’에 대한 상태값을 고려하여, 확률적으로 문장에서 맨 앞에 대명사가 온 뒤에는 동사가 온다는 추론을 거쳐 ‘work’의 품사를 ‘동사’로 판단하게 됩니다. 단어 하나를 은닉층에서 계산해 내보내는 처리의 단위를 cell이라고 하는데, ‘work’를 처리하는 cell에서는 과거에 계산한 ‘I’에 대한 상태값이 수학적으로 같이 고려되어 계산이 되는 것입니다. 뒤이어 오는 ‘at’을 처리할 때는 ‘I’와 ‘work’의 상태값이 고려되고, ‘google’을 처리할 때는 ‘I’와 ‘work’와 ‘at’의 상태값이 고려되겠죠.
t번째 시점에서 현재의 input인 xt와 과거 t-1번째 시점에서 처리한 상태값 ht-1를 고려해 현재의 상태값 ht를 산출하는 식
ht=tanh(xtWxh+ht1Whh+b)h_t = tanh(x_t * W_{xh} + h_{t-1} * W_{hh} + b)
이러한 시점이 여러 번 반복되면 다음 그림과 같이 진행됩니다. 시점마다 동일한 cell이 사용되고, 가중치와 편향값도 동일하게 적용됩니다. 가중치편향값은 RNN 모델을 학습시키는 과정에서 모델이 최종적으로 계산하여 산출한 예측값과 정답값 사이 차이를 줄여주는 방향으로 계속해서 조정되며 최적화됩니다. 이렇게 매개변수인 가중치와 편향값을 최적화시키는 과정인 back propagation 역시 시점에 따라 순차적으로 진행되어서, BPTT(Back Propagation THROUGH TIME)라고 합니다.
시점 t에서 RNN 모델을 지도학습(정답을 알려주는 학습)하는 과정 : 출력층에서 산출한 예측값정답값차이(error)가 줄어들도록 매개변수(가중치 & 편향값)를 최적화

2) RNN의 단점을 보완한 LSTM

첫 번째 시점의 입력값인 x1의 정보량이 뒤로 갈수록 손실된다. 사진출처 https://wikidocs.net/22888
RNN 모델은 비교적 짧은 시퀀스의 데이터를 처리할 때에는 효과적이지만, 데이터가 많아지면 앞서 처리된 데이터를 마지막까지 기억하기 어렵습니다. 그 이유는 RNN의 출력 결과가 이전 결과를 반영하여 처리되기 때문이에요. 이렇게 이전 결과에 의존하는 계산 방식에서는 input 단어 개수가 많아지고 각 단어를 처리하는 시점들이 많아지면 많아질수록, 이전 시점들의 정보가 충분히 전달되기 어려워집니다. 이러한 RNN모델의 문제를 극복하기 위해 등장한 모델이 바로 LSTM(Long Short-Term Memory, 장단기 메모리)입니다! LSTM의 cell 내부에는 입력값을 hidden state 값(상태값)으로 계산하는 과정뿐 아니라, 입력값을 cell state 값으로 계산하는 memory cell의 매커니즘도 추가되어 있어요.
RNN 모델 내부 구조도. 사진출처 https://wikidocs.net/22888
이제 예시 문장과 함께 LSTM의 cell이 구현되는 과정을 살펴볼까요? * ‘[딥러닝] LSTM 쉽게 이해하기’ 영상의 설명과 이미지, 예제를 참고하였습니다. https://www.youtube.com/watch?v=bX6GLbpw-A4
John is my best friend, .... (x1, x2, x3, x4, x5, ....) He likes basketball, .... (x23, x24, x25, ....) 1)_____ is still my best friend, .... (x50, x51, x52, x53, x54, x55, ....) Jane is his wife, .... (x80, x81, x82, x83, ....) 2)_____ knows I am best friend of John .... (x100, x101, x102, x103, ....) → 빈칸 1)에 올 대명사는 ‘He’, 빈칸 2)에 올 대명사는 ‘She
위의 예시 문장들을 LSTM이 학습한다고 생각해 봅시다. 각 단어들의 시점은 임의로 설정해봤어요. 첫 번째 빈칸에 도달할 때까지는 이전 문장들에서 언급된 John에 대한 정보를 기억해두고, 빈칸에 들어갈 말이 ‘he’가 될 것이란 예측을 간직해야 합니다. 그러다 두 번째 빈칸을 처리하는 100번째 시점이 오면 새로 들어온 Jane에 대한 정보를 기억하고, 이전의 John에 대한 정보는 잠시 잊어야 빈칸에 들어갈 말이 ‘she’가 될 것이라 예측할 수 있을 거예요. 이처럼 LSTM 모델에는 불필요한 정보는 잊어버리고, 필요한 정보는 기억을 간직하는 매커니즘이 있습니다. 바로 ‘삭제 게이트’입니다. Jane이란 단어가 처음 등장하는 80번째 시점을 기준으로 LSTM 모델이 정보를 학습하는 과정을 따라가 볼까요?
a. 입력 게이트 (Input Mechanism)
b. 삭제 게이트 (Forget Mechanism)
c. 출력 게이트 (Output Mechanism)
이처럼 LSTM에서는 입력, 삭제, 출력 게이트를 거쳐 기억할 정보와 삭제할 정보를 결정합니다. LSTM 모델을 활용하면 긴 시퀀스의 데이터도 정교하게 분류해낼 수 있답니다.

3) LSTM으로 감정 분류 학습하기

논문에서 제안하는 이별 가사 감정 분류 모델은 이별 노래 데이터 수집 및 학습데이터 전처리 단계, 이별 가사 감정 사전 구축 단계, 이별 가사 감정 분류 단계로 구성되어 있습니다. 그중 가사 감정 분류 단계에서 문장의 흐름을 확인하고 이별 가사의 감정을 정확하게 분류하기 위해 LSTM 모델이 활용되었습니다. LSTM 모델을 지도학습시키기 위해서 학습데이터로 앞서 Word2Vec으로 구축한 감정 사전을 이용했고, 정답 데이터로는 감정 단어의 라벨링된 데이터를 사용했다고 합니다.
학습에 사용된 단어의 개수는 총 30개로, 각각의 단어를 처리하는 시점 역시 30개가 됩니다. 이 30개의 단어들은 3차원 벡터인 워드 임베딩 층에 임베딩이 되어 있습니다. 또한 워드 임베딩 층은 입력값 X, 라벨링된 감정은 결과값 Y로 지정하였습니다. X에 있는 입력값 x1, x2, x3.....x30이 각각 LSTM 내부 cell을 거쳐 y1, y2, y3.... y30의 결과값으로 산출되고, 이 결과값이 다시 최종 예상값으로 산출되는 과정에는 softmax라는 활성화 함수가 적용되었겠네요. 지도학습을 거친 LSTM 모델은 주어진 단어 입력값에 대해 논문에서 분류한 4가지의 이별 감정 ‘슬픔’, ‘부정’, ‘무관심(관조)’, ‘분노’ 중 하나를 판단해낼 수 있습니다. 아래 사진을 보면 같은 ‘헤어지다’라는 단어도 문장의 내용과 문장 내 위치 등이 고려되어 서로 다른 감정으로 분류되는 것을 볼 수 있어요. “우리 헤어져 보자”라는 문장에서는 무관심의 감정으로, “난 못 헤어져 나를 떠나면 안돼” 라는 문장에서는 부정의 감정으로 분류가 되었네요.

Outro. More & More

이렇게 자연어처리 모델 중 Word2Vec와 LSTM를 활용해 이별 노래 가사의 감정을 분류하는 작업이 어떻게 가능한지 알아보았습니다. 이별 노래들 중에서도 무관심, 현실 부정, 슬픔 등 감정의 결이 다르게 나뉠 수 있다는 점과, 이런 섬세한 감정의 차이를 사람이 일일이 분석하지 않아도 자연어처리를 통해 잘 분류해낼 수 있다는 점이 정말 신기한 것 같습니다. 특히 노래 가사는 “이별”이나 “사랑”을 직접적으로 언급하지 않고 다양한 은유로 감정을 전달하는 경우가 많은데, 이러한 은유적 표현의 의미도 Word2Vec 모델을 통해 충분히 파악이 가능하다는 것을 확인할 수 있었어요. NLP를 활용해 노래 가사의 주제나 감정을 분류해내는 모델이 더 발전하게 된다면, 기존의 멜로디의 유사성이나 노래 전반의 장르나 분위기에 대한 해시태그 키워드에 기반하여 노래를 추천하는 시스템에서 더 나아간 노래 추천과 플레이리스트 큐레이션이 가능해질 것 같습니다. 유사한 감정이 드러나는 노래 가사의 한 부분을 추천해주는 시스템이 가능해진다면 추천의 단위가 하나의 곡이 아니라 그보다 더 세분화된 가사 구절이 되는 것이니, 더 풍부한 노래 추천이 가능해질 것으로 보입니다. 더 뛰어난 성능의 NLP 모델을 구현하기 위한 연구자들의 노력도 끝없이 이어지고, 점점 더 발전되는 NLP 모델을 활용할 수 있는 범위도 끝없이 넓어질 앞으로가 기대되지 않으신가요?

참고문헌

 AI ML DL, [케라스] 무작정 튜토리얼1 - Sequential Model 구현, 2019.12.11., https://ebbnflow.tistory.com/120
 AI ML DL, [인공지능] ANN, DNN, CNN, RNN 개념과 차이, 2019.12.10., https://ebbnflow.tistory.com/119?category=895675
[딥러닝] RNN 기초 (순환신경망 - Vanilla RNN), Minsuk Heo 허민석, 2018.9.2., https://www.youtube.com/watch?v=PahF2hZM6cs
하얀종이개발자, 딥러닝에서 가중치(W), 편향(Bias)의 역할, 2021.8.30. https://jh2021.tistory.com/3
[텐서플로2 딥러닝] RNN, Minsuk Heo, 2020.5.25. https://youtu.be/9c4RupbiuZU
[딥러닝] LSTM 쉽게 이해하기, Minsuk Heo 허민석, 2018.9.9, https://www.youtube.com/watch?v=bX6GLbpw-A4
임명진, 박원호, 신주현.(2020).Word2Vec과 LSTM을 활용한 이별 가사 감정 분류.스마트미디어저널,9(3),90-97. https://www-dbpia-co-kr-ssl.access.ewha.ac.kr/journal/articleDetail?nodeId=NODE10659164
ifdean, RNN & LSTM - 시퀀셜 데이터 학습, 2021.5.22., https://ifdean.tistory.com/5?category=859814
28 . LSTM(Long Short-Term Memory), 2020.10.5., https://welcome-to-dewy-world.tistory.com/103?category=913368