데이터 한 그릇

10)시계열 딥러닝 ANN 본문

시계열 분석/Practical TIme Series Analysis

10)시계열 딥러닝 ANN

장사이언스 2022. 11. 4. 09:43

(한양대학교 BI수업 참조)

 

날씨 관련 데이터를 활용하여 간단한 전처리부터, ANN모델까지 돌려볼 예정이다.

task 는 날씨 관련 특징을 예측하는 작업을 수행한다

 


#Read the dataset into a pandas.DataFrame
df = pd.read_csv('PRSA_data_2010.1.1-2014.12.31.csv')
print('Shape of the dataframe:', df.shape)

 

데이터를 읽은 결과 따로 datetime type 의 칼럼이 존재하는 게 아니라, year, month, day 칼럼으로 날짜 feature 가 나뉘어져 있음을 알 수 있다

시계열적인 분석을 하기 위해서는 datetime 이 필요하기 때문에 year, month, day feature를 활용하여 datetime 타입을 가지는 "datetime" 칼럼을 만들어야한다

 

 

# Index creation
df['datetime'] = df[['year', 'month', 'day', 'hour']].\
    apply(lambda row: datetime.datetime(year=row['year'], \
    month=row['month'], day=row['day'],hour=row['hour']), axis = 1)
df.sort_values('datetime', ascending=True, inplace=True)
df.head()

 

year, month, day, hour 칼럼에 접근하여 "datetime" 패키지를 활용해서 datetime 칼럼을 만든다.

오름차순으로 sorting 한다

 

g = sns.boxplot(df['PRES'])
g.set_title('Box plot of Air Pressure')

 

g = sns.lineplot(x='datetime',y='PRES',data=df)
g.set_title('Time series of Air Pressure')
g.set_xlabel('Year')
g.set_ylabel('Air Pressure readings in hPa')

 

 

시계열적으로 'PRES' 를 lineplot 그려보니 계절성을 가지고 있고 정상성을 가지고 있는 데이터임을 확인할 수 있었다.

즉, 평균과 분산이 시점에 상관없이 일정함을 살펴볼 수 있었다

 

# Minmax scaling PRES variable
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
df['scaled_PRES2'] = scaler.fit_transform(np.array(df['PRES']).reshape(-1,1))
df.head()

 

PRES 에 대해서 모든 데이터들을 0 ~ 1 사이로 반환하는 MinMaxScaler 를 활용한다

 

split_date = datetime.datetime(year=2014, month=1, day=1, hour=0)
df_train = df.loc[df['datetime']<split_date]
df_test = df.loc[df['datetime']>=split_date]

 

일반적인 데이터를 훈련 데이터와 테스트 데이터로 나눌 때는 사이킷런 model_selection 의 train_test_split을 활용하여 train 과 test 로 나눈다

이 경우에는 할당된 %에 랜덤으로 train, test set을 나누는 작업이지만, 이와 같은 방식으로 시계열 데이터를 나누게 되면 문제가 발생한다

왜냐하면, 시계열 데이터는 연속성의 특징을 가지고 있기 때문이다. 이전 3일의 데이터를 활용하여 현재 데이터를 예측하는 경우 이전 3일의 데이터의 경우 연속성을 유지해야만한다, 하지만 train_test_split 을 활용하게 되면 시계열 데이터가 연속성을 유지할 수 없다

 

따라서 split_date 변수에 train set과 test set 을 나누는 기준 날짜를 정하고 그 기준으로 train 과 test 를 나눈다

 

df_test.reset_index(drop=True, inplace=True)
df_test.head()

 

train과 test셋을 split_date 변수를 활용하여 나누게 되면 index 가 서로 일치하지 않기 때문에 index 를 맞춰준다

 

g = sns.lineplot(x='datetime',y='scaled_PRES',data=df_train, color='b')
g.set_title('Time series of scaled Air Pressure in train set')
g.set_xlabel('Index')
g.set_ylabel('Scaled Air Pressure readings')

 

