■ 단어를 수치 벡터로 표현했다면, 단어 벡터 사이의 유사도를 계산할 수 있다.
1. 코사인 유사도(Cosine Similarity)
■ 코사인 유사도는 두 벡터의 코사인 각도를 이용하여 계산할 수 있다.
- 두 벡터의 방향이 완전히 동일하면, 두 벡터의 각도는 \( 0^\circ \)이므로 \( \cos 0 = 1 \)
- 두 벡터가 서로 직교하면, 두 벡터의 각도는 \( 90^\circ \)이므로 \( \cos 90^\circ = 0 \)
- 두 벡터의 방향이 완전히 반대 방향이면, 두 벡터의 각도는 \( 180^\circ \)이므로 \( \cos 180^\circ = -1 \)
■ 즉, 코사인 유사도는 -1 이상 1 이하의 값을 가지며, 값이 1에 가까울수록 유사도고 높다고 판단할 수 있다.
■ 두 벡터가 \( \mathbf{x} = (x_1, x_2, x_3, \ldots , x_n), \quad \mathbf{y} = (y_1, y_2, y_3, \ldots , y_n) \)이라면, 코사인 유사도의 식은 다음과 같다.
\( \text{similarity}(\mathbf{x}, \mathbf{y}) = \cos(\theta) = \frac{\mathbf{x} \cdot \mathbf{y}}{\|\mathbf{x}\| \|\mathbf{y}\|} = \frac{x_1y_1 + \cdots + x_ny_n}{\sqrt{x_1^2 + \cdots + x_n^2} \sqrt{y_1^2 + \cdots + y_n^2}} \)
■ 코사인 유사도를 이용해서 DTM이나 TF-IDF 행렬을 이루고 있는 문서들의 유사도를 계산할 수 있다.
■ 예를 들어 다음과 같은 예시에서 각 문서 쌍에 대해 코사인 유사도를 계산하면,
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
docs = ["Dog bites man",
"Dog eats meat",
"Man eats food",
"Man eats food Man eats food",
]
vector = CountVectorizer()
tf_idf = TfidfVectorizer().fit(docs)
DTM = vector.fit_transform(docs).toarray() # 각 단어의 빈도수
tf_idf_matrix = tf_idf.transform(docs).toarray()
print(DTM.shape, tf_idf_matrix.shape)
```#결과#```
(4, 6) (4, 6)
````````````
DTM
```#결과#```
array([[1, 1, 0, 0, 1, 0],
[0, 1, 1, 0, 0, 1],
[0, 0, 1, 1, 1, 0],
[0, 0, 2, 2, 2, 0]], dtype=int64)
````````````
print(vector.vocabulary_) # 각 단어와 맵핑된 인덱스
```#결과#```
{'dog': 1, 'bites': 0, 'man': 4, 'eats': 2, 'meat': 5, 'food': 3}
````````````
tf_idf_matrix
```#결과#```
array([[0.70203482, 0.55349232, 0. , 0. , 0.44809973, 0. ],
[0. , 0.55349232, 0.44809973, 0. , 0. , 0.70203482],
[0. , 0. , 0.53256952, 0.65782931, 0.53256952, 0. ],
[0. , 0. , 0.53256952, 0.65782931, 0.53256952, 0. ]])
````````````
print(tf_idf.vocabulary_)
```#결과#```
{'dog': 1, 'bites': 0, 'man': 4, 'eats': 2, 'meat': 5, 'food': 3}
````````````
import numpy as np
from numpy import dot
from numpy.linalg import norm
def cos_sim(x, y):
return dot(x, y)/(norm(x)*norm(y))
doc1, doc2, doc3, doc4 = DTM[0], DTM[1], DTM[2], DTM[-1]
docs = [doc1, doc2, doc3, doc4]
# 모든 문서 쌍에 대해 코사인 유사도 계산
for i in range(len(docs)):
for j in range(i + 1, len(docs)):
similarity = cos_sim(docs[i], docs[j])
print(f'doc{i+1}와 doc{j+1}의 코사인 유사도: {similarity:.4f}')
```#결과#```
doc1와 doc2의 코사인 유사도: 0.3333
doc1와 doc3의 코사인 유사도: 0.3333
doc1와 doc4의 코사인 유사도: 0.3333
doc2와 doc3의 코사인 유사도: 0.3333
doc2와 doc4의 코사인 유사도: 0.3333
doc3와 doc4의 코사인 유사도: 1.0000
````````````
doc1, doc2, doc3, doc4 = tf_idf_matrix[0], tf_idf_matrix[1], tf_idf_matrix[2], tf_idf_matrix[-1]
docs = [doc1, doc2, doc3, doc4]
for i in range(len(docs)):
for j in range(i + 1, len(docs)):
similarity = cos_sim(docs[i], docs[j])
print(f'doc{i+1}와 doc{j+1}의 코사인 유사도: {similarity:.4f}')
```#결과#```
doc1와 doc2의 코사인 유사도: 0.3064
doc1와 doc3의 코사인 유사도: 0.2386
doc1와 doc4의 코사인 유사도: 0.2386
doc2와 doc3의 코사인 유사도: 0.2386
doc2와 doc4의 코사인 유사도: 0.2386
doc3와 doc4의 코사인 유사도: 1.0000
````````````
■ 결과를 보면 \( 4 \times 6 \) 행렬 MTD와 TF-IDF의 세 번째 문서와 네 번째 문서의 코사인 유사도가 1인 것을 볼 수 있다.
■ 그 이유는 MTD와 TF-IDF의 3행의 벡터(세 번째 문서)와 4행의 벡터(네 번째 문서)가 종속 관계. 즉, 서로를 스칼라배로 나타낼 수 있는 평행 관계에 있기 때문이다. 두 벡터가 평행 관계이면, 두 벡터의 각도는 0이므로 코사인 유사도는 \( \cos 0 = 1 \)이 된다. 단, 이 예에서 4행이 3행보다 2배 차이가 나므로, 네 번째 문서는 세 번째 문서의 길이보다 2배의 길이를 가진다고 할 수 있다.
■ 이렇게 코사인 유사도는 벡터의 크기(길이)가 아닌 벡터의 방향(패턴)에 초점을 두고 유사도를 계산하므로, 비교하고자 하는 문서들의 길이가 서로 다른 경우 비교적 공정한 비교가 가능하다.
2. 유클리드 거리(Euclidean distance)
■ 다차원 공간에서 두 점 \( p \)와 \( q \)가 각각 \( p = (p_1, p_2, \cdots, p_n)\; , q = (q_1, q_2, \cdots, q_n) \)의 좌표를 가질 때, 두 점 사이의 거리를 계산하는 유클리드 거리는 \( \text{distance} (p, q) = \sqrt{(p_1 - q_1)^2 + (p_2 - q_2)^2 + \dots + (p_n - q_n)^2} = \sqrt{\displaystyle\sum_{i=1}^{n} (p_i - q_i)^2} \)
■ 예를 들어, 2차원 좌표평면에서 두 점 사이의 거리는 피타고라스 정리를 이용해 계산할 수 있다. 즉, 2차원 좌표평면에서 두 점 사이의 유클리드 거리는 피타고라스의 정리를 통해 두 점 사이의 거리를 계산한 결과와 동일하다.
■ 이 2차원에서 두 점 사이의 유클리드 거리 계산을 통해 얻게 되는 거리값은 두 점이 가까운지 멀리있는지를 나타낸다. 여기서 두 점은 두 단어로 볼 수 있으므로, 두 단어의 유클리드 거리 계산을 통해 두 단어(점)의 거리가 가까우면 두 단어는 유사하다고 할 수 있다.
■ 예를 들어 위의 예시에서 사용한 다음과 같은 DTM 행렬은 단어의 개수가 6개이므로 6차원 공간이며,
array([[1, 1, 0, 0, 1, 0],
[0, 1, 1, 0, 0, 1],
[0, 0, 1, 1, 1, 0],
[0, 0, 2, 2, 2, 0]], dtype=int64)
■ 이때, 문서 쌍의 유클리드 거리 계산을 통해 가장 유사한 문서 쌍을 찾을 수 있다.
doc1, doc2, doc3, doc4 = tf_idf_matrix[0], tf_idf_matrix[1], tf_idf_matrix[2], tf_idf_matrix[-1]
docs = [doc1, doc2, doc3, doc4]
def dist(p, q): # 유클리드 거리 공식
return np.sqrt(np.sum((p-q)**2))
# 모든 문서 쌍에 대해 유클리드 거리 계산
for i in range(len(docs)):
for j in range(i + 1, len(docs)):
euclidean_distance = dist(docs[i], docs[j])
print(f'doc{i+1}와 doc{j+1}의 유클리드 거리: {euclidean_distance:.4f}')
```#결과#```
doc1와 doc2의 유클리드 거리: 1.1778
doc1와 doc3의 유클리드 거리: 1.2340
doc1와 doc4의 유클리드 거리: 1.2340
doc2와 doc3의 유클리드 거리: 1.2340
doc2와 doc4의 유클리드 거리: 1.2340
doc3와 doc4의 유클리드 거리: 0.0000
````````````
- 이 예시의 결과는 6차원 공간에서 문서3과 문서4의 유클리드 거리가 0인 것을 볼 수 있다. 이는 6차원 공간에서 두 문서 벡터가 완전히 동일하다는 것을 의미한다.
- 문서 3과 문서4를 제외하면, 이 예에서는 문서 1과 문서 2가 가장 유사하다고 볼 수 있다.
3. 자카드 유사도(Jaccard similarity)
■ 자카드 유사도는 \( A \)와 \( B \)라는 두 개의 집합이 있다고 했을 때, 두 집합의 합집합과 두 집합의 교집합의 비율로 두 집합의 유사도를 계산하는 방법이다.
\(
J(A, B) = \dfrac{|A \cap B|}{|A \cup B|} = \dfrac{|A \cap B|}{|A| + |B| - |A \cap B|}
\)
■ 그러므로, 자카드 유사도에서 두 집합의 원소가 완전히 동일하면 \( J(A, B) = 1 \)이 되고 완전히 다르다면. 즉, 두 집합의 교집합이 공집합이라면 분자가 0이되므로 \( J(A, B) = 0 \)이 된다. 즉, 자카드 유사도는 0과 1사이의 값을 가진다.
■ 자카드 유사도로 두 개의 문장(또는 두 개의 문서)을 비교하기 위해서는 두 문장의 토큰을 비교해야 한다. 그래야 다음과 같이 두 집합의 원소 중 어느 것이 교집합인지 쉽게 알 수 있다.
doc1 = "Dog bites man"
doc2 = "Man eats food"
# 띄어쓰기 기준으로 토큰화
doc1_token = doc1.lower().split()
doc2_token = doc2.lower().split()
union = set(doc1_token).union(set(doc2_token)) # 합집합
intersection = set(doc1_token).intersection(set(doc2_token)) # 교집합
print(union, intersection)
print(len(union), len(intersection))
```#결과#```
{'food', 'eats', 'bites', 'dog', 'man'} {'man'}
5 1
````````````
- 이 에에서 합집합의 원소 개수는 5개, 교집합의 원소 개수는 1개이다. 자카드 유사도는 교집합의 크기를 합집합의 크기로 나눈 것이므로 이 예에서 doc1과 doc2의 자카드 유사도는 0.2이다.
print('자카드 유사도:', len(intersection)/len(union))
```#결과#```
자카드 유사도: 0.2
````````````
'자연어처리' 카테고리의 다른 글
Pre-trained Word Embedding (0) | 2025.03.04 |
---|---|
원-핫 인코딩, 워드 임베딩 (0) | 2025.02.28 |
Bag of Words, DTM, TF-IDF (0) | 2025.02.27 |
언어 모델(Language Model) (0) | 2025.02.25 |
텍스트 표준화, 토큰화, 어휘 사전(단어 집합) 인덱싱 (0) | 2025.02.24 |