저번 글에서는 단어 표현이 무엇인지에 대한 내용을 다루었고, 또 CBOW가 무엇인지에 대해서 조금 더 상세하게 설명을 했다.

 

이번 글에서는 그 뒤를 이어 CBOW의 정반대 메커니즘을 가지고 있는 Skip-Gram에 대해서 얘기해보고 그 뒤 내용들을 다루어보도록 하겠다.

 

Skip-Gram은 다음과 같은 과정을 통해 학습이 진행된다.

 

1. 하나의 단어를 원-핫 벡터로 만들어서 입력값으로 사용한다.

2. 가중치 행렬을 원-핫 벡터에 곱해서 N-차원 벡터를 만든다.

3. N-차원 벡터에 다시 가중치 행렬을 곱해서 원-핫 벡터와 같은 차원의 벡터로 만든다.

4. 만들어진 벡터를 실제 예측하려는 주변 단어들 각각의 원-핫 벡터와 비교해서 학습한다.

 

물론 CBOW와 거의 대부분이 똑같기 때문에, 전체적인 구조를 설명하는 그림만 추가해도 충분히 이해할 수 있을 듯하다.

(혹시나 CBOW에 대한 설명을 아직 안 보셨다면, 이전 글을 보고 오시면 충분히 이해가 되실 것 같습니다.)

(예시도 CBOW에서 사용했던 그대로를 사용합니다. 즉 예시 문장은 지금 너무 추워서 패딩 꼭 입어야 해 입니다.)

 

그림 1. Skip-Gram의 전체적인 구조도

Skip-Gram의 전체적인 구조도를 그린 그림이다. CBOW와 마찬가지로 가중치 행렬곱을 M과 M'으로 총 두 번 작업하게 된다. 다른 점이라고 한다면 CBOW의 경우 입력값이 여러 개였으므로, 입력값을 받고 나서 평균을 취해줘서 평균 N-차원 은닉층을 만든 반면, Skip-Gram의 경우는 입력값이 한 개이므로 평균을 취해주지 않아도 된다.

 

 

이러한 Word2Vec의 두 모델은 여러 가지 장점이 있는데,

 

 

첫 번째로는 기존의 카운트 기반 방법으로 만든 단어 벡터보다 단어 간의 유사도를 잘 측정한다는 점이다.

 

 

두 번째로는 단어들의 복잡한 특징까지도 잘 잡아낸다는 점이고

 

 

세 번째로는 Word2Vec을 이용해서 만들어낸 단어 벡터는 서로에게 유의미한 관계를 측정할 수 있다는 점이다. 예를 들어, 4개의 단어(엄마, 아빠, 남자, 여자)를 word2vec 방식을 사용해 단어 벡터로 만들었다고 한다면, '엄마'와 '아빠'라는 단어 벡터 사이의 cosine 거리와 '여자'와 '남자'라는 단어 벡터 사이의 cosine 거리가 같게 나온다.

 

 

Word2Vec의 CBOW와 Skip-Gram 모델 중에서는 보통 Skip-Gram이 성능이 더 좋아 일반적인 경우 Skip-Gram을 사용하는 것으로 알려져 있으나, 절대적으로 항상 좋은 것은 아니므로 두 가지 모두 고려할 만하다.

 

 

이처럼 단어 벡터를 표현하는데 카운트 기반 방법과 예측 기반 방법을 사용하는데, 보통의 경우 예측 기반 방법의 성능이 더 좋아 주로 예측 기반 방법을 사용한다. 그리고 두 가지 방법을 모두 포함하는 "Glove"라는 단어 표현 방법 또한 사용된다. (Glove도 대략 찾아보았는데 생각보다 너무 난해해 일단은 포기하고 나중을 기약한다...)

 

 

단어 표현은 자연어 처리 문제를 해결하는 기반이 되는 가장 근본적인 내용이므로 정확히 이해하는 것이 중요하며, 항상 가장 좋은 성능을 내는 유일한 방법이 있는 것이 아니라서 각 방법 간에 어떤 차이점이 있는지 항상 염두에 두고 상황에 맞게 사용하는 것이 중요하다.

 

<2> 텍스트 분류

 

텍스트 분류(Text Classification)는 자연어 처리 문제 중 가장 대표적이고 많이 접하는 문제다. 자연어 처리 기술을 활용해 특정 텍스트를 사람들이 정한 몇 가지 범주(Class) 중 어느 범주에 속하는지 분류하는 문제다. 분류해야 하는 범주의 수에 따라 문제를 구분하기도 하는데, 보통 2가지 범주에 대해 구분하는 문제는 이진 분류(Binary Classification) 문제라고 하고, 3개 이상의 범주에 대해 분류하는 문제는 다중 범주 분류(Multi Class Classification) 문제라고 한다. 다음 예시를 이용해 텍스트 분류에 대해 좀 더 알아보자.

 

