갈루아의 반서재

scikit-learn (2) - 선형분류 (훈련데이터 만들기)


1. 훈련데이터셋 만들기


이 문제는 속성에 따라 인스턴스의 레이블을 지정하는 분류 문제의 한 종류이다. 선택한 두 속성과 목적값으로 만든 본래 데이터셋의 일부 데이터이다. 데이터셋을 임포트한 후 약 75%의 인스턴스를 무작위로 선택하고 남은 인스턴스를 평가 목적을 위해 보유한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> from sklearn import datasets
>>> iris = datasets.load_iris()
>>> X_iris, y_iris = iris.data, iris.target
>>> print X_iris.shape, y_iris.shape
 
(1504) (150,)
 
>>> from sklearn.cross_validation import train_test_split
>>> from sklearn import preprocessing
>>> # 처음 두개의 속성으로 데이터셋을 얻는다
>>> X, y = X_iris[:, :2], y_iris
>>> # 훈련데이터와 테스트데이터로 나눈다. 25%의 테스트데이터를 구한다.
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=33)
>>> print X_train.shape, y_train.shape
 
(1122) (112,)
cs


train_test_split 함수는 자동으로 표본을 무작위로 선택해 훈련 데이터와 평가 데이터를 나눈다. 75% 인스턴스를 훈련데이터로 선택하므로, 전체 150개 중 112개가 훈련데이터셋에 편입된다.그런데, 문제는 아이리스 데이터셋을 살펴보면, 목적 범주 순서로 인스턴스가 나열되어 있다. 즉, 이대로 훈련데이터셋을 만들게 되면 범주 0 과 1 의 비율이 본래의 데이터셋에 비해 높아지게 되는 것이다.

1
2
3
4
5
>>> print (y_iris[0:111])
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2]
>>>
cs


random_state 파라메터를 이용하여 목적범주별로 적절히 섞는다. 아래와 같이 적절히 섞였음을 볼 수 있다.

1
2
3
4
5
>>> print (y_train[0:111])
[1 0 1 1 1 0 0 1 0 2 0 0 1 2 0 1 2 2 1 1 0 0 2 0 0 2 1 1 2 2 2 2 0 0 1 1 0
 1 2 1 2 0 2 0 1 0 2 1 0 2 2 0 0 2 0 0 0 2 2 0 1 0 1 0 1 1 1 1 1 0 1 0 1 2
 0 0 0 0 2 2 0 1 1 2 1 0 0 1 1 1 0 1 1 0 2 2 2 1 2 0 1 0 0 0 2 1 2 1 2 1 2]
>>>
cs



2. 스케일링 (값의 표준화)


이제 필요한 것은 값의 표준화이다. 큰 값을 가진 속성이 최종 결과에 너무 많은 영향을 주지 않도록 하는 것이다.각 속성에 대해 평균을 계산하고, 각 속성값에서 평균을 뺀 후 표준 편차로 결과를 나눈다. 스케일링을 한 후 각 속성의 평균은 0 이 되고, 표준 편차는 1 을 갖는다.

1
2
3
4
>>> # 속성을 표준화한다
>>> scaler = preprocessing.StandardScaler().fit(X_train)
>>> X_train = scaler.transform(X_train)
>>> X_test = scaler.transform(X_test)
cs



 3. 훈련 인스턴스 분포 확인


그럼 앞서 만든 훈련데이터셋의 분포를 2차원 공간에서 확인해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> %matplotlib inline
>>> 
>>> import matplotlib
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> colors = ['red''greenyellow''blue']
>>> for i in xrange(len(colors)):
>>>     xs = X_train[:,0][y_train == i]
>>>     ys = X_train[:,1][y_train == i]
>>>     plt.scatter(xs, ys, c=colors[i])
>>> plt.legend(iris.target_names) 
>>> plt.xlabel('Sepal length')
>>> plt.ylabel('Sepal width')
>>> 
 
<matplotlib.text.Text at 0x7f89ce027690>
cs


scatter 함수는 각 인스턴스의 첫 번째 속성값(꽃받침 너비) 대 두 번째 속성값(꽃받침 길이), 각기 다른 색인 목적 범주를 사용해 도식화했다. 이로써, 속성이 어떻게 영향을 주어 목적 범주를 결정하는지 알 수 있다.


4. 학습 태스크 재정의


3의 이미지에서 빨간색의 Setosa 품종과 다른 품종의 구별은 뚜렷한 반면, 다른 두 품종의 구별은 모호하다. 그러므로 우리는 다음의 질문들에 답해야 한다. .

- 속성들이 해결하고자 하는 태스크에 유리한가?
- 새로운 속성을 더 추가해야 하는가?
- 새로운 기법을 개발해야 하는가?

즉, 학습 태스크를 재정의해야 한다. 우리의 목표는 아이리스 꽃의 인스턴스를 고려해 목표는 세토사인지 아닌지를 예측하는 일이다. 문제를 이진 분류 태스크로 변환한다. 즉 현재의 3개의 목적 범주를 2개의 목적 범주(세토사, 세토사 아님)로 변환하는 것이다.

