본문 바로가기
『 Python 』/AI

[AI] 분류

by Play IT 2023. 2. 7.
반응형

SGDClassifier 확률적 경사 하강법 분류기

매우 큰 데이터셋을 효율적으로 처리하는 장점

한 번에 하나씩 훈련 샘플을 독립적으로 처리하기 때문임(그러기에 온라인 학습에 잘 맞음)

훈련하는데 무작위성을 사용하기에 확률적이 붙었다.

#SGDClassifier 확률적 경사 하강법 분류기  
from sklearn.linear_model import SGDClassifier

# 결과를 똑같이 재현하기 위해서는 random_State 매개변수를 지정해주면 된다. 
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(x_train,y_train)

sgd_clf.predict([some_digit])
# 예측이 맞는지는 bool로 변환하면 알수 있다.

성능 측정

분류기 평가는 회귀 모델보다 휠씬 어렵기에 많은것을 알아가야한다.

 

# 교차 검증을 사용한 정확도 측정

교차 검증 구현 

가끔 사이킷런이 제공하는 기능보다 교차 검증 과정을 더 많이 제어할 필요가 있다. 이럴 경우 교차 검증 기능을 직접 구현하면 된다.

cross_val_score() 함수와 거의 같은 작업을 수행하고 동일한 결과를 출력합니다.

 

#교차 검증 구현
# 사이킷런의 cross_val_score()함수와 거의 같은 작업을 수행하며 동일한 경과를 출력하는 코드입니다.
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone

skfolds = StratifiedKFold(n_splits=3, random_state=42, shuffle=True)

for train_index, test_index in skfolds.split(x_train,y_train_5):
    clone_clf = clone(sgd_clf)
    x_train_folds = x_train[train_index]
    y_train_folds = y_train_5[train_index]
    x_test_folds = x_train[test_index]
    y_test_folds = y_train_5[test_index]
    
    clone_clf.fit(x_train_folds, y_train_folds)
    y_pred = clone_clf.predict(x_test_folds)
    n_correct = sum(y_pred == y_test_folds)
    print(n_correct / len(y_pred))

 

# 일반적으로 
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, x_train,y_train_5, cv=3, scoring="accuracy")

# 이미지를 5아님 클래스로 분류하는 더미 분류기를 만들어 비교해보기
from sklearn.base import BaseEstimator

class Never5Classifier(BaseEstimator):
    def fit(self, X, y=None):
        return self
    def predict(self, X):
        return np.zeros((len(X),1),dtype=bool)
    
never_5_clf = Never5Classifier()
cross_val_score(never_5_clf, x_train, y_train_5, cv=3, scoring="accuracy")

 

# 분류기의 성능을 평가하는 더 좋은방법은 오차 행렬을 조사하는 것.

 

# 오차행렬
from sklearn.model_selection import cross_val_predict

y_train_pred = cross_val_predict(sgd_clf, x_train, y_train_5, cv=3)
# val_predict은 val_score과 다르게 각 테스트 폴드에서 얻은 예측을 반환해줍니다. 
# 이제 confusiuon_matrix() 함수를 사용해 오차 행렬을 만들준비가 됬습니다.
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
# 첫행은 5아님에 대한 것과 잘못분류한것이다. 두번째 행은 5이미지에 대한 5아님으로 잘못분류, 5개는 재대로 분류했다는 개수임.
# (음성 클래스) (양성클래스)# 

# 이상적인 형태는 다음과 같습니다.
y_train_perfect_predictions = y_train_5
confusion_matrix(y_train_5, y_train_perfect_predictions)

# 분류기는 정밀도와 재현율을 지표로 사용할수 있다. 
# 사이킷런은 이를 재현하는 여러 함수를 제공한다.
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred) # == 4096 / 4096 + 1522
recall_score(y_train_5, y_train_pred) # == 4096 / (4096+1325)

# 조화 평균 F1 점수라하는 정밀도와 재현율을 하나의 숫자로 나타낼수있다.
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)

# 정밀도 재현율 어느 하나가 올라가면 어느하나는 내려가기에 정밀도/재현률 트레이드오프라고 한다. 