텍스트 분류의 예시

 

- 스팸 분류

 

메일 중에서 일반 메일과 스팸 메일을 분류하는 문제가 스팸 분류 문제이다. 현재 이메일 서비스를 하고 있는 포털들은 이 기술들이 잘 적용되고 있어서, 스팸 메일은 알아서 잘 걸러내고 있다. 이 문제에서 분류해야 할 범주(Class)는 스팸 메일과 일반 메일로 두 가지이다.

 

 

- 감정 분류

 

감정 분류란 주어진 글에 대해서 이 글이 긍정적인지 부정적인지 판단하는 문제이다. 이 경우 범주는 긍정 혹은 부정이 된다. 경우에 따라서 범주는 긍정 혹은 부정에 한정되는 것이 아닌 중립의 범주가 추가될 수 있으며, 긍정 혹은 부정의 경우에도 정도에 따라 범주를 세분화하기도 한다. 즉 문제에서 분류하려는 의도에 따라 범주가 정해지는 것이다. 가장 대표적인 감정 분류 문제는 영화 리뷰에 대해 각 리뷰가 긍정적인지 부정적인지 판단하는 문제인데, 4장에서 자세히 설명할 예정이다.

 

 

- 뉴스 기사 분류

 

매일매일 뉴스가 계속해서 쏟아지는데, 이를 분류하지 않는다면 내가 원하는 뉴스를 선택해서 보는 것이 어려울 것이다. 따라서 뉴스 업체들은 사용자들이 원하는 뉴스를 선택해서 볼 수 있게끔 범주를 잘 구분 지어 분류해둬야 한다. 예를 들면 스포츠, 연예, 문화, 경제, 사회 등등이 있을 것이다. 여기에 자연어 처리 기술을 사용한다면 뉴스 기사를 더욱 효율적으로 분류할 수 있다.

 

 

 

텍스트 분류의 예시에 3가지 정도를 알아보았는데, 이외에도 다양한 문제들이 존재한다. 분류하는 단위를 글 전체로 하지 않고 각 단어를 한 단위로 분류하는 문제도 있다. 예를 들면 품사 태깅(POS tagging) 문제는 각 단어를 기준으로 어떤 품사를 가지는 것인지 분류해야 하는 문제이다.

 

 

그렇다면 텍스트 분류 문제는 어떻게 해결해야 하는가? 큰 기준으로 본다면 지도 학습 혹은 비지도 학습을 통해 해결할 수 있을 것이다.

 

지도 학습을 통한 텍스트 분류

 

지도학습을 통해 문장 분류를 하는 전체적인 구조는 다음 그림과 같다.

 

그림 2. 지도학습을 통한 문장 분류 과정

지도 학습의 경우 데이터에 대한 라벨이 주어져 있으므로, 데이터를 통해 학습 후 원하는 데이터에 대한 라벨을 구하면 된다.

 

 

지도 학습을 통한 문장 분류 모델에는 다양한 종류가 있고, 그중에 대표적으로 사용되는 지도 학습의 예는 다음과 같다.

 

- 나이브 베이즈 분류(Naive Bayes Classifier)

- 서포트 벡터 머신(Support Vector Machine)

- 신경망(Neural Network)

- 선형 분류(Linear Classifier)

- 로지스틱 분류(Logistic Classifier)

- 랜덤 포레스트(Random Forest)

 

 

비지도 학습을 통한 텍스트 분류

 

비지도 학습의 경우는 라벨이 없기 때문에, 특성을 찾아내서 적당한 범주를 만들어서 각 데이터를 나누면 된다.

 

어떤 특정한 분류가 있는 것이 아니라, 데이터의 특성에 따라 비슷한 데이터끼리 묶어주는 개념이다.

 

비지도 학습을 통한 텍스트 분류는 텍스트 군집화라고도 불리며, 대표적인 비지도 학습 모델은 다음과 같다.

 

- K-평균 군집화(K-means Clustering)

- 계층적 군집화(Hierarchical Clustering)

 

