본문 바로가기
KT Aivle 5기/프로젝트

[KT AIVLE SCHOOL] KT Aivle 5차 미니프로젝트 후기 1

by G허니 2024. 5. 12.

5차 미니프로젝트에서는 시계열 데이터를 활용한 상품 판매량 예측 모델링을 진행했습니다.

 

EDA 탐색적 분석

 

카테고리별 판매량
지역별 판매량
기간별 판매량

 

중요 범줏값의 비율을 확인합니다.

각 범주별 판매량 및 판매량 추이등을 확인 했습니다.

 

두 범주별 판매량 추이를 이용해 판매량을 선그래프로 시각화해 시계열 패턴을 찾았습니다.

 

 

diff()함수를 사용해 변수에 대한 변화량을 확인할 수 있습니다.

 

 

데이터프레임 구성
  • 기본 변수 구성
  • 날짜 변수에서 요소 추출
  • 데이터 전처리
    • 결측치 처리
    • 범주형 데이터의 가변수화
  • 데이터 분할(학습용, 검증용)
# datetime 형으로 변환
sales['Date'] = pd.to_datetime(sales['Date'] )
oil_price['Date'] = pd.to_datetime(oil_price['Date'] )
orders['Date'] = pd.to_datetime(orders['Date'] )

 

# 각 데이터의 기본 정보를 확인
data_list = [sales, orders, oil_price, stores, products]
data_name = ['sales', 'orders', 'oil_price', 'stores', 'products']

for data, name in zip(data_list, data_name):
    print('*' * 50)
    print('[--------------------', name, '--------------------]')
    print(data.info())
print('*' * 50)

for data, name in zip(data_list, data_name):
    print('[--------------------', name, '--------------------]')
    display(data.describe())

 

 

해당 형태로 데이터셋 구성

 

# 데이터셋 전처리 함수
def create_data(pid, sid=44):
    lead = products[products['Product_ID'] == pid]
    lead = lead['LeadTime'].iloc[0]

    df = sales[(sales['Store_ID'] == sid) & (sales['Product_ID'] == pid)]

    # Qty 0 삭제
    df = df[df['Qty'] != 0]

    # orders merge
    temp = orders[orders['Store_ID'] == sid].drop('Store_ID', axis=1)
    df = pd.merge(df, temp, on='Date', how='left')

    # Weekday, Month 추가
    df['Weekday'] = df['Date'].dt.day_name()
    df['Month'] = df['Date'].dt.month

    # oil_price merge
    temp = oil_price.copy()
    temp['WTI_Price'] =  temp['WTI_Price'].rolling(14, min_periods=1).mean()
    df = pd.merge(df, temp, on='Date', how='left')
    df['WTI_Price'].interpolate(method='linear', inplace=True)

    # Target 추가
    df['Target'] = df['Qty'].shift((-1)*lead)
    # ID열 삭제
    df.drop(['Store_ID', 'Product_ID'], axis=1, inplace=True)

    #결측치 제거
    df.dropna(inplace=True)
    df.reset_index(drop=True, inplace=True)

    return df

 

데이터프레임 구성 및 모델링
  • 데이터 탐색을 바탕으로 변수 추가
  • Linear Regression으로 모델링
  • 모델 성능 평가 및 기록
# Linear 모델

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error, mean_absolute_percentage_error

def create_linear(data):
    x = data.drop(['Date', 'Target'], axis=1)
    y = data['Target']

    weekday = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
    x['Weekday'] = pd.Categorical(x['Weekday'], categories=weekday)
    month = [str(i) for i in range(1, 13)]
    x['Month'] = pd.Categorical(x['Month'], categories=month)

    x = pd.get_dummies(x, drop_first=True, dtype=int)

    x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=4*30, shuffle=False, random_state=1)
    model = LinearRegression()
    model.fit(x_train, y_train)
    y_pred = model.predict(x_val)

    print('RMSE:',  mean_squared_error(y_val, y_pred, squared=False))
    print('MAE:', mean_absolute_error(y_val, y_pred))
    print('MAPE', mean_absolute_percentage_error(y_val, y_pred))
    print('R2:',  r2_score(y_val, y_pred))

    plot_model_result(y_train, y_val, y_pred)
    return model

 

 

함수를 통해 생성된 데이터셋으로 Linear Regression 진행

 

 

앙상블 모델 생성
  • Random Forest, LightGBM 사용
  • RMSE, MAE, MAPE, R2_Score로 성능 평가
  • 결과 기록 및 시각화
# 랜던포레스트 모델
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error, mean_absolute_percentage_error

def create_rf(data):
    x = data.drop(['Date', 'Target'], axis=1)
    y = data['Target']

    weekday = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
    x['Weekday'] = pd.Categorical(x['Weekday'], categories=weekday)
    month = [str(i) for i in range(1, 13)]
    x['Month'] = pd.Categorical(x['Month'], categories=month)

    x = pd.get_dummies(x, drop_first=True, dtype=int)

    x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=4*30, shuffle=False, random_state=1)
    model = RandomForestRegressor()
    model.fit(x_train, y_train)
    y_pred = model.predict(x_val)

    print('RMSE:',  mean_squared_error(y_val, y_pred, squared=False))
    print('MAE:', mean_absolute_error(y_val, y_pred))
    print('MAPE', mean_absolute_percentage_error(y_val, y_pred))
    print('R2:',  r2_score(y_val, y_pred))

    plot_model_result(y_train, y_val, y_pred)
    return model

 

