본문 바로가기
## 오래된 게시글 (미관리) ##/Python (Linux)

42. Python - 5장 연습문제

by #Glacier 2019. 2. 10.
반응형

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

#

서포트 벡터 머신의 근본적 아이디어는 클래스 사이에 가능한 가장 넓은 '도로'를 내는 것입니다.

다시 말해 두 클래스를 결정 경계와 샘플 사이의 마진을 가능한 가장 크게 하는 것이 목적입니다.

소프트 마진 분류를 수행할 때는 SVM이 두 클래스를 완벽하게 나누는 것과 가장 넓은 도로를 만드는 것 사이에 절충안을 찾습니다. (즉, 몇 개의 샘플은 도로 안에 있을 수 있습니다.) 또 하나의 핵심적인 아이디어는 비선형 데이터셋에서 훈련할 때 커널 함수를 이용하는 것입니다.


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

#

서포트 벡터는 SVM이 훈련된 후에 경계를 포함해 도로에 놓인 어떤 샘플입니다. 

결정 경계는 전적으로 서포트 벡터에 의해 결정됩니다. 서포트 벡터가 아닌 (즉, 도로 밖에 있는) 어떤 샘플도 영향을 주지 못합니다. 이런 샘플은 삭제하고 다른 샘플을 더 추가하거나, 다른 곳으로 이동시킬 수 있습니다. 예측을 계산할 때는, 전체 훈련 세트가 아니라 서포트 벡터만 관여됩니다.


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

#

SVM은 클래스 사이에 가능한 가장 큰 도로를 내는 것이므로, 

훈련 세트의 스케일이 맞지 않으면 크기가 작은 특성을 무시하는 경향이 있습니다.


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

#

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

그러나 이 점수를 클래스 확률의 추정값으로 바로 변환할 수는 없습니다. 

사이킷런에서 SVM 모델을 만들 때 probability = True로 설정하면 훈련이 끝난 후 (훈련 데이터에 5-겹 교차 검증을 사용하여 추가로 훈련시킨) SVM의 점수에 로지스틱 회귀를 훈련시켜 확률을 계산합니다. 이 설정은 SVM 모델에 predict_proba() 와 predict_log_proba() 메서드를 추가시킵니다.

** libsvm 라이브러리는 SVM 분류 모델의 확률을 추정하기 위해 5-겹 교차 검증으로 훈련 세트에 대한 예측을 만듭니다.

** 이를 사용하여 predict_proba()나 predict_log_proba() 를 위한 로지스틱 회귀 모델을 훈련시킵니다. 

** 그러므로 probability = True로 설정하면 훈련 속도가 느려지고, predict()와 predict_proba()의 결과가 달라질 수 있습니다.


5. 수백만 개의 샘플과 수백 개의 특성을 가진 훈련 세트에 SVM 모델을 훈련시키려면 

   원 문제와 쌍대 문제 중 어떤 것을 사용하나요?

#

커널 SVM은 쌍대 형식만 사용할 수 있기 때문에, 이 질문은 선형 SVM에만 해당합니다.

원 문제의 계산 복잡도는 훈련 샘플 수 m에 비례하지만, 쌍대 형식의 계산 복잡도는 m의 제곱과 m의 세제곱 사이의 값에 비례합니다. 그러므로 수백만 개의 샘플이 있다면 쌍대 형식은 너무 느려질 것이므로 원 문제를 사용해야 합니다.


6. RBF 커널을 사용해 SVM 분류기를 훈련시켰더니, 훈련 세트에 과소적합된 것 같습니다.

   gamma를 증가시켜야 할까요? 감소시켜야 할까요? C의 경우는 어떤가요?

#

RBF 커널에 훈련된 SVM 분류기가 훈련 세트에 과소적합이라면 규제가 너무 큰 것일 수 있습니다.

규제를 줄이려면 gamma나 C (또는 둘 다) 값을 증가시켜야 합니다.


7. 이미 만들어진 QP 알고리즘 라이브러리를 사용해 소프트 마진 선형 SVM 분류기를 학습시키려면,

   QP 매개변수 (H,f,A,b)는 어떻게 지정해야 하나요?

#

콰드라틱 프로그래밍 참조


8. 선형적으로 분리되는 데이터셋에 LinearSVC를 훈련시켜보세요. 

   그런 다음 같은 데이터셋에 SVC와 SGDClassifier를 적용해보세요. 거의 비슷한 모델이 만들어지는가요?

from sklearn import datasets

iris = datasets.load_iris()
X = iris['data'][:, (2, 3)] #petal length, petal width
y = iris['target']

setosa_or_versicolor = (y==0) | (y==1)

X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor]


from sklearn.svm import SVC, LinearSVC
from sklearn.linear_model import SGDClassifier
from sklearn.preprocessing import StandardScaler

C = 5
alpha = 1 / (C * len(X))

