본문 바로가기
AI / DL/엘리스 AI 데이터 분석 트랙

[AI 데이터 분석] 딥러닝 시작하기 03. 다양한 신경망 (2)

by bri9htstar 2022. 10. 9.

자연어 처리 과정

  1. 자연어 전 처리(Preprocessing)
  2. 단여 표현(Word Embedding)
  3. 모델 적용하기 (Modeling)

원 상태 그대로의 자연어는 전 처리 과정이 필요하다. 대표적 전 처리 과정은 다음과 같다.

오류 교정(Noise canceling)

잘못된 문장을 표준화에 맞게끔 변경하는 일이다. 자연어 문장의 스펠링 체크 및 띄어쓰기 오류 교정 과정이다. 기본적인 오류 교정은 거쳐야 깔끔해지고 성능도 좋아진다.

토큰화(Tokenizing)

문장을 나누는데 그 나눠진 단위를 토큰(Token)이라 한다. 토큰은 어절, 단어 등으로 목적에 따라 사용자가 다르게 정의한다. 문장 데이터를 딥러닝으로 입력해줘야 하는데 이미지 데이터에서도 이미지 그 자체를 입력으로 사용할 수 없었다. 하물며 문장을 잘 넣어도 문장이 숫자 데이터가 아니라 당장 입력으로 사용할 수 없다. 문장을 수치로 변환을 해줘야 한다. 큰 문장 자체를 숫자로 변환하는 게 쉽지 않다. 그래서 나온 방법이 문장 자체를 쪼개고 쪼갠 단위에서 수치를 변환해주면 단어 마다 의미를 부여할 수 있지 않을까라는 생각에서 나온게 토큰화이다.

불용어 제거(StopWord removal)

불필요한 단어를 의미하는 불용어(StopWord)를 제거한다. 감탄사나 의성어(아, 휴, 아이구), 혹은 접속사(그러나, 그런데, 하지만)가 이에 해당한다. 뉴스 카테고리를 나눌 때는 정치나 경제 관련 용어가 있는지가 중요하지 접속사가 들어가는지는 크게 중요하지 않다. 따라서 중요하지 않다고 생각하는 단어를 불용어로 설정해서 없애주는 게 불용어 제거라고 볼 수 있다. 불용어는 상황에 따라서 적절하게 설정하여 제거하면 된다.

 

Bag of Words

데이터가 전 처리 과정을 거쳐서 나왔다고 하자. 사용하는 단어의 모든 토큰을 하나씩 뽑아내서 인덱스를 부여한 것이 Bag of Words이다. 인덱스를 부여하는 과정은 큰 의미가 없어서 나온 순서대로 맵핑했다고 생각하면 된다.

Bag of Words 리스트에 있는 토큰을 각각 맵핑된 숫자 인덱스로 변환해주는 것이 수치형 변환이다. 모든 문장의 길이를 맞추기 위해 기준보다 짧은 문장에는 패딩(Padding)을 수행한다. 일반적으로 자연어 데이터에서 가장 긴 길이를 갖고 있는 문장에 다른 문장들이 패딩을 통해 맞춰준다. 한 문장만 유독 긴 경우에는 해당 문장을 제외하는 방식으로 조절한다.

토큰 시퀀스. 마지막의 경우 문장 길이가 짧아 의미 없는 4를 넣어 맞춰준 것을 볼 수 있다.

Word Embedding

해당되는 토큰의 인덱스가 어떤 의미를 갖고 있는게 아니라, 단순 순서다. CNN에서 그림의 특징을 뽑아냈듯, 자연어 데이터에서 단순하게 Bag of Words의 인덱스로 정의된 토큰들에게 의미를 부여하는 방식이다. 단어의 특징을 나타내기 위해서 한다고 보면 된다.

인덱스에 따라서 어떤 값으로 변환할 지 Embedding Table 안에서 벡터로 주어지게 된다. 토큰의 특징을 설명하는 게 목표인만큼, 이게 벡터로 가능해진다. 벡터 형태면 벡터 간의 유사도도 구할 수 있고 연산도 가능하다. 유사도가 높으면 두 가지 토큰의 의미가 비슷한 거고, 연산의 경우 단어와 단어를 더했을 때 새로운 의미가 나타난다로 해석하는 등 여러 의미로 사용될 수 있다.

Word Embedding

 

