정수 인코딩(Integer Encoding)

ganta·2021년 3월 3일
0

자연어 전처리

목록 보기
6/8
post-thumbnail

이 글은 Wikidocks의 딥 러닝을 이용한 자연어 치리 입문을 공부한 내용을 정리 한 글입니다.

✔️ 컴퓨터는 켁스트보다 숫자를 더 잘 처리할수 있기에 이를 위해 자연어 처리는 텍스트를 숫자로 바꾸는 여러가지 기법들이 존재한다.
이 방법은 첫 단계로 각 단어를 고유한 정수에 맵핑(mapping)시키는 전처리 작업이 필요할 때가 있다.

정수 인코딩(Integer Encoding)


✔️ 단어의 정수를 부여하는 방법 중 하나로 단어를 빈도순으로 정렬한 단어 집합으로 만드록, 빈도수가 높은 순서대로 차례로 낮은 숫자부터 정수를 부여하는 방법이 있다.

from nltk.tokenize import sent_tokenize
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords

# 토큰화 작업
text = "A barber is a person. a barber is good person. a barber is huge person. he Knew A Secret! The Secret He Kept is huge secret. Huge secret. His barber kept his word. a barber kept his word. His barber kept his secret. But keeping and keeping such a huge secret to himself was driving the barber crazy. the barber went up a huge mountain."

text = sent_tokenize(text)
print(text)
vocab = {}
sentences = []
stop_words = set(stopwords.words('english'))

for t in text:
    sentence = word_tokenize(t)
    result = []
    
    for word in sentence:
        word = word.lower()
        if word in stop_words or len(word) <= 2:
            continue
        result.append(word)
        if word not in vocab:
            vocab[word] = 0
        vocab[word] += 1
    sentences.append(result)

1️⃣ 위의 코드는 동일 단어가 대문자로 표기되었다는 이유로 서로 다른 단어가 되지 않도록 모든 단어를 소문자로 바꾸면서 불용어와 길이가 짧은 단어를 제거하는 단계이다.

vocab_sorted = sorted(vocab.items(), key = lambda x:x[1], reverse = True)
[('barber', 8), ('secret', 6), ('huge', 5), ('kept', 4), ('person', 3), ('word', 2), ('keeping', 2), ('good', 1), ('knew', 1), ('driving', 1), ('crazy', 1), ('went', 1), ('mountain', 1)]

2️⃣ 단어를 빈도 순대로 정렬 해 준다.

word_to_index = {}
idx=0
for (word, frequency) in vocab_sorted :
    if frequency == 1:# 빈도수가 낮은 단어는 제거
        break
    idx += 1
    word_to_index[word] = idx
print(word_to_index)
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5, 'word': 6, 'keeping': 7}

3️⃣ 등장 빈도가 낮은 단어는 자연어 처리에서 의미를 가지지 않을 가능성이 높기에 제거해주고 인덱스를 부여해 주도록 한다.

vocab_size = 5
words_frequency = [w for w,c in word_to_index.items() if c >= vocab_size + 1] 
for w in words_frequency:
    del word_to_index[w]
print(word_to_index)

4️⃣ 빈도수가 높은 5개의 단어만 사용하기 위해 5개의 단어만 골라 사전에 넣어준다.

word_to_index['OOV'] = len(word_to_index) + 1

encoded = []
for s in sentences:
    temp = []
    for w in s:
        try:
            temp.append(word_to_index[w])
        except KeyError:
            temp.append(word_to_index['OOV'])
    encoded.append(temp)
print(encoded)
[[1, 5], [1, 6, 5], [1, 3, 5], [6, 2], [2, 4, 3, 2], [3, 2], [1, 4, 6], [1, 4, 6], [1, 4, 2], [6, 6, 3, 2, 6, 1, 6], [1, 6, 3, 6]]

5️⃣ 문장에 없는 단어에 대해서는 OOV라는 단어로 대체하고 문장의 단어마다 인덱스로 치환 해 준다.

⭐️ dictionary를 사용하여 정수 인코딩을 진행 할 수 있지만 Counter, FreqDist또는 케라스 토크나이저를 사용하여 하는 것이 훨씬 간편하고 권장하는 사항이다.

counter 사용

from collections import Counter

words = sum(sentences, [])

