수요일!
어제 사용한 Ozone data를 Python과 Sklearn으로 Simple Linear Regression(단순 선형 회귀)을 구현했을 때,
왜 모양이 다른지 알아보자~
이유 1. Missing Value(결치값) 처리
- 삭제 : 전체 데이터가 100만 건 이상이며 결치값이 5% 이내일 때
- 대체 : 대표값으로 대체(평균, 중위, 최대, 최소, 최빈) 혹은 머신러닝 기법을 사용(더 좋은 방식! 결치값이 종속변수일 때)
이유 2. 이상치 처리
이상치는 값이 일반적인 다른 데이터에 비해 편차가 큰 데이터이기 때문에 평균, 분산에 큰 영향을 미침
→ 데이터를 상당히 불안하게 만드는 요소
- 지대값 : 독립변수(원인)에 있는 이상치
- Outlier : 종속변수(결과)에 있는 이상치
1. 이상치(Outlier) 판단
- 그래프를 그려서 눈으로 확인 : 단변수인 경우 box plot, 다변수인 경우 scatter
- 계산을 통해 이상치 파악 : 많은 기법이 존재. Turkey Fence, 표준화 방법(Z-Score)
Turkey Fence : box plot으로 IQR(Interquatile Range, 사분위)를 그려서,
IQR value(3Q - 1Q) × 1.5 경계 밖에 있는 것들을 확인

Python 코드로 Turkey Fence를 구해보자
import numpy as np
import matplotlib.pyplot as plt
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 22.1])
figure = plt.figure()
ax1 = figure.add_subplot(1,2,1) # 1행 2열의 첫번째
ax2 = figure.add_subplot(1,2,2) # 1행 2열의 두번째
ax1.set_title('Original Data Boxplot')
ax1.boxplot(data)
# 사분위 값을 구하는 Numpy의 함수. 사분위 범위(IQR)
first = np.percentile(data, 25) # 1사분위(Q1, 25%)
third = np.percentile(data, 75) # 3사분위(Q3, 75%)
IQR_value = third - first
print(third, '-', first, '=', IQR_value)
upper_fence = third + (IQR_value * 1.5)
lower_fence = first - (IQR_value * 1.5)
# boolean indexing을 이용해서 이상치를 추출하자
print(data[(data > upper_fence) | (data < lower_fence)]) # [22.1]. |은 or 연산
# boolean indexing을 이용해서 이상치를 제거하고 나머지 데이터를 추출하자
result = data[(data <= upper_fence) & (data >= lower_fence)]
print(result) # [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.]
ax2.boxplot(result)
ax2.set_title('Turkey Fence')
plt.show()

2. 이상치(Outlier) 판단
표준점수(표준값, Z-Score) = x - 평균 / 표준편차. Turkey Fence보다 통상적으로 사용함
Python 코드로 표준점수(Z-Score)를 구해보자
# 이상치(Outlier)를 판단하는 표준화 방법(Z-Score)
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 22.1])
mean = data.mean() # 8.473333333333333
std = data.std() # 5.331974826980671
zscore_data = (data - mean) / std
print(zscore_data)
# [-1.40160702 -1.21405925 -1.02651147 -0.8389637 -0.65141593 -0.46386816
# -0.27632038 -0.08877261 0.09877516 0.28632293 0.4738707 0.66141848
# 0.84896625 1.03651402 2.55565098]
Scipy 모듈의 stats.zscore 함수로 표준점수(Z-Score)를 구해보자
# scipy는 sklearn과 유사한 통계전용 모듈
from scipy import stats
data = np.array([-10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 22.1])
zscore_threshold = 2.0 # 일반적인 기준
# print(np.abs(stats.zscore(data))) # np.abs로 z-score의 절대값을 구해서 -2 ~ 2 범위 내의 것들을 구함
outlier = data[np.abs(stats.zscore(data)) > zscore_threshold]
# print(outlier) # [-10. 22.1]
data[np.isin(data, outlier, invert=True)] # invert=True 옵션을 주면 True False를 역으로 바꿀 수 있음
# array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13.,
# 14.])
* 계산(Turkey Fence, Z-Score)으로 이상치를 판별할 수 있지만,
그 값이 실제 이상치인지는 판단이 필요함! (실제 값일 수 있음)
3. Python 구현, Sklearn 구현 결과의 차이가 이상치 때문인지 확인해 보자 (결치값 · 이상치 처리 후 예측)
→ 아직도 차이가 크다! 왜? 정규화 때문에! → 결치값 · 이상치 처리, 정규화를 꼭 해야 함!!