최적으로 두 범주를 구별하는 직선을 만들어 결정 경계(decision boundary)로 사용한다. 여기서는 확률적 기울기 강하법(Stochastic Gradient Descent)의 SDGClassifier 를 사용한다. 알고리즘은 손실함수를 최소로 만드는 초평면의 계수를 학습한다. 이를 위해서는 해당 분류기 오브젝트를 만들고 훈련 데이터로 모델을 훈련시킨다. 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> from sklearn.linear_model import SGDClassifier
>>> clf = SGDClassifier()
>>> clf.fit(X_train, y_train)
 
SGDClassifier(alpha=0.0001, average=False, class_weight=None, epsilon=0.1,
       eta0=0.0, fit_intercept=True, l1_ratio=0.15,
       learning_rate='optimal', loss='hinge', n_iter=5, n_jobs=1,
       penalty='l2', power_t=0.5, random_state=None, shuffle=True,
       verbose=0, warm_start=False)
 
>>> print clf.coef_
 
[[-34.40522613   4.82101696]
 [  7.45335653 -12.21001281]
 [  9.80914958  -7.53435314]]
 
>>> print clf.intercept_
 
[-11.70270038  -2.86524274   1.45188531]
cs

line 3 에서 보는 fit 메소드는 scikit-learn 에서 가장 중요하다. 훈련 데이터와 훈련 범주를 입력받아 분류기를 만든다.

모든 미래 분류 결정은 초평면에서만 한다. clf 오브젝트와 coef_ 속성은 선형 경계의 계수를 나타내며, intercept_ 속성은 y 축과 경계선의 교차점을 나타낸다. 위의 In [6], [7] 의 결과를 통해 확인해보자. 즉, 위에서 출력된 값을 통해 다음 방정식을 그릴 수 있다.

-11.70270038 - 34.40522613*x1 + 4.82101696*x2 = 0

x1 과 x2(실제 속성값)를 고려해 방정식의 좌측을 계산한다. 이 값이 0 보다 크면 결정경계보다 위쪽(빨간색 측)에 있게 되고, 그렇지 않으면 경계보다 아래 쪽에 있게 된다.

그런데 여기서 계수 매트릭스는 왜 세 개의 행을 가지는가? 앞서 작업을 재정한 것을 기법에 알려주지 않아 아직도 세 개의 범주 문제로 다루고 있기 때문이다. 문제를 하나 대 나머지 모두로 설정해 이진 분류로 변환한다. 다음 코드는 3개의 결정 경계를 그린다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> x_min, x_max = X_train[:,0].min() - .5, X_train[:,0].max() +.5
>>> y_min, y_max = X_train[:,1].min() - .5, X_train[:,1].max() +.5
>>> xs = np.arange(x_min, x_max, 0.5)
>>> fig = plt.figure()
>>> fig, axes = plt.subplots(1,3)
>>> fig.set_size_inches(106)
>>> for i in [012]:
>>>     axes[i].set_aspect('equal')
>>>     axes[i].set_title('Class '+ str(i) + ' versus the rest')
>>>     axes[i].set_xlabel('Sepal length')
>>>     axes[i].set_ylabel('Sepal width')
>>>     axes[i].set_xlim(x_min, x_max)
>>>     axes[i].set_ylim(y_min, y_max)
>>>     pylab.sca(axes[i])
>>>     plt.scatter(X_train[:,0], X_train[:, 1], c=y_train, cmap=plt.cm.prism)
>>>     ys = (-clf.intercept_[i] - xs * clf.coef_[i, 0]) / clf.coef_[i, 1]
>>>     plt.plot(xs, ys, hold=True)
>>>     plt.show()
 
<matplotlib.figure.Figure at 0x7f4578b0a450>
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-d5ca27aa75c3> in <module>()
     12     axes[i].set_xlim(x_min, x_max)
     13     axes[i].set_ylim(y_min, y_max)
---> 14     pylab.sca(axes[i])
     15     plt.scatter(X_train[:,0], X_train[:, 1], c=y_train, cmap=plt.cm.prism)
     16     ys = (-clf.intercept_[i] - xs * clf.coef_[i, 0]) / clf.coef_[i, 1]
 
/root/anaconda/envs/tensorflow/lib/python2.7/site-packages/matplotlib/pyplot.pyc in sca(ax)
    913             m.canvas.figure.sca(ax)
    914             return
--> 915     raise ValueError("Axes instance argument was not found in a figure.")
    916 
    917 
 
ValueError: Axes instance argument was not found in a figure.
cs

※ 코드대로 실행을 시켰으나 위와 같은 에러메시지가 발생. 혹시 이에 대한 해결 방법을 알고 계신 분은 알려주시기 바랍니다. 스택오버플로우에 올렸는데 아직 답이 없네요.


http://stackoverflow.com/questions/40399631/valueerror-axes-instance-argument-was-not-found-in-a-figure