본문 바로가기
Machine Learning

회귀 실습 - (자전거 대여 수요 예측)

by rubyda 2020. 5. 28.
728x90

 

파이썬을 통해서 자전거 수요 데이터를 사용해서 회귀 실습을 해보았습니다. 이번에는 똑같이 R을 사용해서 코드를 변경하고 비교해 가면서 공부를 해보겠습니다.

 

In

# 패키지 불러오기 #
library(lubridate)
library(MLmetrics)
library(caret)
library(stats)
library(dummies)

bike_data <- read.csv("C:\\Users\\User\\ML_Book\\bike_train.csv")
head(bike_data)
str(bike_data)

Out

datetime season holiday workingday weather temp  atemp humidity windspeed casual registered count
1 2011-01-01 00:00:00      1       0          0       1 9.84 14.395       81    0.0000      3         13    16
2 2011-01-01 01:00:00      1       0          0       1 9.02 13.635       80    0.0000      8         32    40
3 2011-01-01 02:00:00      1       0          0       1 9.02 13.635       80    0.0000      5         27    32
4 2011-01-01 03:00:00      1       0          0       1 9.84 14.395       75    0.0000      3         10    13
5 2011-01-01 04:00:00      1       0          0       1 9.84 14.395       75    0.0000      0          1     1
6 2011-01-01 05:00:00      1       0          0       2 9.84 12.880       75    6.0032      0          1     1
'data.frame':	10886 obs. of  12 variables:
 $ datetime  : Factor w/ 10886 levels "2011-01-01 00:00:00",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ season    : int  1 1 1 1 1 1 1 1 1 1 ...
 $ holiday   : int  0 0 0 0 0 0 0 0 0 0 ...
 $ workingday: int  0 0 0 0 0 0 0 0 0 0 ...
 $ weather   : int  1 1 1 1 1 2 1 1 1 1 ...
 $ temp      : num  9.84 9.02 9.02 9.84 9.84 ...
 $ atemp     : num  14.4 13.6 13.6 14.4 14.4 ...
 $ humidity  : int  81 80 80 75 75 75 80 86 75 76 ...
 $ windspeed : num  0 0 0 0 0 ...
 $ casual    : int  3 8 5 3 0 0 2 1 1 8 ...
 $ registered: int  13 32 27 10 1 1 0 2 7 6 ...
 $ count     : int  16 40 32 13 1 1 2 3 8 14 ...

데이터를 살펴보면 NULL 데이터는 없으며 대부분 int와 float 숫자형이고 datetime 컬럼은 Factor형을 가지고 있습니다.

 

datetime 칼럼을 년, 월, 일, 시간으로 분리하도록 하겠습니다. R에서는 lubridate 패키지를 통해서 분리를 할 수 있습니다.

 

In

# 데이터 분리하기 #
year<-year(bike_data$datetime)
month<-month(bike_data$datetime)
day<-day(bike_data$datetime)
hour<-hour(bike_data$datetime)

bike_data<-cbind(bike_data,year, month, day, hour)

head(bike_data)

Out

datetime season holiday workingday weather temp  atemp humidity windspeed casual registered count year month day hour
1 2011-01-01 00:00:00      1       0          0       1 9.84 14.395       81    0.0000      3         13    16 2011     1   1    0
2 2011-01-01 01:00:00      1       0          0       1 9.02 13.635       80    0.0000      8         32    40 2011     1   1    1
3 2011-01-01 02:00:00      1       0          0       1 9.02 13.635       80    0.0000      5         27    32 2011     1   1    2
4 2011-01-01 03:00:00      1       0          0       1 9.84 14.395       75    0.0000      3         10    13 2011     1   1    3
5 2011-01-01 04:00:00      1       0          0       1 9.84 14.395       75    0.0000      0          1     1 2011     1   1    4
6 2011-01-01 05:00:00      1       0          0       2 9.84 12.880       75    6.0032      0          1     1 2011     1   1    5

새로운 칼럼들이 추가된것을 확인하였습니다. 이제 datetime 컬럼은 삭제하도록 하겠습니다.

또한 casual+registered = count 이기 때문에 두 칼럼을 삭제하도록 하겠습니다. 

 

In

# 필요없는 데이터 삭제하기
bike_data<-bike_data[,-1]
bike_data<-bike_data[,-9]
bike_data<-bike_data[,-9]

회귀 모델을 평가하는 방법은 종류가 다양합니다. 여기서는 RMSLE (Root Mean Squared Log

Error), MAE (Mean Absolue Error), RMSE (Root Mean Squared Error)을 사용하겠습니다.

 

In

# mae
mae_fun <- function(y,pred){
  man<- mean(abs(y-pred))
  return(man)
}
  

# rmse 
rmse_fun <- function(y,pred){
  rmse <- sqrt(mean((y-pred)**2))
  return(rmse)
}


#rmsle
rmsle_fun <- function(y,pred){
  log_y <- log1p(y)
  log_pred <- log1p(abs(pred))
  squared_error <- ((log_y -log_pred)**2)
  rmsle <- sqrt(mean(squared_error))
  return(rmsle)
}

다음과 같이 함수로 만들었습니다. 편리를 위하여 세가지의 성능 측정을 결합하여 evaluate라는 함수로 만들어서 사용하도록 하겠습니다.

 

