본문 바로가기
Study/NLP

01. 텍스트 전처리 - 원-핫 인코딩

by GodKim 2020. 3. 14.

www.wikidocs.net/book/2155 의 [딥 러닝을 이용한 자연어 처리 입문]을 공부하고 정리한 글입니다.

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net


단어 집합(vocabulary)

 

 단어 집합은 앞으로 자연어 처리에 있어서 자주 등장하는 개념이다. 단어 사전이라고도 칭하며 이 책에선 단어 집합이라는 정의를 사용하였다. 여기서의 단어 집합은 서로 다른 단어의 집합이다. 서로 다른 단어는 기본적으로 어근, 의미가 같아도 형태가 다른 단어이면 서로 다른 단어로 간주한다. 예컨대, book과 books는 의미는 같고 단순히 단수, 복수의 차이지만 단어 집합 내에서는 다른 단어로 간주하는 것이다.

 이제 배울 원-핫 인코딩을 하기 위해서는 가장 먼저 해야할 일이 단어 집합을 형성하는 것이다. 텍스트의 모든 단어를 중복을 허용하지 않은 상태로 모아놓으면 이를 단어 집합이라고 한다. 그 후 이 단어 집합에 이전에 배운 정수 인코딩을 진행하여 고유한 정수들을 부여해줍니다.

