수달이네 기술 블로그

13. 자전거 대여 수요 예측 본문

AI공부/머신러닝

13. 자전거 대여 수요 예측

슬픈 수달이 2026. 1. 2. 01:06

https://www.kaggle.com/competitions/2024-2-data-solution-ch-6-public

자전거를 얼마나 빌릴 건지 예측하는 회귀 문제(Regression)

  1. 머신러닝/딥러닝 문제해결 프로세스: 크게 ‘경진대회 이해’ → ‘탐색적 데이터 분석’ → ‘베이스라인 모델’ → ‘성능 개선’ 순으로 진행
  2. 타깃값 변환: 타깃값이 정규분포에 가까울수록 회귀 모델의 성능이 좋음. 한쪽으로 치우친 타깃값은 로그변환하면 정규분포에 가까워지고, 결괏값을 지수변환하면 원래 타깃값 형태로 복원됨.
  3. 이상치 제거: 훈련 데이터에서 이상치를 제거하면 일반화 성능이 좋아질 수 있음
  4. 파생 피처 추가: 기존 피처를 분해/조합하여 모델링에 도움되는 새로운 피처를 만들 수 있음
  5. 피처 제거: 반대로 불필요한 피처를 제거하면 성능도 좋아지고, 훈련 속도도 빨라짐
  6. 선형회귀, 릿지, 라쏘 모델: 회귀 문제를 푸는 대표적인 모델, but 너무 기본적이라 실전에서 단독으로 최상의 성능을 기대하기는 어려움
  7. 랜덤 포레스트 회귀 모델: 여러 모델을 묶어 (대체로) 더 나은 성능을 이끌어내는 간단하고 유용한 기법입니다.
  8. 그리드서치: 교차 검증으로 최적의 하이퍼파라미터 값을 찾아주는 기법

주제

워싱턴 D.C자전거 무인 대여 시스템 과거 기록을 바탕으로 향후 자전거 대여 수요 예측

  • 2011년부터 2012년까지 2년간 자전거 대여 데이터 + 날씨 데이터 (대여 날짜, 시간, 요일, 계절, 날씨, 실제 온도, 체감 온도, 습도, 풍속, 회원 여부)
  • 훈련데이터: 매달 1~19일
  • 테스트데이터: 매달 20~월말
  • 테스트데이터, 훈련데이터를 랜덤으로 나누거나 비율대로 막 나누는 경우 오히려 섞일 가능성이 존재.

탐색적 데이터 분석

  1. 주어진 데이터의 구성 분석

판다스를 이용하여 훈련, 테스트, 제출 샘플 데이터를 DataFrame 형태로 불러온다.

import numpy as np # 넘파이 임포트
import pandas as pd # 판다스 임포트
data_path = '/kaggle/input/bike-sharing-demand/' # 데이터 경로
train = pd.read_csv(data_path + 'train.csv') # 훈련 데이터
test = pd.read_csv(data_path + 'test.csv') # 테스트 데이터
submission = pd.read_csv(data_path + 'sampleSubmission.csv') # 제출 샘플 데이터
train.shape, test.shape
#((10886, 12), (6493, 9))
train.head()
  1. 머신러닝/딥러닝 문제해결 프로세스: 크게 ‘경진대회 이해’ → ‘탐색적 데이터 분석’ → ‘베이스라인 모델’ → ‘성능 개선’ 순으로 진행
  2. 타깃값 변환: 타깃값이 정규분포에 가까울수록 회귀 모델의 성능이 좋음. 한쪽으로 치우친 타깃값은 로그변환하면 정규분포에 가까워지고, 결괏값을 지수변환하면 원래 타깃값 형태로 복원됨.
  3. 이상치 제거: 훈련 데이터에서 이상치를 제거하면 일반화 성능이 좋아질 수 있음
  4. 파생 피처 추가: 기존 피처를 분해/조합하여 모델링에 도움되는 새로운 피처를 만들 수 있음
  5. 피처 제거: 반대로 불필요한 피처를 제거하면 성능도 좋아지고, 훈련 속도도 빨라짐
  6. 선형회귀, 릿지, 라쏘 모델: 회귀 문제를 푸는 대표적인 모델, but 너무 기본적이라 실전에서 단독으로 최상의 성능을 기대하기는 어려움
  7. 랜덤 포레스트 회귀 모델: 여러 모델을 묶어 (대체로) 더 나은 성능을 이끌어내는 간단하고 유용한 기법입니다.
  8. 그리드서치: 교차 검증으로 최적의 하이퍼파라미터 값을 찾아주는 기법

