본문 바로가기
ML & DL/tensorflow

one-hot encoding과 Tokenizer를 통한 Word Representation

by 별준 2020. 12. 24.

(tensorflow v2.4.0)

 

텍스트는 시퀀스 형태의 데이터이며, 앞으로 시퀀스 모델에 대해서 실습하기 전에 텍스트를 단어나 문자로 나누어서 벡터화하는 방법에 대해서 이야기해보도록 하겠습니다.

 

텍스트를 나누는 단위를 토큰(Token)이라고 하며, 이러한 작업을 토큰화(tokenization)이라고 합니다.

모든 텍스트 벡터화 작업은 어떤 종류의 토큰화를 적용하고 생성된 토큰에 수치형 벡터를 연결하게 됩니다. 이거 이 벡터는 시퀀스 Tensor로 묶여 모델로 주입됩니다.

 

토큰과 벡터를 연결하는 방법은 여러가지가 있는데, 주로 one-hot encoding과 work embedding이 사용됩니다.

이번 글에서는 one-hot encoding 방법들에 대해서 직접 연습해보도록 하겠습니다.

one-hot encoding

one-hot encoding은 모든 단어에 고유한 정수 index를 부여하고 이 정수 index i를 크기가 N(voca의 크기)이고, i번째 원소만 1이고 나머지는 모두 0인 벡터로 변환합니다.

단어뿐만 아니라 문자 수준으로 적용할 수도 있습니다.

 

다음은 단어 수준의 one-hot encoding 예제입니다.

import numpy as np

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

token_index = {}
for sample in samples:
    for word in sample.split():
        if word not in token_index:
            token_index[word] = len(token_index) + 1
            # index 0은 미사용

max_length = 10

results = np.zeros((len(samples), max_length, max(token_index.values()) + 1))
for i, sample in enumerate(samples):
    for j, word in list(enumerate(sample.split()))[:max_length]:
        index = token_index.get(word)
        results[i,j,index] = 1.
        
print(results)

최대 시퀀스의 길이가 10이고, 각 단어는 각 row열에 해당되고, 그 단어에 해당되는 index가 1로 채워져 있습니다.

 

다음은 문자 수준의 one-hot encoding 입니다.

import string
samples = ['The cat sat on the mat.', 'The dog ate my homework.']
characters = string.printable
token_index = dict(zip(characters, range(1, len(characters) + 1)))

max_length = 50
results = np.zeros((len(samples), max_length, max(token_index.values()) + 1))
for i, sample in enumerate(samples):
    for j, character in enumerate(sample[:max_length]):
        index = token_index.get(character)
        results[i,j,index] = 1.

print(results.shape)
print(results)

최대 50개의 문자를 나타내며(50 rows), 사용되는 문자는 총 101개입니다.

 

다음은 tensorflow에서 제공하는 Tokenizer를 사용해서 one-hot encoding을 수행해보도록 하겠습니다.

import tensorflow as tf

tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=1000)
tokenizer.fit_on_texts(samples)

sequences = tokenizer.texts_to_sequences(samples)

one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')

word_index = tokenizer.word_index

print(f'Found {len(word_index)} unique tokens')
print(sequences)
print(one_hot_results)

Tokenizer는 num_words를 통해서 사용빈도가 가장 높은 N개의 단어만 선택해서 토큰화할 수 있습니다.(default로 특수문자는 제거됩니다)

 

one-hot hashing

one-hot encoding의 변종으로 voca에 있는 고유한 토큰의 수가 너무 커서 모두 다루기 어려울 때 사용합니다. 명시적으로 인덱스를 할당하는 것이 아닌 단어를 해싱해서 고정된 크기의 벡터로 변환합니다. 일반적으로 간단한 해싱함수를 사용하며, 명시적인 단어 인덱스(key-value로 이루어진 단어-인덱스 dictionary)를 사용하지 않기 때문에 메모리를 절약할 수 있습니다. 

단점은 해시 충돌이 발생할 수 있는데, 이는 해싱 공간의 차원이 고유 토큰의 전체 개수보다 훨씬 크면 해시 충돌의 가능성은 감소합니다.

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

dimensionality = 1000
max_length = 10

results = np.zeros((len(samples), max_length, dimensionality))
for i, sample in enumerate(samples):
    for j, word in list(enumerate(sample.split()))[:max_length]:
        # 단어를 해싱하여 0과 1,000 사이의 랜덤한 정수 인덱스로 변환합니다.
        index = abs(hash(word)) % dimensionality
        results[i, j, index] = 1.

print(np.argmax(results[0][0]))

첫번째 sample의 단어 'The'가 880의 index를 갖는 것을 볼 수 있습니다.

 

 

이상 one-hot encoding의 사용방법에 대해서 알아봤고, 다음 글에서 word embedding을 통해서 IMDB dataset의 감성 분류를 진행해보도록 하겠습니다.

 

- 참조

케라스 창시자에게 배우는 딥러닝

댓글