본문 바로가기

멀티캠퍼스 프로젝트형 AI 서비스 개발 5회차/ML

3/30 수

728x90

수요일!

 

어제 사용한 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 구현 vs.&nbsp;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와 코드)!

728x90

'멀티캠퍼스 프로젝트형 AI 서비스 개발 5회차 > ML' 카테고리의 다른 글

4/1 금  (0) 2022.04.01
3/31 목  (0) 2022.03.31
3/29 화  (0) 2022.03.29
3/28 월  (0) 2022.03.28
3/25 금  (0) 2022.03.27