자연어 처리(NIP: Natural Language Processing)

김민지·2024년 4월 13일

데이터패턴인식

목록 보기
11/13
  • 자연어: 우리가 평소에 말하는 음성, 텍스트

텍스트 전처리 과정

1. 텍스트 토큰화

텍스트를 단어별, 문장별, 형태소별로 나누기 ➡️ 토큰: 작게 나누어진 하나의 단위
text_to_word_sequence(바꿀문장)

  • 토큰화 결과의 활용: 각 단어가 몇 번 중복해서 쓰였는지 단어의 등장 빈도를 파악해 텍스트에서 중요한 역할을 하는 단어 파악 ➡️ 단어 단위로 쪼개는 것이 가장 흔한 방법임
  • Bag of words: 단어 단위로 문장을 쪼개서 ('단어', 단어가 등장한 횟수)로 출력
token = Tokenizer() 
token.fit_on_texts(바꿀문장)
  • input 문장:
  • 출력 결과: 단어들은 순서대로 등장 횟수와 함께 출력됨
  • 추가 정보들 정리
    token.document_count: token에 총 몇개의 문장이 들어있는지 세기

    token.word_docs: 단어들이 각 몇개의 문장에 나오는지 세기

    token.word_index: 각 단어에 부여된 인덱스 값 출력

2. 단어의 원-핫 인코딩

단어가 문장의 다른 요소와 어떤 관계를 가지고 있는지 알아봐야 함

  • 아이디어
  1. 단어 수만큼 0으로 채워진 벡터 공간으로 바꾸기
  2. 각 단어가 문장에서 차지하는 위치를 1로 바꿔서 벡터화할 수 있음
  • 실제 코드
  1. 토큰화 함수 Tokenizer()를 불러와 단어 단위로 토큰화하고 각 단어의 인덱스 값 출력해서 확인
token = Tokenizer()
token.fit_on_texts([바꾸려는문장])
print(token.word_index)

  1. Tokenizer()texts_to_sequences() 함수를 사용해서 앞서 만들어진 토큰의 인덱스로만 채워진 새로운 배열 만들기
x = token.texts_to_sequences([text])
print(x)

결과: [[1, 2, 3, 4, 5, 6]]

  1. 이제 to_categorical(바꿀대상, 그 대상의 크기)함수를 이용해 1~6의 정수 6개로 인덱스되어있는 것을 0과 1로만 이루어진 배열로 바꾸기
from tensorflow.keras.utils import to_categorical

word_size = len(token.word_index) + 1 
# 배열 맨 앞에 0이 추가되므로 인덱스 수를 단어 개수보다 하나 더 많게 잡아줌
x = to_categorical(x, num_classes=word_size)
print(x)

3. 단어 임베딩(Word Embedding)

  • 원-핫 인코딩의 단점: 벡터의 길이가 너무 길어짐
    예: 1만개의 단어 토큰으로 이루어진 말뭉치를 다룰 때, 이 데이터를 원-핫 인코딩으로 벡터화하면 9999개의 0과 하나의 1로 이루어진 단어 벡터를 1만개나 만들어야 함 ➡️ 공간 낭비

  • 단어 임베딩: 주어진 배열을 정해진 길이로 압축시킴
    결과적으로 밀집된 정보를 가지고 공간 낭비를 줄이는 결과치를 얻을 수 있음

  • How? 단어 간의 유사도를 계산하기
    예: happy라는 단어는 bad보다 good에 가깝고, cat이라는 단어는 good보다는 dog에 가깝다는 것을 고려해 각 배열을 새로운 수치로 바꾸어 주는 것

  • 단어간의 유사도 계산하는 방법: 적절한 크기로 배열을 바꿔주기 위해 최적의 유사도를 계산하는데 오차 역전파 사용
    Embedding(입력될 총 단어 수, 출력될 벡터 크기, input_length(매번 입력할 단어 개수))함수를 사용
    Embedding(16, 4, input_length=2) : 입력될 총 단어 개수는 16개이지만 매번 두개씩만 넣을 것임. 임베딩 후 출력되는 벡터의 크기는 4

from tensorflow.keras.layers import Embedding
model = Sequential()
model.add(Embedding(16, 4))

4. 실습: 텍스트를 읽고 긍정 / 부정 예측하기

영화를 보고 남긴 리뷰를 딥러닝 모델로 학습해서 각 리뷰가 긍정적인지 부정적인지를 예측하는 것

  1. 짧은 리뷰 10개를 불러와 각각 긍정이면 1이라는 클래스를, 부정이면 0이라는 클래스를 지정
docs = ["너무 재밌네요", "최고에요", ... "별로에요", "생각보다 지루하네요"]
class = array([1, 1, 1, 1, 1, 0, 0, 0, 0, 0])
  1. 토큰화 과정 진행: 각 단어를 하나의 토큰으로 변경 - fit_on_texts 사용
token = Tokenizer()
token.fit_on_texts(docs)
print(token.word_index) 

  1. 토큰에 지정된 인덱스로만 채워진 새로운 배열 생성 - texts_to_sequences()
    각 단어가 1~20까지의 숫자로 토큰화되었다는 것을 알 수 있음
    문제점: 딥러닝 모델에 입력하려면 학습 데이터의 길이가 동일해야 하는데, 각 리뷰 데이터의 토큰 수가 각각 다름 (문장도 있고 단어도 있어서..)
    ➡️ 패딩을 통해 길이를 똑같이 맞춰주자!
x = token.texts_to_sequences(docs)
print(x)

  1. 패딩(padding): pad_sequences(패딩할배열, 원하는길이) 는 원하는 길이보다 짧은 부분은 앞부분에 숫자 0을 넣어서 채워주고(예: [1, 2] ➡️ [0, 0, 1, 2]), 긴 데이터는 잘라서 같은 길이로 맞춤
padded_x = pad_sequences(x, 4) 
print(padded_x)

  1. 단어간의 유사도를 계산하기 위해 Embedding(입력될 총 단어 수, 출력될 벡터 크기, input_length(매번 입력할 단어 개수))함수 사용
    이 때, 만들어진 임베딩 결과는 눈에 보이지 않음, 모델 내부에서 계산해 딥러닝의 층으로 활용
word_size = len(token.word_index) + 1
Embedding(word_size, 8 input_length = 4)
# input_length=4: 앞서 계산한 padding의 원하는 길이를 4로 설정했으므로
# 8은 임의로 정한 값이므로 원한다면 바꿀 수 있음
  1. 최종 모델 만들기
model = Sequential()
Embedding(word_size, 8 input_length = 4)
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy')
model.fit(padded_x, classes, epochs=20)
print("\n Accuracy: %.4f" % (model.evaluate(padded_x, classes)[1]))

0개의 댓글