[문제] 뉴스 주제로 분류

고보·2024년 3월 21일

1 새 문제

  • 이걸 텍스트 정규화 => 피처 벡터화 => 머신러닝 적용 => 파이프라인 적용 => GridSearchCV로 최적화를 진행할 것.
  • 자연어를 분류하는 문제를 위해, TF-IDF나 CountVectorizer로 단어의 등장 빈도나 중요성을 벡터로 표현해야지, 머신러닝을 돌릴 수 있다.

1-1 데이터 들고오기

from sklearn.datasets import fetch_20newsgroups
news_data = fetch_20newsgroups(subset='all', random_state=156)
  • 188846개 뉴스 문서를 20개 뉴스 카테고리로 분류한 것.
  • fetch_20newsgroups는 사이킷런에서 제공하는 함수 중 하나로, 다양한 기계학습 모델과 데이터셋 제공. => 그 중에서도 fetch_20_newsgroups는 20개의 다른 주제로 분류된 뉴스 그룹 데이터셋 들고온다.
    • subset은 train, test로 설정해서 학습용, 테스트용 데이터 들고온다. all은 모두.
  • 아래와 같이 데이터의 구조를 파악한다.
print(news_data.keys())
news_data.target_names
news_data.target

import pandas as pd
print('target 클래스의 값과 분포도 \n', pd.Series(news_data.target).value_counts().
      sort_index())
print('target 클래스의 이름들 \n', news_data.target_names)


#subset = train으로 학습용 데이터만 추출
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))

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)))
  • train, test 데이터로 각각 들고와서 X, y로 만든다. 여기서 remove 매개변수는 뉴스 그룹 데이터에서 제거할 요소 지정. 여기선 headers, footers, quotes 제거. => header는 이메일의 보내는 사람, 받는 사람 등 / footers는 이메일 끝의 회사 정보 등 / 인용구는 이메일 원문에서 다른 이의 이메일 인용하는 부분.

1-2 피처 벡터화(CountVectorizer) 후 로지스틱 회귀

  • 주의점으로 train 데이터에 대해 fit()된 CountVectorizer를 이용해서 test 데이터를 피처 벡터화 해야 한다.
    test 데이터에 대해 CountVectorizer의 fit을 수행하면 안된다. test 데이터에 fit을 수행하면 기존 train 데이터로 학습된 모델에서 가지는 feature 갯수와 달라진다.
from sklearn.feature_extraction.text import CountVectorizer

cnt_vect = CountVectorizer()
cnt_vect.fit(X_train)
X_train_cnt_vect = cnt_vect.transform(X_train)
X_test_cnt_vect = cnt_vect.transform(X_test)
print('학습 데이터 Text의 CountVectorizer Shape:', X_train_cnt_vect.shape)
  • CountVectorizer(): 문서 집합에서 단어 토큰 생성하고, 각 단어의 수를 세서 단어 카운트 벡터를 생성.
  • X train 데이터로 fit => train 데이터와 test 데이터 모두 transform => shape 확인(11314, 101631) 모양.
    즉, train 데이터의 문서 갯수가 11314개고, 101631개는 vocabulary(단어 사전)의 크기. 즉 단어 종류. 각 단어가 각 문서에 몇 개씩 들었는지 카운트.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

lr_clf = LogisticRegression(solver='liblinear')
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)))
  • 로지스틱 회귀에서 solver='liblinear'는 최적화에 사용할 알고리즘. liblinear는 작은 규모 데이터셋에 대해 적합.
  • lrclf에서 앞서 만든 X train을 카운트벡터화한 데이터를 입력하고, y_train을 입력해서 fitting.
  • 그리고 predict를 X_test를 카운트벡터화한 데이터로.

1-3 TF-IDF 피처 변환 후 다시 로지스틱 회귀

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vect = TfidfVectorizer()
tfidf_vect.fit(X_train)
X_train_cnt_vect = tfidf_vect.transform(X_train)
X_test_cnt_vect = tfidf_vect.transform(X_test)

lr_clf = LogisticRegression(solver='liblinear')
lr_clf.fit(X_train_cnt_vect, y_train)
pred = lr_clf.predict(X_test_cnt_vect)
print('TF-IDF Logistic Regression의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test, pred)))
  • 이번엔 X_train을 TF-IDF 벡터라이즈하는 걸 fitting => X_train과 X_test를 둘 다 transform
  • 위와 마찬가지로 lr_clf로 X_train과 y_train으로 fitting하고 => 그 모델에 X_test로 바로 predict. (X_test로 따로 학습 X)
  • 일반적으로 TFIDF가 Countvectorizer보다 좀 더 유리하다.

1-4 stopwords필터링을 추가 => ngram을 (1, 2)까지 피처 벡터화

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')
lr_clf.fit(X_train_tfidf_vect, y_train)
pred = lr_clf.predict(X_test_cnt_vect)
print('TF-IDF Vectorized Logistic Regression의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test, pred)))
  • 똑같은 과정에서 TfidfVectorizer() 생성할 때 stop_words 추가, ngram_range(1, 2)로 설정, max_df = 300 으로만 설정. max_df는 300번 이상 나온 단어 제외. 일반적으로는 0~1 사이 실수로 설정.

1-5 GridsearchCV로 로지스틱 회귀 C 하이퍼파라미터 튜닝

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=1 )
grid_cv_lr.fit(X_train_tfidf_vect , y_train)
print('Logistic Regression best C parameter :',grid_cv_lr.best_params_ )
# 최적 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)))
  • GridSearchCV에서 모델(lr_clf), paras로 설정, cv는 교차 검증 위해 3개의 폴드로 나누기, 성능 측정 지표는 accuracy(정확도), verbose=1은 그리드 서치 진행 과정 출력.
  • 그리고 위에서 tfidf로 벡터화 시킨 데이터와 y_train으로 fitting => 즉, 그리드 서칭 수행.
  • bestparams를 인쇄(C:10).
  • pred(X_test_tfidf_vect) => 여기선 GridSearchCV로 찾은 최적의 하이퍼파라미터로 튜닝된 모델로 예측 수행. => 결과가 더 좋아졌다.

2 전체 과정을 파이프라인으로

# 전체 81번 돈다 3*3*3*3
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
    ('tfidf_vect', TfidfVectorizer(stop_words='english')),
    ('lr_clf', LogisticRegression(solver='liblinear'))
])
params = { 'tfidf_vect__ngram_range': [(1,1), (1,2), (1,3)], #3
           'tfidf_vect__max_df': [100, 300, 700], #3
           'lr_clf__C': [1,5,10] #3
}
grid_cv_pipe = GridSearchCV(pipeline, param_grid=params, cv=3 , scoring='accuracy',verbose=1) #3겹
grid_cv_pipe.fit(X_train , y_train)
print(grid_cv_pipe.best_params_ , grid_cv_pipe.best_score_)
pred = grid_cv_pipe.predict(X_test)
print('Pipeline을 통한 Logistic Regression 의 예측 정확도는 {0:.3f}'.format(accuracy_score(y_test ,pred)))
profile
일본에서 일하는 게임 기획자. 시시해서 죽어버리지 않게, 재밌고 의미 있는 컨텐츠에 관심 있습니다. 그 도구로 데이터, AI도 찝적댑니다.

0개의 댓글