#!/usr/bin/env python3

Neural Network(Deep Learning)

신경망이라 알려진 algorithm은 최근 deep learning이란 이름으로 불림
간단하게 Classifier와 Regression에 쓸 수 있는 multilayer perceptrons, MLP
multilayer perceptrons은 feed-forward, 또는 신경망이라고도 함

여러개의 가중치 합을 계산하는 것은 수학적으로 보면 하나의 가중치 합을 계산하는 것과 같음
각 은닉 유닛의 가중치 합을 계산한 후 그 결과에 비선형 함수인 rectified linear unit, ReLU나
hyperbolic tangent, tanh를 적용, 그 결과 가중치(𝘸)를 계산하여 ŷ 를 만듬

ReLu : 0이하를 잘라버리고,
tanh: x가 -∞일수록 -1, x가 ∞일수록 +1에 수렴

비선형 함수를 이용해 신경망이 선형 모델에서보다 훨씬 더 복잡한 함수를 학습 가능

1. ReLu함수와 tanh함수 살펴보기
# library import
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

# matplotlib 설정
matplotlib.rc('font', family='AppleGothic'# 한글출력
plt.rcParams['axes.unicode_minus'] = False # 축 -설정

line = np.linspace(-3, 3, num=100) # 시작, 끝, 갯수
plt.plot( #  plot함수 호출
    line, np.tanh(line), linestyle='--',label='tanh' # x, y, style, label
    )
plt.plot(
    line, np.maximum(line, 0), label='relu'
    )
plt.xlabel('x'# x축 이름
plt.ylabel('tanh(x), relu(x)'# y축 이름
plt.legend(loc=2) # 범례위치 왼쪽 위
plt.show() # 그래프 출력

tanh(x), relu(x) 함수


2. Two moons Machine Learning(MLP, Multilayer perceptrons)
library load 
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
import mglearn

# data load
x, y = make_moons( # moon data set 만들기
    n_samples=100, noise=0.25, random_state=3 # 갯수, 퍼짐정도, 랜덤상태
)

# data 분할
x_train, x_test, y_train, y_test = \
  train_test_split( # 데이터분할을 위해
      x, y, # 분할할 데이터
      stratify=y, random_state=42 # 그룹(binary 데이터로된 경우), 랜덤상태
      )

# model 생성 및 학습
mlp= MLPClassifier( # model 호출
    solver='lbfgs'# algorithm 
    random_state=42, # 랜덤 상태
    activation='relu', # 적용 함수
    hidden_layer_sizes=[100] # 은닉충 수 ex) [10 ,10] 유닛 10개짜리 은닉충 2개
    )
mlp.fit(x_train, y_train) # model 학습

# visualization
mglearn.plots.plot_2d_separator( # 2차원 데이터셋 분할 평면 그리기
    mlp, x_train, fill=True, alpha=0.3 # model 객체, train 데이터, 평면 칠하기, 투명도
    )
mglearn.discrete_scatter( # 2차원 산점도 그리기 위해
    x_train[:, 0], x_train[:, 1], y_train # x, y, 그룹
    )
plt.xlabel('feature 0'# x축 이름
plt.ylabel('feature 1'#  y축 이름
plt.show() # 그래프 출력

은닉 유닛이 100개인 neural로 학습한 two_moons 데이터셋의 결정 경계


neural_network는 매우 비 선형적이지만 비교적 매끄러운 decision boundary

3. Regularization 과 Visualization
Ridge Regression과 Linear Classifier에서 처럼 L2 penalty(계수를 0에 근접)로 alpha로 모델의 복잡도를 제어
기본값은 0.0001

# visualization
fig, axes = plt.subplots(2, 4) # figure객체를 fig에, plots객체를 axes에 2X4만큼 할당
n_hidden_nodes = [10, 100] # 배열생성
alpha_set = [0.0001, 0.01, 0.1, 1] # 배열 생성
for axe, n_node in zip(axes, n_hidden_nodes): # axes(2X4를 1X4, 1X4)와 n_hidden_nodes를 하나씩 axe와 n_node에 할당
    for ax, alpha in zip(axe, alpha_set): # axe(1X4)와 alpha_set을 하나씩 ax와 alpha에 할당
        mlp = MLPClassifier( # model 생성을 위해
            solver='lbfgs'# algorithm
            random_state=42, # 랜덤 상태
            hidden_layer_sizes=[n_node, n_node], # 은닉충
            alpha=alpha # 규제정도(클수록 강화)
            )
        mlp.fit(x_train, y_train) # 학습
        mglearn.plots.plot_2d_separator(# 2차원 평면을 나누기 위해
            mlp, x_train, # model 객체, train데이터
            fill=True, alpha=0.3, ax=ax # 평면칠하기, 투명도, plot객체
            )
        mglearn.discrete_scatter( # 2차원 산점도 그래프를 위해
            x_train[:, 0], x_train[:, 1], y_train, ax=ax # x, y, 그룹, plot객체
            )
        ax.set_title('n_hidden=[{}, {}]\nalpha={}'.format( # 타이틀
            n_node, n_node, alpha
            ))
plt.show()

은닉 유닛과 alpha에 따라 변하는 decision boundary


4. Breast Cancer Dataset Machine Learning(MLPClassifier)
# load library
from sklearn.datasets import load_breast_cancer
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import matplotlib

# matplotlib 설정
matplotlib.rc('font', family='AppleGothic'# 한글 설정
plt.rcParams['axes.unicode_minus'] = False # -표시

# data load
cancer = load_breast_cancer()

# data  분할
x_train, x_test, y_train, y_test = \
  train_test_split( # 데이터 분할을 위해
      cancer.data, cancer.target, # 분할할 데이터
      random_state=0, test_size=0.3 # 랜덤상태, 테스트 비율
      )

# feature visualization
plt.boxplot(x_train, manage_xticks=False) # 데이터, 소눈금 표시 안하기
plt.yscale('symlog') # 축 스케일을 log 로
plt.xlabel('feature list'# x축 이름
plt.ylabel('feature'# y축 이름
plt.show() # 그래프 출력

breast cancer 데이터셋의 특성 값 범위(y축은 logscale)


# 전처리, pre-processing
# axis =0 ==> 열, axis =1 ==> 행
train_min = x_train.min(axis=0) # 열방향 최솟값
train_range = (x_train - train_min).max(axis=0) # 최솟값을 제거한 후 최댓값

x_train_scaled = (x_train-train_min)/train_range # train data 조정
x_test_scaled = (x_test-train_min)/train_range # test data 조정(train 데이터 범위로 조정)

x_train_scaled.min(axis=0) # 조정된 범위의 최솟값, 0
x_train_scaled.max(axis=0) # 조정된 범위의 최댓값, 1

print('x_train_scaled min \n{}'.format(x_train_scaled.min(axis=0))) # 0
print('x_train_scaled.max \n{}'.format(x_train_scaled.max(axis=0))) # 1

# model  생성 및 학습
mlp = MLPClassifier( # model 생성
    solver='lbfgs', # algorithm
    random_state=0, # 랜덤상태
    hidden_layer_sizes=[100], # 은닉층 수
    alpha=0.001 # 규제 정도
    )
mlp.fit(x_train_scaled, y_train) # 학습

mlp.score(x_train_scaled, y_train) # train set 정확도
mlp.score(x_test_scaled, y_test) # 일반화 정확도

print('train set scaled accuracy \n{:.3f}'.format(mlp.score(x_train_scaled, y_train))) # 1.000
print('test set sclaed accuracy \n{:.3f}'.format(mlp.score(x_test_scaled, y_test))) # 0.965

5. Breast Cancer 데이터셋으로 학습된 가중치 확인
행 : 30개의 입력특성
열 : 100개의 은닉 유닛
밝은 색은 큰 양수 값

plt.figure(figsize=(20, 5)) # 그림 size
plt.imshow(
    mlp.coefs_[0], interpolation='none', cmap='viridis' # 입력과 은닉층 사이의 가중치, 축, 그림 테마
    )
plt.yticks(range(30), cancer.feature_names) # y축 포인트, label
plt.xlabel('은닉 유닛') # x축 이름
plt.ylabel('입력 특성') # y축 이름
plt.colorbar() # colorbar 생성
plt.show() # 그래프 출력

breast cancer 데이터셋으로 학습시킨 신경망의 첫번째 층의 가중치 히트맵


mlp.coefs_[0]은 입력과 은닉충 사이의 가중치가 저장되어있는 (30, 100) NumPy배열이고
mlp.coefs_[1]은 은닉충과 출력사이의 가중치가 저장되어있는 (100, 1) 크기의 NumPy배열



'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Kernelized Support Vector Machines  (0) 2018.03.15
Gradient Boosting Model  (0) 2018.03.15
Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14

/usr/bin/env python3


Kernelized Support Vector Machine


데이터가 단순한 초평면으로 구분되지 않는 더 복잡한 model을 만들 수 있도록 확장한 것이

Kernelized Support Vector Machine입니다.


선형적으로 구분되지 않는 클래스를 가진 binary data 생성하여 시각화해보겠습니다.

from mglearn.datasets import make_blobs

from sklearn.svm import LinearSVC

from mglearn.plots import plot_2d_separator

import matplotlib.pyplot as plt

import numpy as np


x, y = make_blobs(centers=4, random_state=8)

y = y % 2 # y를 binary화


linear_svm = LinearSVC().fit(x, y) 


plot_2d_separator(linear_svm, x, fill=False)


marker_set = ['o', '^']

for i, m in zip(np.unique(y), marker_set):

    plt.scatter(x[y==i][:, 0], x[y==i][:, 1], marker=m,

                label='class {}'.format(i), edgecolors='k')

plt.legend(bbox_to_anchor=(0.1, 1.1), fancybox=True)

plt.xlabel('feature 0', size=15)

plt.ylabel('feature 1', size=15)

plt.show()

선형적으로 구분되지 않는 클래스를 가진 binary data

이런 경우에는 Kernel Support Vector Machine를 사용할 수 있습니다.

support vector machine에서 데이터를 고차원 공간에 매핑하는 방법은 크게 2가지가 있으며


1. 원래 특성의 가능한 조합을 모두 계산((특성1)^2, (특성2)^5) 하거나

2. (radial basis function), RBF커널-- 가우시안 커널을 사용하는 것입니다.

RBF 커널의 경우 특성의 중요도는 테일러급수를 이용하기때문에 고차원일수록 줄어듭니다.



1번 방법으로 고차원 공간에 매핑하겠습니다.

# 선 하나로 구분되지 않는 데이터를 분석하기 위해 수학적방법으로 차수를 하나 늘려 비선형 데이터축 추가합니다.


# x[:, 1:]

# x[:, 1].reshape(-1, 1)

# x[:, 1][:,np.newaxis]

# 위의 3개는 모두 같은 결과입니다.

x_new = np.hstack([x, x[:, 1:]**2])


linear_svm_3d = LinearSVC().fit(x_new, y)


xx = np.linspace(x[:, 0].min()-2, x[:, 0].max()+2, num=50)

yy = np.linspace(x[:, 1].min()-2, x[:, 1].max()+2, num=50)


# xx, yy의 좌표를 이용하여 교차 좌표 만듬 ex) x=[1, 2], y=[3, 4]라면 (1, 3), (1, 4), (2, 3), (2, 4) 좌표 생성합니다.

XX, YY = np.meshgrid(xx, yy)

ZZ = YY ** 2 


params = np.c_[XX.ravel(), YY.ravel(), ZZ.ravel()] # np.c_ : XX, YY, ZZ의 원소를 하나씩 출력하여 한개의 행으로 묶기

dec = linear_svm_3d.decision_function(params)

plt.contourf(XX, YY, dec.reshape(XX.shape), # XX.shape = (50, 50) 

             levels=[dec.min(), 0, dec.max()],

                 alpha=0.5)


for i, m in zip(np.unique(y), marker_set):

    plt.scatter(x[y==i][:, 0], x[y==i][:, 1], marker=m, edgecolors='k')


plt.xlabel('feature 0', size=15)

plt.ylabel('feature 1', size= 15)

plt.show() 

고차원 특성을 이용해 구분한 Support vector machine decision boundary



Support Vector는 학습이 진행되는 동안 SVM은 각 train 데이터 포인트가 두 class 사이의 decision boundary를 구분하는 데 얼마나 중요한지를 배우게 됩니다.

일반적으로 train 데이터의 일부만 decision boundary를 만드는데 영향을 줍니다.


class 두 개의 경계에 위치한 데이터 포인트를 support vector라 합니다.


suppor vector의 중요도는 train 과정에서 학습하며 SVC객체의 dual_coef_에 저장되며

radial basis function, RBF커널, gaussian  : 특성의 중요도는 테일러급수를 이용하기때문에 고차원일수록 줄어듭니다.

이번에는 원래 2개 특성의 투영한 decision boundary를 그려보겠습니다.

from sklearn.svm import SVC

from mglearn.tools import make_handcrafted_dataset

from mglearn.plots import plot_2d_separator


x, y = make_handcrafted_dataset()


svm = SVC(kernel='rbf', C=10, gamma=0.1) # kernel, 규제정도, 커널폭의 역수

svm.fit(x, y)


plot_2d_separator(svm, x, eps=0.5)  # epsilon


marker_set = ['o', '^']

color_set = ['skyblue', 'orange']

class_set = np.unique(y)

for i, m, color in zip(class_set, marker_set, color_set):

    plt.scatter(x[y==i][:, 0], x[y==i][:, 1], marker=m, 

label='class {}'.format(i), edgecolors='k', c=color)


sv = svm.support_vectors_

print('{}'.format(sv))

# [[ 8.1062269   4.28695977]

#  [ 9.50169345  1.93824624]

#  [11.563957    1.3389402 ]

#  [10.24028948  2.45544401]

#  [ 7.99815287  4.8525051 ]]


# dual_coef_의 부호에 의해 서포트 벡터의 클래스 레이블이 결정됩니다.

sv_labels = svm.dual_coef_.ravel() > 0 # 중요도가 0보다 높은  것, 클래스 구분

idx_set = [~sv_labels, sv_labels]


for idx,m, color in zip(idx_set, marker_set, color_set):

    plt.scatter(sv[idx][:, 0], sv[idx][:, 1],  s=100, 

marker=m, edgecolors='k', linewidths=2, c=color)


plt.xlabel('feature 0', size=15)

plt.ylabel('feature 1', size=15) 

plt.show() 

RBF Kernel을 사용한 SVM으로 만든 decision boundary와 support vector




SVM에서 model의 complexity를 제어하는 주요 parmeter

gamma: kernel폭의 역수입니다.

gamma가 작을 수록 knernal폭이 넓어져 train 데이터의 영향 범위도 넓어집니다.


다음은 C와 gamma에 따른 decision area를 보여줍니다.

fig, axes = plt.subplots(3, 3) 


C_args = np.array([-1, 0, 3])

C_set = 10**C_args.astype(float)


gamma_args = np.arange(-1, 2)

gamma_set = 10**gamma_args.astype(float)


marker_set = ['o', '^']

for axe, C in zip(axes, C_set):

    for ax, gamma in zip(axe, gamma_set):

        params = {'C':C, 'gamma':gamma}


        svm = SVC(**params).fit(x, y)


        plot_2d_separator(svm, x, eps=0.5, ax=ax)

        ax.set_title('C={}, gamma={}'.format(C, gamma))


        color_set = ['skyblue', 'orange']

        for i, m, color in zip(np.unique(y), marker_set, color_set):

            ax.scatter(x[y==i][:, 0], x[y==i][:, 1], marker=m,

                       label='class {}'.format(i), c=color, edgecolors='k')


        sv = svm.support_vectors_

        idx = svm.dual_coef_.ravel() < 0

        idx_set = np.array([idx, ~idx])


        for i, idx, color, m in zip(np.unique(y), idx_set, color_set, marker_set):

            ax.scatter(sv[idx][:, 0], sv[idx][:, 1], marker=m,

                       c=color, label='class {} support vector'.format(i), s=100,

                       edgecolors='k', linewidths=2)

        

axes[0, 1].legend(loc='upper center', bbox_to_anchor=(0.5, 1.4), fancybox=True, shadow=True, ncol=4)

plt.show()

C와 gamma 에 따른 decision boundary와 support vector

왼쪽에서 오른쪽으로 가면서 gamma 0.1 -> 10 으로 증가합니다.

작은 gamma는 gaussia kernel의 반경을 크게 하여 많은 포인트들이 가까이 있는 것으로 고려됩니다.

따라서 왼쪽 그림의 decision boundary는 부드럽지만 오른쪽 그림의 decision boundary는 데이터 포인트 하나에 민감합니다.

gamma의 값이 클수록 model의 복잡도가 올라가는 것을 확인할 수 있습니다.


위에서 아래로는 C 값을 0.1 -> 1000으로 증가합니다.

linear model과 마찬가지로 작은 C값은 제약이 큰 model을 만듭니다. ==> 일반화 성능이 증가


C값을 증가시키면 제약이 거의 없어져 포인트 하나 값에 영향이 커집니다. ==> 복잡도 증가



SVM으로 Wine Data를 분석해보겠습니다. 

from sklearn.model_selection import train_test_split

from sklearn.datasets import load_wine

from sklearn.svm import SVC


wine = load_wine()


x_train, x_test, y_train, y_test = train_test_split(

      wine.data, wine.target, stratify=wine.target,

      random_state=0, test_size=0.25)


svc = SVC(random_state=0) # default: C=1, kernel='rbf', gamme='auto'

svc.fit(x_train, y_train) 


train_score = svc.score(x_train, y_train)

test_score = svc.score(x_test, y_test)


print('{:.3f}'.format(train_score))

# 1.000


print('{:.3f}'.format(test_score))

# 0.422

gamma의 기본값은 auto로 : 1/wine.data.shape[1]입니다.

train set는 완벽하지만 일반화는 42.2%의 정확도로 overfitting된 것을 알 수가 있습니다.



SVM은 parameter설정과 data scale에 매우 민감합니다. 이를 해결하기 위해 data preprocessing이 존재합니다.

preprocessing을 하기 전에 wine data의 특성을 시각화하여 알아보겠습니다.

plt.boxplot(x_train, manage_xticks=False) # manage_xticks: x축 눈금 작은 눈금 표시

plt.yscale('symlog') # y축 로그 스케일

plt.xlabel('feature list', size =15

plt.ylabel('feature size', size =15)

plt.show()

wine 데이터셋의 특성 값 범위(y축은 logscale)

그래프를 보면 wine 데이터셋의 특성은 자릿 수 자체가 달라서 SVM에서는 영향이 아주 큼을 알 수 있습니다.



이제는 SVM을 위한 데이터 전처리를 해보겠습니다.

전처리는 특성 값의 범위가 비슷해지도록 조정하는 것입니다.


모든 특성의 값을 0과 1사이로 맞추는 방법을 많이 사용합니다.

train_min = x_train.min(axis=0) 

train_range = (x_train - train_min).max(axis=0)

x_train_scaled = (x_train - train_min) / train_range

x_test_scaled = (x_test - train_min) / train_range

    

x_min_ori = x_train_scaled.min(axis=0)

print('{}'.format(x_min_ori))

# [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]


x_max_ori = x_train_scaled.max(axis=0)

print('{}'.format(x_max_ori))

# [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]



위의 코드는 아래와 같이 전처리 메소드를 로드하여 사용할 수 있으며 앞으로는 이 방법을 사용하겠습니다.

from sklearn.preprocessing import MinMaxScaler


mms_scaler = MinMaxScaler()

mms_scaler.fit(x_train)


x_train_scaled_mms = mms_scaler.transform(x_train)

x_test_scaled_mms = mms_scaler.transform(x_test)


x_min = x_train_scaled.min(axis=0)

print('{}'.format(x_min))

# [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]


x_max = x_train_scaled.max(axis=0)

print('{}'.format(x_max))

# [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]



이제 전처리한 데이터로 머신러닝을 해보겠습니다.

svc = SVC(random_state=0)

svc.fit(x_train_scaled_mms, y_train)


train_score = svc.score(x_train_scaled_mms, y_train)

test_score = svc.score(x_test_scaled_mms, y_test)


print('{:.3f}'.format(train_score))

# 0.970


print('{:.3f}'.format(test_score))

# 0.978

스케일을 조정하니 성능이 매우 좋아졌습니다.



이번에는 C값을 매우 크게 하여 규제를 완화 해보겠습니다.

svc = SVC(C=1000) 

svc.fit(x_train_scaled, y_train) 


train_score = svc.score(x_train_scaled_mms, y_train) 

test_score = svc.score(x_test_scaled_mms, y_test)


print('{:.3f}'.format(train_score))

# 1.000


print('{:.3f}'.format(test_score))

# 1.000

규제를 매우 완화 하니 train_score와 test_score모두 결과가 1이 나와 과소적합된 형태입니다.



SVM은 데이터의 특성이 몇개 안되도 복잡한 decision boundary를 만들 수 있습니다.

SVM은 parameter와 pre-procession(전처리)에 신경을 많이 써야합니다.


random_forest나 gradient boosting 같은 pre-processing이 거의 없는 모델을 많이 사용합니다.

그러나 예측이 어떻게 결정되었는지 이해하기 어렵고 비전문가에게 설명하기또한 난해합니다.




참고 자료:

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Neural Network(Deep Learning)  (0) 2018.03.17
Gradient Boosting Model  (0) 2018.03.15
Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14

#!/usr/bin/env python3


Gradient Boosting Model


gradient boosting regression tree는 여러 개의 decision tree를 묶어 강력한 model을 만드는 ensemble기법입니다.

random forest와 달리 gradient boosting model은 이전 tree의 오차를 보완하는 방식으로 tree를 만듭니다.


gradient boosting은 무작위성이 없어 powerful한 pre-pruning이 사용되며

1~5 정도 깊이의 tree를 사용하므로 메모리를 적게 사용하고 예측도 빠릅니다.


gradient boosting은 이런 얕은 트리들을 계속해서 연결해나가는 것입니다.


gradient boosting은 parmeter설정에 random forest보다 조금 더 민감하지만 잘 조정하면 높은 정확도를 제공합니다.


gradinet boosting에서 주요 parameter는 이전 트리의 오차를 얼마나 강하게 보정할 것인가를 제어하는

learning_rate가 있습니다.


learning_rate를 높이면 보정을 강하게 하기 때문에 복잡한 모델을 만듭니다.

n_estimator 값을 키우면 ensemble에 트리가 더 많이 추가되어 모델의 복잡도가 커지고 train 세트를 더 정확하게 fitting합니다.


Breast Cancer datasets으로 GradientBoostingClassifier을 적용해보겠습니다.

from sklearn.ensemble import GradientBoostingClassifier

from sklearn.model_selection import train_test_split

from sklearn.datasets import load_breast_cancer


cancer = load_breast_cancer()


x_train, x_test, y_train, y_test = train_test_split(cancer.data, cancer.target, 

                                                    stratify=cancer.target, random_state=0)


gbc = GradientBoostingClassifier(random_state=0) # 기본값: max_depth=3, learning_rate=0.1

gbc.fit(x_train, y_train)


score_train = gbc.score(x_train, y_train) # train set 정확도

print('{:.3f}'.format(score_train))

# 1.000


score_test = gbc.score(x_test, y_test) # 일반화 정확도

print('{:.3f}'.format(score_test))

# 0.958

훈련세트의 정확도가 100%이므로 overfitting입니다.



overfitting을 막기 위해 트리의 깊이를 줄여 pre-pruning을 강하게 하겠습니다.

gbc = GradientBoostingClassifier(random_state=0, max_depth=1)

gbc.fit(x_train, y_train)


score_train_pre = gbc.score(x_train, y_train) # train set 정확도

print('{:.3f}'.format(score_train_pre))

# 0.995


score_test_pre = gbc.score(x_test, y_test) # 일반화 정확도

print('{:.3f}'.format(score_test_pre))

# 0.965

이전에 비해 train_set는 낮아졌고, test set의 정확도는 높아졌으므로 모델이 향상된 것을 볼 수 있습니다.



이번에는 learning_rate 조절하여 overfitting을 해결하겠습니다.

gbc = GradientBoostingClassifier(random_state=0, max_depth=3, learning_rate=0.01) # 기본값 0.1

gbc.fit(x_train, y_train)


score_train_lr = gbc.score(x_train, y_train)

print('{:.3f}'.format(score_train_lr))

# 0.995


score_test_lr = gbc.score(x_test, y_test) 

print('{:.3f}'.format(score_test_lr))

# 0.944



위의 결과를 시각화하는 코드는 다음과 같습니다.

import matplotlib

import matplotlib.pyplot as plt

import numpy as np


score_set = np.round([score_train, score_test], 3)

max_depth_score_set = np.round([score_train_pre, score_test_pre], 3)

learning_rete_score_set = np.round([score_train_lr, score_test_lr], 3)


index = np.arange(len(score_set))

bar_width = 0.35

bottom = 0.8

list_set = [max_depth_score_set, learning_rete_score_set]

line_set = ['--', ':']

names = ['train', 'test']


for i, line_set, name in zip(index, line_set, names):

    plt.hlines(score_set[i], xmin=0-bar_width,

               xmax=index[-1]+2*bar_width,

               linestyle=line_set, label='default {}'.format(name))

    plt.text(0-bar_width, score_set[i]+0.005, str(score_set[i]))


plt.bar(index, max_depth_score_set, width=bar_width, label='adj max_depth')

plt.bar(index+bar_width, learning_rete_score_set, width=bar_width, label='adj learning rate')

for i, ts, te in zip(index, max_depth_score_set, learning_rete_score_set):

    plt.text(i, (bottom+ts)/2, str(ts), horizontalalignment='center')

    plt.text(i+bar_width, (bottom+te)/2, str(te), horizontalalignment='center')


plt.ylim(bottom, 1.05)

plt.xticks(index+bar_width/2, names)

plt.ylabel('score', size=15)

plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1), ncol=4, fancybox=True, shadow=False)

plt.show()

max_depth와 learning rate를 조절한 random forest의 score

위 2가지 방식은 model의 복잡도를 감소시키므로 train 세트의 정확도가 낮아졌습니다.

이 예에서 learning_rate를 낮추는 것은 일반화 정확도의 성능을 조금밖에 개선하지 못했지만, 

트리의 최대 깊이를 낮추는 것은 model 성능 향상에 크게 기여한것을 볼 수 있습니다.



max_depth=1일 때 특성 중요도를 시각화 하는 코드는 다음과 같습니다. 

gbc = GradientBoostingClassifier(max_depth=1, random_state=0)

gbc.fit(x_train, y_train)

n_feature = cancer.data.shape[1]

index=np.arange(n_feature)


plt.barh(index, gbc.feature_importances_, align='center')

plt.yticks(index, cancer.feature_names)

plt.xlabel('feature importances', size=15)

plt.ylabel('feature', size=15)

plt.show() 

breast cancer data로 만든 GradientBoostingClassifier의 feature importances

GradientBoostingClassifier의 특성 중요도와 RandomForestClassifier의 특성 중요도가 비슷합니다.

그러나 GradientBoostingClassifier은 일부 특성을 완전 무시합니다.


보통 더 안정적인 RandomForest를 먼저 적용하고 GradientBoosting을 사용하고

예측 시간이 중요하거나, machine learning model에서 마지막 성능까지 사용할 때 많이 사용



참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Neural Network(Deep Learning)  (0) 2018.03.17
Kernelized Support Vector Machines  (0) 2018.03.15
Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14

#!/usr/bin/env python3


Random Forest


ensemble여러 머신러닝 model을 연결하여 강력한 model을 만드는 기법입니다.

ensemble model이 classifier와 regression 문제의 다양한 데이터셋에서 효과적이며

random forestgradient boosting은 둘 다 model을 구성하는 기본 요소로 decision tree를 사용합니다.


random forest는 기본적으로 조금씩 다른 여러 decision tree의 묶음입니다.


random forest의 Idea

각각의 tree는 비교적 예측을 잘 할 수 있지만 데이터의 일부에 overfitting하는 경향을 가진다는 데 기초합니다.

잘 작동하되 서로 다른 방향으로 overfitting된 tree를 많이 만들면 그 결과를 평균냄으로써 overfitting된 양을 줄일 수 있습니다.

이렇게 하면 tree model의 예측 성능이 유지되면서 overfitting이 줄어드는 것이 수학적으로 증명되었습니다.


이런 Idea를 구현하기 위해 decision tree를 많이 만들어야하고

각각의 tree는 타깃 예측을 잘해야 하고 다른 tree와는 구별되어야 합니다.


random forest는 tree들이 달라지도록 tree를 생성할 때 random하게 생성합니다.

random forest에서 tree를 random하게 만드는 방법은 2가지가 있습니다.


1. tree를 만들 때 사용하는 데이터를 무작위로 선택하고

2. 분할 테스트에서 특성을 무작위로 선택합니다.


random forest model에서 주요 parameter

n_estimators : 생성할 tree의 개수와

max_features : 최대 선택할 특성의 수입니다.


random forest는 기본적으로 bootstrap sampling(복원추출)을 사용하며

decision tree 생성으로 algorithm으로 진행합니다.


bootstrap sampling에서 어떤 데이터 포인트는 약 36.6%정도 누락될 확률이 존재하는데

이 것은 다음과정으로 확인할 수 있습니다.

 - 100개의 표본중 표본 하나가 선택되지 않을 확률은 99/100

 - 뽑은 표본을 제외하지 않고 100번 반복할 때 한번도 선택되지 않을 확률: (0.99)^100 = 0.366

 

n_estimators에서

bootstrap sampling은 random forest의 tree가 조금씩 다른 데이터셋으로 만들어지도록합니다.

max_feature에서 각 node는 특성의 일부를 무작위로 추출하여 사용합니다.


max_features를 전체 특성의수로 설정하면 모든 특성을 고려하므로 decision tree에서 무작위성이 들어가지 않습니다.

그러나 bootstrap sampling의 무작위성은 존재합니다.


max_features 값을 크게 하면 

random forest의 tree들은 같은 특성을 고려하므로 tree들이 매우 비슷해지고 가장 두드러진 특성을 이용해 데이터에 잘 맞춰집니다.


max_features를 낮추면 

random forest tree들은 많이 달라지고 각 tree는 데이터에 맞추기 위해 tree의 깊이가 깊어집니다.


인위적 데이터로 random forest를 tree별로 알아보면

from sklearn.ensemble import RandomForestClassifier

from sklearn.datasets import make_moons

from sklearn.model_selection import train_test_split


x, y = make_moons(n_samples=100, noise=0.25, random_state=3) # noise: Standard deviation of Gaussian noise added to the data.


x_train, x_test, y_train, y_test = train_test_split(x, y,

                                                    stratify=y, random_state=42)


forest = RandomForestClassifier(n_estimators=5, n_jobs=-1, random_state=42) # n_estimators: 사용할 tree수

forest.fit(x_train, y_train)


import matplotlib.pyplot as plt

import numpy as np

from mglearn.plots import plot_2d_classification


_, axes = plt.subplots(2, 3)

marker_set = ['o', '^']


for i, (axe, tree) in enumerate(zip(axes.ravel(), forest.estimators_)):

    axe.set_title('tree {}'.format(i))

    plot_2d_classification(tree, x, fill=True, ax=axe, alpha=0.4)


    for i, m in zip(np.unique(y), marker_set):

        axe.scatter(x[y==i][:, 0], x[y==i][:, 1], marker=m,

                    label='class {}'.format(i), edgecolors='k')

        axe.set_xlabel('feature 0')

        axe.set_ylabel('feature 1')


axes[-1, -1].set_title('random forest')

axes[-1, -1].set_xlabel('feature 0')

axes[-1, -1].set_ylabel('feature 1')

plot_2d_classification(forest, x, fill=True, ax=axes[-1, -1], alpha=0.4)


for i, m in zip(np.unique(y), marker_set):

    plt.scatter(x[y==i][:, 0], x[y==i][:, 1], marker=m,

                label='class {}'.format(i), edgecolors='k')

plt.show()

5개의 random dicision tree의 decision boundary와 예측한 확률을 평균내어 만든 random forest

5개의 트리가 만든 decision boundary는 매우 다릅니다.

bootstraping sampling 때문에 한쪽 tree에 나타나는 train point가 다른 tree에는 포함되지 않을 수 있습니다.



random forest를 이용하여 breast cancer datasets을 분석해보겠습니다.

from sklearn.datasets import load_breast_cancer


cancer = load_breast_cancer()


x_train, x_test, y_train, y_test = train_test_split(cancer.data, cancer.target,

                                                    stratify=cancer.target, random_state=0)


n_feature = cancer.data.shape[1]


score_n_tr_est = []

score_n_te_est = []

score_m_tr_mft = []

score_m_te_mft = []


for i in np.arange(1, n_feature+1): # n_estimators와 mat_features는 모두 0보다 큰 정수여야 하므로 1부터 시작합니다.

    params_n = {'n_estimators':i, 'max_features':'auto', 'n_jobs':-1} # **kwargs parameter

    params_m = {'n_estimators':10, 'max_features':i, 'n_jobs':-1}


    forest_n = RandomForestClassifier(**params_n).fit(x_train, y_train)

    forest_m = RandomForestClassifier(**params_m).fit(x_train, y_train)

    

    score_n_tr = forest_n.score(x_train, y_train)

    score_n_te = forest_n.score(x_test, y_test)

    score_m_tr = forest_m.score(x_train, y_train)

    score_m_te = forest_m.score(x_test, y_test)


    score_n_tr_est.append(score_n_tr)

    score_n_te_est.append(score_n_te)

    score_m_tr_mft.append(score_m_tr)

    score_m_te_mft.append(score_m_te)


index = np.arange(len(score_n_tr_est))

plt.plot(index, score_n_tr_est, label='n_estimators train score', color='lightblue', ls='--') # ls: linestyle

plt.plot(index, score_m_tr_mft, label='max_features train score', color='orange', ls='--')

plt.plot(index, score_n_te_est, label='n_estimators test score', color='lightblue')

plt.plot(index, score_m_te_mft, label='max_features test score', color='orange')

plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1),

           ncol=2, fancybox=True, shadow=False) # fancybox: 박스모양, shadow: 그림자

plt.xlabel('number of parameter', size=15)

plt.ylabel('score', size=15)

plt.show()

parameter 갯수에 따른 train score와 test score

parameter의 갯수가 적을 때보다 많을때가 성능이 더 좋아 보입니다.



이번에는 사용한 특성의 중요도를 시각화 하겠습니다.

n_feature = cancer.data.shape[1]

index = np.arange(n_feature)


forest = RandomForestClassifier(n_estimators=100, n_jobs=-1)

forest.fit(x_train, y_train)

plt.barh(index, forest.feature_importances_, align='center')

plt.yticks(index, cancer.feature_names)

plt.ylim(-1, n_feature)

plt.xlabel('feature importance', size=15)

plt.ylabel('feature', size=15)

plt.show()

breast cancer로 만든 random forest의 feature importances

decision tree처럼 random foreset도 featre importances를 제공합니다.

일반적으로 random forest의 feature importances가 하나의 tree에서 제공하는 것보다 더 신뢰할 만합니다.


decision tree에서는 'worst radius'특성이 가장 중요하다고 보지만 random forest에서는 'worst perimeter'를 가장 중요한 요소로 판단했습니다.

random forest는 단일 tree보다 더 넓은 시각으로 데이터를 바라볼 수 있습니다. 




참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Kernelized Support Vector Machines  (0) 2018.03.15
Gradient Boosting Model  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14
Logistic Regression  (1) 2018.03.13

#!/usr/bin/env python3


Decision Tree


Decision Tree결정트리는 True/False 질문을 이어나가면서 학습합니다.


다음 코드는 decision tree의 algorithm을 보여줍니다.

from mglearn.plots import plot_animal_tree

import matplotlib.pyplot as plt


plot_animal_tree()

plt.show()

몇 가지 동물들을 구분하기 위한 결정 트리

여기에서 tree의 node는 질문이나 정답을 담은 네모 상자를 말하며

root node는 최상위 node이고

leaf node는 마지막 node입니다.


decision tree를 학습한다는 것은 정답에 가장 빨리 도달하는 True/False 질문 목록을 학습하는 것입니다.

머신러닝에서 이런 질문들을 'test'라 합니다.


만약 tree를 만들 때 모든 leaf node가 pure node가 될 때 까지 진행하면 model의 complexity는 매우 높아지고 overfitting됩니다. 즉 train set의 모든 데이터포인트가 leaf node에 있다는 뜻입니다.


overfittng을 막는 전략은 크게 2가지가 있습니다.

1. tree 생성을 사전에 중단합니다 : pre-pruning

2. 데이터 포인트가 적은 node를 삭제하거나 병합합니다 : post-pruning



이제는 실제 데이터셋(wine dataset)으로 decision tree 분석을 해보겠습니다.

from sklearn.datasets import load_wine

from sklearn.model_selection import train_test_split

from sklearn.tree import DecisionTreeClassifier


wine = load_wine()


x_train, x_test, y_train, y_test = train_test_split(wine.data, wine.target,

                                                    stratify=wine.target, random_state=0)


tree = DecisionTreeClassifier(random_state=0)

tree.fit(x_train, y_train)


score_tr = tree.score(x_train, y_train)

score_te = tree.score(x_test, y_test)


print('{:.3f}'.format(score_tr))

# 1.000


print('{:.3f}'.format(score_te))

# 0.889

모든 leaf noe가 pure node이므로 train set의 정확도는 100%입니다.

tree는 train set의 모든 포인트들을 완벽하게 구분할 만큼 깊게 만들어집니다. 따라서 overfitting의 가능성이 매우 높아집니다.



이런 overfitting을 막기위해 깊이를 제한하는 방법을 사용할 수 있습니다.

pre-pruning의 방법 중 하나는 깊이의 최대를 설정하는 것입니다.

tree = DecisionTreeClassifier(max_depth=2, random_state=0)

tree.fit(x_train, y_train)


score_tr = tree.score(x_train, y_train)

score_te = tree.score(x_test, y_test)


print('{:.3f}'.format(score_tr))

# 0.910


print('{:.3f}'.format(score_te))

# 756

train_set의 score가 낮아진 것으로 보아 overfitting은 감소한 것처럼 보이지만, test set의 score또한 낮아졌으므로 이 데이터셋에 tree model은 적합하지 않음을 알 수 있습니다.



tree module의 export_graphviz함수를 이용해 tree를 시각화할 수 있습니다.

graphviz를 설치한 후 사용가능합니다

import graphviz

from sklearn.tree import export_graphviz


export_graphviz(tree, out_file='tree.dot',

                class_names=wine.target_names,

                feature_names=wine.feature_names,

                impurity=False# gini 미출력

                filled=True) # filled: node의 색깔을 다르게


with open('tree.dot') as file_reader:

    dot_graph = file_reader.read()


dot = graphviz.Source(dot_graph) # dot_graph의 source 저장

dot.render(filename='tree.png'# png로 저장

tree.png

wine 데이터셋으로 만든 Decision Tree

가장 중요한 특성이 proline아미노산임을 알 수 있습니다.



tree는 어떻게 작동하는지 요약하는 속성들을 사용할 수 있습니다.

가장 널리 사용되는 속성은 tree를 만드는 결정에 각 특성이 얼마나 중요한지는 평가하는 feature importance특성 중요도가 있습니다.

이 값은 0과 1사이의 숫자로 0은 전혀 사용되지 않았다는 뜻이고 1은 완벽하게 예측했다는 뜻입니다.

feature_imp = tree.feature_importances_

print('{}'.format(feature_imp))

# [0.         0.         0.         0.02885475 0.         0.

#  0.         0.         0.         0.43979596 0.         0.

#  0.53134929]



더 자세히 알아보기 위해 feature_importance를 시각화를 하겠습니다.

import numpy as np

n_feature = wine.data.shape[1]

idx = np.arange(n_feature)


plt.barh(idx, feature_imp, align='center')

plt.yticks(idx, wine.feature_names)

plt.xlabel('feature importance', size=15)

plt.ylabel('feature', size=15)

plt.show()

feature importance와 feature

root node에서 사용한 특성 'proline'이 가장 중요한 특성으로 나타납니다.


feature_importances_ 값이 낮다고 해서 이 특성이 유용하지 않다는 뜻은 아닙니다.

단지 이 tree가 특성을 선택하지 않았을 뿐이며, 다른 특성이 동일한 정보를 가지고 있어서 일 수도 있습니다.


feature importance 값은 'proline'가 중요하다가 알려주지만 이 값이 양성인지 음성인지는 알 수 없습니다.




참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Gradient Boosting Model  (0) 2018.03.15
Random Forest  (0) 2018.03.15
Multi Linear Classification  (0) 2018.03.14
Logistic Regression  (1) 2018.03.13
k_NN(k-최근접 이웃) Classifier[2]  (1) 2018.03.12

#!/usr/bin/env python3


Multi Linear Classification


Logistic Regression만 제외하고 많은 linear model은 binary classifier를 지원합니다.

binary classifier algorithm을 multi class classifier algorithm으로 확장하는 보편적인 기법은

one-vs.-rest일대다방법입니다.


one-vs.-rest방식은 각 class를 다른 모든 class와 구분하도록 binary classifier model을 학습합니다.

이렇게하면 class의 수만큼 binary classifier model이 만들어집니다.


예측을 할 때 이렇게 만들어진 모든 binary classifier가 작동하여 가장 높은 점수를 내는 classifier의 class를 예측값으로 선택합니다.


class별 binary classifier를 만들면 각 class가 계수 벡터(w)와 절편(b)를 하나씩 갖게 됩니다.

결국 classifer 신뢰도를 나타내는 다음 공식의 결과값이 가장 높은 class가 해당 데이터의 class 레이블로 할당됩니다.


formula :  w[0] * x[0] + w[1] * x[1] + … + w[p] * x[p] + b


multi class Logistic Regression는 one-vs.-rest 방식과는 다르지만 class마다 하나의 계수벡터와 절편을 만들며 예측방법도 같습니다.


인위적 데이터의 구조를 보기 위해 시각화를 해보겠습니다.

from sklearn.datasets import make_blobs

import matplotlib.pyplot as plt

import numpy as np


x, y = make_blobs(random_state=42)


idx_set = []

marker_set = ['o', 'v', '^']

for i in np.unique(y):

    idx = np.where(y==i)[0]

    idx_set.append(idx)


for i, m in enumerate(marker_set):

    plt.scatter(x[idx_set[i]][:, 0], x[idx_set[i]][:, 1],

                marker=m, label='class {}'.format(i))

plt.legend()

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.show()

3개의 클래스를 가진 2차원 데이터셋



다음으로 LinearSVC의 결과의 속성을 알아보겠습니다.

from sklearn.svm import LinearSVC


linear_svc = LinearSVC().fit(x, y)


print('{}'.format(linear_svc.coef_.shape))

# (3, 2)


print('{}'.format(linear_svc.coef_))

# [[-0.17492614  0.23140501]

#  [ 0.47621354 -0.06937621]

#  [-0.18914141 -0.20400513]]


print('{}'.format(linear_svc.intercept_))

# [-1.07745335  0.13140664 -0.08604991]


print('{}'.format(linear_svc.intercept_.shape))

# (3,)



3개의 binary classifier가 만드는 경계를 시각화하는 코드는 다음과 같습니다.

from mglearn.plots import plot_2d_classification


plot_2d_classification(linear_svc, x, fill=True, alpha=0.5)


idx_set = []

market_set = ['o', '^', 'v']

for i in np.unique(y):

    idx = np.where(y==i)[0]

    idx_set.append(idx)


for i, m in enumerate(marker_set):

    plt.scatter(x[idx_set[i]][:, 0], x[idx_set[i]][:, 1], marker=m,

                label='class {}'.format(i), edgecolors='k')


line = np.linspace(-15, 15, num=50)

for i, (coef, icept) in enumerate(zip(linear_svc.coef_, linear_svc.intercept_)):

    dec_line = -(line*coef[0] + icept) / coef[1]

    plt.plot(line, dec_line, ls='--', dashes=(3, 1), label='class {} line'.format(i)) # dashes: dash간격, 3dot당 공간 1개


plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.legend(loc=(1.01, 0.9))

plt.show()

3개의 one vs.rest classifier가 만든 decision boundary

class 0에 속한 모든 포인트는 class 0을 구분하는 직선 위, 나머지 class는 아래에 있습니다.

class 1에 속한 모든 포인트는 class 1을 구분하는 직선 위, 나머지 class는 아래에 있습니다.

이런 방법으로 모든 class를 분류합니다.


중간에 삼각형 영역은 모두 나머지로 분류하는 영역입니다.

이 영역에 있는 데이터 classifier 공식의 결과가 가장 높은 class로 분류합니다.

즉 가장 가까운 직선의 class가 됩니다.


Linear model에서 주요 parameter는

regression model에서 alpha

LinearSVC, Logistic Regression에서는 C입니다.


alpha값이 클수록, C값이 작을수록 model이 단순해집니다.


Regularization: L1, L2중 선택하고 기본적으로L2를 사용합니다.(모든 특성 사용)

L1은 몇가지 특성만 사용하므로 해당 모델에 중요한 특성이 무엇이고 효과가 어느정도 인지 설명하기 쓉습니다.


데이터셋이 크다면

LogisticRegression과 Ridge에 solver='sag'옵션을 주면 더 빨리처리가 가능합니다.

sag: Stochastic Average Gradient descent(확률적 평균 경사 하강법)입니다.




참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Logistic Regression  (1) 2018.03.13
k_NN(k-최근접 이웃) Classifier[2]  (1) 2018.03.12
k_NN(k-최근접 이웃) Classifier[1]  (0) 2018.03.11

#!/usr/bin/env python3


Logistic Regression[1]


Logistic Regression의 공식은 아래와 같습니다.

formula = ŷ = w[0] * x[0] + w[1] * x[1] + … + w[p] * x[p] + b > 0


Linear Regression와 매우 비슷하지만 가중치(w) 합을 사용하는 대신 예측한 값을 critical value 0 과 비교합니다.

함수에서 계산한 값이 0보다 작으면 class를 -1, 0보다 크면 +1이라고 예측합니다.


이 규칙은 Classifier에 쓰이는 모든 Linear model에서 동일합니다.


가장 널리 알려진 두 개의 linear classifier algorithm은

1. Logistic Regression

2. Support Vector Classifier의 Linear SVC입니다.


이해를 돕기 위해 LogisticRegression과 LinearSVC model를 인위적 데이터인 forge 데이터셋을 분석해보겠습니다.

from mglearn.plots import plot_2d_separator

from mglearn.datasets import make_forge

import matplotlib.pyplot as plt

from sklearn.linear_model import LogisticRegression

from sklearn.svm import LinearSVC

import numpy as np


x, y = make_forge()


_, axes = plt.subplots(1, 2)

models = [LogisticRegression(), LinearSVC()]


for model, axe in zip(models, axes.ravel()):

    clf = model.fit(x, y)

    plot_2d_separator(clf, x, fill=True, eps=0.5, ax=axe, alpha=0.5) # eps: epsilon, ax:그림객체


    idx_set = []

    for i in np.unique(y):

        idx = np.where(y==i)[0] # np.where는 array의 index를 출력결과를 tuple형태로 리턴합니다.

        idx_set.append(idx)


    axe.scatter(x[idx_set[0]][:, 0], x[idx_set[0]][:, 1],

                label='class {}'.format(1), s=40, marker='^', edgecolors='k')

    axe.scatter(x[idx_set[1]][:, 0], x[idx_set[1]][:, 1],

                label='class {}'.format(2), s=40, edgecolors='k')

    axe.set_title('{}'.format(clf.__class__.__name__))

    axe.set_xlabel('feature 0')

    axe.set_ylabel('feature 1')

axes[0].legend(loc=1)

plt.show()

forge 데이터셋에 기본 매개변수를 사용해 만든LinearSVC와 Logistic Regression model의 경계

첫 번째 특성을 x축, 두번째 특성을 y축에 표현했습니다.

LinearSVC와 LogisticRegression으로 만든 결정 경계가 직선으로 표현되었고 위쪽은 class1, 아래쪽은 class0입니다.

즉 새로운 데이터가 직선 위쪽에 놓이면 class1, 아래쪽에 놓이면 class0으로 분류됩니다.



LinearSVC와 LogisticRegression은 모두 L2 Regularization(0으로 근사)를 사용합니다.

Regularization의 강도를 결정하는 parameter는 C입니다. C값이 낮을수록 계수를 0으로 근사하므로 regularization이 강화됩니다.


다음은 C값에 따른 LogisticRegression의 decision boundary를 보여주는 코드입니다.

c_set = [0.001, 1, 1000]

_, axes = plt.subplots(1, 3)


for c_arg, axe in zip(c_set, axes.ravel()):

    params = {'C':c_arg}

    clf = LogisticRegression(**params).fit(x, y)


    plot_2d_separator(clf, x, fill=True, ax=axe, eps=0.5, alpha=0.5)


    idx_set = []

    for i in np.unique(y): # np.unique: 유일한 값만 리턴

        idx = np.where(y==i)[0]

        idx_set.append(idx)


    axe.scatter(x[idx_set[0]][:, 0], x[idx_set[0]][:, 1], s=40, edgecolors='k'# edgecolors='k' : 'k'=black

                label='class 0', marker='^')

    axe.scatter(x[idx_set[1]][:, 0], x[idx_set[1]][:, 1], s=40, edgecolors='k',

                label='class 1', marker='o')

    axe.set_title('{}, C={}'.format(clf.__class__.__name__, c_arg))

    axe.set_xlabel('feature 0')

    axe.set_ylabel('feature 1')

axes[0].legend(loc=3)

plt.show()

forge 데이터셋에 다른 C값으로 만든 LogisticRegression의 decision boundary

왼쪽 그림은 작은 C 값으로 regularization이 많이 적용되었습니다.

Regularization이 강할수록 수평적으로 그려지고

Regularization C값이 클수록 decision boundary는 최대한 train data를 맞추려고 합니다. 즉 overfitting의 가능성이 증가합니다.



LogisticRegression은 linear_model과 성격이 비슷하므로 마찬가지로 coefficient를 가지고 있습니다.

다음은 C값에 따른 coefficient를 시각화하는 코드입니다.

C_set = [0.01, 1, 100]

n_feature = cancer.data.shape[1]

line = np.linspace(0, n_feature, num=n_feature).reshape(-1, 1)

marker_set = ['o', '^', 'v']


for c_args, m in zip(C_set, marker_set):

    params = {'C':c_args}

    logreg = LogisticRegression(**params).fit(x_train, y_train)

    plt.scatter(line, logreg.coef_, marker=m, label='C={}'.format(c_args))


plt.hlines(0, xmin=np.min(line), xmax=n_feature, lw=2, color='k')

plt.xticks(np.arange(n_feature), cancer.feature_names, rotation=90, ha='center') # ha: horizon alignment

plt.ylim(-5, 5)

plt.xlabel('feature', size=15)

plt.ylabel('coef_ size', size=15)

plt.legend(loc=1)

plt.show()

breast cancer 데이터셋에 다른 C 값을 사용하여 만든 LogisticRegression coef_



이번엔 Logistic Regression에서 L1 regularization(계수를 0으로)를 사용하여 시각화해보겠습니다.

C_set = [0.01, 1, 100]

n_feature = cancer.data.shape[1]

line = np.linspace(0, n_feature, num=n_feature).reshape(-1, 1)

marker_set = ['o', '^', 'v']


for c_args, m in zip(C_set, marker_set):

    params = {'C':c_args, 'penalty':'l1'}

    logreg = LogisticRegression(**params).fit(x_train, y_train)

    plt.scatter(line, logreg.coef_, marker=m, label='C={}'.format(c_args))


plt.hlines(0, xmin=np.min(line), xmax=n_feature, lw=2, color='k')

plt.xticks(np.arange(n_feature), cancer.feature_names, rotation=90, ha='center')

plt.ylim(-5, 5)

plt.xlabel('feature', size=15)

plt.ylabel('coef_ size', size=15)

plt.legend(loc=1)

plt.show()

breast cancer 데이터와 L1 Regularization을 사용하여, 다른 C값을 적용한 LogisticRegression coef_

Regularization에서 모든 특성을 이용할지 일부 특성만을 사용할지 결정하는 주요 parameter는 'penalty'입니다.




참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14
k_NN(k-최근접 이웃) Classifier[2]  (1) 2018.03.12
k_NN(k-최근접 이웃) Classifier[1]  (0) 2018.03.11

#!/usr/bin/env python3


k_NN(k-최근접 이웃)  Classifier[2]


k-NN algorithm은 가장 가까운 데이터 포인트중 n_neighbors만큼 이웃으로 삼아 예측으로 사용합니다.

다음은 k-NN algorithm을 보여줍니다.

import matplotlib.pyplot as plt

import mglearn


mglearn.plots.plot_knn_classification(n_neighbors=1)

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.title('n_neighbors=1')

plt.legend(feature_list, loc=(1.01, 0.4))

plt.show()

n_neighbors=1 일 때 algorithm


mglearn.plots.plot_knn_classification(n_neighbors=3)

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.title('n_neighbors=3')

plt.legend(feature_list, loc=(1.01, 0.4))

plt.show()

n_neighbors=1 일 때 algorithm



이번에는 n_neighbors의 숫자에 따른 결정 경계를 그려보겠습니다.

먼저 임의로 데이터셋을 만들어보겠습니다.

from mglearn.datasets import make_forge


x, y = make_forge()


_, axes = plt.subplots(1, 3)

for i, ax in zip([1, 3, 9], axes.ravel()):

    knn = KNeighborsClassifier(i)

    knn.fit(x, y)


    mglearn.plots.plot_2d_separator(knn, x, fill=True,

                                    eps=0.5, alpha=0.5, ax=ax)

    mglearn.discrete_scatter(x[:, 0], x[:, 1],

                             y=y, ax=ax)

    ax.set_title('k={}'.format(i))

    ax.set_xlabel('feature 0')

    ax.set_ylabel('feature 1')

axes[0].legend(loc=(0.01, 1.01))

plt.show()

n_neighbors 값에 따른 k-NN모델이 만든 decision boundary

이 그림을 보면 제일 왼쪽인 k=1일 경우에 모든 데이터를 가장 정확하게 분류했으며 그 경계는 복잡합니다.

k의 값이 커질수록 경계선은 점점 수평에 가까워지며 경계는 완만해짐을 알 수 있습니다.



실제wine 데이터를 분석해보겠습니다.

분석하기전에 wine데이터의 전체적인 구조를 pandas의 scatter_matrix를 이용해서

확인해보겠습니다. 이 작업은 데이터틔 outlier가 있는지 쉽게 확인할 수 있는 장점이 있습니다.

from sklearn.datasets import load_wine

import pandas as pd


wine = load_wine()

print(wine.keys())

# dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])


for i in range(len(wine.feature_names)):

    print('{}:{}'.format(i, wine.feature_names[i]))

# 0:alcohol

# 1:malic_acid

# 2:ash

# 3:alcalinity_of_ash

# 4:magnesium

# 5:total_phenols

# 6:flavanoids

# 7:nonflavanoid_phenols

# 8:proanthocyanins

# 9:color_intensity

# 10:hue

# 11:od280/od315_of_diluted_wines

# 12:proline    


wine_df = pd.DataFrame(wine.data, columns=range(len(wine.feature_names)))

pd.scatter_matrix(wine_df, # dataframe

                  c=wine.target, # color

                  hist_kwds={'bins':30}, # hist kwords

                  s=10, # size

                  alpha=0.5, # alpha

                  marker='o'# marker

plt.show()

wine의 scatter_plot

그래프가 복잡해 보일지 모르겠지만 전체적인 구조를 한 눈에 파악할 수 있습니다.



데이터를 파악했으니 k-NN 최근접 이웃 algorithm, n_neighbors=3으로 데이터를 분석해보겠습니다.

from sklearn.neighbors import KNeighborsClassifier

from sklearn.model_selection import train_test_split


x_train, x_test, y_train, y_test =\

  train_test_split(wine.data, wine.target, stratify=wine.target, # stratify = 계층화

                   random_state=0, test_size=0.3)  


knn = KNeighborsClassifier(n_neighbors=3, n_jobs=-1) # jobs =  사용할 코어의 수, -1 => 모든 코어

knn.fit(x_train, y_train)


print('{:.3f}'.format(knn.score(x_test, y_test)))

# 0.648

약 KNeighborsClassifier(n_neighbors=3)으로 와인의 품종을 테스트한 결과 약 70%의 정확도를 나타내는 것을

볼 수 있습니다.



이번엔 이 데이터로 모델의 복잡도와 일반화사이의 관계를 알아보겠습니다.

위의 임의로 만든 데이터로 알아본 결과 모델의 복잡도를 알아보았는데 실제 데이터셋으로 모델의 복잡도와 정확도를 알아보겠습니다.

from sklearn.datasets import load_wine

import matplotlib.pyplot as plt


wine = load_wine()


x_train, x_test, y_train, y_test = \

  train_test_split(wine.data, wine.target, stratify=wine.target,

                   test_size=0.3, random_state=0)


train_list = []

test_list = []

n_range = range(1, 20)


for i in n_range:

    knn = KNeighborsClassifier(n_neighbors=i, n_jobs=-1)

    knn.fit(x_train, y_train)


    tr_score = knn.score(x_train, y_train)

    te_score = knn.score(x_test, y_test)


    train_list.append(tr_score)

    test_list.append(te_score)


plt.plot(n_range, train_list, color='red', ls='--', lw=2, label='train accuracy') # ls = linestyle, lw= linewidth

plt.plot(n_range, test_list, color='green', lw=2, label='test accuracy')

plt.xticks(n_range)

plt.xlabel('n_neighbors')

plt.ylabel('accuracy')

plt.legend() 

plt.show()

n_neighbors 변화에 따른 훈련 정확도와 테스트 정확도

이웃의 수(n_neighbors)가 작을수록 모델이 복잡해지고 모델의 정확도는 100%입니다.

그러나 예측 정확도는 0.7정도인 것을 알수있으며

이웃의 수를 증가시켜도 예측 정확도는 많이 늘어나지는 않습니다.

따라서 이 데이터에는 k-NN 최근접 algorithm이 적합하지 않음을 알 수 있습니다.



참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14
Logistic Regression  (1) 2018.03.13
k_NN(k-최근접 이웃) Classifier[1]  (0) 2018.03.11
#!/usr/bin/env python3

k_NN(k-최근접 이웃)  Classifier[1]


분석을 위해 iris데이터를 로드합니다.

from sklearn.datasets import load_iris


iris = load_iris()



iris데이터의 형태는 다음의 코드로 확인할 수 있습니다.

print('''iris's key ==> {}'''.format(iris.keys()))

# iris's key ==> dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])


print('''iris's shape ==> {}'''.format(iris.data.shape))

# iris's shape ==> (150, 4)


print('''iris's data의 처음 6행 ==> \n{}'''.format(iris.data[:6]))

# iris's data의 처음 6행 ==> 

# [[5.1 3.5 1.4 0.2]

#  [4.9 3.  1.4 0.2]

#  [4.7 3.2 1.3 0.2]

#  [4.6 3.1 1.5 0.2]

#  [5.  3.6 1.4 0.2]

#  [5.4 3.9 1.7 0.4]]


print('''iris's feature names ==> \n{}'''.format(iris.feature_names))

# iris's feature names ==> 

# ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']



머신러닝 데이터학습을 위해 데이터를 분할합니다. 이 방법은 앞으로도 계속 사용합니다.

from sklearn.model_selection import train_test_split


x_train, x_test, y_train, y_test = \

  train_test_split(iris.data, iris.target,

                   test_size=0.3, random_state=0)



이제 데이터를 분할했으니 머신러닝을 사용할 때입니다.

이번 포스팅에서는 가장 기본적인 KNN 최근접이웃 머신러닝을 사용합니다.

머신러닝 algorithm을 사용하기 위해서는 학습시킬 데이터를 fitting해야합니다.

from sklearn.neighbors import KNeighborsClassifier


# n_neighbors는 가장 가까운 이웃을 몇개를 고를지에 대한 parameter며

# n_jobs는 사용할 코어의 갯수 입니다. -1이면 모든 코어를 사용합니다.

knn = KNeighborsClassifier(n_neighbors=1, n_jobs=-1)

knn.fit(x_train, y_train)



sample data를 만들어 확인해보겠습니다.

import numpy as np


# ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

x_sample = np.array([[5, 2.9, 1, 0.2]])

prediction = knn.predict(x_sample)

results = {p:n for p, n in zip(prediction, iris.target_names[prediction])}

print('sample test ==> {}'.format(results))

# sample test ==> {0: 'setosa'}

knn(n_neighbors=1)를 이용하여 sample test 예측했을 때는 'setosa'라고 결과를 냈습니다.



이제 위에서 분할한 test data로 예측하면

prediction = knn.predict(x_test)

results = [iris.target_names[p] for p in prediction]

print('results의 처음 6개 ==> {}'.format(results[:6]))

# results의 처음 6개 ==> ['virginica', 'versicolor', 'setosa', 'virginica', 'setosa', 'virginica']


bins = np.bincount(prediction)

count = {n:c for n, c in zip(iris.target_names, bins)}

print('test 결과의 빈도수 ==> {}'.format(count))

# test 결과의 빈도수 ==> {'setosa': 16, 'versicolor': 17, 'virginica': 12}



정확도는 2가지 방법으로 구할 수 있습니다.

KNeighborsClassifier의 score메소드를 이용하거나 직접 계산하는 방법입니다.

print('score 메소드 이용 ==> {:.3f}'.format(knn.score(x_test, y_test)))

# score 메소드 이용 ==> 0.978


y_pred = knn.predict(x_test)

print('numpy 이용 ==> {:.3f}'.format(np.mean(y_pred == y_test)))

# numpy 이용 ==> 0.978



훈련 데이터를 시각화 해보겠습니다.

pandas를 이용할 것이기 때문에 훈련 데이터를 DataFrame으로 변환하고

열이름은 featre_names를 사용하겠습니다.

import pandas as pd

import matplotlib.pyplot as plt

import matplotlib


matplotlib.rc('font', family='AppleGothic') # 한글출력

plt.rcParams['axes.unicode_minus'] = False # 축 - 표시


iris_df = pd.DataFrame(x_train, columns=iris.feature_names)

pd.scatter_matrix(iris_df,

                  c=y_train, # 색깔로 구분할 데이터

                  hist_kwds={'bins':30}, # 히스토그램

                  marker='o', # marker

                  s=20, # size

                  alpha=0.5) # 투명도

plt.show()


pandas를 이용한 scatter_matrix

이 그래프를 통해 훈련데이터의 개괄적인 정보를 한번에 파악할 수 있습니다.




참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido


'python 머신러닝 -- 지도학습 > Classifier' 카테고리의 다른 글

Random Forest  (0) 2018.03.15
Decision Tree  (0) 2018.03.14
Multi Linear Classification  (0) 2018.03.14
Logistic Regression  (1) 2018.03.13
k_NN(k-최근접 이웃) Classifier[2]  (1) 2018.03.12

+ Recent posts