주제

워싱턴 D.C자전거 무인 대여 시스템 과거 기록을 바탕으로 향후 자전거 대여 수요 예측

  • 2011년부터 2012년까지 2년간 자전거 대여 데이터 + 날씨 데이터 (대여 날짜, 시간, 요일, 계절, 날씨, 실제 온도, 체감 온도, 습도, 풍속, 회원 여부)
  • 훈련데이터: 매달 1~19일
  • 테스트데이터: 매달 20~월말
  • 테스트데이터, 훈련데이터를 랜덤으로 나누거나 비율대로 막 나누는 경우 오히려 섞일 가능성이 존재.

탐색적 데이터 분석

  1. 주어진 데이터의 구성 분석

판다스를 이용하여 훈련, 테스트, 제출 샘플 데이터를 DataFrame 형태로 불러온다.

import numpy as np # 넘파이 임포트
import pandas as pd # 판다스 임포트
data_path = '/kaggle/input/bike-sharing-demand/' # 데이터 경로
train = pd.read_csv(data_path + 'train.csv') # 훈련 데이터
test = pd.read_csv(data_path + 'test.csv') # 테스트 데이터
submission = pd.read_csv(data_path + 'sampleSubmission.csv') # 제출 샘플 데이터
train.shape, test.shape
#((10886, 12), (6493, 9))
train.head()
submission.head()

 

데이터 타입 파악, 결측값 개수 파악

train.info()
'''
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   datetime    10886 non-null  object 
 1   season      10886 non-null  int64  
 2   holiday     10886 non-null  int64  
 3   workingday  10886 non-null  int64  
 4   weather     10886 non-null  int64  
 5   temp        10886 non-null  float64
 6   atemp       10886 non-null  float64
 7   humidity    10886 non-null  int64  
 8   windspeed   10886 non-null  float64
 9   casual      10886 non-null  int64  
 10  registered  10886 non-null  int64  
 11  count       10886 non-null  int64  
 '''
test.info()
'''
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   datetime    6493 non-null   object 
 1   season      6493 non-null   int64  
 2   holiday     6493 non-null   int64  
 3   workingday  6493 non-null   int64  
 4   weather     6493 non-null   int64  
 5   temp        6493 non-null   float64
 6   atemp       6493 non-null   float64
 7   humidity    6493 non-null   int64  
 8   windspeed   6493 non-null   float64
'''

데이터 시각화

datetime피처를 세부 구성요소로 나누기

print(train['datetime'][100]) # datetime 100번째 원소
print(train['datetime'][100].split()) # 공백 기준으로 문자열 나누기
print(train['datetime'][100].split()[0]) # 날짜
print(train['datetime'][100].split()[1]) # 시간
print(train['datetime'][100].split()[0]) # 날짜
print(train['datetime'][100].split()[0].split("-")) # "-" 기준으로 문자열 나누기
print(train['datetime'][100].split()[0].split("-")[0]) # 연도
print(train['datetime'][100].split()[0].split("-")[1]) # 월
print(train['datetime'][100].split()[0].split("-")[2]) # 일
print(train['datetime'][100].split()[1]) # 시간
print(train['datetime'][100].split()[1].split(":")) # ":" 기준으로 문자열 나누기
print(train['datetime'][100].split()[1].split(":")[0]) # 시간
print(train['datetime'][100].split()[1].split(":")[1]) # 분
print(train['datetime'][100].split()[1].split(":")[2]) # 초

