rueki

텍스트 전처리 - 정수 인코딩(Integer Encoding) 본문

DL/NLP

텍스트 전처리 - 정수 인코딩(Integer Encoding)

륵기 2019. 7. 1. 17:27
728x90
반응형

본문은 https://wikidocs.net/31766 를 참고해서 작성한 글입니다.

 

컴퓨터는 텍스트보다는 숫자를 처리하기가 더 쉽다. 그래서 텍스트를 숫자로 바꾸는 것이 필요하며, 중요하다.

바꾸기 전에, 먼저 고유 숫자로 매핑시키는 작업이 필요할 때가 있다.

1000개의 단어가 있으면, 각 단어에 0부터 999까지 인덱스 번호를 부여하는 것이다.

인덱스 부여 기준은 보통 단어에 대한 빈도수로 정렬한 뒤에 부여한다.

 

from nltk.tokenize import sent_tokenize
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)

예제의 문장을 문장 토큰화를 수행

['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.']

문장 단위로 토큰화가 되었으니, 단어 토큰화를 수행, 정제도 같이 한다.

from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from collections import Counter
voca = Counter()
stop_words = set(stopwords.words('english'))
sentence = []
for i in text:
    sentence = word_tokenize(i)#토큰화 수행
    result =[]
    
    for word in sentence:
        word = word.lower()#단어 소문자화해서 단어 개수 줄이기
        if word not in stop_words:#불용어 제거
            if len(word)>2: #길이 2 이하인 단어제거
                result.append(word)
                voca[word] = voca[word]+1
    sentence.append(result)
    
print(sentence)
['the', 'barber', 'went', 'up', 'a', 'huge', 'mountain', '.', ['barber', 'went', 'huge', 'mountain']]
print(voca)
Counter({'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})

텍스트 전처리를 하고 각 단어에 대해 카운트하여 빈도 수를 기록하였다.

voca_sorted = sorted(voca.items(), key=lambda x:x[1], reverse = True)
print(voca_sorted)
[('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)]

빈도수 순으로 정렬을 하였다. 이제 빈도 수 높은 단어부터 해서 인덱스를 부여해보도록 하겠다.

word_to_index= {}
i = 0
for (word, frequency) in voca_sorted:
    if frequency > 1:
        i = i + 1
        word_to_index[word] = i
print(word_to_index)
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5, 'word': 6, 'keeping': 7}

케라스로 위의 예제를 전처리 해보자

from keras.preprocessing.text import Tokenizer
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."]
t = Tokenizer()
t.fit_on_texts(text) # 텍스트의 리스트를 가지고 단어 빈도수에 기반한 사전 만든다.
print(t.word_index)
{'a': 1, 'barber': 2, 'secret': 3, 'huge': 4, 'his': 5, 'is': 6, 
'kept': 7, 'person': 8, 'the': 9, 'he': 10, 'word': 11, 'keeping': 12, 
'good': 13, 'knew': 14, 'but': 15, 'and': 16, 'such': 17, 'to': 18,
'himself': 19, 'was': 20, 'driving': 21, 'crazy': 22,
'went': 23, 'up': 24, 'mountain': 25}

단어의 빈도 수에 따라 인덱스 숫자를 부여하였다.

print(t.word_counts)
OrderedDict([('a', 8), ('barber', 8), ('is', 4), ('person', 3), 
('good', 1), ('huge', 5), ('he', 2), ('knew', 1), ('secret', 6), 
('the', 3), ('kept', 4), ('his', 5), ('word', 2), ('but', 1), ('keeping', 2),
('and', 1), ('such', 1), ('to', 1), ('himself', 1), ('was', 1), ('driving', 1), 
('crazy', 1), ('went', 1), ('up', 1), ('mountain', 1)])

단어의 빈도 수를 확인할 수가 있다.

print(t.texts_to_sequences(text))
[[1, 2, 6, 1, 8, 1, 2, 6, 13, 8, 1, 2, 6, 4, 8, 10, 14, 1, 3, 9, 3, 10, 7, 6, 4, 3, 4, 3, 5, 2, 7, 5, 11, 1, 2, 7, 5, 11, 5, 2, 7, 5, 3, 15, 12, 16, 12, 17, 1, 4, 3, 18, 19, 20, 21, 9, 2, 22, 9, 2, 23, 24, 1, 4, 25]]

25개의 모든단어에 대해 인덱스한 값이 나오는데, 불용어처리를 안 해주었기 때문이다.

keras로 처리가 가능하지만, 처음 접하는 사람에게는 counter()를 통해서 하는 것이 이해가 더 될 것이다.

 

###

 t = Tokenizer(num_words=숫자)과 같은 방법으로 빈도수가 높은 상위 몇 개의 단어만 남기고 진행시키는 방법이 케라스 토크나이저에도 공식적으로 존재는 하지만, 이를 사용하면 t.texts_to_sequences(text)에서는 적용이 되면서 t_word_index와 t.word_counts에는 여전히 모든 단어가 인식되는 등의 사용자에게 혼란을 주는 문제가 존재합니다.

 

word_frequency = [w for w, c in t.word_counts.items() if c<2] #빈도수가 2 미만단어를 w로 저장
for w in word_frequency:
    del t.word_index[w] # 인덱스 정보 삭제
    del t.word_counts[w] # 카운트 정보 삭제

print(t.texts_to_sequences(text))
print(t.word_index)
[[1, 2, 6, 1, 8, 1, 2, 6, 8, 1, 2, 6, 4, 8, 10, 1, 3, 9, 3, 10, 7, 6, 4, 3, 4, 3, 5, 2, 7, 5, 11, 1, 2, 7, 5, 11, 5, 2, 7, 5, 3, 12, 12, 1, 4, 3, 9, 2, 9, 2, 1, 4]]
{'a': 1, 'barber': 2, 'secret': 3, 'huge': 4, 'his': 5, 'is': 6, 'kept': 7, 'person': 8, 'the': 9, 'he': 10, 'word': 11, 'keeping': 12}

enumerate로 정수 인코딩을 진행해보자

text=[['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']]

문장 토큰화가 위와 같이 진행되있다고 가정하고 단어 집합을 만들기 위해 문장 경계인 [,]을 제거하고 하나의 리스트로 만들자.

vocab = sum(text, [])
print(vocab)
['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']

 빈도 순대로 정렬하고 중복 결과를 제거 해보자

vocab_sorted=(sorted(set(vocab)))
print(vocab_sorted)
['barber', 'crazy', 'driving', 'good', 'huge', 'keeping', 'kept', 'knew', 'mountain', 'person', 'secret', 'went', 'word']

enumerate를 실행해보자

word_to_index = {word:index+1 for index, word in enumerate(vocab_sorted)} #인덱스 1부터 부여
print(word_to_index)
{'barber': 1, 'crazy': 2, 'driving': 3, 'good': 4, 'huge': 5, 'keeping': 6, 'kept': 7, 'knew': 8, 'mountain': 9, 'person': 10, 'secret': 11, 'went': 12, 'word': 13}

이로서 여러가지 방법을 통해 정수 인코딩을 하고, 인덱스 부여까지 해보았다.

728x90
반응형
Comments