본문 바로가기
Natural Language Processing

[Goorm] 딥러닝을 이용한 자연어 처리 6 (LSTM과 CNN으로 IMDB 사용하기)

by 자몽먹은토끼 2024. 7. 7.
728x90
반응형

Embedding 함수

: 각 인덱스는 고유한 벡터 값으로 변환되며,

학습 과정에서 이 벡터 값은 업데이트될 수 있지만, 동일한 인덱스는 항상 동일한 벡터로 매핑된다.

 

시퀀스(Sequence)의 개념

시퀀스는 순서가 있는 데이터의 나열을 의미한다. 시퀀스는 다음과 같은 다양한 형태로 나타날 수 있다:

  • 단어 시퀀스: 문장을 구성하는 단어들의 순서.
    예) "I love machine learning"이라는 문장의 단어 시퀀스: ["I", "love", "machine", "learning"].
  • 문자 시퀀스: 단어를 구성하는 문자들의 순서.
    예) "hello"라는 단어는 문자 시퀀스: ["h", "e", "l", "l", "o"].
  • 숫자 시퀀스: 숫자들이 순서대로 나열된 것.
    예) [1, 2, 3, 4, 5]는 숫자 시퀀스입니다.
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import os

from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, Dropout, Activation
from tensorflow.keras.layers import LSTM, Conv1D, MaxPooling1D
from tensorflow.keras.datasets import imdb
from tensorflow.keras.layers import Bidirectional
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint

# seed 값 설정
seed= 0
np.random.seed(seed)
tf.random.set_seed(seed)

# 학습셋, 테스트셋 지정
# (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=5000)
# num_words(빈도수 상위)와 embedding 인풋 수?는 맞춰주는게 좋다 (비효율성 유발)
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=10000)

# 데이터 전처리
x_train = sequence.pad_sequences(x_train, maxlen= 200)
x_test = sequence.pad_sequences(x_test, maxlen= 200)
  • num_words : data내에서 가장 많이 등장하는 상위 n개의 토큰만을 가져와 사용
  • pad_sequences : 패딩작업, maxlen의 개수에 맞도록 패딩시켜주거나 자름

 

 

# 모델의 설정
model= Sequential()
model.add(Embedding(10000, 200))
model.add(Dropout(0.5))
model.add(Conv1D(64,5, padding= 'valid', activation= 'relu', strides= 1))
model.add(MaxPooling1D(pool_size= 4))
model.add(Bidirectional(LSTM(64, return_sequences= True)))
model.add(Dropout(0.5))
model.add(Bidirectional(LSTM(32)))
model.add(Dense(1, activation= 'sigmoid'))

# 모델의 요약을 보기 위해 한번 모델을 호출하여 초기화
model.build(input_shape=(None, 100)) # 의미없는 호출을 하고 
model.summary()                      # summary를 해야 출력이 됨

# 모델의 컴파일
model.compile(loss= 'binary_crossentropy',metrics= ['accuracy'],
              optimizer= 'adam')
  • Embedding() : 입력 단어 인덱스를 고정된 크기의 밀집 벡터로 변환
    • input_dim= 10000 : n개의 고유 토큰(단어)을 입력데이터 셋으로 사용
    • output_dim= 200 : n차원 벡터로 변환
    • input_length : 시퀀스의 길이 ( padding작업으로 맞춰놓은 max_len과 같은 길이로 들어감 )
  • Dropout() : 과적합 방지를 위해, 학습 중에 랜덤하게 50%의 뉴런을 비활성화하여 일반화를 도움

 

  • Conv1D () : 특정 크기의 필터를 이용해 연속된 단어들의 패턴을 인식하고, 이를 통해 문장 내의 중요한 구조적 정보를 포착 = 특징을 뽑아낼 수 있음
    • filters : 64개의 필터를 사용, 출력 채널의 수
    • kernel_size : 필터의 크기 ( 이 경우에는 5개의 연속된 단어를 보고 특징을 추출하는 필터를 사용하겠다는 의미)
    • padding : 외곽에 패딩을 사용할 것인지 ('valid' - (default) 패딩 사용 x,   'same' - 패딩 사용 o = 출력크기와 입력크기가 동일하도록)
    • activation : 활성화함수
    • strides : 필터를 적용하는 간격, 몇개의 간격으로 필터를 적용할 것인지(한칸씩 필터를 이동)>> 출력 형태로는 (배치사이즈, 컨볼루션 결과, 필터개수) = (batch_size, 196, 64)가 된다.
  • Maxpooling1D() : 1차원 convolution 연산 특징맵에서 가장 큰 값을 선택하여 다음 층으로 전달.
    이는 입력 데이터의 크기를 줄이고, 동시에 중요한 특징을 강조하여 모델의 계산 효율성을 높이고 과적합을 방지함.
    • pool_size : 최대 풀링을 수행할 윈도우 크기 ( 4개의 연속된 값 중에서 최대값을 선택하여 다음 층으로 전달)
      >> 출력 형태로는 (배치사이즈, maxpooling결과, 필터 개수) = (batch_size, 196/4=49, 64)

