당뇨병(diabetes) 데이터셋 예시로 보는 다중 선형 회귀
scikit-learn 패키지를 활용하여 내제된 당뇨병 데이터로 다중 선형 회귀를 학습합니다. 선형 회귀를 통해서 당뇨병 환자들에 대한 경향성을 파악할 수 있습니다. 또한 scikit-learn 활용법 또한 알 수 있습니다.
사이킷런(scikit-learn)은 파이썬에서 사용할 수 있는 머신러닝 라이브러리로, 다양한 머신러닝 도구들을 제공합니다. 이번 글에서는 사이킷런에서 제공하는 당뇨병(diabetes) 환자 데이터를 활용하여 선형 회귀 모델을 구축해 보겠습니다.
Scikit Learn에서 diabetes 데이터셋 load
from sklearn.datasets import laod_diabetes
diabetes = load_diabetes()
사이킷런에서 당뇨병 환자 데이터를 불러오기 위해 load_diabetes()
모듈을 import 하고, 당뇨병 데이터를 diabetes
변수에 로드 했습니다.
load_diabetes()
함수는 return_X_y
(기본값: False)와 as_frame
(기본값: False)이라는 두 가지 인자를 가지고 있으며, 각각 데이터를 배열 형태로 불러올지 여부와 판다스 데이터 프레임 형태로 불러올지 여부를 결정합니다.
저는 diabetes
객체 전체를 가져오고 판다스를 사용하지 않을 것이기 때문에, 두 인자를 모두 기본값(False)으로 설정하여 데이터를 로드했습니다.
diabetes
는 파이썬의 딕셔너리와 유사한 Bunch 클래스입니다. Bunch 클래스는 점 표기법을 사용하여 클래스 내부의 키에 접근할 수 있습니다. 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의 배열 클래스
더 자세한 내용은 아래의 링크에 들어가셔서 확인 할 수 있습니다.
load_diabetes
Gallery examples: Release Highlights for scikit-learn 1.2 Gradient Boosting regression Plot individual and voting regression predictions Model Complexity Influence Model-based and sequential featur…
Data와 Target 받아오기
먼저 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입니다. 따라서, y
도 당연히 442개의 값을 가져야 합니다. 머신러닝에서는 이러한 데이터를 특성(feature)이라고 부릅니다. 앞으로 저도 특성이라는 용어를 사용하겠습니다.
특성 파악
이제, 이 10개의 특성이 각각 무엇을 의미하는지 살펴보겠습니다.
for i, feture_name in enumearte(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 데이터를 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)
LinearRegression
Gallery examples: Principal Component Regression vs Partial Least Squares Regression Plot individual and voting regression predictions Comparing Linear Bayesian Regressors Linear Regression Example…
자세한 내용은 위 사이킷런의 선형 회귀 모델 문서에서 확인 하실 수 있습니다.