문서의 유사도를 구하는 일은 자연어 처리의 주요 주제 중 하나이다.
사람들이 말하는 문서의 유사도란 문서를 볼 때 문서들 간에 동일한 단어 또는 비슷한 단어가 얼마나 많이 쓰였는지를 기준으로 한다.
기계가 계산하는 문서의 유사도의 성능은 각 문서의 단어들을 어떤 방법으로 수치화하여 표현했는지(TDM, Word2Vec 등), 문서 간의 단어들의 차이를 어떤 방법(유클리디안 거리, 코사인 유사도 등)으로 계산했는지에 달려있다.
n-gram 유사도
n-gram 은 주어진 문장에서 n개의 연속적인 단어 시퀀스(단어 나열)를 의미한다.
n-gram은 문장에서 n개의 단어를 토큰으로 사용한다. 이는 이웃한 단어의 출현 횟수를 통계적으로 표현해 텍스트의 유사도를 계산하는 방법이다.
손쉬운 구현 방식에 비해 학습 말뭉치 품질만 좋다면 괜찮은 성능을 보여준다.
서로 다른 문장을 n-gram으로 비교하면 단어의 출현 빈도에 기반한 유사도를 계산할 수 있으며 이를 통해 논문 인용이나 도용 정도를 조사할 수 있다.
n이 1인 경우 1-gram 또는 유니그램, 2인 경우 2-gram 바이그램, 3인 경우 3-gram 또는 트라이그램이라 부르며, 4이상은 숫자만 앞쪽에 붙여 부른다.
𝑡𝑓 𝑡𝑒𝑟𝑚 𝑓𝑟𝑒𝑞𝑢𝑒𝑛𝑐𝑦 는 두 문장 A와 B에서 동일한 토큰의 출현 빈도를 뜻하며, 𝑡𝑜𝑘𝑒𝑛𝑠는 해당 문장에서 전체 토큰 수를 의미한다.
여기서 토큰이란 n-gram으로 분리된 단어이다. 위 수식은 기준이 되는 문장 A에서 나온 전체 토큰 중에서 A와 B에 동일한 토큰이 얼마나 있는지 비율로 표현한 수식 이다. 1.0에 가까울 수록 B가 A에 유사하다고 볼 수 있다.
A : 6월에 뉴턴은 선생님의 제안으로 트리니티에 입학했다.
B : 6월에 뉴턴은 선생님의 제안으로 대학교에 입학했다.
그림은 문장 A와 B의 단어 문서 행렬을 표현한 표이다. 행렬의 열은 문장 A의 2-gram 토큰값이며, 행은 문장 A와 B로 구성되어 있다.
앞의 n-gram유사도 수식에 따르면 4/6 로 0.66 …의 유사도를 가지고 있다. 즉, 문장 B는 문장 A와 66% 유사하다.
# 어절 단위 n-gram
def word_ngram(bow, num_gram):
text = tuple(bow)
ngrams = [text[x:x + num_gram] for x in range(0, len(text))]
return tuple(ngrams)
# 유사도 계산
def similarity(doc1, doc2):
cnt = 0
for token in doc1:
if token in doc2:
cnt = cnt + 1
return cnt / len(doc1)
코사인 유사도(Cosine Similarity)
단어나 문장을 벡터로 표현할 수 있다면 벡터 간 거리나 각도를 이용해 유사성을 파악할 수 있다.
코사인 유사도는 두 벡터간 코사인 각도를 이용해 유사도를 측정하는 방법이다.
코사인 유사도는 벡터의 크기가 중요하지 않을 때 그 거리를 측정하기 위해 사용한다. 예를 들어 단어의 출현 빈도를 통해 유사도 계산을 한다면 동일한 단어가 많이 포함되어 있을수록 벡터의 크기가 커진다.
코사인 값은 -1 ~ 1 사이의 값을 가지며, 두 벡터의 방향이 완전히 동일한 경우에는 1, 반대 방향인 경우에는 -1, 두 벡터가 서로 직각을 이루면 0의 값을 가진다. 즉, 두 벡터의 방향이 같아질 수록 유사하다 볼 수 있다.
단어 문서 행렬이나 TF-IDF 행렬을 통해서 문서의 유사도를 구하는 경우에는 단어 문서 행렬이나 TF-IDF 행렬이 각각의 특징 벡터 A, B가 된다.
문서1 : 저는 사과 좋아요
문서2 : 저는 바나나 좋아요
문서3 : 저는 바나나 좋아요 저는 바나나 좋아요
문서 단어 행렬
def cosine_similarity(x1, x2):
return sum(x1 * x2) / (sum(x1**2)**.5 * (sum(x2**2)**.5))
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])
print(cos_sim(doc1, doc2)) #문서1과 문서2의 코사인 유사도
print(cos_sim(doc1, doc3)) #문서1과 문서3의 코사인 유사도
print(cos_sim(doc2, doc3)) #문서2과 문서3의 코사인 유사도
문서1과 문서2의 코사인 유사도는 0.67이고, 문서2와 문서3의 코사인 유사도는 1을 갖는다.
# 사이킷런의 코사인유사도
sentence = ("저는 사과 좋아요.", "저는 바나나 좋아요.", "저는 바나나 좋아요 저는 바나나 좋아요")
from sklearn.feature_extraction.text import TfidfVectorizer
# 객체 생성
tfidf_vectorizer = TfidfVectorizer()
# 문장 벡터화 진행
tfidf_matrix = tfidf_vectorizer.fit_transform(sentence)
# 각 단어
text = tfidf_vectorizer.get_feature_names()
# 각 단어의 벡터 값
idf = tfidf_vectorizer.idf_
from sklearn.metrics.pairwise import cosine_similarity
cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])
첫번째와 두번째 문장은 0.47400325의 유사도를 갖고, 두번째와 세번째 문장은 1의 유사도를 갖는다.
맨하탄 유사도 ( L1 Distance )
- 맨하탄 거리를 통해 유사도를 측정하는 방법이다.
- L1 Norm은 두 개의 벡터를 빼고, 절대값을 취한 뒤, 합한 것이다.
- 사각형 격자로 이뤄진 지도에서 출발점에서 도착점까지를 가로지르지 않고 갈 수 있는 최단거리를 구하는 공식이다.
def L1_distance(x1, x2):
return sum(abs(x1 - x2))
from sklearn.metrics.pairwise import cosine_similarity
cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])
유클리디언 유사도 ( L2 Distance )
- 가장 기본적인 거리를 측정하는 유사도 공식이다.
- 유클리디언 거리 = L2 거리 : N차원 공간에서 두 점 사이의 최단 거리를 구하는 접근법
def L2_distance(x1, x2):
return sum((x1 - x2)**2)**.5
from sklearn.metrics.pairwise import euclidean_distances
euclidean_distances(tfidf_matrix[0:1], tfidf_matrix[1:2])
검정색 두 점사이의 L1 Norm 은 빨간색, 파란색, 노란색 선으로 표현 될 수 있고, L2 Norm 은 오직 초록색 선으로만 표현될 수 있다. L1 Norm 은 여러가지 path 를 가지지만 L2 Norm 은 Unique shortest path 를 가진다.
자카드 유사도(Jaccard similarity)
- 두 문장을 각각 단어의 집합으로 만든 뒤 두 집합을 통해 유사도를 측정하는 방식 중 하나이다.
- 유사도를 측정하는 방법은 두 집합의 교집합인 공통된 단어의 개수를 두 집합의 합집합, 전체 단어의 갯수로 나눈다.
- 결괏값은 공통의 원소의 개수에 따라 0과 1사이의 값이 나올 것이고, 1에 가까울수록 유사도가 높다.
A : 휴일인 오늘도 서쪽을 중심으로 폭염이 이어졌는데요, 내일은 반가운 비 소식이 있습니다.
B : 폭염을 피해서 휴일에 놀러왔다가 갑작스런 비로 인해 망연자실하고 있습니다.
A = {휴일, 인, 오늘, 도, 서쪽, 을, 중심, 으로, 폭염, 이, 이어졌는데요, 내일, 은, 반가운, 비, 소식, 있습니다.}
B = {폭염, 을, 피해서, 휴일, 에, 놀러왔다가, 갑작스런, 비, 로, 인해, 망연자실, 하고, 있습니다.}
자카드 공식에 대입해 보면 교집합은 6개 합집합은 24개 이므로 6/24=0.25가 된다.
<참고 사이트> wikidocs.net/24602
'NLP' 카테고리의 다른 글
카운트 기반의 단어 표현(Count based word Representation) (0) | 2022.01.28 |
---|---|
패딩(Padding) (0) | 2022.01.28 |
정수 인코딩 (Integer Encoding) (0) | 2022.01.27 |
어간 추출(Stemming)과 표제어 추출(Lemmatization) (0) | 2022.01.27 |
토큰화 Tokenization (0) | 2022.01.27 |