상세 컨텐츠

본문 제목

딥러닝 프로젝트 - Time Series Data Prediction (Matlab, LSTM)

IT/Toy-Project

by HarimKang 2020. 1. 7. 04:05

본문

Writer : Harim Kang

상품 온라인 판매 가격 데이터를 이용한 Time Series Data Prediction 프로젝트 포스팅입니다.

Matlab 코드로 작성하였고, LSTM(Long Short-Term Memory models) 네트워크 모델을 사용하였습니다. 

프로젝트 주제

온라인 상품 가격 데이터를 분석하여 현재의 특정 상품 가격을 예측해보자는 주제를 먼저 잡았습니다.

하지만 너무나 방대한 데이터에 비해 부족한 시간, 제한적인 컴퓨팅 파워로 인하여 청바지의 온라인 가격 데이터를 분석하여 가까운 미래의 청바지 가격을 예측해보는 프로젝트로 수정하였습니다.

여러 품목 중에 청바지를 선택한 이유는 제공받을 수 있는 데이터가 다른 품목에 비해 많고 사계절 내내 입을 수 있는 품목이기 때문입니다. (휴대전화, TV 품목은 과거 데이터가 부족해서 청바지로 선정하였습니다.)

더 많은 품목에 대해서는 추후 방법을 찾아서 시도해보고자 합니다.

사용 데이터

https://www.data.go.kr/dataset/15004449/fileData.do

온라인 수집가격 정보 데이터는 온라인에서 수집된 가격정보, 수집 일자, 품목명, 판매 가격 등 총 8개 항목으로 이루어진 데이터입니다.

데이터 수집기간은 2014년 01월부터 2019년 10월까지의 데이터로 제공됩니다.

제가 분석에서 사용한 데이터는 2015년 01월 ~ 2019년 10월까지의 데이터를 사용하였습니다.

데이터 분석 프로세스

  1. 데이터 정제
  2. 데이터 살펴보기
  3. 분석을 위한 추가적인 데이터 정제
  4. 예측을 위한 모델 선정
  5. 데이터 예측
  6. 예측 평가 (RMSE)
  7. 의미 분석

데이터 정제

아주 많은 데이터 양 (약 1억개의 데이터)에 비해 부족한 컴퓨팅 파워 (제 노트북..)으로 인하여 날짜별 하루 판매 가격 평균을 사용하기로 결정하였습니다.

온라인 수집가격 정보 데이터
데이터 정제 Code

코드를 설명하자면, 날짜별로 데이터를 불러와서 8개의 항목 중 수집 일자, 품목명, 판매 가격만을 추출하였습니다. 그중에서 품목 명이 청바지인 데이터만을 찾아서 평균을 구한 후, 새로운 데이터에 작성하는 코드입니다.

생각보다 많은 양의 데이터를 작업하는 거라, 꽤 걸렸습니다. 1년 데이터 기준 30분 이상 걸렸는데, cpu 좋은 컴퓨터는 더 빠를 것 같습니다.

위의 코드를 사용하여 각 날짜별 하루 청바지 판매가격의 평균을 계산하여 정제된 데이터 세트를 얻었습니다.

정제를 통해 얻은 새로운 데이터 세트

데이터 살펴보기

청바지 판매가격 평균 그래프

사용하기 위해 직접 만든 청바지 판매가격 평균 그래프입니다.

데이터 요약은 아래와 같습니다.

데이터 요약

  • 품목 : 청바지
  • 기간 : 2015년 01월 01일 ~ 2019년 10월 31일 ( 총 1765일)
  • 최솟값 : 32732원
  • 평균값 : 51998원
  • 최댓값 : 166220원

연도별 청바지 판매가격 평균 그래프

위의 그래프는 연도별로 겹쳐서 표현한 그래프입니다. 딱히 뭔가 잘 보이지는 않습니다.

분석을 위한 추가적인 데이터 정제

새로 생성한 청바지의 판매 가격 평균 데이터를 가져와서, 더 좋은 학습 결과를 위해 공백을 메워 줍니다.

% 청바지 평균값 데이터 정제
jean_data = readtable('mean\tt.xlsx');
%    Fill the NaN value with the Nearest value.
jean_data.sales_price = fillmissing(jean_data.sales_price, 'nearest');
lenofdata = length(jean_data.sales_price);

for i=1 : length(jean_data.collect_day)
    jean_data.collect_day(i) = strip(jean_data.collect_day(i),"'");
end

Y = jean_data.sales_price;
data = Y';

matlab에서는 fillmissing이라는 함수로 데이터의 공백을 처리합니다. 이때 가장 근처의 값인 'nearest'값을 파라미터로 넣어주었는데 이것은 데이터에게 추세가 있다고 생각하였기 때문입니다. (급격한 변화 데이터가 아니라고 생각하였습니다.) 그 아래의 코드는 string 문자에 있는 특수문자를 지우는 용도로 strip함수를 사용하였습니다.

