머신러닝/딥러닝 공부

회귀 (3) - 당뇨병 환자 데이터(diabetes)와 다중 선형 회귀(Multi Regression Model) 본문

AI 공부/Machine Learning

회귀 (3) - 당뇨병 환자 데이터(diabetes)와 다중 선형 회귀(Multi Regression Model)

호사린가마데라닌 2021. 10. 19. 20:17

사이킷런(scikit-learn)은 파이썬에서 제공하는 머신러닝 라이브러리입니다. 사이킷런에는 머신러닝을 익힐 수 있는 여러 툴들이 존재합니다. 이번 포스팅에서는 사이킷런에서 제공하는 당뇨병(diabetes) 환자 데이터를 이용하여 선형 회귀 모델을 만들어보도록 하겠습니다.

from sklearn.datasets import load_diabetes

diabetes=load_diabetes()

 


사이킷런에서 당뇨병 환자 데이터를 불러오는 load_diabetes() 모듈을 import 하고 diabetes에 당뇨병 환자 데이터를 load 했습니다. load_diabetes()는 인자로 return_X_y(default : False)와 as_frame(default : False)를 가지고 있습니다. 이들은 각각 데이터만 불러올 것인지에 대한 여부와 판다스(Pandas) 프레임을 사용할 것인지에 대한 여부를 묻습니다. 저는 diabetes 객체 전체를 가져올 것이고 판다스는 사용하지 않을 것이므로 두 인자를 모두 디폴트 값(False)을 주고 불러왔습니다.


diabetes는 파이썬의 딕셔너리(dictionary)와 비슷한 Bunch클래스입니다. Bunch클래스는 점 표기법을 이용하여 클래스 내부의 키(key)에 접근할 수 있습니다. diabetes가 가지는 키(속성)의 종류는 다음과 같습니다.


- data => (442,10) 크기의 ndarray, 입력값
- target => (442, ) 크기의 ndarray, 타깃
- feature_names => data의 각 열의 특징 이름
- frame => (442,11)크기의 데이터 프레임(호출할 때 인자로 받은 as_frame=True일 때만 접근 가능)
- DESCR => 데이터셋의 설명
- data_filename => data의 위치경로
- target_filename => target의 위치경로
- (data,target) => 입력값과 타깃 튜플 (return_X_y=True일 때만 접근 가능)

*ndarray : numpy의 배열 클래스


더 자세한 내용은 아래의 링크에 들어가시면 알 수 있습니다.

https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html

 

sklearn.datasets.load_diabetes

Examples using sklearn.datasets.load_diabetes: Plot individual and voting regression predictions Plot individual and voting regression predictions, Gradient Boosting regression Gradient Boosting re...

scikit-learn.org

 

 

먼저 data와 target을 받아오겠습니다.

x_data=diabetes.data
y_data=diabetes.target

 


diabetes.data와 diabetes.target은 모두 ndarray이므로 shape를 이용하여 모양을 파악할 수 있습니다.

print(x_data.shape)
print(y_data.shape)

 


위의 설명에서 봤듯이 x는 (442,10)의 배열, 즉 10개의 정보를 가진 입력값이 총 442개가 있는 ndarray입니다. x가 442개 있으니 y도 당연히 442개가 있어야겠네요. 머신러닝에서는 이 data가 가진 정보를 특성(feature)이라고 합니다. 저도 앞으로는 특성이라는 말을 사용하도록 하겠습니다.

이제 이 10개의 특성이 각각 어떤 것인지 한번 보겠습니다.

for i,feature_name in enumerate(diabetes.feature_names):
  print(f'feature {i+1} : {feature_name}')


첫번째 특성은 나이, 두 번째 특성은 성별, 세 번째 특성은 체지방 지수... 이런 식이겠군요. 즉, 442개의 데이터는 각각 442명의 당뇨병 환자들의 나이, 성별 등을 기록한 데이터라는 것을 알 수 있습니다. 첫 번째 환자의 데이터를 볼까요?

print('<x_data[0]> : ',x_data[0])
print()
print('<y_data[0]> : ',y_data[0] )


총 x에는 10개의 특성이 있고 모든 특성을 고려한 결과 첫번째 사람의 타깃 값은 151이라는 것을 확인할 수 있습니다.

그런데 숫자가 조금 이상합니다. 나이가 0.03807591이라니 어떻게 된걸까요?

 


사이킷런 사이트에 들어가면 확인할 수 있는데, 이는 모든 특성이 -0.2 ~ 0.2 사이에 분포하도록 조정해서 그렇습니다.

 


앞에서 다뤘던 다중 선형 회귀 모델을 가져와 이 데이터에 대하여 학습을 해보도록 하겠습니다.

