● 선형 모델(Linear Models)
- 과거부터 지금까지 널리 사용되고 연구되고 있는 머신러닝 기법
- 선형 모델은 입력 데이터에 대한 선형 함수를 만들어 예측 수행
- 회귀 분석을 위한 선형 모델: $$ \hat{y}(w, x)=w_0 + w_{1}x_{1}+\ldots+w_{p}x_{p} $$
- \(x\): 입력 데이터
- \(w\): 모델이 학습할 파라미터
- \(w_{0}\): 편향
- \(w_{1}~w_{p}\): 가중치
- 선형 회귀(Linear Regression)
- 선형 회귀 또는 최소제곱법(Ordinary Least Squares)은 가장 간단한 회귀 분석 선형 모델
- 선형 회귀는 모델의 예측값과 실제값 사이의 평균제곱오차(Mean Squared Error)를 최소화 하는 학습 파라미터 \(w\)를 탐색
- 평균 제곱 오차: $$ MSE=\frac{1}{N}\sum_{i=1}^{N}(y_{i}-\hat{y_{i}})^2 $$
- \(y\): 실제값
- \(\hat{y}\): 예측값
- 선형 회귀 모델에서 MSE 외에 사용하는 다양한 오류 측정 방법
- MAE(Mean Absolute Error): 오차의 절댓값의 평균
- MAPE(Mean Absolute Percentage Error): 오차의 절댓값의 평균을 퍼센트로 나타냄
- MSE(Mean Squared Error): 오차의 제곱의 평균
- RMSE(Root Mean Squared Error): 오차의 제곱의 평균에 루트를 씌워, 제곱에 의한 값의 왜곡을 줄임
- MPE(Mean Percentage Error): 오차의 절댓값이 아닌 원래 값의 평균을 퍼센트로 나타냄
- \(R^2\)
import numpy as np
import matplotlib.pyplot as plt
plt.style.use(['seaborn-whitegrid'])
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# 데이터 생성
# X값(feature 데이터)에 약간의 노이즈 부여
noise = np.random.rand(100, 1)
X = sorted(10 * np.random.rand(100, 1)) + noise
y = sorted(10 * np.random.rand(100))
plt.scatter(X, y)
# 위에서 생성한 데이터를 통해 X와 y 사이의 최적 회귀식을 계산
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)
model = LinearRegression()
model.fit(X_train, y_train)
# LinearRegression을 통해 계산된 회귀 가중치(계수)와 회귀식의 편향(절편)
print("선형 회귀 가중치: {}".format(model.coef_))
print("선형 회귀 편향: {}".format(model.intercept_))
# 출력 결과
선형 회귀 가중치: [1.17341194]
선형 회귀 편향: -2.0407947206139223
print("학습 데이터 점수: {}".format(model.score(X_train, y_train)))
print("학습 데이터 점수: {}".format(model.score(X_test, y_test)))
# 출력 결과
학습 데이터 점수: 0.9526520477496929
학습 데이터 점수: 0.9507468780019412
# model에 의해 X_test의 값으로 예측한 값(predict)와 실제 값(y_test)의 비교
predict = model.predict(X_test)
plt.scatter(X_test, y_test)
plt.plot(X_test, predict, '--r')
- 당뇨 가격 데이터
- 442명의 당뇨병 환자 진행 상황 데이터
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
print(diabetes.keys())
print(diabetes.DESCR)
# 출력 결과
dict_keys(['data', 'target', 'frame', 'DESCR', 'feature_names', 'data_filename', 'target_filename', 'data_module'])
...
: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
...
속성 | 설명 |
---|---|
agr | 나이 |
sex | 성별 |
bmi | 체질량 지수 |
bp | 평균 혈압 |
s1 | 혈중 총 콜레스테롤(total serum cholesterol, tc) |
s2 | 저밀도 콜레스테롤(low-density lipoproteins, ldl)(나쁜 콜레스테롤) |
s3 | 고밀도 콜레스테롤(high-density lipoproteins, hdl)(좋은 콜레스테롤) |
s4 | 총 콜레스테롤(total cholesterol, tch) / 고밀도 콜레스테롤(hdl) |
s5 | possibly log of serum triglycerides level, ltg |
s6 | 혈당치(blood sugar level, glu) |
target | 1년 뒤 병의 진행 상황 측정치 |
# 더 편리하게 데이터를 조작하며 EDA를 진행하기 위해 pandas의 DataFrame 형태로 변경
import pandas as pd
diabetes_df = pd.DataFrame(diabetes.data, columns = diabetes.feature_names)
diabetes_df['target'] = diabetes.target
diabetes_df.head()
diabetes_df.describe()
# 시각화 1: 당뇨병의 환자 개인별 각 데이터의 분포
for i, col in enumerate(diabetes_df.columns):
plt.figure(figsize = (8, 4))
plt.plot(diabetes_df[col])
plt.title(col)
plt.xlabel('patient')
plt.tight_layout()
# 시각화 2: 당뇨병 환자에 대한 각 변수별 target 변수와의 관계
for i, col in enumerate(diabetes_df.columns):
plt.figure(figsize = (8, 4))
plt.scatter(diabetes_df[col], diabetes_df['target'])
plt.title(col)
plt.xlabel(col, size = 12)
plt.ylabel('target', size = 12)
plt.tight_layout()
# 시각화 3: 시각화 2의 변수별 target변수와의 관계를 한 눈에 출력한 seaborn 패키지의 pairplot
import seaborn as sns
sns.pairplot(diabetes_df)
# 선형 회귀 모델을 이용하여 당뇨병 환자의 각 특성 변수로부터 target변수(병의 진행 상황) 예측
model = LinearRegression()
X_train, X_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, test_size = 0.2)
model.fit(X_train, y_train)
model.score(X_train, y_train)
print("학습 데이터 점수: {}".format(model.score(X_train, y_train)))
print("평가 데이터 점수: {}".format(model.score(X_test, y_test)))
# 출력 결과
학습 데이터 점수: 0.5136038374290528
평가 데이터 점수: 0.5063114437093292
- 데이터를 분리하였기 때문에 훈련에 사용된 양이 작고, 분리가 잘 안된 경우 잘못된 검증이 될 수 있음
- 이런 경우, 교차 검증 진행
# 교차 검증을 통해 나온 점수
from sklearn.model_selection import cross_val_score
# neg_mean_squared_error은 기존 MSE(Mean Squared Error)에서 부호의 방향이 다른 것
# MSE가 작을수록 좋은 모델인데 부호의 방향을 반대로 하여 점수가 높을수록 좋은 것이라고 더 직관적이므로 표현
scores = cross_val_score(model, diabetes.data, diabetes.target, cv = 10, scoring = 'neg_mean_squared_error')
print("NMSE scores: {}".format(scores))
print("NMSE scores mean: {}".format(scores.mean()))
print("NMSE score std: {}".format(scores.std()))
# 출력 결과
NMSE scores: [-2533.84017856 -2870.77758341 -3512.72914835 -2759.20855951
-3555.69402408 -2900.34540046 -3696.33102548 -2282.33961544
-4122.99489276 -1769.64247356]
NMSE scores mean: -3000.390290160842
NMSE score std: 681.7925615943559
- 회귀 모델의 검증을 위한 또 다른 측정 지표인 \(R^2\) 사용
r2_scores = cross_val_score(model, diabetes.data, diabetes.target, cv = 10, scoring = 'r2')
print("R2 scores: {}".format(r2_scores))
print("R2 scores mean: {}".format(r2_scores.mean()))
print("R2 score std: {}".format(r2_scores.std()))
# 출력 결과
R2 scores: [0.5561455 0.23055827 0.35357673 0.62190752 0.2658727 0.61819798
0.41815142 0.43513747 0.43436229 0.68569253]
R2 scores mean: 0.4619602420450602
R2 score std: 0.1469914507315373
- 생성된 회귀 모델에 대한 평가를 위해 LinearRegression 객체에 포함된 두 개의 속성 값으로 수식 표현
- intercept_: 추정된 회귀식의 상수항
- coef_: 추정된 회귀식의 변수별 가중치 벡터
# model에서 자동으로 계산해주는 회귀식의 절편과 각 변수의 회귀계수를 for문으로 출력하여 하나의 회귀식으로 출력
print('y= '+str(model.intercept_) + ' ')
for i, c in enumerate(model.coef_):
print(str(c) + ' * X' + str(i))
# 출력 결과
y= 149.5089161367518
33.61273499649665 * X0
-247.676515473911 * X1
563.0736968765942 * X2
301.4945423675652 * X3
-666.4117487275887 * X4
387.9055057888012 * X5
53.535258191636544 * X6
118.17345140681505 * X7
714.1812563836739 * X8
33.07049840631021 * X9
- RMSE score와 \(R^2\) score 비교(train 데이터에 대해)
from sklearn.metrics import mean_squared_error, r2_score
predict = model.predict(X_train)
rmse = (np.sqrt(mean_squared_error(y_train, predict)))
r2 = r2_score(y_train, predict)
print("RMSE score: {}".format(rmse))
print("R2 score: {}".format(r2))
# 출력 결과
RMSE score: 52.94257758575594
R2 score: 0.5136038374290528
- RMSE score와 \(R^2\) score 비교(test 데이터에 대해)
from sklearn.metrics import mean_squared_error, r2_score
y_test_predict = model.predict(X_test)
rmse = (np.sqrt(mean_squared_error(y_test, y_test_predict)))
r2 = r2_score(y_test, y_test_predict)
print("RMSE score: {}".format(rmse))
print("R2 score: {}".format(r2))
# 출력 결과
RMSE score: 56.34236344085632
R2 score: 0.5063114437093292
- 모델이 얼마나 잘 예측하는지 시각화
# 시각화 과정의 함수화
def plot_diabetes(expected, predicted):
plt.figure(figsize = (8, 4))
plt.scatter(expected, predicted)
plt.plot([25, 350], [25, 350], '--r')
plt.xlabel('True value')
plt.ylabel('Predicted value')
plt.tight_layout()
# X_test를 model에 넣어 예측한 값인 predict와 실제 값인 y_test를 비교
# 빨간 선은 가장 잘 예측했을 때 나오는 선으로, 점들이 선에 가까울수록 좋은 모델
predicted = model.predict(X_test)
expected = y_test
plot_diabetes(expected, predicted)
- 캘리포니아 주택 가격 데이터(복습용, 위의 당뇨병 데이터 예측 모델과 같은 과정 반복)
- 1990년 미국 census 조사의 데이터로, California의 census 블록 그룹당의 데이터
from sklearn.datasets import fetch_california_housing
california = fetch_california_housing()
print(california.keys())
print(california.DESCR)
# 출력 결과
dict_keys(['data', 'target', 'frame', 'target_names', 'feature_names', 'DESCR'])
...
:Attribute Information:
- MedInc median income in block group
- HouseAge median house age in block group
- AveRooms average number of rooms per household
- AveBedrms average number of bedrooms per household
- Population block group population
- AveOccup average number of household members
- Latitude block group latitude
- Longitude block group longitude
...
속성 | 설명 |
MedInc | 블록의 중간 소득 |
HouseAge | 블록의 중간 주택 연도 |
AveRooms | 평균 방 수 |
AveBedrms | 평균 침실 수 |
Population | 블록 내 거주중인 인구 수 |
AveOccup | 평균 주택점유율 |
Latitude | 주택 블록 위도 |
Longitude | 주택 블록 경도 |
import pandas as pd
california_df = pd.DataFrame(california.data, columns = california.feature_names)
california_df['target'] = california.target
california_df.head()
california_df.describe()
# 시각화 1
import matplotlib.pyplot as plt
for i, col in enumerate(california_df.columns):
plt.figure(figsize = (8, 4))
plt.plot(california_df[col])
plt.title(col)
plt.tight_layout()
# 시각화 2
import matplotlib.pyplot as plt
for i, col in enumerate(california_df.columns):
plt.figure(figsize = (8, 4))
plt.scatter(california_df[col], california_df['target'])
plt.ylabel('target', size = 12)
plt.xlabel(col, size = 12)
plt.title(col)
plt.tight_layout()
# 시각화 3
import seaborn as sns
sns.pairplot(california_df.sample(1000))
- 위도, 경도 데이터가 있으므로 지도로 시각화 가능(캘리포니아의 모양이 나옴)
california_df.plot(kind = 'scatter', x = 'Longitude', y = 'Latitude', alpha = 0.2, figsize = (12, 10))
- 데이터를 색깔로 추가한 버전
california_df.plot(kind = 'scatter', x = 'Longitude', y = 'Latitude', alpha = 0.2,
s = california_df['Population']/100, label = 'Population', figsize = (12, 10),
c = 'target', cmap = plt.get_cmap('viridis'), colorbar = True)
- 원의 크기가 클수록 인구가 많은 곳
- 색깔이 보라색에 가까울수록 가격이 싼 곳
- 색깔이 노란색에 가까울수록 가격이 비싼 곳
- 색깔이 노란색에 가까운 곳은 바닷가로 추정
- 시각화를 통해 바닷가 주변이 주택 가격이 높다는 인사이트를 얻을 수 있음
- 캘리포니아 주택 가격 선형 회귀
model = LinearRegression()
X_train, X_test, y_train, y_test = train_test_split(california.data, california.target, test_size = 0.2)
model.fit(X_train, y_train)
print('학습 데이터 점수: {}'.format(model.score(X_train, y_train)))
print('평가 데이터 점수: {}'.format(model.score(X_test, y_test)))
# 출력 결과
학습 데이터 점수: 0.6067992442029464
평가 데이터 점수: 0.6022552622453919
scores = cross_val_score(model, california.data, california.target, cv = 10, scoring = 'neg_mean_squared_error')
print('NMSE mean: {}'.format(scores.mean()))
print('NMSE std: {}'.format(scores.std()))
# 출력 결과
NMSE mean: -0.5509524296956628
NMSE std: 0.1928858295386518
r2_scores = cross_val_score(model, california.data, california.target, cv = 10, scoring = 'r2')
print('R2 Score mean: {}'.format(r2_scores.mean()))
# 출력 결과
R2 Score mean: 0.5110068610523781
print('y = ' + str(model.intercept_))
for i, c in enumerate(model.coef_):
print(str(c) + ' * X' + str(i))
# 출력 결과
y = -36.4797991177529
0.4433911368525524 * X0
0.00936296088142479 * X1
-0.12111648584893303 * X2
0.6725589967168644 * X3
-3.8254599120021365e-06 * X4
-0.0034486963486539766 * X5
-0.416116329408061 * X6
-0.4292610187302255 * X7
- RMSE와 \(R^2\) 평가 점수(train 데이터에 대해)
y_train_predict = model.predict(X_train)
rmse = (np.sqrt(mean_squared_error(y_train, y_train_predict)))
r2 = r2_score(y_train, y_train_predict)
print("RMSE Score: {}".format(rmse))
print("R2 Score: {}".format(r2))
# 출력 결과
RMSE Score: 0.7258609395455958
R2 Score: 0.6067992442029464
- RMSE와 \(R^2\) 평가 점수(test 데이터에 대해)
y_test_predict = model.predict(X_test)
rmse = (np.sqrt(mean_squared_error(y_test, y_test_predict)))
r2 = r2_score(y_test, y_test_predict)
print("RMSE Score: {}".format(rmse))
print("R2 Score: {}".format(r2))
# 출력 결과
RMSE Score: 0.7184275601386346
R2 Score: 0.6022552622453919
- 얼마나 잘 예측하는지 시각화
def plot_california(expected, predicted):
plt.figure(figsize = (8, 4))
plt.scatter(expected, predicted)
plt.plot([0, 5], [0, 5], '--r')
plt.xlabel('True Price ($100,000s)')
plt.ylabel('Predicted Price ($100,000s)')
predicted = model.predict(X_test)
expected = y_test
plot_california(expected, predicted)
- 빨간색 선이 실제 값을 잘 예측한 것이고, 빨간 선을 중심으로 넓게 퍼져, 분산이 큰 것이 보임
- 따라서, 예측 점수가 크지 않음
'Python > Machine Learning' 카테고리의 다른 글
[머신러닝 알고리즘] 로지스틱 회귀 (0) | 2023.01.04 |
---|---|
[머신러닝 알고리즘] 선형 회귀(2) (0) | 2023.01.02 |
[머신러닝 알고리즘] 사이킷런 제대로 시작하기(2) (0) | 2022.12.28 |
[머신러닝 알고리즘] 사이킷런 제대로 시작하기(1) (1) | 2022.12.26 |
[ML] 모델 훈련 (1) - 선형회귀 (0) | 2022.10.31 |