단어들이나 문장들을 벡터로 표현이 가능할 경우 이 벡터간의 거리를 이용해서 유사성을 알 수 있으며 본 포스팅은 대표적으로 많이 활용하는 코사인 유사도를 설명하고 문장간의 유사도를 Python으로 구현해보도록 한다.
코사인 유사도는 벡터간의 코사인 각도를 이용하여 서로간에 얼마나 유사한지를 산정한다. 각도를 유사도로 판별하기 때문에 거리가 중요하지 않을 경우 사용되는 방식이라 할 수 있겠다.
예를 들어 다음과 같은 문장이 있다고 가정을 해보자
첫번째 문장 : 안녕하세요 저는 자비스 입니다.
두번째 문장 : 안녕하세요 저는 자비스 입니다. 안녕하세요 저는 자비스 입니다.
위 문장에서는 동일한 내용을 다루고 있다. 공백기준으로 벡터화를 한다면, 첫번째 문장은 [안녕하세요, 저는, 자비스, 입니다] 정도로 벡터화가 될 것이며 두번째 문장은 [안녕하세요, 저는, 자비스, 입니다, 안녕하세요, 저는, 자비스, 입니다]로 벡터화가 될 것이다.
두개의 문장은 사실 단어가 동일하고 단어의 빈도수만 다르기 때문에 각도로 표현하게 될 경우 동일한 결과인 1이 나오게 된다. 코사인 유사도의 장점은 바로 이렇게 빈도수를 뛰어넘어 유사한 문장은 매우 쉽게 찾을 수 있다는 것이다.
코사인유사도 공식
코사인 유사도는 -1 ~ 1 사이의 값을 가진다. 벡터들의 방향이 완전히 다를 경우 즉 각도가 180°일 경우에는 -1이고, 방향이 완전히 동일하면 1이며, 값이 90°의 각일 경우 0 값을 가지게 된다.
두개의 벡터인 A와 B에 대해서 유사도를 구하는 것은 다음과 같은 식으로 표현할 수 있다.
예시
문장1 : 안녕 나는 애플을 만든 스티브잡스라고 해
문장2 : 안녕 나는 페이스북을 만든 주커버그라고 해
문장3 : 나는 애플과 스티브잡스를 좋아해. 주커버그는 별로야
이번에는 위 예시를 기반으로 코사인 유사도를 구해보도록 한다. 우선 위 예시를 형태소 분석을 통해서 의미있는 품사인 명사를 벡터로 변환한다.
from konlpy.tag import Okt
okt = Okt()
text1 = '안녕 나는 애플을 만든 스티브잡스야'
text2 = '안녕 나는 페이스북을 만든 주커버그야'
text3 = '나는 애플과 스티브잡스를 좋아해. 주커버그는 별로야'
print(okt.nouns(text1))
print(okt.nouns(text2))
print(okt.nouns(text3))
형태소 분석 결과
['안녕', '나', '애플', '스티브잡스']
['안녕', '나', '페이스북', '주커버그']
['나', '애플', '스티브잡스', '주커버그', '별로']
위 명사 벡터값들을 행렬로 표현하면 다음과 같다.
이를 기반으로 첫번째와 두번째 값의 유사도를 구해보도록 한다. 공식과 쉬운 대조를 위해 첫번째 벡터들을 A로 표현하고 두번째 벡터들을 B라고 표현한다
A = [1,1,1,1,0,0,0]
B = [1,1,0,0,1,1,0]
계산과정, 분모
코사인 유사도에서의 분모는 두 벡터 크기의 곱을 의미한다.
계산과정, 분자
코사인 유사도에서의 분자는 두 벡터의 내적(inner product, 內積)을 의미한다.
계산과정, 최종
두 벡터의 내적은 2이며, 크기의 곱은 4가 나왔다. 즉, 2/4가 나왔으며 최종 유사도는 50%의 유사도를 가지게 된다.
소스 코드
from konlpy.tag import Okt
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))
# 기준이 되는 키워드와 벡터 키워드 리스트를 받아서 키워드별 빈도를 구하는 함수
def make_matrix(feats, list_data):
freq_list = []
for feat in feats:
freq = 0
for word in list_data:
if feat == word:
freq += 1
freq_list.append(freq)
return freq_list
okt = Okt()
text1 = '안녕 나는 애플을 만든 스티브잡스야'
text2 = '안녕 나는 페이스북을 만든 주커버그야'
text3 = '나는 애플과 스티브잡스를 좋아해. 주커버그는 별로야'
v1 = okt.nouns(text1)
v2 = okt.nouns(text2)
v3 = okt.nouns(text3)
# 단어들을 중복제거를 위해, set에 데이터를 쌓는다
v4 = v1 + v2 + v3
feats = set(v4)
v1_arr = np.array(make_matrix(feats, v1))
v2_arr = np.array(make_matrix(feats, v2))
v3_arr = np.array(make_matrix(feats, v3))
cs1 = cos_sim(v1_arr, v2_arr)
cs2 = cos_sim(v1_arr, v3_arr)
cs3 = cos_sim(v2_arr, v3_arr)
print('v1 <-> v2 = ', cs1)
print('v1 <-> v3 = ', cs2)
print('v2 <-> v3 = ', cs3)
결과
v1 <-> v2 = 0.5
v1 <-> v3 = 0.6708203932499369
v2 <-> v3 = 0.4472135954999579
연관포스팅
코사인 유사도(Cosine similarity) 이해 및 Java로 구현하기
맨하탄 거리(Manhattan Distance) 개념과 구현해보기
'인공지능 및 데이터과학 > 머신러닝 및 딥러닝' 카테고리의 다른 글
[딥러닝] 순환신경망(RNN)과 LSTM, GRU (0) | 2021.06.01 |
---|---|
머신러닝의 작업 플로우(Workflow) (0) | 2021.05.24 |
[Java] 직접 구현해본 나이브 베이즈 분류기 #2 (코드포함) (0) | 2020.12.24 |
[Java] 직접 구현해본 나이브 베이즈 분류기 #1 (0) | 2020.12.22 |
[Python] 파이썬으로 나이브베이즈 구현하기 (0) | 2020.12.20 |