AMPds2 Dataset
df['date'] = pd.to_datetime(df['unix_ts'], unit='s', utc=True)
df = df[['date', 'P']]
start_date = pd.to_datetime('2012-04-01 07:00:00+00:00')
# 1년 뒤 날짜
end_date = start_date + pd.DateOffset(years=1)
# 해당 기간 동안의 데이터 필터링
df = df[(df['date'] >= start_date) & (df['date'] < end_date)]
df.set_index('date', inplace=True)
df.head()
df.to_csv('filtered_electricity_data.csv')
원하는 형식으로 Formatting
EDA
800이 넘어가는 이상치가 존재함을 확인.
그 수가 8개로 적기에, 제거하는 방식 선택
date가 끊기는 부분이 없으며, 결측치 또한 없음을 확인.
MinMaxScaler( ) 적용. (정규화)
훈련 데이터 80%, 테스트 데이터 20% 로 나누었음.
LSTM
기존에 만들어 두었던 LSTM 모델 활용.
하이퍼 파라미터 및 변수들 목록과 값
- input_size = 1
- hidden_size = 50 (50단위로 설정)
- num_layers = 2 (default 1)
- output_size = 1
- dropout = 0.2 (default 0)
- num_epochs = 5 (DataSet의 크기가 클수록 적게, 보통 10~100)
- learning_rate = 0.001 (0.1, 0.001과 같은 값들을 사용)
- window_size = 10(데이터 해상도에 따라 상이)
- batch_size = 32 (2^n 사용, 보통 16,32,64 중 하나 사용)
*해당 코드에서 사용하는 window_size와 batch_size 구분
window_size : 시계열 데이터에서 한 번에 처리하는 데이터 포인트의 수
ex ) 주어진 데이터 [1,2,3,4,5]에서 윈도우 크기 3을 설정하면, [1,2,3], [2,3,4]... 형태로 데이터를 처리
batch_size : 학습 중 한 번에 처리하는 데이터 샘플의 수
ex) 배치 크기 32를 설정하면, 32개씩 데이터를 처리하면서 가중치를 업데이트
window_size 를 60으로 변경 -> 1분당 data point한개이므로, 1시간 단위로 예측을 수행
window_size 를 1440으로 변경 -> 1분당 data point한개이므로, 하루간격으로 예측을 수행
10,60,1440 에서 모두 비슷한 성능을 보여줌.
rmse : 0.13228756555322957 mae : 0.125
Total Score: 98.71/100
GRU
위 LSTM의 사진과 거의 유사해보일 수 있지만, 자세히 보면 조금 다르다..!
rmse : 0.06043122708797455 mae : 0.01970047317445278 Total Score: 99.60/100
테스트 데이터셋 이후를 해보려 했는데..
왜... 뒷 데이터는 예측하지를 못하니.....
다시 LSTM부터 제대로 해보자..
df_resampled = df.resample('15T').mean() # '15T'는 15분 단위로 그룹화
1년반동안의 데이터를 사용하기로 다시 결정...
80% 훈련, 20% 테스트.
15분단위로 샘플링. date값은 시작값으로 설정.
window_size : 7 * 24 * 4 (일주일 단위로 수행되도록)
예측 수행 코드
import torch
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from torch.utils.data import DataLoader, Subset
def predict_next_day(model, initial_sequence, scaler, window_size, num_predictions=96, device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')):
model.eval()
predictions = []
seqs = initial_sequence.to(device) # 초기 시퀀스 설정
with torch.no_grad():
for _ in range(num_predictions):
outputs = model(seqs) # 현재 시퀀스를 기반으로 예측
predictions.append(outputs[-1].cpu().numpy()) # 마지막 예측값을 저장
# 새로운 입력 시퀀스를 만듦: 현재 시퀀스에서 첫 번째 값 제거 후, 예측값 추가
next_input = outputs[-1].unsqueeze(0).unsqueeze(1) # 예측값을 새로운 입력으로 사용 (차원 조정)
seqs = torch.cat((seqs[:, 1:], next_input), dim=1) # 윈도우 시퀀스를 한칸 이동
# 예측한 데이터를 원래 값으로 되돌림
predictions = scaler.inverse_transform(np.array(predictions).reshape(-1, 1))
return predictions
# 마지막 시퀀스를 추출하여 예측 준비
model.eval()
# 1년 반 데이터에서 마지막 window_size 만큼의 데이터 가져오기
last_window_start_idx = len(dataset) - window_size
last_window_dataset = Subset(dataset, list(range(last_window_start_idx, len(dataset))))
# 마지막 시퀀스를 로드
last_window_loader = DataLoader(last_window_dataset, batch_size=1, shuffle=False)
initial_sequence, _ = next(iter(last_window_loader)) # 마지막 시퀀스만 가져옴
# 모델을 GPU로 전환
model = model.to(device)
# 1일(96 포인트) 예측 수행
predictions = predict_next_day(model, initial_sequence, scaler, window_size, num_predictions=96, device=device)
# 예측 시작 날짜 설정 (마지막 데이터 이후의 첫날)
start_date = pd.to_datetime('2013-10-01 06:45:00+00:00')
# 15분 간격의 날짜 생성 (96개의 포인트 = 1일)
date_range = pd.date_range(start=start_date, periods=96, freq='15T')
# Pandas 데이터프레임으로 변환 (날짜 열 추가)
df = pd.DataFrame({
'date': date_range,
'predictions': predictions.flatten() # predictions을 1차원으로 변환
})
# 예측 결과 시각화 (날짜를 x축으로 사용)
plt.figure(figsize=(12, 6))
plt.plot(df['date'], df['predictions'], label='Predicted Data for Next Day')
plt.title('Predicted Data for Next 24 Hours (15 min intervals)')
plt.xlabel('Date')
plt.ylabel('Predictions')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()
1년반 뒤, 하루의 데이터를 예측 후 실제 값과 비교해보았다.
from(bucket: "AMPds2")
|> range(start: 2013-10-01T06:45:00Z, stop: 2013-10-02T06:45:00Z)
|> filter(fn: (r) => r["_measurement"] == "Electricity_B2E.csv_measurements")
|> filter(fn: (r) => r["_field"] == "active_power")
|> aggregateWindow(every: 15m, fn: mean)
|> keep(columns: ["_time", "_value"]) // _time과 _value 칼럼만 유지
|> yield(name: "mean")
InfluxDB에서 실제 데이터를 가져오기 위한 Flux 쿼리.
값의 크기가 꽤 달랐고...
피크 부분에 대한 감지도 되지 않고 있음을 확인할 수 있었다...
예상 문제
1. 데이터 값의 크기가 다르기에.. 데이터 스케일이 맞지 않음.
2. 피크 감지가 안되었는데, 해당 시간(데이터포인트)까지의 예측은 수행하기 어려움.
Test data 너머를 예측하는 것은 너무도 어려운 것 같다...
예측 범주를 줄여보니,
여전히 값의 크기는 2정도의 오차가 있지만, 증가와 감소의 경향성은 어느 정도 따라감을 확인해 볼 수 있었다.
이전 데이터의 실제 값이 6, 10, 6, 20, 20으로 마지막에 다소 증가했는데, 이러한 점이 반영된걸까 싶기도 하다.
그러나 테스트 데이터에서 보여주었던 성능에 비교해서는 너무도 아쉬운 것이 사실이다.
잘못 생각하고 있던 걸까?
훈련데이터 80퍼센트를 훈련했을 뿐,
예측한 Test data 20%는 훈련시키지 않았다.
즉, 20%만큼의 기간을 건너뛴 뒤를 나는 예측하고자 했던게 아닐까..?
9개월을 학습시키고, 1년 뒤를 예측하려 하면...
그럼에도 20%부분에서 결과가 잘 나왔기에, 갑작스레 성능이 떨어지는 면에 있어 아쉽다.
기존 학습된 모델에 대해
테스트 데이터 셋으로 파인튜닝을 진행한 후,
그 이후의 데이터를 예측하며, 나온 결과를 다시 입력으로 넣은 후 재 학습 하는 과정을 반복하면
보다 좋은 예측을 할 수 있지 않을까 생각해본다...
test_loader를 훈련 함수에 넣는 것은 어렵지 않지만,
이후의 데이터에 있어 지속적으로 data_loader를 만들어주고, 재 학습시킴에 있어 코드 구현에 어려움이 있다..