8 분 소요

본 게시물은 Linear Regression에 대해 소개한다.

1. Simple Linear Regression

1.1 Concept

입력 특성에 대한 선형 함수를 만들어 예측하는 알고리즘으로 독립변수가 하나인 경우 데이터의 특징을 가장 잘 설명하는 직선을 학습함

  • 통계적 방식의 선형회귀는 정규 방정식을 사용
  • 머신러닝 방식의 선형회귀는 경사 하강법을 사용

📍 정규 방정식

\(\hat{\beta} = (X^TX)^{-1}X^Ty\)

1.2 Parameters

# default
sklearn.linear_model.LinearRegression( * , fit_intercept = True , 
                                      normalize = False , n_jobs = None)
  • fit_intercept: 절편을 계산할지에 대한 여부, False일 경우 원점을 지나는 직선 모델 생성
  • normalize: X에 대한 정규화를 진행할지에 대한 여부, True일 경우 데이터를 $(X-\bar{X})/\sqrt{\sum{x}^2}$로 정규화함, False일 경우 학습전 정규화 필요
  • n_jobs: 보유한 cpu로 병렬학습, -1일시 최대 cpu 사용

1.3 Attribute

  • coef_: 선형 회귀 문제에 대한 추정된 계수
  • rank_: 다중공선성을 확인하기 위해 회귀모델에 사용된 독립 변수들의 선형독립의 정도를 나타내는 속성, 독립 변수들 간의 상관관계가 얼마나 강한지를 나타내는 지표로 사용, 랭크가 독립 변수의 개수와 동일하거나 거의 유사한 경우가 바람직하며 랭크가 독립 변수의 개수보다 작다면, 다중공선성을 해결하기 위해 변수 선택, 변수 변환 등의 전처리 방법을 고려해야 함
  • singular_: 데이터에 대한 특이값 행렬로, 행렬의 데이터 중 하나가 0에 가까운 작은 값이라면, 해당 독립 변수는 다른 독립 변수들과 강한 상관관계를 가지고 있어 다중공성성을 초래할 가능성이 높음, rank 계산에 사용됨
  • intercept_: 절편

1.4 Method

  • fit(X, y[, sample_weight]): 모델 학습
  • get_params([deep]): 매개변수를 가져옴
  • predict(X): 예측
  • score(X, y[, sample_weight]): 모델 학습 결과로 R-sqaure를 계산

📍 sample_weight

일반적인 회귀분석은 모든 데이터가 동일한 중요성을 갖고 연산되나, sample_weight를 사용해 각 데이터 포인트에 대한 가중치를 설정할 수 있음

  • 불균형 데이터: 소수의 클래스에 높은 가중치를 부여
  • 정확성 조절: 신뢰성이 높은 데이터에 높은 가중치 부여
  • 오류에 민감한 데이터: 오류가 큰 데이터에 낮은 가중치를 부여하여 이상치에 덜 민감하게 학습

1.5 Implementation

😗 데이터 불러오기

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
data = pd.read_csv("https://raw.githubusercontent.com/ADPclass/ADP_book_ver01/main/data/insurance.csv")
data.head()

# age대 charges의 관계를 시각화
x=data['age']
y=data['charges']
plt.figure(figsize=(10,5))
plt.scatter(x,y)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

# X, y 선정 및 차원 맞추기
x=np.array(data['age'])
y=np.array(data['charges'])
x=x.reshape(-1 ,1) 
y=y.reshape(-1 ,1)

print(x.shape, y.shape)
(1338, 1) (1338, 1)
# 모델 학습
lr = LinearRegression()
lr.fit(x,y)

# 평가
print("절편", lr.intercept_, "계수", lr.coef_)
print("R-square", lr.score(x,y))
절편 [3165.88500606] 계수 [[257.72261867]]
R-square 0.08940589967885804
# 회귀선을 그려 확인
plt.figure(figsize=(10,5))
plt.plot(x, lr.predict(x), color='red', linewidth=2)
plt.plot(x, y, "b.")
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

2. Gradinet Descent

2.1 Concept

지정한 loss 함수를 최소로 하는 독립 변수의 가중치를 반복적 연산을 통해 찾는 방식

2.2 Gradinet Descent 종류

  • batch: iteration 마다 모든 데이터 세트 사용하여 gradient 계산 및 weight update
  • sthocastic: 한개의 샘플 데이터를 랜덤하게 선택하여 gradient 계산 및 weight update
  • mini batch: 각 step 마다 일정한 batch_size로 gradient 계산 및 weight update

