20 News Group Basic

8 minute read

Reference

  • 파이썬 머신러닝 완벽가이드 - 권철민
  • NCIA shkim.hi@gmail.com

20-뉴스그룹 분류

데이터 로딩과 데이터 구성 확인

from sklearn.datasets import fetch_20newsgroups

news_data = fetch_20newsgroups(subset='all',random_state=156)
## 기본제공해주는 파라미터 
print(type(news_data))
<class 'sklearn.utils.Bunch'>

Bunch type : scikit-learn 쪽에서 주로 사용하는 Bunch type. dict 와 유사한 객체이다.

print(news_data.keys())
dict_keys(['data', 'filenames', 'target_names', 'target', 'DESCR'])
print(type(news_data.data), type(news_data.target_names), type(news_data.target))
<class 'list'> <class 'list'> <class 'numpy.ndarray'>
import numpy as np
print(np.unique(news_data.target))
print(news_data.target_names)
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']
for i,val in zip(np.unique(news_data.target),news_data.target_names):
    print("index ({}) : topic {} ".format(i,val))
index (0) : topic alt.atheism 
index (1) : topic comp.graphics 
index (2) : topic comp.os.ms-windows.misc 
index (3) : topic comp.sys.ibm.pc.hardware 
index (4) : topic comp.sys.mac.hardware 
index (5) : topic comp.windows.x 
index (6) : topic misc.forsale 
index (7) : topic rec.autos 
index (8) : topic rec.motorcycles 
index (9) : topic rec.sport.baseball 
index (10) : topic rec.sport.hockey 
index (11) : topic sci.crypt 
index (12) : topic sci.electronics 
index (13) : topic sci.med 
index (14) : topic sci.space 
index (15) : topic soc.religion.christian 
index (16) : topic talk.politics.guns 
index (17) : topic talk.politics.mideast 
index (18) : topic talk.politics.misc 
index (19) : topic talk.religion.misc 
print(len(news_data.data), len(news_data.data[0]),len(news_data.data[1]))
print(len(news_data.target_names))
print(news_data.target.shape)
18846 1303 2944
20
(18846,)
print(news_data.data[0][:500])
From: egreen@east.sun.com (Ed Green - Pixel Cruncher)
Subject: Re: Observation re: helmets
Organization: Sun Microsystems, RTP, NC
Lines: 21
Distribution: world
Reply-To: egreen@east.sun.com
NNTP-Posting-Host: laser.east.sun.com

In article 211353@mavenry.altcit.eskimo.com, maven@mavenry.altcit.eskimo.com (Norman Hamer) writes:
> 
> The question for the day is re: passenger helmets, if you don't know for 
>certain who's gonna ride with you (like say you meet them at a .... church 
>meeting, yeah

데이터 설명

  • scikit-learn 내장 data
  • 영문 20개 topic data로, target 0 ~ 19 총 20개 topic으로 된 정답지가 있음
  • 18846 의 data(문단) 이 있으며, 각 문단별 길이는 모두 다름
import pandas as pd

print('target 클래스의 값과 분포도 \n', pd.Series(news_data.target).value_counts().sort_index())
print('target 클래스의 이름들 \n',news_data.target_names)
len(news_data.target_names), pd.Series(news_data.target).shape
target 클래스의 값과 분포도 
 0     799
1     973
2     985
3     982
4     963
5     988
6     975
7     990
8     996
9     994
10    999
11    991
12    984
13    990
14    987
15    997
16    910
17    940
18    775
19    628
dtype: int64
target 클래스의 이름들 
 ['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']





(20, (18846,))

학습 & 테스트용 데이터 생성

from sklearn.datasets import fetch_20newsgroups

# subset='train'으로 학습용(Train) 데이터만 추출, remove=('headers', 'footers', 'quotes')로 내용만 추출
# body 만 활용하기 위해 제거함
train_news= fetch_20newsgroups(subset='train', remove=('headers', 'footers', 'quotes'), random_state=156)
X_train = train_news.data
y_train = train_news.target
print(type(X_train))
print(len(X_train))
print(len(y_train))
<class 'list'>
11314
11314
print(train_news.data[0][:500])
What I did NOT get with my drive (CD300i) is the System Install CD you
listed as #1.  Any ideas about how I can get one?  I bought my IIvx 8/120
from Direct Express in Chicago (no complaints at all -- good price & good
service).

BTW, I've heard that the System Install CD can be used to boot the mac;
however, my drive will NOT accept a CD caddy is the machine is off.  How can
you boot with it then?

