Spaces:
Sleeping
Sleeping
Update inference.py
Browse files- inference.py +73 -30
inference.py
CHANGED
|
@@ -11,8 +11,9 @@ import json # 👈 JSON 라이브러리 추가
|
|
| 11 |
import sys
|
| 12 |
sys.path.append('.')
|
| 13 |
|
| 14 |
-
from models import TimeXer
|
| 15 |
-
from utils.metrics import metric
|
|
|
|
| 16 |
|
| 17 |
# --- 1. 인자 파싱 (수정 없음) ---
|
| 18 |
parser = argparse.ArgumentParser(description='Time Series Prediction')
|
|
@@ -48,81 +49,123 @@ parser.add_argument('--freq', type=str, default='t', help='freq for time feature
|
|
| 48 |
args = parser.parse_args()
|
| 49 |
|
| 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
# --- 2. 공통 함수: 모델 및 스케일러 로드 (수정 없음) ---
|
| 52 |
def load_model_and_scaler(args):
|
| 53 |
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 54 |
-
# ⭐️ 수정 사항 2: args에 device 정보 추가 (TimeXer 모델 초기화 시 필요할 수 있음)
|
| 55 |
args.device = device
|
| 56 |
model = TimeXer.Model(args).float().to(device)
|
| 57 |
model.load_state_dict(torch.load(args.checkpoint_path, map_location=device))
|
| 58 |
model.eval()
|
| 59 |
scaler = joblib.load(args.scaler_path)
|
| 60 |
-
# 진행 상황을 stderr로 출력하여 stdout의 JSON 결과와 분리
|
| 61 |
print(f"Using device: {device}", file=sys.stderr)
|
| 62 |
print("Model and scaler loaded successfully.", file=sys.stderr)
|
| 63 |
return model, scaler, device
|
| 64 |
|
| 65 |
-
# --- 3. 모드 1: 단일 미래 예측 함수
|
| 66 |
def predict_future(args, model, scaler, device):
|
| 67 |
-
# ... (이전과 동일한 코드) ...
|
| 68 |
-
# 이 함수는 예측 결과(prediction)만 반환하면 됩니다.
|
| 69 |
df_input = pd.read_csv(args.predict_input_file)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
| 73 |
|
|
|
|
|
|
|
| 74 |
input_scaled = scaler.transform(raw_input)
|
| 75 |
batch_x = torch.from_numpy(input_scaled).float().unsqueeze(0).to(device)
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
with torch.no_grad():
|
| 78 |
-
|
| 79 |
-
# 여기서는 batch_x만 필요하다고 가정. 필요 시 batch_x_mark 등 추가
|
| 80 |
-
outputs = model(batch_x)
|
| 81 |
|
| 82 |
prediction_scaled = outputs.detach().cpu().numpy()[0]
|
| 83 |
|
| 84 |
-
# 스케일 복원
|
| 85 |
-
if
|
| 86 |
padding = np.zeros((prediction_scaled.shape[0], scaler.n_features_in_ - args.c_out))
|
| 87 |
-
# 예측 결과를 마지막 feature 자리에 위치
|
| 88 |
prediction_padded = np.concatenate((padding, prediction_scaled), axis=1)
|
| 89 |
prediction = scaler.inverse_transform(prediction_padded)[:, -args.c_out:]
|
| 90 |
else:
|
| 91 |
prediction = scaler.inverse_transform(prediction_scaled)
|
| 92 |
-
|
| 93 |
return prediction
|
| 94 |
|
| 95 |
-
|
| 96 |
-
# --- 4. 모드 2: 전체 기간 롤링 평가 함수 (수정 없음) ---
|
| 97 |
def evaluate_performance(args, model, scaler, device):
|
| 98 |
-
# ... (이전과 동일한 코드) ...
|
| 99 |
-
# 이 함수는 예측값들과 실제값들을 반환하면 됩니다.
|
| 100 |
df_eval = pd.read_csv(args.evaluate_file)
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
| 104 |
data_scaled = scaler.transform(raw_data)
|
|
|
|
|
|
|
| 105 |
|
| 106 |
preds_unscaled = []
|
| 107 |
trues_unscaled = []
|
| 108 |
|
| 109 |
num_samples = len(data_scaled) - args.seq_len - args.pred_len + 1
|
| 110 |
for i in tqdm(range(num_samples), desc="Evaluating", file=sys.stderr):
|
|
|
|
| 111 |
s_begin = i
|
| 112 |
s_end = s_begin + args.seq_len
|
| 113 |
-
|
| 114 |
-
batch_x =
|
| 115 |
-
|
|
|
|
| 116 |
true_begin = s_end
|
| 117 |
true_end = true_begin + args.pred_len
|
| 118 |
true_scaled = data_scaled[true_begin:true_end]
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
with torch.no_grad():
|
| 121 |
-
outputs = model(batch_x)
|
| 122 |
|
| 123 |
pred_scaled = outputs.detach().cpu().numpy()[0]
|
| 124 |
-
|
| 125 |
-
|
|
|
|
| 126 |
padding = np.zeros((pred_scaled.shape[0], scaler.n_features_in_ - args.c_out))
|
| 127 |
pred_padded = np.concatenate((padding, pred_scaled), axis=1)
|
| 128 |
pred_unscaled = scaler.inverse_transform(pred_padded)[:, -args.c_out:]
|
|
|
|
| 11 |
import sys
|
| 12 |
sys.path.append('.')
|
| 13 |
|
| 14 |
+
from models import TimeXer
|
| 15 |
+
from utils.metrics import metric
|
| 16 |
+
from utils.timefeatures import time_features
|
| 17 |
|
| 18 |
# --- 1. 인자 파싱 (수정 없음) ---
|
| 19 |
parser = argparse.ArgumentParser(description='Time Series Prediction')
|
|
|
|
| 49 |
args = parser.parse_args()
|
| 50 |
|
| 51 |
|
| 52 |
+
|
| 53 |
+
prediction_padded = np.concatenate((padding, prediction_scaled), axis=1)
|
| 54 |
+
prediction = scaler.inverse_transform(prediction_padded)[:, -args.c_out:]
|
| 55 |
+
else:
|
| 56 |
+
prediction = scaler.inverse_transform(prediction_scaled)
|
| 57 |
+
|
| 58 |
+
return prediction
|
| 59 |
+
|
| 60 |
+
|
| 61 |
# --- 2. 공통 함수: 모델 및 스케일러 로드 (수정 없음) ---
|
| 62 |
def load_model_and_scaler(args):
|
| 63 |
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
|
|
|
| 64 |
args.device = device
|
| 65 |
model = TimeXer.Model(args).float().to(device)
|
| 66 |
model.load_state_dict(torch.load(args.checkpoint_path, map_location=device))
|
| 67 |
model.eval()
|
| 68 |
scaler = joblib.load(args.scaler_path)
|
|
|
|
| 69 |
print(f"Using device: {device}", file=sys.stderr)
|
| 70 |
print("Model and scaler loaded successfully.", file=sys.stderr)
|
| 71 |
return model, scaler, device
|
| 72 |
|
| 73 |
+
# --- 3. 모드 1: 단일 미래 예측 함수 ---
|
| 74 |
def predict_future(args, model, scaler, device):
|
|
|
|
|
|
|
| 75 |
df_input = pd.read_csv(args.predict_input_file)
|
| 76 |
+
df_input['date'] = pd.to_datetime(df_input['date'])
|
| 77 |
+
|
| 78 |
+
# ⭐️ 알려주신 정확한 컬럼 이름으로 수정
|
| 79 |
+
cols_to_scale = ['air_pres', 'wind_dir', 'wind_speed', 'air_temp', 'residual']
|
| 80 |
|
| 81 |
+
# 1. 인코더 입력(x_enc) 생성
|
| 82 |
+
raw_input = df_input[cols_to_scale].tail(args.seq_len).values
|
| 83 |
input_scaled = scaler.transform(raw_input)
|
| 84 |
batch_x = torch.from_numpy(input_scaled).float().unsqueeze(0).to(device)
|
| 85 |
|
| 86 |
+
# 2. 인코더 시간 정보(x_mark_enc) 생성
|
| 87 |
+
df_stamp_enc = df_input.tail(args.seq_len)[['date']].reset_index(drop=True)
|
| 88 |
+
enc_mark = time_features(df_stamp_enc, timeenc=0, freq=args.freq)
|
| 89 |
+
batch_x_mark = torch.from_numpy(enc_mark).float().unsqueeze(0).to(device)
|
| 90 |
+
|
| 91 |
+
# 3. 디코더 입력(x_dec) 생성
|
| 92 |
+
dec_inp_label = input_scaled[-args.label_len:]
|
| 93 |
+
dec_inp_pred = np.zeros([args.pred_len, args.enc_in])
|
| 94 |
+
decoder_input = np.concatenate([dec_inp_label, dec_inp_pred], axis=0)
|
| 95 |
+
batch_y = torch.from_numpy(decoder_input).float().unsqueeze(0).to(device)
|
| 96 |
+
|
| 97 |
+
# 4. 디코더 시간 정보(x_mark_dec) 생성
|
| 98 |
+
last_date = df_stamp_enc['date'].iloc[-1]
|
| 99 |
+
future_dates = pd.date_range(start=last_date, periods=args.pred_len + 1, freq='5T')[1:] # 5분 단위 가정
|
| 100 |
+
df_stamp_dec = pd.DataFrame({'date': list(df_stamp_enc['date'].values[-args.label_len:]) + list(future_dates)})
|
| 101 |
+
dec_mark = time_features(df_stamp_dec, timeenc=0, freq=args.freq)
|
| 102 |
+
batch_y_mark = torch.from_numpy(dec_mark).float().unsqueeze(0).to(device)
|
| 103 |
+
|
| 104 |
+
# 5. 모델 호출
|
| 105 |
with torch.no_grad():
|
| 106 |
+
outputs = model(batch_x, batch_x_mark, batch_y, batch_y_mark)
|
|
|
|
|
|
|
| 107 |
|
| 108 |
prediction_scaled = outputs.detach().cpu().numpy()[0]
|
| 109 |
|
| 110 |
+
# 스케일 복원
|
| 111 |
+
if scaler.n_features_in_ > 1:
|
| 112 |
padding = np.zeros((prediction_scaled.shape[0], scaler.n_features_in_ - args.c_out))
|
|
|
|
| 113 |
prediction_padded = np.concatenate((padding, prediction_scaled), axis=1)
|
| 114 |
prediction = scaler.inverse_transform(prediction_padded)[:, -args.c_out:]
|
| 115 |
else:
|
| 116 |
prediction = scaler.inverse_transform(prediction_scaled)
|
|
|
|
| 117 |
return prediction
|
| 118 |
|
| 119 |
+
# --- 4. 모드 2: 전체 기간 롤링 평가 함수 (⭐️⭐️⭐️ 이 함수를 완성했습니다 ⭐️⭐️⭐️) ---
|
|
|
|
| 120 |
def evaluate_performance(args, model, scaler, device):
|
|
|
|
|
|
|
| 121 |
df_eval = pd.read_csv(args.evaluate_file)
|
| 122 |
+
df_eval['date'] = pd.to_datetime(df_eval['date'])
|
| 123 |
+
|
| 124 |
+
# ⭐️ 알려주신 정확한 컬럼 이름으로 수정
|
| 125 |
+
cols_to_scale = ['air_pres', 'wind_dir', 'wind_speed', 'air_temp', 'residual']
|
| 126 |
+
raw_data = df_eval[cols_to_scale].values
|
| 127 |
data_scaled = scaler.transform(raw_data)
|
| 128 |
+
|
| 129 |
+
df_stamp = time_features(df_eval[['date']], timeenc=0, freq=args.freq)
|
| 130 |
|
| 131 |
preds_unscaled = []
|
| 132 |
trues_unscaled = []
|
| 133 |
|
| 134 |
num_samples = len(data_scaled) - args.seq_len - args.pred_len + 1
|
| 135 |
for i in tqdm(range(num_samples), desc="Evaluating", file=sys.stderr):
|
| 136 |
+
# 1. 인코더/디코더 입력 생성 (매 스텝마다)
|
| 137 |
s_begin = i
|
| 138 |
s_end = s_begin + args.seq_len
|
| 139 |
+
|
| 140 |
+
batch_x = data_scaled[s_begin:s_end]
|
| 141 |
+
batch_x_mark = df_stamp[s_begin:s_end]
|
| 142 |
+
|
| 143 |
true_begin = s_end
|
| 144 |
true_end = true_begin + args.pred_len
|
| 145 |
true_scaled = data_scaled[true_begin:true_end]
|
| 146 |
|
| 147 |
+
dec_inp_label = batch_x[-args.label_len:]
|
| 148 |
+
dec_inp_pred = np.zeros([args.pred_len, args.enc_in])
|
| 149 |
+
batch_y = np.concatenate([dec_inp_label, dec_inp_pred], axis=0)
|
| 150 |
+
|
| 151 |
+
dec_mark_label = df_stamp[s_end-args.label_len:s_end]
|
| 152 |
+
dec_mark_pred = df_stamp[true_begin:true_end]
|
| 153 |
+
batch_y_mark = np.concatenate([dec_mark_label, dec_mark_pred], axis=0)
|
| 154 |
+
|
| 155 |
+
# 텐서로 변환
|
| 156 |
+
batch_x = torch.from_numpy(batch_x).float().unsqueeze(0).to(device)
|
| 157 |
+
batch_x_mark = torch.from_numpy(batch_x_mark).float().unsqueeze(0).to(device)
|
| 158 |
+
batch_y = torch.from_numpy(batch_y).float().unsqueeze(0).to(device)
|
| 159 |
+
batch_y_mark = torch.from_numpy(batch_y_mark).float().unsqueeze(0).to(device)
|
| 160 |
+
|
| 161 |
+
# 2. 모델 호출
|
| 162 |
with torch.no_grad():
|
| 163 |
+
outputs = model(batch_x, batch_x_mark, batch_y, batch_y_mark)
|
| 164 |
|
| 165 |
pred_scaled = outputs.detach().cpu().numpy()[0]
|
| 166 |
+
|
| 167 |
+
# 3. 스케일 복원
|
| 168 |
+
if scaler.n_features_in_ > 1:
|
| 169 |
padding = np.zeros((pred_scaled.shape[0], scaler.n_features_in_ - args.c_out))
|
| 170 |
pred_padded = np.concatenate((padding, pred_scaled), axis=1)
|
| 171 |
pred_unscaled = scaler.inverse_transform(pred_padded)[:, -args.c_out:]
|