회귀 – 머신러닝 with 파이썬

회귀는 연속형 변수 값 중에 어떠한 값으로 결정(예측)하는 작업입니다.

독립 변수에 따라 결정(예측)할 종속 변수가 연속형 변수일 때 회귀 모델을 선택합니다.

파이썬의 머신 러닝 모듈인 사이킷 런에서는 다양한 회귀 모델을 제공하고 있습니다.

사이킷 런의 지도 학습 모델은 대부분 회귀와 분류 작업을 위한 모델을 제공합니다. 

사이킷 런의 지도 학습 모델 – 회귀, 분류

Supervised learning 메뉴얼 사이트

사이킷 런의 회귀 모델 종류

사이킷 런에서는 다양한 회귀 모델을 제공합니다.

다음은 크게 회귀 모델을 제공하는 알고리즘과 대표적인 모델 이름입니다.

  • 선형 모델 – LinearRegression
  • 서포트 벡터 머신 – SVR
  • 이웃 – KNeighborsRegressor
  • 트리 – DecisionTreeRegressor
  • 앙상블 – RandomForestRegressor

이들을 사용하기 위해 모듈 포함문은 다음과 같습니다.

from sklearn.linear_model import LinearRegression #선형 회귀
from sklearn.svm import SVR #서포트 벡터 머신 회귀 
from sklearn.neighbors import KNeighborsRegressor #K 최근접 이웃 회귀
from sklearn.tree import DecisionTreeRegressor #결정 트리 회귀
from sklearn.ensemble import RandomForestRegressor #랜덤 포리스트 회귀

이러한 모델의 알고리즘은 별도의 항목에서 다루기로 할게요.

사용할 데이터

회귀 모델을 사용하는 예는 사이킷 런에서 제공하는 당뇨병 환자 데이터를 사용할게요.

sklearn.datasets.load_diabetes(*return_X_y=Falseas_frame=Falsescaled=True)

당뇨병 환자 데이터
from sklearn.datasets import load_diabetes #당뇨병 환자 데이터 로드
from sklearn.model_selection import train_test_split #학습 및 테스트 데이터 분리
import pandas as pd

먼저 데이터를 로드한 후 간단히 살펴봅시다.

data 키에는 독립 변수, target은 종속 변수, feature_names는 독립 변수의 특성 이름들입니다.

diabetes = load_diabetes()
data = diabetes.data #나이, 성별, 체질량지수, 혈압, 콜레스트롤, 혈당 수치 등
target = diabetes.target #1년 후 질병 진행에 대한 실제 정량적 측정
df = pd.DataFrame(data,columns = diabetes.feature_names)
df['target'] = target
display(df.head(3))
당뇨병 환자 데이터
당뇨병 환자 데이터

독립 변수는 10개의 특성으로 구성하고 있습니다.

print(diabetes.DESCR)
[out]
.. _diabetes_dataset:

Diabetes dataset
----------------

Ten baseline variables, age, sex, body mass index, average blood
pressure, and six blood serum measurements were obtained for each of n =
442 diabetes patients, as well as the response of interest, a
quantitative measure of disease progression one year after baseline.

**Data Set Characteristics:**

  :Number of Instances: 442

  :Number of Attributes: First 10 columns are numeric predictive values

  :Target: Column 11 is a quantitative measure of disease progression one year after baseline

  :Attribute Information:
      - age     age in years
      - sex
      - bmi     body mass index
      - bp      average blood pressure
      - s1      tc, total serum cholesterol
      - s2      ldl, low-density lipoproteins
      - s3      hdl, high-density lipoproteins
      - s4      tch, total cholesterol / HDL
      - s5      ltg, possibly log of serum triglycerides level
      - s6      glu, blood sugar level

Note: Each of these 10 feature variables have been mean centered and scaled by the standard deviation times the square root of `n_samples` (i.e. the sum of squares of each column totals 1).

Source URL:
https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html

For more information see:
Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499.
(https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf)

aeg: 나이

sex: 성별

bmi: 체질량 지수

bp: 평균 혈압

s1: 혈청 콜레스트롤 총량

s2: 저밀도 지단백질

s3: 고밀도 지단백질

s4: 콜레스트롤 총량

s5: possibly log of serum triglycerides level 

s6: 혈당 수치

참고로 종속 변수는 1년 후 질병 진행에 대한 실제 정량적 측정 값이라고 나와 있네요. 

개략적으로 독립 변수와 종속 변수의 타입과 개수를 확인해 봅시다.