--Dave
# subset='test'으로 테스트(Test) 데이터만 추출, remove=('headers', 'footers', 'quotes')로 내용만 추출
test_news= fetch_20newsgroups(subset='test',remove=('headers', 'footers','quotes'),random_state=156)
X_test = test_news.data
y_test = test_news.target
print('학습 데이터 크기 {0} , 테스트 데이터 크기 {1}'.format(len(train_news.data) , len(test_news.data)))
학습 데이터 크기 11314 , 테스트 데이터 크기 7532

Count 피처 벡터화 변환과 머신러닝 모델 학습/예측/평가

  • 주의: 학습 데이터에 대해 fit( )된 CountVectorizer를 이용해서 테스트 데이터를 피처 벡터화 해야함.
  • 테스트 데이터에서 다시 CountVectorizer의 fit_transform()을 수행하거나 fit()을 수행 하면 안됨.
  • 이는 이렇게 테스트 데이터에서 fit()을 수행하게 되면 기존 학습된 모델에서 가지는 feature의 갯수가 달라지기 때문임.

sklearn.feature_extraction.text.CountVectorizer

CountVectorizer 설명

  • parameter 간단요약

    stop_words : 불용어 사전을 거치게 하는 것. english 중심이다.
    tokenizer : 내가 만든 tokenizer 객체를 불러올 수 있다. default = None
    ngram_range : n-gram 사용시 활용한다.
    max_df : ex> max_df=100 일 경우, 특정 단어가 100 번 이상 (전체어휘사전기준) 등장하면, 그 단어는 수식어,관용어(a,an,the) 일 수 있으니, 강제로 특정 값이상이면 어휘사전 구성시 제외
    min_df : 상기 max_df 컨셉의 반대

from sklearn.feature_extraction.text import CountVectorizer

# Count Vectorization으로 feature extraction 변환 수행. 
cnt_vect = CountVectorizer() ## max_df=100 . 100개 이상이 되는 단어는 무시한다... min_df : 반대의 의미
cnt_vect.fit(X_train)
X_train_cnt_vect = cnt_vect.transform(X_train) ## vector화 명령어

# 학습 데이터로 fit( )된 CountVectorizer를 이용하여 테스트 데이터를 feature extraction 변환 수행. 
X_test_cnt_vect = cnt_vect.transform(X_test) ## X_train 으로 완성한 어휘사전 기준으로 vectorize

print('학습 & 테스트 데이터 Text의 CountVectorizer Shape:',X_train_cnt_vect.shape, X_test_cnt_vect.shape)
학습 & 테스트 데이터 Text의 CountVectorizer Shape: (11314, 101631) (7532, 101631)
print(type(X_train_cnt_vect))
print(X_train_cnt_vect.todense().shape) ## csr_matrix 를 numpy matrix로 변환해준 이후, shape 확인
print(len(X_train_cnt_vect.todense()[0])) ## 메모리용량때문에 모두는 표현 불가
print(len(X_train_cnt_vect.todense()[1]))
<class 'scipy.sparse.csr.csr_matrix'>
(11314, 101631)
1
1

기본적으로 CSR_MATRIX 구조로 return한다.

print(X_train_cnt_vect[0].todense().shape) ## toarray() 도 가능
print(X_train_cnt_vect[1].todense().shape)
print(X_train_cnt_vect[21].todense().shape)
print(X_train_cnt_vect[0].toarray().shape) 
(1, 101631)
(1, 101631)
(1, 101631)
(1, 101631)

각 문단 크기는 columns : 101631 로 고정된것을 알 수 있다.

print(type(cnt_vect), type(X_train_cnt_vect))
<class 'sklearn.feature_extraction.text.CountVectorizer'> <class 'scipy.sparse.csr.csr_matrix'>
print([i for i in cnt_vect.vocabulary_.items()][:5])
[('what', 96391), ('did', 33551), ('not', 66511), ('get', 43217), ('with', 96917)]
print(X_train_cnt_vect[0].shape)
(1, 101631)
print(X_train_cnt_vect.ndim)
print(X_train_cnt_vect.shape)
print(type(X_train_cnt_vect[0]))
print(X_train_cnt_vect[0]) ## tuple 로 구성된 csr_matrix 를 보여준다.
2
(11314, 101631)
<class 'scipy.sparse.csr.csr_matrix'>
  (0, 2223)	1
  (0, 16251)	1
  (0, 16406)	1
  (0, 17936)	1
  (0, 18903)	1
  (0, 19756)	1
  (0, 20123)	1
  (0, 21987)	1
  (0, 23663)	2
  (0, 23790)	1
  (0, 24444)	1
  (0, 25370)	1
  (0, 25590)	3
  (0, 26271)	3
  (0, 26277)	1
  (0, 26992)	1
  (0, 28805)	1
  (0, 31939)	1
  (0, 33551)	1
  (0, 33799)	1
  (0, 35147)	2
  (0, 38824)	1
  (0, 41715)	1
  (0, 43217)	2
  (0, 43961)	2
  :	:
  (0, 49447)	1
  (0, 50300)	2
  (0, 51136)	3
  (0, 51326)	1
  (0, 56936)	1
  (0, 58921)	1
  (0, 58962)	1
  (0, 64435)	3
  (0, 66242)	1
  (0, 66511)	2
  (0, 67683)	1
  (0, 68102)	1
  (0, 73151)	1
  (0, 81774)	1
  (0, 87099)	2
  (0, 88519)	1
  (0, 88532)	4
  (0, 88587)	1
  (0, 89360)	1
  (0, 92875)	1
  (0, 93870)	1
  (0, 96391)	1
  (0, 96683)	1
  (0, 96917)	2
  (0, 100208)	2
