시계열 예측(Time-Series Forecasting)이란?
시계열(Time-Series) 데이터란 시간의 흐름에 따라 순차적으로(sequentially) 기록된 데이터를 가리킨다.
관측된 시계열 데이터를 분석하여 미래를 예측하는 문제가 바로 시계열 예측 문제이다.
시계열 예측 문제는 흔하게 접하는 문제로써 주로 경제 지표를 예측하거나, 어떤 상품의 수요를 예측하는 문제에 이르기까지 다양한 어플리케이션을 가지고 있다.
특히, 예측된 결과를 바탕으로 여러 정책이나 비즈니스 전략을 결정하는 과정에 활용되기에, 실제 비즈니스 영역에서는 시계열 예측 문제가 매우 중요하게 여겨지고 있다.
일례로 McKinsey Global Institute의 연구에 따르면, 시계열 데이터가 텍스트나 이미지 데이터 보다 더 큰 잠재적 가치를 가지고 있다고 보고있다.
시계열 예측 문제의 주요 챌린지
무엇보다 시계열 문제가 4가지 어려운 점이 있다.
1.예측값은 완벽할 수 없으며, 항상 변동의 가능성을 내포.
따라서 시계열 예측에 있어 불확실성(uncertainty)이 고려되어야 한다. 다시 말해 예측값에 대해 적합한 확률 분포 모델(probability distribution)이 고려되어야 한다. 단순히 어떤 에측 값을 제공한다기 보다는 그 예측값의 불확실성(또는 신뢰도)에 대해 정량적인 평가가 함께 고려되어야 예측 모델로써 더욱 가치가 있다.
2. 시계열 데이터에 숨겨진 여려 형태의 패턴을 찾아 학습하는 문제.
보통 trend,seasonality,cycle 등의 유형으로 패턴을 구분하곤 한다. 대게 시계열 데이터에는 이러한 패턴이 복잡하게 섞여있고 데이터의 길이가 불충분 하거나, 노이즈, 아웃라이어 데이터로 인해 손쉽게 패턴을 구분해 내어 찾기가 어렵다.
3. 다변량 시계열 데이터(multiple time-series)를 다뤄야 하는 경우가 많아지고 있다.
과거에는 주로 다변량의 시계열 데이터(unvariate time-series)의 분석과 예측 문제가 주로 다뤄졌지만 근래에는 다변량 시계열 데이터를 다뤄야 하는 경우가 많아지고 있다.
순차적으로 관찰된 수많은 변수들에 대해, 이 변수들간의 상관 관계를 학습할 수 있어야 한다. 가령, 어떤 상품의 수요가 다른 상품들의 수요 변화에 영향을 받거나, 어떤 지역의 택시 수요가 다른 지역의 택시 수요와 일정한 시간 차이를 두고 상관 관계를 보일 수 있다. 이 변수가 적게는 수백에서 많게는 수백만에 이를 수 있으므로, 이를 효율적으로 처리하고 학습할 수 있는 알고리즘이 매우 중요해 지고 있다.
4. 시계열 데이터에는 노이즈 데이터 또는 관찰되지 못한 기간이 종종 존재한다.
이는 측정하고 데이터를 기록하는 과정에서의 오류나 예측치 못한 상황으로 인해 발생할 수 있다. 예를 들어 상품의 품절로 인하여 장기간 판매량이 없는 경우, 해당 상품에 대한 실제 수요를 판매량으로 유추할 수 없는 경우이다.
시계열 예측 문제에 있어서는 이에 대한 적절한 전처리 과정이 매우 중요하다.
시계열 예측 모델의 평가
일반적으로 supervised machine learning 모델은 데이터 셋을 학습 데이터와 테스트 데이터로 랜덤하게 나누어 평가를 한다. 하지만 시계열 예측 문제는 평가와 학습 대상의 데이터를 특정 시간 기준으로 엄밀하게 분리시켜, data leak이 없이 평가가 이루어져야 한다.
가령 시계열 데이터의 중간 구간을 잘라내 테스트용으로, 그 이전과 이후를 학습용으로 사용하면 정확한 평가가 이루어질 수 없다. 학습 데이터는 반드시 테스트 데이터 보다 이전에 관찰된 것이어야 한다.
모델링 과정에서는 최적의 성능을 가지되, robust한 모델을 찾기 위해 cross-validation을 통해 성능을 평가한다. 제한된 데이터 셋으로부터 여러 쌍의 학습 및 테스트 데이터를 샘플링해서 모델의 성능이 기대치에 부합하며, 안정적인지를 확인하게 된다. 시계열 예측 모델에서는 앞서 언급한 chronological testing 기준을 지키며, cross-validation을 할 수 있는 방법으로 크게 두가지가 고려된다.
1. Sliding window
슬라이딩 윈도우(Sliding Window) 알고리즘은 배열이나 리스트의 요소의 일정 범위의 값을 비교할 때 사용하면 유용한 알고리즘이다.
예를 들어 정수로 이루어진 배열 [2,4,7,10,8,4,5,6,7,1] 에서 길이가 3인 서브배열의 합계가 가장 큰 서브배열은 무엇일까?
특정 길이의 하위 배열의 최대 합계값을 구하는 단순한 방식을 먼저 살펴보자.
단순하게 for 문으로 모든 배열 요소를 특정 길이만큼 순회하며 합계를 구해서 최대 값을 구하는 단순하고 비효율적인 방법이다.
import sys
def max_sub_array(arr, k):
maxsum = -sys.maxsize - 1
arraysize = len(arr)
for i in range(arraysize - k + 1):
current_sum = 0
for j in range(i, i + k):
current_sum += arr[j]
maxsum = max(maxsum, current_sum)
return maxsum
if __name__ == '__main__':
print(max_sub_array([2, 4, 7, 10, 8, 4, 5, 6, 7, 1], 3))
25 (7 + 10 + 8)
설명이 크게 필요 없는 단순한 코드이다.
물론 위와 같이 작성해도 데이터가 방대하지 않다면 큰 차이는 없을 것이다.
그렇다면 좀 더 효율적인 코드를 작성할 방법을 찾아보자.
우선 기존 단순한 방식에서 서브 배열의 요소를 순회하다 보면 중복되는 요소들이 존재한다.
위 이미지 처럼 범위가 5인 서브배열을 탐색하는 경우 0~4 범위의 서브배열과 1~5 범위의 서브배열은 공통적으로 1~4 범위가 중복된다.
중복되는 요소(공통 요소)들을 재상용하는 방법이 슬라이딩 윈도우 알고리즘의 핵심이다.
def max_sub_array(arr, k):
window_sum = 0
max_sum = 0
window_start = 0
for window_end in range(len(arr)):
window_sum += arr[window_end] # 슬라이딩 인덱스 범위 요소 합산
# 슬라이딩 윈도우의 범위가 k 보다 커진 경우
if window_end >= (k - 1):
max_sum = max(max_sum, window_sum)
window_sum -= arr[window_start] # 슬라이드 윈도우 범위를 벗어난 요소를 합계에서 제거
window_start += 1 # 슬라이드 윈도우 시작 인덱스 증가
return max_sum
if __name__ == '__main__':
print(max_sub_array([2, 4, 7, 10, 8, 4], 3))
25 (7 + 10 + 8)
이해를 쉽게 하기 위해 기존 배열보다 길이가 짧은 배열로 변경했다. 각 루프마다 다음과 같은 식으로 진행된다.
우선 슬라이딩 윈도우를 관리하기 위한 변수들로 window_start, window_end, window_sum 등을 사용한다.
-window_start: 슬라이딩 윈도우 시작 인덱스
-window_end: 슬라이딩 윈도우 끝 인덱스
-window_sum: 슬라이딩 윈도우 합계
슬라이딩 윈도우는 하위 윈도우의 범위 k 와 동일하게 유지하며 범위 내의 요소는 모두 합산하고 범위 밖으로 벗어난 요소들은 빼준다. 다만 슬라이딩 윈도우의 범위가 k 보다 커질때까지 (window_end>=k-1)는 요소들을 합산하기만 하고 이후부터는 범위 밖의 요소들을 차감시켜준다.
이렇게 하면 기존처럼 매번 서브배열의 합계를 구할 필요 없이 모든 요소를 1번만 순회하면서 최대값을 구하게 되므로 빠르고 효율적으로 처리된다.
위 sliding window 알고리즘을 통해 고정된 사이즈 2개의 window를 움직여가며 학습,테스트 데이터를 추출하여 cross-validation을 하는 방법이다.
2. Expanding window
위 sliding window를 이해하였다면 이는 간단하게 이해할 수 있다. 데이터 길이가 충분치 않을때 데이터를 확장해 가며 학습,테스트 데이터를 추출하는 알고리즘이다.
시계열 예측 모델의 평가는 문제에 따라 다양한 지표가 활용될 수 있다. Root Mean Squared Error(RMSE) 또는 Mean Absolute Percentage Error(MAPE)가 많이 사용되는 지표이다. 일반적으로 baseline 예측 모델(e.g.최근 평균값으로 예측)과의 비교가 매우 유용하다. 머신러닝을 도입하기 이전의 다소 단순한 방식과의 비교를 통해 개발된 예측 모델의 도입 효과를 가늠해 보곤한다. 예측 모델의 평가는 특정 성능 지표 뿐만 아니라 residual error의 분포, 시간축에 따른 Error의 propagation 패턴을 분석하여 모델의 bias가 있는지 혹은 overfitting 여부 등에 대한 검토가 반드시 필요하다.
주요한 시계열 패턴
전통적으로 시계열 데이터를 분석할 때 보통 trend, seasonality,cycles 그리고 random noise의 패턴으로 분류하여 분석한다.
Trend 는 전체적으로 긴구간에 걸쳐 일정하게 증가 또는 감소하는 패턴을 가리킨다.
Seasonal component는 규칙적인 주기에 따라 반복되는 패턴을 나타내며, 주기가 규칙적이지 않지만 wave형태로 반복되는 패턴을 cyclical component로 분류하여 분석한다.
Trend,seasonality,cycles를 제외한 그외 불규칙한 fluctuation은 random component로 분류한다.
일반적을 전통적인 시계열 분석 모델에서는 이 4가지 유형이 패턴이 linear한 조합으로 이루어진 모델(additive model)을 가정한다. 간혹 seasonal 또는 cyclic 패턴의 변동폭이 시계열 값의 크기에 따라서 함께 변하는 경우 multiplicative model을 가정하기도 한다. 분석 모델 안에서 패턴이 어떻게 구성되느냐에 따라 , 개별 component에 대한 수학적 모델 또한 매우 다양하다. 주어진 시계열 데이터에 맞는 모델을 찾아가는 과정이 예측 모델을 만들어 가는 과정이라고 볼 수 있다.
대표적인 전통 시계열 예측 모델: ARIMA
전통적으로 시계열 데이터에서 패턴을 추출하고 그 패턴을 이용하여 미래를 예측하는 모델로 Autoregressive integrated moving average(ARIMA)가 있다.
ARIMA모델은 크게 세가지 component로 조합되어 만들어진다. 먼저 Autoregressive는 예측 시점(t-0)이 과거 p 개의 데이터 포인트(t-1,t-2,...,t-p)와 연관이 있다는 의미이다. 일련의 과거 관측값을 이용하여 미래의 값을 예측하는 regression 모델인 것이다.
Moving average는 과거의 예측 에러를 예측 대상과 연관 시키는 component이다. 가령 autoregressive 모델로 미래를 예측함에 있어, 과거의 에러들을 고려하여 그 예측값을 보정하는 역할을 한다고 볼 수 있다. 마지막으로 integrated의 의미는 non-stationary time-series의 데이터를 differencing을 통해 시간에 따라 데이터의 통계적 특성(e.g. mean, std)이 변하지 않는 stationary time-series 데이터로 변환하는 모델을 의미한다. 적합한 ARIMA 예측 모델을 만들기 위해서는 differencing order을 조절하여 데이터로부터 non-stationary를 반드시 제거해야 한다.
ARIMA 모델은 추가적인 component를 고려함으로써 다양한 형태로 변형, 발전될 수 있다. 예를 들어 추가적인 seasonal component를 고려하는 SARIMA가 있으며, univariate time-series가 아닌 추가적인 covariates를 함께 고려하여 예측할 수 있는 ARIMAX 모델이 존재한다. 물론 이 두가지를 모두 혼합한 SARIMAX모델도 사용되곤 한다.