자연어처리(NLP) 7일차 (문서 유사도)

정민수
8 min readJun 9, 2019

--

2019.06.09

본 글은 https://wikidocs.net/book/2155을 참고하여 작성되었음을 밝힙니다.

핵심키워드

  • 문서 유사도(Document SImilarity)
  • 코사인 유사도(Cosine Similarity)

문서 유사도(Document Similarity)

문서의 유사도를 구하는 일은 자연어 처리의 주요 주제 중 하나이다. 사람들이 말하는 문서의 유사도란 문서를 볼 때 문서들 간에 동일한 단어 또는 비슷한 단어가 얼마나 많이 쓰였는지를 기준으로 한다. 즉, 기계도 마찬가지이다. 기계가 계산하는 문서의 유사도의 성능은 각 문서의 단어들을 어떤 방법으로 수치화하여 표현했는지(TDM, Word2Vec 등), 문서 간의 단어들의 차이를 어떤 방법(유클리디안 거리, 코사인 유사도 등)으로 계산했는지에 달려있다.

코사인 유사도(Cosine Similarity)

BoW나 BoW에 기반한 단어 표현 방법인 TDM, TF-IDF, 또는 뒤에서 배우게 될 워드투벡터(Word2Vec) 등과 같이 단어를 수치화할 수 있는 방법을 이해했다면, 이러한 표현 방법에 대해서 코사인 유사도를 이용하여 문서의 유사도를 구하는 게 가능하다.

1 . 코사인 유사도

코사인 유사도는 두 벤터 간의 코사인 각도를 이용하여 구할 수 있는 두 벡터의 유사도를 의미한다. 두 벡터의 방향이 완전히 동일한 경우에는 1의 값을 가지며, 90º의 각을 이루면 0, 180º로 반대의 방향을 가지면 -1의 값을 갖게 된다. 즉, 결국 코사인 유사도는 -1 이상 1 이하의 값을 가지며 값이 1에 가까울수록 유사도가 높다고 판단할 수 있다. 이를 직관적으로 이해하면 두 벡터가 가리키는 방향이 얼마나 유사한가를 의미한다.

두 벡터 A, B에 대해서 코사인 유사도는 식으로 표현하면 다음과 같다.

단어 문서 행렬이나 TF-IDF 행렬을 통해서 문서의 유사도를 구하는 경우에는 단어 문서 행렬이나 TF-IDF 행렬이 각각의 특징 벡터 A, B가 된다.

그렇다면 단어 문서 행렬(또는 각 문서에 대한 BoW)에 대해서 코사인 유사도를 구해보는 간단한 예제를 진행해보자.

문서 1 : 저는 사과 좋아요

문서 2 : 저는 바나나 좋아요

문서 3 : 저는 바나나 좋아요 저는 바나나 좋아요

위의 세 문서에 대해서 단어 문서 행렬을 만들면 다음과 같다.

파이썬에서는 코사인 유사돌르 구하는 방법이 여러가지가 있는데 여기서는 Numpy를 이용해서 직접 식을 세워서 계산해보도록 한다.

from numpy import dot
from numpy.linalg import norm
import numpy as np
def cos_sim(A, B):
return dot(A, B)/(norm(A)*norm(B))

코사인 유사도를 계산하는 함수를 만들었다.

doc1 = np.array([0, 1, 1, 1])
doc2 = np.array([1, 0, 1, 1])
doc3 = np.array([2, 0, 2, 2])

예를 들었던 문서1, 문서2, 문서3에 대해서 각각 BoW를 만들었다. 이제 각 문서에 대한 코사인 유사도를 계산해보자.

print(cos_sim(doc1, doc2))
print(cos_sim(doc2, doc3))
print(cos_sim(doc1, doc3))
0.6666666666666667
1.0000000000000002
0.6666666666666667

눈여겨볼만한 점은 문서1과 문서2의 코사인 유사도와 문서1과 문서3의 코사인 유사도가 같다는 점과 문서2와 문서3의 코사인 유사도가 1이 나온다는 것이다. 앞서 1은 두 벡터의 방향이 완전히 동일한 경우에 1이 나오며, 코사인 유사도 관점에서는 유사도의 값이 최대임을 의미한다고 언급한 바 있다.