print(X_train_cnt_vect[0:10].shape) ## 여전히 2차원임  
print(X_train_cnt_vect[0].ndim) ## 여전히 2차원임  
## X_train_cnt_vect 은 csr_matrix 구조로, array slice와는 다르다.
(10, 101631)
2
print(type(cnt_vect.inverse_transform(X_train_cnt_vect[0])))
cnt_vect.inverse_transform(X_train_cnt_vect[0])[0]
<class 'list'>





array(['120', 'about', 'accept', 'all', 'any', 'as', 'at', 'be', 'boot',
       'bought', 'btw', 'caddy', 'can', 'cd', 'cd300i', 'chicago',
       'complaints', 'dave', 'did', 'direct', 'drive', 'express', 'from',
       'get', 'good', 'heard', 'how', 'however', 'ideas', 'iivx', 'in',
       'install', 'is', 'it', 'listed', 'mac', 'machine', 'my', 'no',
       'not', 'off', 'one', 'price', 'service', 'system', 'that', 'the',
       'then', 'to', 'used', 've', 'what', 'will', 'with', 'you'],
      dtype='<U81')

Test numpy array slice 와 비교

data = np.array([[1, 0, 2],[0, 0, 3],[4, 5, 6]])
data.shape
(3, 3)
print(data[0].ndim)
data[0]
1





array([1, 0, 2])

print(y_train.shape)
y_train[0:10]
(11314,)





