본문 바로가기
AI/밑딥

딥러닝) 시소러스 , 통계 기반 기법 , 코사인 유사도

by 채채씨 2021. 4. 25.
728x90
반응형

자연어 처리(Natural Language Processing, NLP)란 한국어와 영어 등 인간이 평소에 사용하는 말을 컴퓨터에게 이해시키기 위한 기술이다. 컴퓨터가 사람의 말을 이해하도록 하여 여러가지 일을 수행하게 하는 것이 자연어 처리의 목표이다.

'단어'는 의미의 최소단위이므로 컴퓨터에게 단어를 이해시키는 것이 중요하다. 그 방법으로 시소러스를 활용한 기법, 통계 기반 기법, 추론 기반 기법(word2vec)이 있다.

 

 

 

1. 시소러스

시소러스유의어 사전으로 뜻이 같거나 비슷한 단어가 그룹으로 분류되어 있다. (예시 car = auto, automobile, machine, motorcar) 뿐만 아니라, 단어 사이의 상위/하위 개념 또는 전체/부분 관계를 그래프로 정의하고 있다. 시소러스는 이렇게 단어의 관계를 연결한 단어 네트워크를 이용하여 단어의 의미를 어느정도 이해시켰다고 할 수 있다. 대표적인 시소러스는 WordNet이고, NLTK모듈로 사용할 수 있다.

 

 

■시소러스의 문제점

1) 시대 변화에 대응하기 어렵다.

2) 인건비가 많다.

3) 단어의 미묘한 차이를 표현할 수 없다.

 

사람이 단어의 의미를 정하는 기법인 시소러스는 위와 같은 문제점들이 있으므로, 이를 보완하기 위해 '통계 기반 기법'과 '추론 기반 기법'이 등장하였다. 이 두 기법은 ★대량의 텍스트 데이터에서 단어의 의미를 자동으로 추출한다. 단어를 일일이 연결짓는 노동을 피한 방법이다.

 

 

 

2. 통계 기반 기법

통계 기반 기법에서는 대량의 텍스트 데이터인 말뭉치(corpus)를 이용한다. 말뭉치는 문장을 쓰는 방법이나 단어를 선택하는 등 자연어에 대한 사람의 지식이 담겨 있다. 자연어 처리에 사용되는 말뭉치에는 품사와 같이 텍스트 데이터에 대한 추가 정보가 포함될 수 있다. 먼저는 추가 정보가 없는 단순한 텍스트 데이터로 실습을 할 것이다.

 

 

■말뭉치 전처리

말뭉치를 단어 단위로 분할

text = 'You say goodbye and I say hello.'

text = text.lower()
text = text.replace('.', ' .')
print(text) #'you say goodbye and i say hello .'

words = text.split()
print(words) #['you', 'say', 'goodbye', 'and', 'i', 'say', 'hello', '.']

단어별로 조작하는 것은 불편하므로 단어에 아이디를 부여

word_to_id = {}
id_to_word = {}

for word in words:
    if word not in word_to_id:
    	new_id = len(word_to_id)
        word_to_id[word] - new_id
        id_to_word[new_id] = word
        
print(word_to_id)
#{'you':0, 'say':1, 'goodbye':2, 'and':3, 'i':4, 'hello':5, '.':6}

print(id_to_word)
#{0:'you', 1:'say', 2:'goodbye', 3:'and', 4:'i', 5:'hello', 6:'.'}

위의 전처리 과정을 process함수로 만들면 다음과 같다.

def preprocess(text):
    text = lower()
    text = text.replace('.', ' .')
    words = text.split()
    
    word_to_id = {}
    id_to_word = {}
    for word in words:
    	new_id = len(word_to_id)
        word_to_id[word] = new_id
        id_to_word[new_id] = word
        
    corpus = np.array([word_to_id[w] for w in words])
    
    return corpus, word_to_id, id_to_word
    


text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)

여기까지 말뭉치를 구축해두었다면, 이제 말뭉치에서 단어의 의미를 추출해야 한다.

 

 

 

■분산표현

