ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 핸즈온 머신러닝[5] 서포트 벡터 머신(1)
    핸즈온머신러닝 2022. 6. 22. 21:59

    https://www.youtube.com/watch?v=dP-cDdP_Y3A&list=PLJN246lAkhQjX3LOdLVnfdFaCbGouEBeb&index=16 

     

    선형 SVM 분류

    라지 마진 분류

    from sklearn.svm import SVC
    from sklearn import datasets
    
    iris = datasets.load_iris()
    X = iris["data"][:, (2, 3)]  # 꽃잎 길이, 꽃잎 너비
    y = iris["target"]
    
    setosa_or_versicolor = (y == 0) | (y == 1)
    X = X[setosa_or_versicolor]
    y = y[setosa_or_versicolor]
    
    # SVM 분류 모델
    svm_clf = SVC(kernel="linear", C=float("inf"))
    svm_clf.fit(X, y)
    
    # SVC(C=inf, kernel='linear')

     

    두 클래스를 가장 넓게 벌리는 결정 경계를 찾는 것이 서포트 벡터 머신의 알고리즘.

    밑의 왼쪽 그림은 결정 경계가 샘플과 너무 가깝기 때문에 좋은 결정 경계라고 보기 어려움.

    반면에 오른쪽 그림은 결정 경계가 샘플과의 거리를 최대로 잘 벌려놓았기 때문에 샘플을 잘 분류하고 있음.

    이때 마진의 경계에 있는 벡터를 서포트 벡터라고 한다.

    특성 스케일에 따른 민감성

    Xs = np.array([[1, 50], [5, 20], [3, 80], [5, 60]]).astype(np.float64)
    ys = np.array([0, 0, 1, 1])
    svm_clf = SVC(kernel="linear", C=100)
    svm_clf.fit(Xs, ys)
    
    plt.figure(figsize=(9,2.7))
    plt.subplot(121)
    plt.plot(Xs[:, 0][ys==1], Xs[:, 1][ys==1], "bo")
    plt.plot(Xs[:, 0][ys==0], Xs[:, 1][ys==0], "ms")
    plot_svc_decision_boundary(svm_clf, 0, 6)
    plt.xlabel("$x_0$", fontsize=20)
    plt.ylabel("$x_1$    ", fontsize=20, rotation=0)
    plt.title("Unscaled", fontsize=16)
    plt.axis([0, 6, 0, 90])
    
    from sklearn.preprocessing import StandardScaler
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(Xs)
    svm_clf.fit(X_scaled, ys)
    
    plt.subplot(122)
    plt.plot(X_scaled[:, 0][ys==1], X_scaled[:, 1][ys==1], "bo")
    plt.plot(X_scaled[:, 0][ys==0], X_scaled[:, 1][ys==0], "ms")
    plot_svc_decision_boundary(svm_clf, -2, 2)
    plt.xlabel("$x'_0$", fontsize=20)
    plt.ylabel("$x'_1$  ", fontsize=20, rotation=0)
    plt.title("Scaled", fontsize=16)
    plt.axis([-2, 2, -2, 2])
    
    save_fig("sensitivity_to_feature_scales_plot")

    특성을 표준화 했을 때와 하지 않았을 때 각각 모델에 훈련했을 때의 결과값

     

    소프트 마진 분류

    이상치에 민감한 하드 마진

    X_outliers = np.array([[3.4, 1.3], [3.2, 0.8]]) # 이상치를 만들어줌
    y_outliers = np.array([0, 0])
    Xo1 = np.concatenate([X, X_outliers[:1]], axis=0)
    yo1 = np.concatenate([y, y_outliers[:1]], axis=0)
    Xo2 = np.concatenate([X, X_outliers[1:]], axis=0)
    yo2 = np.concatenate([y, y_outliers[1:]], axis=0)
    
    svm_clf2 = SVC(kernel="linear", C=10**9)
    svm_clf2.fit(Xo2, yo2)
    
    fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)
    
    plt.sca(axes[0])
    plt.plot(Xo1[:, 0][yo1==1], Xo1[:, 1][yo1==1], "bs")
    plt.plot(Xo1[:, 0][yo1==0], Xo1[:, 1][yo1==0], "yo")
    plt.text(0.3, 1.0, "Impossible!", fontsize=24, color="red")
    plt.xlabel("Petal length", fontsize=14)
    plt.ylabel("Petal width", fontsize=14)
    plt.annotate("Outlier",
                 xy=(X_outliers[0][0], X_outliers[0][1]),
                 xytext=(2.5, 1.7),
                 ha="center",
                 arrowprops=dict(facecolor='black', shrink=0.1),
                 fontsize=16,
                )
    plt.axis([0, 5.5, 0, 2])
    
    plt.sca(axes[1])
    plt.plot(Xo2[:, 0][yo2==1], Xo2[:, 1][yo2==1], "bs")
    plt.plot(Xo2[:, 0][yo2==0], Xo2[:, 1][yo2==0], "yo")
    plot_svc_decision_boundary(svm_clf2, 0, 5.5)
    plt.xlabel("Petal length", fontsize=14)
    plt.annotate("Outlier",
                 xy=(X_outliers[1][0], X_outliers[1][1]),
                 xytext=(3.2, 0.08),
                 ha="center",
                 arrowprops=dict(facecolor='black', shrink=0.1),
                 fontsize=16,
                )
    plt.axis([0, 5.5, 0, 2])
    
    save_fig("sensitivity_to_outliers_plot")
    plt.show()

    오른쪽 그림의 경우 이상치에 의해서 도로의 폭(마진)이 굉장히 좁아짐

    왼쪽 그림의 경우 선형 경계로 분류할 수 없음

     

    라지 마진 vs 마진 오류

    import numpy as np
    from sklearn import datasets
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    from sklearn.svm import LinearSVC
    
    iris = datasets.load_iris()
    X = iris["data"][:, (2, 3)]  # 꽃잎 길이, 꽃잎 너비
    y = (iris["target"] == 2).astype(np.float64)  # Iris virginica
    
    svm_clf = Pipeline([
            ("scaler", StandardScaler()),
            ("linear_svc", LinearSVC(C=1, loss="hinge", random_state=42)),
        ])
    
    svm_clf.fit(X, y)
    
    # Pipeline(steps=[('scaler', StandardScaler()),
    #                ('linear_svc', LinearSVC(C=1, loss='hinge', random_state=42))])
    svm_clf.predict([[5.5, 1.7]])
    # array([1.])

    꽃잎 길이, 꽃잎 너비를 주고 예측을 시킴 --> 양성 클래스로 분류됨

    scaler = StandardScaler()
    svm_clf1 = LinearSVC(C=1, loss="hinge", random_state=42)
    svm_clf2 = LinearSVC(C=100, loss="hinge", random_state=42)
    # 규제를 달리함
    
    scaled_svm_clf1 = Pipeline([
            ("scaler", scaler),
            ("linear_svc", svm_clf1),
        ])
    scaled_svm_clf2 = Pipeline([
            ("scaler", scaler),
            ("linear_svc", svm_clf2),
        ])
    
    scaled_svm_clf1.fit(X, y)
    scaled_svm_clf2.fit(X, y)
    
    Pipeline(steps=[('scaler', StandardScaler()),
    #                ('linear_svc',
    #                 LinearSVC(C=100, loss='hinge', random_state=42))])

    규제(C) 파라미터 값을 1일 때와 100일 때로 각각 달리해봄

    # 스케일되지 않은 파라미터로 변경
    b1 = svm_clf1.decision_function([-scaler.mean_ / scaler.scale_])
    b2 = svm_clf2.decision_function([-scaler.mean_ / scaler.scale_])
    w1 = svm_clf1.coef_[0] / scaler.scale_
    w2 = svm_clf2.coef_[0] / scaler.scale_
    svm_clf1.intercept_ = np.array([b1])
    svm_clf2.intercept_ = np.array([b2])
    svm_clf1.coef_ = np.array([w1])
    svm_clf2.coef_ = np.array([w2])
    
    # 서포트 벡터 찾기 (libsvm과 달리 liblinear 라이브러리에서 제공하지 않기 때문에 
    # LinearSVC에는 서포트 벡터가 저장되어 있지 않습니다.)
    
    # y:(0,1) -> t:(-1,1)
    t = y * 2 - 1
    support_vectors_idx1 = (t * (X.dot(w1) + b1) < 1).ravel()
    support_vectors_idx2 = (t * (X.dot(w2) + b2) < 1).ravel()
    svm_clf1.support_vectors_ = X[support_vectors_idx1]
    svm_clf2.support_vectors_ = X[support_vectors_idx2]
    fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)
    
    plt.sca(axes[0])
    plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^", label="Iris virginica")
    plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs", label="Iris versicolor")
    plot_svc_decision_boundary(svm_clf1, 4, 5.9)
    plt.xlabel("Petal length", fontsize=14)
    plt.ylabel("Petal width", fontsize=14)
    plt.legend(loc="upper left", fontsize=14)
    plt.title("$C = {}$".format(svm_clf1.C), fontsize=16)
    plt.axis([4, 5.9, 0.8, 2.8])
    
    plt.sca(axes[1])
    plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
    plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")
    plot_svc_decision_boundary(svm_clf2, 4, 5.99)
    plt.xlabel("Petal length", fontsize=14)
    plt.title("$C = {}$".format(svm_clf2.C), fontsize=16)
    plt.axis([4, 5.9, 0.8, 2.8])
    
    save_fig("regularization_plot")

    C=1: 규제가 강해서 마진 오류가 많아짐

    C=100: 규제가 약해서 마진 오류가 적음

     

    문제

    1. 하드 마진 분류와 소프트 마진 분류의 차이점을 설명하시오

    2. SVM 모델의 하이퍼파라미터인 C 값에 따라 모델이 어떻게 달라지는지 설명하시오

    3. SVM 모델은 특성의 스케일에 민감하다. 스케일을 조정하지 않은 데이터에 대하여 SVM 모델을 훈련시킬 때의 문제점은 무엇인가??

    댓글

Designed by Tistory.