📍 Terminology

  • Epoch: 전체 데이터 셋을 한번 모델에 통과 시키는 것, 1 Epoch 마다 모델이 전체 데이터에 대해 한 번 학습을 수행한 것
  • Step: 모델 파라미터를 한 번 업데이트하는데 필요한 반복 횟수, epoch는 여러개의 step으로 이루어질 수 있음
  • Iteration: 1 Epcoh 동안 수행되는 step의 총 횟수, 총 iteration은 epoch * step
  • batch: 한번의 step에서 처리하는 데이터의 개수

2.3 Parameters

class sklearn.linear_model.SGDRegressor(loss=squared_error, *, penalty=l2, alpha=0.0001, 
                                        l1_ratio=0.15, fit_intercept=True, max_iter=1000, 
                                        tol=0.001, shuffle=True, verbose=0, 
                                        epsilon=0.1, random_state=None, learning_rate=invscaling, 
                                        eta0=0.01, power_t=0.25, early_stopping=False, 
                                        validation_fraction=0.1, n_iter_no_change=5, warm_start=False, 
                                        average=False) 
  • loss: loss function로 종류로는 ‘squared_error’, ‘huber’, ‘epsilon_insentive’, ‘squared_epslion_insentive’가 있음
  • penalty: loss 함수에 panelty(=정규화)를 적용해 과적합 및 안정적 수렴 ‘l1’, ‘l2’, ‘elasticnet’을 사용할 수 있음
  • alpha: 정규화의 강도
  • l1_ratio: 0=L2 panelty, 1=L1 panelty, 0~1=elastic net
  • fit_intercept: 절편 추정 여부
  • max_iter: max epoch에 대한 최대 pass
  • shuffle: 각 Epoch 이후 학습 데이터를 섞을지 여부
  • eta0: learning rate의 초기값을 설정
  • early_stopping: validation epoch가 해당 변수로 지정한 epoch 동안 개선되지 않을 경우 중단
  • validation_fraction: early stop을 위해 학습 데이터 중 일정 부분을 validation set으로 지정
  • average: 모든 업데이트에서 평균 가중치를 계산하고 이를 coef에 저장

2.4 Attribute

  • coef_: 가중치
  • intercept_: 절편
  • n_iter: 중지까지 걸린 총 반복 횟수
  • t_: 가중치 업데이트 횟수

2.5 Method

  • fit(X,y): 학습
  • get_params([deep]): 매개변수를 가져옴
  • partial_fit(X, y): 주어진 샘플에 대해 한 epoch의 확률적 경사하강 수행
  • predict(X): 예측
  • score(X, y[,sample_weight]): 결정계수 반환

2.6 Implementation

😗 데이터 불러오기

import pandas as pd
import numpy as np
from sklearn.linear_model import SGDRegressor
data= pd.read_csv('https://raw.githubusercontent.com/ADPclass/ADP_book_ver01/main/data/insurance.csv')
x=np.array(data['age'])
y=np.array(data['charges'])
x=x.reshape(1338 ,1) 
y=y.reshape(1338 ,1)

print(x.shape, y.shape)
(1338, 1) (1338, 1)
# 모델 생성 및 학습
sgd_reg =SGDRegressor(max_iter=1000, random_state=45)
sgd_reg.fit(x,y.ravel())
print('SGD 회귀 모델 결과')
print('절편', sgd_reg.intercept_, '계수', sgd_reg.coef_)
SGD 회귀 모델 결과
절편 [6931.05771231] 계수 [90.62149214]
# 예측
x_new=[[19],[64]]
y_hat=sgd_reg.predict(x_new)
print(y_hat)
[ 8652.866063   12730.83320937]

3. Polynomial Regression

3.1 Concept

데이터가 단순한 선형이 아닌 비선형의 형태를 갖고 있을 때, 각 변수의 거듭제곱을 새로운 변수로 사용하면 선형 모델을 사용할 수 있음

📍 다항 변수의 생성

  • scikit-learn의 PolynomialFeatures(degree=d)를 활용해 변수의 제곱항을 feature로 추가할 수 있음
  • 이때 각 항의 교차항을 추가할 수 있음

3.2 Parameters

