upStage_ML_project

TOLL TERRY·2024년 1월 26일
0

아파트 실거래 예측(ML)

1. 평가지표

2. 학습데이터

  • 학습 데이터의 기간은 2007년 1월 1일부터 2023년 6월 30일까지 1,118,822(약 120만개)

  • 평가 데이터는 학습 데이터기간 이후 3개월인 2023년 7월 1일부터 2023년 9월 26일까지의 정보로 구성 (9272개)

3. Model Select

  • focus : 이상치 탐지와 보간법(결측치 데이터의 후처리), 특정 컬럼을 활용하는 것을 생각했었습니다.

파생변수 1

  • 추가로, 파생변수를 생성하기 위해서, 각 아파트의 브랜드명에 따라 달라진다고 생각이 들었고, 최고층에 따라서도 평균 가격을 활용할 수 있다고 판단됩니다.

  • 보시면, RF모델에선 사용된 feature들의 중요도를 0.1이상만 가지고 이용한다고 했을때와의 차이를 보면서 진행하였고, public score는 다음과 같이 0.1이상의 중요도와 2020년 이후의 데이터를 사용했을때 좋은 결과를 보였습니다.

파생변수 2

  • 또 다른, 파생변수로는 apt_counts: 같은 아파트 이름을 갖는 수와 상위 10개에 해당하는 시공사에 대한 변수를 생성하였음.

  • 위의 분포에서 왼쪽으로 치우친 모습이라, log 변환을 이용하여 정규화 모양으로 만들어서 사용하였음. 위 결과 feature중요도가 top2에 해당하는 점에 도달할 수 있었습니다.

Model

  • 기존의 모델은 lightGBM등 tree 모델을 사용하여 보았지만, 다양한 모델을 함께 보기 위하여 pycaret를 이용하여 모델별 비교를 진행했습니다.

  • Tree모델과 linear(예측을 하는데, 이상치가 크면 부족할 수 있음)모델을 비교해보았다면, 각각의 모델을 보면 아무래도 Tree모델에서 성능이 비교적 좋게 나오는 것을 확인할 수 있음.

Model 2

  • 다음으로, 강남 여부의 지리적 위치를 추가하기 위해, 아파트 상위 거래 금액별로 강남구, 서초구, 용산구, 송파구을 하나의 '0' 라벨링을 진행하였고, 이 외 성동구와 마포구는 "1" 라벨링을 진행하고, 나머지 지역구들은 "2"라벨링을 진행하였다.

파생변수 3

  • 다음, 월별 금리데이터를 이용하였다.

  • e-나라지표를 통해서, 기준금리를 이용하였으며, 한국은행의 기준 금리을 보고서 결측된 기준금리 데이터들은 채워넣었다.

  • 그리고, 모델별 날짜 데이터을 범주형 데이터 -> 연속형 데이터로 변경
  • "지역별 동"의 데이터를 다시 재레이블을 진행하였고
  • Test데이터셋이 2023년을 예측하다보니, 2018년 이후의 데이터만으로 최신 데이터만 이용한 모델도 진행해본 결과, 모델의 성능은 2018년 이후의 데이터를 이용할 경우, 오히려 성능이 떨어진 것을 보았습니다.

4. 피드백

질문사항: 2가지

  1. 이상치탐지 관련해서, 25%, 75%에 해당하는 이상치를 제거 중인데, 이를 더 효과적으로 탐지해서, 제거하거나 대체하는 방식이 있을까요?

답변: 예측 모델을 넣어주자. 하지만, 정의가 필요 - 이상치가 나쁜건가?
효과가 크지(크리티컬)않을 듯 보임.

  1. 데이터 특징을 추가로 추천해주실 특징이 있을까요?
    답변 : base code내에서 특징 중요도를 모두 반영할 수 있도록 컬럼이 많은 것이 좋음
    2000년도 데이터로만 진행해보는 것도 가능함.
    건설사 데이터(결측데이터)로 특징을 가질 수 있을 것이라 판단됨.

<추가사항>

  • 테스트 데이터셋이 7월 9월를 맞추는 것임. —> train_test split (랜덤 state)가 시계열 데이터에서 반영이 더디게 모델이 될 수 있음

  • 과적합 판단됨(Tree 모델의 depth의 max를 줄이는 방안, k-fold)

  • 규제 요소가 필요해보임

  • columns가 부족해보임. ---> 학습 데이터 100만개에 해당하기에, 컬럼 추가.

  • 서로 다른 2개의 Tree모델이 예측한 것의 가중치(pred1 <0.8>, pred2 <0.2>) 이용하여 예측 모델 만들어보기. (앙상블 효과 발생가능함)


5. 최종순위

3위를 달성하였습니다.


6. 추가적 사항

  • 고가 아파트를 예측하는 모델 따로 만들어보기
  • 단위기간별, 왜도, 첨도, 분산을 각 단위별로 쪼개서 사용해보기
  • 테스트 데이터에 이상한 데이터가 있는지 확인하기(RMSE가 10만이상 차이가 난다?, 제곱,루트)
  • Time series별 k-fold을 적용해보기