lin_clf = LinearSVC(loss ='hinge', C =C, random_state = 42)
svm_clf = 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_clf.fit(X_scaled, y)
svm_clf.fit(X_scaled, y)
sgd_clf.fit(X_scaled, y)

print('LinearSVC :                                                                                    ', lin_clf.intercept_, lin_clf.coef_)
print('SVC :                                                                                                 ', svm_clf.intercept_, svm_clf.coef_)
print('SGDClassifier(alpha={:.5f}):                                          '.format(sgd_clf.alpha),sgd_clf.intercept_, sgd_clf.coef_)

LinearSVC : [0.28474272] [[1.05364736 1.09903308]] SVC : [0.31896852] [[1.1203284 1.02625193]] SGDClassifier(alpha=0.00200): [0.117] [[0.77666262 0.72787608]]

#각 결정 경계의 기울기와 편향 계산

w1 = -lin_clf.coef_[0, 0]/ lin_clf.coef_[0, 1]
b1 = -lin_clf.intercept_[0]/lin_clf.coef_[0,1]

w2 = -svm_clf.coef_[0, 0]/svm_clf.coef_[0, 1]
b2 = -svm_clf.intercept_[0]/svm_clf.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') #label = Iris-versicolor
plt.plot(X[:, 0][y==0], X[:, 1][y==0], 'yo') #label = Iris-setosa
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개의 숫자를 분류해야 합니다. 

   처리 속도를 높이기 위해 작은 검증 세트로 하이퍼파라미터를 조정하는 것이 좋습니다. 

   어느 정도까지 정확도를 높일 수 있나요?

#먼저, 데이터셋을 로드하고, 훈련/테스트 세트로 나눕니다. 

#train_test_split() 함수를 사용할 수 있지만 보통 처음 60,000개의 샘플을 훈련 세트로 사용하고,

#나머지 10,000개는 테스트 세트로 사용합니다.

#(이렇게 하면 다른 사람들의 모델과 성능을 비교하기 좋습니다.)


from sklearn.datasets import fetch_openml

mnist = fetch_openml('mnist_784', version=1)

X = mnist['data']

y = mnist['target']


X_train = X[:60000]

y_train = y[:60000]

X_test = X[60000:]

y_test = y[60000:]


# 많은 훈련 알고리즘은 훈련 샘플의 순서에 민감하므로 이를 섞는 것이 좋은 습관입니다.


np.random.seed(42)

rnd_idx = np.random.permutation(60000)

X_train = X_train[rnd_idx]

y_train = y_train[rnd_idx]


# 선형 SVM 분류기, 이 모델은 자동으로 OvA 전략을 사용하므로 특별히 할 것은 없습니다.


lin_clf = LinearSVC(max_iter = 10000, random_state=42)

lin_clf.fit(X_train, y_train)

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)

#훈련 세트에 대한 예측을 만들어 정확도를 측정해 보겠습니다.

#최종 모델을 선택해 훈련한 것이 아니기 때문에 아직 테스트 세트 사용 X


from sklearn.metrics import accuracy_score


y_pred = lin_clf.predict(X_train)

accuracy_score(y_train, y_pred)

0.86765

#MNIST 에서 이 정도 정확도는 나쁜 성능입니다.

#선형 모델이 MNIST 문제에 너무 단순하기 때문이지만, 데이터의 스케일을 먼저 조정합니다.


scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train.astype(np.float32))

X_test_scaled = scaler.transform(X_test.astype(np.float32))


lin_clf = LinearSVC(max_iter = 10000, random_state=42)

lin_clf.fit(X_train_scaled, y_train)

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)

y_pred = lin_clf.predict(X_train_scaled)

accuracy_score(y_train, y_pred)

0.9271833333333334

#훨씬 좋아졌지만, 여전히 좋은 성능은 아닙니다.(MNIST에서)

#SVM을 사용한다면 커널 함수를 사용해야 합니다. RBF커널(기본값)로 SVC 적용

#scikit-learn 0.19이하 버전을 사용한다면 기본적으로 OvO 전략을 사용할 것이므로,

#decision_function_shape='ovr'로 지정해주어야 합니다.(이것은 0.19 이후버전은 기본값)


svm_clf = SVC(gamma='auto', decision_function_shape='ovr')

svm_clf.fit(X_train_scaled[:10000], y_train[:10000])

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)

y_pred = svm_clf.predict(X_train_scaled)

accuracy_score(y_train, y_pred)

0.9475833333333333

#이제 좋은 성능이 나옵니다. 6배나 적은 데이터에서 모델을 훈련시켰지만 더 좋은 성능을 얻었습니다.

#교차 검증을 사용한 랜덤 서치로 하이퍼파라미터 튜닝을 해봅니다. 

#진행을 빠르게 하기 위해 작은 데이터셋으로 작업합니다.


from sklearn.model_selection import RandomizedSearchCV

from scipy.stats import reciprocal, uniform


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