(텍스트 전처리 - 정수 인코딩, https://godcode.tistory.com/7

 

원-핫 인코딩 (One-hot encoding)

 

 원-핫 인코딩은 단어 집합의 크기를 벡터의 차원으로 변환하고, 표현하고 싶은 단어의 인덱스의 1의 값을 부여하고, 다른 인덱스에는 0을 부여하는 단어의 벡터 표현 방식이다.

 와우! 문과 출신 신이 바로 이해하기엔 너무 어려운 말이다. 

예컨데, 단어 집합에 정수 인코딩을 해준다. 그러면 각 단어들마다 고유한 정수가 좌아아악 생긴다. 그리고 원-핫 인코딩을 하고 싶은 문장을 받아서 각 단어를 해당 정수 값으로 변환해준다. 그러면 정수가 나열된다. 거기서 표현을 원하는 단어의 위치, 즉, 단어의 고유한 정수값이 인덱스(위치)가 되어 그 해당 단어만 1로 바뀌고 나머지 단어는 0으로 바뀌는 것이다. 못 알아들었으면 예제 코드들을 보면 그냥 바로 이해할 수 있다.

 

from konlpy.tag import Okt

okt = Okt()
token = okt.morphs("나는 자연어 처리를 배운다")

print(token) 
['나', '는', '자연어', '처리', '를', '배운다']

 

 Konlpy의 Okt 형태소 분석기를 사용하여 문장을 토큰화 하였다. Konlpy 설치 및 오류에 대해서는 다음 포스트를 참조하자!

https://godcode.tistory.com/8

불러오는 중입니다...

 

word2index = {}
for voca in token:
    if voca not in word2index.keys():
        word2index[voca]=len(word2index)
        
print(word2index)
{'나': 0, '는': 1, '자연어': 2, '처리': 3, '를': 4, '배운다': 5}

 

 그 후 각 토큰에 대해 정수 인덱스를 부여하여 단어 집합을 형성했다. 현재는 예시여서 빈도수를 고려하지 않았지만 이전의 정수 인덱싱 챕터를 보면 알 수 있듯이 빈도 순서대로 단어를 정렬하여 고유한 인덱스를 부여하는 작업인 경우가 많다.

 

def one_hot_encoding(word, word2index):
    one_hot_vector = [0] * (len(word2index))
    index = word2index[word]
    one_hot_vector[index] = 1
    return one_hot_vector

 

토큰을 입력하면 해당 토큰에 대한 원-핫 벡터를 만들어주는 함수이다.

 

one_hot_encoding('자연어', word2index)
[0, 0, 1, 0, 0, 0]

 

 '자연어' 토큰은 단어 집합에서 부여된 고유한 인덱스 값이 2이다. 그러므로 '자연어'에 대한 원-핫 벡터에서는 인덱스 2의 값이 1로 변하고 나머지 토큰에 대해서는 모두 0으로 처리 된다.

 

  • Keras(케라스)를 이용해서 원-핫 인코딩 하기

 케라스에는 to_categorical()이라는 원-핫 인코딩을 해주는 유용한 도구가 있다. 사용법은 다음과 같다.

 

우선적으로 단어를 토큰화 해준 후 단어 집합을 형성한다.

 

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.utils import to_categorical

text = "나랑 점심 먹으러 갈래 점심 메뉴는 햄버거 갈래 갈래 햄버거 최고야"

t = Tokenizer()
t.fit_on_texts([text])

print(t.word_index)
##각 단어에 대한 인코딩 결과 출력
{'갈래': 1, '점심': 2, '햄버거': 3, '나랑': 4, '먹으러': 5, '메뉴는': 6, '최고야': 7}

 

 위의 단어 집합을 이용하여 단어 집합 내에 있는 단어들로만 구성된 문장이 있다면 아래의 코드와 같이 정수 시퀀스로 변환이 가능하다.

 

sub_text = "점심 먹으러 갈래 메뉴는 햄버거 최고야"

encoded = t.texts_to_sequences([sub_text])[0]  ##인덱스 정수들로 변환하기

print(encoded)
[2, 5, 1, 6, 3, 7]

 

 위의 결과는 원-핫 인코딩이 아닌 이전에 배운 정수 인코딩이다. 이제 to_cateroical()을 이용하여 원-핫 인코딩을 진행해보자. 

one_hot = to_categorical(encoded)

print(one_hot)
[[0. 0. 1. 0. 0. 0. 0. 0.]  #인덱스 2의 원-핫 벡터
 [0. 0. 0. 0. 0. 1. 0. 0.]  #인덱스 5의 원-핫 벡터  
 [0. 1. 0. 0. 0. 0. 0. 0.]  #인덱스 1의 원-핫 벡터
 [0. 0. 0. 0. 0. 0. 1. 0.]  #인덱스 6의 원-핫 벡터
 [0. 0. 0. 1. 0. 0. 0. 0.]  #인덱스 3의 원-핫 벡터
 [0. 0. 0. 0. 0. 0. 0. 1.]] #인덱스 7의 원-핫 벡터

 

 위의 결과 값은 sub_text가 [2, 5, 1, 6, 3, 7]로 정수 인코딩이 된 후, 각 단어 마다의 표현을 

원-핫 벡터로 나타낸 것이다. 출력된 값을 통해 거꾸로 추론해보자. 여기서 첫 번째 행 [0, 0, 1, 0, 0, 0, 0, 0] 을 보면 1의 인덱스(위치)가 2인 것을 알 수 있다. 2는 위의 단어 집합에서 '점심' 토큰에 해당된다. 첫 행([0, 0, 1, 0, 0, 0, 0, 0])은 sub_text의 첫 단어, 즉, 첫 토큰인 '점심'을 나타내기에 첫 행에 2의 원-핫 벡터를 보여준 것이다.

 

  •  원-핫 인코딩의 한계 

 이러한 방식의 표현법은 단어의 개수가 늘어나면 그에 해당하는 원-핫 벡터를 저장하는 공간 또한 늘어난다는 단점이있다. 요컨데, 단어 집합의 크기가 곧 벡터의 차원 수가 되는 것이다. 예를 들어, 1000개의 단어 집합이 있다면, 각 단어들은 각각의 원-핫 벡터를 가지기에 1000개의 차원을 가진 벡터가 되는 것이다. 이러한 방식은 저장 공간의 측면에서 비효율적이라고 할 수 있다. 

 더욱이, 원-핫 벡터는 단어간의 유사도를 나타낼 수 없다는 단점이 있다. 예를 들어, '늑대', '강아지', '냉장고', '가스레인지' 라는 4개의 단어에  각각 [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]이라는 원-핫 벡터가 부여됐다고 하자. 우리는 의미적으로 '늑대'와 '강아지'가 동물이라는 카테고리에서 유사하고, '냉장고'와 '가스레인지'라는 단어는 가전제품이라는 카테고리로 묶이는 것을 알 수 있다. 하지만 이 원-핫 인코딩 방식으로는 이 유사성을 나타낼 방법이없다. 이는 검색 시스템 같은 시퀀스에서는 아주 중요한 문제이다. 검색창에 '최신 가전제품'이라고 검색했을때 'Iot 냉장고', '초경량 노트북' 등과 같은 유사한 단어들이 포함된 결과도 보여주어야한다. 하지만 위의 방식으로는 '가전제품'과 '노트북', '냉장고'의 유사도를 알 수 없기에 연관 검색어로 보여줄 수 가 없다. 

 이러한 문제를 해결하기 위해 단어의 잠재 의미를 반영하여 벡터화하는 방법으로 크게 두 가지가 있다. 하나는 카운트 기반의 벡터화인 LSA, HAL 등이 있으며, 예측 기반인 Word2Vec, NNLM, RNNLM, FastText 등이 있다. GloVe는 위의 두 가지 방법 모두 사용한다. 

 

 

반응형

댓글