기존 다층 퍼셉트론 신경망으로 딥러닝 모델을 사용하기에 한계가 있다. 여러 벡터로 이루어진 데이터를 MLP에 넣으려면 1차원 벡터로 넣으면 묶어놓은 값을 풀어야 하는데 그러면 Embedding 효과도 사라지고 관계 또한 무너지게 된다. 토큰 간의 순서와 관계를 적용할 수 있는 모델을 고민하다 RNN 모델이 나오게 된다.

 

RNN(Recurrent Neural Network)

순환 신경망이라 하고 퍼셉트론과 비슷하게 X Embedding 데이터를 받아 Y를 출력한다. 출력 값이 두 갈래로 나뉘어 다음 RNN 계산 때 전에 사용한 값이 들어간다. 신경망에게 '기억'하는 기능을 부여해서, 전에 사용한 토큰의 기억을 받아와 다음 토큰 계산 때 사용되는 것이다.

순환 신경망의 입출력 구조. 전에 사용했던 정보가 전달된다.

 

순환 신경망 기반 자연어 분류 예시. 이 모델에서는 마지막 결과에 대해서만 loss를 계산한다.


자연어 전 처리한 데이터가 임베딩을 통해서 특징을 뽑게 된다. 이 특징을 바탕으로 RNN 모델을 사용하는데 기억하는 모델이다. 토큰의 기억들이 다 같이 사용되기 때문에 문장 간의 순서 관계가 포함된다. 이후 활성함수로 분류한다. Multi class의 경우 softmax, Binary class의 경우 sigmoid, 회귀의 경우 활성함수 없이 뽑아내도 된다.

import json
import numpy as np
import tensorflow as tf
import data_process
from keras.datasets import imdb
from keras.preprocessing import sequence

import logging, os
logging.disable(logging.WARNING)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"

# 동일한 실행 결과 확인을 위한 코드입니다.
np.random.seed(123)
tf.random.set_seed(123)

# 학습용 및 평가용 데이터를 불러오고 샘플 문장을 출력합니다.
X_train, y_train, X_test, y_test = data_process.imdb_data_load()

max_review_length = 300

# 패딩을 수행합니다.
X_train = sequence.pad_sequences(X_train, maxlen=max_review_length, padding='post')
X_test = sequence.pad_sequences(X_test, maxlen=max_review_length, padding='post')

embedding_vector_length = 32

# 모델을 구현합니다.
model = tf.keras.models.Sequential([
    tf.keras.layers.Embedding(1000, embedding_vector_length, input_length = max_review_length),
    tf.keras.layers.SimpleRNN(5),
    tf.keras.layers.Dense(1, activation='sigmoid')
    ])

# 모델을 확인합니다.
print(model.summary())

# 학습 방법을 설정합니다.
model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])

# 학습을 수행합니다.
model_history = model.fit(X_train, y_train, epochs = 5, verbose = 2)

# 평가용 데이터를 활용하여 모델을 평가합니다.
#  loss와 accuracy를 계산하고 loss, test_acc에 저장합니다.
loss, test_acc = model.evaluate(X_test, y_test, verbose = 0)

# 평가용 데이터에 대한 예측 결과를 predictions에 저장합니다.
predictions = model.predict(X_test)

# 모델 평가 및 예측 결과를 출력합니다.
print('\nTest Loss : {:.4f} | Test Accuracy : {}'.format(loss, test_acc))
print('예측한 Test Data 클래스 : ',1 if predictions[0]>=0.5 else 0)

위 코드 실행 결과.

Embedding Layer

tf.keras.layers.Embedding(input_dim, output_dim, input_length)

들어온 문장을 단어 임베딩(embedding)하는 레이어.

  • input_dim : 들어올 단어의 개수
  • output_dim : 결과로 나올 임베딩 벡터의 크기
  • input_length : 들어오는 단어 벡터의 크기

RNN Layer

tf.keras.layers.SimpleRNN(units)

단순 RNN 레이어. units는 레어어의 노드 수이다.

RNN 평가 방법

model.evaluate(X, Y)

evaluate() 메서드는 학습된 모델을 바탕으로 입력한 feature 데이터 X와 label Y의 loss값과 metrics 값을 출력한다.

RNN 예측 방법

model.predict(X)

X 데이터의 예측 label 값을 출력한다.

 


LSTM(Long short-term memory)