%   2015.01.01 ~ 2019.05.06 (90%) : Training Data Set
%   2019.05.07 ~ 2019.10.31 (10%) : Test Data Set
numTimeStepsTrain = floor(0.9*numel(data));
dataTrain = data(1:numTimeStepsTrain+1);
dataTest = data(numTimeStepsTrain+1:end);

모델을 잘 학습 시키고 이것을 평가하기 위해서 Training dataset과 Test dataset을 구분하였습니다. 데이터양이 아주 많은 것도 아니기 때문에 9:1의 비율로 나누어서 결과가 좀 더 잘 나오도록 해보았습니다.

%   Normalize sales_price to a value between 0 and 1 (Training Data Set)
mu = mean(dataTrain);
sig = std(dataTrain);
dataTrainStandardized = (dataTrain - mu) / sig;
XTrain = dataTrainStandardized(1:end-1);
YTrain = dataTrainStandardized(2:end);

또한, 판매가격은 수치가 꽤 큰 숫자입니다. (만단 위) 그러므로 저는 더 학습이 잘되도록 Normalization을 수행하였습니다. 수치를 0과 1 사이의 상대적인 값으로 바꾼 후에 학습을 시키는 것입니다.

예측을 위한 모델 선정

저는 LSTM(Long Short-Term Memory models) 모델을 선정하여 해당 데이터에 적용해보기로 했습니다. 제가 만든 데이터는 하루 단위의 날짜 순서로 이루어져 있습니다. 이것을 'Time Series'데이터라고 하는데, 이것도 Sequence 데이터 종류 중 하나입니다. Sequence 종류의 데이터는 RNN(Recurrent Neural Networks)이라는 딥러닝 모델을 주로 사용합니다.

RNN

하지만, 사용하고자 하는 데이터는 시간이 중요한 작용을 해야하는데, 기존의 RNN 같은 경우엔, 점점 초반의 데이터와 멀어질수록 연관성이 적어지는 망각 현상이 나타납니다.

RNN에서의 Time Series Data의 문제점

LSTM은 은닉층의 메모리 셀에 입력 게이트, 출력 게이트를 추가하여 불 필요한 기억을 지우고, 기억해야 할 것들을 정합니다. 그래서 RNN보다 Time Series에 적합하도록 개선된 것을 LSTM이라고 합니다.

RNN, LSTM 비교

자세한 알고리즘 설명은 따로 딥러닝을 공부하면서 추가로 정리하도록 하겠습니다.

Matlab에서는 아래와 같은 코드로 LSTM 옵션을 설정합니다. 해당 코드는 기존 Matlab LSTM 튜토리얼에서 MaxEpochs를 500으로 증가시킨 코드입니다.

%LSTM Net Architecture Def
numFeatures = 1;
numResponses = 1;
numHiddenUnits = 200;
layers = [ ...
    sequenceInputLayer(numFeatures)
    lstmLayer(numHiddenUnits)
    fullyConnectedLayer(numResponses)
    regressionLayer];
options = trainingOptions('adam', ...
    'MaxEpochs',500, ...
    'GradientThreshold',1, ...
    'InitialLearnRate',0.005, ...
    'LearnRateSchedule','piecewise', ...
    'LearnRateDropPeriod',125, ...
    'LearnRateDropFactor',0.2, ...
    'Verbose',0, ...
    'Plots','training-progress');

위의 옵션을 사용하여 아래와 같이 Network를 훈련시킬 수 있습니다. Matlab 코드는 아래의 코드와 같습니다.

%    Train LSTM Net
net = trainNetwork(XTrain,YTrain,layers,options);

위의 코드들을 실행하면 아래의 화면과 같이 모델을 학습하게 됩니다. Training dataset 내부에서 자체적으로 평가하면서 반복적으로 학습을 진행합니다.

LSTM Net Architecture 모델 학습 Progress (Epoch:250)

데이터 예측

훈련을 시키기 전에 Training 데이터를 Normalization 하였습니다. Test 데이터도 Normalization 시켜줍니다. 그리고 테스트 데이터 세트에 대해서 예측을 시작합니다.

%    Normalize sales_price to a value between 0 and 1 (Testing Data Set)
dataTestStandardized = (dataTest - mu) / sig;
XTest = dataTestStandardized(1:end-1);
net = predictAndUpdateState(net,XTrain);
[net,YPred] = predictAndUpdateState(net,YTrain(end));