train 데이터의 scaled 된 pres 를 다시 그려보니 아래와 같았다

test 데이터의 scaled 된 pres를 그려보니 아래와 같았다

 

def makeXy(ts, nb_timesteps):
    X = []
    y = []
    for i in range(nb_timesteps, ts.shape[0]): 
        X.append(list(ts.loc[i-nb_timesteps:i-1]))
        y.append(ts.loc[i])                    
    X, y = np.array(X), np.array(y)
    return X, y

 

가장 중요한 부분이라고 할 수 있는데, 함수의 ts는 데이터 즉, 타임 시리즈를 의미하고 nb_timesteps는 사용할 lag를 의미한다

만일 timesteps을 7로 보고싶으면 함수에 7을 집어 넣으면 되는데, 밑에 for문을 살펴보면,,

만일 timeseries가 3000건이라고 한다면  range는 range(7, 3000) 이 된다

맨 처음 i는 7이 되고, 따라서 loc[i-nb_timesteps:i-1], loc[7-7:6] => loc[0:6] 가 된다

이 값이 x에 담기게 되는데 결국 이는 0,1,2,3,4,5,6 까지의 7개 학습 데이터가 담겼다고 할 수 있다

y에는 7번째 데이터가 담기게 되어 target데이터가 담기게 된다

즉, for문으로 지속적으로 이전 7개 데이터와 타겟 1개 데이터가 y에 순차적으로 담기게 된다

 

#train
X_train, y_train = makeXy(df_train['scaled_PRES'], 7)
print('Shape of train arrays:', X_train.shape, y_train.shape)
print(X_train[0])
print(y_train[0])
print(df.shape)

#test
X_test, y_test = makeXy(df_test['scaled_PRES'], 7)
print('Shape of Test arrays:', X_test.shape, y_test.shape)
print(X_test[0])
print(y_test[0])

 

train 과 test 셋에 대해 makeXy 함수를 돌린다

 

from keras.layers import Dense, Dropout
from keras.models import Sequential

model = Sequential()
model.add(Dense(32, activation='relu', input_shape=(7,)))
model.add(Dense(16, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(1, activation='linear'))

 

모델을 생성하는데 해당 모델은 은닉층을 3 layer로 쌓고 각 노드는 32, 16, 16 으로 설정했으며 활성화 함수로는 relu 함수를 사용했다

입력 shape 은 이전 7개의 데이터를 활용하여 학습하기 때문에 shape을 (7,) 로 맞춘다

dropout을 0.2 %를 주어 과적합을 방지하고, 출력층을 linear 모델을 활성화 함수로 두어 설정한다

 

model.compile(loss='mean_squared_error', optimizer='adam')
model.summary()

 

손실 함수로는 mse(mean squared error) 를 사용하고 최적화 방식으로는 adam 방식을 사용한다

 

모델의 구조를 보면 위와 같다

 

history=model.fit(X_train, y_train, batch_size=16, epochs=20,
             verbose=1, validation_split=0.3,
             shuffle=True)

 

batch size를 16, epoch을 20을 주어 돌려본다

 

from sklearn.metrics import r2_score
r2 = r2_score(df_test['PRES'].loc[7:], pred_PRES)
print('R-squared for the test set:', round(r2,4))

 

R^2 를 살펴보니 0.9796 이 도출됐다

 

plt.plot(range(50), df_test['PRES'].loc[7:56], linestyle='-', marker='*', color='r')
plt.plot(range(50), pred_PRES[:50], linestyle='-', marker='.', color='b')
plt.legend(['Actual','Predicted'], loc=2)
plt.title('Actual vs Predicted Air Pressure')
plt.ylabel('Air Pressure')
plt.xlabel('Index')

 

 

실제 데이터와 예측한 데이터를 lineplot 을 그려보니 위의 그림과 같았다

지금까지는 간단한 ANN 모델을 짜서 예측을 해봤는데, 다음으로는 CNN 을 활용하여 예측 모델을 짜볼 예정이다

Comments