# 정밀도/재현률 트레이드 오프
# SGDClassifier가 분류를 어떻게 결정하는지 해당 트레이드 오프
# 해당 분류기는 결정 함수를 사용하여 각 샘플의 점수를 계산한다.
# 해당 점수가 임계값 보다 크면 샘플을 양성 클래스에 할당하며 그렇지 않을경우 음성 클래스에 할당한다. 
# p 137
# 정밀도/재현률 트레이드 오프
# SGDClassifier가 분류를 어떻게 결정하는지 해당 트레이드 오프
# 해당 분류기는 결정 함수를 사용하여 각 샘플의 점수를 계산한다.
# 해당 점수가 임계값 보다 크면 샘플을 양성 클래스에 할당하며 그렇지 않을경우 음성 클래스에 할당한다. 
# p 137 임계값이 높을수록 정밀도는 높아지지만 반면 재현률은 낮아지게 된다.

# 사이킷 런에서는 임계값을 직접 지정할수 없지만 예측에 사용한 점수는 확인할 수 있다.
y_scores = sgd_clf.decision_function([some_digit])
y_scores
threshold = 80000
y_some_degit_pred = (y_scores > threshold)
y_some_degit_pred

y_scores = cross_val_predict(sgd_clf, x_train, y_train_5, cv=3, method="decision_function")
# 적절한 임계값을 찾기위해서는 훈련 세트에 있는 모든 샘플 점수를 구해야 한다. 
# 해당 점수를 precision_recall_curve 함ㅂ수를 사용해 가능한 모든 임계값에 대한 정밀도와 재현율을 계산할 수 있습니다.
from sklearn.metrics import precision_recall_curve
precisions, recalls ,thresholds = precision_recall_curve(y_train_5, y_scores)

def plot_precision_recall_vsthreshold(precisions, recalls, thresholds):
    plt.plot(thresholds,precisions[:-1], "b--", label="정밀도")
    plt.plot(thresholds, recalls[:-1], "g-", label="재현율")
    [...] #임계값을 표시하고 범례 축 이름 그리드를 추가합니다.

plot_precision_recall_vsthreshold(precisions,recalls,thresholds)
plt.show()


threshold_90_precision = thresholds[np.argmax(precisions >= 0.90)]
y_train_pred_90 = (y_scores >= threshold_90_precision)
precision_score(y_train_5, y_train_pred_90)
recall_score(y_train_5, y_train_pred_90)

 

전체코드는 다음과 같다.

import tensorflow as tf
from tensorflow import keras

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
mnist.keys()

x, y = mnist["data"], mnist["target"]
# shape으로 구조를 살펴보면 이미지는 0(흰색) 255(검은색)까지의 픽셀 강도를 나타내는 것이 7만개, 28x28픽셀 784개의 특성을 지님을 확인할 수 있다.
x.shape
y.shape

import matplotlib as mpl
import matplotlib.pyplot as plt

some_digit = x[0]
some_digit_image = some_digit.reshape(28,28)

plt.imshow(some_digit_image, cmap="binary")
plt.axis("off")
plt.show()


y[0]

# 알고리즘은 숫자에 대부분 기대므로 정수로 변환시켜주기
import numpy as np
y= y.astype(np.uint8)

x_train, x_test, y_train, y_test = x[:60000], x[60000:], y[:60000], y[60000:]

# 이진 분류기 예시
y_train_5 = (y_train == 5)
y_test_5 = (y_test == 5)

#SGDClassifier 확률적 경사 하강법 분류기  
from sklearn.linear_model import SGDClassifier

# 결과를 똑같이 재현하기 위해서는 random_State 매개변수를 지정해주면 된다. 
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(x_train,y_train)

sgd_clf.predict([some_digit])
# 예측이 맞는지는 bool로 변환하면 알수 있다.

#교차 검증 구현
# 사이킷런의 cross_val_score()함수와 거의 같은 작업을 수행하며 동일한 경과를 출력하는 코드입니다.
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone

skfolds = StratifiedKFold(n_splits=3, random_state=42, shuffle=True)

for train_index, test_index in skfolds.split(x_train,y_train_5):
    clone_clf = clone(sgd_clf)
    x_train_folds = x_train[train_index]
    y_train_folds = y_train_5[train_index]
    x_test_folds = x_train[test_index]
    y_test_folds = y_train_5[test_index]
    
    clone_clf.fit(x_train_folds, y_train_folds)
    y_pred = clone_clf.predict(x_test_folds)
    n_correct = sum(y_pred == y_test_folds)
    print(n_correct / len(y_pred))
    
    
