[자연어처리] tensorflow Tokenizer
0. 들어가면서
from tensorflow.keras.preprocessing.text import Tokenizer
tensorflow를 활용하여 Tokenizer로 전처리하는 방법을 알아보자. Tokenizer로 처음부터 모델 넣기 전까지의 과정을 진행해 보자. 자연어 처리에 대해 좀 더 알아보려면 아래의 블로그를 참고하자.
1. Tokenizer란
Tokenizer은 사전에 있는 단어의 순서에 맞게 단어를 turning하면서 말뭉치를 벡터화시킨다.
tf.keras.preprocessing.text.Tokenizer(
num_words=None,
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
lower=True,
split=' ',
char_level=False,
oov_token=None,
document_count=0,
**kwargs
)
- num_words : 단어 빈도가 많은 순서로 num_words개의 단어를 보존한다.
- filters : 걸러낼 문자 리스트를 적어준다. 디폴트는 '!"#$%&()*+,-./:;<=>?@[\]^_`{|}~\t\n' 이다.
- lower : 입력받은 문자열을 소문자로 변환할지를 True, False로 적어준다.
- split : 문자열을 적어 줘야 하고, 단어를 분리하는 기준을 적어준다.
- char_level : True인 경우 모든 문자가 토큰으로 처리된다.
- oov_token : 값이 지정된 경우, text_to_sequence 호출 과정에서 word_index에 추가되어 out-of-vocabulary words를 대체한다.
2. Tokenizer 사용하기
아래 코드의 흐름도는 Tokenizer 인스턴스를 만든다. 그리고 fit_on_texts와 word_index를 사용하여 key value로 이루어진 딕셔너리를 만든다. 그 후에 texts_to_sequences를 이용하여 text 문장을 숫자로 이루어진 리스트로 만든다. 마지막으로 pad_sequences를 이용하여 리스트의 길이를 통일시킨다.
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
sentences = [
'I love my dog',
'I love my cat',
'This neighborhood is quiet and nice.',
'After I settle in, I’ll invite you for a coffee.'
]
tokenizer = Tokenizer(num_words = 100)
tokenizer.fit_on_texts(sentences)
word_dic = tokenizer.word_index
print(word_dic)
sequences = tokenizer.texts_to_sequences(sentences)
print(sequences)
padded = pad_sequences(sequences)
print(padded)
---------------------------------------------------------------------
{'i': 1, 'love': 2, 'my': 3, 'dog': 4, 'cat': 5, 'this': 6, 'neighborhood': 7, 'is': 8, 'quiet': 9, 'and': 10, 'nice': 11, 'after': 12, 'settle': 13, 'in': 14, 'i’ll': 15, 'invite': 16, 'you': 17, 'for': 18, 'a': 19, 'coffee': 20}
[[1, 2, 3, 4], [1, 2, 3, 5], [6, 7, 8, 9, 10, 11], [12, 1, 13, 14, 15, 16, 17, 18, 19, 20]]
[[ 0 0 0 0 0 0 1 2 3 4]
[ 0 0 0 0 0 0 1 2 3 5]
[ 0 0 0 0 6 7 8 9 10 11]
[12 1 13 14 15 16 17 18 19 20]]
위의 코드를 하나씩 살펴보자.
tokenizer = Tokenizer(num_words = 100)
> num_words를 단어 빈도수가 높은 순으로 100개만 사용한다는 의미이다. 나머지는 고려하지 않는다.
tokenizer.fit_on_texts(sentences)
> fit_on_texts() 메서드는 문자 데이터를 입력받아서 리스트의 형태로 변환한다.
word_dic = tokenizer.word_index
> tokenizer의 word_index 속성은 단어와 숫자의 키-값 쌍을 포함하는 딕셔너리를 반환한다. 이때, 반환 시 자동으로 소문자로 변환되어 들어간다. 그리고 느낌표나 마침표 같은 구두점은 자동으로 제거된다.
sequences = tokenizer.texts_to_sequences(sentences)
> texts_to_sequences() 메서드는 텍스트 안의 단어들을 숫자의 시퀀스의 형태로 변환한다.
padded = pad_sequences(sequences)
> pad_sequences() 함수에 이 시퀀스를 입력하면 숫자 0을 이용해서 같은 길이의 시퀀스로 변환한다. 가장 긴 시퀀스의 길이가 10이기 때문에 모두 같은 길이의 시퀀스를 포함하는 NumPy 어레이로 변환한 것을 볼 수 있다.
3. oov_token
위에서 우리가 배운 내용을 정리해보면 문장의 단어를 딕셔너리로 바꿔 토큰화 시킨다. 그리고 토큰화 되어 딕셔너리에 있는 key를 기준으로, text를 숫자로 이루어진 NumPy array로 바꾼다. 그렇다면 토큰화 되어있지 않은 단어들이 있으면 어떻게 될까? 바로 실습을 해보자. 아래 코드를 보면, test_sentences 부분을 보면 된다.
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
sentences = [
'I love my dog',
'I love my cat',
'This neighborhood is quiet and nice.',
'After I settle in, I’ll invite you for a coffee.'
]
tokenizer = Tokenizer(num_words = 100)
tokenizer.fit_on_texts(sentences)
word_dic = tokenizer.word_index
# print(word_dic)
sequences = tokenizer.texts_to_sequences(sentences)
# print(sequences)
padded = pad_sequences(sequences)
# print(padded)
test_sentences = [
'They look so yummy.',
'Perfect for tea time',
]
test_sequences = tokenizer.texts_to_sequences(test_sentences)
print(test_sequences)
-------------------------------------------------------------
[[], [18]]
위의 코드에서 알 수 있듯이 딕셔너리 안에 토큰화 되어 있지 않은 단어는 시퀀스에 포함되지 않는 것을 알 수 있다. 그래서 무시하지 말고 oov_token 인자를 사용하여 특수한 값으로 처리가 가능하다. oov_token을 사용하면 아래와 같다.
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
sentences = [
'I love my dog',
'I love my cat',
'This neighborhood is quiet and nice.',
'After I settle in, I’ll invite you for a coffee.'
]
tokenizer = Tokenizer(num_words = 100, oov_token="<OOV>")
tokenizer.fit_on_texts(sentences)
word_dic = tokenizer.word_index
# print(word_dic)
sequences = tokenizer.texts_to_sequences(sentences)
# print(sequences)
padded = pad_sequences(sequences)
# print(padded)
test_sentences = [
'They look so yummy.',
'Perfect for tea time',
]
test_sequences = tokenizer.texts_to_sequences(test_sentences)
print(test_sequences)
print(word_index)
-----------------------------------------------------------------
[[1, 1, 1, 1], [1, 19, 1, 1]]
{'<OOV>': 1, 'my': 2, 'love': 3, 'dog': 4, 'i': 5, 'you': 6, 'cat': 7, 'do': 8, 'think': 9, 'is': 10, 'amazing': 11}
word_index를 참고해보면 <OOV>로 등록되어 있지 않는 단어가 들어오면 1로 대신 만들어 주는 것을 알 수 있다.
4. pad_sequences()
pad_sequences 함수는 시퀀스를 패딩을 하여 리스트의 길이를 동일하게 맞춰준다고 위에서 알아보았다. pad_sequences에 대해 좀 더 자세히 알아보자.
keras.preprocessing.sequence.pad_sequences(sequences, maxlen=None, dtype='int32', padding='pre', truncating='pre', value=0.0)
- sequences: 리스트의 리스트로, 각 성분이 시퀀스이다
- maxlen: 정수, 모든 시퀀스의 최대 길이를 설정하여 제한한다. 10을 넣으면 10보다 큰 것들은 자른다.
- dtype: 출력 시퀀스의 자료형. 가변적 길이의 문자열로 시퀀스를 패딩 하려면object를 사용하시면 됩니다.
- padding: 문자열이 들어간다, 'pre'가 디폴트 값으로 앞쪽에 0이 추가되고, 'post'는 뒤쪽으로 0이 추가되어 각 시퀀스를 패딩 한다.
- truncating: 문자열, 'pre'는 길이가 초과됐을 때 앞쪽을 자르고 'post'는 maxlen보다 큰 시퀀스의 끝의 값들을 제거한다
- value: 부동소수점 혹은 문자열, 패딩 할 값.
리턴 값으로는 Numpy 배열을 리턴한다. 위의 인수들 중에서 필요한 것들만 뽑아 쓰면 된다