class sklearn.preprocessing.PolynomialFeatures(degree=2, *, interaction_only=False, 
                                               include_bias=True, order=C)
  • degree: 다항식의 차수 결정
  • interaction_only: True일 경우 교차항만 추가, False인 경우 교차항과 함께 동일한 변수의 2제곱 이상 항도 추가
  • include_bias: True일 경우 bias 컬럼 추가

3.3 Method

  • fit_transform(X): 데이터를 적합 후 변환
  • transform(X): 데이터를 변환

3.4 Implementation

😗 데이터 불러오기

import pandas as pd
cereal = pd.read_csv("https://raw.githubusercontent.com/ADPclass/ADP_book_ver01/main/data/cereal.csv")
cereal.info()
Data columns (total 16 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   name      77 non-null     object 
 1   mfr       77 non-null     object 
 2   type      77 non-null     object 
 3   calories  77 non-null     int64  
 4   protein   77 non-null     int64  
 5   fat       77 non-null     int64  
 6   sodium    77 non-null     int64  
 7   fiber     77 non-null     float64
 8   carbo     77 non-null     float64
 9   sugars    77 non-null     int64  
 10  potass    77 non-null     int64  
 11  vitamins  77 non-null     int64  
 12  shelf     77 non-null     int64  
 13  weight    77 non-null     float64
 14  cups      77 non-null     float64
 15  rating    77 non-null     float64
dtypes: float64(5), int64(8), object(3)

📍 전처리 수행

# object 제외 및 sugars가 0 이상인 데이터만 추출
cereal = cereal[cereal.columns[3:]]
cereal =cereal[cereal.sugars >=0]
cereal.head()

📍 선형관계 확인

# sugars와 rating 간의 선형관계 확인
import matplotlib.pyplot as plt
cereal2 = cereal[['sugars', 'rating']]
cereal2.sort_values(by=['sugars'], inplace =True)
cereal2.reset_index(drop=True, inplace =True)
x=cereal2['sugars'].values
y=cereal2['rating'].values
plt.scatter(x,y)
plt.show()

  • 아래로 볼록한 형태의 그래프가 그려짐

📍 Train/Test split

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size =0.3, 
random_state =1)
print(X_train.shape, X_test.shape)
print(y_train.shape, y_test.shape)
(53,) (23,)
(53,) (23,)

📍 PolynomialFeatures

from sklearn.preprocessing import PolynomialFeatures
# 아래로 볼록한 그래프가 그려졌기에 차수는 2차로 설정
poly_reg=PolynomialFeatures(degree=2)
X_poly=poly_reg.fit_transform(X_train.reshape(-1,1))
from sklearn.linear_model import LinearRegression
reg = LinearRegression()
reg.fit(X_poly, y_train)

📍 평가

import numpy as np
X_test_poly=poly_reg.transform(X_test.reshape(-1,1))
pred=reg.predict(X_test_poly)
np.set_printoptions(precision=2) # 소수점 둘째자리까지 표현
print(np.concatenate((pred.reshape(len(pred),1),y_test.reshape(len(y_test),1)),1)) 
[[51.63 46.66]
 [32.1  28.74]
 [55.79 59.64]
 [31.08 37.84]
 [32.1  31.44]
 [44.46 44.33]
 [38.82 40.4 ]
 [41.45 55.33]
 [41.45 49.12]
 [31.38 27.75]
 [36.56 34.38]
 [34.7  29.92]
 [65.25 63.01]
 [33.21 31.07]
 [44.46 52.08]
 [38.82 40.45]
 [51.63 53.13]
 [36.56 33.98]
 [41.45 49.51]
 [31.04 22.74]
 [31.38 39.26]
 [31.5  31.23]
 [32.1  21.87]]
from sklearn.metrics import mean_squared_error, mean_absolute_error, mean_squared_error

mse = mean_squared_error(y_test, pred)
mae = mean_absolute_error(y_test, pred)
rmse = np.sqrt(mse)
acc = reg.score(poly_reg.transform(X_test.reshape(-1,1)), y_test)
print('MSE\t{}'.format(round(mse,3)))
print('MAE\t{}'.format(round(mae,3)))
print('RMSE\t{}'.format(round(rmse,3)))
print('ACC\t{}%'.format(round(acc *100,3)))
MSE	33.565
MAE	4.606
RMSE	5.794
ACC	74.376%

📍 시각화

