제목 기계 학습의 A to Z : scikit-learn을 활용한 기계 학습(모델 평가)
분류 Etc 조회수 1582
작성일자 2014.09.19 출처 마이크로소프트웨어 8월호
첨부파일   작성자 dbguide
 
 

◎ 연재기사 ◎

기계 학습의 A to Z : scikit-learn을 활용한 기계 학습(모델 평가)
기계 학습의 A to Z : scikit-learn을 활용한 기계 학습 (차원 축소)
기계 학습의 A to Z : scikit-learn을 활용한 기계 학습 (군집화, Clustering)
기계 학습의 A to Z : IPython notebook과 scikit-learn을 활용한 기계 학습
기계 학습의 A to Z : scikit-learn을 활용한 기계 학습(문서 분류)
기계 학습의 A to Z : 인구 변화 시각화와 예측

기계 학습의 A to Z

scikit-learn을 활용한 기계 학습(모델 평가)

 

문제를 해결하는 데에는 여러 가지 방법이 있으며, 사람들은 목적에 따라 가장 적절한 방법을 선택한다. 기계 학습도 기법에 따라 결과가 다르고, 같은 기법을 사용하더라도 매개 변수에 따라 결과가 다르기 때문에 기법으로 만들어진 모델을 평가하는 방법은 매우 중요하다. 지난 시간 붓꽃(iris) 데이터에 k 최근접 이웃 알고리즘(kNN, k-Nearest Neighbors algorithm)을 사용했으며 매개 변수를 다양하게 조절해 새로운 붓꽃 데이터를 잘 예측할 수 있도록 했다. 지난 글의 코드를 다시 본다.

 

모델 평가(evaluation)


 

<리스트 1> 붓꽃 데이터로 학습한 kNN 모델 평가 import numpy as np from sklearn import datasets from sklearn.neighbors import KNeighborsClassifier # load data iris = datasets.load_iris() X, y = iris.data, iris.target knn = KNeighborsClassifier(n_neighbors=2) knn.fit(X, y) print(knn.score(X, y)) # 결과 0.98

<리스트 1>의 코드와 같이 iris에 붓꽃 데이터를 로드하고 KNeighborsClassifier으로 적합화한다. 지난 코드와 한 가지 다른 것은 마지막에 score 메소드를 호출한 점이다. scikit learn의 모든 기법 클래스에는 score라는 메소드가 있고, 기법에 해당하는 각기 다른 평가법을 제공한다. score 메소드는 knn.predict(X)으로 예측한 값과 실제값 y와 비교해 정확하게 예측한 비율로 반환한다. 매개 변수 n_neighbors를 2로 설정하고 전체 데이터로 학습한 후, 데이터를 예측하면 98%로 정확하다. n_neighbors를 3, 7, 8로 설정하여 예측값을 다시 구하면 각각 0.96, 0.96, 0.96이다. 매개 변수 n_neighbors가 2일 때, 가장 잘 예측한다. 하지만 데이터의 특성상 다른 기법을 적용해도 잘 예측될 수도 있다는 의심이 생긴다. scikit learn은 훈련 데이터에 가장 빈도가 높은 목적 속성이나 훈련 데이터 목적 속성의 분산에 따라 예측하는 DummyClassifier을 제공하여 다른 기법과 비교할 수 있게 한다.

 

 

<리스트 2> DummyClassifier 모델의 평가 from sklearn.dummy import DummyClassifier dummy = DummyClassifier(strategy='stratified',random_state=0) #다른 매개 변수 stratified, most_frequent dummy.fit(X, y) print(dummy.score(X, y)) # 결과 0.36

DummyClaissifier를 사용한 후 앞서 적용한 kNN 기법이 대단히 유효하다는 점을 알 수 있다. 최소한 대충 풀어서 풀 수 없는 문제를 kNN이 잘 하고 있는 것이다. 이는 데이터의 특성이 가장 기본적인 방법으로 구별을 할 수 없는 다소 복잡한 형태를 가지고 있다는 점을 알려준다. score 메소드가 각 기법에 알맞게 구현되어 있다고 하더라도 해결해야 하는 문제가 좀 다른 특성을 가지고 있으면 다른 평가 방법이 필요할 때가 있다. scikit learn에는 다양한 측정 함수와 적용하기 편리한 함수가 포함돼 있다. <리스트 3>과 같이 accuracy_score를 사용한다.
 

 

 

<리스트 3> accuracy_score 함수로 평가 from sklearn.metrics import accuracy_score knn.fit(X, y) print(accuracy_score(knn.predict(X), y)) # 결과 0.98

