자연어 처리 - 정수 인코딩
정수 인코딩
Neural Network에 사용하기 위한 데이터 전처리
텍스트를 숫자로 바꾸는 기법
dictionary 이용
from konlpy.tag import Okt
text = '''
오늘 네가
보고싶다
널 다시 품에
안아보고 싶다
오늘 네가 난
생각난다
너랑 같이 산책하던
그곳에 서있다
너의 체온이
기억난다
따뜻하게 내 쉬던
숨소리 들려
오늘 네가
온 것 같아
우리 같이 잠들던
벤치에 기대니
시간이 흘러흘러
다시 만날 순 없지
그래도 보고싶다 널
그리운 내 사랑아
오늘 네가 정말
보고싶다
너를 다시 내 품에
안아보고 싶다
오늘 네가 난
생각난다
너를 쓰다듬던 손이
너를 기억한다
니가 보고싶다
'''
okt = Okt()
text = text.replace("\n", " ")
text1 = okt.morphs(text)
text2 = []
for word in text1:
if 4 > len(word) > 1:
text2.append(word)
print(text2)
['오늘', '다시', '안아', '보고', '싶다', '오늘', '같이', '산책', '하던', '서있다', '체온', '쉬던', '숨소리', '들려', '오늘', '같아', '우리', '같이', '잠들던', '벤치', '기대니', '시간', '흘러', '흘러', '다시', '만날', '없지', '그래도', '그리운', '사랑', '오늘', '정말', '다시', '안아', '보고', '싶다', '오늘', '기억']
# 총 단어 갯수, 중복제거 단어 갯수
len(text2), len(set(text2))
(38, 27)
vocab = {}
for word in text2:
if word not in vocab:
vocab[word] = 1
else:
vocab[word] += 1
print(vocab)
{'오늘': 5, '다시': 3, '안아': 2, '보고': 2, '싶다': 2, '같이': 2, '산책': 1, '하던': 1, '서있다': 1, '체온': 1, '쉬던': 1, '숨소리': 1, '들려': 1, '같아': 1, '우리': 1, '잠들던': 1, '벤치': 1, '기대니': 1, '시간': 1, '흘러': 2, '만날': 1, '없지': 1, '그래도': 1, '그리운': 1, '사랑': 1, '정말': 1, '기억': 1}
vocab_sorted = sorted(vocab.items(), key=lambda x:x[1], reverse=True)
print(vocab_sorted)
[('오늘', 5), ('다시', 3), ('안아', 2), ('보고', 2), ('싶다', 2), ('같이', 2), ('흘러', 2), ('산책', 1), ('하던', 1), ('서있다', 1), ('체온', 1), ('쉬던', 1), ('숨소리', 1), ('들려', 1), ('같아', 1), ('우리', 1), ('잠들던', 1), ('벤치', 1), ('기대니', 1), ('시간', 1), ('만날', 1), ('없지', 1), ('그래도', 1), ('그리운', 1), ('사랑', 1), ('정말', 1), ('기억', 1)]
- 말뭉치: Corpus
# 말뭉치: 빈도 목록
# 2번이상 반복된 단어만 뽑아 반복된 횟수 순서대로 나열
word_to_index = {}
i = 0
for (word, freq) in vocab_sorted:
if freq > 1:
i += 1
word_to_index[word] = i
word_to_index
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, '같이': 6, '흘러': 7}
i = 0은 padding에 사용된다.
말뭉치
(‘오늘’, 5),
(‘다시’, 3),
(‘안아’, 2),
(‘보고’, 2),
(‘싶다’, 2),
(‘같이’, 2),
(‘흘러’, 2),
# vocab_size를 벗어나는 단어 제거
vocab_size = 5
word_freq_cut = [word for word, index in word_to_index.items() if index >= vocab_size + 1]
word_freq_cut
['같이', '흘러']
print(word_to_index)
for w in word_freq_cut:
del word_to_index[w]
print(word_to_index)
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, '같이': 6, '흘러': 7}
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5}
단어를 숫자로 치환할 때 word_to_index에 없는 단어를 치환하기 위해 OOV(Out-Of-Vocabulary;단어 집합에 없는 단어)를 지정한다.
word_to_index["OOV"] = len(word_to_index) + 1
print(word_to_index)
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, 'OOV': 6}
# 전처리된 문자열(자연어)을 숫자(정수)로 치환
encoded_sentences = []
for word in text2:
try:
encoded_sentences.append(word_to_index[word])
except:
encoded_sentences.append(word_to_index['OOV'])
print(encoded_sentences)
print(text2)
[1, 2, 3, 4, 5, 1, 6, 6, 6, 6, 6, 6, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 6, 6, 6, 6, 6, 1, 6, 2, 3, 4, 5, 1, 6]
['오늘', '다시', '안아', '보고', '싶다', '오늘', '같이', '산책', '하던', '서있다', '체온', '쉬던', '숨소리', '들려', '오늘', '같아', '우리', '같이', '잠들던', '벤치', '기대니', '시간', '흘러', '흘러', '다시', '만날', '없지', '그래도', '그리운', '사랑', '오늘', '정말', '다시', '안아', '보고', '싶다', '오늘', '기억']
문장단위 처리
text = '''
오늘 네가 보고싶다
널 다시 품에 안아보고 싶다
오늘 네가 난 생각난다
너랑 같이 산책하던 그곳에 서있다
너의 체온이 기억난다
따뜻하게 내 쉬던 숨소리 들려
오늘 네가 온 것 같아
우리 같이 잠들던 벤치에 기대니
시간이 흘러흘러 다시 만날 순 없지
그래도 보고싶다 널 그리운 내 사랑아
오늘 네가 정말 보고싶다
너를 다시 내 품에 안아보고 싶다
오늘 네가 난 생각난다
너를 쓰다듬던 손이 너를 기억한다
니가 보고싶다
'''
import kss
text = text.replace('\n', ' ')
text = kss.split_sentences(text)
okt = Okt()
vocab = {}
text2 = []
for sentence in text:
token = okt.morphs(sentence)
rst = []
for word in token:
if 4 > len(word) > 1:
rst.append(word)
if word not in vocab:
vocab[word] = 1
else:
vocab[word] += 1
text2.append(rst)
print(text2)
print(vocab)
[Kss]: Because there's no supported C++ morpheme analyzer, Kss will take pecab as a backend. :D
For your information, Kss also supports mecab backend.
We recommend you to install mecab or konlpy.tag.Mecab for faster execution of Kss.
Please refer to following web sites for details:
- mecab: https://cleancode-ws.tistory.com/97
- konlpy.tag.Mecab: https://uwgdqo.tistory.com/363
[['오늘'], ['다시', '안아', '보고', '싶다'], ['오늘', '같이', '산책', '하던', '서있다'], ['체온'], ['쉬던', '숨소리', '들려', '오늘', '같아', '우리', '같이', '잠들던', '벤치', '기대니', '시간', '흘러', '흘러', '다시', '만날', '없지', '그래도'], ['그리운', '사랑', '오늘', '정말'], ['다시', '안아', '보고', '싶다'], ['오늘', '기억'], []]
{'오늘': 5, '다시': 3, '안아': 2, '보고': 2, '싶다': 2, '같이': 2, '산책': 1, '하던': 1, '서있다': 1, '체온': 1, '쉬던': 1, '숨소리': 1, '들려': 1, '같아': 1, '우리': 1, '잠들던': 1, '벤치': 1, '기대니': 1, '시간': 1, '흘러': 2, '만날': 1, '없지': 1, '그래도': 1, '그리운': 1, '사랑': 1, '정말': 1, '기억': 1}
vocab_sorted = sorted(vocab.items(), key=lambda x:x[1], reverse=True)
word_to_index = {}
i = 0
for (word, freq) in vocab_sorted:
if freq > 1:
i += 1
word_to_index[word] = i
word_to_index
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, '같이': 6, '흘러': 7}
# vocab_size를 벗어나는 단어 제거
vocab_size = 5
word_freq_cut = [word for word, index in word_to_index.items() if index >= vocab_size + 1]
word_freq_cut
print(word_to_index)
for w in word_freq_cut:
del word_to_index[w]
print(word_to_index)
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, '같이': 6, '흘러': 7}
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5}
word_to_index["OOV"] = len(word_to_index) + 1
print(word_to_index)
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, 'OOV': 6}
# 전처리된 문자열(자연어)을 숫자(정수)로 치환
encoded_sentences = []
for sentence in text2:
encoded_sentence = []
for word in sentence:
try:
encoded_sentence.append(word_to_index[word])
except:
encoded_sentence.append(word_to_index['OOV'])
encoded_sentences.append(encoded_sentence)
print(encoded_sentences)
print(text2)
[[1], [2, 3, 4, 5], [1, 6, 6, 6, 6], [6], [6, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 6, 6, 6], [6, 6, 1, 6], [2, 3, 4, 5], [1, 6], []]
[['오늘'], ['다시', '안아', '보고', '싶다'], ['오늘', '같이', '산책', '하던', '서있다'], ['체온'], ['쉬던', '숨소리', '들려', '오늘', '같아', '우리', '같이', '잠들던', '벤치', '기대니', '시간', '흘러', '흘러', '다시', '만날', '없지', '그래도'], ['그리운', '사랑', '오늘', '정말'], ['다시', '안아', '보고', '싶다'], ['오늘', '기억'], []]
encoded_sentences
[[1],
[2, 3, 4, 5],
[1, 6, 6, 6, 6],
[6],
[6, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 6, 6, 6],
[6, 6, 1, 6],
[2, 3, 4, 5],
[1, 6],
[]]
keras의 텍스트 전처리
from keras.preprocessing.text import Tokenizer
text2
[['오늘'],
['다시', '안아', '보고', '싶다'],
['오늘', '같이', '산책', '하던', '서있다'],
['체온'],
['쉬던',
'숨소리',
'들려',
'오늘',
'같아',
'우리',
'같이',
'잠들던',
'벤치',
'기대니',
'시간',
'흘러',
'흘러',
'다시',
'만날',
'없지',
'그래도'],
['그리운', '사랑', '오늘', '정말'],
['다시', '안아', '보고', '싶다'],
['오늘', '기억'],
[]]
tokenizer = Tokenizer()
tokenizer.fit_on_texts(text2)
print(tokenizer.word_index)
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5, '같이': 6, '흘러': 7, '산책': 8, '하던': 9, '서있다': 10, '체온': 11, '쉬던': 12, '숨소리': 13, '들려': 14, '같아': 15, '우리': 16, '잠들던': 17, '벤치': 18, '기대니': 19, '시간': 20, '만날': 21, '없지': 22, '그래도': 23, '그리운': 24, '사랑': 25, '정말': 26, '기억': 27}
print(tokenizer.word_counts)
OrderedDict([('오늘', 5), ('다시', 3), ('안아', 2), ('보고', 2), ('싶다', 2), ('같이', 2), ('산책', 1), ('하던', 1), ('서있다', 1), ('체온', 1), ('쉬던', 1), ('숨소리', 1), ('들려', 1), ('같아', 1), ('우리', 1), ('잠들던', 1), ('벤치', 1), ('기대니', 1), ('시간', 1), ('흘러', 2), ('만날', 1), ('없지', 1), ('그래도', 1), ('그리운', 1), ('사랑', 1), ('정말', 1), ('기억', 1)])
tokenizer.texts_to_sequences(text2)
[[1],
[2, 3, 4, 5],
[1, 6, 8, 9, 10],
[11],
[12, 13, 14, 1, 15, 16, 6, 17, 18, 19, 20, 7, 7, 2, 21, 22, 23],
[24, 25, 1, 26],
[2, 3, 4, 5],
[1, 27],
[]]
OOV 적용
vocab_size = 5
# num_words: 파라미터 -1 만큼의 단어로 단어집합의 크기를 지정
tok_oov = Tokenizer(num_words = vocab_size + 2, oov_token='OOV') # 0과 OOV
tok_oov.fit_on_texts(text2)
print(tok_oov.word_index)
print(tok_oov.word_counts)
tok_oov.texts_to_sequences(text2)
{'OOV': 1, '오늘': 2, '다시': 3, '안아': 4, '보고': 5, '싶다': 6, '같이': 7, '흘러': 8, '산책': 9, '하던': 10, '서있다': 11, '체온': 12, '쉬던': 13, '숨소리': 14, '들려': 15, '같아': 16, '우리': 17, '잠들던': 18, '벤치': 19, '기대니': 20, '시간': 21, '만날': 22, '없지': 23, '그래도': 24, '그리운': 25, '사랑': 26, '정말': 27, '기억': 28}
OrderedDict([('오늘', 5), ('다시', 3), ('안아', 2), ('보고', 2), ('싶다', 2), ('같이', 2), ('산책', 1), ('하던', 1), ('서있다', 1), ('체온', 1), ('쉬던', 1), ('숨소리', 1), ('들려', 1), ('같아', 1), ('우리', 1), ('잠들던', 1), ('벤치', 1), ('기대니', 1), ('시간', 1), ('흘러', 2), ('만날', 1), ('없지', 1), ('그래도', 1), ('그리운', 1), ('사랑', 1), ('정말', 1), ('기억', 1)])
[[2],
[3, 4, 5, 6],
[2, 1, 1, 1, 1],
[1],
[1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1],
[1, 1, 2, 1],
[3, 4, 5, 6],
[2, 1],
[]]
encoded_text = tok_oov.texts_to_sequences(text2)
OOV 적용 - word_index,count에도 적용
tok = Tokenizer(num_words = vocab_size + 1) # oov_token을 적용하지 않으면 단어집합에 없는 단어는 삭제됨
tok.fit_on_texts(text2)
word_freq_cut = [word for word, index in tok.word_index.items() if index >= vocab_size + 1]
print(word_freq_cut)
['같이', '흘러', '산책', '하던', '서있다', '체온', '쉬던', '숨소리', '들려', '같아', '우리', '잠들던', '벤치', '기대니', '시간', '만날', '없지', '그래도', '그리운', '사랑', '정말', '기억']
for w in word_freq_cut:
del tok.word_index[w]
del tok.word_counts[w]
print(tok.word_index)
print(tok.word_counts)
tok.texts_to_sequences(text2)
{'오늘': 1, '다시': 2, '안아': 3, '보고': 4, '싶다': 5}
OrderedDict([('오늘', 5), ('다시', 3), ('안아', 2), ('보고', 2), ('싶다', 2)])
[[1], [2, 3, 4, 5], [1], [], [1, 2], [1], [2, 3, 4, 5], [1], []]
패딩(Padding)
max(len(item) for item in encoded_text)
17
MAX_LEN = 17
for sentence in encoded_text:
while len(sentence) < MAX_LEN:
sentence.append(0)
encoded_text
[[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1],
[1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
keras의 패딩 처리
from tensorflow.keras.preprocessing.sequence import pad_sequences
tokenizer = Tokenizer(num_words = vocab_size + 2, oov_token='OOV')
tokenizer.fit_on_texts(text2)
enc = tokenizer.texts_to_sequences(text2)
enc
[[2],
[3, 4, 5, 6],
[2, 1, 1, 1, 1],
[1],
[1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1],
[1, 1, 2, 1],
[3, 4, 5, 6],
[2, 1],
[]]
padded = pad_sequences(enc)
padded
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 6],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 6],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
Sequence정보는 최근(순서상 마지막)정보가 중요하므로 0을 앞으로 채워 데이터는 뒤쪽으로 밀어둬야한다!
# 뒤로 0을 채우는 경우도 가능하다
padded2 = pad_sequences(enc, padding='post')
padded2
array([[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1],
[1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
댓글남기기