2022.04.30 - [python/파이썬 머신러닝 완벽가이드] - [python] NLP 기초, 텍스트 전처리의 모든 것
앞서는 NLP의 전반적인 설명과 텍스트 클렌징, 토큰화, 스톱워드 제거, Stemming/Lemmatization 에 대하여 개괄적으로 살펴보았다.
이번은 전처리된 텍스트를 어떤 방식으로 피처화를 진행하는지 살펴본다.
또한 단어를 피처화하게 되면 0값을 갖는 단어들이 무수히 많이 발생하게 되는데 이를 희소행렬이라고 한다.
여기서 발생하는 문제는 뭔지, 어떤 모듈로 개선할 수 있는지 살펴본다.
- 피처백터화
텍스트는 특정 의미를 가지는 숫자형 값인 벡터 값으로 변환해야 하는데, 이를 피처 백터화라고 한다.
BOW (Bag of Words)
BOW모델은 문서가 가지는 모든 단어의 문맥이나 순서를 무시하고 일괄적으로 단어에 대해 빈도 값을 부여하여 피처 값을 추출하는 모델이다.
또한 모든 문서에서 모든 단어를 컬럼 형태로 나열하고 각 문서에서 해당 단어의 횟수나 정규화된 빈도를 값으로 부여하는 데이터 세트 모델로 변경하는 것이다.
쉽고 빠른 모델의 구축이 가능하지만, 단순히 단어의 발생 횟수에 기반하여 문맥의 의미를 반영하지 못하고, 희소행렬 문제로 인해 ML알고리즘의 수행성능을 떨어트리는 문제가 존재한다.
BOW 모델의 피처백터화는 두가지로 나누어볼 수 있다.
1) 카운트 기반의 백터화 : 해당 단어가 나타나는 횟수, 즉 카운트 값이 높을수록 중요한 단어로 인식
2) TF-IDF 기반의 백터화 : 개별문서에서 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 자주 나타나는 단어(범용적으로 사용되는 단어)에 대해서는 패널티를 부여
>> 일반적으로 문서마다 텍스트가 길고 문서의 개수가 많은 경우 카운트 방식 보다는 TF-IDF 방식을 사용하는 것이 더 좋은 예측 성능을 보인다.
희소행렬 (COO/CSR)
모든 문서의 단어를 추출해 중복을 제거하고 피처로 만들면 일반적으로 수만 개에서 수십만 개의 단어가 만들어진다.
단, 이런 대규모의 단어가 생성되더라도 레코드의 각 문서가 가지는 단어의 수는 제한적이기 때문에 행렬의 대부분의 값이 0으로 채워질 수 밖에 없다.
즉, 이처럼 행렬의 대부분의 값을 0이 차지하는 행렬을 가리켜 희소행렬이라 부른다!
특히 BOW형태를 갖는 언어 모델의 피처 백터화는 대부분 희소행렬이다.
이 희소행렬은 너무 많은 불필요한 0값이 메모리 공간에 할당되어 많은 메모리 공간이 필요하고, 이 때문에 연산 시에도 데이터 엑세스를 위한 시간이 많이 소요된다.
이를 해결하기 위한 대표적인 방법이 2가지로 나뉜다.
1) COO 형식 : 0이 아닌 데이터만 별도의 배열에 저장하고, 그 데이터가 가리키는 행과 열의 위치를 별도의 배열로 저장하는 방식
ex) [[3,0,1],[0,2,0]]과 같은 2차원 데이터가 있는 경우 0이 아닌 데이터는 [3,1,2]
이 위치를 (row,col)로 저장하면 >> (0,0),(0,2),(1,1)
import numpy as np
dense = np.array([ [3,0,1],[0,2,0] ])
## scipy의 coo_matrix를 활용해 COO형식의 희소행렬로 변환
from scipy import sparse
## 0이 아닌 데이터 추출
data = np.array([3,1,2])
## 행 위치와 열 위치를 각각 배열로 생성
row_pos = np.array([0,0,1])
col_pos = np.array([0,2,1])
## sparse 패키지의 coo_matrix를 이용해 COO형식으로 희소행렬 생성
sparse_coo = sparse.coo_matrix((data, (row_pos,col_pos)))
2) CRS 형식 : COO형식이 행과 열의 위치를 나타내기 위해 반복적인 위치 데이터를 활용하는 문제를 해결한 방식
행 위치 배열 내에 있는 고유한 값의 시작 위치만 다시 별도의 위치 배열로 가지는 변환방식이다.
즉,, [0,0,1,1,1,1,1,2,2,3,4,4,5]를 CSR변환하게 되면 >> [0,2,7,9,10,12] 가 된다.
여기서 맨 마지막에는 데이터의 총 항목 개수를 배열에 추가하여 결론적으로 [0,2,7,9,10,12,13]으로 변환된다.
from scipy import sparse
dense2 = np.array([ [0,0,1,0,0,5],
[1,4,0,3,2,5],
[0,6,0,3,0,0],
[2,0,0,0,0,0],
[0,0,0,7,0,8],
[1,0,0,0,0,0] ])
## 0이 아닌 데이터 추출
data2 = np.array([1,5,1,4,3,2,5,6,3,2,7,8,1])
## 행 위치와 열 위치를 각각 array로 설정
row_pos = np.array([0,0,1,1,1,1,1,2,2,3,4,4,5])
col_pos = np.array([2,5,0,1,3,4,5,1,3,0,3,5,0])
## 행 위치를 고유한 값의 시작 위치 인덱스로 배열 생성
row_pos_ind = np.array([0,2,7,9,10,12,13)]
## CSR형식으로 변환
sparse_csr = sparse.csr_matrix((data2,col_pos,row_pos_ind))
## sklearn의 CountVectorizer나 TfidfVectorizer 클래스로 변환된 피처 백터화 행렬은 모두 CSR형태의 희소행렬이다.
뉴스그룹 분류 실습
1) 데이터 다운로드
fetch_20newsgroups()를 다운받아 실습을 진행한다.
from sklearn.datasets import fetch_20newsgroups
news_data = fetch_20newsgroups(subset='all', random_state=42)
2) 텍스트 정규화
개별 텍스트 데이터를 확인해보면 뉴스내용뿐만 아니라 뉴스그룹 제목, 작성자, 소속, 이메일 등의 다양한 정보를 함께 포함하고 있다.
순수한 뉴스내용만 남기기 위해 나머지 내용들은 제거해준다.
## subset='train'으로 학습용 데이터만 추출, remove=('headers', 'footers', 'quotes')로 내용만 추출
train_news = fetch_20newsgroups(subset='train', remove=('headers','footers','quoters'), random_state=42)
X_train = train_news.data
y_train = train_news.target
## subset='test'로 테스트 데이터만 추출
test_news = fetch_20newsgroups(subset='test', remove=('headers','footers','quoters'), random_state=42)
X_test = test_news.data
y_test = test_news.target
3) 피처백터화 변환
@@@ 피처 백터화 시 유의점 @@@
학습 데이터를 활용해 fit()을 수행한 객체를 활용하여 테스트 데이터를 transform() 해야한다.
그래야 학습시 설정된 피처 개수로 테스트 데이터를 변환할 수 있다.
즉 테스트 데이터에 fit_transform()을 적용하면 안됨.
두가지 중 하나의 방법으로 피처백터화 진행
## CountVectorizer
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)
## TfidfVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
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)
4) ML모델 학습/예측/평가
#tfidf피처백터화 데이터를 활용해 stopwords 필터링을 추가하고 ngram을 (1,2)로 적용
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
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()
lr_clf.fit(X_train_tfidf_vect, y_train)
pred = lr_clf.predict(X_test_tfidf_vect)
print('TF-IDF Vectorized Logistic Regression의 예측 정확도 : {0}'.format(accuracy_score(y_test, pred)))
5) 하이퍼파라미터 최적화
GridSearchCV를 활용한 하이퍼파라미터 최적화
from sklearn.model_selection import GridSearchCV
params = { 'C' : [0.01,0.1,1,5,10]}
grid_search = GridSearchCV(lr_clf, param = params, cv=3, scoring='accuracy', verbose=1)
grid_search.fit(X_train_tfidf_vect, y_train)
print(grid_search.best_params_)
## 최적의 C값으로 예측 및 정확도 평가
pred = grid_search.predict(X_test_tfidf_vect)
print('TF-IDF 피처화의 Logistic Regression 정확도 : {0}'.format(accuracy_score(y_test,pred)))
실제로 TF-IDF 피처 백터화를 활용하여 Logistic Regression 하이퍼파라미터 최적화를 진행한 결과가 가장 높은 정확도를 보였다.
'python > 파이썬 머신러닝 완벽가이드' 카테고리의 다른 글
[NLP] pipeline을 활용한 직관적인 코드짜기! (0) | 2022.05.03 |
---|---|
[NLP] NLP 기초, 텍스트 전처리의 모든 것 (0) | 2022.04.30 |
[python] SMOTE를 활용한 오버샘플링 (0) | 2022.03.26 |
[python] LGBMClassifier 단번에 성능 높이기 (0) | 2022.03.26 |
[python] 교차검증을 간단하게 cross_val_score() (0) | 2022.03.15 |