# 일반적으로 
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, x_train,y_train_5, cv=3, scoring="accuracy")

# 이미지를 5아님 클래스로 분류하는 더미 분류기를 만들어 비교해보기
from sklearn.base import BaseEstimator

class Never5Classifier(BaseEstimator):
    def fit(self, X, y=None):
        return self
    def predict(self, X):
        return np.zeros((len(X),1),dtype=bool)
    
never_5_clf = Never5Classifier()
cross_val_score(never_5_clf, x_train, y_train_5, cv=3, scoring="accuracy")



# 오차행렬
from sklearn.model_selection import cross_val_predict

y_train_pred = cross_val_predict(sgd_clf, x_train, y_train_5, cv=3)
# val_predict은 val_score과 다르게 각 테스트 폴드에서 얻은 예측을 반환해줍니다. 
# 이제 confusiuon_matrix() 함수를 사용해 오차 행렬을 만들준비가 됬습니다.
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
# 첫행은 5아님에 대한 것과 잘못분류한것이다. 두번째 행은 5이미지에 대한 5아님으로 잘못분류, 5개는 재대로 분류했다는 개수임.
# (음성 클래스) (양성클래스)# 

# 이상적인 형태는 다음과 같습니다.
y_train_perfect_predictions = y_train_5
confusion_matrix(y_train_5, y_train_perfect_predictions)

# 분류기는 정밀도와 재현율을 지표로 사용할수 있다. 
# 사이킷런은 이를 재현하는 여러 함수를 제공한다.
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred) # == 4096 / 4096 + 1522
recall_score(y_train_5, y_train_pred) # == 4096 / (4096+1325)

# 조화 평균 F1 점수라하는 정밀도와 재현율을 하나의 숫자로 나타낼수있다.
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)

# 정밀도 재현율 어느 하나가 올라가면 어느하나는 내려가기에 정밀도/재현률 트레이드오프라고 한다. 


# 정밀도/재현률 트레이드 오프
# SGDClassifier가 분류를 어떻게 결정하는지 해당 트레이드 오프
# 해당 분류기는 결정 함수를 사용하여 각 샘플의 점수를 계산한다.
# 해당 점수가 임계값 보다 크면 샘플을 양성 클래스에 할당하며 그렇지 않을경우 음성 클래스에 할당한다. 
# p 137 임계값이 높을수록 정밀도는 높아지지만 반면 재현률은 낮아지게 된다.

# 사이킷 런에서는 임계값을 직접 지정할수 없지만 예측에 사용한 점수는 확인할 수 있다.
y_scores = sgd_clf.decision_function([some_digit])
y_scores
threshold = 80000
y_some_degit_pred = (y_scores > threshold)
y_some_degit_pred

y_scores = cross_val_predict(sgd_clf, x_train, y_train_5, cv=3, method="decision_function")
# 적절한 임계값을 찾기위해서는 훈련 세트에 있는 모든 샘플 점수를 구해야 한다. 
# 해당 점수를 precision_recall_curve 함ㅂ수를 사용해 가능한 모든 임계값에 대한 정밀도와 재현율을 계산할 수 있습니다.
from sklearn.metrics import precision_recall_curve
precisions, recalls ,thresholds = precision_recall_curve(y_train_5, y_scores)

def plot_precision_recall_vsthreshold(precisions, recalls, thresholds):
    plt.plot(thresholds,precisions[:-1], "b--", label="정밀도")
    plt.plot(thresholds, recalls[:-1], "g-", label="재현율")
    [...] #임계값을 표시하고 범례 축 이름 그리드를 추가합니다.

plot_precision_recall_vsthreshold(precisions,recalls,thresholds)
plt.show()


threshold_90_precision = thresholds[np.argmax(precisions >= 0.90)]
y_train_pred_90 = (y_scores >= threshold_90_precision)
precision_score(y_train_5, y_train_pred_90)
recall_score(y_train_5, y_train_pred_90)

 

ROC곡선

수신기조작특성 ROC 곡선도 이진 분류에서 널리 사용되는 도구이다.

정밀도 재현율 곡선과 매우 비슷하지만 속은 다르다

거짓양성비율 FPR에 대한 진짜 양성비율TPR 곡선이다.  양성으로 잘못 분류된 샘플의 비율은 FPR입니다.