> convolution layer와 maxpooling layer는 짝꿍

 

  • Bidirectional( LSTM ( ) ) :  양방향 LSTM 레이어.
    시퀀스의 맥락을 이해하고 중요한 특징을 학습. 정방향과 역방향 LSTM이 함께 사용됨으로써 입력 시퀀스의 앞뒤 관계를 모두 이해
    • 출력 차원 : 64차원으로 기억 정보를 출력.
    • return_sequences : 타임스텝마다 결과를 출력할 것인지
      (True : 타임스텝마다 나오는 모든 시퀀스 데이터를 출력, Fale : 마지막 타임스텝 출력층에서 나오는 시퀀스 데이터를 출력)

 

만약 더 자세한 모델 설명이 필요하다면 이전 포스팅 참고 ❗

 

[Goorm] 딥리닝을 이용한 자연어 처리 5 (LSTM 이용)

LSTM을 이용한 로이터 뉴스 카테고리 분류하기 : 입력된 문장의 의미를 파악하는 것은 단어간의 관계를 파악한다기 보다 모든 단어를 종합하여 하나의 카테고리로 분류하는 작업이라고 할 수 있

data-yun.tistory.com

 

 

 

# 콜백설정
early_stopping= EarlyStopping(monitor= 'val_loss', patience= 4, verbose= 1)
reduce_lr= ReduceLROnPlateau(monitor= 'val_loss', factor= 0.2, patience= 2, verbose= 1)
model_checkpoint= ModelCheckpoint('best_model.keras', monitor= 'val_loss',
                                  verbose= 1, save_best_only= True)

# 모델의 실행
history= model.fit(x_train, y_train, batch_size= 64, epochs= 20,
                   validation_data= (x_test, y_test),
                   callbacks= [early_stopping, reduce_lr, model_checkpoint])

# 테스트 정확도 출력
print("\n Test Accuracy: %.4f"%(model.evaluate(x_test, y_test)[1]))

> Test Accuracy: 0.8822

 

  • ReduceLROnPlateau( ) : 모델 학습 중 개선이 없을 경우, Learning Rate를 조절해 모델의 개선을 유도하는 콜백함수
    • monitor : 해당 값을 기준으로 개선이 없을 시 학습률 조정
    • factor : 콜백 함수 실행시, 학습률 감소량 (기존 학습률 * factor 를 다음 학습률로 사용)
    • patience : monitor에 해당하는 값이 patience 만큼 개선이 없을 경우, 학습률 조정 

 

 

 

# 그래프 그리기
history_dict= history.history
loss= history_dict['loss']
val_loss= history_dict['val_loss']
acc= history_dict['accuracy']
val_acc= history_dict['val_accuracy']

epochs= range(1, len(acc)+1)

# 손실 그래프
plt.figure(figsize= (12,6))
plt.plot(epochs, loss, 'bo', label= 'Training loss')
plt.plot(epochs, val_loss, 'b', label= 'Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

# 정확도 그래프
plt.figure(figsize= (12,6))
plt.plot(epochs, acc, 'ro', label= 'Training acc')
plt.plot(epochs, val_acc, 'r', label= 'Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

 

 

 

합성곱 (Convolution)

 

  • 단순한 곱이 아닌, 이전/현재/이후 의 값들을 모두 고려한 곱 (= 합성 곱)
  • 하나의 출력이 현재의 입력에 의해서만 결정되는 것이 아니라 이전의 입력의 영향을 받기 때문에 그 전반적인 출력을 나타내기 위한 연산

1차원 배열에서의 Convolution

 

MaxPooling1D의 경우

위 그림과 같이 전체 시퀀스 에서 pool_size의 개수씩 자른 것 중 가장 큰 값을 고른다.

(만약, 5개의 시퀀스길이에서 pool_size= 2라면 남는 하나의 가중치는 버린다.)

 

 

728x90
반응형