자연어 처리 모델

이재경·2023년 1월 10일
0

인공지능

목록 보기
9/14

Vanilla RNN 모델 만들기

이번 실습에서는 RNN 모델의 가장 간단한 형태인 Vanilla RNN 모델을 직접 만들어보도록 하겠습니다.

출처:엘리스

Tensorflow에서는 이러한 Vanilla RNN이 SimpleRNN 이라는 이름으로 구현되어 있습니다. 따라서 앞선 CNN 모델에서 사용했던 Conv2D Layer 처럼 사용할 수 있습니다.

이번 실습은 이 SimpleRNN Layer를 활용하여 자연어 데이터를 임베딩 하는 상황을 가정하여 모델을 구성하도록 할 것입니다. 따라서 실제로 데이터를 준비하여 모델에 적용하는 것은 아닙니다.

실습을 통해 SimpleRNN Layer를 어떻게 활용하면 되는지 알아보세요.

지시사항
자연어 데이터를 딥러닝 모델에 적용할 수 있는 형태로 바꾸기 위해서는 임베딩을 활용하여 벡터로 바꾸어야 했습니다.

Tensorflow에서는 이 임베딩을 해주는 Layer로 Embedding이라 불리는 것이 있습니다. 해당 Layer는 데이터셋 내의 전체 단어 개수와 각 단어를 몇개의 원소를 가지는 벡터로 만들지를 설정해주면 알아서 각 단어별로 적절한 벡터를 만들어주게 됩니다.

지시사항을 통해 두 개의 SimpleRNN 기반 모델을 만들어보세요.

첫번째 모델을 만드는 함수 build_model1을 완성하세요. 모델 구성은 다음과 같습니다.
layers.Embedding
전체 단어 개수: 10개
벡터 길이: 5
layers.SimpleRNN
hidden state의 크기: 3

두번째 모델을 만드는 함수 build_model2를 완성하세요. 모델 구성은 다음과 같습니다.
layers.Embedding
전체 단어 개수: 256개
벡터 길이: 100
layers.SimpleRNN
hidden state의 크기: 20
layers.Dense
노드 개수: 10
활성화 함수: softmax

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow.keras import layers, Sequential

# TODO: [지시사항 1번] 첫번째 모델을 완성하세요.
def build_model1():
    model = Sequential()
    
    model.add(layers.Embedding(10, 5))
    model.add(layers.SimpleRNN(3))
    
    return model

# TODO: [지시사항 2번] 두번째 모델을 완성하세요.
def build_model2():
    model = Sequential()
    
    model.add(layers.Embedding(256, 100))
    model.add(layers.SimpleRNN(20))
    model.add(layers.Dense(10,activation="softmax"))
    
    return model
    
def main():
    model1 = build_model1()
    print("=" * 20, "첫번째 모델", "=" * 20)
    model1.summary()
    
    print()
    
    model2 = build_model2()
    print("=" * 20, "두번째 모델", "=" * 20)
    model2.summary()

if __name__ == "__main__":
    main()

Vanilla RNN으로 IMDb 데이터 학습하기