Python 구현
# 온도에 따른 Ozone량 예측 -> Python 구현 + 결측치와 이상치 처리도 같이 진행
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
# 다변수 함수의 수치미분 코드
def numerical_derivative(f,x):
delta_x = 1e-4
derivative_x = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'])
while not it.finished:
idx = it.multi_index
tmp = x[idx]
x[idx] = tmp + delta_x
fx_plus_delta = f(x)
x[idx] = tmp - delta_x
fx_minus_delta = f(x)
derivative_x[idx] = (fx_plus_delta - fx_minus_delta) / (2 * delta_x)
x[idx] = tmp
it.iternext()
return derivative_x
# Raw Data Set Loading
df = pd.read_csv('./data/ozone.csv')
training_data = df[['Ozone', 'Temp']]
training_data.dropna(how='any', inplace=True) # 결치가 존재하는 행을 삭제
# 데이터에 이상치가 있는지 확인하고 제거
zscore_threshold = 2.0
outlier = training_data['Ozone'][(np.abs(stats.zscore(training_data['Ozone'])) > zscore_threshold)]
training_data = training_data.loc[np.isin(training_data['Ozone'], outlier, invert=True)]
# Training Data Set
x_data = training_data['Temp'].values.reshape(-1,1)
t_data = training_data['Ozone'].values.reshape(-1,1)
# Weight, bias
W = np.random.rand(1,1)
b = np.random.rand(1)
# loss function
def loss_func(input_data):
W = input_data[0].reshape(1,1)
b = input_data[1]
y = np.dot(x_data, W) + b # np.dot 행렬곱
return np.mean(np.power(t_data-y,2))
# predict
def predict(x):
y = np.dot(x, W) + b
return y
# learning_rate
learning_rate = 1e-4
# 반복 학습
for step in range(300000):
input_param = np.concatenate((W.ravel(), b.ravel()), axis=0)
derivative_result = learning_rate * numerical_derivative(loss_func, input_param)
W = W - derivative_result[0].reshape(1,1)
b = b - derivative_result[1]
if step % 30000 == 0:
input_param = np.concatenate((W.ravel(), b.ravel()), axis=0)
print('W : {}, b : {}, loss : {}'.format(W, b, loss_func(input_param)))
# 학습종료 후 예측 (Temp : 62)
predict_data = predict(np.array([[62]]))
print('온도가 62일 때 오존량 : {}'.format(predict_data)) # [[15.5421609]]
# 그래프로 표현해보자
import matplotlib.pyplot as plt
plt.scatter(x_data.ravel(), t_data.ravel())
plt.plot(x_data.ravel(), x_data.ravel()*W.ravel() + b, color='r')
plt.show()
Sklearn 구현
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import linear_model
import warnings
warnings.filterwarnings('ignore')
# Raw Data Set Loading
df = pd.read_csv('./data/ozone.csv')
# Linear Regression으로 하나의 독립변수만 사용함
training_data = df[['Ozone', 'Temp']]
# 결치가 존재하는 행을 삭제
training_data.dropna(how='any', inplace=True)
# 데이터에 이상치가 있는지 확인하고 제거
zscore_threshold = 2.0
outlier = training_data['Ozone'][(np.abs(stats.zscore(training_data['Ozone'])) > zscore_threshold)]
training_data = training_data.loc[np.isin(training_data['Ozone'], outlier, invert=True)]
# Training Data Set
x_data = training_data['Temp'].values.reshape(-1,1)
t_data = training_data['Ozone'].values.reshape(-1,1)
# model 생성
model = linear_model.LinearRegression()
# model 학습
model.fit(x_data, t_data)
# 예측
result = model.predict(np.array([[62]]))
print('sklearn으로 구한 온도가 62도 일 때의 오존량 : {}'.format(result)) # [[4.51299041]]
# 그래프 표현
plt.scatter(x_data.ravel(), t_data.ravel())
plt.plot(x_data.ravel(),
x_data.ravel()*model.coef_.ravel() + model.intercept_, color='g')
plt.show()
4. 정규화(Normalization) : 데이터가 가진 feature들의 scale을 맞춰주는 작업. Sklearn은 자체(내부)적으로 정규화 진행
- 표준화(Standardization) : 정규분포. Z-score. -2 ~ 2 사이의 값. 양수, 음수가 있고 이상치에 영향을 덜 받음

