728x90

스무딩을 왜 했었더라..

 

영상 잡음 노이즈 제거용으로 스무딩 처리를 한다고 하더라

 

가장 많이 사용되는게 가우시안 스무딩, 블러링이라고도 한다.

 

 

 

 

그런데 지금까지 내가 구현한 코드가 잘못된 부분이 있었다.

 

평균 스무딩, 평균 임계화를 다루면서

 

내가 패딩 처리를 제대로 하지 않은것도 있엇고

 

사실 커널, 마스크를 만들어 곱 연산 후 나누어주었어야 했는데, 그냥 평균으로 내버린 점이 그렇다.

 

일단 평균 임계/스무딩까지는 그렇게 해도 괜찬았는데

 

 

 

가우시안 스무딩을 구현하면서가 문제가 되더라..

 

ref : youngest-programming.tistory.com/235?category=877383

 

 

그러다 한 학생이 구현한 내용들을 봤는데

 

보기 좋고, 이해하기도 쉽고 정말 잘 만들었더라.

 

 

 

나는 대충 돌아가게만 만들어놓고 내버리거나 하는 경우가

 

아무튼 이분 내용 참고해서 간단하게 나마 구현할 수 있었다.

 

 

 

 

 

 

 

가우시안 커널, 마스크를 어떻게 구현하나 막막 했는데 많은 참고가 되었다.

 

 

def gaussian_kernel(k_size, sigma):
    """
    param
    k_size : Gaussian kernel size
    sigma : gaussian kernel standard variance
    
    return
    filter = k_size * k_size gaussian filter
    """
    size = k_size//2
    y, x = np.ogrid[-size:size+1, -size:size+1]
    #ref : https://en.wikipedia.org/wiki/Gaussian_filter
    filter = 1/(2*np.pi * (sigma**2)) * np.exp(-1 *(x**2 + y**2) /(2*(sigma**2)))
    sum = filter.sum()
    filter /= sum
    return filter

 

 

 

 

 

 

내가 이전에 만든 코드에는 인덱싱 최대, 최소 범위를 지정해서 하다보니

 

이런 마스크와 이미지 부분을 컨벌루션 시킬수가 없었다.

 

이 문제를 패딩 이미지를 만들어서 해결할수 있었다.

 

def padding(img, k_size):
    """
    param
    img : padding img
    k_size : kernel size
    
    return 
    res : padded img
    """
    pad_size = k_size//2
    rows, cols, ch = img.shape
    
    res = np.zeros((rows + (2*pad_size), cols+(2*pad_size), ch), dtype=np.float)
    
    if pad_size == 0:
        res = img.copy()
    else:
        res[pad_size:-pad_size, pad_size:-pad_size] = img.copy()
    return res
    

 

 

 

 

인덱싱은 기존 이미지 사이즈 대로 인덱싱대로 되지만

 

곱 연산은 패딩 이미지와 커널을 중심으로 수행된다.

 

커널을 기존 이미지에 적용하여 이미지 밖으로 넘어가는 문제는 제거됬다.

 

def gaussian_filtering(img, k_size=3,sigma=1):
    """
    param
    img : input img
    k_size : kernel size
    sigma : standard deviation
    
    return
    filtered_img : gaussian filtered image returned
    """
    rows, cols, channels = img.shape
    filter = gaussian_kernel(k_size, sigma)
    pad_img = padding(img,k_size)
    filtered_img = np.zeros((rows, cols, channels), dtype=np.float32)
    
    for ch in range(0, channels):
        for i in range(rows):
            for j in range(cols):
                filtered_img[i, j, ch] = np.sum(filter * pad_img[i:i+k_size, j:j+k_size, ch])

    return filtered_img.astype(np.uint8)

 

 

 

 

 

 

 

 

 

마지막으로 시각화

 

커널 사이즈가 작을때에는 sigma를 크게주어도 큰 차이가 없지만

 

사이즈가 클땐 sigma 값에 따라 블러링 정도가 강해지는걸 볼수 있다.

plt.figure(figsize=(12,12))
row = 3
col = 3

for i in range(0, 3):
    for j in range(0,3):
        plt.subplot(3, 3, 1+ i*3 + j)
        k_size = 3+2*i
        sigma = 3*j + 1
        res = gaussian_filtering(img, k_size = k_size, sigma = sigma)
        title = "k size : " + str(k_size) + ",  sigma = " + str(sigma)
        plt.title(title)
        plt.imshow(res)

 

 

 

 

 

 

300x250
728x90

컴퓨터 비전에서의 연산으로

 

점 연산, 영역 연산 등이 있었던걸로 기억한다.

 

 

