Air Pollution Forecasting
데이터셋 : Kaggle의 Air Pollution Forecasting - LSTM Multivariation
Air Pollution Forecasting - LSTM Multivariate
Lstm multivariate sample dataset for architecture design and orchestration
www.kaggle.com
목표 : LSTM을 활용해 pollution 수치 예측, 시계열 데이터 올바르게 전처리하기
!pip install torchmetrics
!pip install optuna
!pip install torchviz
import zipfile
import os
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OneHotEncoder
import numpy as np
import optuna
from torchviz import make_dot
1.파일 압축해제/불러오기
zip_filename = 'air_pollution.zip'
extract_folder = './air_pollution'
os.makedirs(extract_folder, exist_ok=True)
with zipfile.ZipFile(zip_filename, 'r') as zipf:
zipf.extractall(extract_folder)
2. 데이터 EDA
압축을 해제해보니, train 데이터와 test 데이터로 보이는 제목의 csv 문서가 2개 있어서 살펴보았다.
train_path = './air_pollution/LSTM-Multivariate_pollution.csv'
test_path = './air_pollution/pollution_test_data1.csv'
#train 데이터 살펴보기
train_df = pd.read_csv(train_path)
train_df.info()

train_df.head()

train_df.tail()

2010년 ~ 2014년의 데이터셋인 것을 확인했다.
Column 정보 :
- date : 날짜
- pollution : 오염 정도(타겟 column)
- dew : 이슬점(공기 중 수증기가 포화되어 응결되기 시작되는 온도)
- temp : 온도
- press : 기압
- wind_dir : 풍향
- wnd_spd : 풍속
- snow : 적설량
- rain : 강우량
이후 수치형 column(date, win_dir column 제외하고 모든 column)의 형태를 보기 위해 시각화를 진행했다.
train_df.hist(bins = 30, figsize = (20, 20))
plt.tight_layout()
plt.show()

EDA를 위해 copy 생성
train_df_eda = train_df.copy()
유일한 범주형 column인 wnd_dir column을 보았다.
train_df_eda['wnd_dir'].value_counts()

-> CV는 SW방향이라고 판단
이후 object 타입의 date column을 시간을 나타내는 datetime type으로 바꿔주어, 시계열 데이터의 형태를 띄도록 했다.
train_df_eda['date'] = pd.to_datetime(train_df_eda['date'], format = '%Y-%m-%d %H:%M:%S')
train_df_eda.sort_values(by = 'date', inplace = True)
#시간에 따른 pollution 시각화
df_2010 = train_df_eda[train_df_eda['date'].dt.year == 2010]
plt.figure(figsize = (20, 10))
plt.plot(df_2010['date'], df_2010['pollution'])
plt.xlabel('date')
plt.ylabel('Pollution')
plt.title('Pollution Over Time(2010)')
plt.show()

train 데이터만 살펴보았을 때는 여름과 겨울의 pollution 수치가 달라지는 것을 관찰할 수 있어서 계절에 해당하는 column을 추가하려 했지만 test 데이터의 column을 보고 결정하기로 했다.
#test 데이터 info
test_df = pd.read_csv(test_path)
test_df.info()