색상을 표현할 때, '코발트블루', '싱크레드'와 같은 고유이름으로 표현할 수 있지만, RGB의 비율을 활용하여 '비색'을 (R,G,B) = (170, 33, 22)로 표현하면 R계열임을 알 수 있고 색끼리 비슷한 색인지도 쉽게 알 수 있다. 이처럼 단어의 의미도 벡터로 표현할 것인데, 이를 분산 표현(distributional representation)이라 한다.

 

단어를 벡터로 표현하는 것의 핵심 아이디어는 '단어의 의미는 주변 단어에 의해 형성된다'는 것이며 이를 

분포 가설(distributional hypothesis)이라 한다. 즉, 맥락(context)이 의미를 만들며 유사한 단어는 유사한 맥락에서 자주 쓰인다는 것을 강조한다. 여기서 주변 몇개의 단어를 고려할 것인지를 윈도우 크기(window size)라고 한다.

 

 

 

■동시발생 행렬

"you say goodbye and i say hello ."에서 각 단어를 맥락을 통해 vector로 표현하면 다음과 같다.

 

동시발생 행렬(co-occurence matrix)

 

 

말뭉치로부터 위와 같은 동시발생 행렬을 자동으로 만드는 함수를 구현해볼 것이다.

def creat_co_matrix(corpus, vocab_size, window_size = 1):
    corpus_size = len(corpus)
    co_matrix = np.zeros((vocab_size, vocab_size), dtype = np.int32)
    
    for idx, word_id in enumerate(corpus):
        for i in range(1, window_size + 1):
            left_idx = idx - i
            right_idx = idx + i
            
            if left_idx >= 0:
                left_word_id = corpus[left_idx]
                co_matrix[word_id, left_word_id] += 1
                
                if right_idx < corpus_size:
                    right_word_id = corpus[right_idx]
                    co_matrix[word_id, right_word_id] += 1
                    
    return co_matrix

 

 

 

■벡터 간 유사도

벡터 사이의 유사도를 측정하는 방법으로 벡터의 내적 또는 유클리드 거리 등이 있지만, 코사인 유사도(cosine similarity)를 자주 이용한다.

 

 

코사인 유사도의 수식

 

import numpy as np

def cos_similarity(x, y, eps=1e-8):
    nx = x / (np.sqrt(np.sum(x**2)) + eps)
    ny = y / (np.sqrt(np.sum(y**2)) + eps)
    return np.dot(nx, ny)

 

 

 

■유사 단어의 랭킹 표시

검색어가 주어지면 검색어와 비슷한 단어를 유사도 순으로 출력하는 함수를 만들어보면 다음과 같다.

def most_similar(query, word_to_id, id_to_word, word_matrix, top=5)
	if query not in word_to_id:
    	print('%s(d을)를 찾을 수 없습니다.', %query)
        return
        
    print('\n[query]' + query)
    query_id = word_to_id[query]
    query_vec = word_matrix[query_id]
    
    vocab_size = len(id_to_word)
    similarity = np.zeros(vocab_size)
    for i in range(vocab_size):
    	similarity[i] = cos_similarity(word_matrix[i], query_vec)
        
    count = 0
    for i in (-1 * similarity).argsort():
    	if id_to_word[i] == query:
	        continue
    	print('%s: %s' %(id_to_word[i], similarity[i]))
    
   		count += 1
        if count >= top:
        	return

참고로 most_similarity의 인자 중 word_matrix는 단어 벡터들을 한 곳에 모은 행렬로 각 행에는 대응하는 단어의 벡터가 저장되어 있다고 가정한 것이다.

 


 

여기까지 동시발생 행렬을 통해 통계 기반 기법의 기본을 다루었다. 그러나 오늘 본 내용은 단어가 발생한 횟수에 초점을 두고 유사도를 측정한 것이다. 하지만 The와 같이 빈도는 높지만 관련성이 높지는 않은 단어들을 생각하면, 단순 '빈도'는 좋은 특징이 아니다. 이를 개선하기 위해 점별 상호정보량(Pointwise Mutual Information, PMI)이라는 척도를 사용하는데, 이와 관련된 내용은 다음 포스팅에서 다룰 것이다.

 

728x90
반응형

댓글