- Min-Max Scaling : Xscaled로 숫자 데이터를 최소 0 ~ 최대 1 사이로 만듦. 이상치가 존재하면 정규화 효과가 떨어짐

정규화 작업(Min-Max Scaling) - Python
import pandas as pd
import seaborn as sns
titanic = sns.load_dataset('titanic')
df = titanic[['age', 'fare']]
display(df.head())
def min_max(s):
return (s - s.min()) / (s.max() - s.min())
result = df.apply(min_max, axis=0)
display(result.head())

5. 정규화 작업(Min-Max Scaling) - Sklearn의 MinMaxScaler
Python 구현 + 결치값 · 이상치 처리 + 정규화
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import linear_model # LinearRegression 모델
from scipy import stats # 이상치 처리
from sklearn.preprocessing import MinMaxScaler # 정규화 처리
import warnings
warnings.filterwarnings('ignore')
# figure - plt.figure() # python과 sklearn으로 구현한 그래프를 한번에 비교하자
# python_ax = figure.add_subplot(1,2,1)
# sklearn_ax = figure.add_subplot(1,2,2)
# Raw Data Set Loading
df = pd.read_csv('./data/ozone.csv')
# Linear Regression으로 하나의 독립변수만 사용함
training_data = df[['Ozone', 'Temp']]
# 결치가 존재하는 행을 삭제
training_data.dropna(how='any', inplace=True)
# 데이터에 이상치가 있는지 확인하고 제거
zscore_threshold = 2.0
outlier = training_data['Ozone'][(np.abs(stats.zscore(training_data['Ozone'])) > zscore_threshold)]
training_data = training_data.loc[np.isin(training_data['Ozone'], outlier, invert=True)]
# 정규화 처리
scaler_x = MinMaxScaler() # 온도 처리를 위한 scaler(독립변수를 위한)
scaler_t = MinMaxScaler() # 오존량 처리를 위한 scaler(종속변수를 위한)
scaler_x.fit(training_data['Temp'].values.reshape(-1,1)) # 2차원으로
scaler_t.fit(training_data['Ozone'].values.reshape(-1,1))
scaled_Temp = scaler_x.transform(training_data['Temp'].values.reshape(-1,1))
scaled_Ozone = scaler_t.transform(training_data['Ozone'].values.reshape(-1,1))
training_data['Temp'] = scaled_Temp
training_data['Ozone'] = scaled_Ozone
display(training_data.head())
# Training Data Set
p_x_data = training_data['Temp'].values.reshape(-1,1)
p_t_data = training_data['Ozone'].values.reshape(-1,1)
# Weight, bias
W = np.random.rand(1,1)
b = np.random.rand(1)
# loss function
def loss_func(input_data):
W = input_data[0].reshape(1,1)
b = input_data[1]
y = np.dot(p_x_data, W) + b
return np.mean(np.power(p_t_data-y,2))
# predict
def predict(x):
y = np.dot(x, W) + b
return y
# learning_rate
learning_rate = 1e-4
# 반복 학습
for step in range(300000):
input_param = np.concatenate((W.ravel(), b.ravel()), axis=0)
derivative_result = learning_rate * numerical_derivative(loss_func, input_param)
W = W - derivative_result[0].reshape(1,1)
b = b - derivative_result[1]
if step % 30000 == 0:
input_param = np.concatenate((W.ravel(), b.ravel()), axis=0)
print('W : {}, b : {}, loss : {}'.format(W, b, loss_func(input_param)))
# 학습종료 후 예측 (Temp : 62) -> [[53.03797526]]?!?! 독립변수도 정규화 해줘야 한다!
predict_data = np.array([[62]])
scaled_predict_data = scaler_x.transform(predict_data) # 입력값 또한 정규화
python_result = predict(scaled_predict_data)
python_result = scaler_t.inverse_transform(python_result) # 정규화 이전의 값으로 복원해야 함
print(python_result) # [[5.74013754]]