test 데이터에는 date데이터가 없어서 test 데이터셋에서는 계절 특성 추출이 불가능하였고, 따라서 특성 일치를 위해서 추가하지 않기로 판단했다.
3. 데이터셋 구축 / 데이터로더 정의
- train 데이터와 validation 데이터의 경우에는 랜덤으로 나누지 않고, 2014년의 데이터를 validation 데이터로 활용하고 2014년 이전의 데이터를 train 데이터로 활용했다.
- train/val 데이터셋과 test 데이터셋의 클래스를 따로 정의하여 가독성과 유지보수성을 높였다.
Train/Val Dataset
#Train, Validation 데이터셋 구축
class PollutionDataset_Train_Val(Dataset):
def __init__(self, csv_path, seq_len, mode = 'train', seq_x_scaler = None, encoder = None, y_scaler = None) :
self.seq_len = seq_len
df = pd.read_csv(csv_path)
#dateTime 타입으로 변경 후 sort
df['date'] = pd.to_datetime(df['date'], format = '%Y-%m-%d %H:%M:%S')
df.sort_values(by = 'date', inplace = True)
seq_x = ['dew', 'temp', 'press', 'wnd_spd', 'snow', 'rain']
cate_x = ['wnd_dir']
target = ['pollution']
if mode == 'train': #Train Dataset
df_subset = df[df['date'].dt.year < 2014]
else: #Validation Dataset
df_subset = df[df['date'].dt.year >= 2014]
if mode == 'train' : #Train Dataset
self.seq_x_scaler = StandardScaler()
self.encoder = OneHotEncoder(sparse_output = False)
self.y_scaler = StandardScaler()
self.seq_x = self.seq_x_scaler.fit_transform(df_subset[seq_x])
self.cate_x = self.encoder.fit_transform(df_subset[cate_x])
self.X = np.concatenate([self.seq_x, self.cate_x], axis = 1)
self.Y = self.y_scaler.fit_transform(df_subset[target])
else : #Validation Dataset
self.seq_x_scaler = seq_x_scaler
self.encoder = encoder
self.y_scaler = y_scaler
self.seq_x = self.seq_x_scaler.transform(df_subset[seq_x])
self.cate_x = self.encoder.transform(df_subset[cate_x])
self.X = np.concatenate([self.seq_x, self.cate_x], axis = 1)
self.Y = self.y_scaler.transform(df_subset[target])
def __len__(self) : #슬라이싱 위해 len - seq_len
return len(self.X) - self.seq_len
def __getitem__(self, idx) : #윈도우 슬라이싱
x = self.X[idx : idx + self.seq_len]
y = self.Y[idx + self.seq_len]
return torch.tensor(x, dtype = torch.float32), torch.tensor(y, dtype = torch.float32)
Test Dataset
#Test Dataset 구축
class PollutionDataset_Test(Dataset):
def __init__(self, csv_path, seq_len, seq_x_scaler, encoder, y_scaler) :
self.seq_len = seq_len
df = pd.read_csv(csv_path)
#Date Column이 없으므로 처리 불필요
seq_x = ['dew', 'temp', 'press', 'wnd_spd', 'snow', 'rain']
cate_x = ['wnd_dir']
target = ['pollution']
self.seq_x_scaler = seq_x_scaler
self.encoder = encoder
self.y_scaler = y_scaler
self.seq_x = self.seq_x_scaler.transform(df[seq_x])
self.cate_x = self.encoder.transform(df[cate_x])
self.X = np.concatenate([self.seq_x, self.cate_x], axis = 1)
self.Y = self.y_scaler.transform(df[target])
def __len__(self) :
return len(self.X) - self.seq_len
def __getitem__(self, idx) :
x = self.X[idx : idx + self.seq_len]
y = self.Y[idx + self.seq_len]
return torch.tensor(x, dtype = torch.float32), torch.tensor(y, dtype = torch.float32)
Train Dataset / Validation Dataset 가져오기
#1시간마다 기록되어있으므로, 일주일을 하나의 seq로 잡음(24 * 7)
seq_len = 168
train_ds = PollutionDataset_Train_Val(train_path, seq_len)
val_ds = PollutionDataset_Train_Val(train_path, seq_len, mode = 'val',
seq_x_scaler = train_ds.seq_x_scaler,
encoder = train_ds.encoder,
y_scaler = train_ds.y_scaler)
Train Dataset / Validation Dataset 데이터로더 생성
#DataLodaer 생성
train_loader = DataLoader(train_ds, batch_size = 128, shuffle = True)
val_loader = DataLoader(val_ds, batch_size = 128, shuffle = False)
#배치의 shape 확인 -> [Batch, seq_len, features]
input_x, target_y = next(iter(train_loader))
print(f"Batch X Shape: {input_x.shape}")
print(f"Batch Y Shape: {target_y.shape}")

[배치 크기, seq_len, 특성 개수]로 잘 들어간 것을 확인할 수 있었다.
3. 모델링
예측은 LSTM모델을 사용했고, 이후 베이지안 튜닝을 위해서 파라미터들은 따로 상수로 정의하지 않고 변수로 정의해놓았다.
class LSTMModel(nn.Module) :
def __init__(self, input_size, hidden_size, num_layers, dropout) :
super(LSTMModel, self).__init__()
#LSTM 레이어
self.lstm = nn.LSTM(input_size, hidden_size,
num_layers = num_layers,
batch_first = True, dropout = dropout)
#레이어정규화
self.ln = nn.LayerNorm(hidden_size)
#ReLU 활성화
self.relu = nn.ReLU()
#선형변환 수행(최종 출력)
self.fc = nn.Linear(hidden_size, 1)
def forward(self, x) :
x, _ = self.lstm(x)
#마지막 타입스템 선택
x = x[:, -1, :]
x = self.ln(x)
x = self.relu(x)
output = self.fc(x)
return output'My IT > Codes' 카테고리의 다른 글
| [My IT : Codes] X-ray Image Dataset Classification(1) (시작~ 1차 테스트) (0) | 2026.04.01 |
|---|---|
| [My IT : Codes] LSTM 활용 Air Pollution Forecast 데이터셋 예측(2) 하이퍼파라미터 튜닝 ~ 예측 시각화 (2) | 2026.03.25 |
| [My IT : Codes] AutoEncoder 활용 Denoising (0) | 2026.03.23 |
| [My IT : Codes] Bank-Marketing 데이터 분석(2) (모델 비교 학습 ~ 결론 도출) (1) | 2026.03.09 |
| [My IT : Codes]Bank-Marketing 데이터 분석(1) (데이터셋 설명 ~ 데이터 전처리) (2) | 2026.03.07 |