vocab = Counter(words) # 빈도수별로 key-value형식으로 반환
 
vocab_size = 5
vocab = vocab.most_common(vocab_size) # 등장 빈도수가 높은 단어 5개만 저장 -> [('barber', 8), ('secret', 6), ('huge', 5), ('kept', 4), ('person', 3)]

word_to_index = {}
i = 0
for (word, frequency) in vocab :
    i = i+1
    word_to_index[word] = i

NLTK의 FreqDist 사용

from nltk import FreqDist
import numpy as np

vocab = FreqDist(np.hstack(sentences))
print(vocab["barber"]) # 8
vocab_size = 5
vocab = vocab.most_common(vocab_size) # 등장 빈도수가 높은 단어 5개만 저장 -> [('barber', 8), ('secret', 6), ('huge', 5), ('kept', 4), ('person', 3)]

word_to_index = {word[0] : index + 1 for index, word in enumerate(vocab)}
print(word_to_index)

케라스(Keras)의 텍스트 전처리

from tensorflow.keras.preprocessing.text import Tokenizer
sentences=[['barber', 'person'], ['barber', 'good', 'person'], ['barber', 'huge', 'person'], ['knew', 'secret'], ['secret', 'kept', 'huge', 'secret'], ['huge', 'secret'], ['barber', 'kept', 'word'], ['barber', 'kept', 'word'], ['barber', 'kept', 'secret'], ['keeping', 'keeping', 'huge', 'secret', 'driving', 'barber', 'crazy'], ['barber', 'went', 'huge', 'mountain']]

tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences) #빈도수를 기준으로 단어 집합을 생성

print(tokenizer.word_index)
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5, 'word': 6, 'keeping': 7, 'good': 8, 'knew': 9, 'driving': 10, 'crazy': 11, 'went': 12, 'mountain': 13}

word_counts()는 각 단어의 카운트를 수행하였을 때 몇개였는지를 보여준다.

print(tokenizer.texts_to_sequences(sentences))
[[1, 5], [1, 8, 5], [1, 3, 5], [9, 2], [2, 4, 3, 2], [3, 2], [1, 4, 6], [1, 4, 6], [1, 4, 2], [7, 7, 3, 2, 10, 1, 11], [1, 12, 3, 13]]

texts_to_sequences()는 입력으로 들어온 코퍼스에 대해 각 단어를 이미 정해진 인덱스로 변환한다.

vocab_size = 5
# 상위 5개 단어 사용
# word_index, word_counts함수 사용 시에는 적용 된 것을 확인 못함
# texts_to_sequences를 사용할 때 적용
# vocab_size + 1를 해 주는 이유는 num_words는 숫자를 0부터 카운트 하기 때문에 
# 5를 넣으면 0 ~4번 단어 보존하기에 index 5에 해당하는 단어는 포함이 안된다.
tokenizer = Tokenizer(num_words = vocab_size + 1)
tokenizer.fit_on_texts(sentences)
print(tokenizer.texts_to_sequences(sentences))
[[1, 5], [1, 5], [1, 3, 5], [2], [2, 4, 3, 2], [3, 2], [1, 4], [1, 4], [1, 4, 2], [3, 2, 1], [1, 3]]

이때, 상위 5개의 단어를 제외한 단어들은 제외가 된 것을 확인 할 수 있다.
word_index, word_counts함수에서도 상위 5개 만큼의 단어만 남기고 싶으면 del을 이용하여 정보를 삭제하면 된다.(key - value형식으로 되어 있음)

vocab_size = 5
tokenizer = Tokenizer(num_words = vocab_size + 2, oov_token = 'OOV')
tokenizer.fit_on_texts(sentences)

단어 집합에 없는 단어의 인덱스 부여를 하기 위해서는 oov_token파라미터를 이용하면 되고 기본적으로 인덱스는 1로 부여를 한다.

print(tokenizer.texts_to_sequences(sentences))
[[2, 6], [2, 1, 6], [2, 4, 6], [1, 3], [3, 5, 4, 3], [4, 3], [2, 5, 1], [2, 5, 1], [2, 5, 3], [1, 1, 4, 3, 1, 2, 1], [2, 1, 4, 1]]
profile
한걸음씩 꾸준히

0개의 댓글