스무딩은 아마 영역 연산이었던것 같은데

 

이전 코드를 조금 수정해서 평균 스무딩을 구현하였다.

 

 

먼저 기본 이미지 부터 읽어보고

 

 

 

 

 

컬러 이미지를 다룰수 있도록 기존의 Histogram을 조금 수정 후 플로팅 시켰다.

 

 

 

마지막은 커널 사이즈별 블러링 차이

 

 

 

 

import numpy as np
import matplotlib.pyplot as plt
from utils import Histogram, Threshold
from PIL import Image

img = Image.open("./res/lena.jpg").convert("RGB")
img = np.asarray(img)

plt.imshow(img)


def Histogram(img):
    row, col, channels = img.shape
    hist = np.zeros((256, channels))
    for channel in range(0,channels):
        for i in range(0, row):
            for j in range(0, col):
                hist[img[i, j, channel], channel] += 1
    
    return hist

hist = Histogram(img)
plt.figure(figsize=(12,12))
plt.subplot(3,1,1)
plt.bar(np.arange(0,256), hist[:,0])
plt.subplot(3,1,2)
plt.bar(np.arange(0,256), hist[:,1])
plt.subplot(3,1,3)
plt.bar(np.arange(0,256), hist[:,2])



def meanBlur(img,  block_size=5):

    if type(img) is not np.ndarray:
        raise AssertionError("img is not ndarray")
    row, col, channels = img.shape


    res = np.zeros((row, col, channels),dtype=int)
    if (block_size % 2 == 0):
        block_size += 1
    
    for ch in range(0, channels):
        for i in range(0, row):
            for j in range(0, col):
                x_min = j-block_size//2
                x_max = j+block_size//2
                y_min = i-block_size//2
                y_max = i+block_size//2

                if x_min <= 0:
                    x_min = 0

                if x_max >= col:
                    x_max = col

                if y_min <= 0:
                    y_min = 0

                if y_max >= row:
                    y_max = row


                val = img[y_min:y_max, x_min:x_max, ch].mean()
                res[i, j, ch] = int(val)
    return res
    
    
res = meanBlur(img, block_size=3)
plt.figure(figsize=(12,12))
plt.subplot(3,1,1)
res = meanBlur(img, block_size=3)
plt.imshow(res)
plt.subplot(3,1,2)
res = meanBlur(img, block_size=5)
plt.imshow(res)
plt.subplot(3,1,3)
res = meanBlur(img, block_size=9)
plt.imshow(res)
300x250
728x90

 

파이토치과정 - 6. 구글드라이브,코랩에서 kaggle-api연동

ref : throwexception.tistory.com/1018

 

 

일단 캐글 api로 데이터 부터 준비하자

 

다운되면 압축 풀고 로드 준비

!kaggle competitions download -c santander-customer-satisfaction

 

 

일단 필요한 모듈들과 데이터부터 로드하자

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from lightgbm import LGBMClassifier
from sklearn.model_selection import GridSearchCV

path = "./res/santander-customer-satisfaction/"
test = pd.read_csv(path+"test.csv")
train = pd.read_csv(path+"train.csv")

 

 

일단 info를 보면

 

총 76020개, 371개 속성

 

마지막 컬럼이 타겟으로 되어있다.

 

 

결측치가 존재하는지 보려고, 했으나

 

속성이 너무많아 잘 보이지가 않는다.

 

 

 

sum에 또 sum해보니 없다고 한다.

 

일단 타겟으로 만족여부를 살펴보자.

 

불만족 고객이 매우 적어보인다.

 

이 문제에서 대부분 고객들이 만족하므로 정확도 대신 ROC-AUC를 사용하여야한다.

train["TARGET"].value_counts()

 

 

 

lgbm 모델로

 

accuracy_score와 roc_auc_score를 살펴보았는데

 

roc-auc score가 많이 낮더라

 

데이터 전처리를 제대로 안해서 그런것같다.

 

from sklearn.metrics import roc_auc_score,accuracy_score
from sklearn.model_selection import train_test_split