결과값은 0.98이다. <리스트1>의 knn.score()를 호출한 값과 같다. 실제 knn의 score메소드 코드 안을 들어다 보면 ClassifierMixin을 상속 받은 KNeighborsClassifier의 score 메소드를 호출하게 돼 있다. <리스트 4>는 <리스트 3>과 똑같은 코드다. 더 나아가 자신이 필요한 score 함수를 만들어 평가 할 수도 있다. make_scorer 함수가 이를 위해 필요하다.
 

 

 

<리스트 4> KNeighborsClassifier가 상속 받은 ClassifierMixin의 score 메소드 class ClassifierMixin(object): from .metrics import accuracy_score return accuracy_score(y, self.predict(X), sample_weight=sample_weight)

machine_learning01.png

 


<그림 1> 기법에 따른 score 메소드 구분과 상속 관계
 

지금까지 DummyClaissifier으로 데이터의 형태가 간단하게 구별할 수 없는 점과 kNN 기법이 상당히 잘 예측하고 있음을 알았다. 또한, 다른 측정 함수로 kNN 기법을 평가하는 방법을 보았다. 그러면 매개 변수인 k를 8에서 20까지 변경하면서 제일 예측력이 좋은 k값을 찾아본다.
 

 

 

<리스트 5> 최적의 k값 검색 result = [] score = [] k_range = range(8, 20) for k in k_range: result.append(k) knn = KNeighborsClassifier(n_neighbors=k) knn.fit(X, y) print('k is %d, score is %f' %(k, knn.score(X, y))) score.append(knn.score(X, y))

결과는 <리스트 6>과 같다.
 

 

 

<리스트 6> 결과 k is 8, score is 0.980000 k is 9, score is 0.980000 k is 10, score is 0.980000 k is 11, score is 0.973333 k is 12, score is 0.980000 k is 13, score is 0.980000 k is 14, score is 0.980000 k is 15, score is 0.986667 k is 16, score is 0.986667 k is 17, score is 0.980000 k is 18, score is 0.973333 k is 19, score is 0.980000

machine_learning02.png

 


<그림 2> 결과 그래프
 

k가 15, 16일 때 score 값이 가장 높다. 기본적으로 복잡도가 낮은 것이 유리하기 때문에 k를 15로 정한다. 그러면 이 k값이 새로운 데이터를 가장 잘 예측할 수 있는 값일까.

결론부터 말t)가 될 수 있다. 훈련 데이터에 너무 치우쳐 적합화돼 학습한 데이터와 다소 다른 데이터를 예측할 때 예측력은 현저히 떨어진다. 실제로도 훈련 데이터에서는 좋은 예측력을 얻지만 학습하지 않은 데이터로 테스트를 할 때 예측이 좋지 않다. 그러면 어떻게 모델이 과적합화 됐는지 알 수 있을까.

 

홀드 아웃(Hold out)과 교차 검증(Cross-validation)

과적합화를 탐지하기 위해 데이터를 훈련 데이터와 테스트 데이터를 나누어 평가하는 홀드 아웃(hold out)을 사용한다. scikit learn은 훈련 데이터와 테스트 데이터를 분할하는 다양한 함수를 포함하고 있다. 먼저 train_test_split 함수는 입력 받은 일정 비율로 데이터를 훈련 데이터와 테스트 데이터로 나눈다. <리스트 7>처럼 훈련 데이터와 테스트 데이터를 나눠 kNN을 다시 평가한다.
 

 

 

<리스트 7> train_test_split롤 훈련 데이터와 테스트 데이터 분할 from sklearn.cross_validation import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0) k_range = range(3, 20) train_scores = [] test_scores = [] for k in k_range: knn = KNeighborsClassifier(n_neighbors=k) knn.fit(X_train, y_train) train_score = knn.score(X_train, y_train) train_scores.append(train_score) test_score = knn.score(X_test, y_test) test_scores.append(test_score) print('k is %d, train score=%f, test score=%f' % (k, train_score, test_score)) print("Train score's mean %f" % (sum(train_scores)/len(k_range))) print("Test score's mean %f" % (sum(test_scores)/len(k_range)))

<리스트 8>은 <리스트 7>의 결과 중 일부다.
 

 

 

