| 실험가설 | 탐구 결과 |
|---|---|
| 학습데이터와 예측데이터의 아파트는 다른 아파트인지 | train.csv안에 test.csv에 있는 아파트 명이 그대로 있는 경우가 많은(과반수 이상) 것으로 보여서, 일부러 다른 아파트명을 갖도록 대회를 설계하지는 않은 것으로 보임. 그것보다는 오히려 의도적으로 같은 브랜드의 아파트가 훈련과 시험 데이터세트에 분배되도록 세심하게 데이터를 나눈 것으로 보인다. 그렇지 않고 가나다 순으로 짤랐다면, 같은 아파트가 train과 test 양쪽에 모두 있지는 않았을 것. 다만 train.csv에서 아파트명이 공백인 갯수가 2126건. test.csv에서 10건이 있음을 고려 필요 |
| 학습 도중 질문 | 찾아본 결과 |
|---|---|
| RMSE가 실제 아파트 실거래가와 예측값의 차이를 의미하는지 | MAE (Mean Absolute Error)와 RMSE (Root Mean Squared Error)는 둘 다 예측값과 실제값 간의 차이를 측정하지만, 결과는 다릅니다. 간단히 설명하자면: MAE는 예측 오차의 절대값을 평균한 값으로, 오차 크기를 단순히 합산한 뒤 평균을 내는 방식입니다. RMSE는 오차의 제곱을 평균한 후 제곱근을 취한 값으로, 큰 오차일수록 더 크게 반영합니다. 이렇게 MAE는 모든 오차를 동일하게 취급하는 반면, RMSE는 큰 오차에 더 민감하게 반응하기 때문에 값이 달라집니다.![]() |
| .ipynb를 .py로 바꾸기 | .ipynb는 가설을 바꿔서 코드수정을 하면 하나하나씩 실행을 해야 해서 힘들다. jupyter nbconvert --to script base1.ipynb 명령어로 .py로 변환함. 단위테스트와 기능별 학습에는 .ipynb이, 전체 코드를 완성하고 여러가지 모듈을 결합해서 실행할 때는 .py 및 클래스화가 필요 |
| RandomForest | str, datetime 등 int가 아닌 값이 들어가면 오류난다. 옆에는 datetime으로 형변환 시도했던 코드dt_train['cont_date'] = pd.to_datetime(dt_train['cont_year']+'-'+dt_train['cont_month']+'-01') |
| One-Hot Encoding의 문제 | Label Encoding은 순서형 변수로 기계한테 오해받기 딱 좋아서 One-Hot Encoding 했더니 고유값이 1만개 가까워서, 1만개의 칼럼이 생성됐는데, 모델 훈련하는 부분에서 갑자기 프로그램이 그냥 중단되면서, 결과 csv 파일도 나오지 않아서 살펴보니, 너무 많은 칼럼이 들어가면 시스템 성능에 제한을 걸었을 경우 그렇다고 한다. 그래서 결국 Label Encoding으로 함. |
| 데이터 전처리 하기 전에 train과 test를 합치자 | 인코딩을 하면 새로운 칼럼을 만드는데, 구와 동의 경우는 가짓수가 많지 않아 train에 있던 3-400개 데이터 종류와 test의 종류가 같고, 또 새롭게 칼럼을 만드는 방식도 같을 수 있다. 그런데 주소(고유값 9천개)의 경우 train set에 없던 row가 test set에 포함되면 전체 순서가 꼬여서, train과 test가 매치되지 않아서 결국 엉뚱한 데이터로 훈련시키니 결과값이 안 나오는 것 같다.안 된다. 또, 매번 두번씩 전처리하기도 힘들다. |
| RandomForest Estimator | 베이스라인에 기본 5로 되었었는데 GPT가 100이 좋다 그래서 50으로 올렸더니 검증 RMSE가 12,000에서 11,200으로 소폭 올랐다. 100으로 올렸지만 검증 RMSE는 11241로 큰 차이가 없었다. 역시 하이퍼파라미터는 점진적 개선만 가능한 듯 싶다. 과격한 상승은 발상 전환. |
| 칼럼명 한글화? | 칼럼명이 한글이면 학습 못한다고 해서 한글칼럼 하나를 영어로 했는데 검증 11191로 10정도 뿐이 차이가 없었다. |
| 검증 후에 검증셋은 훈련을 | 원래 베이스라인에는 train.csv와 test.csv와 주고, train.csv를 8:2로 분리하고 8로 훈련하고, 2는 검증셋으로 훈련하고, 8로 훈련한 모델로 test.csv를 훈련하고 마무리하는데, 생각해보니 10으로 훈련하는게 좋겠다 싶어서, 모델 훈련하기 전에 train을 백업하고 X_train = dt_train.copy() 8:2로 나눠서 훈련해서 검증값 RMSE 뽑아낸 뒤에, 백업한 파일을 가지고 와서 다시 훈련을 시켰다. |
| 절감한 필요 | 필요를 느낀 배경 |
|---|---|
| 파이썬 공부 | 엑셀로 데이터를 파악해보니 기술적인 부분, 예를 들어 결측치 수량, 고유값 갯수 등은 백만개의 데이터를 다 보기 어려웠고, 엑셀 수식을 쓰는 것도 불편하여서, 파이썬 통계 라이브러리에 익숙해지는 것의 필요성을 제대로 느꼈다. 기본기가 가장 중요하다. |
| 개인 코드 저장소 보유 필요 | 여러 곳의 파이썬 강의 때마다 나오는 파이썬 코드를 여러곳의 장소에 메모만 하다보니 막상 필요할 때 생각나거나 갖다 쓸 수 없었다. 한 곳의 코드 저장소에 주석과 함께, 남에게 보이기 보다는 나를 위한 파이썬 코드 저장소가 필요함을 절감했다. (파이썬 기본, 데이터분석, AI모델링, AI프로젝트) 깃헙 저장소를 만들어서 필요할 때마다 복습하며 재사용해야겠다. 다른 사람이 보도록 하기 위한 Github을 만들어야 한다는 부담감 때문에 그동안 별로 흥미를 느끼지 못했는데, 내가 실제로 사용할 저장소라고 생각하니, 꼭 필요한 정보만 넣어서 필요할 때 요긴하게 사용할 수 있는 연장통으로 만들어야겠다고 다짐했다. |
| 시각화 및 통계 | 감이 아닌 전체적인 통계나 시각화를 통해서 전문적인 역량이 필요함을 느꼈다. |
| 원천 데이터 검증 | 특히 raw데이터에 의외로 오류가 많았다. " " 로 되어있어서 결측치인 것을 초반에 몰랐던 것도 있었고, 주소를 찾아봤더니 없는 주소도 있었다. 백만건 미만 정보는 엑셀, 이상은 Power BI, 중간에 출력해서 오래도록 데이터를 보는 것도 필요했다. 통계와 시각화로만 볼 수 없는 흐름과 스토리를 그릴 수 있었다. |
| 점수 | 검증셋 | 테스트셋/검증셋 | 제목 | 피처 | 실험내용 |
|---|---|---|---|---|---|
| 125,621 | 코드 미실행 | 관계없음 | 베이스라인 코드 실행하지 않고 주어진 sample.csv 샘플 제출 | ||
| 48,453 | 5,851 | 8.3배 | 베이스코드 실행 | 베이스라인 실행 후 나오는 40여개 피처 | Upstage제공 베이스라인 코드를 그대로 실행. 코드 이해와 학습을 중심으로 작업 ( RMSE: 검증만 이상치 제거하여 훈련시킨 모델로 이상치 제거 안 된 테스트 데이터를 추론하니 차이 큼) |
| 25,112 | 7,852 | 3.2배 | 베이스코드에서 이상치제거 부분 삭제 | 베이스라인 실행 후 나오는 40여개 피처 | 베이스라인에서 이상치 제거 소스를 주석화시켜서, 결국 이상치 제거 없이 실행한 코드 ( RMSE: 이상치 제거 안 한 원본 검증 데이터로 훈련한 모델이 똑같이 이상치 제거 안 한 테스트 데이터를 훈련하여 차이가 8.3배에서 -> 3.2배로 줄어듬) |
| 24,157 | 7,828 | 3.1배 | 이상치 280미만으로 조정 | 베이스라인 40여개 피처 | 이상치를 전용면적 280개 초과하는 것들을 test셋에서 지웠음 ( RMSE: 이상치 제거로 인한 영향은 거의 없음) |
| 테스트셋 | 검증셋 | 테스트셋/검증셋 | 제목 | 피처 | 실험내용 |
|---|---|---|---|---|---|
| 55,338 | 23,649 | 2.3배 | 나만의 실험설계 시작 | 전용면적, 계약년월==(2개) | 한번에 실행하기 쉽도록 베이스라인.ipynb를 .py로 옮기고, 베이스라인 코드에서 전처리 및 내가 왜 쓰는지 이해하지 못하는 코드 제거 후 데이터 읽고, 피처 읽어들이고, 바로 훈련하는 소스만 넣어서 MVP(Minimum Viable Product: 최소가능제품) 개념으로 나만의 데이터가설 실험 시작. (검증셋 RMSE: 23,649) |
| 54,033 | 24,031 | 2.25배 | 계약년과 월을 추가 | 전용면적, 계약년, 계약월 | '계약년월'을 계약년과 계약월로 분리생성 후, '계약년월' 칼럼 삭제. 년월을 분리하면 기계가 날짜(시계열)로 인지를 더 잘할 것 같다고 하여 적용. 약 1300점 정도의 미비한 효과. 기계가 날짜를 더 잘 인식하도록 날짜열 형식을 int에서 datetime으로 변환해 보았으나 Random Regression 모델이 int만 받아들여서 포기.( RMSE: ) |
| 26,017 | 10,631 | 2.45배 | 구와 동을 One-hot으로 추가 | 전용면적, 계약년, 계약월, 구, 동, Ver.1 | 구와 동은 숫자가 아니라 RandomForest가 받아들이지 못하여 Encoding이 필요하다. 주소정보(구와 동)는 순서가 있는 Ordinal 정보가 아니라서 Label Encoding보다는 One-hot encoding이 적합하다는 GPT의견 수용하여 구와 동을 각각 One-Encoding 수행 (검증셋 RMSE: 10,631) |
| 26,017 | 10,631 | 2.45배 | 년월 int로 형변환 | 전용면적, 계약년, 계약월, 구, 동, Ver.2 | 다시 보니 이전 모델에서 년월이 str로 되어있었어도 에러가 안나고 잘 되었었기에 혹시 더 잘 나올까 싶어 타입을 int로 바꾸고 다시 해보니 2개 행이 각각 target값이 1씩 차이가 났었다. 70159 to 70160. 점수는 0.0001점 올라갔다. 미비한 효과(검증셋 RMSE: 10,631) |
| 25,877 | 10,971 | 2.36배 | 층 칼럼 추가 | 전용면적, 계약년, 계약월, 구, 동, 층 | 지하층의 경우 가격이 낮은 경우가 있다하여 층 칼럼 추가(검증셋 RMSE: 10,971) |
| 54,571 | 9,272 | 5.86배 | 아파트 단지와 비슷한 급의 단지번지 주소 추가 | 전용면적, 계약년, 계약월, 구, 동, 층, 시군구+번지 | 좌표로 주소를 구분하는 것보다는, 이미 지역별 특성에 대한 가격정보를 담고 있는 시군구/아파트단지명이 있어서 이걸로 하기로 결정. 구,동의 지역 정보보다도 상세한 아파트 단지별 가중치를 아파트 단지와 번지, 도로명이 가지고 있으나 아파트 단지는 '현대'로만 되어있어서 지역별 '현대'가 겹치는 경우가 있기에 고유값이 6천여개뿐이고, 도로와 번지는 둘다 고유값이 9천여개로 품질은 비슷하지만 도로는 1200여개, 번지는 225여개 결측치가 있어서 번지로 선택. 시군구+번지를 붙여서 칼럼 생성하고 기계가 읽을 수 있도록 9천여개 고유값을 Label Encoding 성공하였으나, Label Encoding은 순서대로 0부터 고유번호를 넣는데 잘 모르고 train과 test를 따로 순차적으로 하게 되니 test에는 고유값이 2천여개 밖에 안 되어서 같은 숫자라도 train과 test가 다른 의미를 갖게 되어서 테스트 RMSE가 엄청 높게 나왔다. 대신에 검증 셋은 먼저 고유번호를 Encoding했으니 잘 나온거는 타당하다. (검증셋 RMSE: 9,272) |
| 21,791 | 8,771 | 2.48배 | train+test 전처리 | 상동 | 위의 실수를 만회하기 위해 test와 train을 합치고 Label Encoding을 하고 다시 나누었더니 테스트 RMSE가 이전 최고치보다 4,086점 떨어졌다. (검증셋 RMSE: 8,771) |
| 20,774 | 7,853 | 2.65배 | 건축년도 추가 | 상동 + 건축년도 | 건축연도는 오래되었어도 재건축 때문에 투자/기 목적으로 가격이 더 높은 경우가 많기에 기계에 혼선을 줄거라고 판단되어 제거했으나, 팀장님이 여러 툴을 돌리신 결과 유의미하다고 하셔서 첨가하여 약 1천점 줄임.(검증셋 RMSE 7,853) |
| 19,331 | 8,213 | 2.35배 | 로그변환 | 위줄과 같음 | 멘토님 조언대로 로그변환 (Log Transformation, 단위가 높은 y값을 로그 시켜서 모델 훈련한 뒤 테스트 예측해서 나온 값을 제곱하여서 높은 단위의 차이가 완만하게 적용되도록 하는 기법으로 이해) 적용하여 높은 오차의 반영율을 낮춤. 희한한 것은 검증셋 RMSE는 오히려 올라가서 하마터면 리더보드에 제출 안 할뻔. 왜 로그변환에서 검증셋과 테스트셋이 역으로 측정되었을까? (검증셋 RMSE 8,213) |
| 19,435 | 8,280 | 2.38배 | 번지가 있으니 구 정보가 중복일지도 몰라 지워봄 | 위에서 구 정보만 제거 | 번지명이 들어갔으니 구 정보는 중복이라 편향될 수 있을 것 같아 지워봤으나 오히려 올라갔다. 이번거는 검증셋에서도 동일한 방향으로 변했다. (검증셋 RMSE 8,280) |
| 중간19,207 (최종15,342) | 8,028 | 2.39배 | 전용면적 기준 이상치 소량 제거 | 전용면적, 계약년, 계약월, 구, 동, 층, 시군구+번지, 건축년도 | 전용면적이 280이상인 것들만 이상치 처리해서 14건 제거했는데, 25천점짜리 베이스라인에서는 1천점 떨어졌는데, 내것 19,331에서는 1백점뿐이 떨어지지 않았다. 성능이 올라갈수록 상승폭이 줄어드는 것 같다.(검증 RMSE 8,028) |
| 대회마감 | 8,115 | 계절 파생변수 추가 | season칼럼 | 시계열정보가 필요하다하여 월정보로 계절정보 추가. 12,1,2 -> summer, 이런식으로 4계절 속성으로 칼럼을 만들고, RandomForest가 winter같은 문자를 안 받아서, One-hot encoding을 했다. 이 때 drop_first=True를 하면 원래 칼럼인 'season'과 첫번째 칼럼인 'winter'를 없애고, 나머지 3개 조합으로 4가지를 다 표현한다. concat = pd.get_dummies(concat, columns=['season'], drop_first=True) 검증RMSE가 오히려 올라갔는데, 실제 리더보드에서는 마감이라 확인할 수 없었다. (검증RMSE: 8,115) |
print(f'RMSE test: {np.sqrt(metrics.mean_squared_error(y_val, pred))}')