여러기법 적용해보기

from joblib import Parallel, delayed

train_df = train_no_duplicates
test_df = test

# 특성 선택
features = ['latitude', 'longitude', 'area_m2', 'floor', 'built_year', 'contract_year_month']

# Train 데이터를 train과 validation으로 분리
train_df, val_df = train_test_split(train_df, test_size=0.2, random_state=42)

# 스케일링
scaler = StandardScaler()
train_scaled = scaler.fit_transform(train_df[features])
val_scaled = scaler.transform(val_df[features])
test_scaled = scaler.transform(test_df[features])

# 특성 가중치 조정
weights = np.array([1, 1, 5, 0.5, 0.5, 0.5])  # latitude, longitude, area_m2, floor, built_year, contract_year_month 순서
train_scaled = train_scaled * weights
val_scaled = val_scaled * weights
test_scaled = test_scaled * weights

# BallTree 구축
tree = BallTree(train_scaled, leaf_size=40)

# 예측 함수
def predict_deposit(neighbors, distances):
    weights = 1 / (distances + 1e-5)
    weighted_deposits = neighbors['deposit'] * weights
    return np.sum(weighted_deposits) / np.sum(weights)

# 단일 예측 함수
def predict_single(data_point, min_k, max_k):
    distances, indices = tree.query([data_point], k=max_k)
    k = min(max_k, max(min_k, int(np.sum(distances < np.median(distances)))))
    neighbor_indices = indices[0][:k]
    neighbor_distances = distances[0][:k]
    neighbors = train_df.iloc[neighbor_indices]
    return predict_deposit(neighbors, neighbor_distances)

# 병렬 예측 수행 함수
def make_predictions_parallel(data_scaled, min_k, max_k):
    return Parallel(n_jobs=-1)(delayed(predict_single)(data_point, min_k, max_k) for data_point in data_scaled)

# Validation 예측
min_k = 3
max_k = 10
val_predictions = make_predictions_parallel(val_scaled, min_k, max_k)

# Validation MAE 계산
val_mae = mean_absolute_error(val_df['deposit'], val_predictions)
print(f"Validation MAE: {val_mae}")

# Test 예측
test_predictions = make_predictions_parallel(test_scaled, min_k, max_k)

여기부터였나?

이상치 처리

def handle_outliers(predictions, threshold=3):
    median = np.median(predictions)
    mad = np.median(np.abs(predictions - median))
    lower_bound = median - threshold * mad
    upper_bound = median + threshold * mad
    return np.clip(predictions, lower_bound, upper_bound)

predictions = make_predictions(test_scaled, k)
predictions = handle_outliers(predictions)

Validation MAE: 8552.217652688742


앙상블

def ensemble_predictions(data_scaled, k_values):
    all_predictions = []
    for k in k_values:
        predictions = make_predictions(data_scaled, k)
        all_predictions.append(predictions)
    return np.mean(all_predictions, axis=0)

test_predictions = ensemble_predictions(test_scaled, [3, 5, 7, 9])

Validation MAE: 8405.48183286097


거리에 따른 가중치 함수 조정

def predict_deposit(neighbors, distances):
    weights = np.exp(-distances)  # 지수 함수 사용
    weighted_deposits = neighbors['deposit'] * weights
    return np.sum(weighted_deposits) / np.sum(weights)


StandardScaler, RobustScaler, MinMaxScaler / 로그변환 비교

기존의 StandardScaler방식을 다른 방식으로도 적용해보았다.

StandardScaler without Log Transform - Validation MAE: 8405.48183286097
StandardScaler with Log Transform - Validation MAE: 8468.876950218077
RobustScaler without Log Transform - Validation MAE: 8584.839449059366
RobustScaler with Log Transform - Validation MAE: 8540.002242918164
MinMaxScaler without Log Transform - Validation MAE: 8566.245673670523
MinMaxScaler with Log Transform - Validation MAE: 8771.50220388082

Results sorted by MAE:
StandardScaler without Log: 8405.48183286097
StandardScaler with Log: 8468.876950218077
RobustScaler with Log: 8540.002242918164
MinMaxScaler without Log: 8566.245673670523
RobustScaler without Log: 8584.839449059366
MinMaxScaler with Log: 8771.50220388082

Best method: StandardScaler without Log with MAE: 8405.48183286097

기존의 정규화 방법이 가장 성능이 좋았고, 아마 이때까지도 1/100 sampleing 한 걸로 해서 확실한 그건진 모르겠지만 사실 이때까지도 무슨 한번 돌리는데 20분넘어가는 코드들이 너무 많아서 살짝 어지러웠다. 입력 하면 딱 출력 바로바로 나오고 판단하고 싶은데 공부의 속도에 진전이 너무 느렸다. 스트레스 받을 뻔.


거리 계산 방식을 변경

기존의 유클리드 거리 방식을 3가지로 나누어 비교해보았다. 맨해튼 방식이 가장 성능이 좋게 나오긴 했다.

Euclidean Validation MAE: 8486.101253090092
Manhattan Validation MAE: 8031.291888515132
Minkowski (p=3) Validation MAE: 8742.976038886232

Best performing distance metric: Manhattan with MAE: 8031.291888515132

Test predictions using Manhattan distance metric have been generated

맨해튼 방식의 베이스코드 - 아마 앙상블할때 이거 아니면 밑에 코드 쓸듯?

# 데이터 로드
train_df = pd.read_csv('merged_data.csv')
test_df = pd.read_csv

# 특성 선택
features = train_df.columns.drop(['deposit', 'index']).tolist()