피처엔지니어링(기존 피처에서 파생된 피처(파생피처)만들기)

판다스의 apply()함수로 앞의 나눈 시간을 피처로 생성

train['date'] = train['datetime'].apply(lambda x: x.split()[0]) # 날짜 피처 생성
# 연도, 월, 일, 시, 분, 초 피처를 차례로 생성
train['year'] = train['datetime'].apply(lambda x: x.split()[0].split('-')[0])
train['month'] = train['datetime'].apply(lambda x: x.split()[0].split('-')[1])
train['day'] = train['datetime'].apply(lambda x: x.split()[0].split('-')[2])
train['hour'] = train['datetime'].apply(lambda x: x.split()[1].split(':')[0])
train['minute'] = train['datetime'].apply(lambda x:
x.split()[1].split(':')[1])
train['second'] = train['datetime'].apply(lambda x:
x.split()[1].split(':')[2])

요일 피처도 생성

from datetime import datetime # datetime 라이브러리 임포트
import calendar
print(train['date'][100]) # 날짜
print(datetime.strptime(train['date'][100], '%Y-%m-%d')) # datetime 타입으로 변경
# 정수로 요일 반환
print(datetime.strptime(train['date'][100], '%Y-%m-%d').weekday())
# 문자열로 요일 반환
print(calendar.day_name[datetime.strptime(train['date'][100], '%Y-%m-%d').weekday()])

계절, 날씨 피처도 추가

train['season'] = train['season'].map({1: 'Spring',
					2: 'Summer',
					3: 'Fall',
					4: 'Winter' })
train['weather'] = train['weather'].map({1: 'Clear',
					2: 'Mist, Few clouds',
					3: 'Light Snow, Rain, Thunderstorm',
					4: 'Heavy Rain, Thunderstorm, Snow,Fog'})

시각화

import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline

mpl.rc('font', size=15) # 폰트 크기를 15로 설정
sns.displot(train['count']); # 분포도 출력

타깃값이 정규분포를 따르는게 좋음. (로그 변환)

  • 모델이 잘 학습하는 형태이다.

이거에서

sns.displot(np.log(train['count']));

  • 나중에 지수변환해주어야함.

# 스텝 1 : m행 n열 Figure 준비
figure, axes = plt.subplots(nrows=2, ncols=2) # 2행 2열
plt.tight_layout()
figure.set_size_inches(10, 10)
# 스텝 2 : 서브플롯 할당
# 계절, 날씨, 공휴일, 근무일별 대여 수량 박스플롯
sns.boxplot(x='season', y='count', data=train, ax=axes[0, 0])
sns.boxplot(x='weather', y='count', data=train, ax=axes[0, 1])
sns.boxplot(x='holiday', y='count', data=train, ax=axes[1, 0])
sns.boxplot(x='workingday', y='count', data=train, ax=axes[1, 1])
# 스텝 3 : 세부 설정
# 3-1 서브플롯에 제목 달기
axes[0, 0].set(title='Box Plot On Count Across Season')
axes[0, 1].set(title='Box Plot On Count Across Weather')
axes[1, 0].set(title='Box Plot On Count Across Holiday')
axes[1, 1].set(title='Box Plot On Count Across Working Day')
# 3-2 x축 라벨 겹침 해결
axes[0, 1].tick_params(axis='x', labelrotation=10) # 10도 회전

연도별은 점차 늘고 있구나

날이 따뜻할 수록 대여가 많구나

day피처는 훈련 데이터와 테스트데이터는 피처가 서로 달라 사용 할 수 없구나

시간별은 출퇴근, 등하교때 많구나

분과 초는 의미

가 없으니 제거해도 괜찮구나

박스플롯