# LGBM 모델

from lightgbm import LGBMRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error, mean_absolute_percentage_error

def create_lgb(data):
    x = data.drop(['Date', 'Target'], axis=1)
    y = data['Target']

    weekday = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
    x['Weekday'] = pd.Categorical(x['Weekday'], categories=weekday)
    month = [str(i) for i in range(1, 13)]
    x['Month'] = pd.Categorical(x['Month'], categories=month)

    x = pd.get_dummies(x, drop_first=True, dtype=int)

    x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=4*30, shuffle=False, random_state=1)
    model = LGBMRegressor()
    model.fit(x_train, y_train)
    y_pred = model.predict(x_val)

    print('RMSE:',  mean_squared_error(y_val, y_pred, squared=False))
    print('MAE:', mean_absolute_error(y_val, y_pred))
    print('MAPE', mean_absolute_percentage_error(y_val, y_pred))
    print('R2:',  r2_score(y_val, y_pred))

    plot_model_result(y_train, y_val, y_pred)
    return model

 

 

 

 

하이퍼파라미터 튜닝
  • 튜닝을 통한 성능 향상 시도 ( GridSearchCV를 사용해 성능 최적화 진행)
  • 최적의 모델 선정 
def create_rf(x_train, x_val, y_train, y_val):
    params = {'max_depth':range(5, 24), 'n_estimators':range(5, 26)}
    model = GridSearchCV(
        RandomForestRegressor(random_state=1),
        n_jobs=-1,
        param_grid=params,
        scoring='r2',
        cv=5,
    ).fit(x_train, y_train)
    y_pred = model.best_estimator_.predict(x_val)
    temp = pd.Series(model.best_estimator_.feature_importances_, index=x_train.columns).sort_values(ascending=False)
    plot_model_result(y_train, y_val, y_pred)
    print(*model.best_params_.items())
    print('예측 정확도:', model.best_score_)
    print('RMSE:',  mean_squared_error(y_val, y_pred, squared=False))
    print('MAE:', mean_absolute_error(y_val, y_pred))
    print('MAPE', mean_absolute_percentage_error(y_val, y_pred))
    print('R2:',  r2_score(y_val, y_pred))
    plt.figure(figsize=(15, 5))
    sns.barplot(x=temp, y=temp.index)
    plt.show()
    return model.best_estimator_, y_pred

 

GridSearchCV를 사용해 성능 최적화 진행

 

 

def create_lgb(x_train, x_val, y_train, y_val):
    # LGBMRegressor 모델 생성
    model = LGBMRegressor()

    # 튜닝할 하이퍼파라미터 그리드 설정 {'learning_rate': 0.2, 'max_depth': 3, 'min_child_samples': 15, 'n_estimators': 100}
    param_grid = {
        'n_estimators': list(range(30,40,1)),  # 결정 트리 개수
        'learning_rate': [0.1, 0.2, 0.3],  # 학습률
        'max_depth': [3,5,7,9],  # 트리의 최대 깊이
        'min_child_samples': list(range(13,24,1))  # 리프 노드의 최소 데이터 수
    }

    # GridSearchCV를 사용하여 하이퍼파라미터 튜닝
    grid_search = GridSearchCV(estimator=model, param_grid=param_grid,
                               cv=3, n_jobs=-1, scoring='r2')  # scoring='neg_mean_squared_error'
    grid_search.fit(x_train, y_train)

    # 최적의 하이퍼파라미터 출력
    print("Best Parameters:", grid_search.best_params_)

    # 최적의 모델로 예측 수행
    y_pred = grid_search.best_estimator_.predict(x_val)

    # 성능 지표 출력
    print('RMSE:',  mean_squared_error(y_val, y_pred, squared=False))
    print('MAE:', mean_absolute_error(y_val, y_pred))
    print('MAPE', mean_absolute_percentage_error(y_val, y_pred))
    print('R2:',  r2_score(y_val, y_pred))

    # plot_model_result 함수가 정의되어 있다고 가정합니다.
    plot_model_result(y_train, y_val, y_pred)

    # 최적의 모델과 예측값 반환
    return grid_search.best_estimator_, y_pred

 

 

데이터 파이프라인 함수 구현
  • 전처리 과정 자동화 함수 구현
  • Input: Raw 데이터
  • Output: 예측 및 성능 평가 가능한 데이터

 

데이터 전처리, 탐색, 모델링, 하이퍼파라미터 튜닝 등 전반적인 머신러닝/딥러닝 프로세스를 경험할 수 있었던 좋은 기회였던 것 같습니다.

 

아쉬었던 부분은 시계열 데이터의 경우 shuffle이나 random sampling을 하게 되면 데이터의 시간순서가 뒤섞이기 때문에 모델이 패턴과 트렌드를 제대로 학습하지 못할 수 있습니다. 이 부분을 간과하고 진행하다 보니 프로젝트가 더 어렵고 힘들었을 것이라 생각됩니다.

 

그리고 데이터 파이프라인 함수를 직접 구현해보면서 raw 데이터를 모델이 사용 가능한 형태로 가공하는 일련의 과정을 체계적으로 정리해보신 것이 인상 깊었습니다.