Group Study (2024-2025)/Machine Learning 입문

[ML 입문] 8주차 스터디

hnid00 2024. 11. 27. 18:06

8. 텍스트를 위한 인공 신경망

8.1. 순차 데이터와 순환 신경망

8.1.1. 개요 - 용어 정리

  • 순차 데이터(Sequential data)
    • 순서에 의미가 있는 데이터
    • 예시
      • 텍스트 데이터 ⇒ 어순에 따라서 텍스트의 의미가 달라짐
      • 시계열 데이터(Time series data) ⇒ 온도 기록 데이터에서 시간 순서를 섞으면 다음 온도를 예상하기 어려움

순환신경망 이미지

  • 순환 신경망(Recurrent Neural Network, RNN)
    • 순환되는 고리가 있는 신경망
    • 앞의 샘플의 출력을 새로운 샘플을 계산할 때 재사용
    • 즉, 계산된 데이터는 이전 샘플에 대한 기억을 가지고 있다고 볼 수 있음
    • 입력에 가중치를 곱하고 활성화 함수를 통과시켜 다음 층으로 보내는 것은 다른 신경망과 동일하나, 층의 출력을 재사용하는 특징이 있는 신경망
    • 이러한 특징으로 순차 데이터에 잘 맞는 신경망의 한 종류임

타임 스텝 이미지

  • 타임 스텝(Time step)
    • 샘플을 처리하는 단계를 세는 단위
    • 위 이미지에서는 A, B, C가 타임 스텝이라고 볼 수 있음
    • 타임 스텝이 오래될수록 초기에 순환됐던 정보는 희미해짐

순환신경망 상세 구조

  • 셀(Cell)
    • 순환신경망에서 층을 부르는 용어
    • 한 셀에는 여러 개의 뉴런이 포함되어 있지만, 뉴런을 모두 표시하지 않고 하나의 셀로 표현함
    • 은닉 상태(hidden state)  ⇒  셀의 출력을 부르는 용어 

  • 하이퍼볼릭 탄젠트(hyperbolic tangent, tanh)
    • 순환 신경망에서 자주 사용되는 활성화 함수
    • -1~1 사이의 범위를 가짐

 

8.1.2. 순환 신경망 가중치

타임스텝으로 펼친 셀

  • 기본적으로 순환 신경망도 다른 신경망처럼 입력과 가중치를 곱하면 됨
  • 하지만 추가로 이전 타임스텝의 은닉 상태에 가중치를 곱해야 함
    • 가중치 w_x를 샘플마다 동일하게 사용
    • 가중치 w_h를 샘플 타임 스텝마다 동일하게 공유
  • 가중치 w_h는 타임스텝에 따라 변화되는 뉴런의 출력을 학습
  • 타입스텝 1에서는 이전 은닉 상태(h_0)를 0으로 초기화하고 진행

순환 신경망의 가중치

  • 순환 신경망의 각각의 뉴런들은 완전 연결되어 있음
  • 순환층의 뉴런마다의 출력 h (은닉 상태) 또한 다른 모든 순환층의 뉴런과도 연결되어 있음

 

8.1.3. 순환 신경망 입출력

  • 순환층은 일반적으로 샘플(=시퀀스(sequence))마다 2개의 차원을 가짐
  • 시퀀스 안에는 여러 개의 아이템이 들어 있고, 시퀀스 길이 == 타임스텝 길이
  • 위 이미지의 (1, 4, 3) 의미
    • 1 ⇒ 1 개의 샘플 (I am a boy 문장)
    • 4 ⇒ 4개의 단어 (I, am, a, boy)
    • 3 ⇒ 각 단어를 3 표현하는 숫자의 크기 (본 예시에는 3개로 가정한 것)
  • (1,4,3) 같은 입력이 순환층을 통과하면 두/세 번째 차원이 사라지고 순환층의 뉴런 개수만큼 출력됨
  • 순환층은 기본적으로 마지막 타임스텝의 은닉 상태만 출력으로 내보내기에 2차원 배열이 기본 출력 형식임

  • 순환 신경망의 입력은 3차원 배열을 기대하기 때문에, 다중 사용시에는 마지막이 아닌 앞선 셀들이 3차원 배열로 출력되게 해야 연결할 수 있음

