1 5.3 Gradient Descent
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
np.random.seed(0)
2 y = 4X + 6
Y=4X+6 모델에 실제값을 시뮬레이션하는 데이터 값 생성
(w1=4, w0=6)
np.random.seed(0)
np.random.rand(100, 1)[0:10]
# 0~1 값 표준정규분포를 나타내는 100행 1열의 numpy array
# np.random.rand 0부터 1사이의 균일 분포에서 난수 matrix array생성
>>>
array([[0.5488135 ],
[0.71518937],
[0.60276338],
[0.54488318],
[0.4236548 ],
[0.64589411],
[0.43758721],
[0.891773 ],
[0.96366276],
[0.38344152]])
np.random.seed(0)
# X값은 0~2 값 표준정규분포를 나타내는 100행 1열의 numpy array
X = 2*np.random.rand(100, 1)
X[0:10]
>>>
array([[1.09762701],
[1.43037873],
[1.20552675],
[1.08976637],
[0.8473096 ],
[1.29178823],
[0.87517442],
[1.783546 ],
[1.92732552],
[0.76688304]])
# y는 랜덤으로 생성된 X값에 매칭되는 값
y = 6 + 4*X + np.random.randn(100, 1) # random값은 Noise를 위해 만듦
# 과연 머신러닝이 이 값으로 수렴하는 일차 방정식을 만들어 낼까?
# X, y 데이터 셋 scatter plot으로 시각화
plt.scatter(X, y)
X.shape, y.shape
((100, 1), (100, 1))
** 오차함수를 최소화 할 수 있도록 w0과 w1의 값을 업데이트 수행하는 함수 생성.**
y_pred = w1*x + w0
* 예측 배열 y_pred는 np.dot(X, w1.T) + w0 임
입력 데이터 X(1,2,...,100)이 있다면,
예측값은 w0 + X(1)*w1 + X(2)*w1 +..+ X(100)*w1이며, 이는 입력 배열 X와 w1 배열의 내적임.
* 새로운 w1과 w0를 update함
# w1 과 w0 를 업데이트하는 함수
def get_weight_updates(w1, w0, X, y, learning_rate=0.01):
N = len(y)
# 먼저 w1_update, w0_update를 각각 w1, w0의 shape와 동일한 크기를 가진 0 값으로 초기화
w1_update = np.zeros_like(w1)
w0_update = np.zeros_like(w0)
# zeros_like(w1) : 0으로 만드는데, shape은 (w1)으로 만듬
# 예측 배열 계산하고 예측과 실제 값의 차이 계산
y_pred = np.dot(X, w1.T) + w0
diff = y - y_pred
# w0_update를 dot 행렬 연산으로 구하기 위해 모두 1값을 가진 행렬 생성
w0_factors = np.ones((N, 1))
# w1과 w0을 업데이트할 w1_update와 w0_update 계산
w1_update = -(2/N)*learning_rate*(np.dot(X.T, diff))
w0_update = -(2/N)*learning_rate*(np.dot(w0_factors.T, diff))
return w1_update, w0_update
w0 = np.zeros((1, 1))
w1 = np.zeros((1, 1))
w1, w0
>>> (array([[0.]]), array([[0.]]))
y_pred = np.dot(X, w1.T) + w0
diff = y - y_pred
# 예측의 차이값 갯수 : N개
print(diff.shape, '\n')
>>> (100, 1)
w0_factors = np.ones((100,1))
w1_update = -(2/100)*0.01*(np.dot(X.T, diff))
w0_update = -(2/100)*0.01*(np.dot(w0_factors.T, diff))
print(w1_update.shape, w0_update.shape)
>>> (1, 1) (1, 1)
# 반복적으로 w1과 w0를 업데이트 하는 함수
def gradient_descent_steps(X, y, iters=10000):
# w0와 w1을 모두 0으로 초기화.
w0 = np.zeros((1, 1))
w1 = np.zeros((1, 1))
# 인자로 주어진 iters 만큼 반복적으로 get_weight_updates() 호출하여
for ind in range(iters):
# w1, w0 업데이트 수행.
w1_update, w0_update = get_weight_updates(w1, w0, X, y, learning_rate=0.01)
w1 = w1 - w1_update
w0 = w0 - w0_update
return w1, w0 # iter만큼 업데이트된 w1, w0 반환
# 오차 함수 정의
def get_cost(y, y_pred):
N = len(y)
cost = np.sum(np.square(y - y_pred))/N
return cost
3 경사하강법 수행해서 w1, w0 업데이트
w1, w0 = gradient_descent_steps(X, y, iters=70000)
print("w1: {0:.3f} w0: {1:.3f}".format(w1[0,0], w0[0,0]), '\n')
y_pred = w1[0,0] * X + w0
print('Gradient Descent Total Cost:{0:.4f}'.format(get_cost(y, y_pred)))
>>>
w1: 3.895 w0: 5.948
Gradient Descent Total Cost:0.9587
plt.scatter(X, y)
plt.plot(X, y_pred)
3.1 미니 배치 확률적 경사 하강법(SGD)을 이용한 최적 비용함수 도출
전체 데이터에 대해 경사하강법 연산을 수행하려면 시간이 오래걸리기 때문에 미니 배치로 샘플링하면 빠르게 경사하강법을 수행할 수 있음
def stochastic_gradient_descent_steps(X, y, batch_size=10, iters=1000):
w0 = np.zeros((1, 1))
w1 = np.zeros((1, 1))
prev_cost = 100000
iter_index =0
for ind in range(iters):
np.random.seed(ind)
# 전체 X, y 데이터에서 랜덤하게 batch_size만큼 데이터 추출하여 sample_X, sample_y로 저장
stochastic_random_index = np.random.permutation(X.shape[0])
sample_X = X[stochastic_random_index[0:batch_size]]
sample_y = y[stochastic_random_index[0:batch_size]]
# 미니 배치 임의로 추출
# 랜덤하게 batch_size만큼 추출된 데이터 기반으로 w1_update, w0_update 계산 후 업데이트
w1_update, w0_update = get_weight_updates(w1, w0, sample_X, sample_y, learning_rate=0.01)
w1 = w1 - w1_update
w0 = w0 - w0_update
return w1, w0
# 100개의 랜덤 순열 만들기
np.random.permutation(X.shape[0])
>>>
array([66, 71, 54, 88, 82, 12, 36, 46, 14, 67, 10, 3, 62, 29, 97, 69, 70,
93, 31, 73, 60, 96, 28, 27, 21, 19, 33, 78, 32, 94, 1, 41, 40, 76,
37, 87, 24, 23, 50, 2, 47, 20, 77, 17, 56, 64, 68, 25, 15, 22, 16,
98, 63, 92, 86, 38, 6, 57, 95, 44, 9, 42, 81, 99, 35, 84, 59, 48,
75, 65, 85, 90, 55, 43, 58, 89, 30, 80, 34, 18, 51, 49, 52, 74, 26,
45, 39, 4, 11, 53, 91, 79, 8, 0, 5, 13, 61, 72, 7, 83])
w1, w0 = stochastic_gradient_descent_steps(X, y, iters=50000)
print("w1:", round(w1[0,0],3),"w0:", round(w0[0,0],3))
y_pred = w1[0,0] * X + w0
print('Stochastic Gradient Descent Total Cost:{0:.4f}'.format(get_cost(y, y_pred)))
>>>
w1: 3.991 w0: 6.202
Stochastic Gradient Descent Total Cost:0.9926
-> 앞과 거의 비슷하게 w1, w0값이 도출된다.
4 5.4 사이킷런 LinearRegression을 이용한 보스턴 주택 가격 예측
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from scipy import stats
from sklearn.datasets import load_boston
%matplotlib inline
4.1 데이터 로드 및 확인
# boston 데이타셋 로드
boston = load_boston()
# boston 데이타셋 DataFrame 변환
bostonDF = pd.DataFrame(boston.data , columns = boston.feature_names)
# boston dataset의 target array는 주택 가격임. 이를 PRICE 컬럼으로 DataFrame에 추가함.
bostonDF['PRICE'] = boston.target
print('Boston 데이타셋 크기 :',bostonDF.shape)
bostonDF.head()
- CRIM: 지역별 범죄 발생률
- ZN: 25,000평방피트를 초과하는 거주 지역의 비율
- NDUS: 비 상업 지역 넓이 비율
- CHAS: 찰스강에 대한 더미 변수(강의 경계에 위치한 경우는 1, 아니면 0)
- NOX: 일산화질소 농도
- RM: 거주할 수 있는 방 개수
- AGE: 1940년 이전에 건축된 소유 주택의 비율
- DIS: 5개 주요 고용센터까지의 가중 거리
- RAD: 고속도로 접근 용이도
- TAX: 10,000달러당 재산세율
- PTRATIO: 지역의 교사와 학생 수 비율
- B: 지역의 흑인 거주 비율
- LSTAT: 하위 계층의 비율
- PRICE: 본인 소유의 주택 가격(중앙값)
각 컬럼별로 주택 가격에 미치는 영향도를 조사
# 2개의 행과 4개의 열을 가진 subplots를 이용. axs는 4x2개의 ax를 가짐.
fig, axs = plt.subplots(figsize=(16, 8), ncols=4, nrows=2)
lm_features = ['RM','ZN','INDUS','NOX','AGE','PTRATIO','LSTAT','RAD']
for i, feature in enumerate(lm_features):
row = int(i/4) # 4행
col = i%4
# sns.regplot : 회귀 직선을 그려줌
sns.regplot(x=feature, y='PRICE', data=bostonDF, ax=axs[row][col])
강한 양의 상관관계 best : RM
강한 음의 상관관계 best : LSTAT
4.1.1 학습과 테스트 데이터 세트로 분리하고 학습/예측/평가 수행
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error , r2_score
# feature, target 데이터 분리
y_target = bostonDF['PRICE']
X_data = bostonDF.drop(['PRICE'], axis=1, inplace=False)
# train, test 데이터 분리
X_train , X_test , y_train , y_test = train_test_split(X_data , y_target ,test_size=0.3, random_state=156)
# Linear Regression
lr = LinearRegression()
# 학습
lr.fit(X_train , y_train )
# 예측
y_preds = lr.predict(X_test)
# 평가
mse = mean_squared_error(y_test, y_preds)
rmse = np.sqrt(mse)
print('MSE : {0:.3f} , RMSE : {1:.3F}'.format(mse , rmse))
print('Variance score : {0:.3f}'.format(r2_score(y_test, y_preds)))
>>> MSE : 17.297 , RMSE : 4.159
Variance score : 0.757
-> Variance score, R2 score는 회귀 모델이 자료를 얼마나 잘 설명하는지를 말한다. (모델의 설명력)
print('절편 값:', lr.intercept_)
print('회귀 계수값:', np.round(lr.coef_, 1))
>>>
절편 값: 40.995595172164336
회귀 계수값: [ -0.1 0.1 0. 3. -19.8 3.4 0. -1.7 0.4 -0. -0.9 0.
-0.6]
# 회귀 계수를 큰 값 순으로 정렬하기 위해 Series로 생성. index가 컬럼명에 유의
coeff = pd.Series(data=np.round(lr.coef_, 1), index=X_data.columns )
coeff.sort_values(ascending=False)
>>>
RM 3.4
CHAS 3.0
RAD 0.4
ZN 0.1
B 0.0
TAX -0.0
AGE 0.0
INDUS 0.0
CRIM -0.1
LSTAT -0.6
PTRATIO -0.9
DIS -1.7
NOX -19.8
dtype: float64
RM의 양의 절대값이 제일 크다.
NOX가 음의 절대값이 너무 크다.
4.1.2 cross_val_score( )로 MSE 구한 뒤 이를 기반으로 RMSE 구하기
from sklearn.model_selection import cross_val_score
# features, target 데이터 정의
y_target = bostonDF['PRICE']
X_data = bostonDF.drop(['PRICE'], axis=1, inplace=False)
# 선형 회귀 객체 생성
lr = LinearRegression()
lr
# 5 folds 의 개별 Negative MSE scores
neg_mse_scores = cross_val_score(lr, X_data, y_target, scoring="neg_mean_squared_error", cv = 5)
neg_mse_scores
>>> array([-12.46030057, -26.04862111, -33.07413798, -80.76237112, -33.31360656])
# RMSE를 구하기 위해서는 MSE값들에 -1을 곱한 후 평균을 내면 된다.
rmse_scores = np.sqrt( -1 * neg_mse_scores )
rmse_scores
>>> 5.828658946215792
# cross_val_score(scoring="neg_mean_squared_error")로 반환된 값은 모두 음수
print(' 5 folds 의 개별 Negative MSE scores: ', np.round(neg_mse_scores, 2))
print(' 5 folds 의 개별 RMSE scores : ', np.round(rmse_scores, 2))
print(' 5 folds 의 평균 RMSE : {0:.3f} '.format(avg_rmse))
>>>
5 folds 의 개별 Negative MSE scores: [-12.46 -26.05 -33.07 -80.76 -33.31]
5 folds 의 개별 RMSE scores : [3.53 5.1 5.75 8.99 5.77]
5 folds 의 평균 RMSE : 5.829
-> 보스턴 주택가격 예측을 했더니 RMSE가 5.829가 나왔다.
'Machine Learning > 머신러닝 완벽가이드 for Python' 카테고리의 다른 글
ch.5.6 규제 선형 회귀 (릿지, 라쏘, 엘라스틱넷) (0) | 2022.10.13 |
---|---|
ch.5.5 다항회귀 (Polynomial Regression) (0) | 2022.10.13 |
ch.5.4 사이킷런 LinearRegression 클래스 (보스턴주택가격예측) (0) | 2022.10.12 |
ch.5.3 경사하강법 - 비용최소화하기 (0) | 2022.10.12 |
ch.5.2 단순 선형 S회귀를 통한 회귀의 이해 (0) | 2022.10.12 |