이 글은 Wikidocks의 딥 러닝을 이용한 자연어 치리 입문을 공부한 내용을 정리 한 글입니다.
✔️ 컴퓨터는 켁스트보다 숫자를 더 잘 처리할수 있기에 이를 위해 자연어 처리는 텍스트를 숫자로 바꾸는 여러가지 기법들이 존재한다.
이 방법은 첫 단계로 각 단어를 고유한 정수에 맵핑(mapping)시키는 전처리 작업이 필요할 때가 있다.
✔️ 단어의 정수를 부여하는 방법 중 하나로 단어를 빈도순으로 정렬한 단어 집합으로 만드록, 빈도수가 높은 순서대로 차례로 낮은 숫자부터 정수를 부여하는 방법이 있다.
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또는 케라스 토크나이저를 사용하여 하는 것이 훨씬 간편하고 권장하는 사항이다.
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
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)
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]]