📗강의노트/핸즈온 머신러닝

[핸즈온 머신러닝] 제 5장 코드 & 연습문제 풀이

728x90
반응형
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)

svm_clf = Pipeline([
    ('scaler',StandardScaler()),
    ('classifier', LinearSVC(C=1,loss='hinge'))
])
svm_clf.fit(X,y)
Out[23]:
Pipeline(memory=None,
     steps=[('scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('classifier', LinearSVC(C=1, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='hinge', max_iter=1000, multi_class='ovr',
     penalty='l2', random_state=None, tol=0.0001, verbose=0))])
In [25]:
svm_clf.predict([[5.5, 1.7]])
Out[25]:
array([1.])
In [33]:
from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures

X,y = make_moons(n_samples=100,noise=0.15, random_state=42)
# n_samples : The total number of points generated.
# noise : Standard deviation of Gaussian noise added to the data.

polynomial_svm_clf= Pipeline([
    ('scaler', StandardScaler()),
    ('poly_scaler',PolynomialFeatures(degree=3)),
    ('svm_clf',LinearSVC(C=10, loss='hinge',max_iter=2000,random_state=42))
])

polynomial_svm_clf.fit(X,y)
Out[33]:
Pipeline(memory=None,
     steps=[('scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('poly_scaler', PolynomialFeatures(degree=3, include_bias=True, interaction_only=False)), ('svm_clf', LinearSVC(C=10, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='hinge', max_iter=2000, multi_class='ovr',
     penalty='l2', random_state=42, tol=0.0001, verbose=0))])
In [37]:
polynomial_svm_clf.predict([[5.5,1.7]])
Out[37]:
array([1])
In [45]:
import matplotlib.pyplot as plt

def plot_dataset(X, y, axes):
    plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")     # 1열 중에서, 2열 중에서 y==0 
    plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
    plt.axis(axes)
    plt.grid(True, which='both')
    plt.xlabel(r"$x_1$", fontsize=20)
    plt.ylabel(r"$x_2$", fontsize=20, rotation=0)


def plot_predictions(clf, axes):
    x0s = np.linspace(axes[0], axes[1], 100)
    x1s = np.linspace(axes[2], axes[3], 100)
    x0, x1 = np.meshgrid(x0s, x1s)
    X = np.c_[x0.ravel(), x1.ravel()]
    y_pred = clf.predict(X).reshape(x0.shape)
    y_decision = clf.decision_function(X).reshape(x0.shape)
    plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
    plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1)
    
plot_predictions(polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])

plt.show()
In [54]:
# 다항식 커널 
from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
    ('scaler',StandardScaler()),
    ('svm_clf',SVC(kernel='poly',degree=3,coef0=1,C=5)),      # 3차 다항식 커널 이용 
])

poly_kernel_svm_clf.fit(X,y)

# 실제로 특성 추가하지 않으면서 다항식의 특성을 추가한 것과 같은 결과를 냄 
Out[54]:
Pipeline(memory=None,
     steps=[('scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('svm_clf', SVC(C=5, cache_size=200, class_weight=None, coef0=1,
  decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
  kernel='poly', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False))])
In [55]:
rbf_kernel_svm_clf = Pipeline([
    ('scaler',StandardScaler()),
    ('svm_clf', SVC(kernel='rbf',gamma=5,C=0.001))
])