# 스텝 1 : m행 n열 Figure 준비
figure, axes = plt.subplots(nrows=2, ncols=2) # 2행 2열
plt.tight_layout()
figure.set_size_inches(10, 10)
# 스텝 2 : 서브플롯 할당
# 계절, 날씨, 공휴일, 근무일별 대여 수량 박스플롯
sns.boxplot(x='season', y='count', data=train, ax=axes[0, 0])
sns.boxplot(x='weather', y='count', data=train, ax=axes[0, 1])
sns.boxplot(x='holiday', y='count', data=train, ax=axes[1, 0])
sns.boxplot(x='workingday', y='count', data=train, ax=axes[1, 1])
# 스텝 3 : 세부 설정
# 3-1 서브플롯에 제목 달기
axes[0, 0].set(title='Box Plot On Count Across Season')
axes[0, 1].set(title='Box Plot On Count Across Weather')
axes[1, 0].set(title='Box Plot On Count Across Holiday')
axes[1, 1].set(title='Box Plot On Count Across Working Day')
# 3-2 x축 라벨 겹침 해결
axes[0, 1].tick_params(axis='x', labelrotation=10) # 10도 회전

  1. 계절별 대여 수량
  2. 날씨별 대여 수량(좋을수록 많다)
  3. 공휴일 여부에 따른(공휴일이 아닐때 이상치가 많다)
  4. 근무일 여부에 따른 수량(근무일일때 이상치가 많다)

포인트플롯

# 스텝 1 : m행 n열 Figure 준비
mpl.rc('font', size=11)
figure, axes = plt.subplots(nrows=5) # 5행 1열
figure.set_size_inches(12, 18)
# 스텝 2 : 서브플롯 할당
# 근무일, 공휴일, 요일, 계절, 날씨에 따른 시간대별 평균 대여 수량 포인트플롯
sns.pointplot(x='hour', y='count', data=train, hue='workingday', ax=axes[0])
sns.pointplot(x='hour', y='count', data=train, hue='holiday', ax=axes[1])
sns.pointplot(x='hour', y='count', data=train, hue='weekday', ax=axes[2])
sns.pointplot(x='hour', y='count', data=train, hue='season', ax=axes[3])
sns.pointplot(x='hour', y='count', data=train, hue='weather', ax=axes[4]);

공휴일일때는 11시~3시사이가 많다

공휴일이 아닐때는 출퇴근 시간에 많았다

회귀선 포함 산점도

# 스텝 1 : m행 n열 Figure 준비
mpl.rc('font', size=15)
figure, axes = plt.subplots(nrows=2, ncols=2) # 2행 2열
plt.tight_layout()
figure.set_size_inches(7, 6)
# 스텝 2 : 서브플롯 할당
# 온도, 체감 온도, 풍속, 습도 별 대여 수량 산점도 그래프
sns.regplot(x='temp', y='count', data=train, ax=axes[0, 0],
scatter_kws={'alpha': 0.2}, line_kws={'color': 'blue'})
sns.regplot(x='atemp', y='count', data=train, ax=axes[0, 1],
scatter_kws={'alpha': 0.2}, line_kws={'color': 'blue'})
sns.regplot(x='windspeed', y='count', data=train, ax=axes[1, 0],
scatter_kws={'alpha': 0.2}, line_kws={'color': 'blue'})
sns.regplot(x='humidity', y='count', data=train, ax=axes[1, 1],
scatter_kws={'alpha': 0.2}, line_kws={'color': 'blue'});

온도, 체감온도가 높을수록 대여수량이 많음

풍속의 경우 결측치가 너무 많아서 제거

습도가 낮을수록 대여수량이 많음

히트맵

train[['temp', 'atemp', 'humidity', 'windspeed', 'count']].corr()

조합이 너무 많음. 따라서 히트맵이 필요!