class MultiLinear:
  def __init__(self,learning_rate=0.001):
    self.w=None #모델의 weight 벡터 self.w=(w_1,w_2)
    self.b=None #모델의 bias
    self.lr=learning_rate #모델의 학습률
    self.losses=[] #매 에포크마다 손실을 저장하기 위한 리스트
    self.weight_history=[] #매 에포크마다 계산된 weight를 저장하기 위한 리스트
    self.bias_history=[] #매 에포크마다 계산된 bias를 저장하기 위한 리스트

  def forward(self,x):
    y_pred=np.sum(x*self.w)+self.b #np.sum함수는 인자로 받은 numpy배열의 모든 원소의 합을 return합니다.
    return y_pred

  def loss(self,x,y):
    y_pred=self.forward(x)
    return (y_pred-y)**2

  def gradient(self,x,y):
    y_pred=self.forward(x)
    w_grad=2*x*(y_pred-y)
    b_grad=2*(y_pred-y)

    return w_grad,b_grad

  def fit(self,x_data,y_data,epochs=20):
    self.w=np.ones(x_data.shape[1]) #모델의 weight들을 전부 1로 초기화
    self.b=0 #모델의 bias를 0으로 초기화
    for epoch in range(epochs):
      l=0 #계산할 손실값
      w_grad=np.zeros(x_data.shape[1]) #weight의 기울기를 누적할 numpy배열
      b_grad=0  #bias의 기울기를 누적할 변수

      for x,y in zip(x_data,y_data):
        l+=self.loss(x,y)
        w_i,b_i=self.gradient(x,y)

        w_grad+=w_i #weight누적
        b_grad+=b_i #bias누적

      self.w-=self.lr*(w_grad/len(y_data)) #weight 업데이트
      self.b-=self.lr*(b_grad/len(y_data)) #bias 업데이트
 
      print(f'epoch ({epoch+1}) ===> loss : {l/len(y_data):.5f}')
      self.losses.append(l/len(y_data)) #손실값 저장
      self.weight_history.append(self.w) #weight 배열 저장
      self.bias_history.append(self.b) #bias값 저장

fit함수에서 가중치를 초기화할 때의 코드를 살짝 바꿔주었습니다. x.shape[1]으로 특성의 개수를 알 수 있으므로, 이를 이용하여 10개의 가중치를 1로 초기화해주었습니다.

 

 

model=MultiLinear(learning_rate=0.1)
model.fit(x_data,y_data,epochs=40)


이제 모델 객체를 생성하고 학습률을 0.1로 주었습니다. 훈련 데이터에 대해 40 에포크 동안 모델을 학습시켜 주었습니다.


loss값이 조금 크게 나오긴 하지만 어느 정도 수렴하는 것이 보이네요. loss값이 큰 이유는 상대적으로 x값들에 비해 y값이 매우 크기 때문에 ( -0.2 <x <0.2, 25 <y <346) 일어나는 현상이기 때문에 숫자가 크다는 것에 지금은 신경 쓸 필요가 없습니다( 추후에 '데이터 셋 -전처리(preprocessing)'이라는 제목으로 포스팅을 할 예정입니다. 이 때 loss값이 큰 이유와 이를 해결하는 방법을 정리하겠습니다).

중요한 것은 처음에는 빠르게 어느 값에 수렴하다가 나중에 천천히 수렴한다는 것입니다. 이는 저희가 옵티마이저로 경사하강법을 채택했기에 일어나는 현상입니다.

 

 

(혹시 loss값이 너무 크다고 느껴지신다면 y_data를 346으로 나눈 뒤(정규화,Normalization) 훈련을 시켜보시면 숫자가 작아질 것입니다.) 


사이킷런에서는 선형 회귀 모델을 제공합니다. 회귀 모델은 다음 코드와 같이 간단히 객체를 생성하는 것만으로 사용할 수 있습니다. 저는 잘 쓰지 않아 궁금하신 분들을 위해 자세한 내용은 사이트 링크를 남겨두겠습니다.

from sklearn.linear_model import LinearRegression

#scikit-learn에서 제공하는 선형 회귀 모델
skmodel=LinearRegression()

#model을 x와 y 데이터셋을 이용하여 학습시킴
skmodel.fit(x,y)

#model이 'patient' 데이터에 대해 예측한 값을 array로 반환
skmodel.predict(patient)

#testset에 대해 model의 정확도를 판단
skmodel.score(test_x,test_y)

 

https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html

 

sklearn.linear_model.LinearRegression

Examples using sklearn.linear_model.LinearRegression: Principal Component Regression vs Partial Least Squares Regression Principal Component Regression vs Partial Least Squares Regression, Plot ind...

scikit-learn.org

 

 

이것으로 회귀에 대한 정리는 마무리하겠습니다. 다음 포스팅에서는 머신러닝 지도학습의 분류 모델에 대해 정리하겠습니다.

Comments