<jupyter_start><jupyter_text>"[머신러닝 가이드] 5-1 단순선형회귀"- author: <NAME> - categories: [book, jupyter, Guide, math, Regression] - image: images/210920.png  경사하강법<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

np.random.seed(8)
x = 2 * np.random.randn(100,1)
y = 6 + 4 * x + np.random.randn(100,1)
plt.scatter(x,y)<jupyter_output><empty_output><jupyter_text>y = 4x + 6 근사np.random.randn => 표준정규분포에서 값 생성. 100,1 은 값 행렬 형식 선언 입니다.<jupyter_code>def get_cost(y, y_pred):
    N = len(y)
    cost = np.sum(np.square(y-y_pred)) / N
    return cost<jupyter_output><empty_output><jupyter_text>편차 제곱 평균을 계산해주는 함수.np.square => 제곱 해주는 함수<jupyter_code>def get_weight_updates(w1, w0, x, y, learning_rate = 0.01):
    N = len(y)
    # w1, w0 동일한 행렬 크기를 갖는 0 값으로 초기화 
    w1_update = np.zeros_like(w1)
    w0_update = np.zeros_like(w0)

    #np.dot 행렬의 곱
    y_pred = np.dot(x, w1.T) + w0
    diff = y - y_pred

    w0_factors = np.ones((N, 1))
    
    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<jupyter_output><empty_output><jupyter_text>편미분한 w1, w0값을 이용해서 w0, w1값을 지속적으로 업데이트 해줍니다np.zeros_like(w1) => w1값과 같은 형태에 값은 0인 행렬 생성np.dot(,) => 행렬 연산<jupyter_code>def gradient_descent_steps(x,y, iters = 10000):
    w0 = np.zeros((1,1))
    w1 = np.zeros((1,1))

    for ind in range(iters):
        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<jupyter_output><empty_output><jupyter_text>위 두 함수를 통해 w1, w0 값을 지속적으로 업데이트 하여 최적에 값에 도달하게 합니다.<jupyter_code>w1, w0 = gradient_descent_steps(x,y, 1000)
print('w1 :', np.round(w1[0,0],4), 'w0 :', np.round(w0[0,0],4))
y_pred = w1[0,0] * x + w0
print('편차제곱평균:', np.round(get_cost(y, y_pred),4))
plt.scatter(x,y)
plt.plot(x,y_pred)<jupyter_output><empty_output><jupyter_text>경사하강법을 이용해 회귀선이 잘 만들어졌습니다.다만 데이터에 개수가 100개보다 훨씬 많아지면 전체데이터로 계수를 업데이트 하지 못합니다.그 때문에 실전에서는 대부분 (미니배치)확률적 경사 하강법을 이용합니다.이 방식은 전체 데이터가 아닌 일부 데이터로 계수를 업데이트 하기 때문에 속도가 상대적으로 빠릅니다.이를 구현해보겠습니다.<jupyter_code>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)
        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]]

        w1_update, w0_update = get_weight_updates(w1, w0, sample_x, sample_y)
        w1 = w1 - w1_update
        w0 = w0 - w0_update

    return w1, w0<jupyter_output><empty_output><jupyter_text>np.random.permutation(x.shape[0]) => 주어진 데이터를 셔플해서 출력함앞 함수와 바뀐 부분은 x, y를 샘플링해서 넣는다는 점 입니다.<jupyter_code>w1, w0 = stochastic_gradient_descent_steps(x,y,iters=1000)
print('w1:', np.round(w1[0,0],3), 'w0:', np.round(w0[0,0],4))

y_pred = w1[0,0] * x + w0

print('편차제곱 평균:', np.round(get_cost(y,y_pred),4))<jupyter_output>w1: 4.006 w0: 5.9135
편차제곱 평균: 1.1996<jupyter_text>편차제곱 평균 값이 전체 x,y를 투입했을때와 큰 차이가 없습니다.그러므로 계산 속도가 훨씬 빠른 미니배치 경사하강법을 많이 사용합니다.  단순 선형 회귀(보스턴 주택 가격)<jupyter_code>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

