Home [Chatbot] Chapter5 텍스트 유사도
Post
Cancel

[Chatbot] Chapter5 텍스트 유사도

텍스트 유사도

  • 임베딩으로 각 단어들의 벡터를 구한 다음 벡터 간의 거리를 계산하여 단어 간의 유사성 계산 가능
  • 유사도 계산을 위해 단어들을 수치화해야 함
  • 언어 모델에 따라 통계 / 인공 신경망 이용 방법으로 나뉨

1. n-gram

  • n-gram은 주어진 문장에서 n개의 연속적인 단어 시퀀스를 의미
  • 문장에서 n개의 단어를 토큰으로 사용
  • 이웃한 단어의 출현 횟수를 통계적으로 표현해 텍스트의 유사도를 계산
  • 논문 인용, 도용 정도 조사 가능
  • 모든 단어의 출현 빈도가 아닌 연속되는 문장에서 일부 단어(n만큼)만 확인하므로 정확도가 떨어질 수 있음
  • 일반적으로 n은 1 ~ 5 사이 값을 사용
1
2
3
4
5
6
7
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)
1
2
3
4
5
6
7
# 유사도 계산
def similarity(doc1, doc2):
    cnt = 0
    for token in doc1:
        if token in doc2:
            cnt += 1
    return cnt / len(doc1)
1
2
3
4
# 문장 정의
sentence1 = '6월에 뉴턴은 선생님의 제안으로 트리니티에 입학했다.'
sentence2 = '6월에 뉴턴은 선생님의 제안으로 대학교에 입학했다.'
sentence3 = '나는 맛있는 밥을 뉴턴 선생님과 함께 먹었다.'
1
2
3
4
5
6
7
# 형태소 분석기에서 명사 추출
komoran = Komoran()
bow1 = komoran.nouns(sentence1)
bow2 = komoran.nouns(sentence2)
bow3 = komoran.nouns(sentence3)

print(bow1)
1
['6월', '뉴턴', '선생님', '제안', '트리니티', '입학']
1
2
3
4
5
6
# 단어 n-gram 토큰 추출 (2-gram 방식)
doc1 = word_ngram(bow1, 2)
doc2 = word_ngram(bow2, 2)
doc3 = word_ngram(bow3, 2)

print(doc1)
1
(('6월', '뉴턴'), ('뉴턴', '선생님'), ('선생님', '제안'), ('제안', '트리니티'), ('트리니티', '입학'), ('입학',))
1
2
3
4
5
6
# 유사도 계산
r1 = similarity(doc1, doc2)
r2 = similarity(doc3, doc1)

print(r1)
print(r2)
1
2
0.6666666666666666
0.0

2. 코사인 유사도

  • 두 벡터 간 코사인 각도를 이용한 유사도 측정 방법
  • 출현 빈도를 통해 유사도를 계산하면 동일한 단어가 많이 포함될수록 벡터의 크기가 커짐
  • 코사인 유사도는 벡터 크기와 상관 없이 안정적


  • 두 벡터의 방향이 동일할 경우 1
  • 두 벡터의 방향이 반대일 경우 -1
  • 두 벡터가 서로 직각일 경우 0
  • 코사인은 -1 ~ 1 사이의 값을 가짐
1
2
3
from konlpy.tag import Komoran
import numpy as np
from numpy.linalg import norm
1
2
3
# 코사인 유사도 계산
def cos_sim(vec1, vec2):
    return np.dot(vec1, vec2) / (norm(vec1) * norm(vec2))
1
2
3
4
5
6
7
8
9
10
11
12
# DTM 만들기
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
1
2
3
4
5
6
# 단어 벡터 만들기
def make_vector(dtm):
    vec = []
    for key in dtm:
        vec.append(dtm[key])
    return vec
1
2
3
4
# 문장 정의
sentence1 = '6월에 뉴턴은 선생님의 제안으로 트리니티에 입학했다.'
sentence2 = '6월에 뉴턴은 선생님의 제안으로 대학교에 입학했다.'
sentence3 = '나는 맛있는 밥을 뉴턴 선생님과 함께 먹었다.'
1
2
3
4
5
6
7
8
9
# 형태소 분석기를 이용해 단어 묶음 리스트 생성
komoran = Komoran()
bow1 = komoran.nouns(sentence1)
bow2 = komoran.nouns(sentence2)
bow3 = komoran.nouns(sentence3)

print(bow1)
print(bow2)
print(bow3)
1
2
3
['6월', '뉴턴', '선생님', '제안', '트리니티', '입학']
['6월', '뉴턴', '선생님', '제안', '대학교', '입학']
['밥', '뉴턴', '선생', '님과 함께']
1
2
3
# 단어 묶음 리스트를 하나로 합침
bow = bow1 + bow2 + bow3
print(bow)
1
['6월', '뉴턴', '선생님', '제안', '트리니티', '입학', '6월', '뉴턴', '선생님', '제안', '대학교', '입학', '밥', '뉴턴', '선생', '님과 함께']
1
2
3
4
5
6
7
# 단어 묶음에서 중복을 제거해 단어 사전 구축
word_dics = []
for token in bow:
    if token not in word_dics:
        word_dics.append(token)

print(word_dics)
1
['6월', '뉴턴', '선생님', '제안', '트리니티', '입학', '대학교', '밥', '선생', '님과 함께']
1
2
3
4
5
6
7
8
# 문장별 단어 문서 행렬 계산
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)
1
2
3
{'6월': 1, '뉴턴': 1, '선생님': 1, '제안': 1, '트리니티': 1, '입학': 1, '대학교': 0, '밥': 0, '선생': 0, '님과 함께': 0}
{'6월': 1, '뉴턴': 1, '선생님': 1, '제안': 1, '트리니티': 0, '입학': 1, '대학교': 1, '밥': 0, '선생': 0, '님과 함께': 0}
{'6월': 0, '뉴턴': 1, '선생님': 0, '제안': 0, '트리니티': 0, '입학': 0, '대학교': 0, '밥': 1, '선생': 1, '님과 함께': 1}
1
2
3
4
5
6
7
8
# 문장 벡터 생성
doc1 = np.array(make_vector(freq_list1))
doc2 = np.array(make_vector(freq_list2))
doc3 = np.array(make_vector(freq_list3))

print(doc1)
print(doc2)
print(doc3)
1
2
3
[1 1 1 1 1 1 0 0 0 0]
[1 1 1 1 0 1 1 0 0 0]
[0 1 0 0 0 0 0 1 1 1]
1
2
3
4
5
6
# 코사인 유사도 계산
r1 = cos_sim(doc1, doc2)
r2 = cos_sim(doc3, doc1)

print(r1)
print(r2)
1
2
0.8333333333333335
0.20412414523193154
This post is licensed under CC BY 4.0 by the author.