rbf_kernel_svm_clf.fit(X,y)
Out[55]:
Pipeline(memory=None,
     steps=[('scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('svm_clf', SVC(C=0.001, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=5, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False))])
In [56]:
# https://bskyvision.com/163   SVM 설명

from sklearn.svm import LinearSVR

svm_reg = LinearSVR(epsilon=1.5)
svm_reg.fit(X,y)
Out[56]:
LinearSVR(C=1.0, dual=True, epsilon=1.5, fit_intercept=True,
     intercept_scaling=1.0, loss='epsilon_insensitive', max_iter=1000,
     random_state=None, tol=0.0001, verbose=0)
In [57]:
from sklearn.svm import SVR

svm_poly_reg = SVR(kernel='poly',gamma='auto',degree=2,C=100,epsilon=0.1)
svm_poly_reg.fit(X,y)
Out[57]:
SVR(C=100, cache_size=200, coef0=0.0, degree=2, epsilon=0.1, gamma='auto',
  kernel='poly', max_iter=-1, shrinking=True, tol=0.001, verbose=False)

Exercise Part. 5

Support Vector Machine

1. 서포터 벡터 머신의 근본 아이디어는 무엇인가요?

근본적인 아이디어는 클래스 사이에 가능한 한 가장 넓은 ' 도로 ' 를 내는 것이다. 두 클래스를 구분하는 결정 경계와 샘플 사이의 마진을 가능한 크게 내겠다는 것 이다.

2. 서포트 벡터가 무엇인가요?

SVM이 훈련된 후에 경계를 포함해 도로에 놓인 어떤 샘플 (속해있다)

결정 경계는 서포트 벡터에 의해 결정이 된다. 서포트 벡터가 아닌, 어떤 샘플도 영향을 주지 못한다.

예측할 때에는 전체 훈련 세트가 아니라 서포트 벡터만 관여됨.

3. SVM을 사용할 때 입력값의 스케일이 왜 중요한가요?

가장 큰 마진을 내는 결정 경계를 만드는 것이 목표인데, 훈련세트의스케일이 맞지 안으면 크기가 작은 특성을 무시하는 경향이 있다.

4. SVM 분류기가 샘플을 분류할 때 신뢰도 점수와 확률을 출력할 수 있나요?

테스트 샘플과 결정 경계 사이의 거리를 출력할 수 있으므로 이를 신뢰도 점수로 사용할 수 있다.

하지만 점수를 바로 확률의 추정값으로는 변환하지 못한다. sklearn에서 SVM모델을 만들 때 probability = True 로 설정하면 훈련이 끝난 후 SVM의 점수에 로지스틱 회귀를 훈련시켜 확률을 계산한다. 이 설정은 predict_proba() 와 predict_log_proba() 메서드를 추가 시킨다.

5. 수백만 개의 샘플과 수백 개의 특성을 가진 훈련 세트에 SVM 모델을 훈련시키려면 원 문제와 쌍대 문제 중 어떤 것을 사용해야 하나요?

커널 SVM은 쌍대 형식만 사용 가능하기에 이 문제는 '선형 SVM'에만 해당한다.

원 문제의 계산 복잡도는 훈련 샘플 수(m)에 비례하지만, 쌍대 형식의 계산 복잡도는  ~  사이의 값에 비례한다. 그러므로 수백 만개의 샘플과 특성이 있다면 쌍대는 너무 느려서 원 문제를 사용해야 한다.

6. RBF 커널을 사용해 SVM 분류기를 훈련시켰더니 훈련 세트에 과소적합된 것 같습니다. (gamma)를 증가시켜야 할까, 감소시켜야 할까? C의 경우는 어떠한가?

훈련 세트에 과소 적합 되었다는 것은 규제가 너무 큰 것일 수도 있다. 따라서 규제를 줄이기 위해 와 C를 둘 다 증가시키는 것이 모델에 규제를 주어 과소적합을 해결할 수 있다.

7. 이미 만들어진 QP 알고리즘 라이브러리를 사용해 소프트 마진 선형 SVM 분류기를 학습 시키려면 QP 매개변수 ( H,f,A,b)를 어떻게 저장해야 하나요?

하드 마진 문제에 관한 QP 파라미터를 H' , f' , A' , b' 라고 하자. 소프트 마진 문제의 QP 파라미터 m개의 추가적인 파라미터()와 m개의 추가적인 제약()을 가진다.

  • H는 다음과 같은 행렬이다

  • f는 f' 하이퍼파라미터 C와 동일한 값의 원소 m 개가 추가된 벡터이다.

  • b는 b'에 값이 0인 원소 m개가 추가된 벡터이다.

  • A는 다음과 같은 행렬이다.

8. 선형적으로 분리되는 데이터셋에 LinearSVC를 훈련시켜보세요. 그런 다음 같은 데이터셋에 SVC와 SGDClassifier를 적용해보세요. 거의 비슷한 모델이 만들어지는지 확인해보세요.

In [97]:
from sklearn import datasets
import numpy as np
from sklearn.svm import LinearSVC, SVC
from sklearn.linear_model import SGDClassifier
from sklearn.preprocessing import StandardScaler
In [98]:
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]
In [99]:
C = 5 
alpha = 1 / (C * len(X))
In [101]:
lin_svc = LinearSVC(loss='hinge',C=C,random_state=42)
svc = SVC(kernel='linear',C=C)
sgd_clf = SGDClassifier(loss='hinge', learning_rate = 'constant', eta0=0.001, tol=1e-3,alpha=alpha,
                       max_iter=100000, random_state=42)

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

lin_svc.fit(X_scaled,y)
svc.fit(X_scaled,y)
sgd_clf.fit(X_scaled,y)

print('LinearSVC :',lin_svc.intercept_,lin_svc.coef_)
print('SVC :',svc.intercept_,svc.coef_)
print('SGDClassifier :',sgd_clf.intercept_,sgd_clf.coef_)
LinearSVC : [0.28474532] [[1.05364923 1.09903601]]
SVC : [0.31896852] [[1.1203284  1.02625193]]
SGDClassifier : [0.117] [[0.77666262 0.72787608]]
In [105]:
# 세 개 모델의 결정 경계를 그리자
# 먼저 결정 결제들의 기울기(W)와 편향(b)을 구함

w1 = -lin_svc.coef_[0,0] / lin_svc.coef_[0,1]
b1 = -lin_svc.intercept_[0] / lin_svc.coef_[0,1]
w2 = -svc.coef_[0, 0]/svc.coef_[0, 1]
b2 = -svc.intercept_[0]/svc.coef_[0, 1]
w3 = -sgd_clf.coef_[0, 0]/sgd_clf.coef_[0, 1]
b3 = -sgd_clf.intercept_[0]/sgd_clf.coef_[0, 1]

# 결정 결계를 원본 스케일로 변환하기
line1 = scaler.inverse_transform([[-10,-10*w1+b1],[10,10*w1+b1]])
line2 = scaler.inverse_transform([[-10,-10*w2+b2],[10,10*w2+b2]])
line3 = scaler.inverse_transform([[-10,-10*w3+b3],[10,10*w3+b3]])

# 세 개의 결정 경계를 모두 그리자
plt.figure(figsize=(11,4))
plt.plot(line1[:,0], line1[:,1], "k:",label="LinearSVC")
plt.plot(line2[:,0],line2[:,1],"b--",linewidth=2,label="SVC")
plt.plot(line3[:,0],line3[:,1],'r-',label="SGDClassifier")

plt.plot(X[:,0][y==1],X[:,1][y==1], 'bs')
plt.plot(X[:,0][y==0],X[:,1][y==0],'yo')

plt.xlabel("꽃잎 길이", fontsize=14)
plt.ylabel("꽃잎 너비",fontsize=14)
plt.legend(loc='upper center',fontsize=14)
plt.axis([0,5.5,0,2])
plt.show()

# 아주 비슷한 결정 경계를 보인다. 

9. MNIST 데이터셋에 SVM 분류기를 훈련시켜보세요. SVM 분류기는 이진 분류기라서 OvA전략을 사용해 10개의 숫자를 분류해야 합니다. 처리 속도를 높이기 위해 작은 검증 세트로 하이퍼파라미터를 조정하는 것이 좋습니다. 어느 정도까지 정확도를 높일 수 있나요?

In [106]:
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784',version=1)
In [107]:
X = mnist['data']
y = mnist['target']

X_train = X[:60000]
y_train = y[:60000]
X_test = X[60000:]
y_test=y[60000:]
In [108]:
# 훈련 샘플 순서 섞기
np.random.seed(42)
rnd_idx = np.random.permutation(60000)
X_train = X_train[rnd_idx]
y_train = y_train[rnd_idx]
In [109]:
lin_clf = LinearSVC(max_iter=10000,random_state=42)
lin_clf.fit(X_train,y_train)
/Users/charming/anaconda3/lib/python3.6/site-packages/sklearn/svm/base.py:931: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[109]:
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='squared_hinge', max_iter=10000,
     multi_class='ovr', penalty='l2', random_state=42, tol=0.0001,
     verbose=0)
In [110]:
from sklearn.metrics import accuracy_score

y_pred = lin_clf.predict(X_train)
accuracy_score(y_train,y_pred)
Out[110]:
0.8673166666666666
In [111]:
# 전처리 과정 ( 스케일 조정 )
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.astype(np.float32))
X_test_scaled = scaler.fit_transform(X_test.astype(np.float32))
In [112]:
lin_clf = LinearSVC(max_iter=10000,random_state=42)
lin_clf.fit(X_train_scaled,y_train)
/Users/charming/anaconda3/lib/python3.6/site-packages/sklearn/svm/base.py:931: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[112]:
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='squared_hinge', max_iter=10000,
     multi_class='ovr', penalty='l2', random_state=42, tol=0.0001,
     verbose=0)