rnd_search_cv = RandomizedSearchCV(svm_clf, param_distributions, 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:   13.1s finished
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 0x7f02e56e97f0>, 'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x7f02e56e9588>},
          pre_dispatch='2*n_jobs', random_state=None, refit=True,
          return_train_score='warn', scoring=None, verbose=2)

rnd_search_cv.best_estimator_

SVC(C=10.136573776743578, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.0010505123928869005,
  kernel='rbf', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False)

rnd_search_cv.best_score_

0.871

#점수는 낮지만 1000개의 샘플만 사용했음을 기억해야 합니다.

#전체 데이터셋으로 최선의 모델을 재훈련 시켜보겠습니다.

#아주 오랜 시간이 걸릴 지 모릅니다..


rnd_search_cv.best_estimator_.fit(X_train_scaled, y_train)

SVC(C=10.136573776743578, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.0010505123928869005,
  kernel='rbf', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False)

y_pred = rnd_search_cv.best_estimator_.predict(X_train_scaled)

accuracy_score(y_train, y_pred)


#아주 훌륭합니다. 이 모델을 선택하여 테스트 세트로 모델을 테스트합니다.

y_pred = rnd_search_cv.best_estimator_.predict(X_test_scaled)

accuracy_score(y_test, y_pred)

0.9728

#나쁘지 않지만 확실히 모델이 과대적합되었습니다.

#하이퍼파라미터를 조금 더 수정할 수 있지만, (ex, C, gamma 감소)

# 그렇게 하면 테스트 세트에 과대적합될 위험이 있습니다.

#다른 사람들은 하이퍼파라미터 C=5, gamma=0.005에서 더 나은 성능(98%이상)을 얻었습니다.

#훈련 세트를 더 많이 사용해서 더 오래 랜덤 서치를 수행하면 이런 값을 얻을 수 있을 지 모릅니다.



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

from sklearn.datasets import fetch_california_housing


housing = fetch_california_housing()

X = housing['data']

y= housing['target']


#훈련 / 테스트 세트로 나눕니다.

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)


from sklearn.preprocessing import StandardScaler


scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train)

X_test_scaled = scaler.transform(X_test)


#LinearSVR 훈련


lin_svr = LinearSVR(max_iter = 10000, random_state=42)

lin_svr.fit(X_train_scaled, y_train)

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)

#훈련 세트에 대한 성능 확인


from sklearn.metrics import mean_squared_error


y_pred = lin_svr.predict(X_train_scaled)

mse = mean_squared_error(y_train, y_pred)

mse

0.9567664024747587

#rmse 확인

np.sqrt(mse)

0.9781443668880165


#훈련 세트에 대한 타깃은 만달러 단위입니다. 

#RMSE는 기대할 수 있는 정도의 에러를 대략 가늠하게 도와줍니다. (에러가 클 수록 큰 폭으로 증가)

#이 모델의 에러가 대략 10,000 달러 정도로 예상할 수 있습니다.

#썩 훌륭하지 않으니 RBF 커널이 더 나을지 확인해 보겠습니다.

#하이퍼파라미터 C와 gamma의 적절한 값을 찾기 위해 교차 검증을 사용한 랜덤 서치 적용


from sklearn.svm import SVR

from sklearn.model_selection import RandomizedSearchCV

from scipy.stats import reciprocal, uniform


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

rnd_search_cv = RandomizedSearchCV(SVR(), param_distributions, 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:  2.1min finished
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 0x7f02e5a32cc0>, 'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x7f02e5a32f60>},
          pre_dispatch='2*n_jobs', random_state=42, refit=True,
          return_train_score='warn', scoring=None, verbose=2)

rnd_search_cv.best_estimator_

SVR(C=4.745401188473625, cache_size=200, coef0=0.0, degree=3, epsilon=0.1,
  gamma=0.07969454818643928, kernel='rbf', max_iter=-1, shrinking=True,
  tol=0.001, verbose=False)

#이제 훈련 세트에서 RMSE 측정


y_pred = rnd_search_cv.best_estimator_.predict(X_train_scaled)

mse = mean_squared_error(y_train, y_pred)

np.sqrt(mse)

0.5727524770785356

#선형 모델보다 훨씬 나아졌고, 모델을 선택하여 테스트 세트에서 평가해봅니다.


y_pred = rnd_search_cv.best_estimator_.predict(X_test_scaled)

mse = mean_squared_error(y_test, y_pred)

np.sqrt(mse)

0.592916838552874


 블로그 

출처


이 글의 상당 부분은  [핸즈온 머신러닝, 한빛미디어/오렐리앙 제롱/박해선] 서적을 참고하였습니다.


나머지는 부수적인 함수나 메서드에 대해 부족한 설명을 적어두었습니다.

학습용으로 포스팅 하는 것이기 때문에 복제보다는 머신러닝에 관심이 있다면 구매해보시길 추천합니다.


도움이 되셨다면 로그인 없이 가능한

아래 하트♥공감 버튼을 꾹 눌러주세요! 



반응형