Gradient Descent Implementation¶
문제 1¶
Real Estate 데이터셋 Housing.csv으로 Gradient Descent를 구현하세요.
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.preprocessing import StandardScaler
%matplotlib inline
다음의 코드들을 실행하여 housing 변수에 데이터를 로드하고 데이터 전처리를 수행합니다.
# 데이터셋을 불러옵니다
import pandas as pd
housing = pd.read_csv("./Housing.csv")
housing.head()
price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | furnishingstatus | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 13300000 | 7420 | 4 | 2 | 3 | yes | no | no | no | yes | 2 | yes | furnished |
1 | 12250000 | 8960 | 4 | 4 | 4 | yes | no | no | no | yes | 3 | no | furnished |
2 | 12250000 | 9960 | 3 | 2 | 2 | yes | no | yes | no | no | 2 | yes | semi-furnished |
3 | 12215000 | 7500 | 4 | 2 | 2 | yes | no | yes | no | yes | 3 | yes | furnished |
4 | 11410000 | 7420 | 4 | 1 | 2 | yes | yes | yes | no | yes | 2 | no | furnished |
# Yes를 1로, No를 0으로 변환
housing["mainroad"] = housing["mainroad"].map({"yes": 1, "no": 0})
housing["guestroom"] = housing["guestroom"].map({"yes": 1, "no": 0})
housing["basement"] = housing["basement"].map({"yes": 1, "no": 0})
housing["hotwaterheating"] = housing["hotwaterheating"].map({"yes": 1, "no": 0})
housing["airconditioning"] = housing["airconditioning"].map({"yes": 1, "no": 0})
housing["prefarea"] = housing["prefarea"].map({"yes": 1, "no": 0})
1-1 (7점) - 전처리¶
'furnishingstatus' 칼럼을 pd.get_dummies()를 이용하여 binary DataFrame으로 변환해주세요.(3점) </br> </br> 그 후 drop_first 파라미터를 이용하여 'furnished' 칼럼을 제거 후, concat을 이용하여 변환한 binary DataFrame과 기존의 housing DataFrame을 결합해주세요. (2점) </br> </br> 결합 후, 전체 DataFrame의 'furnishingstatus' 칼럼을 제거해주세요. (2점)
status = pd.get_dummies(housing["furnishingstatus"], drop_first=True)
housing = pd.concat([housing, status], axis=1)
housing.drop(["furnishingstatus"], axis=1, inplace=True)
housing.head()
price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | semi-furnished | unfurnished | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 13300000 | 7420 | 4 | 2 | 3 | 1 | 0 | 0 | 0 | 1 | 2 | 1 | False | False |
1 | 12250000 | 8960 | 4 | 4 | 4 | 1 | 0 | 0 | 0 | 1 | 3 | 0 | False | False |
2 | 12250000 | 9960 | 3 | 2 | 2 | 1 | 0 | 1 | 0 | 0 | 2 | 1 | True | False |
3 | 12215000 | 7500 | 4 | 2 | 2 | 1 | 0 | 1 | 0 | 1 | 3 | 1 | False | False |
4 | 11410000 | 7420 | 4 | 1 | 2 | 1 | 1 | 1 | 0 | 1 | 2 | 0 | False | False |
1-2 (5점) - 전처리¶
두 방법의 결과가 반드시 같아야 합니다!!
StandardScalar를 이용하여 housing DataFrame전체를 standardization 수행해주세요 .(2점) </br> </br> 동일한 과정을 수식을 이용하여 직접 구현해주세요. (2점) </br>
- (힌트: 표본표준편차가 아닌 모표준편차로 계산하도록 파라미터를 조정하세요)
- $Z = \frac {(X - m)} {\sigma} $
두 방법이 같게 나오는지 첫 5행만 확인해보세요. (1점)
# StandardScalar를 사용해주세요.
scalar = StandardScaler()
scaled_housing = scalar.fit_transform(housing)
housing = pd.DataFrame(scaled_housing, columns=housing.columns)
# 첫 5개의 행만 확인해주세요.
housing.head()
price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | semi-furnished | unfurnished | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 4.566365 | 1.046726 | 1.403419 | 1.421812 | 1.378217 | 0.405623 | -0.465315 | -0.734539 | -0.219265 | 1.472618 | 1.517692 | 1.804941 | -0.844888 | -0.696429 |
1 | 4.004484 | 1.757010 | 1.403419 | 5.405809 | 2.532024 | 0.405623 | -0.465315 | -0.734539 | -0.219265 | 1.472618 | 2.679409 | -0.554035 | -0.844888 | -0.696429 |
2 | 4.004484 | 2.218232 | 0.047278 | 1.421812 | 0.224410 | 0.405623 | -0.465315 | 1.361397 | -0.219265 | -0.679063 | 1.517692 | 1.804941 | 1.183588 | -0.696429 |
3 | 3.985755 | 1.083624 | 1.403419 | 1.421812 | 0.224410 | 0.405623 | -0.465315 | 1.361397 | -0.219265 | 1.472618 | 2.679409 | 1.804941 | -0.844888 | -0.696429 |
4 | 3.554979 | 1.046726 | 1.403419 | -0.570187 | 0.224410 | 0.405623 | 2.149083 | 1.361397 | -0.219265 | 1.472618 | 1.517692 | -0.554035 | -0.844888 | -0.696429 |
# 수식으로 직접 구현해주세요.
housing = (housing - housing.mean()) / housing.std(ddof=0)
# 첫 5개의 행만 확인해주세요.
housing.head()
price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | semi-furnished | unfurnished | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 4.566365 | 1.046726 | 1.403419 | 1.421812 | 1.378217 | 0.405623 | -0.465315 | -0.734539 | -0.219265 | 1.472618 | 1.517692 | 1.804941 | -0.844888 | -0.696429 |
1 | 4.004484 | 1.757010 | 1.403419 | 5.405809 | 2.532024 | 0.405623 | -0.465315 | -0.734539 | -0.219265 | 1.472618 | 2.679409 | -0.554035 | -0.844888 | -0.696429 |
2 | 4.004484 | 2.218232 | 0.047278 | 1.421812 | 0.224410 | 0.405623 | -0.465315 | 1.361397 | -0.219265 | -0.679063 | 1.517692 | 1.804941 | 1.183588 | -0.696429 |
3 | 3.985755 | 1.083624 | 1.403419 | 1.421812 | 0.224410 | 0.405623 | -0.465315 | 1.361397 | -0.219265 | 1.472618 | 2.679409 | 1.804941 | -0.844888 | -0.696429 |
4 | 3.554979 | 1.046726 | 1.403419 | -0.570187 | 0.224410 | 0.405623 | 2.149083 | 1.361397 | -0.219265 | 1.472618 | 1.517692 | -0.554035 | -0.844888 | -0.696429 |
1-3 (15점) - Simple linear Regression¶
featue 데이터 세트 X에 'area' 칼럼을, target값 y에 'price'칼럼을 assign해주세요.(1점) </br> </br> pairplot을 사용하여 다음 그림과 같이 나타내주세요. (size=7, aspect=0.7로 지정해주세요) (2점) </br> </br>
# Assign X
X = housing["area"]
# Assign y
y = housing["price"]
# pairplot을 이용하여 그림과 동일하게 시각화해주세요.
sns.pairplot(housing, x_vars="area", y_vars="price", size=7, aspect=0.7, kind="scatter")
/Users/eric/anaconda3/lib/python3.11/site-packages/seaborn/axisgrid.py:2095: UserWarning: The `size` parameter has been renamed to `height`; please update your code. warnings.warn(msg, UserWarning) /Users/eric/anaconda3/lib/python3.11/site-packages/seaborn/axisgrid.py:118: UserWarning: The figure layout has changed to tight self._figure.tight_layout(*args, **kwargs)
<seaborn.axisgrid.PairGrid at 0x16a3e8d10>
# X, y를 각각 2차원 ndarray로 변환해줍니다.
import numpy as np
X = np.array(X).reshape(-1, 1)
y = np.array(y).reshape(-1, 1)
X.shape, y.shape
((545, 1), (545, 1))
예측 오차 비용 계산을 수행하는 함수 get_cost를 생성해주세요. (1점)
# 예측 오차 비용 계산을 수행하는 함수 생성
def get_cost(y, y_pred):
N = len(y)
cost = np.sum(np.square(y - y_pred)) / N
return cost
각 iteration 마다 w1, w0를 업데이트하고 cost를 계산할 get_weight_updates 함수를 생성해주세요. (3점)
# w1 과 w0 를 업데이트 할 w1_update, w0_update를 반환.
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)
# 예측 배열 계산하고 예측과 실제 값의 차이 계산
y_pred = np.dot(X, w1.T) + w0
diff = y - y_pred
# w0_update를 dot 행렬 연산으로 구하기 위해 모두 1값을 가진 행렬 생성
w0_factor = 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_factor.T, diff))
# get_cost function을 이용하여 cost 변수에 예측 오차 비용을 assign
cost = get_cost(y, y_pred)
return w1_update, w0_update, cost
각 iteration별 w1, w0, cost를 기록할 gd_df DataFrame을 반환하는 함수 gradient_descent_steps를 생성해주세요. (4점)
# 입력 인자 iters로 주어진 횟수만큼 반복적으로 w1과 w0를 업데이트 적용함.
def gradient_descent_steps(X, y, iters=1000):
# w0와 w1을 모두 0으로 초기화.
w0 = np.zeros((1, 1))
w1 = np.zeros((1, 1))
# 'w1', 'w0', 'cost'를 column으로 가지는 새로운 DataFrame gd_df 생성.
gd_df = pd.DataFrame(columns=["w1", "w0", "cost"])
# 인자로 주어진 iters 만큼 반복적으로 get_weight_updates() 호출하여 w1, w0 업데이트 수행하고 cost 반환
for ind in range(iters):
w1_update, w0_update, cost = get_weight_updates(
w1, w0, X, y, learning_rate=0.01
)
w1 -= w1_update
w0 -= w0_update
# gd_df에 각 iteration별 'w1', 'w0', 'cost'를 기재
gd_df.loc[ind] = [w1[0, 0], w0[0, 0], cost]
# gd_df를 반환
return gd_df
gradient_descent_steps의 반환값을 이용하여 gd_df에 assign 해주세요.
gd_df = gradient_descent_steps(X, y)
gd_df의 상위 5개 행의 정보가 다음과 일치하는지 확인하세요. (2점)
from IPython.display import Image
Image("simple_linear_regression.png")
gd_df.head()
w1 | w0 | cost | |
---|---|---|---|
0 | 0.010720 | 1.100037e-18 | 1.000000 |
1 | 0.021225 | -1.279303e-18 | 0.988623 |
2 | 0.031521 | 2.966027e-18 | 0.977697 |
3 | 0.041610 | 1.906732e-18 | 0.967203 |
4 | 0.051498 | -1.874138e-19 | 0.957125 |
gd_df를 이용하여 다음의 그래프를 그려주세요. (x축: 'index' y축: 'cost', 힌트: reset_index()를 사용하세요) (2점)
# plotting cost against num_iterations
gd_df.reset_index().plot.line(x="index", y=["cost"])
<Axes: xlabel='index'>
1-4 (23점) - Linear Regression w/ Multiple Features¶
featue 데이터 세트 X에 'area', 'bedroom' 칼럼을, target값 y에 'price'칼럼을 assign해주세요.(1점) </br> </br> 값이 1만으로 되어있는 X에 'intercept' 칼럼을 추가해주시고, reindex를 이용하여 X의 칼럼 순서를 "intercept", "area", "bedrooms"으로 바꿔주세요 (1점) </br> </br>
# Assign X
X = housing[["area", "bedrooms"]]
# Assign y
y = housing["price"]
# columns of 1s을 X의 intercept 칼럼으로 추가해주세요.
X["intercept"] = 1
# reindex를 사용하여 X의 칼럼 순서를 바꿔주세요.
X = X.reindex(["intercept", "area", "bedrooms"], axis=1)
X.head()
/var/folders/xx/t2g6ggws01d3pfwzk8m969kr0000gn/T/ipykernel_76635/2832820216.py:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy X["intercept"] = 1
intercept | area | bedrooms | |
---|---|---|---|
0 | 1 | 1.046726 | 1.403419 |
1 | 1 | 1.757010 | 1.403419 |
2 | 1 | 2.218232 | 0.047278 |
3 | 1 | 1.083624 | 1.403419 |
4 | 1 | 1.046726 | 1.403419 |
# X, y를 2차원 ndarray로 변환
import numpy as np
X = np.array(X)
y = np.array(y).reshape(-1, 1)
예측 오차 비용 계산을 수행하는 함수 get_cost를 생성해주세요. (1점)
# 예측 오차 비용 계산을 수행하는 함수 생성
def get_cost(y, y_pred):
N = len(y)
cost = np.sum(np.square(y - y_pred)) / N
return cost
(참고사항)
각 iteration 마다 w를 업데이트하고 cost를 계산할 get_weight_updates 함수를 생성해주세요. (5점)
# w를 업데이트 할 w_update를 반환.
def get_weight_updates(w, X, y, learning_rate=0.01):
N = len(y)
# 먼저 w_update를 w의 shape와 동일한 크기를 가진 0 값으로 초기화
w_update = np.zeros_like(w)
# 예측 배열 계산하고 예측과 실제 값의 차이 계산
y_pred = np.dot(X, w)
diff = y - y_pred
# w을 업데이트할 w_update 계산
w_update = -(1 / N) * (learning_rate) * (np.dot(X.T, diff))
# get_cost function을 이용하여 cost 변수에 예측 오차 비용을 assign
cost = get_cost(y, y_pred)
return w_update, cost
각 iteration별 w, cost를 기록할 gd_df DataFrame을 반환하는 함수 gradient_descent_steps를 생성해주세요. (7점)
gd_df = pd.DataFrame(columns=["w", "cost"])
for ind in range(10):
gd_df.loc[ind] = [ind, ind+1]
gd_df
w | cost | |
---|---|---|
0 | 0 | 1 |
1 | 1 | 2 |
2 | 2 | 3 |
3 | 3 | 4 |
4 | 4 | 5 |
5 | 5 | 6 |
6 | 6 | 7 |
7 | 7 | 8 |
8 | 8 | 9 |
9 | 9 | 10 |
# 입력 인자 iters로 주어진 횟수만큼 반복적으로 w를 업데이트 적용함.
def gradient_descent_steps(X, y, iters=1000):
# w을 모두 0으로 초기화.
w = np.zeros((3, 1))
# 'w', 'cost'를 column으로 가지는 새로운 DataFrame gd_df 생성.
gd_df = pd.DataFrame(columns=["w", "cost"])
# 인자로 주어진 iters 만큼 반복적으로 get_weight_updates() 호출하여 w 업데이트 수행하고 cost 반환
for ind in range(iters):
w_update, cost = get_weight_updates(w, X, y, learning_rate=0.01)
w -= w_update
# gd_df에 각 iteration별 'w', 'cost'를 기재
gd_df.loc[ind] = [w.flatten(), cost]
# gd_df를 반환
return gd_df
pd.set_option('display.max_colwidth', 100)
gd_df의 상위 5개 행의 정보가 다음과 일치하는지 확인하세요. (6점)
from IPython.display import Image
Image("Multiple_Feature_linear_regression.png")
gd_df = gradient_descent_steps(X, y)
gd_df.head()
w | cost | |
---|---|---|
0 | [1.7519115617938253e-19, 0.005359973457780798, 0.003664940257738677] | 1.000000 |
1 | [-9.370689749129762e-20, 0.010660781658211174, 0.007285091538379784] | 0.991616 |
2 | [-1.8333958204819102e-19, 0.015903084269726957, 0.010860991579158095] | 0.983422 |
3 | [1.4667166563855279e-19, 0.021087533547479997, 0.01439317173817354] | 0.975414 |
4 | [5.785382366854027e-19, 0.026214774417158263, 0.017882157069440255] | 0.967588 |
gd_df를 이용하여 다음의 그래프를 그려주세요. (x축: 'index' y축: 'cost', 힌트: reset_index()를 사용하세요) (2점)
# print cost
gradient_descent_steps(X, y).reset_index().plot.line(
x="index", y=["cost"]
)
<Axes: xlabel='index'>