머신러닝

인공지능에 RNN에 의한 학습법 및 UNSEEN 신종코로나감염지자수 데이타 예측

coding art 2020. 1. 30. 20:18
728x90

20201월에 돌발적으로 발생하여 퍼지고 있는 중국 우한폐렴에 의한 신종 코로나 바이러스 감염자 수 예측을 위한 RNN 알고리듬을 살펴보자. RNN 알고리듬은 머신 러닝 알고리듬 중에서도 시계열 데이터 방식으로 학습을 시킬 수 있는 특이한 알고리듬으로서 충분한 수의 시계열 입력 데이터가 있으면 시간 스텝 별로 현재 데이터에 대해서 1 타임 스텝 다음 데이터를 학습을 위한 목표 라벨 값으로 잡아 sequence length 만큼 단위로 뉴럴 네트워크를 학습 시키는 방식이다. 첫 번째 sequence length 만큼의 학습이 완료되면 1 타임 스템 만큼 스트라이딩하여 학습을 반복하게 된다. 아래 그림에서처럼 n 개의 데이터를 대상으로 RNN 연산이 실행되면 (n+1)번째 예측 값인 Xn을 얻어낼 수 있다.


 

만약 더 이상의 학습 데이터가 없는 상황에서 지속적으로 다음 스텝의 X 값을 예측하기 위해서는 전 단계에서 계산된 Xn 값을 다시 입력해 주게 되면 Xn+1 데이터를 계산해 낼 수 있게 된다.

 

대부분의 RNN 예제에서는 주어진 입력 데이터 X에 대해서 적절한 비율로 학습 데이터 개수를 설정하고 나머지는 테스트용으로 두어 RNN 연산 실행 후 테스트용 X 데이터와 RMSE(Root Mean Square Error)오차를 계산하여 비교하게 된다. RMSE 오차는 정현 파형 신호들이 합성된 복잡한 Signal 파형을 대상으로 사용하기에 편리한 오차 계산 방식이다. RNN 예측 값들과 이미 알고 있는 테스트 데이터들과의 오차가 최소화 되어야 제대로 예측이 이루어지는 것이다.

 

한편 우환폐렴 감염자수 RNN 예측 계산에서의 문제는 공식적으로 발표된 데이터를 락습용으로 사용하게 될 경우 테스트용 데이터가 별도로 없다는 점이다. Unseen 데이터 문제에 봉착하게 된다는 점이다. 학습용 데이터를 사용하여 예측한 마지막 값 Xn을 그 다음 스텝의 Unseen 데이터 Xn+1 으로 사용해야 한다.

아울러 테스트용 데이터와 RNN 예측 데이터와의 오차를 계산하여 그래프 처리를 하는 코드의 알고리듬 특성 상 처음에는 임의로 Gussing 에 의해 Unseen 데이터를 입력해 주어야 한다는 점이다. 이 값 자체가 Guessing 이므로 Xn 값이 Guessing 과 정확하게 일치하기는 어렵다.

 

RNN 하이퍼 파라메터 값을 설정하자. sequence_length 는 일주일 단위의 데이터를 기반으로 하는 time series 처리를 하기로 한다. 학습 반복횟수는 1000회면 충분하며 data dimension 은 시간 축을 1일 단위로 증가시키면서 감염수를 감안하면 2가 된다. 데이터는엑셀 CSV 포맷 데이타로 준비한다. 결국 이 RNN 방식은 증권데이타 종가를 예측하는 예제와 동일한 파이선 코드를 사용한다. 따라서 과거의 7time series 데이터를 사용하여 다음 번 데이터를 예측 계산하는 방식이다. 은닉층(hidden layer)의 출력은 임의이며 10개를 사용하였다. learning rateAdam Optimizer를 사용할 경우 Default 값이 0.001 이지만 Cost 함수 수렴을 모니터링 하여 최적 값을 찾도록 한다.


학습 데이터는 중국 정부가 공식 발표했다는 데이타 20개를 사용하기로 하며 나머지 5개는 결코 본 적이 없었던 UNSEEN 테스트 데이터이다. 이 데이터는 사용자가 RNN 예측 값과 UNSEEN 테스트 데이터와의 오차가 가의 없을 정도로 반복 수정해서 얻어야 한다. Matplotlib 그래픽 처리에 의한 그래프를 작업을 통해서 오차를 최종적으로 확인하도록 한다.


