최종 knn 모델 for ensemble

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import BallTree
from sklearn.metrics import mean_absolute_error
from joblib import Parallel, delayed

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

# Train과 Test 데이터 분리(_type으로 분리가 안 되서 deposit이 0인가 아닌가로 분리함)
train_df = merged_data[merged_data['deposit'] != 0].copy()
test_df = merged_data[merged_data['deposit'] == 0].copy()

# 학습이 너무 오래걸려서 어쩔 수 없이 특성 선택
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])

# 특성 가중치 조정
# 그리드 서치로 최적 값 찾음 Valid MAE: 4646.19  
weights = np.array([10, 10, 10, 0.1, 10, 2])  # 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': range(len(test_predictions)),
    'deposit': test_predictions
})

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

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

어제 랜덤 서치로 최적의 성능을 찾아봤는데

Validation MAE: 4646.197436650035

이제 그냥 앙상블용으로 써야지! 하고 리더보드에 제출했는데 생각보다 점수가 잘 나왔다. 1등과 몇십점차이?

어, 그래서 갑자기 욕심이 생겼다. 오래 걸리겠지만 모든 피처 써서 한번 해보자. 근데 자꾸 오류나는데. 그럼 걍 수동으로 하드코딩으로 채우지 뭐! 가 아래다.

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import BallTree
from sklearn.metrics import mean_absolute_error
from joblib import Parallel, delayed

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

# Train과 Test 데이터 분리(_type으로 분리가 안 되서 deposit이 0인가 아닌가로 분리함)
train_df = merged_data[merged_data['deposit'] != 0].copy()
test_df = merged_data[merged_data['deposit'] == 0].copy()

# 코드오류나서 모든 피쳐 직접 입력
features = [
    'area_m2', 'contract_year_month', 'contract_day', 'contract_type', 'floor', 
    'built_year', 'latitude', 'longitude', 'age', 'deposit', 'nearest_park_distance',
    'park_count_500m', 'total_park_area_500m', 'park_count_1000m', 'total_park_area_1000m',
    'park_count_2000m', 'total_park_area_2000m', 'weighted_park_score', 'avg_distance_5_parks',
    'park_distance_skewness', 'park_distance_kurtosis', 'nearest_large_park_distance',
    'large_park_count_3km', 'large_park_count_5km', 'large_park_count_10km',
    'total_large_park_area_10km', 'nearest_subway_distance_km', 'school_count_within_1km',
    'closest_elementary_distance', 'closest_middle_distance', 'closest_high_distance',
    'deposit_mean', 'interest_rate', 'interest_rate_diff'
]
# 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([
    10,  # area_m2
    2,  # contract_year_month
    1,  # contract_day
    1,  # contract_type
    0.1,  # floor
    10,  # built_year
    10,  # latitude
    10,  # longitude
    1,  # age
    1,  # deposit
    1,  # nearest_park_distance
    1,  # park_count_500m
    1,  # total_park_area_500m
    1,  # park_count_1000m
    1,  # total_park_area_1000m
    1,  # park_count_2000m
    1,  # total_park_area_2000m
    1,  # weighted_park_score
    1,  # avg_distance_5_parks
    1,  # park_distance_skewness
    1,  # park_distance_kurtosis
    1,  # nearest_large_park_distance
    1,  # large_park_count_3km
    1,  # large_park_count_5km
    1,  # large_park_count_10km
    1,  # total_large_park_area_10km
    1,  # nearest_subway_distance_km
    1,  # school_count_within_1km
    1,  # closest_elementary_distance
    1,  # closest_middle_distance
    1,  # closest_high_distance
    1,  # deposit_mean
    1,  # interest_rate
    1   # interest_rate_diff
])
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': range(len(test_predictions)),
    'deposit': test_predictions
})

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

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

Validation MAE: 3280.8897024956227

2시간이 걸렸다. 와, 3000대? 이거 리더보드 2000대 나오는 거 아니야? 하고 제출했더니 성능이 떨어졌다…

그래서 그냥 맨 위걸로 사용하기로 했다. 쓰읍 아쉽넹.

이러고있는데 동준이가 시간 오래걸리는 코드 tqdm을 사용해보라고 했다.

tqdm? 그게 뭐지? 하고 써봤는데

image-20241018174258234

아, 진작 쓸걸. 다음부터는 오래걸리는 코드는 tqdm을 꼭 사용해야겠다.

이제 뭐하지?

팀이 적은건데

  • 모델 모듈화 작업 마무리
  • 군집화 마무리 -> 피처 추가
  • sin cos 주기성 반영해보기
  • MLP + GNN 합쳐보기
  • MLP - DB 연결시키는 코드
  • DB 연결 함수/파일 만들기 + 결과 볼 수 있는 쿼리(파이썬에서 볼 수 있게)
  • 하이퍼 파라미터 튜닝 파일 or wandb
  • 동준이가 MLP 써본대
  • 더 추가할만한 변수 있는지 생각해보기
  • requirement 파일 추가하자!

난 여기서

  • 하이퍼파라미터랑 앙상블 쪽 모듈 해보고싶은 느낌

  • 모듈화되어있는 거 계속 돌려볼까?

  • 내 knn이랑 다른 트리기반모델이랑 앙상블 돌려보기

오케이. 주말에 저거하자.