# 데이터 로드
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])

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

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

# 예측 수행 함수
def make_predictions(data_scaled, k):
    distances, indices = tree.query(data_scaled, k=k)
    predictions = []
    for i in range(len(data_scaled)):
        neighbor_indices = indices[i]
        neighbor_distances = distances[i]
        neighbors = train_df.iloc[neighbor_indices]
        pred = predict_deposit(neighbors, neighbor_distances)
        predictions.append(pred)
    return predictions

# Validation 예측
k = 5
val_predictions = make_predictions(val_scaled, k)

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

# Test 예측
test_predictions = make_predictions(test_scaled, k)

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

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

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

저번에 만든 베이스 모델 코드에서 여러가지 시도해보려고 한다.

우선 활용할 정식피처들은 너무 많아서 오래걸릴 것 같아 피처는 적게 진행하고 여러 테스트들을 통해 가장 잘나온 모델로 정식피처들을 도입할 계획

image-20241016134203177

8분정도 걸리기에 우선은 1/100 샘플링 한걸로 테스트해보니 1분정도로 시간이 줄긴 했다. 근데 확실히 위치들이 너무 흩어져서 그런가 MAE도 급격히 떨어지긴 했다. 그래도 이걸로 테스트해야될것 같다.

1/100 샘플링

  • 시간: 8분 -> 1분
  • mae: 5399 -> 8816

image-20241016134252502

  1. 특성 가중치 조정

    weights = [1, 1, 2, 0.5, 0.5, 1]  # 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
    

    아무래도 area_m2의 중요도가 가장 높기때문에 가중치를 조정해서 돌려봤다. 300 정도감소했다.

    Validation MAE: 8816.34 -> 8580.52

    weights = [1, 1, 5, 0.5, 0.5, 0.5] # latitude, longitude, area_m2, floor, built_year, contract_year_month
    

    Validation MAE: 8580-> 8537.94

    더이상 유의미한 감소는 드라마틱하게 나타나지 않을 것 같으니 우선 넘어가고 나중에 자동으로 찾아보자.

  2. 계약 시점과의 시간 차이를 고려하여 최신 데이터에 더 큰 가중치를 줄 수 있습니다.

이게 점점 시간이 지날수록 계속 전세값이 올라가는 그래프니까 최신 데이터에 더 가중치를 주면 점수가 올라갈 것 같았다.

def predict_deposit(neighbors, distances, query_date):
    time_diff = query_date - neighbors['contract_year_month']
    time_weights = np.exp(-time_diff / 12)  # 1년 차이에 대해 e^-1 가중치
    weights = 1 / (distances + 1e-5) * time_weights
    weighted_deposits = neighbors['deposit'] * weights
    return np.sum(weighted_deposits) / np.sum(weights)

def make_predictions(data_scaled, k, dates):
    distances, indices = tree.query(data_scaled, k=k)
    predictions = []
    for i in range(len(data_scaled)):
        neighbor_indices = indices[i]
        neighbor_distances = distances[i]
        neighbors = train_df.iloc[neighbor_indices]
        pred = predict_deposit(neighbors, neighbor_distances, dates[i])
        predictions.append(pred)
    return predictions

# Validation 예측
k = 5
val_predictions = make_predictions(val_scaled, k, val_df['contract_year_month'].values)

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

# Test 예측
test_predictions = make_predictions(test_scaled, k, test_df['contract_year_month'].values)

Validation MAE: 8537.94 -> 14897.64

왜 올라가지? 여러 다른 방법으로도 테스트 해봤는데 점수가 좋아지지 않았다. 나중에 코드 한번 더 만져보자.

  1. 동적 k 선택: 데이터 포인트마다 다른 k 값을 사용할 수 있습니다. 예를 들어, 밀집된 지역에서는 작은 k를, 희소한 지역에서는 큰 k를 사용할 수 있습니다.