X = train.iloc[:,:-1]
y = train.iloc[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

lgbm = LGBMClassifier()
lgbm.fit(X_train, y_train)
y_pred = lgbm.predict(X_test)

print("accuracy Score : {}".format(accuracy_score(y_test, y_pred)))
print("ROC-AUC Score : {}".format(roc_auc_score(y_test, y_pred)))

 

 

 

전처리 단계에서

 

결측치를 확인했다.

 

트리 모델이니 스케일링은 필요없고,

 

이상치 문제인것같았다.

 

train.describe()로 다시 살펴보았더니 일부 기술통계량이 이상한 값들이 존재한다.

 

var3의 경우 min인 -9999999인 데이터가 존재하는데 평균과 표준편차를 고려하면 너무 엇나간 값이다.

 

imp_ent_var16_ult1도 마찬가지다.

 

 

 

 

 

 

이상치를 다루는 방법은 제거하거나 이 값을 대치시켜주어야 한다.

 

한 변수에 이상치가 많으면 제거 대신, 평균을 구하여 넣어주곤 한다.

 

평균 +- 3 * std를 기준으로 이상치 갯수부터 살펴보자

 

var3 변수의 경우 이상치가 116개가 존재한다.

 

var = train["var3"]
var_mean = train["var3"].mean()
var_std = train["var3"].std()

lower_bound_idx = var < var_mean - 3* var_std
upper_bound_idx = var > var_mean + 3* var_std

print(lower_bound_idx.sum())
print(upper_bound_idx.sum())

 

이 외에 다른 변수들은 이상치가 몇개씩 있을까 확인해보자

 

mean +- 3* std를 넘어가는걸 아웃라이어로 볼때

 

이상치가 100개 넘어가는 변수들이 총 187개가 있다고 한다.

 

num_over_th = 0
num_outlier_th = 100
columns = train.columns
for col in columns:
    var = train[col]
    var_mean = train[col].mean()
    var_std = train[col].std()

    lower_bound_idx = var < var_mean - 3* var_std
    upper_bound_idx = var > var_mean + 3* var_std

    num_outliers = lower_bound_idx.sum() + upper_bound_idx.sum()
    if num_outliers > num_outlier_th:
        num_over_th += 1
        print("  columns : {}".format(col))
        print("    -> num of oulier : {}\n".format(num_outliers))

print("num over th : []".format(num_over_th))

 

 

 

내가 중간에 빠트린 작업이 있는데

 

"ID"열을 삭제 해주지 않았었다.

 

할일 다시 정리하면

 

1. ID열을 삭제하고

2. 각 열의 아웃라이어들을 해당 열의 최빈값으로 바꿔주자

 

 

아웃라이어 대치 전후를 비교할수 있도록

 

 

우선 바꾸기전에 한번 결과보고

 

아웃라이어를 바꾼 후 결과를 보겠다.

 

 

 

 

 

 

정확도는 0.9632333596421995

roc-auc는 0.8345281581059178

describe()한결과 이상치들이 많아보인다.

 

 

X.drop(columns="ID", inplace=True)


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

lgbm = LGBMClassifier()
lgbm.fit(X_train, y_train)

print("before replace outlier")
print("accuracy Score : {}".format(accuracy_score(y_test, lgbm.predict(X_test))))
print("ROC-AUC Score : {}\n".format(roc_auc_score(y_test, lgbm.predict_proba(X_test)[:, 1])))


X.describe()

 

 

 

 

 

최빈값으로 대치한 결과

 

describe()를 보면 이상치들이 많이 줄어든걸 볼수 있지만

 

정확도와 roc-auc가 많이 줄어들었다.

 

교차 검증이 필요해보인다.

 

 

columns = X.columns
for col in columns:
    var = X[col]
    var_mean = X[col].mean()
    var_std = X[col].std()

    lower_bound_idx = var < var_mean - 5* var_std
    upper_bound_idx = var > var_mean + 5* var_std
    X.loc[lower_bound_idx, col] = X[col].mode()
    X.loc[upper_bound_idx, col] = X[col].mode()



X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

lgbm = LGBMClassifier()
lgbm.fit(X_train, y_train)
print("after replace outlier")
print("accuracy Score : {}".format(accuracy_score(y_test, lgbm.predict(X_test))))
print("ROC-AUC Score : {}".format(roc_auc_score(y_test, lgbm.predict_proba(X_test)[:, 1])))

X.describe()

 

 

 

 

 

 

아까 0.83에서 0.82로 떨어진것보다 정도는 덜하지만

 

여전히 전처리 후 더 줄어들어 있다.

 

아웃라이어를 너무 많이 잡아서 그런것 같아 

 

5 * std 대신 100* std로 심각한 아웃라이어들만 대치시키도록 바꿔보았다.

from sklearn.model_selection import cross_val_score

X = train.iloc[:,:-1]
X.drop(columns="ID", inplace=True)
lgbm = LGBMClassifier()

scores = cross_val_score(lgbm, X, y, cv= 5, scoring="roc_auc")
print("before replace outlier")
print("ROC-AUC Score : {}\n".format(np.mean(scores)))



columns = X.columns
for col in columns:
    var = X[col]
    var_mean = X[col].mean()
    var_std = X[col].std()

    lower_bound_idx = var < var_mean - 5* var_std
    upper_bound_idx = var > var_mean + 5* var_std
    X.loc[lower_bound_idx, col] = X[col].mode()
    X.loc[upper_bound_idx, col] = X[col].mode()



lgbm = LGBMClassifier()
scores = cross_val_score(lgbm, X, y, cv= 5, scoring="roc_auc")
print("after replace outlier")
print("ROC-AUC Score : {}\n".format(np.mean(scores)))

 

 

 

위 코드에서 5를 100으로 바꿧더니

 

아까보다 전처리 후 성능은 좋아졌지만

 

전처리 전보다 떨어지는건 여전하다..

 

describe()로 봤더니

 

100이 너무 커 아웃라이어들을 너무 많이 놓쳣더라

 

대신 15 정도로 바꿔 보았다.

 

    lower_bound_idx = var < var_mean - 100* var_std
    upper_bound_idx = var > var_mean + 100* var_std

 

 

 

 

또 떨어졌다..

    lower_bound_idx = var < var_mean - 15* var_std
    upper_bound_idx = var > var_mean + 15* var_std

 

 

 

std * 300으로 바꿔주었더니

 

바꾸기 전이랑 동일한 결과가 나왔다.

    lower_bound_idx = var < var_mean - 300* var_std
    upper_bound_idx = var > var_mean + 300* var_std

 

 

ID 제거하는것 빼고

 

전처리를 안한 결과를 봐도

 

성능 변화가 없는걸 봐서

* ID 같은 정말 의미없는 데이터를 없애는것 빼곤

 

LightGBM에서 전처리는 큰의미가 없는것 같았다.

X = train.iloc[:,:-1]
X.drop(columns="ID", inplace=True)
lgbm = LGBMClassifier()

scores = cross_val_score(lgbm, X, y, cv= 5, scoring="roc_auc")
print("ROC-AUC Score : {}\n".format(np.mean(scores)))

300x250
728x90

LightGBM 

- GBM Gradient Boost Machine이나, XGBoost보다 학습 시간이 적은 모델

- 데이터가 적은 경우(10000개 이하) 과적합이 발생하기 쉬움

- 기존의 트리 기반 알고리즘들은 균형 트리 분할 방식을 따르나 LightGBM은 리프위주 분할을 함.

 -> 손실이 큰 리프 노드를 분할해나감. 트리구조가 비대칭적이나 손실을 최소화할수 있음.

- XGBoost와 마찬가지로 병렬 처리 수행 가능.

 

 

LightGBM 하이퍼 파라미터

- num_iteration : 디폴트 100, 반복 수행하는 트리 개수

- learning_rate : 디폴트 0.1

- max_depth : 디폴트 -1

- min_data_in_leaf : 디폴트 20, 리프노드 최소 데이터 개수

- num_leaves : 디폴트 31, 한 트리의 최대 리프개수 등

=> num_leaves를 중심으로, min_data_in_leaf, max_depth 위주로 하이퍼파라미터 조정

 

 

 

 

pip로 설치하여

 

유방암 데이터를 교차 검증 수행

!pip install lightgbm

from lightgbm import LGBMClassifier
import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score

data = load_breast_cancer()
X = data.data
y = data.target

lgbm = LGBMClassifier()
res = cross_val_score(lgbm, X, y, cv= 5, n_jobs=-1, scoring="accuracy")
print(res)
print(np.mean(res))

 

평균 정확도 0.968

 

300x250
728x90

시계열 분석

- 시계열 : 시간의 변화에 따라 변동하는 변수들

- 시계열 분석 : 시계열 데이터를 이용하여 예측하는 방법

- 접근 방법 : 시간영역분석, 주파수 영역 분석

- 정상성 : 시간에 따라 변동해도 분산이 일정한 성질

 

시계열 데이터의 패턴

- 추세 : 데이터가 점점 증가 혹은 감소해나가는 형태

- 계절성 : 계절에 따라 영향을 받는 패턴

- 주기성 : 일정 주기를 갖는 패턴 ex. 경졔

- 자기 상관 : 시계열 데이터 사이 선형적사이 선형적 관계

 

 

시계열 분석 모형

1. 선형 회귀 모형

- 아래는 단순 선형회귀 모형과 다중 선형회귀모형

- 최소 제곱법을 통해 구함. 오차 제곱 합이 최소가 되는 beta들을 구하여야함.

 

2. 자기회귀 모형 AR Auto Correlation 모형

- 현 시점을 과거 데이터들의 선형 결합으로 구하는 모형

 

3. 이동평균 모형 MA Moving Average 모형

- 각 시간들의 데이터의 평균으로 현 시점을 추정하는 모형

 

4. 자기회귀 평균이동 모형 ARMA Auto Regressive Moving Average 모형

- AR, MA모형의 결합으로 시간별 평균 데이터로 회귀로 추정하는 모형

 

5. 자기회귀 누적이동평균 모형 ARIMA Auto Regressive Intergrated Moving Average 모형

- ARMA 모델은 ARMA 모델에 적분 연산을 통해 과거 데이터들의 추세성 까지 반영한 모델

300x250
728x90

다음 링크에서 오츠 이진화에 대해 잘 정리하여 참고

 

medipixel.github.io/post/2019-06-07-image-thresholding/

 

 

* 본 내용을 정리하면서 앞의 일부 내용들은 엉망이라 유의하자.

 

오츠 이진화

- 최적의 임계치를 찾아 영상을 이진화하는 알고리즘

- 흑색 영상과 백색 영상의 분산 가중 합을 가장 적게하는 임계치가 최적 임계치

- 위 내용을 대강 그림으로 그리면 히스토그램에 대해서 다음과 같다

 

인줄 알았는데

 

해당 블로그에서 hat h(t)에 대한 설명부분이나, between class variance에 대한 설명이 빠져있어

 

조금 해매버렸다.

 

 

 

 

이 블로그도 조금은 잘못됬지만 대강

 

j07051.tistory.com/364

 

클래스내 분산 within class variance와 클래스간 분산 between class variance에 대해서 보다 잘 설명해주고 있다.

 

다만 틀린 부분은 

 

클래스 내 분산이 최소가 되도록 하는 임계치 t의 인덱스가 오츠 임계값이고,

 

이는 클래스간 분산이 최대가 되는것과 동일한 의미이다.

 

 

 

 

오츠 임계치를 다시 정리하면,

 

클래스 내 분산이 최소가 되는 인덱스나

 

클래스 간 분산이 최대가 되는 인덱스를 찾는 것이고

 

이 인덱스가 가장 잘 영상을 이진화 시키는 오츠 임계치 값이 된다.

 

 

이런 식으로 클래스 간 분산이 최대(클래스 내 분산이 최소)로 분할하는 임계치이다.

 

 

아래의 코드는 내가 구현한 오츠 임계치 함수인데

def otsuThreshold(img):
    
    if type(img) is not np.ndarray:
        raise AssertionError("img is not ndarray")
    
    hist = Histogram(img)
    
    vars_within = []
    vars_between = []

    zero = 1.e-17
    for t in range(0, 256):
        sumb = np.sum(hist[:t]) + zero
        sumw = np.sum(hist[t:]) + zero
        sum = sumb + sumw
        wb = sumb/sum
        ww = sumw/sum
        
        mub = zero
        muw = zero
        for i in range(0, t):
            mub += i * hist[i]/sumb
        for i in range(t, 256):
            muw += i * hist[i]/sumw

        vb = zero
        vw = zero
        for i in range(0, t):
            vb += hist[i] * ((i - mub)**2)/sumb
        for i in range(t, 256):
            vw += hist[i] * ((i - muw)**2)/sumw
    
        var_within = wb * vb + ww * vw
        vars_within.append(var_within)


    th = vars_within.index(min(vars_within))
    print(th)
    res = Threshold(img, th)
    return res

res = otsuThreshold(img)

 

위 함수를 사용한 결과 오츠 임계치는 96가 되며 아래의 이미지를 얻었다.

 

 

 

 

 

히스토그램으로 보면 가장 흑백 영상 클래스가 잘 분할하도록 분포하고 있다.

 

* 위 이미지 처리 관련 코드들은 사이킷런에서도 잘 구현되 있었다.

* 내것보다 유효성 검증, cumsum 같은 함수 사용, 인덱싱 처리에서 훨씬 간결하다.

ref : github.com/scikit-image/scikit-image/blob/master/skimage/filters/thresholding.py#L237

 

 

아무튼 이론과 구현에 대한 내용들은

 

learning opencv에서 잘 제공하고 있었다

 

www.learnopencv.com/otsu-thresholding-with-opencv/

 

 

300x250
728x90

부스팅 기법

- 여러개의 약분류기를 순차 적으로 배치

- 앞의 모델에서 틀린 데이터에 가중치를 주어 이후 분류기에서 더 잘 분류할수 있게 도움

- 약 분류기의 분류 기준과 가중치들을 결합하여 강한 분류기를 만듬.

=> 아다부스트

 

 

GBM Gradient Boost Machine

- 아다부스트와 동일하나 가중치 갱신을 경사 하강법을 이용.

- 랜덤포래스트와 달리 가중치 갱신을 순차적으로 수행하여 병렬처리가 불가함

 => 좋은 성능을 보이나, 학습시 오랜 시간이 걸림.

 * 랜덤 포레스트로 우선 모델을 만들기도 함.

 

 

GBM으로 사용자 행동 인식 분류하기

 

이전과 동일하게 사용한 결과

gbm acc : 0.9379029521547336로

랜덤 포래스트보다 개선된 성능을 보임.

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings("ignore")

path = "./res/UCI HAR Dataset/features.txt"

features_df = pd.read_csv(path,sep="\s+",
                         header=None, names=["column_index", "column_name"])

feature_names = features_df["column_name"].values.tolist()

path = "./res/UCI HAR Dataset/"

X_train = pd.read_csv(path+"train/X_train.txt",sep="\s+", names=feature_names)
X_test = pd.read_csv(path+"test/X_test.txt",sep="\s+", names=feature_names)


y_train = pd.read_csv(path+"train/y_train.txt", sep="\s+", header=None,
                     names=["action"])

y_test = pd.read_csv(path+"test/y_test.txt", sep="\s+", header=None,
                     names=["action"])
                     
gbm = GradientBoostingClassifier(random_state=100)
gbm.fit(X_train, y_train)
y_pred = gbm.predict(X_test)
print("gbm acc : {}".format(accuracy_score(y_test, y_pred)))

300x250
728x90

랜덤 포레스트

- 배깅 알고리즘 중 하나로 비교적 빠르며, 높은 성능을 보이고 있음.

- 배깅 알고리즘인 만큼 결정 트리 기반으로 하며, 많은 양의 트리를 이용하여 편향-분산을 잘 상쇄시킴.

 

 

 

 

랜덤 포레스트 모델로

 

이전에 결정트리로 풀어본 사용자 행동 인식 데이터셋을 다뤄보겠습니다.

 

ref : throwexception.tistory.com/1039

 

일단 단순하게 성능을 확인해봅시다.

 

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

path = "./res/UCI HAR Dataset/features.txt"

features_df = pd.read_csv(path,sep="\s+",
                         header=None, names=["column_index", "column_name"])

feature_names = features_df["column_name"].values.tolist()

path = "./res/UCI HAR Dataset/"

X_train = pd.read_csv(path+"train/X_train.txt",sep="\s+", names=feature_names)
X_test = pd.read_csv(path+"test/X_test.txt",sep="\s+", names=feature_names)


y_train = pd.read_csv(path+"train/y_train.txt", sep="\s+", header=None,
                     names=["action"])

y_test = pd.read_csv(path+"test/y_test.txt", sep="\s+", header=None,
                     names=["action"])



rf = RandomForestClassifier(random_state=100)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
print("rf acc : {}".format(accuracy_score(y_test, y_pred)))

 

 

기본 상태로 랜덤 포레스트의 성능이 0.9015948422124194으로 나오고 있습니다.

 

 

중요한 변수들을 한번 살펴봅시다.

 

워낙 변수들이 많아서인지 각 변수들의 중요도가 매우 작은 값들을 가지고 있습니다.

ftr_importance_val = rf.feature_importances_
ftr_importances = pd.Series(ftr_importance_val, index=X_train.columns)
ftr_top30 = ftr_importances.sort_values(ascending=False)[:30]

sns.barplot(x=ftr_top30, y=ftr_top30.index)

 

 

 

성능 개선을 위해 

 

gridsearchcv로 한번 최적 파라미터를 찾아봅시다.

 

그리드 탐색 결과를 보면

 

 

max depth : 10

min samples leaf 18

min samples split 12일때

 

아까 정확도가 0.9015948422124194 나온것 보다는 

 

아주 약간 개선된 0.910092가 나옵니다.

 

이 부근으로 탐색을 더해보면 더 나은 하이퍼 파라미터들을 찾을수 있을것같습니다.

* 모델이나 gridsearchcv의 파라미터로  n_jobs=-1로 설정하면 모든 CPU 코어를 사용하게 됨.

from sklearn.model_selection import GridSearchCV

param = {
    "min_samples_leaf": [4, 12, 18],
    "min_samples_split": [6, 12],
    "max_depth" :[10, None]
}

rf = RandomForestClassifier(random_state=100, n_jobs=1)
gs = GridSearchCV(rf, param_grid=param, cv=3, n_jobs=-1)
gs.fit(X_train, y_train)


res_df = pd.DataFrame.from_dict(gs.cv_results_)
res_df[['param_max_depth', 'param_min_samples_leaf', 'param_min_samples_split' ,'mean_test_score']]

 

위 표를 보면 min samples leaf가 커질수록 성능이 개선되었고,

 

min samples split은 6, 12이든 별 차이가 나지는 않았습니다.

 

하지만 max_depth는 None보다 10일때 타 파라미터 동일 조건에서 더 좋은 성능이 나왔으니

 

 

 

min samples split은 빼고

 

대신 n_estimators를 추가하여

 

다음과 같이 파라미터를 조정하여 돌려보았습니다.

 

param = {
    "min_samples_leaf": [18, 25, 30],
    "max_depth" :[10, 15],
    "n_estimators" :[50, 100, 200]
}

rf = RandomForestClassifier(random_state=100, n_jobs=1)
gs = GridSearchCV(rf, param_grid=param, cv=3, n_jobs=-1)
gs.fit(X_train, y_train)


res_df = pd.DataFrame.from_dict(gs.cv_results_)
res_df[['param_min_samples_leaf','param_max_depth', 'param_n_estimators' ,'mean_test_score']]

 

결과는

 

min samples leaf : 25

max depth : 10

n estimators : 100

 

일때 

 

최대 스코어가 0.917982로 나왔습니다.

 

이전 성능인 0.910092보다 0.007정도 개선되었습니다.

 

 

 

 

일단 대체로 보면 n estimator 100부근에서 좋은 성능이 나왔고

 

min samples leaf는 타 파라미터에따라 조금씩 바뀌는것 같습니다.

 

max depth의 경우 더 깊을수록 성능이 떨어지고 있습니다.

 

 

다시 하이퍼 파라미터를 다음과 같이 조정해서 동작시켜보았습니다.

 

 

param = {
    "min_samples_leaf": [21, 24, 27],
    "max_depth" :[4, 6, 8],
    "n_estimators" :[80, 100, 120]
}

rf = RandomForestClassifier(random_state=100, n_jobs=1)
gs = GridSearchCV(rf, param_grid=param, cv=3, n_jobs=-1)
gs.fit(X_train, y_train)


res_df = pd.DataFrame.from_dict(gs.cv_results_)
res_df[['param_min_samples_leaf','param_max_depth', 'param_n_estimators' ,'mean_test_score']]

 

최대 스코어가 0.914로 이전보다 0.003정도 줄어들었습니다.

 

 

 

이번에는 최대 특징 개수를 추가했습니다.

 

param = {
    "min_samples_leaf": [25, 27],
    "max_depth" :[9, 10, 11],
    "n_estimators" :[100],
    "max_features": [200, "auto"]
}

rf = RandomForestClassifier(random_state=100, n_jobs=1)
gs = GridSearchCV(rf, param_grid=param, cv=3, n_jobs=-1)
gs.fit(X_train, y_train)


res_df = pd.DataFrame.from_dict(gs.cv_results_)
res_df[['param_min_samples_leaf','param_max_depth', 'param_max_feature' ,'mean_test_score']]

 

아까와 동일하게

 

min samples leaf 25

 

max depth 10

 

n estimator 100

 

일때와 스코어가 0.917982로 가장 좋은 성능을 보였습니다.

 

이전에 결정 트리로 0.86425 나왔을때보다는 랜덤 포레스트 모델을 통해 분류 성능을 크개 개선할수 있었습니다.

 

 

300x250
728x90

사이킷런의 데이터셋 모델에서

 

위스콘신 유방암 데이터셋을 제공하고 있습니다.

 

 

 

voting classifier를 사용할 건데

 

여기서 로지스틱 회귀 모델, K최근접 이웃 이 두 가지를 사용하겠습니다.

 

일단 필요한 라이브러리들 부터 임포트 해줍시다.

 

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.ensemble import VotingClassifier
from sklearn.metrics import accuracy_score
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

 

 

load_breast_cancer()로 유방암 관련 데이터를 읽어줍시다.

 

sklearn.dataset에서 load 혹은 fetch 함수로 가져온 데이터들은

 

sklearn.utils.Bunch 타입으로

 

이 데이터는 기본적으로

 

데이터셋에 대한 설명인 DESCR, 타겟변수명 target_names, 피처명 feature_names, data, target 등 변수들을

 

가지고 있습니다.

 

description을 읽어보면

 

데이터수는 569개

 

속성은 30개 정도 된다고 합니다.

 

타겟은 악성, 양성 종양 2개

cancer = load_breast_cancer()
print(type(cancer))

 

 

 

bunch 클래스 내용을 참고해서 바로 데이터프레임으로 만들어줍시다.

 

df = pd.DataFrame(data=cancer.data, columns=cancer.feature_names)
df["class"] = cancer.target
df.head()

 

데이터들을 분리해주고

 

모델들을 만들어줍시다.

 

voting_classifier(분류기 리스트, voting="보팅기법")으로 선언해주어야 합니다.

 

X_train, X_test, y_train, y_test = train_test_split(df.iloc[:,:-1], df.iloc[:,-1],test_size=0.2)


lr = LogisticRegression()
knn = KNeighborsClassifier()

vo = VotingClassifier([("lr", lr), ("knn", knn)], voting="soft")

vo.fit(X_train, y_train)
y_pred = vo.predict(X_test)

print("acc : {}".format(accuracy_score(y_test, y_pred)))

for clf in [lr, knn]:
    clf_name = clf.__class__.__name__
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    acc = accuracy_score(y_test, y_pred)
    print("{}'s acc : {}'".format(clf_name,acc))

 

분류기 정확도는 0.956140350877193

 

두 뷴류기의 성능을 보면

 

로지스틱 회귀가 0.9385964912280702

 

K최근접이웃은 0.9473684210526315으로 k 최근접 이웃이 조금 더 좋은 성능을 보이고 있습니다.

 

 

 

 

 

 

로지스틱 회귀의 경우 전처리로

 

데이터셋을 표준 정규분포로 정규화 시키면 더 좋은 성능을 보인다고 합니다.

 

preprocessing 모듈의 StandardScaler를 사용해봅시다.

 

 

보팅 분류기가 0.9561403에서 0.973684로

 

로지스틱 회귀가 0.93859에서 0.964912

 

K최근접이웃이 0.947368에서 0.973684로 조금더 성능이 개선되었습니다.

from sklearn.preprocessing import StandardScaler


scaler1 = StandardScaler()
scaler2 = StandardScaler()

X_train = scaler1.fit_transform(X_train)
X_test = scaler2.fit_transform(X_test)

lr = LogisticRegression()
knn = KNeighborsClassifier()

vo = VotingClassifier([("lr", lr), ("knn", knn)], voting="soft")

vo.fit(X_train, y_train)
y_pred = vo.predict(X_test)

print("acc : {}".format(accuracy_score(y_test, y_pred)))

for clf in [lr, knn]:
    clf_name = clf.__class__.__name__
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)
    acc = accuracy_score(y_test, y_pred)
    print("{}'s acc : {}'".format(clf_name,acc))

 

 