RNN 모델은 언어 데이터와 같은 시계열 데이터를 잘 다룰 수 있는 모델이다. 그러나, RNN의 가장 큰 단점 중 하나는 장기의존성 문제(long-term dependency problem, short-term memory problem, vanishing gradient problem)다. 어려워 보일 수 있지만, 쉽게 얘기하자면 단순 RNN은 단기 기억은 잘 할 수 있지만, 장기 기억에 어려움을 겪는다. 그래서 입력 데이터의 길이가 길어지면, 초기에 입력되었던 데이터가 출력에 거의 반영되지 않는 문제가 발생한다.

예를 들어, 위의 그림처럼 어떤 문장이 입력되고 다음으로 입력될 단어를 문법에 맞게 예측하여야 하는 task를 생각해보자. RNN 모델은 I have a pen과 같이 짧은 문장에서는 pens가 아니라 pen이 정답인 것을 쉽게 맞힐 수 있다. 그러나, I have a wonderful and beautiful and amazing and powerful and … and amazing pen과 같이 긴 문장이 올 때에는 pen이 정답인지 pens가 정답인지 맞히기 어렵다.

이런 문제를 완화하고자 고안된 모델이 LSTM(Long short-term memory) 모델이다. 간단한 RNN 모델은 모든 입력 sequence를 동등하게 취급하는 반면, LSTM은 입력값이 중요한지 중요하지 않은지를 판단한다. 그래서 중요하지 않은 정보는 과감하게 버리고, 중요한 정보를 집중해서 기억함으로써 길이가 긴 데이터를 더 잘 다룰 수 있다.

LSTM 모델은 단순 RNN 모델과 마찬가지로 keras 라이브러리를 활용하여 쉽게 구현할 수 있다.

model = tf.keras.models.Sequential([
    ... , 
    tf.keras.layers.LSTM( N ),
    ...
    ])

import json
import numpy as np
import tensorflow as tf
import data_process
from keras.datasets import imdb
from keras.preprocessing import sequence

import logging, os
logging.disable(logging.WARNING)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"

# 동일한 실행 결과 확인을 위한 코드입니다.
np.random.seed(123)
tf.random.set_seed(123)

'''
1. 데이터를 불러옵니다.
'''

# 학습용 및 평가용 데이터를 불러오고 샘플 문장을 출력합니다.
X_train, y_train, X_test, y_test = data_process.imdb_data_load()

max_review_length = 300

# 패딩을 수행합니다.
X_train = sequence.pad_sequences(X_train, maxlen=max_review_length, padding='post')
X_test = sequence.pad_sequences(X_test, maxlen=max_review_length, padding='post')


embedding_vector_length = 128

'''
2. SimpleRNN 모델을 학습해봅니다.
'''

# SimpleRNN 모델을 구현합니다.
simpleRNN_model = tf.keras.models.Sequential([
    tf.keras.layers.Embedding(1000, embedding_vector_length, input_length = max_review_length),
    tf.keras.layers.SimpleRNN(5),
    tf.keras.layers.Dense(1, activation='sigmoid')
    ])

# 학습 방법을 설정합니다.
simpleRNN_model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])

# 학습을 수행합니다.
simpleRNN_model.fit(X_train, y_train, epochs = 5, verbose = 2)

# 평가용 데이터를 활용하여 모델을 평가합니다
loss, test_acc = simpleRNN_model.evaluate(X_test, y_test, verbose = 0)

# 모델 평가 및 예측 결과를 출력합니다.
print('\nSimpleRNN Test Loss : {:.4f} | Test Accuracy : {}'.format(loss, test_acc))

'''
3. LSTM 모델을 학습해봅니다.
'''

# LSTM 모델을 구현합니다.
LSTM_model = tf.keras.models.Sequential([
    tf.keras.layers.Embedding(1000, embedding_vector_length, input_length = max_review_length),
    tf.keras.layers.LSTM(5),
    tf.keras.layers.Dense(1, activation='sigmoid')
    ])

# 학습 방법을 설정합니다.
LSTM_model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])

# 학습을 수행합니다.
LSTM_model.fit(X_train, y_train, epochs = 5, verbose = 2)

# 평가용 데이터를 활용하여 모델을 평가합니다
loss, test_acc = LSTM_model.evaluate(X_test, y_test, verbose = 0)

# 모델 평가 및 예측 결과를 출력합니다.
print('\nLSTM Test Loss : {:.4f} | Test Accuracy : {}'.format(loss, test_acc))

위 코드 실행 결과

 

 

댓글