6. Sklearn 구현 + 결치값 · 이상치 처리 + 정규화
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import linear_model
import warnings
warnings.filterwarnings('ignore')
# Raw Data Set Loading
df = pd.read_csv('./data/ozone.csv')
# Linear Regression으로 하나의 독립변수만 사용함
training_data = df[['Ozone', 'Temp']]
# 결치가 존재하는 행을 삭제
training_data.dropna(how='any', inplace=True)
# 데이터에 이상치가 있는지 확인하고 제거
zscore_threshold = 2.0
outlier = training_data['Ozone'][(np.abs(stats.zscore(training_data['Ozone'])) > zscore_threshold)]
training_data = training_data.loc[np.isin(training_data['Ozone'], outlier, invert=True)]
# 정규화 처리는 sklearn이 알아서 진행
# Training Data Set
s_x_data = training_data['Temp'].values.reshape(-1,1)
s_t_data = training_data['Ozone'].values.reshape(-1,1)
# model 생성
model = linear_model.LinearRegression()
# model 학습
model.fit(s_x_data, s_t_data)
# 예측
result = model.predict(np.array([[62]]))
print('sklearn으로 구한 온도가 62도 일 때의 오존량 : {}'.format(result)) # [[4.51299041]]
# python과 sklearn으로 구현한 그래프를 한번에 비교하자
import matplotlib.pyplot as plt
figure = plt.figure(figsize=(15,5))
python_ax = figure.add_subplot(1,2,1)
sklearn_ax = figure.add_subplot(1,2,2)
python_ax.set_title('python_ax')
python_ax.scatter(p_x_data.ravel(), p_t_data.ravel())
python_ax.plot(p_x_data.ravel(), p_x_data.ravel()*W.ravel() + b, color='r')
sklearn_ax.set_title('sklearn_ax')
sklearn_ax.scatter(s_x_data.ravel(), s_t_data.ravel())
sklearn_ax.plot(s_x_data.ravel(), s_x_data.ravel()*model.coef_.ravel() + model.intercept_, color='g')
plt.show()

여기까지가 독립변수가 1개인 단순선형회귀(Simple Linear Regression)!
이제 독립변수(multi-variable)가 여러 개인 다중선형회귀(Multiple Linear Regression)로 들어간다~
독립변수가 늘어나면, W(가중치)도 여러 개가 됨
행렬곱 연산(데이터의 열 × W의 행)이 된 결과가 나옮! 각각의 W와 b에 대한 편미분을 해야 함

