# 22.02.07 머신러닝 스터디 8장. 텍스트 분석 - 1
<파이썬 머신러닝 완벽 가이드>¶
텍스트 분석 - 1 ( p466 ~ p496 )¶
NLP ( National Language Processing ) : 머신이 인간의 언어를 이해하는 데 중점
텍스트 분석 ( Text Analytics ) : ( 텍스트 마이닝 ) 비정형 텍스트에서 의미 있는 정보를 추출하는 것에 중점
- 텍스트 분류 ( Text Classification ) : 문서가 특정 분류 또는 카테고리에 속하는 것을 예측하는 기법을 통칭한다.
- 감성 분석 ( Sentiment Analysis ) : 텍스트에서 나타나는 감정/판단/믿음/의견/기분 등의 주관적인 요소를 분석하는 기법을 총칭한다.
- 텍스트 요약 ( Summaraization ) : 텍스트 내에서 중요한 주제나 중심 사상을 추출하는 기법을 말한다. ( ex. 토픽 모델링 )
- 텍스트 군집화와 유사도 측정 : 비슷한 유형의 문서에 대해 군집화를 수행하는 기법을 말한다.
01. 텍스트 분석 이해¶
텍스트 분석은 비정형 데이터인 텍스트를 분석하는 것이다. 지금까지 머신러닝 모델은 주어진 정형 데이터 기반에서 모델을 수립하고 예측을 수행하였는데, 텍스트를 머신러닝에 적용하기 위해서는 머신러닝 알고리즘이 숫자형의 피처 기반 데이터만 입력받을 수 있기 때문에 word 기반의 다수의 피처로 추출하고 이 피처에 단어 빈도수와 같은 숫자 값을 부여한 벡터 값으로 표현하는 텍스트 변환 방법인 피처 벡터화 ( 피처 추출 )를 거쳐야 한다. 대표적인 방법으로 BOW ( Bag of Words )와 Word2Vec 방법이 있다.
텍스트 분석 수행 프로세스
- 텍스트 사전 준비작업 ( 텍스트 전처리 ) : 텍스트를 피처로 만들기 전에 미리 대/소문자 변경, 특수문자 삭제 등의 클렌징 작업, 단어 등의 토큰화 작업, 의미 없는 단어 ( Stop Word ) 제거 작업, 어근 추출 ( Stemming / Lemmatization ) 등의 텍스트 정규화 작업을 수행하는 것을 말한다.
- 피처 벡터화 / 추출 : 사전 준비 작업으로 가공된 텍스트에서 피처를 추출하고 여기에 벡터 값을 할당한다. 대표적인 방법으로는 BOW 와 Word2Vec이 있으며 BOW는 대표적으로 Count 기반과 TF-IDF 기반 벡터화가 있다.
- ML 모델 수립 및 학습/예측/평가 : 피처 벡터화된 데이터 세트에 ML 모델을 적용해 학습/예측 및 평가를 수행한다.
파이썬 기반의 NLP,텍스트 분석 패키지
- NLTK ( Natural Language Toolkit for Python ) : 파이썬의 가장 대표적인 NLP 패키지로 방대한 데이터 세트와 서브 모듈을 가지고 있으며 NLP의 거의 모든 영역을 커버하고 있다. 하지만 수행 속도 면에서는 아쉬운 부분이 있어 실제 대량의 데이터 기반에서는 제대로 활용되지 못하고 있다.
- Gensim : 토픽 모델링 분야에서 가장 두각을 나타내는 패키지로 오래전부터 토픽 모델링을 쉽게 구현할 수 있는 기증을 제공해 왔으며 Word2Vec 구현 등의 다양한 신기능도 제공한다.
- SpaCy : 뛰어난 수행 성능으로 최근 가장 주목을 받는 NLP 패키지이다.
02. 텍스트 사전 준비 작업(텍스트 전처리) - 텍스트 정규화¶
텍스트 자체를 바로 피처를 만들 수 없기 때문에 사전에 텍스트를 가공하는 준비 작업이 필요하다.
- 클렌징 ( Cleansing )
- 토큰화 ( Tokenization )
- 필터링 / 스톱 워드 제거 / 철자 수정
- Stemming
- Lemmatization
클렌징¶
텍스트에서 분석에 방해가 되는 불필요한 문자, 기호 등을 사전에 제거하는 작업 ( ex. HTML, XML 태그나 특정 기호 등을 사전에 제거 )
# 각각의 문장으로 구성된 list 객체 반환
from nltk import sent_tokenize
import nltk
nltk.download('punkt')
text_sample = 'The Matrix is everywhere its all around us, here even in this room. \
You can see it out your window or on your television. \
You feel it when you go to work, or go to church or pay your taxes.'
sentences = sent_tokenize(text=text_sample)
print(type(sentences),len(sentences))
print(sentences)
<class 'list'> 3 ['The Matrix is everywhere its all around us, here even in this room.', 'You can see it out your window or on your television.', 'You feel it when you go to work, or go to church or pay your taxes.']
[nltk_data] Downloading package punkt to [nltk_data] C:\Users\admin\AppData\Roaming\nltk_data... [nltk_data] Package punkt is already up-to-date!
단어 토큰화
from nltk import word_tokenize
sentence = "The Matrix is everywhere its all around us, here even in this room."
words = word_tokenize(sentence)
print(type(words), len(words))
print(words)
<class 'list'> 15 ['The', 'Matrix', 'is', 'everywhere', 'its', 'all', 'around', 'us', ',', 'here', 'even', 'in', 'this', 'room', '.']
from nltk import word_tokenize, sent_tokenize
#여러개의 문장으로 된 입력 데이터를 문장별로 단어 토큰화 만드는 함수 생성
def tokenize_text(text):
# 문장별로 분리 토큰
sentences = sent_tokenize(text)
# 분리된 문장별 단어 토큰화
word_tokens = [word_tokenize(sentence) for sentence in sentences]
return word_tokens
#여러 문장들에 대해 문장별 단어 토큰화 수행.
word_tokens = tokenize_text(text_sample)
print(type(word_tokens),len(word_tokens))
print(word_tokens)
<class 'list'> 3 [['The', 'Matrix', 'is', 'everywhere', 'its', 'all', 'around', 'us', ',', 'here', 'even', 'in', 'this', 'room', '.'], ['You', 'can', 'see', 'it', 'out', 'your', 'window', 'or', 'on', 'your', 'television', '.'], ['You', 'feel', 'it', 'when', 'you', 'go', 'to', 'work', ',', 'or', 'go', 'to', 'church', 'or', 'pay', 'your', 'taxes', '.']]
스톱 워드 제거¶
스톱 워드 ( Stop Word )는 분석에 큰 의미가 없는 단어를 지칭한다.
import nltk
nltk.download('stopwords')
[nltk_data] Downloading package stopwords to [nltk_data] C:\Users\admin\AppData\Roaming\nltk_data... [nltk_data] Package stopwords is already up-to-date!
True
print('영어 stop words 갯수:',len(nltk.corpus.stopwords.words('english')))
print(nltk.corpus.stopwords.words('english')[:20])
영어 stop words 갯수: 179 ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his']
import nltk
stopwords = nltk.corpus.stopwords.words('english')
all_tokens = []
# 위 예제의 3개의 문장별로 얻은 word_tokens list 에 대해 stop word 제거 Loop
for sentence in word_tokens:
filtered_words=[]
# 개별 문장별로 tokenize된 sentence list에 대해 stop word 제거 Loop
for word in sentence:
#소문자로 모두 변환합니다.
word = word.lower()
# tokenize 된 개별 word가 stop words 들의 단어에 포함되지 않으면 word_tokens에 추가
if word not in stopwords:
filtered_words.append(word)
all_tokens.append(filtered_words)
print(all_tokens)
[['matrix', 'everywhere', 'around', 'us', ',', 'even', 'room', '.'], ['see', 'window', 'television', '.'], ['feel', 'go', 'work', ',', 'go', 'church', 'pay', 'taxes', '.']]
Stemming과 Lemmatization¶
문법적으로 또는 의미적으로 변화하는 단어의 원형을 찾는 것
- Stemming ( 어간 추출 ) : 원형 단어로 변환 시 일반적인 방법을 적용하거나 더 단순화된 방법을 적용해 원래 단어에서 일부 철자가 훼손된 어근 단어를 추출하는 경향이 있다. ( Porter, Lancaster, Snowball Stemmer )
- Lemmatization ( 표제어 추출 ) : 품사와 같은 문법적인 요소와 더 의미적인 부분을 감안해 정확한 철자로 된 어근 단어를 찾아준다. ( WordNetLemmatizer )
PorterStemmer
from nltk.stem import PorterStemmer
stemmer = PorterStemmer()
print(stemmer.stem('working'),stemmer.stem('works'),stemmer.stem('worked'))
print(stemmer.stem('amusing'),stemmer.stem('amuses'),stemmer.stem('amused'))
print(stemmer.stem('happier'),stemmer.stem('happiest'))
print(stemmer.stem('fancier'),stemmer.stem('fanciest'))
print(stemmer.stem('was'), stemmer.stem('were'))
work work work amus amus amus happier happiest fancier fanciest wa were
LancasterStemmer
from nltk.stem import LancasterStemmer
stemmer = LancasterStemmer()
print(stemmer.stem('working'),stemmer.stem('works'),stemmer.stem('worked'))
print(stemmer.stem('amusing'),stemmer.stem('amuses'),stemmer.stem('amused'))
print(stemmer.stem('happier'),stemmer.stem('happiest'))
print(stemmer.stem('fancier'),stemmer.stem('fanciest'))
work work work amus amus amus happy happiest fant fanciest
Lemmatization
from nltk.stem import WordNetLemmatizer
import nltk
nltk.download('wordnet')
lemma = WordNetLemmatizer()
print(lemma.lemmatize('amusing','v'),lemma.lemmatize('amuses','v'),lemma.lemmatize('amused','v'))
print(lemma.lemmatize('happier','a'),lemma.lemmatize('happiest','a'))
print(lemma.lemmatize('fancier','a'),lemma.lemmatize('fanciest','a'))
[nltk_data] Downloading package wordnet to [nltk_data] C:\Users\admin\AppData\Roaming\nltk_data... [nltk_data] Package wordnet is already up-to-date!
amuse amuse amuse happy happy fancy fancy
03. Bag of Words - BOW¶
Bag of Words 모델은 문서가 가지는 모든 단어를 문맥이나 순서를 무시하고 일괄적으로 단어에 대해 빈도 값을 부여해 피처 값을 추출하는 모델이다. 문서 내 모든 단어를 한꺼번에 봉투 안에 넣은 뒤에 흔들어서 섞는다는 의미로 Bag of Words 모델이라고 한다. BOW 모델의 장점은 쉽고 빠르게 구축할 수 있다는 것이고 단순히 단어의 발생 횟수에 기반하고 잇지만, 예상보다 문서의 특징을 잘 나타낼 수 있는 모델이다. 하지만 BOW 기반의 NLP 연구는 여러 가지 제약을 지니는데 우선은 문맥의 의미를 반영하지 못한다는 단점을 지닌다. BOW는 단어의 순서를 고려하지 않기 때문에 문장 내에서 단어의 문맥적인 의미가 무시된다. 이를 보완하기 위해 n_gram 기법을 활용할 수는 있지만 이는 제한적으로 언어의 많은 부분을 차지하는 문맥적인 해석을 처리하지 못한다는 단점이 있다. 두번째는 희소 행렬 문제이다. BOW로 피처 벡터화를 수행하면 희소 행렬 ( 대규모의 칼럼으로 구성된 행렬에서 대부분의 값이 0으로 채워지는 행렬 <-> 밀집 행렬) 형태의 데이터 세트가 만들어지기 쉽다. 많은 문서에서 단어를 추출하면 문서마다 서로 다른 단어로 구성되어 매우 많은 단어가 칼럼으로 만들어지기 때문이다.
BOW 피처 벡터화¶
머신러닝 알고리즘은 일반적으로 숫자형 피처를 데이터로 입력받아 동작하기 때문에 텍스트 자체를 바로 피처로 머신러닝 알고리즘에 입력할 수가 없다. 따라서 텍스트를 특정 의미를 가지는 숫자형 값인 벡터 값으로 변환해야 하는데, 이러환 변환을 피처 벡터화라고 한다. BOW 모델에서 피처 벡터화는 모든 문서에서 모든 단어를 칼럼 형태로 나열하고 각 문서에서 해당 단어의 횟수나 정규화된 빈도를 값으로 부여하는 데이터 세트 모델로 변경하는 것이다. BOW의 피처 벡터화 방식에는 2가지가 존재한다.
- 카운트 기반 벡터화 : 단어 피처에 값을 부여할 때 각 문서에서 해당 단어가 나타나는 횟수에 Count를 부여하는 경우를 카운트 벡터화라고 한다. 카운트 값이 높을수록 중요한 단어로 인식되며, 이에 그 문서의 특징을 나타내는 단어가 아닌 언어의 특성상 문장에서 자주 사용될 수 밖에 없는 단어까지 높은 값을 부여하게 된다.
- TF-IDF ( Term Frequency - Inverse Document Frequency ) 기반 벡터화 : 위와 같은 카운트 기반 벡터화의 문제를 보완하기 위해 개별 문서에서 자주 나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 자주 나타나는 단어에 대해서는 페널티를 주는 방식으로 값을 부여한다.
TF_i = 개별 문서에서의 단어 i 빈도
DF_i = 단어 i를 가지고 있는 문서 개수
N = 전체 문서 개수
사이킷런의 Count 및 TF-IDF 벡터화 구현 : CountVectorizer, TfidVectorizer¶
CountVectorizer, TfidVectorizer의 파라미터
- max_df : 너무 높은 빈도수를 가지는 단어 피처를 제외, 정수 값을 가지면 전체 문서에 걸쳐 n개 이하로 나타나는 단어만 피처로 추출하고 부동 소수점 값을 가지면 전체 문서에 걸쳐 빈도수% 까지의 단어만 피처로 추출한다.
- min_df : 너무 낮은 빈도수를 가지는 단어 피처를 제외
- max_features : 높은 빈도를 가지는 단어 순으로 몇 개를 추출할 건지 입력
- stop_words : english로 지정하면 여어의 스톱 워드로 지정된 단어는 추출에서 제외한다.
- n_gram_range : BOW 모델이 문맥의 의미를 반영하지 못한다는 단점을 극복하기 위해 n_gram 범위를 설정한다. 튜플 형태로 (범위 최솟값, 범위 최댓값)을 지정
- analyzer : 피처 추출을 수행한 단위를 지정한다. default = 'word'
- token_pattern : 토큰화를 수행하는 정규 표현식 패턴을 지정합니다. default = '\b\w\w+\b'
- tokenizer : 토큰화를 별도의 커스텀 함수로 이용시 적용한다.
CountVectorizer 클래스를 이용한 피처 벡터화 방법
- 사전 데이터 가공 : 영어의 경우 모든 문자를 소문자로 변경하는 등의 전처리 작업을 수행한다.
- 토큰화 : 디폴트로 단어 기준으로 n_gram_range를 반영해 각 단어를 토큰화한다.
- 텍스트 정규화 : Stop Word 필터링을 수행한다. ( Stemmer, Lemmatize는 자체 지원 x )
- 피처 벡터화 : max_df, min_df, max_features 등의 파라미터를 이용해 토큰화된 단어를 피처로 추출하고 단어 빈도수 벡터 값을 적용한다.
BOW 벡터화를 위한 희소 행렬¶
BOW 벡터화를 수행할 경우 불필요한 0값이 메모리 공간에 할당되어 메모리 공간이 많이 필요하다. 따라서 적은 메모리 공간을 차지할 수 있도록 변환이 필요하다. 대표적인 방법으로 COO 형식과 CSR 형식이 있다. 일반적으로 큰 희소 행렬을 저장하고 계산을 수행하는 능력이 CSR 형식이 더 뛰어나기 때문에 CSR 형식을 많이 사용한다.
희소 행렬 - COO형식
COO(Coordinate : 좌표) 형식은 0이 아닌 데이터만 별도의 데이터 배열에 저장하고, 그 데이터가 가리키는 행과 열의 위치를 별도의 배열로 저장하는 방식이다.
import numpy as np
dense = np.array( [ [ 3, 0, 1 ], [0, 2, 0 ] ] )
from scipy import sparse
# 0 이 아닌 데이터 추출
data = np.array([3,1,2])
# 행 위치와 열 위치를 각각 array로 생성
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)))
sparse_coo.toarray()
array([[3, 0, 1], [0, 2, 0]])
희소 행렬 - CSR형식
CSR(Compressed Sparse Row) 형식은 COO 형식이 행과 열의 위치를 나타내기 위해서 반복적인 위치 데이터를 사용해야 하는 문제점을 해결한 방식이다. COO 형식의 행 위치 배열을 자세히 살펴보면 순차적인 같은 값이 반복적으로 나타나는 것을 확인할 수 있다. 행 위치 배열이 0부터 순차적으로 증가하는 값으로 이루어졌다는 특성을 고려하면 행 위치 배열의 고유한 값의 시작 위치만 표기하는 방법으로 이러한 반복을 제거할 수 있다.
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]) # 열 위치 배열
# COO 형식으로 변환
sparse_coo = sparse.coo_matrix((data2, (row_pos,col_pos)))
# 행 위치 배열의 고유한 값들의 시작 위치 인덱스를 배열로 생성
row_pos_ind = np.array([0, 2, 7, 9, 10, 12, 13]) # 마지막에 총 항목 개수 배열을 추가로 넣어준다
# CSR 형식으로 변환
sparse_csr = sparse.csr_matrix((data2, col_pos, row_pos_ind))
print('COO 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인')
print(sparse_coo.toarray())
print('CSR 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인')
print(sparse_csr.toarray())
COO 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인 [[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]] CSR 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인 [[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]]
dense3 = 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]])
coo = sparse.coo_matrix(dense3)
csr = sparse.csr_matrix(dense3)
print(coo)
(0, 2) 1 (0, 5) 5 (1, 0) 1 (1, 1) 4 (1, 3) 3 (1, 4) 2 (1, 5) 5 (2, 1) 6 (2, 3) 3 (3, 0) 2 (4, 3) 7 (4, 5) 8 (5, 0) 1
print(csr)
(0, 2) 1 (0, 5) 5 (1, 0) 1 (1, 1) 4 (1, 3) 3 (1, 4) 2 (1, 5) 5 (2, 1) 6 (2, 3) 3 (3, 0) 2 (4, 3) 7 (4, 5) 8 (5, 0) 1
04. 텍스트 분류 실습 - 20 뉴스그룹 분류¶
텍스트 정규화¶
from sklearn.datasets import fetch_20newsgroups
news_data = fetch_20newsgroups(subset='all',random_state=156)
print(news_data.keys())
dict_keys(['data', 'filenames', 'target_names', 'target', 'DESCR'])
import pandas as pd
print('target 클래스의 값과 분포도 \n',pd.Series(news_data.target).value_counts().sort_index())
print('target 클래스의 이름들 \n',news_data.target_names)
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']
print(news_data.data[0])
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, that's the ticket)... What are some guidelines? Should I just >pick up another shoei in my size to have a backup helmet (XL), or should I >maybe get an inexpensive one of a smaller size to accomodate my likely >passenger? If your primary concern is protecting the passenger in the event of a crash, have him or her fitted for a helmet that is their size. If your primary concern is complying with stupid helmet laws, carry a real big spare (you can put a big or small head in a big helmet, but not in a small one). --- Ed Green, former Ninjaite |I was drinking last night with a biker, Ed.Green@East.Sun.COM |and I showed him a picture of you. I said, DoD #0111 (919)460-8302 |"Go on, get to know her, you'll like her!" (The Grateful Dead) --> |It seemed like the least I could do...
from sklearn.datasets import fetch_20newsgroups
# subset='train'으로 학습용(Train) 데이터만 추출, remove=('headers', 'footers', 'quotes')로 내용만 추출
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))
# 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)))
<class 'list'> 학습 데이터 크기 11314 , 테스트 데이터 크기 7532
피처 벡터화 변환과 머신러닝 모델 학습/예측/평가¶
from sklearn.feature_extraction.text import CountVectorizer
# Count Vectorization으로 feature extraction 변환 수행.
cnt_vect = CountVectorizer()
cnt_vect.fit(X_train)
X_train_cnt_vect = cnt_vect.transform(X_train)
# 학습 데이터로 fit( )된 CountVectorizer를 이용하여 테스트 데이터를 feature extraction 변환 수행.
X_test_cnt_vect = cnt_vect.transform(X_test)
print('학습 데이터 Text의 CountVectorizer Shape:',X_train_cnt_vect.shape)
학습 데이터 Text의 CountVectorizer Shape: (11314, 101631)
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# LogisticRegression을 이용하여 학습/예측/평가 수행.
lr_clf = LogisticRegression(max_iter=500)
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.597
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)
# LogisticRegression을 이용하여 학습/예측/평가 수행.
lr_clf = LogisticRegression()
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.674
# 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(max_iter=500)
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.692
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)))
Fitting 3 folds for each of 5 candidates, totalling 15 fits Logistic Regression best C parameter : {'C': 10} TF-IDF Vectorized Logistic Regression 의 예측 정확도는 0.701
사이킷런 파이프라인(Pipeline) 사용 및 GridSearchCV와의 결합¶
피처 벡터화와 ML 알고리즘 학습 / 예측을 위한 코드 작성을 한 번에 진행할 수 있다. 사이킷런은 GridSearchCV 클래스의 생성 파라미터로 Pipeline을 입력해 Pipeline 기반에서도 하이퍼 파라미터 튜닝을 GridSearchCV 방식으로 진행할 수 있게 지원한다. 이 때 Estimator가 아닌 Pipeline을 GridSearchCV에 입력할 경우에는 딕셔너리 형태의 key를 입력할 때 하이퍼 파라미터명이 객체 변수명과 결합되어 제공되기 때문에 객체 변수명에 언더바(\'_\') 2개를 연달아 붙인 뒤 파라미터 명을 붙여 Key 값으로 할당한다.
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(max_iter=500, 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.701
# Grid Search 시간이 오래 걸려 실행은 생략
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('tfidf_vect', TfidfVectorizer(stop_words='english')),
('lr_clf', LogisticRegression(max_iter = 500))
])
# 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_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)))
희소 행렬의 분류를 효과적으로 잘 처리할 수 있는 알고리즘은 로지스틱 회귀, 선형 서포트 벡터 머신, 나이브 베이즈이다.
'머신러닝(Machine Learning)' 카테고리의 다른 글
[파이썬 머신러닝 완벽 가이드] 8장. 텍스트 분석 - 2 (0) | 2022.02.12 |
---|---|
[파이썬 머신러닝 완벽 가이드] 7장. 군집화 (0) | 2022.02.03 |
[파이썬 머신러닝 완벽 가이드] 6장. 차원 축소 (0) | 2022.01.30 |
댓글