# 피처 간 상관관계 매트릭스
corrMat = train[['temp', 'atemp', 'humidity', 'windspeed', 'count']].corr()
fig, ax= plt.subplots()
fig.set_size_inches(10, 10)
sns.heatmap(corrMat, annot=True) # 상관관계 히트맵 그리기
ax.set(title='Heatmap of Numerical Data');

상관관계가 큰것을 한눈에 파악할 수 있다

베이스라인 모델

뼈대가 되는 기본적인 모델 선정

1. 피처 엔지니어링

데이터를 변환하는 작업

import pandas as pd
# 데이터 경로
data_path = '/kaggle/input/bike-sharing-demand/'
train = pd.read_csv(data_path + 'train.csv')
test = pd.read_csv(data_path + 'test.csv')
submission = pd.read_csv(data_path + 'sampleSubmission.csv')
  • 데이터를 불러오는 코드

훈련 데이터, 테스트 데이터를 병합 후, 타입을 변경하거나, 일부 데이터 삭제, 추가한다.

  • 이후엔 다시 나눠줘야한다.

1) 이상치 제거

데이터를 합치기 전 이상치를 제거한다

# 훈련 데이터에서 weather가 4가 아닌 데이터만 추출
train = train[train['weather'] != 4]
  • 위는 폭우, 폭설인 날 자전거를 빌린 데이터

2) 데이터 합치기

all_data = pd.concat([train, test], ignore_index=True)
all_data
  • 훈련데이터와 테스트 데이터에 같은 피처 엔지니어링(데이터를 변환할때 이상이 없어야하기 때문)을 적용
  • 훈련데이터와 테스트 데이터의 차이를 알아야함.

3) 파생피처 추가

from datetime import datetime
# 날짜 피처 생성
all_data['date'] = all_data['datetime'].apply(lambda x: x.split()[0])
# 연도 피처 생성
all_data['year'] = all_data['datetime'].apply(lambda x: x.split()[0].split('-')[0])
# 월 피처 생성
all_data['month'] = all_data['datetime'].apply(lambda x: x.split()[0].split('-')[1])
# 시 피처 생성
all_data['hour'] = all_data['datetime'].apply(lambda x: x.split()[1].split(':')[0])
# 요일 피처 생성
all_data["weekday"] = all_data['date'].apply(lambda dateString : datetime.strptime(dateString,"%Y-%m-%d").weekday())
  • 위에서 만들었던 추가 피처들을 데이터에 추가해준다.

4) 필요없는 피처 제거

  • casual과 registered 피처는 테스트 데이터에 없으므로 제거(분석 정리 4)
  • datetime 피처는 인덱스 역할이고, date 피처가 갖는 정보는 다른 피처들(year, month, day)에도 담겨 있기 때문에 datetime과 date 피처도 필요 없음(분석 정리 5, 6)
  • season 피처가 month 피처의 대분류 성격이라 month 피처도 제거(분석 정리 7)
  • windspeed 피처도 타깃값과 상관관계가 약해서 제거(분석 정리 11)
drop_features = ['casual', 'registered', 'datetime', 'date', 'windspeed', 'month']
all_data = all_data.drop(drop_features, axis=1)
# 위에서 말한 필요없는 데이터를 drop_features로 묶은 후 제거
  • 탐색적 데이터 분석에서 얻은 인사이트를 활용해 의미 있는 피처와 불필요한 피처를 구분(피처 선택)
    • 피처가 많다고 좋은게 아님
    • 타깃값과 관련이 있는 피처를 적용하는게 중요
    • 탐색 데이터 분석, 피처 중요도, 상관관계 매트릭스, 배경지식을 종합적으로 활용해야함.

5) 데이터 나누기

# 훈련 데이터와 테스트 데이터 나누기
X_train = all_data[~pd.isnull(all_data['count'])]
X_test = all_data[pd.isnull(all_data['count'])]
# 타깃값 count 제거
X_train = X_train.drop(['count'], axis=1)
X_test = X_test.drop(['count'], axis=1)
y = train['count'] # 타깃값
  • 피처 엔지니어링 완료 후 훈련데이터와 테스트데이터를 다시 나눠줌
    • 타깃값의 유무에 따라 나눴음
    • count는 타깃값이므로 제거