이는 1에서 음성으로 정확하게 분류한 음성 샘플의 비율인 진짜음성 비율을 뺀 값이다.

1-특이도 그래프?

 

import tensorflow as tf
from tensorflow import keras

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
mnist.keys()

x, y = mnist["data"], mnist["target"]
# shape으로 구조를 살펴보면 이미지는 0(흰색) 255(검은색)까지의 픽셀 강도를 나타내는 것이 7만개, 28x28픽셀 784개의 특성을 지님을 확인할 수 있다.
x.shape
y.shape

import matplotlib as mpl
import matplotlib.pyplot as plt

some_digit = x[0]
some_digit_image = some_digit.reshape(28,28)

plt.imshow(some_digit_image, cmap="binary")
plt.axis("off")
plt.show()


y[0]

# 알고리즘은 숫자에 대부분 기대므로 정수로 변환시켜주기
import numpy as np
y= y.astype(np.uint8)

x_train, x_test, y_train, y_test = x[:60000], x[60000:], y[:60000], y[60000:]

# 이진 분류기 예시
y_train_5 = (y_train == 5)
y_test_5 = (y_test == 5)

#SGDClassifier 확률적 경사 하강법 분류기  
from sklearn.linear_model import SGDClassifier

# 결과를 똑같이 재현하기 위해서는 random_State 매개변수를 지정해주면 된다. 
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(x_train,y_train)

sgd_clf.predict([some_digit])
# 예측이 맞는지는 bool로 변환하면 알수 있다.

#교차 검증 구현
# 사이킷런의 cross_val_score()함수와 거의 같은 작업을 수행하며 동일한 경과를 출력하는 코드입니다.
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone

skfolds = StratifiedKFold(n_splits=3, random_state=42, shuffle=True)

for train_index, test_index in skfolds.split(x_train,y_train_5):
    clone_clf = clone(sgd_clf)
    x_train_folds = x_train[train_index]
    y_train_folds = y_train_5[train_index]
    x_test_folds = x_train[test_index]
    y_test_folds = y_train_5[test_index]
    
    clone_clf.fit(x_train_folds, y_train_folds)
    y_pred = clone_clf.predict(x_test_folds)
    n_correct = sum(y_pred == y_test_folds)
    print(n_correct / len(y_pred))
    
    
# 일반적으로 
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, x_train,y_train_5, cv=3, scoring="accuracy")

# 이미지를 5아님 클래스로 분류하는 더미 분류기를 만들어 비교해보기
from sklearn.base import BaseEstimator

class Never5Classifier(BaseEstimator):
    def fit(self, X, y=None):
        return self
    def predict(self, X):
        return np.zeros((len(X),1),dtype=bool)
    
never_5_clf = Never5Classifier()
cross_val_score(never_5_clf, x_train, y_train_5, cv=3, scoring="accuracy")



# 오차행렬
from sklearn.model_selection import cross_val_predict

y_train_pred = cross_val_predict(sgd_clf, x_train, y_train_5, cv=3)
# val_predict은 val_score과 다르게 각 테스트 폴드에서 얻은 예측을 반환해줍니다. 
# 이제 confusiuon_matrix() 함수를 사용해 오차 행렬을 만들준비가 됬습니다.
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
# 첫행은 5아님에 대한 것과 잘못분류한것이다. 두번째 행은 5이미지에 대한 5아님으로 잘못분류, 5개는 재대로 분류했다는 개수임.
# (음성 클래스) (양성클래스)# 

# 이상적인 형태는 다음과 같습니다.
y_train_perfect_predictions = y_train_5
confusion_matrix(y_train_5, y_train_perfect_predictions)

# 분류기는 정밀도와 재현율을 지표로 사용할수 있다. 
# 사이킷런은 이를 재현하는 여러 함수를 제공한다.
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred) # == 4096 / 4096 + 1522
recall_score(y_train_5, y_train_pred) # == 4096 / (4096+1325)

# 조화 평균 F1 점수라하는 정밀도와 재현율을 하나의 숫자로 나타낼수있다.
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)

# 정밀도 재현율 어느 하나가 올라가면 어느하나는 내려가기에 정밀도/재현률 트레이드오프라고 한다. 