지도 학습과 비지도학습 중 어떤 방법을 사용할지 결정하는데 기여하는 가장 큰 기준은 데이터에 정답 라벨이 있느냐 없느냐이다. 정답 라벨이 있는 경우 지도학습 방법으로 문제를 해결하면 되고, 정답 라벨이 없는 경우 비지도 학습을 사용해서 문제를 해결하면 된다. 그리고 일반적인 분류의 경우에는 지도 학습을 사용하지만, 정확한 범주가 없고 단순히 군집화만 할 경우에는 비지도 학습을 통해 데이터를 군집화하면 된다.

 

<3> 텍스트 유사도

 

인공지능 스피커에게 다음과 같은 질문을 한다고 생각해보자.

 

  • 이 노래 누가 만들었어?
  • 지금 나오는 노래의 작곡가가 누구야?

 

위 두 문장은 똑같은 의미이지만 인공지능 스피커에게는 다른 문장으로 인식될 것이다. 따라서 각기 다른 대답을 만들어야 하는데, 좀 더 효율성을 위해 비슷한 의미를 가진 문장에 대해서는 같은 대답을 준비할 수 있을 것이다. 이때 문장이 유사한지 측정해야 하며, 텍스트 유사도(Text Similarity) 측정 방법을 사용하면 된다.

 

 

텍스트 유사도란, 말 그대로 텍스트가 얼마나 유사한지를 표현하는 방식 중 하나다. 앞에서 예로 든 두 문장의 경우 다른 구조의 문장이지만 의미는 비슷하므로 두 문장의 유사도는 높다고 판단할 수 있다. 물론, 유사도를 판단하는 척도는 매우 주관적이므로 데이터를 구성하기가 쉽지가 않으며 정량화하는데 한계가 있다.

 

 

일반적으로 유사도를 측정하기 위해 정량화하는 방법에는 여러 가지가 있다. 단순히 같은 단어의 개수를 사용해서 유사도를 판단하는 방법, 형태소를 나누어 형태소를 비교하는 방법, 자소 단위로 나누어 단어를 비교하는 방법 등 다양한 방법이 있다.

 

 

이 책에서는 그중에서도 딥러닝을 기반으로 텍스트의 유사도를 측정하는 방식을 주로 다룰 것이다. 단어, 형태소, 유사도의 종류에 상관없이, 텍스트를 벡터화한 후 벡터화된 각 문장 간의 유사도를 측정하는 방식이다. 그리고 자주 쓰이는 4개의 유사도 측정 방법에 대해 알아볼 것이다. 

 

 

우선 유사도를 측정해보기 전에 다음의 두 가지 예시 문장을 보자.

 

  • 휴일인 오늘도 서쪽을 중심으로 폭염이 이어졌는데요, 내일은 반가운 비 소식이 있습니다.
  • 폭염을 피해서 휴일에 놀러 왔다가 갑작스런 비로 인해 망연자실하고 있습니다.

 

이제 이 두 문장에 대해 각 유사도 측정 방법으로 유사도를 측정해 보겠다. 우선은 유사도 측정을 하기 전에 단어를 벡터화한다. 여기서는 TF-IDF를 통해 벡터화한다.

 

 

- 복습 -

TF-IDF를 이용하기 전에, 이전에 다루었던 내용을 잠시 복습하고 가고자 한다.

 

sklearn에서 제공하는 특징 추출 모듈을 사용할 것인데, 자연어 처리에서 특징 추출이란 텍스트 데이터에서 단어나 문장들을 어떤 특징 값으로 바꿔주는 것을 의미한다. 기존에 문자로 구성되어 있던 데이터를 모델에 적용할 수 있도록 특징을 뽑아 어떤 값으로 바꿔서 수치화하는 것이다. 

 

그중에서 TF-IDF이라는 값을 사용해 특징 추출을 하는 TfidfVectorizer를 사용한다.

 

TF는 Term Frequency의 약자로, 특정 단어가 하나의 데이터 안에서 등장하는 횟수를 의미한다.

 

DF는 Document Frequency의 약자로, 특정 단어가 여러 데이터에 자주 등장하는지를 알려주는 지표이다.

 

IDF는 Inverse Document Frequency의 약자로, DF의 역수를 취해 구할 수 있다.

 

마지막으로 TF-IDF는 TF 값과 IDF 값을 곱해서 구하게 된다. 특정 단어가 하나의 데이터 안에서는 자주 등장하고, 여러 데이터에서는 자주 등장하지 않을수록 TF-IDF의 값은 높아지게 된다.

 

 

from sklearn.feature_extraction.text import TfidfVectorizer

sent = ("휴일 인 오늘 도 서쪽 을 중심 으로 폭염 이 이어졌는데요, 내일 은 반가운 비 소식 이 있습니다.", 
        "폭염 을 피해서 휴일 에 놀러왔다가 갑작스런 비 로 인해 망연자실 하고 있습니다.")
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(sent) # 문장 벡터화 진행

