티스토리 뷰

 kaggle 사이트에서 competitions 카테고리를 보다가 "House Prices - Advanced Regression Techniques" 이라는 제목을 봤는데 뭔가 재미있어 보여서 연습도 할겸 시도해보았다. 문제는 train set로 여러 집들의 세부사항과  그 판매 가격이 주어질 때 적절한 모델을 통해 이를 학습 시켜 test set에 있는 집들의 세부사항으로 그 가격들을 모두 예측하는 것이다. 이때 집들의 세부사항은 무려 80개나 주어진다. 주어지는 세부 사항들의 예시는 아래와 같다.

○ MSSubClass: The building class
○ MSZoning: The general zoning classification
○ LotFrontage: Linear feet of street connected to property
○ LotArea: Lot size in square feet
○ Street: Type of road access
○ Alley: Type of alley access
○ LotShape: General shape of property
○ LandContour: Flatness of the property
○ Utilities: Type of utilities available
......

 

 

 

1. train, test data 마련

 

 Google Colab으로 코딩했어서 데이터를 쉽게 불러왔다. 먼저 origin_data에 문제에서 주어진 train case를 저장하였다. 또한 input 값에서는 집의 Id와 SalePrice (판매가격)이 필요하지 않기 때문에 제외해주었다.

origin_data = pd.read_csv('//content//train_case.csv')
origin_input = origin_data.drop(["Id","SalePrice"], axis=1)

 

근데 input 데이터를 마련할 때 하나 문제가 되었던건 집들의 세부사항 중에 숫자로 표현되어있는 것도 있었지만 Street (근처 도로 유형 e.g. Pave, Gravel) 과 같이 문자 형태로 저장되어있는 정보가 많았다. 그래서 문자열 형태의 정보들을 모두 One-Hot-Vector 형태로 바꾸어주는 과정이 필요했다. 아래 코드에서 categorical_columns는 문자열 형태(object)의 정보가 담긴 column들의 집합이다. 이 집합에 포함된 column들의 값들을 모두 one-hot-vector로 바꾸어주었다. 추가적으로 머신러닝 모델을 훈련시킬 때 NaN(비어있는값)이 있으면 안돼서 비어있는 값들을 모두 평균으로 해주었다. (const = 0으로 했을 때보다 좋았음)

#one-hot-vector로 변환하기
categorical_columns = origin_input.select_dtypes(include=['object', 'category']).columns.tolist()
input_data = pd.get_dummies(origin_input, columns=categorical_columns)

#NaN을 평균으로 교체하기
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy='mean')
input_data_imputed = imputer.fit_transform(input_data)

 

 test 데이터는 원래 데이터에서 집의 판매가격이어야하므로 SalePrice 열의 값들을 모두 저장해주었다.

target_data = origin_data.loc[:,"SalePrice"]

 

 결과적으로 이를 이용해 데이터를 나누어주었다.

train_input, test_input, train_target, test_target = train_test_split(input_data_imputed, target_data, random_state=42)

 

 

2. 다중 선형 회귀 모델 MLR

 아직 머신러닝을 심도있게(?) 배운 것은 아니라서 그냥 선형 회귀를 이용하였다. 차수는 2차항까지만 하였고 전처리도 해주었다.

# 2차항 추가
poly = PolynomialFeatures(degree=2, include_bias=False)
train_poly = poly.fit_transform(train_input)
test_poly = poly.transform(test_input)

# 전처리
scaler = StandardScaler()
train_poly = scaler.fit_transform(train_poly)
test_poly = scaler.transform(test_poly)

 

 이후 만들어 놓은 훈련 세트를 바탕으로 선형 회귀 모델을 학습시켰다. 이 머신러닝 모델의 편향 여부를 확인하기 위해 train 세트와 test 세트 각각에서의 점수를 출력해보았다.

lr = LinearRegression()

# 모델 훈련하기
lr.fit(train_poly, train_target)

# 평가
print("train TC score: ",lr.score(train_poly,train_target))
print("test TC score: ",lr.score(test_poly,test_target))

 

 

training samples 1.0
testing samples 0.8792

 

 출력값을 보았을 때 training sample에 과대적합 된 것을 볼 수 있다.

 

 

3. 규제 모델

 선형 회귀 모델이 과대적합 되었기 때문에 규제 모델을 추가해줄 필요가 있다. Ridge 모델이랑 Lasso 모델 중에 고민을 했다. 과대적합이 일어난 이유가 집의 특성들 중 가격에 크게 영향을 미치지 못하는 요인까지 고려되었기 때문이라고 생각해서 가중치를 0에 가깝게 주는 Lasso 모델을 선택했다. 또한 차수를 2차에서 1차로 낮추는 것도 고려해보았다.

 Lasso 모델에서 가장 좋은 알파값을 찾기 위해 잘 알려진 방식을 사용했다.

lasso.fit(train_poly, train_target)

train_score = []
test_score = []
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]

for a in alpha_list:
  lasso = Lasso(alpha=a, max_iter=10000)
  lasso.fit(train_poly, train_target)
  train_score.append(lasso.score(train_poly, train_target))
  test_score.append(lasso.score(test_poly, test_target))

plt.plot(alpha_list, train_score, label='train')
plt.plot(alpha_list, test_score, label='test')
plt.xlabel('alpha')
plt.ylabel('score')
plt.legend()
plt.show()

 

왼쪽 : 차수 2, 오른쪽 : 차수 1

 

 결과를 보면 차수가 2일 때는 여전히 과대적합이 일어나는 것을 볼 수 있고, 차수가 1일 때 점수는 낮지만 어느정도 해결된 것을 볼 수 있다. 또한 알파값이 100일 때 성능이 가장 좋다. 그래서 차수를 1로, 알파값을 100으로 하여 라쏘 회귀를 하였다.

 

 

4. 실제 데이터 예측

 마지막으로 훈련한 모델을 이용하여 주어진 테스트 케이스에 대해 집값을 예측하였다.

# 데이터 불러오기
predict_data = pd.read_csv("//content//test_case.csv")

# Id 열 제거
if "Id" in predict_data.columns:
    ids = predict_data["Id"]  # Save IDs for the final output
    predict_data = predict_data.drop("Id", axis=1)
else:
    ids = None

# 문자열 one-hot-vector로 변환
predict_data = pd.get_dummies(predict_data, columns=categorical_columns)
predict_data = predict_data.reindex(columns=input_data.columns, fill_value=0)

# NaN값 처리
predict_data = imputer.transform(predict_data)

# 다항 항 추가
predict_poly = poly.transform(predict_data)

# 전처리
predict_poly = scaler.transform(predict_poly)

# 예측값
predicted_prices = lasso.predict(predict_poly)

# 집 Id와 예측 가격을 저장하는 dataframe
output_df = pd.DataFrame({
    "Id": ids if ids is not None else range(len(predicted_prices)),
    "SalePrice": predicted_prices
})

# csv로 저장
output_df.to_csv("//content//predicted_prices.csv", index=False)

 

 

 코드를 통해 나온 결과를 실제로 제출 해보았다. Ridge로 훈련한 것도 제출해보았는데 Lasso의 경우가 확실히 오차가 더 적은 것을 볼 수 있다. 그래도 아직 오차는 크지만 아직은 이정도로 만족해야할 것 같다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함