ch03 선형모델 - linear model¶
학습 목표¶
- 선형 모델(Linear Regression)에 대해 이해합니다.
- 보스턴 집값 데이터 셋을 활용하여 회귀 모델을 만들어 봅니다.
학습 내용¶
- Boston 데이터 셋 불러오기
- 집값 예측 선형모델 구축
- 모델 평가 지표 알아보기
- MAE
- MSE
- MSLE
- RMSE
- RMLSE
- MAPE
- MPE
In [3]:
from IPython.display import display, Image
In [4]:
### 한글 폰트 설정
import matplotlib
from matplotlib import font_manager, rc
import matplotlib.pyplot as plt
import platform
path = "C:/Windows/Fonts/malgun.ttf"
if platform.system() == "Windows":
font_name = font_manager.FontProperties(fname=path).get_name()
rc('font', family=font_name)
elif platform.system()=="Darwin":
rc('font', family='AppleGothic')
else:
print("Unknown System")
matplotlib.rcParams['axes.unicode_minus'] = False
%matplotlib inline
In [5]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
print("numpy 버전 : ", np.__version__)
print("matplotlib 버전 : ", matplotlib.__version__)
# 설치가 안되어 있을 경우, 설치 필요.
import mglearn
import sklearn
print("sklearn 버전 : ", sklearn.__version__)
print("mglearn 버전 : ", mglearn.__version__)
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
numpy 버전 : 1.26.4 matplotlib 버전 : 3.9.0 sklearn 버전 : 1.0.2 mglearn 버전 : 0.1.9
데이터 설명¶
- 1970년대의 보스턴 주변의 주택 평균 가격 예측
- 506개의 데이터 포인트와 13개의 특성
(1) 모델 만들기 [ 모델명 = 모델객체() ] (2) 모델 학습 시키기 [ 모델명.fit() ] (3) 모델을 활용한 예측하기 [ 모델명.predict() ] (4) 모델 평가
In [6]:
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston
import pandas as pd
데이터 불러오기 2가지 방법¶
In [7]:
# sklearn 이용
boston = load_boston()
X = boston.data # 입력 데이터 - 문제
y = boston.target # 출력 데이터 - 답
C:\Users\user\anaconda3\envs\sklearn102\lib\site-packages\sklearn\utils\deprecation.py:87: FutureWarning: Function load_boston is deprecated; `load_boston` is deprecated in 1.0 and will be removed in 1.2. The Boston housing prices dataset has an ethical problem. You can refer to the documentation of this function for further details. The scikit-learn maintainers therefore strongly discourage the use of this dataset unless the purpose of the code is to study and educate about ethical issues in data science and machine learning. In this special case, you can fetch the dataset from the original source:: import pandas as pd import numpy as np data_url = "http://lib.stat.cmu.edu/datasets/boston" raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None) data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]]) target = raw_df.values[1::2, 2] Alternative datasets include the California housing dataset (i.e. :func:`~sklearn.datasets.fetch_california_housing`) and the Ames housing dataset. You can load the datasets as follows:: from sklearn.datasets import fetch_california_housing housing = fetch_california_housing() for the California housing dataset and:: from sklearn.datasets import fetch_openml housing = fetch_openml(name="house_prices", as_frame=True) for the Ames housing dataset. warnings.warn(msg, category=FutureWarning)
In [10]:
boston = load_boston()
X = boston.data # 입력 데이터 - 문제
y = boston.target # 출력 데이터 - 답
print( boston.keys() )
print( boston.feature_names )
dict_keys(['data', 'target', 'feature_names', 'DESCR', 'filename', 'data_module']) ['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO' 'B' 'LSTAT']
In [11]:
# 판다스 데이터프레임으로 변환
df = pd.DataFrame(X, columns=boston.feature_names)
df['target'] = y
df
Out[11]:
CRIM | ZN | INDUS | CHAS | NOX | RM | AGE | DIS | RAD | TAX | PTRATIO | B | LSTAT | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.00632 | 18.0 | 2.31 | 0.0 | 0.538 | 6.575 | 65.2 | 4.0900 | 1.0 | 296.0 | 15.3 | 396.90 | 4.98 | 24.0 |
1 | 0.02731 | 0.0 | 7.07 | 0.0 | 0.469 | 6.421 | 78.9 | 4.9671 | 2.0 | 242.0 | 17.8 | 396.90 | 9.14 | 21.6 |
2 | 0.02729 | 0.0 | 7.07 | 0.0 | 0.469 | 7.185 | 61.1 | 4.9671 | 2.0 | 242.0 | 17.8 | 392.83 | 4.03 | 34.7 |
3 | 0.03237 | 0.0 | 2.18 | 0.0 | 0.458 | 6.998 | 45.8 | 6.0622 | 3.0 | 222.0 | 18.7 | 394.63 | 2.94 | 33.4 |
4 | 0.06905 | 0.0 | 2.18 | 0.0 | 0.458 | 7.147 | 54.2 | 6.0622 | 3.0 | 222.0 | 18.7 | 396.90 | 5.33 | 36.2 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
501 | 0.06263 | 0.0 | 11.93 | 0.0 | 0.573 | 6.593 | 69.1 | 2.4786 | 1.0 | 273.0 | 21.0 | 391.99 | 9.67 | 22.4 |
502 | 0.04527 | 0.0 | 11.93 | 0.0 | 0.573 | 6.120 | 76.7 | 2.2875 | 1.0 | 273.0 | 21.0 | 396.90 | 9.08 | 20.6 |
503 | 0.06076 | 0.0 | 11.93 | 0.0 | 0.573 | 6.976 | 91.0 | 2.1675 | 1.0 | 273.0 | 21.0 | 396.90 | 5.64 | 23.9 |
504 | 0.10959 | 0.0 | 11.93 | 0.0 | 0.573 | 6.794 | 89.3 | 2.3889 | 1.0 | 273.0 | 21.0 | 393.45 | 6.48 | 22.0 |
505 | 0.04741 | 0.0 | 11.93 | 0.0 | 0.573 | 6.030 | 80.8 | 2.5050 | 1.0 | 273.0 | 21.0 | 396.90 | 7.88 | 11.9 |
506 rows × 14 columns
데이터 살펴보기¶
features | 내용 | 값 | |
---|---|---|---|
crim | 마을별 1인당 범죄율 | - | |
zn | 25,000 평방 피트 이상의 대형 주택이 차지하는 주거용 토지의 비율 | - | |
indus | 소매상 이외의 상업 지구의 면적 비율 | - | |
chas | Charles River(찰스강 접한 지역인지 아닌지) (강 경계면=1, 아니면=0 | - | |
nox | 산화 질소 오염도(1000만분 율) | - | |
rm | 주거 당 평균 방수 | - | |
age | 1940년 이전에 지어진 소유주 집들의 비율 | - | |
dis | 보스턴 고용 센터 5곳까지의 가중 거리 | - | |
rad | 도시 순환 고속도로에의 접근 용이 지수 | - | |
tax | 만 달러당 주택 재산세율 | - | |
ptratio | 학생 - 선생 비율 | - | |
black-(B) | 흑인 인구 비율(Bk)이 지역 평균인 0.63과 다른 정도의 제곱 | - | |
lstat | 저소득 주민들의 비율 퍼센트 | - | |
(target) MEDV | 소유주가 거주하는 주택의 중간 가치($ 1000) | - |
주택 가격 - 히스토 그램¶
In [13]:
plt.hist(y)
Out[13]:
(array([ 21., 55., 82., 154., 84., 41., 30., 8., 10., 21.]), array([ 5. , 9.5, 14. , 18.5, 23. , 27.5, 32. , 36.5, 41. , 45.5, 50. ]), <BarContainer object of 10 artists>)
- 주택 가격은 정규 분포를 따르는 것을 확인
- (실습) DataFrame으로 만들어 기본 시각화 등을 통해 확인해 보자.
데이터 나누기 - 학습, 평가¶
In [15]:
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.3,
random_state=42)
In [16]:
model = LinearRegression().fit(X_train, y_train) # 학습
pred = model.predict(X_test)
pred
Out[16]:
array([28.64896005, 36.49501384, 15.4111932 , 25.40321303, 18.85527988, 23.14668944, 17.3921241 , 14.07859899, 23.03692679, 20.59943345, 24.82286159, 18.53057049, -6.86543527, 21.80172334, 19.22571177, 26.19191985, 20.27733882, 5.61596432, 40.44887974, 17.57695918, 27.44319095, 30.1715964 , 10.94055823, 24.02083139, 18.07693812, 15.934748 , 23.12614028, 14.56052142, 22.33482544, 19.3257627 , 22.16564973, 25.19476081, 25.31372473, 18.51345025, 16.6223286 , 17.50268505, 30.94992991, 20.19201752, 23.90440431, 24.86975466, 13.93767876, 31.82504715, 42.56978796, 17.62323805, 27.01963242, 17.19006621, 13.80594006, 26.10356557, 20.31516118, 30.08649576, 21.3124053 , 34.15739602, 15.60444981, 26.11247588, 39.31613646, 22.99282065, 18.95764781, 33.05555669, 24.85114223, 12.91729352, 22.68101452, 30.80336295, 31.63522027, 16.29833689, 21.07379993, 16.57699669, 20.36362023, 26.15615896, 31.06833034, 11.98679953, 20.42550472, 27.55676301, 10.94316981, 16.82660609, 23.92909733, 5.28065815, 21.43504661, 41.33684993, 18.22211675, 9.48269245, 21.19857446, 12.95001331, 21.64822797, 9.3845568 , 23.06060014, 31.95762512, 19.16662892, 25.59942257, 29.35043558, 20.13138581, 25.57297369, 5.42970803, 20.23169356, 15.1949595 , 14.03241742, 20.91078077, 24.82249135, -0.47712079, 13.70520524, 15.69525576, 22.06972676, 24.64152943, 10.7382866 , 19.68622564, 23.63678009, 12.07974981, 18.47894211, 25.52713393, 20.93461307, 24.6955941 , 7.59054562, 19.01046053, 21.9444339 , 27.22319977, 32.18608828, 15.27826455, 34.39190421, 12.96314168, 21.01681316, 28.57880911, 15.86300844, 24.85124135, 3.37937111, 23.90465773, 25.81792146, 23.11020547, 25.33489201, 33.35545176, 20.60724498, 38.4772665 , 13.97398533, 25.21923987, 17.80946626, 20.63437371, 9.80267398, 21.07953576, 22.3378417 , 32.32381854, 31.48694863, 15.46621287, 16.86242766, 28.99330526, 24.95467894, 16.73633557, 6.12858395, 26.65990044, 23.34007187, 17.40367164, 13.38594123, 39.98342478, 16.68286302, 18.28561759])
예측값이 0이하의 값이 있다. 이 경우 RMLSE에서 에러 발생. pred를 0이하는 0으로 처리한다.¶
In [17]:
type(pred)
Out[17]:
numpy.ndarray
In [18]:
pred[ pred < 0] = 0
pred
Out[18]:
array([28.64896005, 36.49501384, 15.4111932 , 25.40321303, 18.85527988, 23.14668944, 17.3921241 , 14.07859899, 23.03692679, 20.59943345, 24.82286159, 18.53057049, 0. , 21.80172334, 19.22571177, 26.19191985, 20.27733882, 5.61596432, 40.44887974, 17.57695918, 27.44319095, 30.1715964 , 10.94055823, 24.02083139, 18.07693812, 15.934748 , 23.12614028, 14.56052142, 22.33482544, 19.3257627 , 22.16564973, 25.19476081, 25.31372473, 18.51345025, 16.6223286 , 17.50268505, 30.94992991, 20.19201752, 23.90440431, 24.86975466, 13.93767876, 31.82504715, 42.56978796, 17.62323805, 27.01963242, 17.19006621, 13.80594006, 26.10356557, 20.31516118, 30.08649576, 21.3124053 , 34.15739602, 15.60444981, 26.11247588, 39.31613646, 22.99282065, 18.95764781, 33.05555669, 24.85114223, 12.91729352, 22.68101452, 30.80336295, 31.63522027, 16.29833689, 21.07379993, 16.57699669, 20.36362023, 26.15615896, 31.06833034, 11.98679953, 20.42550472, 27.55676301, 10.94316981, 16.82660609, 23.92909733, 5.28065815, 21.43504661, 41.33684993, 18.22211675, 9.48269245, 21.19857446, 12.95001331, 21.64822797, 9.3845568 , 23.06060014, 31.95762512, 19.16662892, 25.59942257, 29.35043558, 20.13138581, 25.57297369, 5.42970803, 20.23169356, 15.1949595 , 14.03241742, 20.91078077, 24.82249135, 0. , 13.70520524, 15.69525576, 22.06972676, 24.64152943, 10.7382866 , 19.68622564, 23.63678009, 12.07974981, 18.47894211, 25.52713393, 20.93461307, 24.6955941 , 7.59054562, 19.01046053, 21.9444339 , 27.22319977, 32.18608828, 15.27826455, 34.39190421, 12.96314168, 21.01681316, 28.57880911, 15.86300844, 24.85124135, 3.37937111, 23.90465773, 25.81792146, 23.11020547, 25.33489201, 33.35545176, 20.60724498, 38.4772665 , 13.97398533, 25.21923987, 17.80946626, 20.63437371, 9.80267398, 21.07953576, 22.3378417 , 32.32381854, 31.48694863, 15.46621287, 16.86242766, 28.99330526, 24.95467894, 16.73633557, 6.12858395, 26.65990044, 23.34007187, 17.40367164, 13.38594123, 39.98342478, 16.68286302, 18.28561759])
In [19]:
import pandas as pd
import numpy as np
In [20]:
error = y_test - pred
error2 = (y_test - pred)**2
dict_dat = {"실제값":y_test, "예측값":pred, "오차":error,
"오차제곱":error2}
dat = pd.DataFrame(dict_dat )
dat
Out[20]:
실제값 | 예측값 | 오차 | 오차제곱 | |
---|---|---|---|---|
0 | 23.6 | 28.648960 | -5.048960 | 25.491998 |
1 | 32.4 | 36.495014 | -4.095014 | 16.769138 |
2 | 13.6 | 15.411193 | -1.811193 | 3.280421 |
3 | 22.8 | 25.403213 | -2.603213 | 6.776718 |
4 | 16.1 | 18.855280 | -2.755280 | 7.591567 |
... | ... | ... | ... | ... |
147 | 17.1 | 17.403672 | -0.303672 | 0.092216 |
148 | 14.5 | 13.385941 | 1.114059 | 1.241127 |
149 | 50.0 | 39.983425 | 10.016575 | 100.331779 |
150 | 14.3 | 16.682863 | -2.382863 | 5.678036 |
151 | 12.6 | 18.285618 | -5.685618 | 32.326247 |
152 rows × 4 columns
In [21]:
dat['오차절대값'] = abs(dat['오차'])
dat
Out[21]:
실제값 | 예측값 | 오차 | 오차제곱 | 오차절대값 | |
---|---|---|---|---|---|
0 | 23.6 | 28.648960 | -5.048960 | 25.491998 | 5.048960 |
1 | 32.4 | 36.495014 | -4.095014 | 16.769138 | 4.095014 |
2 | 13.6 | 15.411193 | -1.811193 | 3.280421 | 1.811193 |
3 | 22.8 | 25.403213 | -2.603213 | 6.776718 | 2.603213 |
4 | 16.1 | 18.855280 | -2.755280 | 7.591567 | 2.755280 |
... | ... | ... | ... | ... | ... |
147 | 17.1 | 17.403672 | -0.303672 | 0.092216 | 0.303672 |
148 | 14.5 | 13.385941 | 1.114059 | 1.241127 | 1.114059 |
149 | 50.0 | 39.983425 | 10.016575 | 100.331779 | 10.016575 |
150 | 14.3 | 16.682863 | -2.382863 | 5.678036 | 2.382863 |
151 | 12.6 | 18.285618 | -5.685618 | 32.326247 | 5.685618 |
152 rows × 5 columns
평가 지표¶
- 모델을 평가하기 위해 회귀모델은 일반적으로 사용하는 지표는 다음을 사용합니다.
- MAE(mean absolute error) : 평균 절대값 오차
- MAPE(mean absolute percentage error) : 평균 절대값 백분율 오차
- MSE(mean squared error) : 평균 제곱 오차
- RMSE(root mean squared error) : 평균 제곱근 오차
- RMLSE(Root Mean Squared Logarithmic Error)
In [22]:
### MAE (mean absolute error)
mae_val = dat['오차절대값'].sum() / dat.shape[0]
mae_val
Out[22]:
3.114403581586905
In [23]:
### MSE (mean squared error)
mse_val = dat['오차제곱'].sum() / dat.shape[0]
mse_val
Out[23]:
20.461135916905192
In [24]:
### RMSE(Root Mean Squared Error)
rmse_val = np.sqrt(mse_val)
rmse_val
Out[24]:
4.523398713014937
(실습) RMLSE 값을 구해보자.¶
In [25]:
dat['실제값_log'] = np.log( dat['실제값'] + 1 )
dat['예측값_log'] = np.log( dat['예측값'] + 1 )
dat['log오차제곱'] = (dat['실제값_log'] - dat['예측값_log']) ** 2
In [26]:
dat
Out[26]:
실제값 | 예측값 | 오차 | 오차제곱 | 오차절대값 | 실제값_log | 예측값_log | log오차제곱 | |
---|---|---|---|---|---|---|---|---|
0 | 23.6 | 28.648960 | -5.048960 | 25.491998 | 5.048960 | 3.202746 | 3.389427 | 0.034850 |
1 | 32.4 | 36.495014 | -4.095014 | 16.769138 | 4.095014 | 3.508556 | 3.624208 | 0.013375 |
2 | 13.6 | 15.411193 | -1.811193 | 3.280421 | 1.811193 | 2.681022 | 2.797964 | 0.013675 |
3 | 22.8 | 25.403213 | -2.603213 | 6.776718 | 2.603213 | 3.169686 | 3.273486 | 0.010774 |
4 | 16.1 | 18.855280 | -2.755280 | 7.591567 | 2.755280 | 2.839078 | 2.988470 | 0.022318 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
147 | 17.1 | 17.403672 | -0.303672 | 0.092216 | 0.303672 | 2.895912 | 2.912550 | 0.000277 |
148 | 14.5 | 13.385941 | 1.114059 | 1.241127 | 1.114059 | 2.740840 | 2.666251 | 0.005563 |
149 | 50.0 | 39.983425 | 10.016575 | 100.331779 | 10.016575 | 3.931826 | 3.713168 | 0.047811 |
150 | 14.3 | 16.682863 | -2.382863 | 5.678036 | 2.382863 | 2.727853 | 2.872596 | 0.020951 |
151 | 12.6 | 18.285618 | -5.685618 | 32.326247 | 5.685618 | 2.610070 | 2.959360 | 0.122003 |
152 rows × 8 columns
In [27]:
### RMLSE(Root Mean )
mlse_val = dat['log오차제곱'].sum() / dat.shape[0]
print("mlse value :", mlse_val)
rmlse_val = np.sqrt(mlse_val)
print("rmlse value :", rmlse_val)
mlse value : 0.12426815124184605 rmlse value : 0.3525168807899078
sklearn을 활용한 평가지표 구하기¶
In [28]:
import numpy as np
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_squared_log_error
In [29]:
### MAE(mean absolute error) : 평균 절대값 오차
mean_absolute_error(y_test, pred)
Out[29]:
3.114403581586905
In [30]:
### MSE (mean squared error)
mean_squared_error(y_test, pred)
Out[30]:
20.461135916905192
In [31]:
### MSLE (mean squared log error)
mean_squared_log_error(y_test, pred)
Out[31]:
0.12426815124184605
In [32]:
### RMSE(Root Mean Squared Error)
rmse_val = np.sqrt( mean_squared_error(y_test, pred) )
rmse_val
Out[32]:
4.523398713014937
In [33]:
### RMLSE(Root Mean Log Squared Error)
rmlse_val = np.sqrt( mean_squared_log_error(y_test, pred) )
rmlse_val
Out[33]:
0.3525168807899078
평가지표를 함수로 만들기¶
MAE (mean absolute error)¶
- 각각의 값에 절대값을 취한다. 이를 전부 더한 후, 갯수로 나누어주기
In [34]:
def mae_1(y_test, y_pred):
error = y_test - y_pred
abs_error = np.abs(error)
mae_val = np.mean(abs_error)
return mae_val
mae_1(y_test, pred)
Out[34]:
3.114403581586905
MSE (mean squared error)¶
- 각각의 데이터의 (실제값-예측값) ^ 2 의 합를 데이터의 샘플의 개수로 나누어준것
In [35]:
def mse_1(y_test, y_pred):
error = y_test - y_pred
error_2 = (error) ** 2
mse_val = np.mean(error_2)
return mse_val
mse_1(y_test, pred)
Out[35]:
20.461135916905192
RMSE (root mean squared error)¶
- 각 데이터의 (실제값-예측값) ^ 2 의 합을 데이터의 샘플의 개수로 나누어 준 이후에 제곱근 씌우기
In [36]:
def rmse_1(y_test, y_pred):
error = y_test - y_pred
error_2 = (error) ** 2
rmse_val = np.sqrt( np.mean(error_2) )
return rmse_val
rmse_1(y_test, pred)
Out[36]:
4.523398713014937
MAPE(Mean Absolute Percentage Error)¶
- MAE를 퍼센트로 변환
In [37]:
def MAPE(y_test, y_pred):
error = (y_test - y_pred)/y_test
mape_error = np.abs(error)
return np.mean( mape_error ) * 100
MAPE(y_test, pred)
Out[37]:
15.858437956856678
MPE(Mean Percentage Error)¶
- MAPE에서 절대값을 제외한 지표
In [38]:
def MAE(y_test, y_pred):
return np.mean( (y_test - y_pred) / y_test ) * 100
MAE(y_test, pred)
Out[38]:
-1.4494655388253086
RMLSE¶
In [39]:
def rmsle(y_test, y_pred):
log_y = np.log1p(y_test)
log_pred = np.log1p(y_pred)
squared_error = (log_y - log_pred) ** 2
rmsle = np.sqrt(np.mean(squared_error))
return rmsle
rmsle(y_test, pred)
Out[39]:
0.3525168807899078
- 만약, 값 중에 음수가 있을 경우, 예측값이 0이하의 값이 존재하므로 log1p(_)해도 무한으로 음수에 가까워짐. 정상적인 동작이 안되므로 이를 처리(음수 제거) 해주어야 한다.
실습 과제¶
kaggle 데이터 셋 : https://www.kaggle.com/datasets/harlfoxem/housesalesprediction
- 실습 01. 데이터 나누기
- 실습 02. 선형회귀 모델 만들기
- 실습 03. MAE, MSE, RMSE 지표로 비교해 보기
교육용으로 작성된 것으로 배포 및 복제시에 사전 허가가 필요합니다.
Copyright 2022 LIM Co. all rights reserved.