<리스트 8> 일부 결과 k is 5, train score=0.960000, test score=0.973333 k is 6, train score=0.960000, test score=0.973333 k is 7, train score=0.986667, test score=0.973333 k is 8, train score=0.986667, test score=0.960000 k is 9, train score=0.960000, test score=0.973333 k is 10, train score=0.960000, test score=0.973333 k is 11, train score=0.973333, test score=0.973333 k is 12, train score=0.973333, test score=0.960000 k is 13, train score=0.986667, test score=0.973333 k is 14, train score=0.960000, test score=0.946667 k is 15, train score=0.960000, test score=0.960000 k is 16, train score=0.960000, test score=0.933333 k is 17, train score=0.960000, test score=0.946667

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

machine_learning03.png

 


<그림 3> 결과 그래프
 

훈련 데이터로 학습하고, 데이터 그대로 테스트를 할 경우 k가 15, 16에서 가장 좋은 결과를 얻었다. 하지만 데이터를 나눠 일부 데이터로 훈련하고 훈련할 때 사용하지 않은 나머지 데이터로 테스트할 경우 15나 16에서 결과가 좋지 않다. 이를 통해 k가 15, 16일 때 모델이 훈련 데이터로 과적합화돼 있다는 사실을 알 수 있다. 가지고 있는 데이터로 충분히 훈련하고 훈련 데이터 이외 데이터로 테스트하는 것이 모델에 효과적이다. 이를 위해 k 중첩 교차 검증(k fold cross validation)이라는 방법을 사용한다. k 중첩 교차 검증의 k값은 전체 데이터를 나눌 개수다. 가령, 다음 그림과 같이 k가 2라면 전체 데이터를 2개로 나누고 첫 번째 데이터로 모델을 훈련하고 두 번째 데이터로 모델을 테스트한다. 그리고 또 다시 두 번째 데이터로 모델을 훈련하고 첫 번째 데이터로 모델을 테스트한다. 하지만 계산량은 두 배로 증가한다.
 



machine_learning04.png

 


<그림 4> k가 2인 2 중첩 교차 검증
 

scikit learn는 역시 k 중첩을 만드는 함수를 제공한다. KFold 함수의 매개 변수를 데이터 전체 개수와 원하는 중첩개수인 k로 설정한다. 다음 코드는 k를 다섯 개로 설정해 훈련 데이터와 테스트 데이터를 분할한 후 총 다섯 번 계산한다.
 

 

 

<리스트 9> KFold 함수로 교차 검증 from sklearn.cross_validation import KFold folds = 5 kf = KFold(len(y), n_folds=folds, indices=True) k_range = range(3, 20) score_means = [] for k in k_range: test_scores = [] knn = KNeighborsClassifier(n_neighbors=k) for train, test in kf: X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test] knn.fit(X_train, y_train) score = knn.score(X_test, y_test) test_scores.append(score) # print('k is %d, test score is %f' % (k, score)) score_means.append(sum(test_scores)/folds) print("K is %d, test score's mean %f" % (k, sum(test_scores)/folds))

<리스트 10>은 <리스트 9>의 결과 중 일부다.
 

 

 

<리스트 10> 일부 결과 K is 3, test score's mean 0.906667 K is 4, test score's mean 0.906667 K is 5, test score's mean 0.913333 K is 6, test score's mean 0.906667 K is 7, test score's mean 0.920000 K is 8, test score's mean 0.913333

machine_learning05.png

 


<그림 5> 결과 그래프
 

이렇게 해서 모델을 훈련 데이터와 테스트 데이터로 훈련, 평가할 수 있다. 하지만 k중첩에서 k값이 늘어나면 k번만큼 계산량도 늘어난다. k값은 모델이 정확히 예측하는데 매우 중요한 역할을 할 뿐만 아니라 계산에도 큰 영향을 미친다. 이를테면 100,000 * 1,000(데이터 수와 속성 수)인 데이터에 k값을 매우 높게 정한다면 시간이 많이 걸리며, 컴퓨팅 파워가 좋은 시스템이 필요하다. 그럼, k값은 어떻게 정해야 할까? 일반적으로 k는 10을 사용한다. 총 데이터의 90%를 훈련 데이터로 사용하고 나머지 10%를 테스트를 하여 총 10번에 테스트를 실시한다는 의미다. 이는 여러 가지 방법으로 증명된 합리적인 수치이다. 훈련과 테스트를 모두 할 수 있을 만큼 데이터가 크지 않다면 어떠할까. 데이터 하나를 제외하고 모든 데이터로 훈련하고 남긴 데이터로 테스트하는 방법을 사용한다. 이 방법을 단일 잔류(leave one out)라고 한다. 데이터를 최대한 훈련하는데 사용할 수 있으나, 계산량은 그만큼 늘어가게 된다. 단일 잔류를 위한 데이터 분할도 상당히 간단하다. LeaveOneOut 함수에 총 데이터 수를 넣으면 데이터를 자동으로 분할한다. 붓꽃 데이터가 150개이기 때문에 총 150번 계산한다.(<리스트 11> 참조)
 

 

 

