프로젝트

국민대 무역 수입가격 예측 프로젝트

hayoung.ee 2025. 2. 21. 22:58

[data dictionary]

pid- 품목 ID (p001-p005)

year -연도 (2017-2022)

month-월 (1-12)

day- 일 (10/20/30)

weight - 수입 중량(kg)

quantity - 수입 수량(unit)

value - 수입금액 (us달러)

 

  • day: 산업 품목별 10일 단위의 수입자료로 10, 20, 30으로 구성
    • day=10 : 매월 1~10일
    • day=20 : 매월 11~20일
    • day=30 : 매월 21~말일
  • quantity
    • 품목이 원자재, 소재 등인 경우, 수량 측정이 불가하여 수입 신고시 중량만 측정하여 기재함
    • 그에 따라 대부분 quantity 값에 결측치가 존재하며 결측치는 0으로 처리

[데이터 전처리]

기존에 있던 열들을 이용해서 새로운 열(피처)를 최대한 많이 만드는 식으로 계획 . 

 

1. 전월 value 가져오기 (시계열 특성)

전월 및 전년도 value 평균값 생성

train = train.sort_values(by=['pid', 'date'])
train['value_last_month'] = train.groupby('pid')['value'].shift(1)
# value_last_month이 NaN인 경우 해당 날짜의 value로 대체
train['value_last_month'] = train.apply(
    lambda row: row['value'] if pd.isna(row['value_last_month']) else row['value_last_month'],
    axis=1
)

 

2. 전월 weight 가져오기 (시계열 특성)

전월 및 전년도 weight 평균값 생성 

train.sort_values(by=['pid','date'])
train['weight_last_month']=train.groupby('pid')['weight'].shift(1)
train['weight_last_month'] = train.apply(
    lambda row: row['weight'] if pd.isna(row['weight_last_month']) else row['weight_last_month'],
    axis=1
)

 

3. price 누적 데이터 피쳐

4. weight 누적 데이터 피쳐 

train['cumulative_import_price'] = train.groupby(['year', 'pid'])['value'].cumsum()
train['cumulative_import_weight'] = train.groupby(['year', 'pid'])['weight'].cumsum()

 

5. 누적 평균

연도와 품목별 평균 증가 수치 

train['cumulative_avg_weight'] = train['cumulative_import_weight'] / (train.groupby(['year', 'pid']).cumcount() + 1)

 

6. 각 달의 금액이 연간 총 금액에서 차지하는 비율 피처

train['yearly_total_value'] = train.groupby(['pid', 'year'])['value'].transform('sum')

train['monthly_value_ratio'] = train['value'] / train['yearly_total_value']

 

7. 각 pid 별로 value_last_month의 표준편차 계산하여 z-score 계산 

train['value_semi_z_score'] = train.groupby('pid').apply(
    lambda group: (group['value'] - group['value_last_month']) / group['value_last_month'].std()
).reset_index(level=0, drop=True)

 

8. 월 별 평균 수입 증량 

train['monthly_avg_weight'] = train.groupby(['pid', 'month'])['weight'].transform('mean')

 

9. 수입 금액의 상대적 변화율 

# value_change_rate와 weight_change_rate 계산
train['value_change_rate'] = train['value'].pct_change()
train['weight_change_rate'] = train['weight'].pct_change()

# 상대적 변화율 (relative_change_rate) 계산
train['relative_change_rate'] = train['value_change_rate'] / train['weight_change_rate']

# NaN 값 제거 (첫 번째 row와 변화율이 0인 경우)
train['relative_change_rate'].fillna(0, inplace=True)
train['value_change_rate'].fillna(0, inplace=True)

 

이 외에 환율데이터셋, GDP, 수출금액데이터셋을 활용해서 피쳐를 만들어봤지만 rmse 값이 떨어지는 것을 확인하고 피쳐 삭제.... 

 

[데이터 분할]

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error

# 사용할 특성과 타겟 변수 선택

features = ['value_last_month','weight_last_month', 'weight','cumulative_import_price','cumulative_import_weight','cumulative_avg_weight','monthly_value_ratio',
            'value_semi_z_score','monthly_avg_weight','relative_change_rate','value_change_rate']
target = 'value'

# 학습 및 검증 데이터 분리 (2022년 데이터를 검증용으로 사용)
train_set = train[train['year'] < 2022]
test_set = train[train['year'] == 2022]

X_train = train_set[features]
y_train = train_set[target]
X_test = test_set[features]
y_test = test_set[target]

 

[모델링]

from lightgbm import LGBMRegressor
from sklearn.linear_model import LinearRegression
voting_regressor = VotingRegressor(estimators=[
    #("lgbm", LGBMRegressor(random_state=0)),
    ("xgb", XGBRegressor(n_estimators=100, max_depth=6, random_state=0)),
    ("lr", LinearRegression())
])

# Create the final pipeline
model = Pipeline(steps=[("model", voting_regressor)])

# Fit the model
model.fit(X_train, y_train)

 

from catboost import CatBoostRegressor
from sklearn.linear_model import LinearRegression
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
from sklearn.ensemble import VotingRegressor
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error
import numpy as np
from sklearn.linear_model import ElasticNet
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble import ExtraTreesRegressor

# CatBoost 기본 모델 생성
cat_model = CatBoostRegressor(
    iterations=1000,
    learning_rate=0.1,
    depth=6,
    silent=True,  # 학습 로그를 출력하지 않음
    random_seed=42
)
elasticnet_model = ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42)

rf_model = RandomForestRegressor(n_estimators=100, max_depth=None, random_state=42)
gbr_model = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42)
et_model = ExtraTreesRegressor(n_estimators=100, max_depth=None, random_state=42)
from sklearn.neural_network import MLPRegressor

mlp_model = MLPRegressor(hidden_layer_sizes=(100, 50), activation='relu', solver='adam', random_state=42)



# VotingRegressor 생성
voting_regressor = VotingRegressor(estimators=[
    
    ("xgb", XGBRegressor(
        n_estimators=137,
        max_depth=4,
        learning_rate=0.10567854073293201,
        subsample=0.39344347297910576,
        random_state=0
    )),
    #("ela", elasticnet_model),
    #("rf", rf_model),
    #("gbr", gbr_model),
    #("et", et_model),
    #("mlp", mlp_model),

    #("cat", cat_model),
    ("lr", LinearRegression())
])

# 파이프라인 생성
model = Pipeline(steps=[("model", voting_regressor)])

# 모델 학습
model.fit(X_train, y_train)

# 테스트 데이터 예측
y_pred = model.predict(X_test)

# 평가 (MSE와 RMSE)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)

print(f"테스트 데이터 MSE: {mse}")
print(f"테스트 데이터 RMSE: {rmse}")
테스트 데이터 MSE: 1180863632903322.0
테스트 데이터 RMSE: 34363696.43829549

 

[예측 및 성능평가]

# 예측 및 성능 평가 - 베스트 (11/17)
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
# RMSE 계산
rmse = np.sqrt(mean_squared_error(y_test, y_pred))

mse, rmse
#최적의 XGBoost 하이퍼파라미터: {'n_estimators': 173, 'max_depth': 5, 'learning_rate': 0.10906855810380661, 'subsample': 0.48296754965125244}
(1176656501706542.8, 34302427.05271076)

 

우리끼리 보는 리더보드에서는 그래도 7위 안에 들었는데 .. 

막상 본게임에서는 순위가 완전히 하락했다 ..

왤까 ...