X_new=np.linspace(0,15,100).reshape(100,1)
X_new_poly=poly_reg.transform(X_new)
y_new = reg.predict(X_new_poly)
plt.plot(x,y, 'o', label ='Actual')
plt.plot(X_new, y_new, 'r-', label ="Prediction")
plt.legend(loc='upper right')
plt.xlabel("$Sugars_1$")
plt.ylabel("$Rating$")
plt.show()

4. Multiple Regression

4.1 Concept

다중 독립변수가 있는 회귀분석을 말하며, 여러개의 독립변수가 복합적으로 종속변수에 영향을 미치는 경우 다중 회귀 모형으로 데이터를 표현할 수 있음

4.2 Regularization

  • 여러개의 독립 변수가 있을 경우 부분 집합을 선택하여 모델의 성능을 향상시킬 수 있음
  • 이는 각 독립변수의 회귀계수를 0으로 수렴 or 0으로 만들어 제거하는 방식을 사용함

5. Ridge Regression

5.1 Concept

Loss function(MSE)에 L2 panelty를 추가하는 것으로 독립변수의 계수를 0으로 수렴하는 효과가 있음 (학습시에 적용)

5.2 Parameteres

class sklearn.linear_model.Ridge(alpha=1.0, *, fit_intercept=True, normalize=deprecated, 
                                 copy_X=True, max_iter=None, tol=0.001, solver=auto, 
                                 positive=False, random_state=None) 
  • alpha: 정규화 강도, 강도가 클수록 유연성이 줄어들어 bias는 증가하지만 variance는 감소
  • normalize: 데이터를 모델 학습 전에 정규화 함, False일 경우 따로 scaler 적용
  • positive: 계수를 양수로함

5.3 Attribute

  • coef_: 계수
  • inetercept_: 절편

5.4 Method

  • fit: 모델 학습
  • get_params: 매개변수를 가져옴
  • predict: 예측
  • score: 결정계수 반환

5.5 Implementation

😗 데이터 불러오기

from sklearn.datasets import load_diabetes
import pandas as pd
diabetes = load_diabetes()
x = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = diabetes.target
x.head()

📍 Ridge Regression

from sklearn.linear_model import Ridge
## 다양한 alpha 값 설정
alpha = np.logspace(-3, 1, 5)
data=[]
for i, a in enumerate(alpha):
 ridge=Ridge(alpha=a, random_state=45)
 ridge.fit(x, y)
 data.append(pd.Series(np.hstack([ridge.coef_])))
df_ridge=pd.DataFrame(data, index=alpha)
df_ridge.columns=x.columns
df_ridge

plt.semilogx(df_ridge)
plt.xticks(alpha, labels=np.log10(alpha))
plt.legend(labels=df_ridge.columns, bbox_to_anchor=(1, 1))
plt.title("Ridge")
plt.xlabel('alpha')
plt.ylabel('Coefficient (size)')
plt.axhline(y=0, linestyle='--', color='black', linewidth=3)

  • alpha 값이 증가하면 회귀계수의 값이 0에 수렴하는 것을 볼 수 있음
from sklearn.linear_model import LinearRegression
lr=LinearRegression()
lr.fit(x,y)
plt.axhline(y=0, linestyle='--', color='black', linewidth=2)
plt.plot(df_ridge.loc[0.001], '^-', label='Ridge alpa = 0.001')
plt.plot(df_ridge.loc[0.010], 's', label='Ridge alpa = 0.010')
plt.plot(df_ridge.loc[0.100], 'v', label='Ridge alpa = 0.100')
plt.plot(df_ridge.loc[1.000], '*', label='Ridge alpa = 1.000')
plt.plot(df_ridge.loc[10.000], 'o-', label='Ridge alpa = 10.000')
plt.plot(lr.coef_,label="LinearRegression")
plt.xlabel('Feature Names')
plt.ylabel('Coefficient (size)')
plt.legend(bbox_to_anchor=(1,1))

  • LinearRegression을 사용하여 얻은 계수들은 MSE를 통해 얻은 것
  • 각 feature에 대해 alpha값이 작은 모델의 회귀 계수는 MSE와 비슷한 계수 값을 얻음
  • alpha 값이 증가하면서 회귀계수가 0에 가까워지는 것을 확인할 수 있음

6. Lasso

6.1 Concept

Loss function(MSE)에 L1 panelty를 추가하는 것으로 독립변수의 계수를 0으로 만듦 (학습시에 적용)

6.2 Parameters

