-
정수 인코딩(Integer Encoding)Natural Language Processing/딥 러닝을 이용한 자연어 처리 입문 2021. 5. 18. 11:42반응형
https://wikidocs.net/31766 참고하여 공부한 내용 정리
06) 정수 인코딩(Integer Encoding)
텍스트를 숫자로 바꾸는 여러 기법들 중에서 각 단어를 고유한 정수에 매핑(mapping)시키는 전처리 작업
인덱스를 부여하는 방법은 랜덤으로 부여, 단어 빈도수 기준으로 정렬한 후 부여하는 방법이 있음
1. 정수 인코딩(Integer Encoding)
단어에 정수를 부여하는 방법 중 하나로 단어를 빈도수 순으로 정렬한 단어 집합을 만들고 빈도수가 높은 순서대로 인덱스를 낮은 숫자부터 정수를 부여하는 방법
1) dictionary 사용하기
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.']
문장 토큰화 수행 결과. 다음은 단어 토큰화 수행
vocab={} sentences=[] stop_words=set(stopwords.words('english')) 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) if word not in vocab: vocab[word]=0 vocab[word]+=1 sentences.append(result) print(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']]
단어 토큰화 수행 결과
print(vocab)
{'barber': 8, 'person': 3, 'good': 1, 'huge': 5, 'knew': 1, 'secret': 6, 'kept': 4, 'word': 2, 'keeping': 2, 'driving': 1, 'crazy': 1, 'went': 1, 'mountain': 1}
vocab에는 중복 제거한 단어와 각 단어에 대한 빈도수가 기록되어 있음. 단어를 key로 빈도수를 value로 저장되어 있음
vocab_sorted=sorted(vocab.items(), key = lambda x:x[1], reverse=True) print(vocab_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 vocab_sorted: if frequency>1: # 빈도수가 적은 단어는 제외함 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}
빈도수가 높은 단어일수록 낮은 정수 인덱스를 부여함. 1 인덱스를 가진 단어가 가장 빈도수가 높은 단어임
vocab_size=5 # 빈도수가 5를 초과하는 경우(ex. 6, 7)를 저장 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] # 빈도수가 5를 초과하는 경우 삭제함 print(word_to_index)
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5}
텍스트 데이터에 있는 모든 단어를 사용하는 것이 아닌 빈도수가 높은 상위 n개의 단어만 사용함. 이 경우 빈도수가 높은 상위 5개의 단어를 사용.
word_to_index
를 사용하여 단어 토큰화가 진행된 sentences에 대하여 각 단어를 정수로 변환예를 들어, sentences의 첫 번째 문장 ['barber', 'person']의 경우 [1, 5]로 인코딩함. 두 번째 문장 ['barber', 'good', 'person']의 경우
word_to_index
(단어집합)에 존재하지 않는 단어인 'good' 이 있음이처럼 단어 집합에 없는 단어들은 Out-Of_Vocabulary의 약자로 'OOV'라 함.
word_to_index
에 'OOV'라는 단어를 추가하고 단어 집합에 없는 단어들은 'OOV'의 인덱스로 인코딩함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']) # 단어 사전에 단어가 존재하지 않아 '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]]
정수 인코딩 완료!
2) Counter 사용하기
from collections import Counter print(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']]
sentences에는 단어 토큰화된 결과가 저장되어 있음
단어 집합을 만들기 위해 단어들을 하나의 리스트로 만듦
words=sum(sentences, []) print(words)
['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']
Counter() 사용하면 중복 제거하고 단어의 빈도수를 계산할 수 있음
vocab=Counter(words) print(vocab)
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})
빈도수가 높은 상위 5개 단어만 저장
vocab_size=5 vocab=vocab.most_common(vocab_size) print(vocab)
[('barber', 8), ('secret', 6), ('huge', 5), ('kept', 4), ('person', 3)]
빈도수가 높을수록 낮은 정수 인덱스를 부여함
word_to_index={} i=0 for (word, frequency) in vocab: i+=1 word_to_index[word]=i print(word_to_index)
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5}
3) NLTK의 FreqDist 사용하기
FreqDist()
는 빈도수 계산 도구임from nltk import FreqDist import numpy as np
np.hstack
으로 여러 리스트를 하나의 array로 만든 후FreqDist()
를 이용해 빈도수를 반환함vocab=FreqDist(np.hstack(sentences))
vocab_size=5 vocab=vocab.most_common(vocab_size) print(vocab)
[('barber', 8), ('secret', 6), ('huge', 5), ('kept', 4), ('person', 3)]
빈도수 상위 5개의 단어만 단어 집합으로 저장
word_to_index={word[0]:index+1 for index, word in enumerate(vocab)} print(word_to_index)
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5}
enumerate
을 사용하여 인덱스 부여4) enumerate 이해하기
test=['a', 'b', 'c', 'd', 'e'] for index, value in enumerate(test): print('value: {}, index: {}'.format(value, index))
value: a, index: 0 value: b, index: 1 value: c, index: 2 value: d, index: 3 value: e, index: 4
입력 순서대로 인덱스를 순차적으로 부여함
2. 케라스의 텍스트 전처리
from tensorflow.keras.preprocessing.text import Tokenizer
fit_on_texts
는 입력 텍스트로부터 빈도수가 높은 순으로 낮은 정수 인덱스를 부여함tokenizer=Tokenizer() tokenizer.fit_on_texts(sentences)
word_index
사용해 각 단어에 인덱스가 어떻게 부여되었는지 확인할 수 있음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.word_counts)
OrderedDict([('barber', 8), ('person', 3), ('good', 1), ('huge', 5), ('knew', 1), ('secret', 6), ('kept', 4), ('word', 2), ('keeping', 2), ('driving', 1), ('crazy', 1), ('went', 1), ('mountain', 1)])
texts_to_sequences()
는 입력에 대해 각 단어를 정해진 인덱스로 변환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]]
빈도수가 높은 상위 5개의 단어만 사용
vocab_size=5 tokenizer=Tokenizer(num_words=vocab_size+1) # num_words는 숫자를 0부터 카운트하기 때문에 1을 더해줌 tokenizer.fit_on_texts(sentences)
다시
word_index
를 확인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}
상위 5개의 단어만 사용하겠다고 선언했는데 여전히 13개의 단어가 모두 출력됨.
word_counts
확인print(tokenizer.word_counts)
OrderedDict([('barber', 8), ('person', 3), ('good', 1), ('huge', 5), ('knew', 1), ('secret', 6), ('kept', 4), ('word', 2), ('keeping', 2), ('driving', 1), ('crazy', 1), ('went', 1), ('mountain', 1)])
word_counts
도 마찬가지로 13개의 단어가 모두 출력됨. 사실 실제 적용은texts_to_sequences
를 사용할 때 적용이 됨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개만 사용한다고 지정했으므로 1번 단어부터 5번 단어까지만 보존되고 나머지 단어들은 제거된 것을 확인할 수 있음
만약
word_index
와word_counts
에서도 지정된num_words
만큼 단어를 남기고 싶으면 아래의 코드를 이용tokenizer=Tokenizer() tokenizer.fit_on_texts(sentences)
vocab_size=5 words_frequency=[w for w, c in tokenizer.word_index.items() if c>=vocab_size+1] for w in words_frequency: del tokenizer.word_index[w] del tokenizer.word_counts[w] print(tokenizer.word_index) print(tokenizer.word_counts) print(tokenizer.texts_to_sequences(sentences))
{'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5} OrderedDict([('barber', 8), ('person', 3), ('huge', 5), ('secret', 6), ('kept', 4)]) [[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]]
모두 1~5번 까지만 저장된 것을 확인할 수 있음
케라스 토크나이저는 기본적으로 단어 집합에 없는 단어인 OOV에 대해서는 인코딩 과정에서 아예 단어를 제거한다는 특징이 있음. 단어 집합에 없는 단어들을 OOV로 간주하여 보존하고 싶다면
Tokenizer
의 인자oov_token
을 사용함vocab_size=5 # 빈도수 상위 5개만 사용, 숫자 0과 oov 고려해서 단어 집합의 크기는 +2 해줌 tokenizer=Tokenizer(num_words=vocab_size+2, oov_token='OOV') tokenizer.fit_on_texts(sentences)
oov_token
사용하면 케라스 토크나이저는 기본적으로 'OOV'의 인덱스를 1로 함print('단어 OOV의 인덱스: {}'.format(tokenizer.word_index['OOV']))
단어 OOV의 인덱스: 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]]
빈도수 상위 5개의 단어는 2~6까지의 인덱스를 가지고 그 외 단어 집합에 없는 단어는 'OOV'의 인덱스인 1로 인코딩됨
반응형'Natural Language Processing > 딥 러닝을 이용한 자연어 처리 입문' 카테고리의 다른 글
원-핫 인코딩 (One-hot Encoding) (0) 2021.05.18 패딩(Padding) (0) 2021.05.18 정규 표현식(Regular Expression) (0) 2021.05.17 불용어(Stopwords) (0) 2021.05.17 어간 추출(Stemming) & 표제어 추출(Lemmatization) (0) 2021.05.17