6) 평가지표 계산 함수 작성

import numpy as np
def rmsle(y_true, y_pred, convertExp=True):
# 지수변환
if convertExp:
y_true = np.exp(y_true)
y_pred = np.exp(y_pred)
# 로그변환 후 결측값을 0으로 변환
log_true = np.nan_to_num(np.log(y_true+1))
log_pred = np.nan_to_num(np.log(y_pred+1))
# RMSLE RM계산
output = np.sqrt(np.mean((log_true - log_pred)**2))
return output

  • 해당 경진대회의 평가지표인 RMSLE를 사용했다.

7) 모델 생성 후 훈련

from sklearn.linear_model import LinearRegression
linear_reg_model = LinearRegression() # 선형 회귀 모델 생성
log_y = np.log(y) # 타깃값 로그변환
linear_reg_model.fit(X_train, log_y) # 모델 훈련
  • 가장 간단한 선형 회귀 모델인 LinearRegression을 활용함
  • fit()는 사이킷런의 훈련 메서드이다.

  • 피처와 타깃값의 최적 가중치(회귀 계수)를 찾는 과정
  • 최적 가중치에서 새로운 데이터가 주어질때 타깃값을 추청하는 과정

8) 모델 성능 검증

preds = linear_reg_model.predict(X_train)

print (f'선형회귀의 RMSLE 값 : {rmsle(log_y, preds, True):.4f}')
#선형회귀의 RMSLE 값 : 1.0205
  • 훈련된 모델로 예측 수행
  • 위는 학습을 한 결과를 input으로 넣어서 실제 잘나오는지 확인한다.
  • RMLSE를 계산해 실제로 예측이 잘되었는지 확인

9) 예측 결과 제출

linearreg_preds = linear_reg_model.predict(X_test) # 테스트 데이터로 예측
submission['count'] = np.exp(linearreg_preds) # 지수변환
submission.to_csv('submission.csv', index=False) # 파일로 저장
  • to_csv는 DataFrame을 csv파일로 저장하는 함수
  • 제출 결과도 확인

성능 개선

1. 릿지 회귀 모델

  • L2 규제를 적용한 선형 회귀 모델
    • 규제regularization란 모델이 훈련 데이터에 과대적합overfitting되지 않도록 해주는 방법
    • 훈련 데이터에 과대적합되면 모델이 훈련 데이터에만 너무 잘 들어맞고, 테스트 데이터로는 제대로 예측하지 못함

  • 모델 훈련 단계에서 하이퍼 파라미터 최적화 수행
  • 성능이 만족스러울 때 까지 최적화(피처엔지니어링 포함)

1) 하이퍼 파라미터 최적화(모델 훈련)

모델이 가진 파라미터를 바꿔주는 것

더보기
더보기

그리드 서치

하이퍼 파라미터 최적화 기법중 하나, 격자처럼 촘촘히 순회하며 최적 파라미터 값을 찾음,

각 하이퍼 파라미터를 적용한 모델을 교차검증해 가장 좋은 성능을 찾음(여러가지를 테스트해봄)

아니면 수동으로 해야함.

from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV
from sklearn import metrics
ridge_model = Ridge()
  • 릿지 모델 생성 후