# Train 데이터를 train과 validation으로 분리
train_df, val_df = train_test_split(train_df, test_size=0.2, random_state=42)

# 스케일링
scaler = StandardScaler()
train_scaled = scaler.fit_transform(train_df[features])
val_scaled = scaler.transform(val_df[features])
test_scaled = scaler.transform(test_df[features])

# 특성 가중치 조정
weights = np.array([1, 1, 5, 0.5, 0.5, 0.5])  # latitude, longitude, area_m2, floor, built_year, contract_year_month 순서
train_scaled = train_scaled * weights
val_scaled = val_scaled * weights
test_scaled = test_scaled * weights

# BallTree 구축 (맨해튼 거리 사용)
tree = BallTree(train_scaled, leaf_size=40, metric='manhattan')

# 예측 함수 (거리에 따른 가중치 함수 조정)
def predict_deposit(neighbors, distances):
    weights = np.exp(-distances)  # 지수 함수 사용
    weighted_deposits = neighbors['deposit'] * weights
    return np.sum(weighted_deposits) / np.sum(weights)

# 단일 예측 함수
def predict_single(data_point, k):
    distances, indices = tree.query([data_point], k=k)
    neighbors = train_df.iloc[indices[0]]
    return predict_deposit(neighbors, distances[0])

# 병렬 예측 수행 함수
def make_predictions_parallel(data_scaled, k):
    return Parallel(n_jobs=-1)(delayed(predict_single)(data_point, k) for data_point in data_scaled)

# 앙상블 예측 함수
def ensemble_predictions(data_scaled, k_values):
    all_predictions = Parallel(n_jobs=-1)(
        delayed(make_predictions_parallel)(data_scaled, k) for k in k_values
    )
    return np.mean(all_predictions, axis=0)

# k 값 리스트 정의
k_values = [3, 5, 7, 9]

# Validation 예측
val_predictions = ensemble_predictions(val_scaled, k_values)

# Validation MAE 계산
val_mae = mean_absolute_error(val_df['deposit'], val_predictions)
print(f"Validation MAE: {val_mae}")

# Test 예측
test_predictions = ensemble_predictions(test_scaled, k_values)

# 결과 저장
submission_df = pd.DataFrame({
    'index': test_df['index'],
    'deposit': test_predictions
})

submission_df.to_csv('submission.csv', index=False)

print("예측이 완료되었습니다. 'submission.csv' 파일을 확인하세요.")


이제 여기서 엥간한 건 다 해봤으니 위 코드로 파생변수 추가한 데이터로 돌려보려고 했다.

1867783 rows × 35 columns

1시간 돌렸는데 안돌아가지더라.. 그래서 피처 10개만 설정해서 돌렸다.

Validation MAE: 5591.6022472139475

화나서 XGBoost로 gpt한테 바꿔달라고 했다.

XGBoost 예제

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import mean_absolute_error
import xgboost as xgb

# 데이터 로드
merged_data = pd.read_csv('merged_data_cleaned.csv')

# Train과 Test 데이터 분리
train_df = merged_data[merged_data['deposit'] != 0].copy()
test_df = merged_data[merged_data['deposit'] == 0].copy()

# 타겟 변수 분리
y_train = train_df['deposit']
X_train = train_df.drop('deposit', axis=1)

# test_df에서 deposit 열 제거
X_test = test_df.drop('deposit', axis=1)

# 범주형 변수 인코딩
le = LabelEncoder()
for col in X_train.select_dtypes(include=['object']).columns:
    X_train[col] = le.fit_transform(X_train[col].astype(str))
    X_test[col] = le.transform(X_test[col].astype(str))

# Train 데이터를 train과 validation으로 분리
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

# XGBoost 모델 생성 및 학습
model = xgb.XGBRegressor(
    n_estimators=500,
    learning_rate=0.1,
    max_depth=6, 
    subsample=0.8, 
    colsample_bytree=0.8, 
    random_state=42,
    tree_method='hist',
    n_jobs=-1
)

model.fit(
    X_train, y_train, 
    eval_set=[(X_val, y_val)], 
    verbose=100
)

# Validation 예측 및 MAE 계산
val_predictions = model.predict(X_val)
val_mae = mean_absolute_error(y_val, val_predictions)
print(f"Validation MAE: {val_mae}")

# Test 예측
test_predictions = model.predict(X_test)

# 결과 저장
submission_df = pd.DataFrame({
    'index': test_df.index,
    'deposit': test_predictions
})

submission_df.to_csv('submission.csv', index=False)

print("예측이 완료되었습니다. 'submission.csv' 파일을 확인하세요.")

# 특성 중요도 출력
feature_importance = model.feature_importances_
feature_names = X_train.columns
feature_importance_df = pd.DataFrame({'feature': feature_names, 'importance': feature_importance})
feature_importance_df = feature_importance_df.sort_values('importance', ascending=False)
print("\n상위 10개 중요 특성:")
print(feature_importance_df.head(10))

Validation MAE: 4714.424762007194

1분만에 실행되었는데 지금까지 못 넘은 4000의 벽을 가뿐히 깨버렸다.

허허.. 암울하다. 며칠동안 쟤만 삽질했는데. 그래서 걍 그리드서치해서 최적 가중치 찾고 나중에 앙상블할때 쓰는 용도로 써야겠다. 이게 특히 모델링이 너무 오래걸리니까 하루 동안 스트레스가 너무 심했다. 중간에 공부하기도 뭐하고 집중도 잘 안되고 종종 확인하기도 귀찮고 아오 그냥.