In [113]:
y_pred = lin_clf.predict(X_train_scaled)
accuracy_score(y_train,y_pred)
Out[113]:
0.9272
In [114]:
svm_clf = SVC(gamma='auto',decision_function_shape='ovr')
svm_clf.fit(X_train_scaled[:10000],y_train[:10000])
Out[114]:
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)
In [116]:
y_pred = svm_clf.predict(X_train_scaled)
accuracy_score(y_train,y_pred)
Out[116]:
0.9475833333333333
In [117]:
# 랜덤 서치로 하이퍼파라미 튜닝하기 

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import reciprocal, uniform

param_dist = {'gamma':reciprocal(0.001,0.1),"C":uniform(1,10)}

rnd_search_cv =  RandomizedSearchCV(svm_clf,param_dist,cv=3,n_iter=10,verbose=2,n_jobs=-1)
rnd_search_cv.fit(X_train_scaled[:1000],y_train[:1000])
Fitting 3 folds for each of 10 candidates, totalling 30 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.
[Parallel(n_jobs=-1)]: Done  30 out of  30 | elapsed:   17.2s finished
Out[117]:
RandomizedSearchCV(cv=3, error_score='raise-deprecating',
          estimator=SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False),
          fit_params=None, iid='warn', n_iter=10, n_jobs=-1,
          param_distributions={'gamma': <scipy.stats._distn_infrastructure.rv_frozen object at 0x1a18cd7518>, 'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x1a18bf97f0>},
          pre_dispatch='2*n_jobs', random_state=None, refit=True,
          return_train_score='warn', scoring=None, verbose=2)