전처리 후 성능이 개선된건 좋지만

 

매번 할때마다 성능이 조금씩 변하고 있습니다.

 

cross_val_score() 함수로 교차검증 까지 해봅시다.

 

 

교차 검증 결과 확실히 성능이 개선됨을 알수 있었습니다.

 

VotingClassifier : 0.9753443632166217

LogisticRegression : 0.9806848787995384

KNeighborsClassifier : 0.9595075028857252

from sklearn.model_selection import cross_val_score

for clf in [vo, lr, knn]:
    scores = cross_val_score(clf, np.concatenate((X_train,X_test)), 
                             np.concatenate((y_train, y_test)),
                            scoring="accuracy", cv=5)
    print(clf.__class__.__name__)
    print(scores)
    print(np.mean(scores))
    print()

 

300x250
728x90

앙상블

- 기존의 머신러닝 모델들이 단일 모델을 이용하여 값을 추정하였다면,

- 앙상블은 여러개의 모델로부터 결과를 얻는 방법

- 보팅, 배깅, 부스팅, 스태킹 등 다양한 기법들이 있음.

 

 

보팅

- 말그대로 서로 다른 분류기들간에 투표를 통해서 결정

- 하드 보팅 : 가장 많은 표를 받은 클래스를 선정

- 소프트 보팅 : 모든 분류기들의 클래스 라벨 확률들을 다 더한후 평균을 취한 후 가장 높은 값의 클래스 선정

ex. 사이킷런에서 votingclassifier 제공

 

배깅 bagging

- boostrap aggregation의 줄임말

- 보팅과 달리 한 종류의 알고리즘을 사용하나, 부트스트랩 기법으로 서로 다른 데이터셋으로 학습시킴

 * 부트스트랩 : 기존 데이터셋에서 일부 데이터셋을 추출하여 사용. 각 모델들은 동일하지 않은 데이터셋에 학습 

ex. 랜덤 포레스트

 

 

부스팅 boosting

- 여러 개의 약 모델들을 생성하여, 연결되어 순차 학습 수행

- 잘못 예측한 데이터에 가중치를 주어, 다음 모델에서 학습/예측 수행

- 오류들을 점점 더 줄여나갈수 있도록 개선한 방식

ex. 아다부스트(adaboost : adaptive boosting), XGBoost, LightGBM

300x250

+ Recent posts