print(tfidf_matrix)

print(tfidf_vectorizer.vocabulary_)

idf = tfidf_vectorizer.idf_
print(dict(zip(tfidf_vectorizer.get_feature_names(), idf)))

결과창

먼저 처음 출력한 tfidf_matrix는 각각의 단어를 TF-IDF 값을 이용하여 특징 추출한 matrix를 나타낸다.

 

 

(1, 0) 0.34287125... 이런 식으로 출력되는데, 여기서 (1, 0) 중에 앞부분인 1은 문장의 index를 의미하고(즉 index가 1인, 두 번째 문장이라는 의미), 뒷부분인 0은 단어 중에서 0에 해당하는 단어라는 말이다.(즉 이 경우 (1, 0)에 해당하는 것은 '갑작스런' 임.)

 

 

※ tfidf_matrix에 대한 자세한 설명이 책에는 없었기 때문에, https://stackoverflow.com/questions/42002859/creating-a-tf-idf-matrix-python-3-6 stackoverflow를 참고하였다.

 

 

해당 숫자에 어떤 단어가 매칭 되고 있는지를 알아보기 위해서는 tfidf_vectorizer.vocabulary_를 출력하면 확인할 수 있다. 이를 출력하면 단어와 그에 해당하는 숫자가 dictionary 형태로 출력된다.

 

 

마지막으로 출력된 내용은 각 단어들의 idf 값을 출력한 것이다. (책에서 있어서 출력한 것이지만, 사실 이게 왜 갑자기 필요한지는 모르겠음.)

 

 

 

1. 자카드 유사도

 

자카드 유사도(Jaccard Similarity), 또는 자카드 지수는 두 문장을 각각 단어의 집합으로 만든 뒤 두 집합을 통해 유사도를 측정하는 방식 중 하나다. 유사도를 측정하는 방법은 두 집합의 교집합인 공통된 단어의 개수를 두 집합의 합집합, 즉 전체 단어의 수로 나누면 된다. 결괏값은 공통의 원소의 개수에 따라 0과 1 사이의 값이 나올 것이고, 1에 가까울수록 유사도가 높다는 의미이다.

 

자카드 유사도

 

앞에서 제시한 두 예시 문장을 통해 자카드 유사도를 측정해보자. 유사도를 측정할 때 단어에서 조사는 따로 구분해서 사용하므로 두 문장 A, B는 다음과 같이 정의될 것이다.

 