print(df.info())
[out]
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 442 entries, 0 to 441
Data columns (total 11 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   age     442 non-null    float64
 1   sex     442 non-null    float64
 2   bmi     442 non-null    float64
 3   bp      442 non-null    float64
 4   s1      442 non-null    float64
 5   s2      442 non-null    float64
 6   s3      442 non-null    float64
 7   s4      442 non-null    float64
 8   s5      442 non-null    float64
 9   s6      442 non-null    float64
 10  target  442 non-null    float64
dtypes: float64(11)
memory usage: 38.1 KB
None

보시는 것처럼 442개의 데이터가 있습니다. 

모두 결측치(NULL)가 없고 수치(float64)라는 것을 알 수 있습니다.

기본 통계값도 확인합시다.

print(df.describe())
[out]
                age           sex           bmi            bp            s1  \
count  4.420000e+02  4.420000e+02  4.420000e+02  4.420000e+02  4.420000e+02   
mean  -2.511817e-19  1.230790e-17 -2.245564e-16 -4.797570e-17 -1.381499e-17   
std    4.761905e-02  4.761905e-02  4.761905e-02  4.761905e-02  4.761905e-02   
min   -1.072256e-01 -4.464164e-02 -9.027530e-02 -1.123988e-01 -1.267807e-01   
25%   -3.729927e-02 -4.464164e-02 -3.422907e-02 -3.665608e-02 -3.424784e-02   
50%    5.383060e-03 -4.464164e-02 -7.283766e-03 -5.670422e-03 -4.320866e-03   
75%    3.807591e-02  5.068012e-02  3.124802e-02  3.564379e-02  2.835801e-02   
max    1.107267e-01  5.068012e-02  1.705552e-01  1.320436e-01  1.539137e-01   

                 s2            s3            s4            s5            s6  \
count  4.420000e+02  4.420000e+02  4.420000e+02  4.420000e+02  4.420000e+02   
mean   3.918434e-17 -5.777179e-18 -9.042540e-18  9.293722e-17  1.130318e-17   
std    4.761905e-02  4.761905e-02  4.761905e-02  4.761905e-02  4.761905e-02   
min   -1.156131e-01 -1.023071e-01 -7.639450e-02 -1.260971e-01 -1.377672e-01   
25%   -3.035840e-02 -3.511716e-02 -3.949338e-02 -3.324559e-02 -3.317903e-02   
50%   -3.819065e-03 -6.584468e-03 -2.592262e-03 -1.947171e-03 -1.077698e-03   
75%    2.984439e-02  2.931150e-02  3.430886e-02  3.243232e-02  2.791705e-02   
max    1.987880e-01  1.811791e-01  1.852344e-01  1.335973e-01  1.356118e-01   

           target  
count  442.000000  
mean   152.133484  
std     77.093005  
min     25.000000  
25%     87.000000  
50%    140.500000  
75%    211.500000  
max    346.000000

10개의 특성들은 평균 값이 0에 가깝고 최솟값은 -0.1, 최댓값은 +1.0 근처의 값입니다.

종속 변수인 target은 25~346사이의 수치로 되어 있네요.

회귀 모델을 사용하기 위해 학습 데이터, 검증 데이터, 평가 데이터로 분리해 보기로 합시다.

*검증 데이터는 학습 데이터의 일부로 구성합니다.

x_train, x_test, y_train, y_test = train_test_split(data,target)
x_train_,x_val, y_train_,y_val = train_test_split(x_train,y_train)
print(y_train.shape, y_val.shape, y_test.shape)
[out]
(331,) (83,) (111,)

현재 학습 데이터는 331개, 검증 데이터는 83개, 테스트 데이터는 111개로 분리하였습니다.

모델 생성 및 학습

여러 가지 회귀 모델을 생성하고 학습한 후 어느 모델이 나은지 파악해 봅시다.

먼저 모델들을 생성하여 models 리스트에 보관할게요.

models = []
models.append(LinearRegression())
models.append(SVR())
models.append(KNeighborsRegressor())
models.append(DecisionTreeRegressor())
models.append(RandomForestRegressor())

 models에 있는 각 모델을 순차적으로 학습 및 검증 데이터로 평가 해 봅시다.

참고로 회귀 모델은 R2 결정 계수를 이용하여 평가하는 것이 디폴트입니다.

R2 결정 계수에 관한 사항은 다음 항목(4. 회귀 모델 평가 도구)에서 다루기로 할게요.

from sklearn.metrics import r2_score #r2 결정 계수(회귀)
for model in models:
  print(model.__class__.__name__,"###")
  model.fit(x_train_,y_train_)
  pred_val = model.predict(x_val)
  print(f"R2 결정 계수:{r2_score(y_val,pred_val):.4f}")
[out]
LinearRegression ###
R2 결정 계수:0.4175
SVR ###
R2 결정 계수:0.0140
KNeighborsRegressor ###
R2 결정 계수:0.1834
DecisionTreeRegressor ###
R2 결정 계수:-0.0252
RandomForestRegressor ###
R2 결정 계수:0.3242

R2 결정 계수는 1에 가까우면 아주 좋은 결과입니다. 0에 가까우면 쓰기 어려운 수준으로 볼 수 있고 음수가 나오면 욕이 나올 수준입니다.

위 결과를 보면 검증 데이터로 평가했을 때 결정 트리, 랜덤 포레스트, K 최근접 이웃, 선형 회귀, 서프트 벡터 머신 순이라고 할 수 있네요.

검증 데이터로 평가한 결과를 보면 선형 회귀 모델로 선정하는 것이 바람직할 것입니다.

한 번 테스트 데이터로 평가를 다시 해 봅시다.

model = models[0]
print(model.__class__.__name__,"###")
pred_test = model.predict(x_test)
print(f"R2 결정 계수:{r2_score(y_test,pred_test):.4f}")
[out]
LinearRegression ###
R2 결정 계수:0.4687

테스트 결과도 검증 결과와 비슷하네요.

아직 충분히 만족할 만한 모델은 아닌 것 같습니다.

나머지 모델들도 테스트 데이터로 평가해 봅시다.

for model in models:
  print(model.__class__.__name__,"###")
  model.fit(x_train_,y_train_)
  pred_val = model.predict(x_val)
  print(f"R2 결정 계수:{r2_score(y_val,pred_val):.4f}")
  pred_test = model.predict(x_test)
  print(f"R2 결정 계수:{r2_score(y_test,pred_test):.4f}")
[out]
LinearRegression ###
R2 결정 계수:0.4175
R2 결정 계수:0.5307
SVR ###
R2 결정 계수:0.0140
R2 결정 계수:0.1144
KNeighborsRegressor ###
R2 결정 계수:0.1834
R2 결정 계수:0.5587
DecisionTreeRegressor ###
R2 결정 계수:-0.1214
R2 결정 계수:0.0854
RandomForestRegressor ###
R2 결정 계수:0.3490
R2 결정 계수:0.5546

테스트 데이터로 평가하면 K 최근접 이웃, 랜덤 포레스트, 선형 회귀 , 서포트 벡터 머신, 결정 트리 순이네요.

현재 이 상태로는 어떤 모델을 선정해야 할 지 판단하기 어렵습니다. 

독립 변수의 특성을 새롭게 발굴하거나 모델의 하이퍼 파라미터 인자를 변경하면서 더 나은 학습 결과를 도출해야 결정할 수 있겠네요.

이러한 방법들은 모두 별도의 항목에서 다루기로 할게요.

여기에서는 교차 검증을 한 번 더 해보는 것으로 실험을 마감합시다.

사이킷 런에서는 cross_val_score 함수를 제공하고 있습니다.

sklearn.model_selection.cross_val_score(estimator, X, y=None, *, groups=None, scoring=None, cv=None, n_jobs=None, verbose=0, fit_params=None, pre_dispatch=’2*n_jobs’, error_score=nan)

cross_val_score 메뉴얼 사이트

전달할 인자는 머신 러닝 모델, 독립 변수, 종속 변수 순입니다.

from sklearn.model_selection import cross_val_score #교차 검증 점수

전체 데이터를 학습 데이터와 평가 데이터로 분리한 후에 모델 순으로 교차 검증하고 테스트 데이터로 다시 한 번 평가합시다.

x_train, x_test, y_train, y_test = train_test_split(data,target)
models = []
models.append(LinearRegression())
models.append(SVR())
models.append(KNeighborsRegressor())
models.append(DecisionTreeRegressor())
models.append(RandomForestRegressor())
for model in models:
  print(model.__class__.__name__,"###")
  scores = cross_val_score(model,x_train,y_train)
  print(f"교차 검증 점수(평균):{scores.mean():.4f}")
  model.fit(x_train,y_train)
  pred_test = model.predict(x_test)
  print(f"R2 결정 계수:{r2_score(y_test,pred_test):.4f}")
[out]
LinearRegression ###
교차 검증 점수(평균):0.4503
R2 결정 계수:0.5354
SVR ###
교차 검증 점수(평균):0.1044
R2 결정 계수:0.1632
KNeighborsRegressor ###
교차 검증 점수(평균):0.3553
R2 결정 계수:0.3765
DecisionTreeRegressor ###
교차 검증 점수(평균):-0.3206
R2 결정 계수:0.1554
RandomForestRegressor ###
교차 검증 점수(평균):0.3709
R2 결정 계수:0.4686

교차 검증의 점수와 테스트 검증 모두 선형 회귀 모델이 제일 나은 것으로 나오고 있습니다.

하지만 아직 모델마다 적합한 하이퍼 파라미터를 구하는 작업이나 독립 변수의 특성을 변환하는 등의 작업이 더 필요한 것으로 보입니다.

이러한 것들은 다른 항목에서 다루기로 할게요.