In [119]:
rnd_search_cv.best_estimator_
Out[119]:
SVC(C=8.852316058423087, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.001766074650481071,
  kernel='rbf', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False)
In [120]:
rnd_search_cv.best_score_
Out[120]:
0.865
In [121]:
rnd_search_cv.best_estimator_.fit(X_train_scaled,y_train)
Out[121]:
SVC(C=8.852316058423087, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.001766074650481071,
  kernel='rbf', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False)
In [122]:
y_pred = rnd_search_cv.best_estimator_.predict(X_train_scaled)
accuracy_score(y_train,y_pred)
Out[122]:
0.99965
In [124]:
# test to the Test Set
y_pred = rnd_search_cv.best_estimator_.predict(X_test_scaled)
accuracy_score(y_test,y_pred)
Out[124]:
0.9705

아주 나쁘지 않지만 확실히 모델이 다소 과대적합되었습니다. 하이퍼파라미터를 조금 더 수정할 수 있지만(가령, C와/나 gamma를 감소시킵니다) 그렇게 하면 테스트 세트에 과대적합될 위험이 있습니다. 다른 사람들은 하이퍼파라미터 C=5와 gamma=0.005에서 더 나은 성능(98% 이상의 정확도)을 얻었습니다. 훈련 세트를 더 많이 사용해서 더 오래 랜덤 서치를 수행하면 이런 값을 얻을 수 있을지 모릅니다.

10. 캘리포니아 주택 가격 데이터셋에 SVM 회귀를 훈련시켜보세요.

In [129]:
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()

X = housing['data']
y = housing['target']
In [130]:
# 훈련 / 테스트 세트로 나누기 
from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=42)
In [131]:
# 데이터 스케일링
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
In [133]:
# LinearSVC 먼저 학습
lin_svr = LinearSVR(max_iter=10000,random_state=42)
lin_svr.fit(X_train_scaled,y_train)
Out[133]:
LinearSVR(C=1.0, dual=True, epsilon=0.0, fit_intercept=True,
     intercept_scaling=1.0, loss='epsilon_insensitive', max_iter=10000,
     random_state=42, tol=0.0001, verbose=0)
In [134]:
# 성능 확인
from sklearn.metrics import mean_squared_error

y_pred = lin_svr.predict(X_train_scaled)
mse = mean_squared_error(y_train,y_pred)
mse
Out[134]:
0.957048788116759
In [135]:
#RMSE

np.sqrt(mse)
Out[135]:
0.9782887038685252
In [140]:
# rbf kernel 이용하기 위해 랜덤서치로 하이퍼파라미터(C,gamma) 찾기

from sklearn.svm import SVR
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import reciprocal,uniform

param_dist = {'gamma':reciprocal(0.001,0.1),'C':uniform(1,10)}
rnd_search_cv = RandomizedSearchCV(SVR(),param_dist,cv=3,n_iter=10,verbose=2,random_state=42,n_jobs=-1)
rnd_search_cv.fit(X_train_scaled,y_train)
Fitting 3 folds for each of 10 candidates, totalling 30 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.
[Parallel(n_jobs=-1)]: Done  30 out of  30 | elapsed:  1.6min finished
Out[140]:
RandomizedSearchCV(cv=3, error_score='raise-deprecating',
          estimator=SVR(C=1.0, cache_size=200, coef0=0.0, degree=3, epsilon=0.1,
  gamma='auto_deprecated', kernel='rbf', max_iter=-1, shrinking=True,
  tol=0.001, verbose=False),
          fit_params=None, iid='warn', n_iter=10, n_jobs=-1,
          param_distributions={'gamma': <scipy.stats._distn_infrastructure.rv_frozen object at 0x1a1b52cbe0>, 'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x1a1b52c908>},
          pre_dispatch='2*n_jobs', random_state=42, refit=True,
          return_train_score='warn', scoring=None, verbose=2)
In [146]:
y_pred = rnd_search_cv.best_estimator_.predict(X_train_scaled)
mse = mean_squared_error(y_train,y_pred)
mse

# 선형모델보다 나아졌다. 
Out[146]:
0.3280453999995988
In [147]:
y_pred = rnd_search_cv.best_estimator_.predict(X_test_scaled)
mse = mean_squared_error(y_test,y_pred)
np.sqrt(mse)
Out[147]:
0.5929168385528734


728x90
반응형