# 하이퍼파라미터 값 목록
ridge_params =
{'max_iter':[3000], 'alpha':[0.1, 1, 2, 3, 4, 10, 30, 100, 200, 300, 400, 800, 900, 1000]}
# 교차 검증용 평가 함수(RMSLE 점수 계산)
rmsle_scorer = metrics.make_scorer(rmsle, greater_is_better=False)
# 그리드서치(with 릿지) 객체 생성
gridsearch_ridge_model =  GridSearchCV(estimator=ridge_model, # 릿지 모델
						param_grid=ridge_params,            # 하이퍼파라미터 값 목록
						scoring=rmsle_scorer,               # 평가지표
						cv=5)                               # 교차검증 분할 수
  • 그리드 서치 객체 생성
    • 하이퍼 파라미터 값 목록(검증해볼 것)
    • 대상 모델
    • 교차 검증용 평가 수단(평가함수)
    • 위의 3가지로 하이퍼 파라미터를 비교하여 최적을 찾는다.
  • 그리드 서치 수행
  • log_y = np.log(y) # 타깃값 로그변환 gridsearch_ridge_model.fit(X_train, log_y) # 훈련(그리드서치) print('최적 하이퍼파라미터 :', gridsearch_ridge_model.best_params_) #최적 하이퍼파라미터 : {'alpha': 0.1, 'max_iter': 3000}
  • fit 메서드로 값을 순회하며 교차검증 평가지표 계산
  • 이후 최적 하이퍼 파라미터를 찾음

2) 성능검증

# 예측
preds = gridsearch_ridge_model.best_estimator_.predict(X_train)
# 평가
print(f'릿지 회귀 RMSLE 값 : {rmsle(log_y, preds, True):.4f}')

2. 라쏘 회귀 모델

from sklearn.linear_model import Lasso
# 모델 생성
lasso_model = Lasso()
# 하이퍼파라미터 값 목록
lasso_alpha
= 1/np.array([0.1, 1, 2, 3, 4, 10, 30, 100, 200, 300, 400, 800, 900, 1000])
lasso_params = {'max_iter':[3000], 'alpha':lasso_alpha}
# 그리드서치(with 라쏘) 객체 생성
gridsearch_lasso_model = GridSearchCV(estimator=lasso_model,
param_grid=lasso_params,
scoring=rmsle_scorer,
cv=5)
# 그리드서치 수행
log_y = np.log(y)
gridsearch_lasso_model.fit(X_train, log_y)

전체 흐름은 릿지와 같음

# 예측
preds = gridsearch_lasso_model.best_estimator_.predict(X_train)
# 평가
print(f'라쏘 회귀 RMSLE 값 : {rmsle(log_y, preds, True):.4f}')

검증

3. 랜덤 포레스트 회귀 모델

from sklearn.ensemble import RandomForestRegressor
# 모델 생성
randomforest_model = RandomForestRegressor()
# 그리드서치 객체 생성
rf_params = {'random_state':[42], 'n_estimators':[100, 120, 140]}
gridsearch_random_forest_model = GridSearchCV(estimator=randomforest_model,
param_grid=rf_params,
scoring=rmsle_scorer,
cv=5)
# 그리드서치 수행
log_y = np.log(y)
gridsearch_random_forest_model.fit(X_train, log_y)
print('최적 하이퍼파라미터 :', gridsearch_random_forest_model.best_params_)
#최적 하이퍼파라미터 : {'n_estimators': 140, 'random_state': 42}
# 예측
preds = gridsearch_random_forest_model.best_estimator_.predict(X_train)
# 평가
print(f'랜덤 포레스트 회귀 RMSLE 값 : {rmsle(log_y, preds, True):.4f}')
#랜덤 포레스트 회귀 RMSLE 값: 0.1126

큰폭으로 개선되었음을 확인

# 예측
randomforest_preds = gridsearch_random_forest_model.best_estimator_.predict(X_test)
submission['count'] = np.exp(randomforest_preds) # 지수변환
submission.to_csv('submission.csv', index=False) # 예측 결과를 csv 파일로 저장

 

위처럼 이번엔 교과서에서 배우는 대로 자전거 수요 예측 모델을 구성해 보았다.
해당 지식을 바탕으로 다음엔 실제 예측 연습을 해볼 것이다.
주제는 악성 URL 판단으로 정했다.