<리스트 11> LeaveOneOut 함수로 교차 검증 from sklearn.cross_validation import LeaveOneOut loo = LeaveOneOut(len(y)) print(loo) knn = KNeighborsClassifier(n_neighbors=5) scores = [] for train, test in loo: X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test] knn.fit(X_train, y_train) test_score = knn.score(X_test, y_test) scores.append(test_score) # print('test score=%f' % (test_score)) print('mean score %f' % (sum(scores)/150)) # mean score 0.966667

데이터를 분할하는 여러 가지 함수뿐만 아니라, 평가 전체 과정을 쉽게 할 수 있도록 scikit learn은 cross_val_score 함수를 제공한다. cross_val_score 함수에 적용할 기법과 데이터를 입력하면 평가값을 출력한다.
 

 

 

<리스트 12> cross_val_score 함수 사용 from sklearn.cross_validation import cross_val_score, KFold kf = KFold(len(y), n_folds=5) print(kf) knn = KNeighborsClassifier(n_neighbors=5) scores = cross_val_score(knn, X, y, cv=kf) print(sum(scores)/5)

지난 글에서 언급했던 최적의 매개 변수를 찾아 주는 Grid SearchCV 함수도 그대로 사용할 수 있다.
 

 

 

<리스트 13> GridSearchCV와 KFold를 사용한 최적의 매개 변수 검색 from sklearn.grid_search import GridSearchCV from sklearn.cross_validation import cross_val_score, KFold, ShuffleSplit kf = KFold(len(y), n_folds=5) # kf = ShuffleSplit(n = len(X), n_iter=10) k_range = range(3, 20) parameters = {'n_neighbors':k_range} #parameters = {'n_neighbors':(1, 3, 10), 'weights':('uniform', 'distance')} knn_base = KNeighborsClassifier() grid_search = GridSearchCV(knn_base, parameters, cv=kf) grid_search.fit(X, y) print(grid_search) print(grid_search.best_params_) print(grid_search.grid_scores_)

출력 결과는 <리스트 14>와 같다.
 

 

 

<리스트 14> 일부 결과 mean: 0.90667, std: 0.09286, params: {'n_neighbors': 3}, mean: 0.90667, std: 0.09286, params: {'n_neighbors': 4}, mean: 0.91333, std: 0.08327, params: {'n_neighbors': 5}, mean: 0.90667, std: 0.09286, params: {'n_neighbors': 6}, mean: 0.92000, std: 0.08589, params: {'n_neighbors': 7}, mean: 0.91333, std: 0.08844, params: {'n_neighbors': 8},

<리스트 10>과 같은 결과다. GridSearchCV은 최적의 매개 변수인 {'n_neighbors': 7}를 찾아 주었기 때문에 편리하다. 더욱이 parameters가 더 많이 있다면 for문을 추가해 코드를 작성하지 않아도 되기 때문에 매개 변수가 많아질수록 GridSearchCV의 편의성은 점점 더 증대된다. 매개 변수 weights을 더 추가하여 다음과 같이 parameters를 변경하여 다시 실행한다.

parameters = {'n_neighbors':(1, 3, 10), 'weights':('uniform', 'distance')}

결과는 {'n_neighbors': 7, 'weights': 'distance'}로 최적의 매개 변수를 찾아낸다.

 

통합

이제 모두를 합하여 보자. 단지 추가한 것은 GridSearchCV의 매개 변수 중 cv에 KFold 값인 kf를 입력한 점이다.

 

 

<리스트 15> pipeline, GridSearchCV, KFold 사용 from sklearn.decomposition import PCA from sklearn.pipeline import Pipeline from sklearn.svm import SVC from sklearn.linear_model import LogisticRegression kf = KFold(len(y), n_folds=5) ## KNN estimators = [('pca', PCA()), ('knn', KNeighborsClassifier())] parameters = {'pca__n_components':(2, 3), 'knn__n_neighbors': (6,7,8,9,10,11,12,13), 'knn__weights':('uniform', 'distance')} ## SVM #estimators = [('pca', PCA()), ('svm', SVC())] #parameters = {'pca__n_components':(2, 3), 'svm__C':(0.4, 1, 1.5, 2, 3)} ## LogisticRegression #estimators = [('pca', PCA()), ('lr', LogisticRegression())] #parameters = {'pca__n_components':(2, 3), 'lr__penalty':( 'l1', 'l2')} pipeline = Pipeline(estimators) print(pipeline) grid_search = GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=1, cv=kf) print("Performing grid search...") print("pipeline:", [name for name, _ in pipeline.steps]) grid_search.fit(X, y) print("Best score: %0.3f" % grid_search.best_score_) print("Best parameters set:") best_parameters = grid_search.best_estimator_.get_params() for param_name in sorted(parameters.keys()): print("\t%s: %r" % (param_name, best_parameters[param_name]))

