#!/usr/bin/env python # coding: utf-8 # # **SVM을 이용한 뉴스 Topic 분류기** # 뉴스 데이터를 활용하여 주제를 구분하는 모델을 만든다 # 1. **Tf-IDF** # 1. **SVM** # ## **1 스펨메일을 활용한 Tf-Idf를 정의한다** # 1. Naive Bayse 에서 실습한 분류기 학습을 복습한다 # 1. 텍스트 데이터 **임베딩을 Tf-Idf** 로 생성한다 # 1. 앞의 **Smoothing / Naive Bayse** 보다, **Tf-IDF / Naive Bayse** 의 AUC가 더 큼을 알 수 있다. # # ### **01 Naive Bayse 내용의 복습** # 앞에서 진행한 파일 불러오기 및 전처리 작업을 진행합니다 # In[2]: # 앞에서 진행한 내용 복습 # 정상매일은 0, 스펨매일은 1 import glob,os emails, labels = [], [] for no, file_path in enumerate(['./data/enron1/ham/','./data/enron1/spam/']): for filename in glob.glob(os.path.join(file_path, '*.txt')): with open(filename, 'r', encoding = "ISO-8859-1") as infile: emails.append(infile.read()) labels.append(no) # In[4]: from nltk.corpus import names from nltk.stem import WordNetLemmatizer def letters_only(astr): for c in astr: if not c.isalpha(): return False return True def clean_text(docs): cleaned_docs = [] for doc in docs: cleaned_docs.append(' '.join([lemmatizer.lemmatize(word.lower()) for word in doc.split() if letters_only(word) and word not in all_names])) return cleaned_docs # In[4]: all_names = set(names.words()) lemmatizer = WordNetLemmatizer() cleaned_emails = clean_text(emails) # In[5]: from sklearn.model_selection import StratifiedKFold import numpy as np k = 10 k_fold = StratifiedKFold(n_splits=k) cleaned_emails_np = np.array(cleaned_emails) labels_np = np.array(labels) smoothing_factor_option = [1.0, 2.0, 3.0, 4.0, 5.0] # In[6]: from collections import defaultdict auc_record = defaultdict(float) #
# # ### **02 Tf-IDf 객체를 생성한다** # 1. Naive Bayse 에서 실습한 분류기 학습을 복습한다 # 1. 이를 활용하여 Tf-Idf 를 생성해본다 # In[7]: get_ipython().run_line_magic('timeit', '') from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import roc_auc_score for train_indices, test_indices in k_fold.split(cleaned_emails, labels): X_train, X_test = cleaned_emails_np[train_indices], cleaned_emails_np[test_indices] Y_train, Y_test = labels_np[train_indices], labels_np[test_indices] tfidf_vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5, stop_words='english', max_features=8000) term_docs_train = tfidf_vectorizer.fit_transform(X_train) term_docs_test = tfidf_vectorizer.transform(X_test) for smoothing_factor in smoothing_factor_option: clf = MultinomialNB(alpha=smoothing_factor, fit_prior=True) clf.fit(term_docs_train, Y_train) prediction_prob = clf.predict_proba(term_docs_test) pos_prob = prediction_prob[:, 1] auc = roc_auc_score(Y_test, pos_prob) auc_record[smoothing_factor] += auc # In[8]: from pprint import pprint pprint(auc_record) # In[11]: print('max features smoothing fit prior auc') for smoothing, smoothing_record in auc_record.items(): print('\t8000 \t {0} \t true \t {1:.4f}'.format(smoothing, smoothing_record/k)) #
# # ## **2 SVM 개념** # 1. 최적의 **Hyperplane (데이터가 잘 구분된 최적의 공간)** 을 찾는다 # 1. 이를 구분하는 데이터 포인트 들을 **Support Vectors**라고 한다 # # #
# # ### **01 SVM의 원리** # 1. 시나리오1 : **Hyperplane**로 데이터 선별 **ex) $wx + b > 0$ , $wx + b < 0$** 로 분할 # 1. 시나리오2 : 최적의 **Hyperplane** 결정 # 1. 시나리오3 : **Outlier(이상치)** 처리 - 에러값이 최소가 되게끔 해야 한다 #
# # ### **02-1 2개 Topic을 구분하는 SVM (전처리)** # 1. 특정한 뉴스토픽 데이터를 불러온 뒤 # 1. 테스트 데이터를 전처리 작업 후 # 1. Tf-IDf 로 임베딩 한다 # In[14]: # 구분할 2개의 카테고리 데이터를 불러온다 from sklearn.datasets import fetch_20newsgroups categories = ['comp.graphics', 'sci.space'] data_train = fetch_20newsgroups(subset='train', categories=categories, random_state=42) data_test = fetch_20newsgroups(subset='test', categories=categories, random_state=42) # In[15]: # 불러온 데이터들을 전처리 작업한다 cleaned_train = clean_text(data_train.data) label_train = data_train.target cleaned_test = clean_text(data_test.data) label_test = data_test.target # In[19]: # 개별 뉴스그룹내 비슷한 분포로 0, 1 데이터가 분포되어 있다 from collections import Counter print('훈련 데이터 : {} \n테스트 데이터 : {}'.format( Counter(label_train), Counter(label_test))) # In[20]: # 데이터를 Tf-IDF 로 임베딩 변환한다 tfidf_vectorizer = TfidfVectorizer(sublinear_tf = True, max_df = 0.5, stop_words = 'english', max_features = 8000) term_docs_train = tfidf_vectorizer.fit_transform(cleaned_train) term_docs_test = tfidf_vectorizer.transform(cleaned_test) #
# # ### **02-2 2개 Topic을 구분하는 SVM (학습)** # 1. Kernal 파라미터는 **Linear**로 설정 # 1. 패널티 값은 1로 설정 # 1. SVM 알고리즘 모델을 초기화 한다 # In[21]: # 위에서 정의한 파라미터로 모델을 학습한다 from sklearn.svm import SVC svm = SVC(kernel='linear', C=1.0, random_state=42) svm.fit(term_docs_train, label_train) # In[22]: # 학습한 모델을 활용하여 데이터를 예측해본다 accuracy = svm.score(term_docs_test, label_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) #
# # ### **03 여러개 Topic을 구분하는 SVM** # 1. **다중 클래스를** 분류하는 모델 # 1. **K-클래스의** 문제를 **K개의 서로다른 분류기를** 생성한다 # 1. **일대 일 방법**은 전체 중 **일부를 사용하여** 컴퓨팅 속도등이 빠르다 # 1. **일대 다 방법**은 **전체 데이터를** 사용하여 **서로 다른 모듈을** 만든다 # # # In[24]: # 다양한 클래스의 뉴스데이터 불러오기 categories = [ 'alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space', 'rec.sport.hockey'] data_train = fetch_20newsgroups(subset='train', categories=categories, random_state=42) data_test = fetch_20newsgroups(subset='test', categories=categories, random_state=42) # In[26]: get_ipython().run_line_magic('time', '') # 뉴스그룹 데이터 전처리 작업 cleaned_train = clean_text(data_train.data) label_train = data_train.target cleaned_test = clean_text(data_test.data) label_test = data_test.target # In[28]: get_ipython().run_line_magic('time', '') # Tf-IDF 로 임베딩 term_docs_train = tfidf_vectorizer.fit_transform(cleaned_train) term_docs_test = tfidf_vectorizer.transform(cleaned_test) # SVM 모델을 정의하고 학습한다 svm = SVC(kernel='linear', C=1.0, random_state=42) svm.fit(term_docs_train, label_train) # In[29]: # 학습 모델의 정확도 측정 accuracy = svm.score(term_docs_test, label_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) # In[30]: # 학습된 내용을 자세히 살펴보기 from sklearn.metrics import classification_report prediction = svm.predict(term_docs_test) report = classification_report(label_test, prediction) print(report) #
# # ## **3 SVM 커널 함수들** # **RBF 커널을** 활용하여 **선형모델로 분류할 수 없는** 문제를 해결 (XOR) 가능하다 #
# # ### **01 RBF 커널 살펴보기** # 1. 각각이 다른값을 갖는 데이터에 **RBF함수를** 적용해본다 # 1. 특별한 목정이 정해지지 않은면 **RBF**를 많이 사용한다 # 1. **Polynomial kernel(다항)** 커널은 변경해야 하는 파라미터가 많고 # 1. **Sigmoid kernel(시그모이드)** 커널은 특정한 파라미터가 지정되었을 때 효과가 좋기 때문이다 # In[50]: import numpy as np X = np.c_[(.3,-.8),(-1.5, -1),(-1.3,-.8),(-1.1,-1.3),(-1.2,-.3),(-1.3, -.5),(-.6,1.1),(-1.4, 2.2), (1,1), (1.3,.8), (1.2, .5), (.2, -2), (.5,-2.4), (.2, -2.3), (0, -2.7), (1.3, 2.1)].T X.shape # In[48]: gamma_option = [1,2,4] Y = [-1] * 8 + [1] * 8 Y # In[53]: get_ipython().run_line_magic('matplotlib', 'inline') import matplotlib.pyplot as plt plt.figure(1, figsize=(4*len(gamma_option), 4)) for i, gamma in enumerate(gamma_option, 1): svm = SVC(kernel='rbf', gamma=gamma) svm.fit(X,Y) plt.subplot(1, len(gamma_option), i) plt.scatter(X[:,0], X[:,1], c=Y, zorder=10, cmap=plt.cm.Paired) plt.axis('tight') XX, YY = np.mgrid[-3:3:200j, -3:3:200j] Z = svm.decision_function(np.c_[XX.ravel(), YY.ravel()]) Z = Z.reshape(XX.shape) plt.pcolormesh(XX, YY, Z > 0, cmap=plt.cm.Paired) plt.contour(XX, YY, Z, color=['k','k','k'], linestyles=['--','-','--'], levels=[-.5, 0, .5]) plt.title('gamma=%d'%gamma) plt.show() #
# # ### **02 선형 커널 함수와 RBF 커널 함수의 비교** # 1. RBF 방식의 커널이 항상 유용한 것은 아니듯, 선형이 유용한 경우도 존재한다 # 1. 시나리오1 : **Feacture** 갯수와 **Sample의** 갯수가 **모두 큰 경우** 계산량이 복잡하면 선형모델로 구분하는게 더 효과적이다 # 1. 시나리오2 : **Feacture** 갯수가 학습데이터 샘플 갯수보다 **훨씬 큰 경우** RBF는 Overfitting이 잘 일어나기 때문이다 # 1. 시나리오3 : Feacture 갯수보다 **학습데이터 샘플의 갯수가 훨씬 많은 경우** # 1. 위의 3가지 경우가 아니라면 **RBF**를 사용하여 분석해보자 #
# # ## **4 SVM을 활용한 News Topic 분류** # 1. 뉴스그룹 20개의 데이터를 불러와서 정제작업을 진행한다 # 1. 분류모델은 **선셩회귀식**을 사용한다 # 1. **category 검정은** 반복문이 아닌 Sklearn의 **GridSearchCV를** 사용한다 #
# # ### **01 SVC를 활용한 모델의 학습** # # > **GridSearchCV()** # # 1. **데이터 분할, 폴드 생성, 교차학습 및 검증, 가장 효과적인 파라미터 추출등을** 함께 진행한다 # 1. **n_jobs = -1 :** 개별 코어들을 병렬로 진행한다 # 1. **cv = 3** : 3폴드 교차검증을 진행한다 # In[54]: # 학습할 데이터를 불러온다 categories = None data_train = fetch_20newsgroups(subset='train', categories=categories, random_state=42) data_test = fetch_20newsgroups(subset='test', categories=categories, random_state=42) # 데이터를 전처리 작업을 진행한다 cleaned_train = clean_text(data_train.data) label_train = data_train.target cleaned_test = clean_text(data_test.data) label_test = data_test.target # Tf-IDF로 텍스트를 임베딩 작업 진행한다 tfidf_vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5, stop_words='english', max_features=8000) term_docs_train = tfidf_vectorizer.fit_transform(cleaned_train) term_docs_test = tfidf_vectorizer.transform(cleaned_test) # In[32]: # 텍스트 데이터를 분류하는 경우 선형함수를 사용한다 arameters = {'C': [0.1, 1, 10, 100]} svc_libsvm = SVC(kernel='linear') # 전체 20개의 파라미터를 순차적 검증하기 위하여 for 반복문 대신에 from sklearn.model_selection import GridSearchCV grid_search = GridSearchCV(svc_libsvm, parameters, n_jobs=-1, cv=3) # In[32]: # 위에서 정의한 모델을 활용하여 학습을 진행한다 import timeit start_time = timeit.default_timer() grid_search.fit(term_docs_train, label_train) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) print(grid_search.best_params_) print(grid_search.best_score_) # In[56]: get_ipython().run_line_magic('time', '') # 최적화된 파라미터로 세팅된 SVM모델을 Test 한다 svc_libsvm_best = grid_search.best_estimator_ accuracy = svc_libsvm_best.score(term_docs_test, label_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) #
# # ### **02 SVC 모델의 튜닝 1** # > **LinearSVC()** # # 1. 원본 데이터세트를 활용하여 튜닝을 한다 # 1. sklearn에서 제공하는 **LinearSVC()** 모듈을 사용해보자 # 1. SVC와 유사하지만, **libsvm** 모듈 대신에, **liblinear** 를 기반으로 구성 # 1. 모델생성 속도측면에서 10배 이상 빠르다 # In[57]: # LinearSVC() 를 활용하여 분류모델을 생성한다 from sklearn.svm import LinearSVC svc_linear = LinearSVC() grid_search = GridSearchCV(svc_linear, parameters, n_jobs=-1, cv=3) start_time = timeit.default_timer() grid_search.fit(term_docs_train, label_train) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) print(grid_search.best_params_) print(grid_search.best_score_) # In[58]: # 생성한 모델의 성능을 비교평가한다 svc_linear_best = grid_search.best_estimator_ accuracy = svc_linear_best.score(term_docs_test, label_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) #
# # ### **03 SVC 모델의 튜닝 2** # > **Pipeline()** # # 1. Feacture 추출기를 **Pipeline API로** 교체한다 # 1. max_df : 문서빈도 중 최대값을 설정 # 1. max_feature : 중요하게 모델에서 학습 할 Feacture 수 # 1. sublinear_tf : 로그함수 또는 다른함수를 이용하여 출현빈도를 변환 # 1. smooth_idf : 용어의 출현 빈도에 대한 Smoothing 초기값 # 1. Grid 검색모델은 **파이프라인 전체에** 걸쳐서 **최적의 Setting을** 찾는다 # In[59]: # Tf-IDF 추출기와 SVM 분류기를 1개의 PipeLine로 묶는다 from sklearn.pipeline import Pipeline pipeline = Pipeline([ ('tfidf', TfidfVectorizer(stop_words='english')), ('svc', LinearSVC()), ]) # In[60]: # Pipe Line 단계의 이름과 파라미터를 묶어서 키로 설정한다 parameters_pipeline = { 'tfidf__max_df': (0.25, 0.5), 'tfidf__max_features': (40000, 50000), 'tfidf__sublinear_tf': (True, False), 'tfidf__smooth_idf': (True, False), 'svc__C': (0.1, 1, 10, 100),} # In[61]: grid_search = GridSearchCV(pipeline, parameters_pipeline, n_jobs=-1, cv=3) start_time = timeit.default_timer() grid_search.fit(cleaned_train, label_train) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) print(grid_search.best_params_) print(grid_search.best_score_) # In[62]: pipeline_best = grid_search.best_estimator_ accuracy = pipeline_best.score(cleaned_test, label_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) #
# # ## **5 SVM을 활용한 심전도 데이터로 태아의 상태 분류** # 1. 뉴스그룹 20개의 데이터를 불러와서 정제작업을 진행한다 # 1. 분류모델은 **선셩회귀식**을 사용한다 # 1. **category 검정은** 반복문이 아닌 Sklearn의 **GridSearchCV를** 사용한다 # In[71]: # 태아의 심전도 데이터 불러오기 import pandas as pd df = pd.read_excel('./data/CTG.xls', 'Raw Data') X = df.iloc[1:2126, 3:-2].values Y = df.iloc[1:2126, -1].values # 데이터 분포비율 분석 Counter(Y) # In[73]: # Test를 위해 20% 데이터를 분리한다 from sklearn.model_selection import train_test_split X_train, X_test, Y_train, Y_test = train_test_split( X, Y, test_size=.2, random_state=42) # In[77]: # RBF 기반 SVM모델을 튜닝한다 svc = SVC(kernel='rbf') parameters = {'C':(100, 1e3, 1e4, 1e5), 'gamma':(1e-8, 1e-7, 1e-6, 1e-5)} grid_search = GridSearchCV(svc, parameters, n_jobs=-1, cv=3) start_time = timeit.default_timer() grid_search.fit(X_train, Y_train) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) print(grid_search.best_params_) print(grid_search.best_score_) # 최적화된 모델의 파라미터를 출력 svc_best = grid_search.best_estimator_ svc_best # In[84]: # 최적화 파라미터를 사용하여 모델을 Test 한다 accuracy = svc_best.score(X_test, Y_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) # In[87]: # 개별 클래스멸 성능을 측정한다 prediction = svc_best.predict(X_test)#, Y_test) report = classification_report(Y_test,prediction) print(report)