In

# 성능 측정 함수 결합
evaluate <- function(y,pred){
  
  mae_val <- mae_fun(y,pred)
  rmse_val <- rmse_fun(y,pred)
  rmsle_val <- rmsle_fun(y,pred)
  
  cat('MAE:',round(mae_val,3), 'RMSE:',round(rmse_val), 'RMSLE:',round(rmsle_val,3))
  
}  

 

자 이제 데이터를 학습과 테스트 데이터셋으로 분리하도록 하겠습니다.

In

# 학습 테스트 분리하기
split<- createDataPartition(y=bike_data$count, p=0.7, list=F)
train<-bike_data[split,]
test<-bike_data[-split,]

다음으로 모델을 학습시키도록 하겠습니다.

In

# 모델 학습하기
lr_reg = lm(count~., data=bike_data) 
pred <- predict(lr_reg,test)

 

이제 evaluate 함수를 사용해서 성능을 측정해보도록 하겠습니다.

In

evaluate(test$count,pred)

Out

MAE: 105.408 RMSE: 140 RMSLE: 1.202

다음과 같은 결과가 나왔습니다. 그렇게 좋은 결과는 아닌 것 같습니다. 

데이터의 이상점을 파악해보기 위해서 먼저 target 변수를 살펴보도록 하겠습니다.

 

 

그래프를 보면 정규분포의 형태를 이루지 않게 되어있습니다. 0~200에서 많이 분포되어 데이터가 왜곡되어 있음을 알 수 있습니다. 이렇게 왜곡된 형태는 로그를 적용해 변환을 해주는 것이 필요합니다.

 

여기서 주의!! 해야할 부분은 테스트의 적용을 할때는 다시 원래 상태로 돌려서 적용을 해주어야 합니다!!

 

In

bike_data_log<-bike_data
bike_data_log$count<-log1p(bike_data_log$count)
hist(bike_data_log$count, col = 'aquamarine')

Out

로그 변환 결과 어느정도 왜곡 정도가 많이 향상 되어있습니다. 이 데이터를 통해서 다시 학습을 해보도록 하겠습니다.

 

In

bike_data_log<-bike_data
bike_data_log$count<-log1p(bike_data_log$count)
hist(bike_data_log$count, col = 'aquamarine')

split_log<- createDataPartition(y=bike_data_log$count, p=0.7, list=F)
train_log<-bike_data_log[split,]
test_log<-bike_data_log[-split,]

lr_reg_log = lm(count~., data=bike_data_log) 
pred_log <- predict(lr_reg_log,test_log)

# 원래 상태로 바꿔주기
y_test_exp <- expm1(test_log$count)
pred_exp <- expm1(pred_log)
evaluate(y_test_exp,pred_exp)

Out

MAE: 107.65 RMSE: 161 RMSLE: 1.016
MAE: 105.408 RMSE: 140 RMSLE: 1.202

RMSE값은 줄었지만 RMSLE는 늘어나는 결과가 나왔습니다. 

 

Year은 연도를 의미하기 때문에 카테고리형 이지만 숫자형 값으로 입력되어 있습니다.

 

이러한 숫자형 카테고리 값을 선형 회귀에 사용할 경우 회귀 계수를 연살할 때 이 숫자형 값에 크게 영향을 받는 경우가 발생합니다. 그래서 이러한 피처들을 더미변수를 사용하여 변화를 해주어야 합니다.

 

library(dummies)를 사용해서 컬럼들을 모두 원=핫 인코딩을 하여 다시 성능을 평가해보도록 하겠습니다.

 

In

bike_data$season<-as.factor(bike_data$season)
bike_data$weather<-as.factor(bike_data$weather)
bike_data$workingday<-as.factor(bike_data$workingday)
bike_data$year<-as.factor(bike_data$year)
bike_data$month<-as.factor(bike_data$month)
bike_data$day<-as.factor(bike_data$day)
bike_data$hour<-as.factor(bike_data$hour)
bike_data$holiday<-as.factor(bike_data$holiday)

dum_data<-dummy.data.frame(bike_data)
head(dum_data,10)

Out

season1 season2 season3 season4 holiday0 holiday1 workingday0 workingday1 weather1 weather2 weather3 weather4
1        1       0       0       0        1        0           1           0        1        0        0        0
2        1       0       0       0        1        0           1           0        1        0        0        0
3        1       0       0       0        1        0           1           0        1        0        0        0
4        1       0       0       0        1        0           1           0        1        0        0        0
5        1       0       0       0        1        0           1           0        1        0        0        0
6        1       0       0       0        1        0           1           0        0        1        0        0
7        1       0       0       0        1        0           1           0        1        0        0        0
8        1       0       0       0        1        0           1           0        1        0        0        0
9        1       0       0       0        1        0           1           0        1        0        0        0
10       1       0       0       0        1        0           1           0        1        0        0        0

 

추가작성 필요

 

 

'Machine Learning' 카테고리의 다른 글

고유값, 고유벡터, 고유값 분해  (0) 2020.08.11
차원 축소(Dimension Reduction)  (0) 2020.06.17
회귀 실습 - (자전거 대여 수요 예측)  (0) 2020.05.28
회귀 평가 지표  (0) 2020.05.27
로지스틱 회귀  (2) 2020.05.27