# 예측 수행 함수
def make_predictions_dynamic_k(data_scaled, min_k, max_k):
    distances, indices = tree.query(data_scaled, k=max_k)
    predictions = []
    for i in range(len(data_scaled)):
        k = min(max_k, max(min_k, int(np.sum(distances[i] < np.median(distances)))))
        neighbor_indices = indices[i][:k]
        neighbor_distances = distances[i][:k]
        neighbors = train_df.iloc[neighbor_indices]
        pred = predict_deposit(neighbors, neighbor_distances)
        predictions.append(pred)
    return predictions

# Validation 예측
min_k = 3
max_k = 10
val_predictions = make_predictions_dynamic_k(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_dynamic_k(test_scaled, min_k, max_k)

Validation MAE: 8537.94 -> 8538.69

아주 조금 MAE가 올랐다. 근데 이 정도면 샘플링때문에 좀 그런게 있을 것 같아 원본 데이터로 두 코드를 다시 비교해봤다.

이전 코드(9분 소요): 5190.820037476034

아니 하려했는데 45분짼데 아직도 안되네. 도랏네.

하도 안되서 gpt한테 빠르게 할 수 있는 방법을 찾아달라고 했다.

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)

위처럼 코드를 수정하니 40분넘어도 안되던게 4분안에 끝났다. 뭐지?

Validation MAE: 5197.31

병렬 처리:

  • 첫 번째 코드: 순차적으로 처리합니다.
  • 두 번째 코드: joblib의 Parallel과 delayed를 사용하여 병렬 처리를 수행합니다. 이는 여러 CPU 코어를 동시에 활용하여 처리 속도를 크게 향상시킬 수 있습니다.

BallTree 구성:

  • 첫 번째 코드: leaf_size=2로 설정되어 있습니다.
  • 두 번째 코드: leaf_size=40으로 설정되어 있습니다. 이는 트리 구축 시간과 쿼리 시간의 균형을 맞추기 위한 것으로, 일반적으로 더 큰 leaf_size가 성능을 향상시킬 수 있습니다.

예측 함수 구조:

  • 첫 번째 코드: make_predictions_dynamic_k 함수가 전체 데이터셋에 대해 한 번에 쿼리를 수행하고 루프를 돕니다.
  • 두 번째 코드: predict_single 함수가 각 데이터 포인트에 대해 개별적으로 쿼리를 수행합니다. 이는 병렬 처리를 위해 설계되었습니다.

예측 실행:

  • 첫 번째 코드: make_predictions_dynamic_k 함수를 직접 호출합니다.
  • 두 번째 코드: make_predictions_parallel 함수를 통해 predict_single 함수를 병렬로 실행합니다.

numpy 사용:

  • 첫 번째 코드: 리스트를 사용하여 예측 결과를 저장합니다.
  • 두 번째 코드: numpy 배열을 더 광범위하게 사용하여 계산 효율성을 높입니다.

데이터 타입:

  • 첫 번째 코드: weights를 리스트로 정의합니다.
  • 두 번째 코드: weights를 numpy 배열로 정의하여 계산 효율성을 높입니다.

코드 모듈화:

  • 첫 번째 코드: 예측 로직이 하나의 함수에 포함되어 있습니다.
  • 두 번째 코드: 예측 로직이 predict_single과 make_predictions_parallel로 나뉘어 있어 더 모듈화되어 있습니다.

메모리 사용:

  • 첫 번째 코드: 전체 데이터셋에 대한 거리와 인덱스를 한 번에 메모리에 저장합니다.
  • 두 번째 코드: 각 데이터 포인트에 대해 개별적으로 쿼리를 수행하므로 메모리 사용량이 줄어들 수 있습니다.

코드 신기하네. 근데 결국 mae가 향상되지는 않았다.

Validation MAE: 9124.02829433044

val_predictions = handle_outliers(val_predictions, threshold=5) # 임계값을 높임 Validation MAE: 8768.353798504284

Validation MAE: 8552.217652688742

Validation MAE (Log-based): 8552.217652688742

Validation MAE (Percentile-based): 8643.713458870265

Validation MAE (Region-based): 8545.55447674819