7. 1등코드리뷰

# '아파트명'이 '힐스테이트 서초 젠트리스'인 행의 '번지', '본번', '부번' 업데이트
concat.loc[concat['아파트명'] == '힐스테이트 서초 젠트리스', '번지'] = '557'
concat.loc[concat['아파트명'] == '힐스테이트 서초 젠트리스', '본번'] = 557
concat.loc[concat['아파트명'] == '힐스테이트 서초 젠트리스', '부번'] = 0

# '아파트명'이 '서초포레스타2단지'인 행의 '번지', '본번', '부번' 업데이트
concat.loc[concat['아파트명'] == '서초포레스타2단지', '번지'] = '384'
concat.loc[concat['아파트명'] == '서초포레스타2단지', '본번'] = 384
concat.loc[concat['아파트명'] == '서초포레스타2단지', '부번'] = 0

# 도로명을 기준으로 그룹화하고, 각 그룹 내에서 '아파트명'의 결측치를 채웁니다.
concat['아파트명'] = concat.groupby('도로명')['아파트명'].transform(lambda x: x.fillna(method='ffill').fillna(method='bfill'))

# 각 행별로 '도로명'에 '구로동로'가 포함 된다면 '아파트명'의 결측치를 해당 행의 도로명으로 대체(아파트명이 실제로 없기에 도로명으로 대체)
concat['아파트명'] = concat.apply(lambda row: row['도로명'] if (row['시군구'] == '서울특별시 구로구 구로동' and pd.isna(row['아파트명']) and ('구로동로' in row['도로명'] or '도림로' in row['도로명'] or '디지털로' in row['도로명'])) else row['아파트명'], axis=1)

# 아파트명 직접 대체
concat.loc[concat['도로명'] == '천호대로77다길 11-6', '아파트명'] = '해오름아파트'
concat.loc[concat['도로명'] == '동호로11마길 20-8', '아파트명'] = '성민아트'
concat.loc[concat['도로명'] == '여의대방로62길 24', '아파트명'] = '효성아파트'
concat.loc[concat['도로명'] == '국사봉1길 18', '아파트명'] = '상진빌딩'
concat.loc[concat['도로명'] == '동호로11바길 30-8', '아파트명'] = '석종아파트'
concat.loc[concat['도로명'] == '신수로 89', '아파트명'] = '신수동 자이언트 아파트'
concat.loc[concat['도로명'] == '양재대로71길 2-11', '아파트명'] = '올림픽아트빌'
concat.loc[concat['도로명'] == '청계천로 295', '아파트명'] = '동대문신발상가'
concat.loc[concat['도로명'] == '대학로7길 15-4', '아파트명'] = '연건동아파트'
concat.loc[concat['도로명'] == '강남대로27길 7-14', '아파트명'] = '한일인텔빌라'
concat.loc[concat['도로명'] == '명륜2길 9-14', '아파트명'] = '명륜2길 9-14'
concat.loc[concat['도로명'] == '동호로5길 12', '아파트명'] = '다다유크레스'
concat.loc[concat['도로명'] == '증산로23길 8-8', '아파트명'] = '우일아파트'
concat.loc[concat['도로명'] == '낙산성곽동길 35-1', '아파트명'] = '낙산성곽동길 35-1'
concat.loc[concat['도로명'] == '서리풀길 14', '아파트명'] = 'callia vill'
concat.loc[concat['도로명'] == '효령로 200', '아파트명'] = '효령로 200'
concat.loc[concat['도로명'] == '화곡로 176-5', '아파트명'] = '화곡로 176-5'
concat.loc[concat['도로명'] == '광나루로12길 5', '아파트명'] = '광나루로12길 5'
concat.loc[concat['도로명'] == '개운사길 83-15', '아파트명'] = '개운사길 83-15'
concat.loc[concat['도로명'] == '도곡로93길 23', '아파트명'] = '대치 르엘'
concat.loc[concat['도로명'] == '까치산로14길 26-13', '아파트명'] = '힐탑아파트'
concat.loc[concat['도로명'] == '양천로 677', '아파트명'] = '염창하이츠빌딩'
concat.loc[concat['도로명'] == '천호대로118길 10', '아파트명'] = '천호대로118길 10'
concat.loc[concat['도로명'] == '난계로15길 36-6', '아파트명'] = '난계로15길 36-6'
concat.loc[concat['도로명'] == '난계로15길 36-8', '아파트명'] = '난계로15길 36-8'
concat.loc[concat['도로명'] == '이문로35아길 16', '도로명'] = '이문로16길 35' # 잘못된 도로명 주소 수정
concat.loc[concat['도로명'] == '이문로16길 35', '아파트명'] = '이문로16길 35'
concat.loc[concat['도로명'] == '마조로1길 45', '아파트명'] = '아파트'
concat.loc[concat['도로명'] == '성지3길 7', '아파트명'] = '성지3길 7'

감사합니다.

profile
행복을 찾아서(크리스 가드너)

0개의 댓글