# 정밀도/재현률 트레이드 오프
# SGDClassifier가 분류를 어떻게 결정하는지 해당 트레이드 오프
# 해당 분류기는 결정 함수를 사용하여 각 샘플의 점수를 계산한다.
# 해당 점수가 임계값 보다 크면 샘플을 양성 클래스에 할당하며 그렇지 않을경우 음성 클래스에 할당한다. 
# p 137 임계값이 높을수록 정밀도는 높아지지만 반면 재현률은 낮아지게 된다.

# 사이킷 런에서는 임계값을 직접 지정할수 없지만 예측에 사용한 점수는 확인할 수 있다.
y_scores = sgd_clf.decision_function([some_digit])
y_scores
threshold = 80000
y_some_degit_pred = (y_scores > threshold)
y_some_degit_pred

y_scores = cross_val_predict(sgd_clf, x_train, y_train_5, cv=3, method="decision_function")
# 적절한 임계값을 찾기위해서는 훈련 세트에 있는 모든 샘플 점수를 구해야 한다. 
# 해당 점수를 precision_recall_curve 함ㅂ수를 사용해 가능한 모든 임계값에 대한 정밀도와 재현율을 계산할 수 있습니다.
from sklearn.metrics import precision_recall_curve
precisions, recalls ,thresholds = precision_recall_curve(y_train_5, y_scores)

def plot_precision_recall_vsthreshold(precisions, recalls, thresholds):
    plt.plot(thresholds,precisions[:-1], "b--", label="정밀도")
    plt.plot(thresholds, recalls[:-1], "g-", label="재현율")
    [...] #임계값을 표시하고 범례 축 이름 그리드를 추가합니다.

plot_precision_recall_vsthreshold(precisions,recalls,thresholds)
plt.show()


threshold_90_precision = thresholds[np.argmax(precisions >= 0.90)]
y_train_pred_90 = (y_scores >= threshold_90_precision)
precision_score(y_train_5, y_train_pred_90)
recall_score(y_train_5, y_train_pred_90)


from sklearn.metrics import roc_curve

fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)

def plot_roc_curve(fpr, tpr, label=None):
    plt.plot(fpr,tpr,linewidth=2, label = label)
    plt.plot([0,1],[0,1], "k--")
    
plot_roc_curve(fpr,tpr)
plt.show()

# 여기서도 트레이드 오프가 있다. 재현율이 높을수록 TPR, 거짓양성 FPR이 늘어납니다. 
# 좋은 분류기는 점선에서 멀리있어야 한다. 


# 곡선아래의 면적을 측증하면 분류기들을 비교할수 있다 AUC
# 완벽한 분류기는 AUC가 1이며 완전한 랜덤 분류기는 0.5이다.
from sklearn.metrics import roc_auc_score
roc_auc_score(y_train_5, y_scores)


# RandomForestClassifier을 훈련시켜  sgd ROC 곡선과 ROC AUC 점수를 비교할수 있다.
from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(random_state=42)
y_probas_forest = cross_val_predict(forest_clf, x_train, y_train_5, cv=3, method="predict_proba")

# roc_curve()함수는 레이블과 점수를 기대합니다. 하지만 점수 대신에 클래스 확률을 전달할수 있습니다.
y_scores_forest = y_probas_forest[:,1] # 양성 크랠스에 대한 확률을 점수로 사용합니다.
fpt_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5, y_scores_forest)

plt.plot(fpr,tpr, "b:", label="SGD")
plot_roc_curve(fpt_forest, tpr_forest, "랜덤 포레스트")
plt.legend(loc="lower right")
plt.show()

roc_auc_score(y_train_5, y_scores_forest)



# 다중분류
# 이전에는 5일경우만 했다면 이번에는 숫자 5이상을 감지해보기
# 다중분류기는 둘 이상의 클래스를 구별할 수 있다.
# 일부 알고리즘은 여러 개의 클래스를 직접 처리할수 있는 반면
# (로지스틱회귀, svm)다른 알고리즘은 이진 분류만 가능하다.
# 하지만 이진분류기를 여러 개 사용해 다중 클래스를 분류하는 기법도 존재하낟.

