시계열(timeseries)
이번 호에서는 시계열(timeseries) 데이터로부터
24시간 이후의 온도를 예측하는 문제를 다뤄보고자 한다. 이러한
미래 온도를 예측하는 문제에서 사용하는 시계열이란 데이터는 지금까지 다루어 왔던 데이터 형식과 다른 형태로, 주가의
일일 변동, 매장의 일주일간의 매출액, 환자의 몸에 부착된
장치에서 측정되는 파형과 같이 일정한 간격으로 측정되어 확보된 데이터세트를 뜻한다. 따라서 시계열데이터는
반복 주기, 안정 상태와 갑작스러운 돌출파형 등의 시간의 흐름에 따른 경과 등의 시스템의 역학관계를
이해하는 것이 중요하다. 주로 이러한 시계열 관련되어 머신러닝 작업에서 가장 많이 수행하는 작업이 예측이다. 반복되는 계열로부터 이후의 계열에서 어떤 일이 발생할지를 예측하는 것이다.
딥러닝을 이용하여 이러한 문제를 해결하기위해 우선 순환신경망(Recurrent Neural Network)을 사용하고, 독일의 Jena에 위치한 막스플랑크 생물지구화학 연구소의 기상 관측소에 2009년부터
2016년 사이에 매 10분 간격으로 저장된 온도, 압력, 습도 등등의 14가지
측정값 들이 저장된 시계열 데이터를 사용한다. 문제의 정의는 이러한 누적된 날씨관련 데이터로부터 다음
날 온도를 예측하는 것이다. 순환신경망(RNN)의 동작원리에
대해 좀 더 알고 싶을 경우에는 지난 뉴스레터9호를 참고.
온도 예측 문제
우선 사용할 jena 기상관측소의 데이터를
아래와 같이 다운로드 받는다.
<그림 1. 시계열 데이터 다운로드>
그 다음 아래 그림 2와 같이 csv 파일을 열어, 파일을 살펴보자. 전체 420,451
행의 데이터가 각각 datetime 시간 기록, 압력, 온도 등의 14개 일기관련 값과 헤더를 가지고 있다. lines 에는 header 가 분리된 내용만 담김을 알 수 있다.
<그림 2. Jena 기후 데이터의 탐색>
데이터의 첫번째 행을 보면 아래와 같이 14개의 컬럼에 대응하는 값들이 ‘,’ 에 의해 구별되어 부동소숫점(float) 형식으로 저장되어 있다.
데이터들 로부터 타겟과 데이터를 분리한다. 섭씨 온도를 담는 temperature 와 모든 데이터를 담은 raw_data 의 두 개의 Numpy array 를 생성. ①번에서 첫번째 컬럼인 datatime 이 배제되었고 ②에서 데이터의 온도에 해당하는 내용이 temperature 에 담기고 ③에서 모든 데이터(온도 포함)가 raw_data에 담긴다.
<그림3. 타겟과 데이터의 분리>
아래 그림 4와 같이 matplot 을 사용해 2009년부터 2016년까지의 8년간
420,451 레코드의 온도를 표시해본다. 8번 매년 반복되는
패턴이 선명하게 나타난다. 데이터는 매 10분 간격으로 측정되어
하루는 24 * 6 = 144 개, 1년은 52,560, 8년은 420,480 이다.
<그림 4. 8년간의 온도(섭씨) 변화>
주어진 지난 시계열 데이터로 미래의 예측을 하려면 validation이나 test 데이터가 training 데이터보다 최신의 것이어야 한다. 왜냐하면 과거를 기반으로
미래를 예측하는 것이기때문에 훈련데이터 할당이 validation 이나 test 보다 우선시되어야 한다. 시간 순서로 처음의 50%를 train에 그 다음 25%를
validation 그리고 마지막 최신의 25%를 test 로 할당한다. 아래 그림과 같이 train은 210225 샘플을 보유한다.
<그림 5. 분할 된 데이터의 샘플 수
산출>
데이터 정규화
데이터들이 모두 숫자로 되어있어서 신경망이 소화할 수 있는 벡터화는 필요 없지만, 14개 항목의 값들의 범위는 제 각각이다. 할당된 210,225 개의 훈련데이터에 대해 평균과 표준편차를 계산하여 정규화를 시킨다.
<그림 6. 훈련데이터 정규화>
아래 그림 7에 keras 의 내장된 데이터셋 유틸리티인 timeseries_dataset_from_array 예를 보자. ①을 통해 0부터 9까지의 정수 배열을 생성한다. dummy_dataset을 생성하는 인자로 ②는 우리가 생성할 시퀀스는 처음부터 끝에서 세번째까지 샘플링 할 것임을 설정 즉 [0 1 2 3 4 5 6], ③은 시퀀스가 만약 데이터의 N번째에서 시작이라면 타겟은 데이터의 N+3 이 된다는 설정. 아래 출력과 같이 [0, 1, 2]에서 타겟은 3 이 된다. ④ 시퀀스 길이는 3
데이터세트 구성
위에서 살펴본 keras 의 timeseries_dataset_from_array
를 활용하여 아래 그림 8과 같이 설정한다. ①샘플
비율은 기록이 10분마다 이루어지므로 한 시간에 6개 데이터
샘플링. ②시퀀스 길이는 5일 동안 즉 120 시간. ③의 delay는
결국 5일 동안의 시퀀스 길이에 더한 24시간 후의 샘플링
숫자를 가리키는 10분마다의 총 샘플링 숫자. ④는 위의
그림 7의 ②와 같이 우리가 생성할 시퀀스를 설정하는 것. ⑤는
위의 그림 7의 ③과 같이 타겟 설정. ⑥은 앞의 그림 5의 num_train_samples 을 가리킨다.
<그림 8. train 데이터세트 설정>
train 데이터세트는 (samples, targets)의 튜플 형태로 만들어진다. 여기서 sample 은 batch 당 256개의
샘플을, 각 샘플은 계속되는 120시간의 입력 데이터로 보유된다. target은 해당하는 256 개의 온도 array 형태 데이터이다. 샘플은 임의로 섞이기 때문에(shuffle=True), sample[0] 와 sample[1] 과
같이 batch에서 연속된 두 시퀀스가 반드시 유사하다고 볼 수 없다.
val_dataset 은 그림 8의 train 데이터세트 설정과 모두 내용이 같다 다만 이전 그림
5. 에서 설명한 바와 같이, 훈련 데이터세트 이후에 validation set 를 할당하므로, start_index=num_train_samples,
end_index=num_train_samples+num_val_sample로 바뀐다.
test_dataset 은 마찬가지로 모든
설정이 같고 다만, start_index = num_train_samples +num_val_sample이고 end_index 는 결국 마지막까지 에 해당하므로 별도 표시하지 않는다.
순환신경망 동작 개요
단순한 순환신경망(RNN) 구조를 통해 작동 원리를 살펴보자. 아래 그림 9를 보면, input, output, state 라고 하는 값들이 우측으로 진행됨에 따라 t-1, t, t+1 이라는 timestep을 가지고 움직이는 것을 볼 수 있다. 아주 단순하게 RNN을 다른 신경망과 구별 지어 설명한다면, 이전 loop의 반복 동안에 연산 된 값을 재사용하는 for loop으로 설명할 수 있다. ③번의 output_t 를 보면, input_t 와 weight Wo 가 vector dot product 수행한 ①과, 이전 상태의 output 인 state_t에 weight Uo 가 vector dot product 가 된 ②와 bias bo 를 합산하여 activation 함수를 거쳐서 산출됨을 알 수 있다. 여기서는 t 는 timestep 을 의미하는데 아래 그림은 결국 이러한 ③번의 output 연산이 timestep t 를 따라 반복되는 것임을 나타낸다.
<그림 9. RNN 개념도>
아래 그림 10은 아주 간단한 RNN 실행을 보여준다. 위의 그림9와 함께 보면 좀 더 이해하는데 도움이 될 것 같다. ①번에 RNN에 (timesteps,
input_features)의
형태로 2D tensor 가 입력 값으로 들어갈 수 있게 설정한다.
②에서 우선 초기 상태 state_t 를 모두 0인
벡터로 초기화 한다. ③④의 U, W, b 는 weight 매트릭스다. 임의의 초기값으로 생성한다. ⑤번에서 input_t 는 (input_features)
형태의 벡터이다. ⑥번은 위의 그림 9의 가운데
박스에서 일어나는 연산이다. 결국 ⑤번부터 ⑧번까지의 사이클은,
timestep 만큼 loop 를 수행하는데, 이전
반복 연산의 결과 state_t와 현재시점t시간에서의 입력(input_t)으로 (input_features)형태의 값을 받아, 이 둘 즉 state_t 와 input_t
를 합쳐서 output t를 만들어내는 것이다. ⑦번에서
이 output 을 successive_outputs 라는
리스트에 timestep 에 따라 계속 추가해 넣는다. ⑧에서
output_t 를 state_t 로 지정해주어 다음 timestep 에서 이 output_t 가 다시 입력으로 들어갈 수
있도록 해준다. 그리하여 for loop 이 끝나면, ⑨와 같이 output_t 가 timestep
만큼 쌓인 final_output_sequence 가 2D
tensor 로 (timesteps, output_features) 형태로 만들어진다.
<그림 10. Numpy 로 작성된 단순한
RNN>
LSTM 층을 사용한 24시간 후 온도 예측
자 이제 실제 LSTM 층을 통해서 온도예측을 수행해보자. 아래 그림 11과 같이 keras 를 통하여 ①과 같이 input 을 정의하고, ② 32개 unit 을 가진 LSTM 층을 설정하는데, dropout 없이 실행하면 바로 과적합이 일어난다. ③번의 Dense 층에도 일반화를 위해 Dropout 층을 추가한다. 여기서 한가지, ⑥번에 시간을 측정했는데 이유는 ②번의 unroll=True 를 설명하기 위해서이다.
Keras LSTM 이나 GRU 를 GPU 상에서 수행할 때, 복잡한 RNN 이 아닌, 디폴트
인자로 수행할 경우, cuDNN 커널을 사용하게 되는데, 과적합을
방지하기위해 ②번의 recurrent dropout 과 같은 인자를 사용하면 cuDNN 커널의 도움을 받을 수 없어 2~5배 정도의 GPU 속도 저하를 경험한다. ⑦번에
WARNING 메시지가 그것이다. cuDNN을 사용하지 않을 때 속도를 올리기 위해 unrolling=True 로 설정하는 것이 좋다. 설정하지 않았을
때 Nvidia RTX 2080Ti 1 GPU에서 50 epoch 를
훈련하는데 276분이 소요되던 것이 설정하고는 101분으로
줄었다. ⑤번에서 평가는 mean absolute error 로
측정하였다. 즉, 예측한 24시간
이후의 정답 온도의 평균 몇 도(섭씨)의 차이를 보이는 가를
측정한다는 뜻이다
결언
지금까지
기상관련 8년간 10분 간격으로 측정된 시계열 데이터세트를
이용하여 순환신경망으로 24시간 후의 온도를 예측하는 것을 살펴보았다.
위의 그림 11의 훈련을 50 epoch 수행하고
난 결과는 아래와 같다. Validated Mean Absolute Error 값은 2.3176 을 얻었다. MAE 는 예측 값과 실제 값의 차이에 대한 절대값에 대해 평균을 낸 값으로, 간단한
RNN 층을 이용하여 24시간 이후 온도 예측에서 실제 값과
약 2.32도 차이를 보이는 결과를 얻었다.
댓글 없음:
댓글 쓰기