문서3은 문서2에서 단지 모든 단어의 빈도수가 1씩 증가했을 뿐이다. 이는 한 문서 내의 모든 단어의 빈도수가 똑같이 증가하는 경우에는 기존의 문서와 코사인 유사도의 값이 1이라는 것이다.

2. 유사도를 이용한 추천 시스템 구현하기

캐글에서 사용되었던 무비 데이터셋을 가지고 영화 추천 시스템을 만들어보자. TF-IDF와 코사인 유사도만으로 영화의 줄거리에 기반해서 영화를 추천하는 추천 시스템을 만들 수 있다.

다운로드 링크 : https://www.kaggle.com/rounakbanik/the-movies-dataset

import pandas as pd
data = pd.read_csv('./the-movies-dataset/movies_metadata.csv', low_memory=False)
data.head(2)

훈련 데이터는 총 24개의 열을 갖고 있으며 코사인 유사도에 사용할 데이터는 overview이다. 좋아하는 영화를 입력하면, 해당 영화의 줄거리에 대해서 줄거리가 유사한 영화를 찾아서 추천하는 시스템을 만들것이다.

data = data.head(20000)

20,000개의 데이터로만 학습한다.

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer(stop_words='english')
data['overview'] = data['overview'].fillna('')
# 줄거리에서 NaN 값을 가진 경우를 제거하기 위하여

tf-idf를 할 때 NaN값이 들어있으면 에러가 나기 때문에 NaN값을 제거했다. (해당 행을 그대로 두고 overivew의 NaN값만 제거함)

tfidf_matrix = tfidf.fit_transform(data['overview'])
print(tfidf_matrix.shape)
(20000, 47487)

overview컬럼에 대해서 tf-idf를 수행했다. 20,000개의 영화를 표현하기 위해 총 47,487개의 단어가 사용되었음을 보여주고 있다. 이제 코사인 유사도를 사용하면 바로 문서의 유사도를 구할 수 있다.

from sklearn.metrics.pairwise import linear_kernel
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)

코사인 유사도를 구한다.

indices = pd.Series(data.index, index=data['title']).drop_duplicates()print(indices.head())
title
Toy Story 0
Jumanji 1
Grumpier Old Men 2
Waiting to Exhale 3
Father of the Bride Part II 4
dtype: int64

영화 타이틀과 인덱스를 가진 테이블을 생성하였다. 이 테이블의 용도는 영화의 타이틀을 입력하면 인덱스를 리턴하기 위함이다.

idx = indices['Father of the Bride Part II']
print(idx)
4

이제 선택한 영화에 대해서 코사인 유사도를 이용하여, 가장 overview가 유사한 10개의 영화를 찾아내는 함수를 만든다.

def get_recommendations(title, cosine_sim = cosine_sim):
idx = indices[title]

# 모든 영화에 대해서 해당 영화의 유사도를 구한다.
sim_scores = list(enumerate(cosine_sim[idx]))

# 유사도에 따라 영화들을 정렬한다.
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

# 가장 유사한 10개의 영화를 받아온다.
sim_scores = sim_scores[1:11]

# 가장 유사한 10개의 영화의 인덱스를 받아온다.
movie_indices = [i[0] for i in sim_scores]

# 가장 유사한 10개의 영화의 제목을 리턴한다.
return data['title'].iloc[movie_indices]

영화 다크 나이트 라이즈와 overview가 유사한 영화들을 찾아보자.

get_recommendations('The Dark Knight Rises')12481                            The Dark Knight
150 Batman Forever
1328 Batman Returns
15511 Batman: Under the Red Hood
585 Batman
9230 Batman Beyond: Return of the Joker
18035 Batman: Year One
19792 Batman: The Dark Knight Returns, Part 1
3095 Batman: Mask of the Phantasm
10122 Batman Begins
Name: title, dtype: object

줄거리를 비교하여 코사인 유사도를 통해 가장 유사한 영화가 출력되는데, 영화 다크 나이트가 첫번째고, 그 외에도 전부 배트맨 영화를 찾아낸 것을 확인할 수 있다.

--

--

정민수
정민수

No responses yet