텍스트 유사도
자연어 처리에서 문장 간의 의미가 얼마나 유사한지 계산은 매우 중요하다. 사람은 두 개의 문장에 동일한 단어나 의미상 비슷한 단어의 분포를 직감적으로 파악하지만, 컴퓨터의 경우에는 공식을 통해 두 문장 간의 유사도를 계산할 수 있음
n-gram 유사도
n-gram은 주어진 문장에서 n개의 연속적인 단어 시퀀스(단어 나열)를 기준으로 의미 파악, n-gram은 문장에서 n개의 단어를 토큰으로 사용, 이웃한 단어의 출현 횟수를 통계적으로 표현해 텍스트의 유사도를 계산
n-gram은 문장을 토큰으로 분리한 후, 단어 문서 행렬(Term - Docuemnt Matrix TDM)을 생성하여 두 문장을 서로 비교해 동일한 단어의 출현 빈도를 확률로 계산해 유사도를 구할 수 있다.
- tf(term frequency): 두 문장 A와 B에서 동일한 토큰의 출현 빈도
- tokens는 문장에서 전체 토큰 수(n-gram으로 분리된 단어)를 의미
결과가 1.0에 가까울수록 B는 A와 유사하다고 볼 수 있다.
from konlpy.tag import Komoran
# 어절 단위 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)
# 문장 정의
sentence1 = '6월에 뉴턴은 선생님의 제안으로 트리니티에 입학했다.'
sentence2 = '6월에 뉴턴은 선생님의 제안으로 대학교에 입학했다.'
sentence3 = '나는 맛있는 밥을 뉴턴 선생님과 먹었다.'
# 형태소 분석기에서 명사(단어) 추출 - 리스트 형태로 추출
komoran = Komoran()
bow1 = komoran.nouns(sentence1)
bow2 = komoran.nouns(sentence2)
bow3 = komoran.nouns(sentence3)
# 단어 n-gram 토큰 추출
doc1 = word_ngram(bow1, 2)
doc2 = word_ngram(bow2, 2)
doc3 = word_ngram(bow3, 2)
# 추출된 n-gram 토큰 출력
print(doc1)
print(doc2)
# 유사도 계산
r1 = similarity(doc1, doc2) # 유사도 0.66
r2 = similarity(doc3, doc1) # 유사도 0
# 계산된 유사도 출력
print(r1)
print(r2)
n-gram은 문장에 존재하는 모든 단어의 출현 빈도를 확인하는 것이 아닌 연속되는 문장에서 일부 단어(n으로 설정된 개수)만 확인하다 보니 전체 문장을 고려한 언어 모델보다 정확도가 떨어질 수 있다.
n-gram의 n 값에 따라 n을 크게 잡을수록 비교 문장의 토큰과 비교할 때 카운트를 놓칠 확률이 증가
n을 적게 잡으면 카운트 확률을 높아지지만 문맥을 파악하는 정확도는 떨어진다.
n-gram 모델에서 n은 매우 중요 보통 1~5 사이 사용
코사인 유사도(cosine similarity)
단어나 문장을 벡터로 표현할 수 있다면 벡터 간 거리나 각도를 이용하여 유사성을 파악할 수 있다. 벡터 간 거리를 구하는 방법은 다양하며 그중 코사인 유사도가 하나의 방식
코사인 유사도는 두 벡터 간 코사인 각도를 이용해 유사도를 측정하는 방법, 일반적으로 코사인 유사도는 벡터의 크기(출현 빈도)가 중요하지 않을 때 그 거리를 측정하기 위해 사용하며, 벡터의 크기에 상관없이 결과가 안정적
- 벡터의 크기는 단어의 출현 빈도를 통해 유사도를 계산한다면 동일한 단어가 많이 포함될수록 벡터의 크기가 증가
코사인은 -1 ~ 1의 값을 가지며, 두 벡터의 방향이 완전 동일하다면 1, 반대의 경우 -1, 두 벡터가 직각을 이루면 0의 값을 가짐. 즉 두 벡터의 방향 즉 1에 가까울수록 유사
from konlpy.tag import Komoran
import numpy as np
from numpy import dot
from numpy.linalg import norm
# 코사인 유사도 계산
# dot 인자로 들어온 2개의 넘파이 배열의 내적곱
# norm 벡터 계산 - L2 노름(유클리드 노름)
def cos_sim(vec1, vec2):
return dot(vec1, vec2) / (norm(vec1) * norm(vec2))
# TDM 만들기
def make_term_doc_mat(sentence_bow, word_dics):
freq\_mat = {}
for word in word_dics:
freq_mat[word] = 0
for word in word_dics:
if word in sentence_bow:
freq_mat[word] += 1
return freq_mat
# 단어 벡터 만들기
def make_vector(tdm):
vec = []
for key in tdm:
vec.append(tdm[key])
return vec
# 문장 정의
sentence1 = '6월에 뉴턴은 선생님의 제안으로 트리니티에 입학했다.'
sentence2 = '6월에 뉴턴은 선생님의 제안으로 대학교에 입학했다.'
sentence3 = '나는 맛있는 밥을 뉴턴 선생님과 함께 먹었다.'
# 형태소 분석기에서 명사(단어) 추출 - 리스트 형태로 추출
komoran = Komoran()
bow1 = komoran.nouns(sentence1)
bow2 = komoran.nouns(sentence2)
bow3 = komoran.nouns(sentence3)
# 단어 묶음 리스트를 하나로 합침
bow = bow1 + bow2 + bow3
# 단어 묶음에서 중복을 제거하여 단어 사전 구축
word_dics = []
for token in bow:
if token not in word_dics:
word_dics.append(token)
# 문장별 단어 문서 행렬 계산
freq_list1 = make_term_doc_mat(bow1, word_dics)
freq_list2 = make_term_doc_mat(bow2, word_dics)
freq_list3 = make_term_doc_mat(bow3, word_dics)
print(freq_list1)
print(freq_list2)
print(freq_list3)
# 문장 벡터 생성
doc1 = np.array(make_vector(freq_list1))
doc2 = np.array(make_vector(freq_list2))
doc3 = np.array(make_vector(freq_list3))
# 코사인 유사도 계산
r1 = cos_sim(doc1, doc2)
r2 = cos_sim(doc3, doc1)
print(r1)
print(r2)
참고
'FullStack > 50. ML' 카테고리의 다른 글
[BOOK] 처음 배우는 딥러닝 챗봇 #2 임베딩 (0) | 2023.01.25 |
---|---|
[BOOK] 처음 배우는 딥러닝 챗봇 #1 (0) | 2023.01.23 |
[BOOK] Building Machine Learning Pipelines #4 데이터 검증 (0) | 2023.01.12 |
[BOOK] Building Machine Learning Pipelines #3 데이터 수집 (0) | 2023.01.12 |
[BOOK] Building Machine Learning Pipelines #2 TFX (텐서플로 익스텐디드) (0) | 2023.01.11 |