boston = load_boston()

bostonDF = pd.DataFrame(boston.data, columns = boston.feature_names)

bostonDF['PRICE'] = boston.target
print('보스턴 데이터 세트 크기:', bostonDF.shape)
bostonDF.head()<jupyter_output>보스턴 데이터 세트 크기: (506, 14)<jupyter_text>사이킷런에 내장되어있는 보스턴 주택 데이터를 불러왔습니다.<jupyter_code>bostonDF.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 14 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   CRIM     506 non-null    float64
 1   ZN       506 non-null    float64
 2   INDUS    506 non-null    float64
 3   CHAS     506 non-null    float64
 4   NOX      506 non-null    float64
 5   RM       506 non-null    float64
 6   AGE      506 non-null    float64
 7   DIS      506 non-null    float64
 8   RAD      506 non-null    float64
 9   TAX      506 non-null    float64
 10  PTRATIO  506 non-null    float64
 11  B        506 non-null    float64
 12  LSTAT    506 non-null    float64
 13  PRICE    506 non-null    float64
dtypes: float64(14)
memory usage: 55.5 KB<jupyter_text>결측값은 없으며 모든 피처가 float 형 입니다.<jupyter_code>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)
    col = i%4
    sns.regplot(x=feature, y='PRICE', data=bostonDF, ax=axs[row][col])<jupyter_output><empty_output><jupyter_text>sns.regplot(x,y) => x,y 산점도와 함께 회귀직선을 그려줌.plt.subplots(ncols = , nrows= ) 여러개의 그림을 그릴 수 있게 해줌.RM과 LSTAT 변수가 가장 PRICE 변수와 연관성이 있어보입니다.<jupyter_code>from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

y_target = bostonDF['PRICE']
x_data = bostonDF.drop(['PRICE'], axis = 1, inplace=False)

x_train, x_test, y_train, y_test = train_test_split(x_data, y_target, test_size = 0.3, 
                                                    random_state = 156)

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 :', np.round(mse,4), ', rmse :', np.round(rmse, 4))
print('결정계수:', np.round(r2_score(y_test, y_preds), 4))<jupyter_output>mse : 17.2969 , rmse : 4.159
결정계수: 0.7572<jupyter_text>모델을 어느정도 설명해 준 모습입니다.<jupyter_code>print('절편 값:',lr.intercept_)
print('회귀 계수값:', np.round(lr.coef_,1))
coeff = pd.Series(data=np.round(lr.coef_, 1), index = x_data.columns)
coeff.sort_values(ascending=False)<jupyter_output><empty_output><jupyter_text>변수 이름과 추정 회귀 계수를 맵핑 시킨 모습입니다.NOX 변수의 계수 값이 크게 작아보입니다.<jupyter_code>from sklearn.model_selection import cross_val_score

neg_mse_scores = cross_val_score(lr, x_data, y_target, scoring='neg_mean_squared_error', cv = 5)
rmse_scores = np.sqrt(-1 * neg_mse_scores)
avg_rmse = np.mean(rmse_scores)

print('mse scores', np.round(neg_mse_scores,2))
print('rmse scores', np.round(rmse_scores, 2))
print('평균 rmse score:', np.round(avg_rmse,2))<jupyter_output>mse scores [-12.46 -26.05 -33.07 -80.76 -33.31]
rmse scores [3.53 5.1  5.75 8.99 5.77]
평균 rmse score: 5.83
{'fraction_non_alphanumeric': 0.13375019063596158, 'fraction_numerical': 0.055818209547048955, 'mean_word_length': 3.8541820873427093, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 56, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '376713', 'license': 'Apache-2.0', 'n_tokens_mistral': 3545, 'n_tokens_neox': 3706, 'n_words': 620, 'path': '_notebooks/2021-09-20-PythonMachine5_1.ipynb', 'repo_name': 'KSY1526/myblog', 'size': '6565'}