Keras와 RNN로 하는 주류 판매량 시계열 예측
1. 모듈 임포트 및 데이터셋 불러오기
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import os
%matplotlib inline
warnings.filterwarnings('ignore')
plt.rcParams['font.family'] = 'NanumGothic' # 시각화시 한글깨짐 방지
df= pd.read_csv("C:/Users/Owner/Downloads/Alcohol_Sales.csv",index_col='DATE',parse_dates=True)
df
참고로 데이터는 kaggle에 있는 주류 판매량 데이터셋을 불러옴
https://www.kaggle.com/code/ayushmehar7/alcohol-sales-prediction
Alcohol Sales Prediction
Explore and run machine learning code with Kaggle Notebooks | Using data from (for simple exercises) Time Series Forecasting
www.kaggle.com
2. 주류 판매량 시각화
df.columns =['Sales']
df.plot(figsize=(16,9))
종속 변수를 Sales로 넣고 EDA를 해줌
3. seasonal decomposition
from statsmodels.tsa.seasonal import seasonal_decompose
results = seasonal_decompose(df['Sales'])
results.plot();
그래프가 두쌍이 나왔을 때, 옆에 세미콜론을 붙이면 한쌍만 나옴
results.seasonal.plot(figsize=(16,9))
추세요소를 시각화
4. train test split
len(df)-12
계절성을 반영하기 때문에 12를 빼서 그 기준으로 train/ test 셋으로 분리함.
train = df.iloc[:313]
test = df.iloc[313:]
5. Scaling
신경망 실행시 데이터 스케일링 해야함, 신경망은 편향과 가중치를 계산하기 때문에 심하게 왜곡된 데이터가 있을때 최솟값 최대값 비율 차이가 있음
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaled_train = scaler.fit_transform(train) #train만fit
scaled_test= scaler.transform(test) #test data set을 예측할 이유는 없기 때문
빅분기 준비하면서 엄청많이 타닥거려서 외워부렷다
6. Time Series Generator
시퀀스 데이터를 만들기 위해 timeseriesgenerator 를 심어줍니다.
from keras.preprocessing.sequence import TimeseriesGenerator
n_input = 3
n_features = 1
generator =TimeseriesGenerator(scaled_train,scaled_train,length=n_input,batch_size=2)
같은 간격으로 수집된 데이터 포인트의 시퀀스를 받아들이는 역할을 함. 같은 간격으로 수행되어야 하고, 히스토리의 길이, 스트라이드와 같은 시계열 매개변수가 있어야함.
인풋이 많아질수록 generator의 길이가 더 적어짐. 보통 인풋을 12로 해야 1년의 계절이 나옴. 배치사이즈가 작을수록 훈련이 더 잘됨 1로하면 한번에 한시점만 입력, 너무 크게하면 과적합옴. 시계열 데이터에서는 batch 사이즈 작은게 좋움
7. modeling
X,y =generator[0]
X
array([[[0.03658432],
[0.03649885],
[0.08299855]],
[[0.03649885],
[0.08299855],
[0.13103684]]])
X는 3차원 데이터로 shape을 찍으면 (2,3,1)이 나옴. 시계열 정보의 배치를 신경망 객체로 입력하기 위해 형식화 필요, 넘파이로 하면 형식화 매우 복잡하니까 timeseriesgenerator 쓰는거임
만약 n_input 3을 으로 한다면 generator의 길이 310(325-12-3), generator가 하는일은 원하는 입력 갯수대로 데이터를 가져오고, 바로 다음시점을 만들어 반환,
훈련 시퀀스가 얼마나 길어야 하는지, 바로 예측할 다음 포인트를 정의
입력갯수는 어떻게 정의할까 ? -> 순환 신경망이 계절을 찾을 수 있도록 적어도 12개의 데이터가 필요하다
입력갯수(n_input)가 더 크다는 것은 특징(n_features)이 훨씬 더 많다
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
필요한 모듈 임포트
n_input = 12
n_features = 1
train_generator =TimeseriesGenerator(scaled_train,scaled_train,length=n_input,batch_size=2)
n_input을 12로 설정한 이유는 12개월치 데이터를 13개월치 예측 전 볼 수 있음, n_features =1은 y에 대한 타임스탬프
model = Sequential()
model.add(LSTM(150,activation='relu',input_shape =(n_input,n_features))) #위에서 정의했던 기억하기 , 시계열에서 항상 n_features는 항상 1
model.add(Dense(1)) #뉴런 하나 예측값 직접 출력
model.compile(optimizer='adam',loss='mse')
시계열에서 항상 n_features는 항상 1이고, 뉴런을 몇개 넣을지는 정해져 있지 않기 때문에 뉴런을 100개부터 늘려가면서 성능에 어떤영향을 미치는지 보면서 조정하여 시퀀스 모델을 만든다.
model.summary()
output shape는 모델에 사용된 뉴런의 수
model.fit_generator(train_generator,epochs=25)
에폭수 조정해가면서 loss값 가장 낮은것 찾기
model.history.history.keys() #loss 값 반환
dict_keys(['loss'])
loss값에 대한 EDA
myloss = model.history.history['loss']
plt.plot(range(len(myloss)),model.history.history['loss'])
대략 15근처에서 수렴하기 시작한다.
8. evaluate test data
first_eval_batch = scaled_train[-12:]
12개의 포인트를 지나서 13번째 포인트를 예측할 것임 훈련데이터의 마지막 12개를 뽑아옴
first_eval_batch
my_first_pred = model.predict(current_batch)[0]
1차원 배열 가지게됨 0제거 하면 2차원뜸
my_first_pred
array([0.6840132], dtype=float32)
우리는 0.68보다 앞선 한시점 앞을 예측해야한다.
current_batch
array([[[0.63432772],
[0.80776135],
[0.72313873],
[0.89870929],
[1. ],
[0.71672793],
[0.88648602],
[0.75869732],
[0.82742115],
[0.87443371],
[0.96025301],
[0.5584238 ]]])
첫번째 값을 버리고 예측값을 맨 마지막에 추가할 예정
#forecast using rnn model
test_predictions = [] #예측값 저장할 빈 리스트
# last n_input points from the training set
first_eval_batch = scaled_train[-n_input:] #모델이 지난 포인트 12개를 받아 13번째 포인트를 출력
# RNN이 원하는 shape으로 바꿈-> timeseriesgenerator와 똑같은 format
current_batch = first_eval_batch.reshape((1,n_input,n_features)) #지난 훈련값들의 첫번째 배치, 13번째 값을 예측할 것
#어디까지 예측할 것인지
#len(test)
for i in range(len(test)): #테스트셋 길이 만큼 미래값 예측
current_pred = model.predict(current_batch)[0] #첫번째로 현재 예측값 가져오기,포인트 12보다 앞선다
#예측값 저장
test_predictions.append(current_pred)
#예측값을 포함한 현재 배치값 업데이트
current_batch = np.append(current_batch[:,1:,:],[[current_pred]],axis=1) #이코드 이용해 첫번쨰 예측할거임
마지막줄의 코드를 조금더 뜯어보자면
current_batch[:,1:,:]
참고로 current_batch의 shape은 (1,12,1)이 나온다. 앞선 current_batch를 보면 맨 앞 0.634가 빠졌다. 두번째 인덱스를 1부터 했기 때문에 current_batch[:,1:,:].shape은 (1,11,1)이 나옴.
np.append(current_batch[:,1:,:], [[my_first_pred]],axis=1)
지난 11개값을 받고 예측값을 추가 다음 12개의 포인트가됨
결국 순환신경망은 계속 예측하다보면 예측값을 가지고 예측하게됨. 12개의 지난포인트를 가져와 13번째 포인트를 예측함.
test_predictions
테스트 데이터셋의 예측값 모두 스케일링한 값임
true_predctions = scaler.inverse_transform(test_predictions)
true_predctions
inverse.transform을 통해 스케일링 과정을 반대로 하여 본래의 값을 갖도록함.
test['Predictions']=true_predctions
test
마지막으로 actual과 prediction을 비교하는 EDA를 그려준다
test.plot(figsize=(12,9))
순환 신경망을 훈련할 때 약간의 임의성이 있어서 얼마나 많은 에폭을 훈련함에 따라 신경망이 하는 많은 최적화 과정이 임의적으로 시작됨.
정말 알지 못하는 미래는 예측길이를 늘리면됨
for i in range 숫자 하지만 미래로 갈수록 무의미한 값이 많아지고 예측이 틀릴수도 있음
출처: udemy x kmooc Python, Pandas, Numpy, Statsmodels를 사용해 시계열 데이터를 예측하고 분석하는 방법에 대해 배워보세요!