# 메모리 초기화 (이전의 함수와 변수들을 메모리에서 지우는 매직 펑션)
%reset
1. Python으로 다중선형회귀(Multiple Linear Regression) 구현
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import linear_model # LinearRegression 모델
from scipy import stats # 이상치 처리
from sklearn.preprocessing import MinMaxScaler # 정규화 처리
import warnings
warnings.filterwarnings('ignore')
# 다변수 함수의 수치미분 코드
def numerical_derivative(f,x):
delta_x = 1e-4
derivative_x = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'])
while not it.finished:
idx = it.multi_index
tmp = x[idx]
x[idx] = tmp + delta_x
fx_plus_delta = f(x)
x[idx] = tmp - delta_x
fx_minus_delta = f(x)
derivative_x[idx] = (fx_plus_delta - fx_minus_delta) / (2 * delta_x)
x[idx] = tmp
it.iternext()
return derivative_x
# Raw Data Loading. NaN, Outlier 존재하지 않음. 각 변수의 scale이 비슷하기 때문에 정규화가 굳이 필요 없음
df = pd.read_csv('./data/student_exam_score.csv')
# display(df)
# Training Data Set
x_data = df.drop('exam', axis=1, inplace=False) # 독립변수들
t_data = df.iloc[:,-1].values.reshape(-1,1) # 종속변수
# Weight, bias
W = np.random.rand(3,1) # 행렬곱연산(데이터의 열 × W의 행)이 일어날 수 있도록 shape을 맞춰주기
b = np.random.rand(1)
# loss function
def loss_func(input_data): # [w1 w2 w3 b]
input_w = input_data[:-1].reshape(-1,1) # 행은 알아서, 열은 1로 만들어줘! (3,1)
input_b = input_data[-1:]
y = np.dot(x_data, input_w) + input_b
return np.mean(np.power(t_data-y,2)) # MSE(평균제곱오차)
# predict
def predict(x):
y = np.dot(x, W) + b
return y
# learning_rate
learning_rate = 1e-5
# 반복 학습
for step in range(300000):
input_param = np.concatenate((W.ravel(), b.ravel()), axis=0) # [w1 w2 w3 b]
derivative_result = learning_rate * numerical_derivative(loss_func, input_param)
W = W - derivative_result[:-1].reshape(-1,1) # (3,1)
b = b - derivative_result[-1]
if step % 30000 == 0:
input_param = np.concatenate((W.ravel(), b.ravel()), axis=0)
print('W : {}, b : {}, loss : {}'.format(W, b, loss_func(input_param)))
# 학습종료 후 예측
result = predict(np.array([[89, 100, 95]]))
print(result) # [[191.84931203]]
2. Sklearn으로 다중선형회귀(Multiple Linear Regression) 구현
from sklearn import linear_model
model = linear_model.LinearRegression() # model 생성
model.fit(x_data, t_data) # model 학습
result_sklearn = model.predict(np.array([[89, 100, 95]])) # 예측
print(result_sklearn) # [[192.50147537]]
1. 3/15 화 | 2. 3/16 수 | 3. 3/17 목 | 4. 3/18 금 | 5. 3/21 월 |
ML 환경 설정 (아나콘다, 주피터 노트북), Pandas, Numpy, ndarray |
ML Numpy, 행렬곱연산, 전치행렬, iterator, axis, Pandas, Series, DataFrame |
ML Pandas, DataFrame, 함수들 |
ML Pandas, 데이터 분석, 데이터 전처리 |
ML Pandas, 데이터 전처리, 수행평가 |
6. 3/22 화 | 7. 3/23 수 | 8. 3/24 목 | 9. 3/25 금 | 10. 3/28 월 |
ML 데이터 시각화, Matplotlib, 기술통계 |
ML 기술통계 |
ML 기술통계, 머신러닝 |
ML 미분, Regression |
ML 머신러닝 기법들, Regression |
11. 3/29 화 | 12. 3/30 수 | 13. 3/31 목 | 14. 4/1 금 | 15. 4/4 월 |
ML Regression 구현 (Python, Scikit-Learn) |
ML Outlier, Z-score, MinMaxScaler, 다변수 |
ML Tensorflow |
ML Logistic |
* 이상치 판별은 Turkey Fence(IQR를 활용한 코드), Z-Score(scipy 모듈의 stats.zscore와 코드)!
정규화는 Z-score, Min-Max Scaling(Sklearn의 MinMaxScaler와 코드)!