IMDb(Interet Movie Database)는 영화 정보를 데이터베이스화 해서 제공하는 인터넷 사이트입니다.(https://www.imdb.com/)

이 사이트에는 각 영화의 기본적인 정보 뿐 아니라 사용자들의 리뷰도 포함되어 있습니다. 사용자 리뷰는 별점 기반으로 10개 만점인데, 스탠포드 대학에서 이 리뷰들이 긍정적인지 부정적인지를 분석하여 클래스가 두개인 데이터셋으로 구성하였습니다.

따라서 많은 자연어 처리 예제에서 감성 분석(Sentimental Analysis) 을 위한 데이터셋으로 많이 활용하고 있습니다. 이번 실습에서도 이 데이터셋을 활용하여 SimpleRNN 모델을 직접 만들어 학습하는 과정을 알아보도록 하겠습니다.

지시사항
Tensorflow는 유명한 데이터셋의 일부를 라이브러리에서 바로 활용할 수 있는 API를 제공하고 있습니다. 이 데이터셋들은tensorflow.keras.datasets이라는 모듈에서 찾아볼 수 있습니다. IMDb 데이터셋 또한 이 모듈에서 제공하고 있기 때문에 여기서는 이를 활용하여 데이터셋을 불러오도록 하겠습니다.

앞서 언급했듯 이 데이터셋은 긍정과 부정 두가지의 클래스를 가지고 있으니 이진 분류(Binary Classification)을 할 수 있는 모델을 구성하도록 하겠습니다.

지시사항에 따라 코드를 완성하세요.

SimpleRNN 기반 모델을 완성하는 함수 build_rnn_model을 완성하세요. 모델의 Layer 구성은 다음과 같습니다.
layers.Embedding
전체 단어 개수: num_words
벡터 길이: embedding_len
layers.SimpleRNN
hidden state 크기: 16
layers.Dense
노드 개수: 1개
활성화 함수: Sigmoid

main 함수에서 모델 학습을 위한 Optimizer, 손실 함수, 평가 지표(Metrics)을 설정하세요.
Optimizer: Adam
Learning rate: 0.001
손실 함수: binary_crossentropy
평가 지표: accuracy

모델 학습을 위한 hyperparameter를 설정하세요.
epochs=epochs
batch_size=100
validation_split=0.2
shuffle=True
verbose=2

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow.keras import layers, Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences

def load_data(num_words, max_len):
    # imdb 데이터셋을 불러옵니다. 데이터셋에서 단어는 num_words 개를 가져옵니다.
    (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=num_words)

    # 단어 개수가 다른 문장들을 Padding을 추가하여
    # 단어가 가장 많은 문장의 단어 개수로 통일합니다.
    X_train = pad_sequences(X_train, maxlen=max_len)
    X_test = pad_sequences(X_test, maxlen=max_len)
    
    return X_train, X_test, y_train, y_test

def build_rnn_model(num_words, embedding_len):
    model = Sequential()
    
    # TODO: [지시사항 1번] 지시사항에 따라 모델을 완성하세요.
    model.add(layers.Embedding(num_words,embedding_len))
    model.add(layers.SimpleRNN(16))
    model.add(layers.Dense(1,activation="sigmoid"))
    
    return model

def main(model=None, epochs=5):
    # IMDb 데이터셋에서 가져올 단어의 개수
    num_words = 6000
    
    # 각 문장이 가질 수 있는 최대 단어 개수
    max_len = 130
    
    # 임베딩 된 벡터의 길이
    embedding_len = 100
    
    # IMDb 데이터셋을 불러옵니다.
    X_train, X_test, y_train, y_test = load_data(num_words, max_len)
    
    if model is None:
        model = build_rnn_model(num_words, embedding_len)
    
    # TODO: [지시사항 2번] 모델 학습을 위한 optimizer와 loss 함수를 설정하세요.
    optimizer =Adam(learning_rate=0.001)
    model.compile(optimizer=optimizer,loss="binary_crossentropy",metrics=["accuracy"])
    
    # TODO: [지시사항 3번] 모델 학습을 위한 hyperparameter를 설정하세요.
    hist = model.fit(X_train,y_train,epochs=epochs,batch_size=100,validation_split=0.2,shuffle=True,verbose=2)
    
    # 모델을 테스트 데이터셋으로 테스트합니다.
    test_loss, test_acc = model.evaluate(X_test, y_test, verbose=0)
    print()
    print("테스트 Loss: {:.5f}, 테스트 정확도: {:.3f}%".format(test_loss, test_acc * 100))
    
    return optimizer, hist

if __name__=="__main__":
    main()

Vanilla RNN을 통한 항공 승객 수 분석

이번 실습에서는 항공 승객 수 데이터셋을 통해 월별로 항공기를 이용하는 승객 수가 어떻게 변화하는지 확인하고 예측하는 모델을 만들어보도록 하겠습니다.

사용할 데이터셋은 시계열 데이터 분석을 위한 예제 데이터로, 1949년 1월부터 1960년 12월까지 항공기 이용 승객 수를 월별로 기록한 데이터셋입니다.

이 데이터에서 앞쪽 데이터의 80%를 모델 학습을 위한 데이터로 사용하고, 나머지 20%를 모델이 예측하도록 하겠습니다.

지시사항
데이터셋을 불러와 학습 데이터와 테스트 데이터로 나누는 부분은 load_data 함수에 구현되어 있습니다.

시계열 데이터를 사용하여 RNN 기반 모델을 학습할 때는 window size라는 개념을 사용합니다.

이는 모델을 한번 학습할 때 사용할 데이터의 개수를 의미하는 것으로, 아래 그림처럼 총 10개의 데이터에서 4개의 데이터를 한번 학습에 사용한다면 window size는 4가 됩니다.

이 실습에서는 데이터셋을 구성할 때 각 입력 데이터의 window size가 4가 되도록 설정하였습니다.

지시사항에 따라 코드를 완성하세요.

SimpleRNN 기반 모델을 만드는 함수 build_rnn_model을 완성하세요. Layer 구성은 아래와 같습니다.
layers.SimpleRNN
hidden state의 크기: 4
input_shape=(window_size, 1)
layers.Dense
노드 개수: 1개

main 함수 내에서 모델 학습을 위한 optimizer, loss 함수, 평가 지표(metrics)를 설정하세요.
Optimizer: Adam
Learning Rate: 0.001
Loss 함수: Mean Squared Error(MSE)

모델 학습을 위한 hyperparameter를 설정하세요.
batch_size=8
epochs=epochs
shuffle=True
verbose=2
이번 실습에서는 예측한 값이 실제 값이 어떻게 나타나는지 마지막에 그래프로 그립니다. 그래프를 보고 값이 잘 예측되고 있는지 확인해보세요.

from elice_utils import EliceUtils

elice_utils = EliceUtils()

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow.keras import layers, Sequential
from tensorflow.keras.optimizers import Adam

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def load_data(window_size):
    raw_data = pd.read_csv("./airline-passengers.csv")
    raw_passengers = raw_data["Passengers"].to_numpy()

    # 데이터의 평균과 표준편차 값으로 정규화(표준화) 합니다.
    mean_passenger = raw_passengers.mean()
    stdv_passenger = raw_passengers.std(ddof=0)
    raw_passengers = (raw_passengers - mean_passenger) / stdv_passenger
    data_stat = {"month": raw_data["Month"], "mean": mean_passenger, "stdv": stdv_passenger}

    # window_size 개의 데이터를 불러와 입력 데이터(X)로 설정하고
    # window_size보다 한 시점 뒤의 데이터를 예측할 대상(y)으로 설정하여
    # 데이터셋을 구성합니다.
    X, y = [], []
    for i in range(len(raw_passengers) - window_size):
        cur_passenger = raw_passengers[i:i + window_size]
        target = raw_passengers[i + window_size]

        X.append(list(cur_passenger))
        y.append(target)

    # X와 y를 numpy array로 변환합니다.
    X = np.array(X)
    y = np.array(y)

    # 각 입력 데이터는 sequence 길이가 window_size이고, featuer 개수는 1개가 되도록
    # 마지막에 새로운 차원을 추가합니다.
    # 즉, (전체 데이터 개수, window_size) -> (전체 데이터 개수, window_size, 1)이 되도록 변환합니다.
    X = X[:, :, np.newaxis]

    # 학습 데이터는 전체 데이터의 80%, 테스트 데이터는 20%로 설정합니다.
    total_len = len(X)
    train_len = int(total_len * 0.8)

    X_train, y_train = X[:train_len], y[:train_len]
    X_test, y_test = X[train_len:], y[train_len:]

    return X_train, X_test, y_train, y_test, data_stat

def build_rnn_model(window_size):
    model = Sequential()

    # TODO: [지시사항 1번] SimpleRNN 기반 모델을 구성하세요.
    model.add(layers.SimpleRNN(4,input_shape=(window_size, 1)))
    model.add(layers.Dense(1))
    return model
    
def plot_result(X_true, y_true, y_pred, data_stat):
    # 표준화된 결과를 다시 원래 값으로 변환합니다.
    y_true_orig = (y_true * data_stat["stdv"]) + data_stat["mean"]
    y_pred_orig = (y_pred * data_stat["stdv"]) + data_stat["mean"]

    # 테스트 데이터에서 사용한 날짜들만 가져옵니다.
    test_month = data_stat["month"][-len(y_true):]

    # 모델의 예측값을 실제값과 함께 그래프로 그립니다.
    fig = plt.figure(figsize=(8, 6))
    ax = plt.gca()
    ax.plot(y_true_orig, color="b", label="True")
    ax.plot(y_pred_orig, color="r", label="Prediction")
    ax.set_xticks(list(range(len(test_month))))
    ax.set_xticklabels(test_month, rotation=45)
    ax.set_title("RNN Result")
    ax.legend(loc="upper left")
    plt.savefig("airline_rnn.png")
    elice_utils.send_image("airline_rnn.png")

def main(model=None, epochs=10):
    tf.random.set_seed(2022)

    window_size = 4
    X_train, X_test, y_train, y_test, data_stat = load_data(window_size)

    if model is None:
        model = build_rnn_model(window_size)

    # TODO: [지시사항 2번] 모델 학습을 위한 optimizer와 loss 함수를 설정하세요.
    optimizer = Adam(learning_rate=0.001)
    model.compile(optimizer=optimizer,loss="mse")
    # 분류 CROSS ENTROPY, 값자체예측(회귀분석):mse

    # TODO: [지시사항 3번] 모델 학습을 위한 hyperparameter를 설정하세요.
    hist = model.fit(X_train,y_train,batch_size=8,epochs=epochs,shuffle=True,verbose=2)
    
    # 테스트 데이터셋으로 모델을 테스트합니다.
    test_loss = model.evaluate(X_test, y_test, verbose=0)
    print()
    print("테스트 MSE: {:.5f}".format(test_loss))
    print()
    
    # 모델의 예측값과 실제값을 그래프로 그립니다.
    y_pred = model.predict(X_test)
    plot_result(X_test, y_test, y_pred, data_stat)

    return optimizer, hist

if __name__ == "__main__":
    main()

심층 Vanilla RNN 모델

이전 실습까지 만든 Vanilla RNN 모델은 모두 SimpleRNN Layer가 하나로만 이루어진 모델이었습니다. 하지만 이 SimpleRNN 또한 Convolutional Layer 처럼 하나의 Layer 라고 볼 수 있기 때문에 여러 층으로 쌓는 것이 가능합니다.

이렇게 여러 SimpleRNN 층으로 이루어진 모델을 심층 RNN(Deep RNN) 모델이라고 부릅니다. 이번 실습에서는 SimpleRNN이 하나로 이루어진 모델과 두개로 이루어진 모델을 각각 만들어보고 성능을 비교해보도록 하겠습니다.

사용할 데이터는 아래 그림처럼 numpy를 이용하여 2개의 sin 함수를 조합한 간단한 시계열 데이터입니다. 앞서 설명한 Window Size는 50으로 두었습니다.

생성한 데이터에서 일부를 테스트로 두어 모델이 얼마나 잘 예측하는지 정도를 Mean Squared Error(MSE) 점수를 통해 확인해보세요.

지시사항
하나의 SimpleRNN Layer로 이루어진 모델을 만드는 함수 build_rnn_model을 완성하세요. Layer 구성은 다음과 같습니다.
layers.SimpleRNN
hidden state 크기: 20
input_shape=(window_size, 1)
layers.Dense
노드 개수: 1개

두개의 SimpleRNN Layer로 이루어진 모델을 만드는 함수 build_deep_rnn_model을 완성하세요. Layer 구성은 다음과 같습니다.
layers.SimpleRNN
hidden state 크기: 20
return_sequences=True
input_shape=(window_size, 1)
layers.SimpleRNN
hidden state 크기: 20
layers.Dense
노드 개수: 1개

run_model 함수 내에서 모델 학습을 위한 optimizer와 loss 함수를 설정하세요.
Optimizer: Adam
Learning Rate: 0.001
Loss 함수: Mean Squared Error(MSE)

run_model 함수 내에서 모델 학습을 위한 hyperparameter를 설정하세요.
epochs=epochs
batch_size=256
shuffle=True
verbose=2
참고
load_data 함수 부분에서 window_size로 X와 y 데이터를 나누는 부분에서 기존(영상)에는 X 안에 y 값 또한 포함시키는 에러가 있었습니다. 현재 코드에서는 12번줄과 15번 줄의 window_size 부분을 window_size + 1로 수정하였습니다.


import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow.keras import layers, Sequential
from tensorflow.keras.optimizers import Adam
import numpy as np

def load_data(num_data, window_size):
    freq1, freq2, offsets1, offsets2 = np.random.rand(4, num_data, 1)
    
    time = np.linspace(0, 1, window_size + 1)
    series = 0.5 * np.sin((time - offsets1) * (freq1 * 10 + 10))
    series += 0.1 * np.sin((time - offsets2) * (freq2 * 10 + 10))
    series += 0.1 * (np.random.rand(num_data, window_size + 1) - 0.5)
    
    num_train = int(num_data * 0.8)
    X_train, y_train = series[:num_train, :window_size], series[:num_train, -1]
    X_test, y_test = series[num_train:, :window_size], series[num_train:, -1]
    
    X_train = X_train[:, :, np.newaxis]
    X_test = X_test[:, :, np.newaxis]
    
    return X_train, X_test, y_train, y_test

def build_rnn_model(window_size):
    model = Sequential()

    # TODO: [지시사항 1번] SimpleRNN 기반 모델을 구성하세요.
    model.add(layers.SimpleRNN(20,input_shape=(window_size, 1)))
    model.add(layers.Dense(1))
    return model

def build_deep_rnn_model(window_size):
    model = Sequential()

    # TODO: [지시사항 2번] 여러개의 SimpleRNN을 가지는 모델을 구성하세요.
    model.add(layers.SimpleRNN(20,return_sequences=True,input_shape=(window_size, 1)))
    model.add(layers.SimpleRNN(20))
    model.add(layers.Dense(1))

    return model

def run_model(model, X_train, X_test, y_train, y_test, epochs=20, name=None):
    # TODO: [지시사항 3번] 모델 학습을 위한 optimizer와 loss 함수를 설정하세요.
    optimizer = Adam(learning_rate=0.001)
    model.compile(optimizer=optimizer,loss="mse")

    # TODO: [지시사항 4번] 모델 학습을 위한 hyperparameter를 설정하세요.
    hist = model.fit(X_train,y_train,epochs=epochs,batch_size=256,shuffle=True,verbose=2)
    
    # 테스트 데이터셋으로 모델을 테스트합니다.
    test_loss = model.evaluate(X_test, y_test, verbose=0)
    print("[{}] 테스트 MSE: {:.5f}".format(name, test_loss))
    print()

    return optimizer, hist
    
def main():
    tf.random.set_seed(2022)
    np.random.seed(2022)

    window_size = 50
    X_train, X_test, y_train, y_test = load_data(10000, window_size)

    rnn_model = build_rnn_model(window_size)
    run_model(rnn_model, X_train, X_test, y_train, y_test, name="RNN")

    deep_rnn_model = build_deep_rnn_model(window_size)
    run_model(deep_rnn_model, X_train, X_test, y_train, y_test, name="Deep RNN")


if __name__ == "__main__":
    main()
profile
코딩으로 빛나게

0개의 댓글