출력층에 3개의 뉴런, 소프트맥스 활성화 함수를 사용한 순환 신경망 출력 예시

  • 순환 신경망 또한 마지막에는 밀집층을 두어 클래스 분류 진행
  • 순환 신경망은 합성곱 신경망과 달리 마지막 셀의 출력이 1차원   셀의 출력을 그대로 밀집층에 사용할 수 있음

8.2. 순환 신경망으로 IMDB 리뷰 분류

8.2.1. 개요 - 용어 정리 및 목표 설정

  • 용어 정의
    • NLP(Natural Language Processing)  ⇒ 컴퓨터를 사용해 인간의 언어를 처리하는 분야 
    • 말뭉치(corpus)  자연어 처리 분야의 훈련 데이터
    • 토큰 텍스트 분석을 위해 분리된 단어. 보통 텍스트에서 공백으로 구분되는 문자열을 의미
    • 어휘사전 토큰의 집합, 훈련세트에서 고유한 단어를 뽑아 만든 목록 
  • 목표
    • IMDB ⇒ 인터넷 무비 데이터베이스
    • IMDB 리뷰 데이터를 이용해 영화를 좋게 판단하는지(양성), 나쁘게 판단하는지(음성) 감성 분석 진행
#케라스로 IMDB 데이터 불러오기
#본래는 텍스트를 기계가 처리할 수 있게 숫자로 바꿔주는 작업이 필요한데, 케라스에서 이를 제공해주고 있음
from tensorflow.keras.datasets import imdb

#num_words => 모든 단어를 사용하면 분석이 힘드니 몇 개의 단어를 분석할 지 제한
#제한 기준은 전체 데이터셋에서 가장 자주 등장하는 단어 순임
(train_input, train_target), (test_input, test_target) = imdb.load_data(
    num_words=200)

print(train_input.shape, test_input.shape)


#1 => 문장의 시작부분을 나타내는 예약어
#2 => 위처럼 몇 개의 단어를 사용할건지 num_words로 제한했을 경우,
#테스트 문장에 현재 분석하지 않는 단어가 나올 수 있음. 이런 경우를 예약어인 2로 표현함
print(train_input[0]) #출력값 =>[1, 14, 22, 16, 43, 2, 2, (중략) 2, 19, 178, 32]

#1은 양성, 0은 음성 (본 예제에서는 양성 == 긍정, 음성 == 부정)
print(train_target[:20]) #출력값 => [1 0 0 1 0 (중략) 0 0 0 1 1 0 1]

 

8.2.2. 코드 - 훈련 세트 준비 및 시퀀스 패딩

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

#20%를 validation set로 사용
train_input, val_input, train_target, val_target = train_test_split(
    train_input, train_target, test_size=0.2, random_state=42)

#mean과 median으로 미루어보아 한 리뷰 내 단어의 개수 평균은 239개 정도 되지만 중간값은 178
#리뷰 길이 분포가 치우쳐져 있을 것으로 예상
lengths = np.array([len(x) for x in train_input])
print(np.mean(lengths), np.median(lengths)) #출력값 239.00925, 178.0

#리뷰 길이 분포를 히스토그램으로 확인
plt.hist(lengths)
plt.xlabel('length')
plt.ylabel('frequency')
plt.show()

리뷰별 길이 분포

  • 위 히스토그램에서 대부분의 리뷰 길이가 300 미만임을 확인할 수 있음
  • 이를 바탕으로 본 교재에서는 각 리뷰에서 100개의 단어만 추출해서 사용
  • 100개 보다 적은 단어로 이루어진 문장은 패딩 처리를 해야함   시퀀스 패딩
from tensorflow.keras.preprocessing.sequence import pad_sequences

#maxlen=100 => 100개의 토큰 사용
#100개가 넘으면 100개로 자르고, 100개가 안되면 빈 공간을 0으로 채워넣음
#보통 문장의 앞부분이 잘리고, 뒷부분을 사용함 (문장의 뒷 부분이 더 의미있다 가정)
train_seq = pad_sequences(train_input, maxlen=100)

print(train_seq.shape) #출력값 => (20000, 100) / (샘플 개수, 토큰(타임스텝 개수))