다음의 그래프는 실효치(RMS: Root Mean Squares)방식에 의한 오차 계산 결과로서 1.6% 오차 범위를 보여준다.

   

B 컬럼의 첫 번째 데이터 값은 111일 기준이며 130일 오전에 중국 내 총 감염자수 7711명을 기록하고 있다. RNN 예측 결과에 의하면 131일 내일 13000 선에 도달하여 5일 후인 24일 화요일 73000 명 선으로 예측되었다.

감염자 수 예측은 현재 환자를 수용할 수 있는 병원 침상을 초단기에 준비함에 있어 우한에 1,000 단위 규모의 병상을 준비한다고 하지만 그 정도로 환자를 수용하기에는 엄청나게 부족하지 않을까 하는 예상도 가능하다.




RNN 코드 배경에 관해서는 필자의 저서 6장을 참조하기 바란다.


첨부된 파이선 RNN 코드를 다운해서 실행해 보기 바라며 엑셀 CSV 데이터는 본인이 준비하여 실행코드와 동일한 폴더에 두도록 한다.

 

#rnn_coronavirus_pred.py

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import os

tf.compat.v1.reset_default_graph()
tf.set_random_seed(777)  # reproducibility


def MinMaxScaler(data):
    ''' Min Max Normalization

    Parameters
    ----------
    data : numpy.ndarray
        input data to be normalized
        shape: [Batch size, dimension]

    Returns
    ----------
    data : numpy.ndarry
        normalized data
        shape: [Batch size, dimension]

    References
    ----------
    .. [1] http://sebastianraschka.com/Articles/2014_about_feature_scaling.html

    '''
    numerator = data - np.min(data, 0)
    denominator = np.max(data, 0) - np.min(data, 0)
    # noise term prevents the zero division
    return numerator / (denominator + 1e-7)


# train Parameters
seq_length = 7
iterations = 1000
data_dim = 2
hidden_dim = 10
output_dim = 1
learning_rate = 0.01


# Open, High, Low, Volume, Close
xy = np.loadtxt('data_infect_02.csv', delimiter=',')
#xy = xy[::-1]  # reverse order (chronically ordered)
xy = MinMaxScaler(xy)
x = xy
y = xy[:, [-1]]  # Close as label

# build a dataset
dataX = []
dataY = []
for i in range(0, len(y) - seq_length):
    _x = x[i:i + seq_length]
    _y = y[i + seq_length]  # Next close price
    print(_x, "->", _y)
    dataX.append(_x)
    dataY.append(_y)

# train/test split
train_size = int(len(dataY) * 0.8)
test_size = len(dataY) - train_size
trainX, testX = np.array(dataX[0:train_size]), np.array(
    dataX[train_size:len(dataX)])
trainY, testY = np.array(dataY[0:train_size]), np.array(
    dataY[train_size:len(dataY)])

# input place holders
X = tf.placeholder(tf.float32, [None, seq_length, data_dim])
Y = tf.placeholder(tf.float32, [None, 1])

# build a LSTM network
cell = tf.contrib.rnn.BasicLSTMCell(
    num_units=hidden_dim, state_is_tuple=True, activation=tf.tanh)
outputs, _states = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
Y_pred = tf.contrib.layers.fully_connected(
    outputs[:, -1], output_dim, activation_fn=None)

# cost/loss
loss = tf.reduce_sum(tf.square(Y_pred - Y))  # sum of the squares
# optimizer
optimizer = tf.train.AdamOptimizer(learning_rate)
train = optimizer.minimize(loss)

# RMSE
targets = tf.placeholder(tf.float32, [None, 1])
predictions = tf.placeholder(tf.float32, [None, 1])
rmse = tf.sqrt(tf.reduce_mean(tf.square(targets - predictions)))

with tf.Session() as sess:
    init = tf.global_variables_initializer()
    sess.run(init)

    # Training step
    for i in range(iterations):
        _, step_loss = sess.run([train, loss], feed_dict={
                                X: trainX, Y: trainY})
        #print("[step: {}] loss: {}".format(i, step_loss))

    # Test step
    test_predict = sess.run(Y_pred, feed_dict={X: testX})
    rmse_val = sess.run(rmse, feed_dict={
                    targets: testY, predictions: test_predict})
    print("RMSE: {}".format(rmse_val))

    # Plot predictions
    plt.plot(testY)
    plt.plot(test_predict)
    plt.xlabel("x")
    plt.ylabel("Infected")
    plt.show()