1. 데이터 로드
import pandas as pd
import numpy as np
train = pd.read_csv('train_prep.csv')
test = pd.read_csv('test_prep.csv')
# 'date' 컬럼을 datetime 타입으로 변경
train['date'] = pd.to_datetime(train['date'])
test['date'] = pd.to_datetime(test['date'])
2. 전체 train 데이터에서 실제 학습 기간 및 품목 설정하기
분석기간 item_selected과 start_date_train를 원하는 품목과 기간으로 설정하여, 이후 분석을 수행합니다.
# 연-월 혹은 연-월-일 설정
start_date_train = '2020-01'
train_selected = train[train['date'] >= start_date_train].copy().reset_index(drop=True)
# '배추', '무', '깻잎', '시금치', '토마토', '청상추', 캠벨얼리', '샤인마스캇
item_selected = '배추'
3. Feature engineering : 날짜 관련 feature 추가
학습 데이터에서 특정 분석 기간과 날짜 관련 피처 추출합니다.
- train_selected['date']에서 월(month) 정보를 추출해야 합니다. Pandas의 dt 속성과 그 하위의 month 속성을 사용해서 월 정보를 추출할 수 있습니다.
- train_selected['date']에서 주(week) 정보를 ISO 주차로 추출합니다. dt.isocalendar().week를 사용하여 ISO 주차 정보를 가져올 수 있습니다. 이후에 astype(np.int32)를 사용하여 데이터 타입을 정수로 변환합니다.
- 마지막으로, train_selected['date']에서 요일(weekday) 정보를 추출합니다. 이를 위해서는 dt 속성과 그 하위의 weekday 속성을 사용합니다.
이렇게 각각의 날짜 관련 정보를 추출하여 새로운 피처로 활용할 수 있습니다.
# 날짜 관련 feature 추가 하기
train_selected['month'] = train_selected['date'].dt.month
train_selected['week'] = train_selected['date'].dt.isocalendar().week.astype(np.int32)
train_selected['weekday'] = train_selected['date'].dt.weekday
# 날자 관련 피처를 저장해 둔다.
features_date = ['month', 'week', 'weekday']
display(train_selected.columns)
Index(['date', '배추_거래량', '배추_가격', '무_거래량', '무_가격', '깻잎_거래량', '깻잎_가격',
'시금치_거래량', '시금치_가격', '토마토_거래량', '토마토_가격', '청상추_거래량', '청상추_가격',
'캠벨얼리_거래량', '캠벨얼리_가격', '샤인마스캇_거래량', '샤인마스캇_가격', 'month', 'week',
'weekday'],
dtype='object')
4. 1,2,4주 후 가격 예측을 위한 학습 데이터 생성
선택한 품목의 1주, 2주, 4주 후 가격을 예측하기 위한 학습 데이터를 생성합니다. generate_target_columns 함수를 통해 각 주차별로 예측할 가격(true_1week, true_2week, true_4week)과 해당 날짜(date_1week, date_2week, date_4week)를 새로운 컬럼으로 추가합니다.
- row[date_col] + pd.DateOffset(weeks=week)를 이용하여 목표 날짜(target_date)를 생성합니다. 이 코드는 현재 행의 날짜에 주(week)를 더하여 목표 날짜를 계산합니다.
- df.loc[df[date_col] == target_date, item_price_name]를 이용하여 목표 날짜에 해당하는 아이템의 가격(target_value)을 찾습니다. 이 코드는 목표 날짜와 일치하는 행에서 아이템 가격을 추출합니다.
- target_date.date()를 이용하여 목표 날짜의 시간 정보를 제거하고 날짜 정보만을 남깁니다. 이를 통해 모델 학습이나 분석에서 시간 정보 없이 날짜 정보만을 사용할 수 있게 됩니다.
# 'true_1week', 'true_2week', 'true_4week' 컬럼을 생성하기 위한 함수
def generate_target_columns(df, date_col, item , week):
item_price_name = f'{item}_가격'
target_price_name = f'true_{week}week'
target_date_name = f'date_{week}week'
for i, row in df.iterrows():
target_date = row[date_col] + pd.DateOffset(weeks=week)
target_value = df.loc[df[date_col] == target_date, item_price_name]
if not target_value.empty:
df.at[i, target_date_name] = target_date.date()
df.at[i, target_price_name] = target_value.values[0]
else:
df.at[i, target_date_name] = np.nan
df.at[i,target_price_name] = np.nan
df = df.dropna().reset_index(drop=True)
return df
features = [f'{item_selected}_가격','month', 'week', 'weekday']
# 1,2,4주 후를 예측하는 모델에 필요한 학습, 검증을 위한 데이터를 각각 생성합니다.
train_1week = train_selected[['date'] + features ].copy()
train_2week = train_selected[['date'] + features ].copy()
train_4week = train_selected[['date'] + features ].copy()
#1,2,4주 후를 예측하는 모델에 필요한 학습, 검증을 위한 데이터에 정답지 컬럼을 생성합니다.
train_1week = generate_target_columns(train_1week, 'date', item_selected, 1)
train_2week = generate_target_columns(train_2week, 'date', item_selected, 2)
train_4week = generate_target_columns(train_4week, 'date', item_selected, 4)
# 어떤 컬럼이 생성 되었는지 각각 확인
display(train_1week.columns)
display(train_2week.columns)
display(train_4week.columns)
# 생성된 컬럼 확인
display(train_1week.head(10))
5. LightGBM을 이용한 1,2,4주 후 예측 성능 검증(1)
※ 한번의 학습으로 기간 예측하는 방식 (Batch Learning)
LightGBM 모델을 사용하여 1주, 2주, 4주 후의 가격을 예측합니다. 여기서는 학습 데이터를 한 번만 사용하여 모델을 학습시키고, 이를 사용하여 검증 기간 동안의 가격을 예측합니다. 이러한 방식을 Batch Learning이라고 합니다.
※ Batch Learning과 Online Learning의 차이
Batch Learning에서는 모델을 한 번만 학습시킵니다. 이는 데이터가 시간에 따라 변하지 않거나, 일정 기간 동안은 변하지 않을 것이라고 가정할 때 유용합니다. 반면, Online Learning에서는 데이터가 주기적으로 업데이트되며 모델도 이에 따라 지속적으로 업데이트됩니다. 이러한 차이로 인해 두 방식은 서로 다른 적용 사례와 성능 특성을 가집니다.
import lightgbm as lgb
from sklearn.metrics import mean_absolute_error
from tqdm import tqdm
# Stage 4에서 사용한 평가 산식 (nmae) 함수
def nmae(true_values, pred_values):
# NMAE (Normalized Mean Absolute Error) 계산
# NMAE = (1/n) * Σ(|true_value - pred_value| / true_value)
mask = true_values != 0
filtered_true_values = true_values[mask]
filtered_pred_values = pred_values[mask]
return np.sum(np.abs(filtered_true_values - filtered_pred_values) / filtered_true_values) / len(filtered_true_values)
def evaluate_lgbm_model(item_name, df, features, start_date, n_week, nmae_function):
target_price_col = f'true_{n_week}week'
# 학습 및 검증 데이터 분리
_train_data = df[df['date'] < start_date].reset_index(drop=True)
_valid_data = df[df['date'] >= start_date].dropna().reset_index(drop=True)
model_week = lgb.LGBMRegressor(learning_rate = 0.01, num_leaves = 5, max_depth = 5, objective = 'regression', random_state = 42, verbose=-1)
model_week.fit(_train_data[features],_train_data[target_price_col])
pred_week = model_week.predict(_valid_data[features])
score_week = nmae_function(_valid_data[target_price_col] , pred_week)
return score_week
start_valid_date = '2021-05-01'
score_1week = evaluate_lgbm_model(item_selected, train_1week, features , start_valid_date, 1, nmae)
score_2week = evaluate_lgbm_model(item_selected, train_2week, features, start_valid_date, 2, nmae)
score_4week = evaluate_lgbm_model(item_selected, train_4week, features, start_valid_date, 4, nmae)
# 평균 NMAE 점수 계산
average_score = (score_1week + score_2week + score_4week) / 3
display(f'mean : {average_score} ({score_1week,score_2week,score_4week})')
display(average_score)
'mean : 0.39112473206702075 ((0.3263019308771582, 0.3639351513498384, 0.4831371139740656))'
0.39112473206702075
6. LightGBM을 이용한 1,2,4주 후 예측 성능 검증(2)
※ 주기적으로 데이터를 업데이트하여 학습하고 기간 예측하는 방식 (Online Learning)
ightGBM 모델을 사용하여 1주, 2주, 4주 후의 가격을 예측합니다. 다만 이번에는 모델을 학습 데이터와 함께 검증 데이터를 주기적으로 업데이트하여 계속 학습시킵니다. 이러한 방식을 Online Learning이라고 합니다.
import lightgbm as lgb
from sklearn.metrics import mean_absolute_error
from tqdm import tqdm
def evaluate_lgbm_model(item_name, df, features, start_date, n_week, nmae_function):
true_week_lst,pred_week_lst = [],[]
target_price_col = f'true_{n_week}week'
# 학습 및 검증 데이터 분리
_train_data = df[df['date'] < start_date].reset_index(drop=True)
_valid_data = df[df['date'] >= start_date].dropna().reset_index(drop=True)
# 매일 train 데이터 업데이트하고, 1주후 가격 예측하기
model_week = lgb.LGBMRegressor(learning_rate = 0.01, num_leaves = 5, max_depth = 5, objective = 'regression', random_state = 42, verbose=-1)
model_week.fit(_train_data[features],_train_data[target_price_col])
valid_day_lst = _valid_data['date'].to_list()
for date in tqdm(valid_day_lst):
valid_updated = _valid_data[_valid_data['date'] <= date]
train_updated = pd.concat([ _train_data, valid_updated ] )
# 매일 train 데이터 업데이트하고, 1주후 가격 예측하기
model_week = lgb.LGBMRegressor(learning_rate = 0.01, num_leaves = 5, max_depth = 5, objective = 'regression', random_state = 42, verbose=-1)
model_week.fit(train_updated[features],train_updated[target_price_col])
pred_week = model_week.predict(valid_updated[features].iloc[-1:])
true_week_lst.append(df.loc[df['date'] == date, target_price_col].values[0])
pred_week_lst.append(pred_week[0])
score_week = nmae_function( np.array(true_week_lst) , np.array(pred_week_lst))
return score_week, pred_week_lst
start_valid_date = '2021-05-01'
score_1week, pred_1week_lst = evaluate_lgbm_model(item_selected, train_1week, features , start_valid_date, 1, nmae)
score_2week, pred_2week_lst = evaluate_lgbm_model(item_selected, train_2week, features, start_valid_date, 2, nmae)
score_4week, pred_4week_lst = evaluate_lgbm_model(item_selected, train_4week, features, start_valid_date, 4, nmae)
# 평균 NMAE 점수 계산
average_score = (score_1week + score_2week + score_4week) / 3
display(f'mean : {average_score} ({score_1week,score_2week,score_4week})')
display(average_score)
100%|██████████| 143/143 [00:01<00:00, 107.89it/s]
100%|██████████| 136/136 [00:01<00:00, 109.07it/s]
100%|██████████| 122/122 [00:01<00:00, 111.41it/s]
'mean : 1.0301274353271601 ((0.9757589284282557, 1.3506168933640925, 0.7640064841891322))'
1.0301274353271601
7.검증 기간 예측 결과 시각화
1) 1주후 가격 예측 결과 시각화 하기
# 예측 결과를 DataFrame으로 만들기
_valid_1week_data = train_1week[train_1week['date']>= start_valid_date].reset_index()
predicted_1week_df = pd.DataFrame({'1week_date': list(_valid_1week_data['date_1week'])})
predicted_1week_df['pred_1week'] = pred_1week_lst
predicted_1week_df = predicted_1week_df.dropna()
# 그래프 그리기
plt.figure(figsize=(12, 4))
# 전체 기간에 대한 그래프
plt.plot(train_selected['date'], train_selected[f'{item_selected}_가격'], label='실제가격', color='blue')
# 1주 후 예측값 그리기
plt.plot(predicted_1week_df['1week_date'], predicted_1week_df['pred_1week'], label='1주후 예측 가격', color='orange', linestyle='--')
# 검증 기간의 시작과 끝을 표시
plt.axvline(x=_valid_1week_data['date'].min(), color='r', linestyle='--', label='검증 기간 시작일')
plt.axvline(x=_valid_1week_data['date'].max(), color='r', linestyle='--', label='검증 기간 종료일')
plt.title('실제 가격 vs 1주후 예측 가격')
plt.legend()
plt.show()
2) 시계열 예측 모델의 성능 시각화 함수 구현 및 1,2,4주 예측 behavior 시각화 하기
import matplotlib.pyplot as plt
def plot_predict_behavior(df_all, df_valid_weeks, pred_weeks):
df_valid_week1 = df_valid_weeks[0]
df_valid_week2 = df_valid_weeks[1]
df_valid_week4 = df_valid_weeks[2]
pred_1week = pred_weeks[0]
pred_2week = pred_weeks[1]
pred_4week = pred_weeks[2]
predicted_1week_df = pd.DataFrame({'1week_date' : list(df_valid_week1['date_1week'])})
predicted_1week_df['pred_1week'] = pred_1week
predicted_1week_df = predicted_1week_df.dropna()
predicted_2week_df = pd.DataFrame({'2week_date' : list(df_valid_week2['date_2week'])})
predicted_2week_df['pred_2week'] = pred_2week
predicted_2week_df = predicted_2week_df.dropna()
predicted_4week_df = pd.DataFrame({'4week_date' : list(df_valid_week4['date_4week'])})
predicted_4week_df['pred_4week'] = pred_4week
predicted_4week_df = predicted_4week_df.dropna()
start_date_valid = df_valid_week1['date'].min()
end_date_valid = df_valid_week1['date'].max()
# 그래프 그리기
plt.figure(figsize=(12, 6))
# 위쪽 그래프 (전체 기간)
plt.subplot(2, 1, 1)
plt.plot(df_all['date'], df_all[f'{item_selected}_가격'], label='Real', color='blue')
# 1week, 2week, 4week 예측값 그리기
plt.plot(predicted_1week_df['1week_date'], predicted_1week_df['pred_1week'], label='pred_1week', color='orange',linestyle='--')
plt.plot(predicted_2week_df['2week_date'], predicted_2week_df['pred_2week'], label='pred_2week', color='green',linestyle='--')
plt.plot(predicted_4week_df['4week_date'], predicted_4week_df['pred_4week'], label='pred_4week', color='black',linestyle='--')
plt.axvline(x=start_date_valid, color='r', linestyle='--', label='Start of Validation')
plt.axvline(x=end_date_valid, color='r', linestyle='--', label='End of Validation')
plt.title('실제가격 vs 예측가격(검증기간)')
plt.legend()
# zoom_graph_start_date
zoom_graph_start_date = start_date_valid
target_max = df_all.loc[ df_all['date'] >= zoom_graph_start_date, f'{item_selected}_가격'].max()
target_min = df_all.loc[ df_all['date'] >= zoom_graph_start_date, f'{item_selected}_가격'].min()
y_min_zoomed_graph = min(target_min,
predicted_1week_df['pred_1week'].min(),
predicted_2week_df['pred_2week'].min(),
predicted_4week_df['pred_4week'].min() )
y_max_zoomed_graph = max(target_max,
predicted_1week_df['pred_1week'].max(),
predicted_2week_df['pred_2week'].max(),
predicted_4week_df['pred_4week'].max() )
# 아래쪽 그래프 (검증 기간 확대)
plt.subplot(2, 1, 2)
plt.plot(df_all['date'], df_all[f'{item_selected}_가격'], label='Real', color='blue')
# 1week, 2week, 4week 예측값 그리기
plt.plot(predicted_1week_df['1week_date'], predicted_1week_df['pred_1week'], label='pred_1week', color='orange',linestyle='--')
plt.plot(predicted_2week_df['2week_date'], predicted_2week_df['pred_2week'], label='pred_2week', color='green',linestyle='--')
plt.plot(predicted_4week_df['4week_date'], predicted_4week_df['pred_4week'], label='pred_4week', color='black',linestyle='--')
plt.axvline(x = start_date_valid, color='r', linestyle='--', label='Start of Validation')
plt.axvline(x = end_date_valid, color='r', linestyle='--', label='End of Validation')
plt.xlim(left=start_date_valid - pd.DateOffset(days=14))
plt.ylim(y_min_zoomed_graph, y_max_zoomed_graph)
plt.title('실제가격 vs 예측가격(검증기간)')
plt.legend()
plt.tight_layout()
plt.show()
# 해당 날짜 이하의 검증 데이터
_valid_data_1week = train_1week[train_1week['date'] >= start_valid_date]
_valid_data_2week = train_2week[train_2week['date'] >= start_valid_date]
_valid_data_4week = train_4week[train_4week['date'] >= start_valid_date]
_valid_data_weeks = [_valid_data_1week, _valid_data_2week, _valid_data_4week]
pred_weeks = [np.array(pred_1week_lst),np.array(pred_2week_lst),np.array(pred_4week_lst)]
# plot_predict_behavior 함수 호출하여 예측 거동 시각화
plot_predict_behavior(train_selected,_valid_data_weeks, pred_weeks)
'딥러닝 > 딥러닝: 실전 프로젝트 학습' 카테고리의 다른 글
시계열 : 서울시 평균기온 예측 프로젝트 1(데이터 이해) (0) | 2025.02.02 |
---|---|
시계열 : 농산물 가격 예측 프로젝트 6 (전체 정리) (0) | 2025.02.01 |
시계열 : 농산물 가격 예측 프로젝트 4 (ARIMA 모델을 활용한 시계열 예측 기초와 적용) (0) | 2025.01.30 |
시계열 : 농산물 가격 예측 프로젝트 3 (데이터 전처리와 시계열 정상성(Stationarity)) (0) | 2025.01.30 |
시계열 : 농산물 가격 예측 프로젝트 2 (데이터 분석과 시각화) (0) | 2025.01.30 |