#안 잘리면 맨 앞이 0이 아님
print(train_seq[0]) #출력값 => [ 10   4  20   9   2 중략 20  10  10   2 158]

#앞 부분이 패딩됨
print(train_seq[5]) #출력값 => [  0   0   0   0   1   2 195  19 중략 48  64  18   4   2]

val_seq = pad_sequences(val_input, maxlen=100)

 

8.2.3. 코드 - 순환 신경망 모델 만들기

  • 앞서 텍스트를 프로그램이 처리할 수 있게 하기 위해서 고유의 숫자로 변경해서 처리했음
  • 프로그램은 숫자의 대소에 영향을 받기 때문에 영향 받지 않기 위해 데이터를 바꿔줘야함
  • 원-핫 인코딩
    • 데이터를 바꾸는 방법 중 하나
    • 어떤 클래스에 해당하는 원소만 1이고 나머지는 모두 0인 벡터 (ex. [ 1, 0, 0, 0, 0, 0, 0 ] , [ 0, 0, 0, 1, 0, 0, 0 ])
from tensorflow import keras

model = keras.Sequential()

#SimpleRNN => 가장 간단한 순환신경망 클래스. 8의 자리는 뉴런의 개수를 의미
#SimpleRNN 사용 시 별도의 activation를 설정하지 않으면 tanh가 기본값
model.add(keras.layers.SimpleRNN(8, input_shape=(100, 200)))
model.add(keras.layers.Dense(1, activation='sigmoid'))

#keras.utils.to_categorical => 원핫 인코딩을 해주는 메소드
train_oh = keras.utils.to_categorical(train_seq)
val_oh = keras.utils.to_categorical(val_seq)

8.2.4. 코드 - 순환 신경망 훈련

#케라스 API에서는 다른 신경망과 순환 신경망 훈련 방법이 크게 다르지 않음

rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model.compile(optimizer=rmsprop, loss='binary_crossentropy',
              metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-simplernn-model.keras',
                                                save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
                                                  restore_best_weights=True)

history = model.fit(train_oh, train_target, epochs=100, batch_size=64,
                    validation_data=(val_oh, val_target),
                    callbacks=[checkpoint_cb, early_stopping_cb])
 
#훈련 손실과 검증 손실을 그래프로 확인
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

8.2.5. 코드 - 임베딩

  • 원-핫 인코딩 사용 시 입력 데이터가 매우 커진다는 단점이 있음
  • 임베딩
    • 각 단어를 고정된 크기의 실수 밀집 벡터로 만드는 방법으로 원-핫 인코딩 단점 보완
    • 반복된 훈련을 통해 각 단어의 유사성 또한 반영할 수 있음
model2 = keras.Sequential()

#레이어로 Embedding 사용
model2.add(keras.layers.Embedding(200, 16, input_shape=(100,)))
model2.add(keras.layers.SimpleRNN(8))
model2.add(keras.layers.Dense(1, activation='sigmoid'))

rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model2.compile(optimizer=rmsprop, loss='binary_crossentropy',
               metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-embedding-model.keras',
                                                save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
                                                  restore_best_weights=True)

history = model2.fit(train_seq, train_target, epochs=100, batch_size=64,
                     validation_data=(val_seq, val_target),
                     callbacks=[checkpoint_cb, early_stopping_cb])
                     
#훈련 손실과 검증 손실을 그래프로 확인
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

8.3. LSTM 셀과 GRU 셀

8.3.1. 개요 - LSTM 셀, GRU셀

LSTM

  • LSTM(Long Short-Term Memory) 구조
    • 단기 기억을 보다 길게 기억하기 위해 고안된 구조
    • 은닉 상태 외에 또 다른 순환되는 상태인 셀 상태를 계산해서 같이 표현
    • 상태 ⇒ 다음 층으로 전달되지 않고 LSTM 셀에서 순환만 되는 값
    • 셀 상태를 계산하기 위한 각 곱셈에 대한 용어
      • 삭제 게이트 ⇒ 셀 상태에 있는 정보를 제거하는 역할
      • 입력 게이트 ⇒ 새로운 정보를 셀 상태에 추가하는 역할
      • 출력 게이트 ⇒ 다음 은닉 상태로 현재 셀 상태를 출력하는 역할