"""
예를 들면 특정 숫자 하나만 구분하는 숫자별 이진 분류기 10개를 훈련시켜 클래스가 10개인 숫ㄱ자 이미지 분류 시스템을 만들수 있다.
이미지를 분류할 때 각 분류기의 결정 점수 중에서 가장 높은 것을 클래스로 선택하면된다. 이를 OvR전략이라고 한다.

또다른 전략으로 0과 1구별 0과 2구별 1과 2 구별등곽 ㅏㅌ이 각 숫자의 조합 마다 이진 분류기를 훈련시키는 것이다.
이를 OvO전략이라 한다.

만약 클래스가 N개라면 분류기는 Nx(N-1)/2 개가 필요하게 된다. 
MNIST 문제에서는 45개의 분류기를 훈련시켜한다는 뜻이다.
45개의 분류기를 모두 통과시켜 가장 많이 양성으로 분류된 클래스를 선택하면된다.
주요 장점은 각 분류기의 훈련에 전체 훈련 세트 중 구별할 두 클래스에 해당하는 샘플만 필요하다는 점이다.

일부 알고리즘 svm과 같은 건 훈련 세트의 크기에 민감해서 큰 훈련 세트에서 몇개의 분류기를 훈련시키는 것보다 작은 훈련 세트에서 많은 분류기를 훈련시키는 쪽으로 빠르므로
OVO방식을 선호한다.  하지만 대부분의 이진 분류 알고리즘은 OVR을 선호한다.


이진 분류 알고리즘을 선택하면 사이킷런이 자동으로 선택해준다.

"""

from sklearn.svm import SVC
svm_clf = SVC()
svm_clf.fit(x_train, y_train)
svm_clf.predict([some_digit])

# 해당 클래스는 간단하게 0에서 9까지의 원래 타깃을 훈련시켜 예측하나를 만든다.
# 45개의 이진 분류기를 훈련시키고 각각의 결정 점수를 얻어 점수가 가장 높은 클래스를 선택한다.
# decision_function 메서드를 호출하면 샘플 당 10개의 점수를 반환하는것을 확인할 수 있다. 
some_digit_scores = svm_clf.decision_function([some_digit])
some_digit_scores
# 다음을 확인해보면 가장 높은 점수가 클래스 5에 해당하는 값인것을 알수 있다. 
np.argmax(some_digit_scores)
svm_clf.classes_

# 사이킷런에서 OvO 나 OvR을 사용하도록 강제하려면 OneVsOneClassifier이나 OneVsRestClassifier을 사용해야한다.

# SVC기반으로 OvR전략을 사용하는 다중 분류기
from sklearn.multiclass import OneVsRestClassifier
ovr_clf = OneVsRestClassifier(SVC())
ovr_clf.fit(x_train,y_train)
ovr_clf.predict([some_digit])
len(ovr_clf.estimators_)



# sgd도 간단하다.
sgd_clf.fit(x_train,y_train)
sgd_clf.predict([some_digit])
# sgd 분류기는 직접 샘플을 다중 클래스로 분류할 수있기 떄문에 별도로 지정할 필요가 없다. 
# decision_function() 매서드를 사용해 부여한 점수를 확인할수있다.


# 분류기 평가에는 일반적으로 교차 검증을 사용함
# cross_val_score() 함수를 사용해 평가에 사용함
cross_val_score(sgd_clf, x_train, y_train, cv=3, scoring="accuracy")

# 성능을 더 올리는 방법으로는 스케일을 조정해주는것이있습니다.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_train_scaled = scaler.fit_transform(x_train.astype(np.float64))
cross_val_score(sgd_clf, x_train_scaled, y_train, cv=3, scoring="accuracy")

# 에러분석
# 실제 프로젝트라면 머신러닝 프로젝트 체크리스트의 단계를 따른다.
"""_summary_
데이터 준비 단계에서 가능한 선택 사항을 탐색하고 여러 모델을 시도하고 가장 좋은 몇개를 골라 GridSearchCV를 사용해 하이퍼파라미터를 세밀하게 튜닝하고, 앞 장에서 한 것처럼 가능한한 자동화시킬거다
해당 절에서는 가능성이 높은 모델을 하나 찾았다고 가정하고
해당 모델의 성능을 향상시키는 방법을 찾을것이다.

하나의 방법으로는 만들어진 에러으 ㅣ종류를 분석하는 것이다.

"""

# 먼저 오차행렬을 살펴보기
y_train_pred = cross_val_predict(sgd_clf, x_train_scaled, y_train, cv=3)
conf_mx = confusion_matrix(y_train, y_train_pred)
conf_mx

plt.matshow(conf_mx, cmap=plt.cm.gray)
plt.show()

# 이미지를 보게되면은 올바르게 분류되어있음을 알수 있는 주대각선 형태를볼수있다.
# 5는 다른 숫자들보다 조금더 어두워 보이는대 이는 이미지가 적거나 잘 분류하지 못한다는 것이다.

# 둘다 확인해봐야하는대
# 그래프의 에러 부분에 초점을 맞춰보면된다.
# 먼저 오차 행렬의 각 값을 대응된느 클래스의 이미지 개수로 나누어 에러 비율을 비교합니다.


row_sums = conf_mx.sum(axis=1, keepdims=True)
norm_conf_mx = conf_mx / row_sums

# 에러를 보기위해 주대각석만 0으로 채워서 그래프를 새로 그려보면 

np.fill_diagonal(norm_conf_mx, 0)
plt.matshow(norm_conf_mx, cmap=plt.cm.gray)
plt.show()


# 에러를 확실히 볼수 있다. 
# 행은 실제 클래스를 나타내며
# 열은 예측한 클래스를 나타낸다.

# 이미지를 보면 3과 5가 서로 혼동하고 있음을 알수있다
# 이를 통해 성능 향상 방안에 대한 통찰을 얻을수 있다.
# 먼저 해당 그래프에서는 8로 잘못 분류되는 것을 줄이도록 개선을 해야함
# 8처럼 보이는 데이터를 더많이모아 학습시킬수있다.
# 예를 들어 동심원의 수를 세는 알고리즘ㅇ같은것이다. 또는 동심원 같은 어떠 ㄴ패턴을 드러나도록 이미지를 전처리 할수 있다.

# 해당 방법은 더 어렵고 시간이 오래 걸린다.

# 예를 들어 3과 5의 샘플을 그려 보겠다
cl_a, cl_b = 3, 5
x_aa = x_train[(y_train == cl_a) & (y_train_pred == cl_a)]
x_ab = x_train[(y_train == cl_a) & (y_train_pred == cl_b)]
x_ba = x_train[(y_train == cl_b) & (y_train_pred == cl_a)]
x_bb = x_train[(y_train == cl_b) & (y_train_pred == cl_b)]

plt.figure(figsize=(8,8))
plt.subplot(221); plot_digits(x_aa[:25], images_per_row=5)
plt.subplot(222); plot_digits(x_ab[:25], images_per_row=5)
plt.subplot(223); plot_digits(x_ba[:25], images_per_row=5)
plt.subplot(224); plot_digits(x_bb[:25], images_per_row=5)
plt.show()

# 여기서 문제는 선형 모델인 sgd를 사용했기떄문이다.
# 선형 분류기는 클래스마다 픽셀에 가중치를 할당하고 새로운 이미지에 대해 단순히 픽셀 강도의 가중치 합을 클래스의 점수로 계산합니다. 
# 따라서 3과 5는 몇 개의 픽셀만 다르기 떄문에 모델이 쉽게 혼동됩니다.

# 3과 5의 주요 차이는 위쪽 선과 아래쪽 호를 이어주는 작은 직선의 위치입니다.
# 분류기는 이미지의 위치나 회전 방향에 매 ㅜ민감하기에이미지를 중앙에 위치하고 회전되어있지 않도록 전처리하는 것입니다.

# 다중 레이블 분류
# 지금까지는 각 샘플이 하나의 클래스에만 할당되어왔다. 하지만 분류기가 샘플마다 여러 개의 클래스를 출력해야 하는 경우가 생긴다.
from sklearn.neighbors import KNeighborsClassifier

y_train_large = (y_train >=7)
y_train_odd = (y_train % 2 == 1)
y_multilabel = np.c_[y_train_large, y_train_odd]

knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train, y_multilabel)

# 다중 출력 다중 클래스 분류
# 이미지에서 잡음을 제거하는 시스템

# 잡음을 추가해보기
noise = np.random.randint(0, 100, (len(x_train), 784))
x_train_mod = x_train + noise
noise = np.random.randint(0, 100, (len(x_test),784))
x_test_mod = x_test + noise

y_train_mod = y_train
y_test_mod = y_test

knn_clf.fit(x_train_mod, y_train_mod)
clean_digit = knn_clf.predict([x_test_mod[some_index]])
plot_digit(clean_digit)
반응형

댓글