A = {휴일, 인, 오늘, 도, 서쪽, 을, 중심,으로, 폭염, 이, 이어졌는데요, 내일, 은, 반가운, 비, 소식, 있습니다.

B = {폭염, 을, 피해서, 휴일, 에, 놀러 왔다가, 갑작스런, 비, 로, 인해, 망연자실, 하고, 있습니다.}

 

두 집합을 벤 다이어그램으로 그리면 다음과 같다.

 

벤 다이어그램

자카드 공식에 위의 예제를 대입해 본다면, 두 문장의 교집합은 {휴일, 폭염, 비, 을, 있습니다}로 5개, A-B는 12개, B-A는 8개로 두 문장의 합집합은 25개가 되어 자카드 유사도는 5/25 = 0.2이다.

 

2. 코사인 유사도

 

코사인 유사도는 두 개의 벡터 값에서 코사인 각도를 구하는 방법이다. 코사인 유사도 값은 -1과 1 사이의 값을 가지고 1에 가까울수록 유사하다는 것을 의미한다. 코사인 유사도는 유사도를 계산할 때 가장 널리 쓰이는 방법 중 하나이다. 다른 유사도 접근법에 비해 일반적으로 성능이 좋은데, 이는 단순히 좌표 상의 거리를 구하는 다른 유사도 측정 방법에 비해 코사인 유사도는 말 그대로 두 벡터 간의 각도를 구하는 것이기 때문에 방향성의 개념이 더해지기 때문이다. 두 문장이 유사하다면 같은 방향을 가리킬 것이고, 유사하지 않을수록 직교로 표현될 것이다.

 

 

코사인 유사도

앞서 TF-IDF로 벡터화한 문장을 사용해 코사인 유사도를 구해볼 수 있다. 코사인 유사도의 경우 직접 함수를 구현할 필요 없이 sklearn에서 유사도 측정을 위한 함수를 제공한다.

 

from sklearn.metrics.pairwise import cosine_similarity

cosine_similarity(tfidf_matrix[0], tfidf_matrix[1]) # 첫 번째와 두 번째 문장 비교

코사인 유사도에서 문장 A와 문장 B의 유사도는 대략 18% 정도로 산출된다.

 

3. 유클리디언 유사도

 

유클리디언 유사도는 가장 기본적인 거리를 측정하는 유사도 공식이며, 공식은 다음과 같다.

 

유클리디언 유사도

여기서 구하는 거리는 유클리디언 거리(Euclidean Distance) 혹은 L2거리(L2-Distance)라고 불리며, n차원 공간에서 두 점 사이의 최단 거리를 구하는 접근법이다. 

 

python 코드를 이용해서 유클리디언 유사도를 구해보자. sklearn에서 제공하는 유클리디언 거리 측정 함수를 사용하면 된다.

 

from sklearn.metrics.pairwise import euclidean_distances

euclidean_distances(tfidf_matrix[0], tfidf_matrix[1])

유클리디언 거리는 단순히 두 점 사이의 거리를 뜻하기 때문에, 이전에 소개했던 유사도 방식들이 0과 1 사이의 값을 가지는 것과 달리 유클리디언 거리는 1 이상의 값을 가질 수 있다. 

 

이러한 제한이 없는 유사도 값은 사용하기가 어려우므로 값을 제한하는 방법을 취한다. 다른 유사도 측정 방식과 동일하게 0과 1 사이의 값을 갖도록 만들어줄 것인데, 이는 벡터화된 각 문장들을 일반화(Normalize) 한 후 다시 유클리디언 유사도를 측정하면 0과 1 사이의 값을 갖게 된다. 여기서는 L1 정규화 방법(L1-Normalization)을 사용하도록 한다. L1 정규화 방법에 대해서 간단히 설명하자면 각 벡터 안의 요소 값을 모두 더한 것이 크기가 1이 되도록 벡터들의 크기를 조절하는 방법이다.

 

import numpy as np

def l1_normalize(v):
    norm = np.sum(v)
    return v / norm

tfidf_norm_l1 = l1_normalize(tfidf_matrix)
euclidean_distances(tfidf_norm_l1[0], tfidf_norm_l1[1])

정규화를 거친 후 유클리디언 유사도를 측정한 결과는 0.205 정도가 나온다. 유클리디언 유사도를 측정할 때는 편의를 위해 정규화한 후 측정하는 방법이 있다는 점을 기억하도록 하자.

 

4. 맨하탄 유사도

 

맨하탄 유사도(Manhattan Similarity)는 맨하탄 거리를 통해 유사도를 측정하는 방법이다. 맨하탄 거리란, 사각형 격자로 이뤄진 지도에서 출발점에서 도착점까지를 가로지르지 않고 갈 수 있는 최단거리를 구하는 공식이다. 유클리디언 거리를 L2 거리로 부르는 반면, 맨하탄 거리는 L1 거리로 부른다.

 

맨하탄 거리 공식

 

맨하탄 유사도 또한 유클리디언 유사도와 마찬가지로 거리를 통해 유사도를 측정하는 방법이라 값이 계속해서 커질 수 있다. 따라서 0과 1 사이의 값을 갖도록 L1 정규화 방법을 사용해 벡터 안의 요소 값을 정규화한 뒤 유사도를 측정한다.

 

from sklearn.metrics.pairwise import manhattan_distances

manhattan_distances(tfidf_norm_l1[0], tfidf_norm_l1[1])

위에서 사용한 네 가지 유사도 방법 중, 맨해튼 유사도로 측정했을 때가 가장 높게 나왔다. 측정 방법에 따라 같은 것을 재더라도 유사도가 크게 달라질 수 있으므로 의도하고자 하는 방향에 맞는 유사도 측정 방법을 고르는 것이 매우 중요하다.

 

 

 

 

이번에도 생각보다 글이 많이 길어진듯 하다.

 

여기서 글을 마무리 하고, 그 다음으로 이어가려고 한다.

 

이번 글에서도 역시 많은 것들을 다루었기 때문에, 이번 글에서 다룬 내용들을 바탕으로 정리할 수 있는 문제들을 준비했다.

 

- 점검할 내용 -

 

첫번째, Skip-Gram의 전체적인 구조에 대해서 설명하라.

 

 

두번째, Word2Vec이 가진 장점 세 가지는 무엇인가?

 

 

세번째, TF-IDF란 무엇이며, TF-IDF가 높을수록 어떤 의미를 갖고 있는가?

 

 

네번째, 텍스트 유사도를 나타내는데 사용되는 네 가지의 유사도는 무엇이며, 각각을 설명하라.

 

+ Recent posts