GRU

  • GRU(Gated Recurrent Unit) 구조
    • LSTM을 간소화한 버전
    • 셀 상태를 계산하지 않음
    • LSTM보다 가중치가 적어 계산량이 적지만 좋은 성능을 냄

8.3.2. 코드 - LSTM 셀, GRU셀

  • LSTM 신경망 코드 구현
    #데이터 준비 과정 생략
    from tensorflow import keras
    
    model = keras.Sequential()
    
    model.add(keras.layers.Embedding(500, 16, input_shape=(100,)))
    
    #keras.layer.LSTM(뉴런개수)를 하면 LSTM 사용 가능
    model.add(keras.layers.LSTM(8))
    model.add(keras.layers.Dense(1, activation='sigmoid'))
    
    model.summary()
    
    rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
    model.compile(optimizer=rmsprop, loss='binary_crossentropy',
                  metrics=['accuracy'])
    
    checkpoint_cb = keras.callbacks.ModelCheckpoint('best-lstm-model.keras',
                                                    save_best_only=True)
    early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
                                                      restore_best_weights=True)
    
    history = model.fit(train_seq, train_target, epochs=100, batch_size=64,
                        validation_data=(val_seq, val_target),
                        callbacks=[checkpoint_cb, early_stopping_cb])

  • GRU 신경망 코드 구현
    #데이터 준비 과정 생략
    
    model4 = keras.Sequential()
    
    model4.add(keras.layers.Embedding(500, 16, input_shape=(100,)))
    
    #keras.layer.GRU(뉴런개수)를 하면 GRU 사용 가능
    model4.add(keras.layers.GRU(8))
    model4.add(keras.layers.Dense(1, activation='sigmoid'))
    
    model4.summary()
    
    rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
    model4.compile(optimizer=rmsprop, loss='binary_crossentropy',
                   metrics=['accuracy'])
    
    checkpoint_cb = keras.callbacks.ModelCheckpoint('best-gru-model.keras',
                                                    save_best_only=True)
    early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
                                                      restore_best_weights=True)
    
    history = model4.fit(train_seq, train_target, epochs=100, batch_size=64,
                         validation_data=(val_seq, val_target),
                         callbacks=[checkpoint_cb, early_stopping_cb])
  • 코드 세부 설정
    • 순환층 드롭아웃 적용
      • 드롭아웃 ⇒ 은닉층에 있는 뉴런의 출력을 랜덤하게 꺼서 과대적합을 방지하는 기법
      • 순환층은 매개변수로 드롭아웃 기능 제공
    • 2개의 순환층 연결 
      • 순환층은 기본적으로 마지막 타입스텝 값만 반환하지만, 다중 순환층으로 연결할 때는 전부 반환해야함
      model3 = keras.Sequential()
      
      model3.add(keras.layers.Embedding(500, 16, input_shape=(100,)))
      
      #dropout=0.3 => 30%의 입력을 드롭아웃하겠다는 뜻
      #return_sequences를 True로 하면 전체 타입스텝 값이 반환됨
      #LSTM 외 GRU도 동일하게 적용할 수 있음
      model3.add(keras.layers.LSTM(8, dropout=0.3, return_sequences=True))
      model3.add(keras.layers.LSTM(8, dropout=0.3))
      model3.add(keras.layers.Dense(1, activation='sigmoid'))
      
      model3.summary()

참고자료 :

1. 혼자 공부하는 머신러닝, 박해선, 한빛미디어

2. 혼자 공부하는 머신러닝 인프런 강의, 박해선, https://www.inflearn.com/course/%ED%98%BC%EC%9E%90%EA%B3%B5%EB%B6%80-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9D/dashboard

'Group Study (2024-2025) > Machine Learning 입문' 카테고리의 다른 글

[ML 입문] 7주차 스터디  (1) 2024.11.20
[ML 입문] 6주차 스터디  (3) 2024.11.10
[ML 입문] 5주차 스터디  (1) 2024.11.06
[ML 입문] 4주차 스터디  (0) 2024.10.30
[ML 입문] 3주차 스터디  (1) 2024.10.16