# 11. 이진 분류, 선형 분류 모델, 로지스틱 회귀, 지원 벡터 기계
*본 글은 '비즈니스를 위한 데이터 과학(포스터 프로보스트, 톰 포셋 지음, 한빛출판사)' 4장과
'파이썬 라이브러리를 활용한 머신러닝(안드레아스 뮐러, 세라 가이도 지음, 한빛출판사)' 2장을 참고하여 작성되었습니다.
*필자가 배운 것을 조합해 작성한 것이므로 오류가 있을 수 있습니다.
1. 분류용 선형모델¶
1.1 이진 분류 Binary Classification¶
$\hat{y} = w[0]x[0] + w[1]x[1] + w[2]x[2] +...+ w[p]x[p] + b$
이진 분류의 예측을 위한 방정식은 선형 회귀와 비슷하다.
다른 점은 특성들의 가중치의 합을 그대로 사용하는 것이 아니라, 임계치 0과 비교해서 사용한다는 것이다.
예측한 값이 0보다 작으면 클래스를 -1이라고 예측, 0보다 크면 +1이라고 예측한다.
이진분류이기 때문에 둘 중 하나의 클래스에 분류되는 것이며, 그 기준이 임계치 0이 되는 것이다.
이처럼 분류용 선형 모델에서는 선, 평면, 초평면을 사용해 데이터를 두 개의 클래스로 구분한다.
1.2 분류 트리 모델과 선형 모델의 차이점¶
이전에 공부했던 분류 모델(분류 트리)에서는 데이터를 순수한 부분집합으로 만드는 특성을 정보증가량 순서로 선택해 분할해왔다.
그래서 좌표평면에 모델의 분류 결과를 표현하면 여러개의 선들에 의해 데이터가 여러 계층들로 분할되어 있는 것을 확인할 수 있다.
특성의 정보증가량IG을 계산해서 분류했다는 것은, 속성들이 가중치(파라미터)를 계산해서 모델을 학습한 것이라고 생각하면 된다.
그러나 선형 모델에 기반한 여러 데이터 마이닝 절차의 프레임워크는 파라미터(가중치)를 알아낼 필요 없이 속성과 모델 구조만 지정하면
모델이 데이터 마이닝 기법을 적용해서 훈련 데이터 세트에 최적화된 파라미터를 계산할 후 있다.
이때 속성은 속성 선택 절차(이전 글에서 알아본 L1, L2제약), 데이터 과학자의 전문 지식 등으로 선택한다.
이처럼 데이터 과학자는 모델의 형태와 속성만을 지정하며, 모델이 파라미터를 튜닝한다.
이러한 기법을 파라미터 학습parameter learning 또는 파라메트릭 모델링parametric modeling이라고 한다.
해당 데이터 마이닝 절차를 가진 모델들은 선형 모델에 기반하고 있다.
그리고 같은 선형 모델이어도 목적 함수의 형태에 따라 선형 회귀, 로지스틱 회귀, 지원 벡터 기계 등으로 구분된다.
1.3 선형 모델의 선형 분류자(분류 트리와는 무엇이 다른가?)¶
선형 모델은 객체 공간을 선형 분류자linear classifier로 분할한다. 이 선형 분류자는 선택된 속성들의 가중치의 합weighted sum과 같다.
선형 모델은 최적의 모델 파라미터를 찾아내 그것들의 가중치 합인 최적화된 선형 분류자로 데이터를 분류하고자 한다. 모델을 데이터에 맞추는 것이다.
선형 분류자는 차원에 따라(속성의 개수에 따라) 결정선(2차원), 결정면(3차원), 초평면(4차원 이상)의 형태를 띤다.
분류 트리와 비교해보면 데이터를 감독 세분화 한다는 것은 동일하나,
트리 구조 모델이 한 번에 하나의 속성을 사용해서 연속적으로 분할하는 것에 반해서
선형 모델은 가중치의 합으로 표현한 선형 분류자를 통해 여러 속성을 한꺼번에 사용해 분류한다.
1.4 선형 함수에서의 데이터 마이닝¶
선형 함수에 사용된 속성들의 가중치가 파라미터라고 했다.
데이터 마이닝 절차에서 최적화된 파라미터를 찾아내 그것들의 표현식인 선형 분류자를 사용해, 파라미터화된 모델이 데이터 세트에 맞도록 해야 한다.
그러므로 선형 분류자를 고른다는 것은 곧 최적화된 파라미터를 찾는 것과 같다.
그러나 최적화된 파라미터를 찾는 것은 쉽지 않다. 계층을 분할하는 선은 무수히 많으며, 각각의 선이 가진 파라미터 값은 전부 다르다.
따라서 우리는 해결하고자 하는 문제의 목적에 알맞는 선형 분류자를 선택할 줄 알아야 한다.
이는 곧 어떤 가중치가 중요한가? 와 같은 본질적인 문제로 돌아가게 한다. 우리는 목적 함수(목표)를 제대로 표현하는 가중치를 선택해야 한다.
목적 함수(최적화된 선형 분류자)를 찾는 방법에는 여러 가지가 있다.
이번 글에서는 로지스틱 회귀 분석, 지원 벡터 기계에 대해서 알아보자.
표준 선형 함수에 관한 글을 이전 글 #10을 확인하자.
2. 로지스틱 회귀 Logistic Regression¶
로지스틱 회귀 분석은 선형 모델을 사용해 계층 확률을 추정하는 방법이다.
계층 확률 추정은 아래 두 가지 조건을 만족해야 한다.
- 정확한 확률 추정: 계층a에 속할 확률이 0.2라면 실제 100중에서 20개의 객체는 계층a에 속해야 한다.
- 변별력 있는 확률 추정: 객체마다 의미가 있는 확률추정. (어느 객체에 사건이 일어날 것인지 변별 가능)
그러나 로지스틱 회귀 분석을 이해하려면, 계층 확률을 추정하려는 것 때문에 발생하는 문제를 해결해야 한다.
바로 추정값의 ($\hat{y}$) 범위는 $-\infty$에서 $\infty$인데 반해 확률값의 범위는 0에서 1까지 뿐이라는 것이다.
어떻게 해야 이들 범위를 통일할 수 있을까?
2.1 확률과 승산¶
자세히 다루지는 않겠지만, 승산odd의 개념으로 확률을 무한대의 범위로 표현할 수 있다.
승산은 '몇 대 몇'이라고 생각하면 된다. 일어날 사건이 80%라면, 80:20의 비율로 일어날 확률과 그렇지 않을 확률을 표현할 수 있다.
따라서 80%의 확률은 80:20 = 4:1이 되고, 승산 4로 표현할 수 있다.
다른 예로 일어날 확률이 0.99999%라고 한다면 99999:1, 승산은 99999가 된다.
그러면 확률을 승산으로 표현하면 범위는 '0에서 1까지'에서 '0에서 $\infty$까지'로 확장된다.
음수의 영역은 어떻게 할까? 음의 무한대를 표현하기 위해서는 승산에 로그를 취한다.
결과적으로 어떤 사건이 일어날 확률을 승산으로 표현하고 그것에 로그를 취하면 함숫값과 확률 사이의 범위 문제를 해결할 수 있다.
2.2 로지스틱 회귀 모델에서 선형 함수(선형 분류자)의 의미¶
로지스틱 회귀 모델로 돌아와서 거꾸로 생각해보자.
우리는 확률을 추정하고자 로지스틱 회귀 분석 모델을 사용하려고 한다.
로지스틱 회귀 모델의 선형 함수$f(x)$을 통해 함숫값을 알면 그것의 로그 승산을 추정할 수 있다. (함숫값과 로그 승산은 서로 대응되므로)
그리고 로그 승산을 통해 우리가 앞에서 했던 과정을 거슬러가면 확률을 알아낼 수 있게 된다.
따라서 로지스틱 회귀 모델에서 우리는 어떤 객체가 특정 계층에 속할 확률을 알아낼 필요가 없다.
선형 함수로 로그 승산을 추정한 후 확률을 알아낼 수 있기 때문이다.
따라서 이 모델에서는 사건의 확률보다는 사건 그 자체만 고려하면 된다.
2.3 정리¶
확률 추정에 있어 로지스틱 회귀 분석은 표준 선형 모델을 사용한다.
로지스틱 회귀 모델의 선형 분류자의 함숫값은 계층에 속할 로그 승산이다.
로그 승산은 계층에 속할 확률로 변환할 수 있다.
3. 지원 벡터 기계 Support Vector Machine¶
지원 벡터 기계도 선형 모델을 기반으로 하는 기법이다. 선형 판별식을 사용한다.
지원 벡터 기계가 모델을 데이터에 맞추는 방식은 로지스틱 회귀 방식과 조금 다르다.
지원 벡터 기계가 파라미터를 데이터에 최적화하기 위해 사용하는 목적 함수는 무엇인가?
3.1 지원 벡터 기계의 목적 함수¶
객체 공간 상에서 선형 분류자의 위치는 파라미터의 값에 따라 무수히 많다고 했다.
로지스틱 회귀 분석에서는 두 개의 계층으로 구분하는 선형 분류자의 모양은 선이다.
그러나 지원 벡터 기계의 목적 함수는 선형 판별식을 찾아내기 전에 먼저 두꺼운 막대 모양(두 개의 평행한 점선으로 표현)으로 두 계층을 분할한다.
지원 벡터 기계의 목적 함수는 두 개의 평행한 점선의 거리가 서로 멀 수록(막대가 두꺼울수록) 좋은 모델이라고 생각한다.
일단, 가장 두꺼운 막대를 찾아내면 선형 판별식은 막대의 중앙을 통과한다.
3.2 지원 벡터 기계의 장점¶
이처럼 칼같이 분류하지 않는 성격 때문에 지원 벡터 기계는 새로운 데이터를 잘못 분류할 가능성이 낮아진다.
(예시)a계층에 속해야 하는 객체가 b계층에 속하는 걸로 잘못 판단되려면 선형 분류자를 넘어서야 할 뿐만 아니라
b계층 위에 있는 점선 분류자를 통과해야한 한다.
또한, '선형 분류자로 두 계층을 완벽히 분할할 수 없는 데이터셋'을 다뤄야 할 경우 지원 벡터 기계가 도움이 된다.
반드시 모든 데이터 포인트를 올바르게 분류할 필요는 없기 때문에, 여러 선형 분류자 중에서 고민할 필요 없이 지원 벡터 기계를 사용하면 된다.
4. 구현하기¶
분류용 선형 모델에서는 선형 함수가 곧 선형 분류자(결정 경계)이다.
선형 모델을 학습시키는 방법은 아래 두 가지가 있다.
- 최적의 파라미터 조합(계수와 절편의 조합)이 데이터에 얼마나 잘 맞는지 측정
- 사용할 수 있는 규제 검토하기
import matplotlib.pyplot as plt
import numpy as np
import mglearn
import sklearn
from sklearn.linear_model import LogisticRegression #로지스틱 회귀 알고리즘 불러오기
from sklearn.svm import LinearSVC #서포트 벡터 머신 알고리즘 불러오기
X, y = mglearn.datasets.make_forge() #forge 데이터셋 불러오기
fig, axes = plt.subplots(1, 2, figsize = (10, 3)) #객체 공간 표현하기. 1row 2column, 사이즈는 가로10 세로3
#알고리즘과 좌표평면 각각 대응시키기고 for문을 사용해서 각각의 상황에 대해 모델 학습시키기.
for model, ax in zip([LinearSVC(), LogisticRegression()], axes):
clf = model.fit(X, y)
mglearn.plots.plot_2d_separator(clf, X, fill = False, eps = 0.5, ax = ax, alpha = .7)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax = ax)
ax.set_title("{}".format(clf.__class__.__name__))
ax.set_xlabel("trait 0")
ax.set_ylabel("trait 1")
axes[0].legend()
/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/deprecation.py:87: FutureWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn warnings.warn(msg, category=FutureWarning) /opt/anaconda3/lib/python3.9/site-packages/sklearn/svm/_base.py:1206: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations. warnings.warn(
<matplotlib.legend.Legend at 0x7fce53f32160>
로지스틱 회귀와 지원 벡터 기계 모두 L2규제(릿지)를 사용한다. (필자는 과소적합을 막는 규제라고 생각하며 떠올렸다.)
가중치의 영향력을 규제하는 매개변수는 C이다.
C의 값이 높을수록 모델은 훈련 세트에 최적화하려고 하고, 낮을수록 계수의 절댓값이 0에 가까워지게 만든다.
다르게 말하면 매개변수C 값이 높을수록 모델은 개별 데이터 포인트를 정확하게 분류하고자 한다.
#정규화 과정(매개변수로 규제)에 따른 결정 경계 변화 확인하기
#오른쪽으로 갈 수록 C값이 높다. 선형 분류자가 개별 데이터 포인트의 영향을 많이 받는다.
mglearn.plots.plot_linear_svc_regularization()
위와 같은 2차원 데이터(특성1, 특성2)를 분류하는 선형 분류자는 직선의 모양을 띠고 있지만,
세 개 이상의 여러 특성을 사용해 구현한 선형 분류자는 평면, 초평면의 모양으로 표현된다.
고차원 데이터의 분류에서는 선형 모델의 과적합을 막는 일이 더욱 중요해진다.
유방암 데이터셋으로 여러 특성을 가진 데이터셋을 분류하는 과정을 살펴보자.
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer #유방암 데이터셋 불러오기
cancer = load_breast_cancer() #데이터셋을 객체cancer로 표현
X_train, X_test, y_train, y_test = train_test_split( #훈련, 시험 데이터셋으로 나누기
cancer.data, cancer.target, stratify = cancer.target, random_state = 42) #X, y에 대응하는 필드 지정, 난수생성기 설정
logreg = LogisticRegression().fit(X_train, y_train) #로지스틱 회귀모델로 훈련 데이터로 학습.
#함수 안에 매개변수를 안 넣었으므로 C는 기본값 1로 설정
print("훈련 세트 점수: {:.3f}".format(logreg.score(X_train, y_train))) #훈련 세트 점수 소숫점 3자리까지 표현
print("시험 세트 점수: {:.3f}".format(logreg.score(X_test, y_test))) #시험 세트 점수 소숫점 3자리까지 표현
훈련 세트 점수: 0.948 시험 세트 점수: 0.951
/opt/anaconda3/lib/python3.9/site-packages/sklearn/linear_model/_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT. Increase the number of iterations (max_iter) or scale the data as shown in: https://scikit-learn.org/stable/modules/preprocessing.html Please also refer to the documentation for alternative solver options: https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression n_iter_i = _check_optimize_result(
#훈련 세트와 시험 세트의 점수가 너무 비슷한 것으로 보아 과소적합된 것.
#L2 매개변수 C의 값이 너무 높아서 그런 것이므로 C의 제약을 풀어준다. (C의 값을 증가시킨다.)
#함수 안에 매개변수 C = 100으로 지정.
logreg100 = LogisticRegression(C = 100).fit(X_train, y_train)
print("훈련 세트 점수: {:.3f}".format(logreg100.score(X_train, y_train)))
print("시험 세트 점수: {:.3f}".format(logreg100.score(X_test, y_test)))
훈련 세트 점수: 0.948 시험 세트 점수: 0.965
/opt/anaconda3/lib/python3.9/site-packages/sklearn/linear_model/_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT. Increase the number of iterations (max_iter) or scale the data as shown in: https://scikit-learn.org/stable/modules/preprocessing.html Please also refer to the documentation for alternative solver options: https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression n_iter_i = _check_optimize_result(
#매개변수의 값이 100이 되면서 시험 세트의 점수가 개선되었다. 복잡도가 올라가면서 모델의 성능이 좋아진 것이다.
#이번에는 반대로 규제를 더 강하게 해서 C = 0.01의 상황으로 만들어보자.
logreg0001 = LogisticRegression(C = 0.001).fit(X_train, y_train)
print("훈련 세트 점수: {:.3f}".format(logreg0001.score(X_train, y_train)))
print("시험 세트 점수: {:.3f}".format(logreg0001.score(X_test, y_test)))
훈련 세트 점수: 0.948 시험 세트 점수: 0.944
/opt/anaconda3/lib/python3.9/site-packages/sklearn/linear_model/_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT. Increase the number of iterations (max_iter) or scale the data as shown in: https://scikit-learn.org/stable/modules/preprocessing.html Please also refer to the documentation for alternative solver options: https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression n_iter_i = _check_optimize_result(
#이미 C = 1의 상황에서 과소적합이었으므로 0.01일 때는 과소적합의 상황이 더 심화되었다.
#상대적으로 모델이 훈련 데이터를 충분히 학습하지 못해 점수는 C = 1일때보다 더 낮다.
#L2
plt.plot(logreg.coef_.T, 'o', label = "C = 1")
plt.plot(logreg100.coef_.T, '^', label = "C = 100")
plt.plot(logreg0001.coef_.T, 'v', label = "C = 0.001")
plt.xticks(range(cancer.data.shape[1]), cancer.feature_names, rotation = 90)
plt.hlines(0, 0, cancer.data.shape[1])
plt.ylim(-5, 5)
plt.xlabel("trait")
plt.ylabel("coefficient size")
plt.legend()
#육안으로 쉽게 구분되지는 않지만 자세히 보면 계수에 대한 규제가 심할수록(C값이 작을수록) 계수의 크기가 0에 가까운 것을 확인할 수 있다.
<matplotlib.legend.Legend at 0x7fce80d969a0>
#L1
#L L2와 달리 몇 개의 속성을 선택해서 사용하지만 보다 이해하기 쉽다.
#결과 좌표평면을 보면 이진분류에서의 선형 모델과 회귀 선형 모델의 유사점이 많다는 것을 확인할 수 있다.
for C, marker in zip([0.001, 1, 100], ['o', '^', 'v']):
lr_l1 = LogisticRegression(C = C, penalty = "l1", solver = "liblinear").fit(X_train, y_train)
#scikit-learn이 업데이트 되면서 LogisticRegression의 기본 solver가 lbfgs로 바뀌었다.
#이 solver는 l1을 지원하지 않으므로, 지원하는 솔버인 liblinear로 바꿔주었다.
print("C = {:.3f}인 l1로지스틱 회귀의 훈련 정확도: {:.2f}".format(C, lr_l1.score(X_train, y_train)))
print("C = {:.3f}인 l1로지스틱 회귀의 테스트 정확도: {:.2f}".format(C, lr_l1.score(X_test, y_test)))
plt.plot(lr_l1.coef_.T, marker, label = "C = {:.3f}".format(C))
plt.xticks(range(cancer.data.shape[1]), cancer.feature_names, rotation = 400)
plt.hlines(0, 0, cancer.data.shape[1])
plt.ylim(-5, 5)
plt.xlabel("trait")
plt.ylabel("coefficient size")
plt.legend(loc = 3)
C = 0.001인 l1로지스틱 회귀의 훈련 정확도: 0.91 C = 0.001인 l1로지스틱 회귀의 테스트 정확도: 0.92 C = 1.000인 l1로지스틱 회귀의 훈련 정확도: 0.96 C = 1.000인 l1로지스틱 회귀의 테스트 정확도: 0.96 C = 100.000인 l1로지스틱 회귀의 훈련 정확도: 0.99 C = 100.000인 l1로지스틱 회귀의 테스트 정확도: 0.98
<matplotlib.legend.Legend at 0x7fce70553250>
'배우는 것 > Maching Learning' 카테고리의 다른 글
#12. 결정 트리, 특성중요도, 결정 트리 앙상블, 부트스트랩 샘플링 (0) | 2021.12.29 |
---|---|
#10. 선형 모델, 선형 회귀, 릿지 회귀, 라쏘 회귀, 복잡도 제어 (0) | 2021.12.27 |
#9. 과적합, 과소적합, 일반화, 이전 글에 대한 배경지식 확장 (0) | 2021.12.27 |
#8. 최근접 이웃 알고리즘 실습, 이웃의 개수가 지도학습에 미치는 영향 (0) | 2021.12.27 |
#7. 파이썬 기본지식 공부(2)_스칼라형, 튜플, 리스트, 사전 (0) | 2021.12.23 |