array([ 4, 15, 10,  2,  0,  0,  6, 18, 13, 17])
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# LogisticRegression을 이용하여 학습/예측/평가 수행. 
lr_clf = LogisticRegression(solver='liblinear')  # default:lbfgs
lr_clf.fit(X_train_cnt_vect , y_train)
pred = lr_clf.predict(X_test_cnt_vect)
print('CountVectorized Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test,pred)))
CountVectorized Logistic Regression 의 예측 정확도는 0.617

TF-IDF 피처 변환과 머신러닝 학습/예측/평가

from sklearn.feature_extraction.text import TfidfVectorizer

# TF-IDF Vectorization 적용하여 학습 데이터셋과 테스트 데이터 셋 변환. 
tfidf_vect = TfidfVectorizer()
tfidf_vect.fit(X_train)
X_train_tfidf_vect = tfidf_vect.transform(X_train)
X_test_tfidf_vect = tfidf_vect.transform(X_test)

print('학습 & 테스트 데이터 Text의 TfidfVectorizer Shape:',X_train_tfidf_vect.shape, X_test_tfidf_vect.shape)
학습 & 테스트 데이터 Text의 TfidfVectorizer Shape: (11314, 101631) (7532, 101631)
X_train_tfidf_vect[0]
<1x101631 sparse matrix of type '<class 'numpy.float64'>'
	with 55 stored elements in Compressed Sparse Row format>
# LogisticRegression을 이용하여 학습/예측/평가 수행. 
lr_clf = LogisticRegression(solver='liblinear')  # default:lbfgs
lr_clf.fit(X_train_tfidf_vect , y_train)
pred = lr_clf.predict(X_test_tfidf_vect)
print('TF-IDF Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test ,pred)))
TF-IDF Logistic Regression 의 예측 정확도는 0.678

stop words 필터링을 추가하고 ngram을 기본(1,1)에서 (1,2)로 변경하여 피처 벡터화

# stop words 필터링을 추가하고 ngram을 기본(1,1)에서 (1,2)로 변경하여 Feature Vectorization 적용.
tfidf_vect = TfidfVectorizer(stop_words='english', ngram_range=(1,2), max_df=300 )
tfidf_vect.fit(X_train)
X_train_tfidf_vect = tfidf_vect.transform(X_train)
X_test_tfidf_vect = tfidf_vect.transform(X_test)

lr_clf = LogisticRegression(solver='liblinear')  # default:lbfgs
lr_clf.fit(X_train_tfidf_vect , y_train)
pred = lr_clf.predict(X_test_tfidf_vect)
print('TF-IDF Vectorized Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test ,pred)))
TF-IDF Vectorized Logistic Regression 의 예측 정확도는 0.690

GridSearchCV로 LogisticRegression C 하이퍼 파라미터 튜닝

  • C(float, default=1.0) : Inverse of regularization strength(alpha); C=1/alpha, must be a positive float. Like in support vector machines, smaller values specify stronger regularization.
from sklearn.model_selection import GridSearchCV

# 최적 C 값 도출 튜닝 수행. CV는 3 Fold셋으로 설정. 
params = { 'C':[0.01, 0.1, 1, 5, 10]}
grid_cv_lr = GridSearchCV( lr_clf , param_grid= params, cv=3 , scoring='accuracy' , verbose=0 )
grid_cv_lr.fit(X_train_tfidf_vect , y_train)
print('Logistic Regression best C parameter :',grid_cv_lr.best_params_ )
Logistic Regression best C parameter : {'C': 10}
# 최적 C 값으로 학습된 grid_cv로 예측 수행하고 정확도 평가. 
pred = grid_cv_lr.predict( X_test_tfidf_vect)
print('TF-IDF Vectorized Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test ,pred)))
TF-IDF Vectorized Logistic Regression 의 예측 정확도는 0.704

사이킷런 파이프라인(Pipeline) 사용 및 GridSearchCV와의 결합

from sklearn.pipeline import Pipeline

# TfidfVectorizer 객체를 tfidf_vect 객체명으로, LogisticRegression객체를 lr_clf 객체명으로 생성하는 Pipeline생성
pipeline = Pipeline([
    ('tfidf_vect', TfidfVectorizer(stop_words='english', ngram_range=(1,2), max_df=300)),
    ('lr_clf', LogisticRegression(solver='liblinear', C=10))
])

# 별도의 TfidfVectorizer객체의 fit_transform( )과 LogisticRegression의 fit(), predict( )가 필요 없음. 
# pipeline의 fit( ) 과 predict( ) 만으로 한꺼번에 Feature Vectorization과 ML 학습/예측이 가능. 
pipeline.fit(X_train, y_train)
pred = pipeline.predict(X_test)
print('Pipeline을 통한 Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test ,pred)))
Pipeline을 통한 Logistic Regression 의 예측 정확도는 0.704
print(len(pred), pred[:10])
7532 [ 3 11  1  7  8  1 16  6  4 18]
from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ('tfidf_vect', TfidfVectorizer(stop_words='english')),
    ('lr_clf', LogisticRegression(solver='liblinear'))
])

# Pipeline에 기술된 각각의 객체 변수에 언더바(_)2개를 연달아 붙여 GridSearchCV에 사용될 
# 파라미터/하이퍼 파라미터 이름과 값을 설정. . 
params = { 'tfidf_vect__ngram_range': [(1,1), (1,2), (1,3)],
           'tfidf_vect__max_df': [100, 300, 700],
           'lr_clf__C': [1,5,10]
}

# GridSearchCV의 생성자에 Estimator가 아닌 Pipeline 객체 입력
grid_cv_pipe = GridSearchCV( pipeline,param_grid =params, cv=3 , scoring='accuracy',verbose=1)
grid_cv_pipe.fit(X_train , y_train)
print(grid_cv_pipe.best_score_ , grid_cv_pipe.best_params_)

pred = grid_cv_pipe.predict(X_test)
print('Pipeline을 통한 Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test ,pred)))
Fitting 3 folds for each of 27 candidates, totalling 81 fits


[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done  81 out of  81 | elapsed: 18.1min finished


0.7550828826229531 {'lr_clf__C': 10, 'tfidf_vect__max_df': 700, 'tfidf_vect__ngram_range': (1, 2)}
Pipeline을 통한 Logistic Regression 의 예측 정확도는 0.702

CSR-Matrix

from scipy import sparse
import numpy as np
row_ind = np.array([0, 1, 2])
col_ind = np.array([1, 2, 1])
k = np.ones(3)
k
array([1., 1., 1.])
y_sparse = sparse.csr_matrix((k, (row_ind, col_ind)))
print(y_sparse)
  (0, 1)	1.0
  (1, 2)	1.0
  (2, 1)	1.0
k[0]
1.0
row_ind[0],col_ind[0]
(0, 1)
row_ind[1],col_ind[1]
(1, 2)
row_ind[2],col_ind[2]
(2, 1)

Comments