class sklearn.linear_model.Lasso(alpha=1.0, *, fit_intercept=True, normalize=deprecated, 
                                 precompute=False, copy_X=True, max_iter=1000, tol=0.0001, 
                                 warm_start=False, positive=False, random_state=None, 
                                 selection=cyclic)
  • alpha: alpha가 0이면 기본 MSE와 동일
  • 나머지 ridge와 동일

6.3 Attribute & Method

  • ridge와 동일

6.4 Implementation

😗 데이터 불러오기

from sklearn.linear_model import Lasso
alpha=np.logspace(-3, 1, 5)
data=[]
for i, a in enumerate(alpha):
 lasso=Lasso(alpha=a, random_state=45)
 lasso.fit(x, y)
 data.append(pd.Series(np.hstack([lasso.coef_])))
df_lasso = pd.DataFrame(data, index=alpha)
df_lasso.columns=x.columns
df_lasso

📍 Lasso Regression

plt.semilogx(df_lasso)
plt.xticks(alpha, labels=np.log10(alpha))
plt.legend(labels=df_lasso.columns, bbox_to_anchor=(1, 1))
plt.title("Lasso")
plt.xlabel('alpha')
plt.ylabel('Coefficient (size)')
plt.axhline(y=0, linestyle='--', color='black', linewidth=3)

plt.axhline(y=0, linestyle='--', color='black', linewidth=2)
plt.plot(df_lasso.loc[0.001], '^', label='Lasso alpa = 0.001')
plt.plot(df_lasso.loc[0.010], 's', label='Lasso alpa = 0.010')
plt.plot(df_lasso.loc[0.100], 'v', label='Lasso alpa = 0.100')
plt.plot(df_lasso.loc[1.000], '*', label='Lasso alpa = 1.000')
plt.plot(df_lasso.loc[10.000], 'o-', label='Lasso alpa = 10.000')
plt.plot(lr.coef_,label="LinearRegression")
plt.xlabel('Feature Names')
plt.ylabel('Coefficient (size)')
plt.legend(bbox_to_anchor=(1,1))

  • Ridge와 다르게 회귀계수가 0이 되는 것을 볼 수 있음

7. Elastic Net

7.1 Concept

Ridge와 Lasso를 혼합 비율에 따라 절충한 것

7.2 Parameters

class sklearn.linear_model.ElasticNet(alpha=1.0, *, l1_ratio=0.5, fit_intercept=True, 
                                      normalize=deprecated, precompute=False, 
                                      max_iter=1000, copy_X=True, tol=0.0001, 
                                      warm_start=False, positive=False, random_state=None, 
                                      selection=cyclic) 
  • l1_ratio: 혼합 정도를 나타내며, 0일경우 L2, 1일경우 L1을 사용

7.3 Attribute & Method

  • 위와 동일

😗 데이터 불러오기

from sklearn.linear_model import ElasticNet
alpha = np.logspace(-3, 1, 5)
data=[]
for i, a in enumerate(alpha):
 ela=ElasticNet(alpha=a, l1_ratio=0.5, random_state=45)
 ela.fit(x, y)
 data.append(pd.Series(np.hstack([ela.coef_])))
df_ela=pd.DataFrame(data, index=alpha)
df_ela.columns=x.columns
df_ela

plt.semilogx(df_ela)
plt.xticks(alpha, labels=np.log10(alpha))
plt.legend(labels=df_ela.columns, bbox_to_anchor=(1, 1))
plt.title("Elastic")
plt.xlabel('alpha')
plt.ylabel('Coefficient (size)')
plt.axhline(y=0, linestyle='--', color='black', linewidth=3)

plt.axhline(y=0, linestyle='--', color='black', linewidth=2)
plt.plot(df_ela.loc[0.001], '^-', label='Elastic alpa = 0.001')
plt.plot(df_ela.loc[0.010], 's', label='Elastic alpa = 0.010')
plt.plot(df_ela.loc[0.100], 'v', label='Elastic alpa = 0.100')
plt.plot(df_ela.loc[1.000], '*', label='Elastic alpa = 1.000')
plt.plot(df_ela.loc[10.000], 'o-', label='Elastic alpa = 10.000')
plt.plot(lr.coef_,label="LinearRegression")
plt.xlabel('Feature Names')
plt.ylabel('Coefficient (size)')
plt.legend(bbox_to_anchor=(1,1))

댓글남기기