%   Predict as long as the test period (2019.05.07 ~ 2019.10.31)
numTimeStepsTest = numel(XTest);
for i = 2:numTimeStepsTest
    [net,YPred(:,i)] = predictAndUpdateState(net,YPred(:,i-1),'ExecutionEnvironment','cpu');
end

Test데이터만큼 for문을 돌면서 예측된 값을 YPred에 넣고, net을 업데이트 및 초기화하는 코드였습니다.

예측 평가 (RMSE)

YPred에는 예측된 값이 YTest에는 실제 정답이 들어 있습니다. 모델의 성능을 평가하기 위해 해당 데이터들의 오차를 확인하는데, MSE 또는 RMSE를 많이 사용합니다. 여기서는 RMSE를 사용하겠습니다. (설명은 다음에 추가하도록 하겠습니다.)

%   RMSE calculation of test data set
YTest = dataTest(2:end);
YTest = (YTest - mu) / sig;
rmse = sqrt(mean((YPred-YTest).^2))

결과는 아래와 같습니다.

Epoch : 250 Test set과 Prediction 오차 비교

Epoch를 250으로 주었을때 RMSE : 9192.5498 (0과 1 사이로 표준화 시, 약 0.4)

Epoch : 500 Test set과 Prediction 오차 비교

Epoch를 500으로 주었을때 RMSE : 7956.2427 (0과 1 사이로 표준화 시, 약 0.3)

결과 및 의미 분석

Normalization 했던 것을 풀어주고, 그래프로 나타내어 보겠습니다.

%    Denormalize Data
YPred = sig*YPred + mu;
YTest = sig*YTest + mu;

%   X Label : Collect Day
x_data = datetime(jean_data.collect_day);
x_train = x_data(1:numTimeStepsTrain+1);
x_train = x_train';
x_pred = x_data(numTimeStepsTrain:numTimeStepsTrain+numTimeStepsTest);

%   Train + Predict Plot
figure
plot(x_train(1:end-1),dataTrain(1:end-1))
hold on
plot(x_pred,[data(numTimeStepsTrain) YPred],'.-')
hold off
xlabel("Collect Day")
ylabel("Sales Price")
title("Forecast")
legend(["Observed" "Forecast"])

%   Train + Test + Predict Plot
figure
plot(x_data,Y)
hold on
plot(x_pred,[data(numTimeStepsTrain) YPred],'.-')
hold off
xlabel("Collect Day")
ylabel("Sales Price")
title("Compare Data")
legend(["Raw" "Forecast"])

아래는 Epoch 250 학습하였을 때의 Train + Predict + Test 그래프입니다.

Train + Predict + Test Plot (Epoch : 250)

아래는 Epoch 500 학습하였을 때의 Train + Predict 그래프입니다.

Train + Predict Plot (Epoch : 500)

의미를 해석해 보겠습니다. 예측이 시작된 시점부터 가까운 거리의 예측은 상대적으로 오차가 적은 것이 보입니다. 이것은 LSTM의 특징으로서, 가까운 시일의 영향이 크다는 것을 알 수 있습니다. 뒤로 갈수록 오차가 커지는 것은 점차 오차가 쌓여가기 때문이라고 해석됩니다. (틀린 해석이면 댓글 부탁드립니다. 수정하겠습니다.)

하나의 품목인 '청바지' 가격만을 예측하였지만, 이를 시간과 컴퓨팅 파워가 허락한다면, 모든 품목의 모든 데이터로 확장하여, 주식 예측과 비슷하게 상품 시세 또한 충분히 예측할 수 있겠다고 생각하였습니다. 이를 통해 사고 싶은 물건의 시세를 미리 파악해보거나 적절한 가격을 매기는데 도움이 될 것이라고 생각합니다.

아쉬운 점

2015년 데이터가 상대적으로 높게 나왔습니다. 해당 데이터를 제외하고 학습을 시켰다면 더 좋은 결과가 나오지 않을까라는 생각도 됩니다.

그리고 기회가 된다면, 다른 품목들도 시도해보고 싶습니다.

프로젝트 코드

실제 코드는 제 깃허브 레포지토리에 LSTM-Prediction이라는 제목으로 올렸습니다.

처음의 Raw 데이터는 없고, 정제를 위한 코드와, 정제된 데이터, 학습을 위한 코드가 있습니다.

https://github.com/harimkang/LSTM-Prediction

 

harimkang/LSTM-Prediction

LSTM Prediction for time series data (jean sales data set) using matlab - harimkang/LSTM-Prediction

github.com

Reference

RNN 및 LSTM 이미지 : https://ratsgo.github.io/natural language processing/2017/03/09/rnnlstm/ 
해당 프로젝트로 소프트웨어학과의 고급 컴퓨터 수학 A+ 받았습니다! 후후..!

 

관련글 더보기