최적의 매개 변수는 다음과 같다.
Best score: 0.933
Best parameters set:
knn__n_neighbors: 7
knn__weights: 'distance'
pca__n_components: 3

 

결론

처음에는 가지고 있는 데이터를 모두로 모델을 훈련하고, 훈련한 데이터 그대로 평가하니 좋은 결과가 나왔다. 하지만 모델이 보지 못하던 데이터를 예측할 때, 예측력이 현저히 떨어지는 경우가 생긴다. 이를 파악하고, 피하는 방법으로 홀드 아웃과 k 중첩 교차 검증을 사용했다. scikit learn 라이브러리는 정확도를 계측할 수 있는 다양한 함수와 pipeline, GridSearchCV등과 자연스럽게 연결될 수 있도록 교차 검증 함수도 제공한다. 이러한 편의성은 사용자에게 데이터 자체에 집중할 수 있게 해주며 좀 더 뛰어난 성능을 가지는 모델을 만들 수 있도록 한다. 전체 코드는 https://github.com/brenden17/iris/blob/master/iris_cv.py에서 볼 수 있다.

 

출처 : 마이크로소프트웨어 8월호

제공 : 데이터 전문가 지식포털 DBguide.net

[출처] http://www.dbguide.net/knowledge.db?cmd=view&boardUid=181570&boardConfigUid=20&boardStep=&categoryUid=574

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
37 윈도우에서 easy_install, pip 사용하기. 졸리운_곰 2015.11.24 1450
36 윈도우에서 파이썬 설치하기 (virtualenv, pip 사용법) file 졸리운_곰 2015.11.24 707
35 How to parse JSON string in Python 졸리운_곰 2015.11.10 451
34 windows 에서 python 2.x 와 3.x 동시 설치 사용하기 졸리운_곰 2015.09.17 628
33 Title: HTML Scraper using pyhton 졸리운_곰 2015.09.01 400
32 기계 학습의 A to Z : 인구 변화 시각화와 예측 file 졸리운_곰 2015.05.21 1736
31 기계 학습의 A to Z : scikit-learn을 활용한 기계 학습(문서 분류) file 졸리운_곰 2015.05.21 2402
30 기계 학습의 A to Z : IPython notebook과 scikit-learn을 활용한 기계 학습 file 졸리운_곰 2015.05.21 2006
29 기계 학습의 A to Z : scikit-learn을 활용한 기계 학습 (군집화, Clustering) file 졸리운_곰 2015.05.21 2084
28 기계 학습의 A to Z : scikit-learn을 활용한 기계 학습 (차원 축소) file 졸리운_곰 2015.05.21 2182
» 기계 학습의 A to Z : scikit-learn을 활용한 기계 학습(모델 평가) [1] file 졸리운_곰 2015.05.21 1411
26 Tkinter로 하는 GUI 프로그래밍 file 졸리운_곰 2015.05.12 1628
25 위대한 LG SMART SMA LG 빅데이터 플랫폼 [파이썬 웹 크롤러] 웹 스파이더 file 졸리운_곰 2015.05.02 1154
24 위대한 LG SMART SMA LG 빅데이터 플랫폼 [소셜 웹 마이닝] 데이터 마이닝, 웹 마이닝 file 졸리운_곰 2015.05.02 775
23 Python CGI Programming file 졸리운_곰 2015.04.28 756
22 파이썬(Python)으로 CGI 프로그램 작성하기 졸리운_곰 2015.04.28 1403
21 winglet: 간단한 web crawler 졸리운_곰 2015.04.08 1748
20 python scrapy 예제, [python] Scrapy - 네이버 영화 파싱 졸리운_곰 2014.12.11 1496
19 python Scrapy 사용법 졸리운_곰 2014.12.11 2163
18 python 2.X 와 python 3.X 의 tkinter 코드 호환 유지 기법 : from Tkinter import * 에러시 졸리운_곰 2014.11.03 1767
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED