ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 핸즈온 머신러닝[3] 분류(2)
    핸즈온머신러닝 2022. 5. 5. 13:40

    https://www.youtube.com/watch?v=Ie5pFrpKyvM&list=PLJN246lAkhQjX3LOdLVnfdFaCbGouEBeb&index=12 

     

    거짓 양성 비율에 대한 진짜 양성 비율 곡선 (Receiver Operating Characteristic)

    from sklearn.metrics import roc_curve
    
    fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)

    fpr: 특이도, tpr: 민감도

    def plot_roc_curve(fpr, tpr, label=None):
        plt.plot(fpr, tpr, linewidth=2, label=label)
        plt.plot([0, 1], [0, 1], 'k--') # 대각 점선
        plt.axis([0, 1, 0, 1])                                    # Not shown in the book
        plt.xlabel('False Positive Rate (Fall-Out)', fontsize=16) # Not shown
        plt.ylabel('True Positive Rate (Recall)', fontsize=16)    # Not shown
        plt.grid(True)                                            # Not shown
    
    plt.figure(figsize=(8, 6))                                    # Not shown
    plot_roc_curve(fpr, tpr)
    fpr_90 = fpr[np.argmax(tpr >= recall_90_precision)]           # Not shown
    plt.plot([fpr_90, fpr_90], [0., recall_90_precision], "r:")   # Not shown
    plt.plot([0.0, fpr_90], [recall_90_precision, recall_90_precision], "r:")  # Not shown
    plt.plot([fpr_90], [recall_90_precision], "ro")               # Not shown
    save_fig("roc_curve_plot")                                    # Not shown
    plt.show()

    왼쪽 위의 끝으로 갈수록 좋음

    from sklearn.metrics import roc_auc_score
    
    roc_auc_score(y_train_5, y_scores)

    0.9604938554008616

    roc_auc_score: 면적을 계산

    from sklearn.ensemble import RandomForestClassifier
    forest_clf = RandomForestClassifier(n_estimators=100, random_state=42)
    y_probas_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3,
                                        method="predict_proba")

    100개의 결정트리로 훈련

    predict_proba: 예측이 아닌 예측의 확률값을 제공

    y_scores_forest = y_probas_forest[:, 1] # 점수 = 양성 클래스의 확률
    fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)
    recall_for_forest = tpr_forest[np.argmax(fpr_forest >= fpr_90)]
    
    plt.figure(figsize=(8, 6))
    plt.plot(fpr, tpr, "b:", linewidth=2, label="SGD")
    plot_roc_curve(fpr_forest, tpr_forest, "Random Forest")
    plt.plot([fpr_90, fpr_90], [0., recall_90_precision], "r:")
    plt.plot([0.0, fpr_90], [recall_90_precision, recall_90_precision], "r:")
    plt.plot([fpr_90], [recall_90_precision], "ro")
    plt.plot([fpr_90, fpr_90], [0., recall_for_forest], "r:")
    plt.plot([fpr_90], [recall_for_forest], "ro")
    plt.grid(True)
    plt.legend(loc="lower right", fontsize=16)
    save_fig("roc_curve_comparison_plot")
    plt.show()

    roc_auc_score(y_train_5, y_scores_forest)

    0.9983436731328145

    y_train_pred_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3)
    precision_score(y_train_5, y_train_pred_forest)

    0.9905083315756169

    recall_score(y_train_5, y_train_pred_forest)

     

    다중 분류

    OvR, OvA, OvO

    from sklearn.svm import SVC
    
    svm_clf = SVC(gamma="auto", random_state=42)
    svm_clf.fit(X_train[:1000], y_train[:1000]) # y_train_5이 아니라 y_train입니다
    svm_clf.predict([some_digit])

    array([5], dtype=uint8)

    some_digit_scores = svm_clf.decision_function([some_digit])
    some_digit_scores

    array([[ 2.81585438, 7.09167958, 3.82972099, 0.79365551, 5.8885703 , 9.29718395, 1.79862509, 8.10392157, -0.228207 , 4.83753243]])

    이 값들 중 가장 높은 점수의 값이 예측값이 됨 여기서는 9.29718395

    np.argmax(some_digit_scores)

    5

    svm_clf.classes_

    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)

    svm_clf.classes_[5]

    5

    from sklearn.multiclass import OneVsRestClassifier
    ovr_clf = OneVsRestClassifier(SVC(gamma="auto", random_state=42))
    ovr_clf.fit(X_train[:1000], y_train[:1000])
    ovr_clf.predict([some_digit])

    array([5], dtype=uint8)

    len(ovr_clf.estimators_)

    10

    sgd_clf.fit(X_train, y_train)
    sgd_clf.predict([some_digit])

    array([3], dtype=uint8)

    sgd_clf.decision_function([some_digit])
     
    array([[-31893.03095419, -34419.69069632, -9530.63950739, 1823.73154031, -22320.14822878, -1385.80478895, -26188.91070951, -16147.51323997, -4604.35491274, -12050.767298 ]])
    cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring="accuracy")

    array([0.87365, 0.85835, 0.8689 ])

    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")

    array([0.8983, 0.891 , 0.9018])

    경사하강법 -> 거리에 민감해서 scale을 조정해줘야함

     

    에러 분석

    다중분류인 경우 오차행렬

    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

    def plot_confusion_matrix(matrix):
        """If you prefer color and a colorbar"""
        fig = plt.figure(figsize=(8,8))
        ax = fig.add_subplot(111)
        cax = ax.matshow(matrix)
        fig.colorbar(cax)
    plt.matshow(conf_mx, cmap=plt.cm.gray)
    save_fig("confusion_matrix_plot", tight_layout=False)
    plt.show()

    높은 값이 밝게 나옴, 어둡게 나온 값이 분류가 잘 되지 않은 값

    row_sums = conf_mx.sum(axis=1, keepdims=True)
    norm_conf_mx = conf_mx / row_sums
    np.fill_diagonal(norm_conf_mx, 0)
    plt.matshow(norm_conf_mx, cmap=plt.cm.gray)
    save_fig("confusion_matrix_errors_plot", tight_layout=False)
    plt.show()

    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)
    save_fig("error_analysis_digits_plot")
    plt.show()

    1. 진짜 3을 3으로 분류한 경우

    2. 진짜 3을 5로 잘못 분류한 경우

    3. 5를 3으로 잘못 분류

    4. 진짜 5를 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)

    타깃값이 7보다 큰지, 타깃값이 홀수인지 두 개의 레이블 (다중 레이블)

    KNeighborsClassifier()

    knn_clf.predict([some_digit])

    array([[False, True]])

    y_train_knn_pred = cross_val_predict(knn_clf, X_train, y_multilabel, cv=3)
    f1_score(y_multilabel, y_train_knn_pred, average="macro")

    0.976410265560605

    Macro: 레이블별로 f1 score를 계산해서 평균값을 냄, weighted, micro

    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 = X_train
    y_test_mod = X_test
    some_index = 0
    plt.subplot(121); plot_digit(X_test_mod[some_index])
    plt.subplot(122); plot_digit(y_test_mod[some_index])
    save_fig("noisy_digit_example_plot")
    plt.show()

    왼쪽: 노이즈가 섞인 그래프

    knn_clf.fit(X_train_mod, y_train_mod)
    clean_digit = knn_clf.predict([X_test_mod[some_index]])
    plot_digit(clean_digit)
    save_fig("cleaned_digit_example_plot")

     

    더미 (즉 랜덤) 분류기

    from sklearn.dummy import DummyClassifier
    # 0.24버전부터 strategy의 기본값이 'stratified'에서 'prior'로 바뀌므로 명시적으로 지정합니다.
    dmy_clf = DummyClassifier(strategy='prior')
    y_probas_dmy = cross_val_predict(dmy_clf, X_train, y_train_5, cv=3, method="predict_proba")
    y_scores_dmy = y_probas_dmy[:, 1]
    fprr, tprr, thresholdsr = roc_curve(y_train_5, y_scores_dmy)
    plot_roc_curve(fprr, tprr)

     

    KNN 분류기

    from sklearn.neighbors import KNeighborsClassifier
    knn_clf = KNeighborsClassifier(weights='distance', n_neighbors=4)
    knn_clf.fit(X_train, y_train)

    KNeighborsClassifier(n_neighbors=4, weights='distance')

    weights='distance': 가까운 거리에 가중치를 크게 줌

    y_knn_pred = knn_clf.predict(X_test)
    from sklearn.metrics import accuracy_score
    accuracy_score(y_test, y_knn_pred)

    0.9714

    from scipy.ndimage.interpolation import shift
    def shift_digit(digit_array, dx, dy, new=0):
        return shift(digit_array.reshape(28, 28), [dy, dx], cval=new).reshape(784)
    
    plot_digit(shift_digit(some_digit, 5, 1, new=100))

    이미지를 이동시키는 함수를 정의함

    X_train_expanded = [X_train]
    y_train_expanded = [y_train]
    for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
        shifted_images = np.apply_along_axis(shift_digit, axis=1, arr=X_train, dx=dx, dy=dy)
        X_train_expanded.append(shifted_images)
        y_train_expanded.append(y_train)
    
    X_train_expanded = np.concatenate(X_train_expanded)
    y_train_expanded = np.concatenate(y_train_expanded)
    X_train_expanded.shape, y_train_expanded.shape

    ((300000, 784), (300000,))

    원본 세트를 네 번 이동함 60000 * 5 = 300000

    knn_clf.fit(X_train_expanded, y_train_expanded)

    KNeighborsClassifier(n_neighbors=4, weights='distance')

    y_knn_expanded_pred = knn_clf.predict(X_test)
    accuracy_score(y_test, y_knn_expanded_pred)

    0.9763

    ambiguous_digit = X_test[2589]
    knn_clf.predict_proba([ambiguous_digit])

    plot_digit(ambiguous_digit)

     

     

    문제

    1. ROC 곡선이 무엇인지 그리고 ROC 곡선이 어떤 모양을 띄는 것이 좋은 분류기를 나타내는지 설명하시오
    2. ROC 곡선 밑의 면적을 계산해주는 sklearn 함수 이름이 무엇인가?
    3. 다중분류에서 OvO, OvA, OvR이 무엇인지 설명하시오
    4. f1_score(y_multilabel, y_train_knn_pred, average="macro") 다음 코드에서 fi_score를 계산하는 함수의 average 파라미터 값이 Macro, weighted, micro 일때의 기능 차이를 설명하시오

    3. 위의 사진은 다중 분류의 오차 행렬을 matshow 함수로 cmap 파라미터 값을 plt.cm.gray 두고 그린 그래프이다 그래프가 무엇을 나타내는지 설명하시오

    댓글

Designed by Tistory.