06 토픽 모델링(Topic Modeling) - 20 뉴스그룹
토픽 모델링(Topic Modeling): 문서 집합에 숨어 있는 주제를 찾아내는 것, 핵심 주제를 찾는 것은 많은 시간이 소모되므로, 머신러닝의 토픽 모델링 적용하여 숨어있는 중요 주제 찾아냄
사람: 더 함축적인 의미로 문장 요약 <-> 머신러닝 기반: 숨겨진 주제를 효과적으로 표현하기 위해 중심단어를 함축적으로 추출
대표적으로 사용되는 토픽 모델링 기법
- LSA(Latent Semantic Analysis)
- LDA(Latent Dirichlet Allocation)
#fetch_20newgroups() API는 카테고리 파라미터를 통해 필요한 주제만 필터링 후 추출, 추출된 텍스트를 count 기반으로 벡터화 변환
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
# 모터사이클, 야구, 그래픽스, 윈도우즈, 중동, 기독교, 의학, 우주 주제를 추출.
cats = ['rec.motorcycles', 'rec.sport.baseball', 'comp.graphics', 'comp.windows.x',
'talk.politics.mideast', 'soc.religion.christian', 'sci.electronics', 'sci.med' ]
# 위에서 cats 변수로 기재된 category만 추출. featch_20newsgroups( )의 categories에 cats 입력
news_df= fetch_20newsgroups(subset='all',remove=('headers', 'footers', 'quotes'),
categories=cats, random_state=0)
#LDA는 Count 기반의 Vectorizer만 적용합니다.
count_vect = CountVectorizer(max_df=0.95, max_features=1000, min_df=2, stop_words='english', ngram_range=(1,2))
feat_vect = count_vect.fit_transform(news_df.data)
print('CountVectorizer Shape:', feat_vect.shape)
#llda 클래스의 n_components의 parameter 이용하여 이용 개수 조정
lda = LatentDirichletAllocation(n_components=8, random_state=0)
lda.fit(feat_vect)
#components_의 형태와 속성값 확인
print(lda.components_.shape)
lda.components_
def display_topics(model, feature_names, no_top_words):
for topic_index, topic in enumerate(model.components_):
print('Topic #',topic_index)
# components_ array에서 가장 값이 큰 순으로 정렬했을 때, 그 값의 array index를 반환.
topic_word_indexes = topic.argsort()[::-1]
top_indexes=topic_word_indexes[:no_top_words]
# top_indexes대상인 index별로 feature_names에 해당하는 word feature 추출 후 join으로 concat
feature_concat = ' '.join([feature_names[i] for i in top_indexes])
print(feature_concat)
# CountVectorizer객체내의 전체 word들의 명칭을 get_features_names( )를 통해 추출
feature_names = count_vect.get_feature_names()
# Topic별 가장 연관도가 높은 word를 15개만 추출
display_topics(lda, feature_names, 15)
Out [ ]:
Topic # 0
year 10 game medical health team 12 20 disease cancer 1993 games years patients good
Topic # 1
don just like know people said think time ve didn right going say ll way
Topic # 2
image file jpeg program gif images output format files color entry 00 use bit 03
Topic # 3
like know don think use does just good time book read information people used post
Topic # 4
armenian israel armenians jews turkish people israeli jewish government war dos dos turkey arab armenia 000
Topic # 5
edu com available graphics ftp data pub motif mail widget software mit information version sun
Topic # 6
god people jesus church believe christ does christian say think christians bible faith sin life
Topic # 7
use dos thanks windows using window does display help like problem server need know run
-> Topic #0, Topic #5, Topic #7이 주로 애매한 주제어 추출되었음, 모토사이클과 야구 주제의 경우 명확한 주제어 추출 X
07 문서 군집화 소개와 실습
문서 군집화(Document Clustering): 비슷한 텍스트 구성의 문서를 군집화(Clustering), 동일한 군집에 속하는 문서를 같은 카테고리 소속으로 분류하나, "학습 데이터"가 필요없는 비지도학습 기반으로 동작.
Opinion Review 데이터 세트를 이용한 문서 군집화 수행(텍스트 기반의 문서 군집화 적용)
import pandas as pd
import glob, os
import warnings
warnings.filterwarnings('ignore')
pd.set_option('display.max_colwidth', 700)
# 아래는 제 컴퓨터에서 압축 파일을 풀어 놓은 디렉터리이니, 여러분의 디렉터리를 설정해 주세요
path = r'C:\Users\chkwon\Text\OpinosisDataset1.0\OpinosisDataset1.0\topics'
# path로 지정한 디렉터리 밑에 있는 모든 .data 파일들의 파일명을 리스트로 취합
all_files = glob.glob(os.path.join(path, "*.data"))
filename_list = []
opinion_text = []
# 개별 파일들의 파일명은 filename_list 리스트로 취합,
# 개별 파일들의 파일 내용은 DataFrame 로딩 후 다시 string으로 변환하여 opinion_text 리스트로 취합
for file_ in all_files:
# 개별 파일을 읽어서 DataFrame으로 생성
df = pd.read_table(file_,index_col=None, header=0,encoding='latin1')
# 절대경로로 주어진 file 명을 가공. 만일 Linux에서 수행시에는 아래 \\를 / 변경.
# 맨 마지막 .data 확장자도 제거
filename_ = file_.split('\\')[-1]
filename = filename_.split('.')[0]
# 파일명 리스트와 파일 내용 리스트에 파일명과 파일 내용을 추가.
filename_list.append(filename)
opinion_text.append(df.to_string())
# 파일명 리스트와 파일 내용 리스트를 DataFrame으로 생성
document_df = pd.DataFrame({'filename':filename_list, 'opinion_text':opinion_text})
document_df.head()
TD-IDF 형태로 피처 벡터화
tokenizer의 Lemmztization의 LemNormalize()함수 사용하여, 어근 변환 수행
TfidVectorizer의 fit_transform()의 인자로 다큐먼트의 칼럼 입력후, 개별 문서 텍스트의 TF-IDF 변환된 피처 벡터화 행렬 구함.
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vect = TfidfVectorizer(tokenizer=LemNormalize, stop_words='english' , \
ngram_range=(1,2), min_df=0.05, max_df=0.85 )
#opinion_text 컬럼값으로 feature vectorization 수행
feature_vect = tfidf_vect.fit_transform(document_df['opinion_text'])
from sklearn.cluster import KMeans
# 5개 집합으로 군집화 수행. 예제를 위해 동일한 클러스터링 결과 도출용 random_state=0
km_cluster = KMeans(n_clusters=5, max_iter=10000, random_state=0)
km_cluster.fit(feature_vect)
cluster_label = km_cluster.labels_
cluster_centers = km_cluster.cluster_centers_
document_df['cluster_label'] = cluster_label
document_df.head()
document_df[document_df['cluster_label']==0].sort_values(by='filename')
document_df[document_df['cluster_label']==1].sort_values(by='filename')
document_df[document_df['cluster_label']==2].sort_values(by='filename')
document_df[document_df['cluster_label']==3].sort_values(by='filename')
document_df[document_df['cluster_label']==4].sort_values(by='filename')
from sklearn.cluster import KMeans
# 3개의 집합으로 군집화
km_cluster = KMeans(n_clusters=3, max_iter=10000, random_state=0)
km_cluster.fit(feature_vect)
cluster_label = km_cluster.labels_
# 소속 클러스터를 cluster_label 컬럼으로 할당하고 cluster_label 값으로 정렬
document_df['cluster_label'] = cluster_label
document_df.sort_values(by='cluster_label')
군집별 핵심 단어 추출
각 군집(Cluster)에 속한 문서는 핵심 단어를 주축으로 군집화되어있기에, 이를 구성하는 핵심 단어 추출
cluster_centers = km_cluster.cluster_centers_
print('cluster_centers shape :',cluster_centers.shape)
print(cluster_centers)
# 군집별 top n 핵심단어, 그 단어의 중심 위치 상대값, 대상 파일명들을 반환함.
def get_cluster_details(cluster_model, cluster_data, feature_names, clusters_num, top_n_features=10):
cluster_details = {}
# cluster_centers array 의 값이 큰 순으로 정렬된 index 값을 반환
# 군집 중심점(centroid)별 할당된 word 피처들의 거리값이 큰 순으로 값을 구하기 위함.
centroid_feature_ordered_ind = cluster_model.cluster_centers_.argsort()[:,::-1]
#개별 군집별로 iteration하면서 핵심단어, 그 단어의 중심 위치 상대값, 대상 파일명 입력
for cluster_num in range(clusters_num):
# 개별 군집별 정보를 담을 데이터 초기화.
cluster_details[cluster_num] = {}
cluster_details[cluster_num]['cluster'] = cluster_num
# cluster_centers_.argsort()[:,::-1] 로 구한 index 를 이용하여 top n 피처 단어를 구함.
top_feature_indexes = centroid_feature_ordered_ind[cluster_num, :top_n_features]
top_features = [ feature_names[ind] for ind in top_feature_indexes ]
# top_feature_indexes를 이용해 해당 피처 단어의 중심 위치 상댓값 구함
top_feature_values = cluster_model.cluster_centers_[cluster_num, top_feature_indexes].tolist()
# cluster_details 딕셔너리 객체에 개별 군집별 핵심 단어와 중심위치 상대값, 그리고 해당 파일명 입력
cluster_details[cluster_num]['top_features'] = top_features
cluster_details[cluster_num]['top_features_value'] = top_feature_values
filenames = cluster_data[cluster_data['cluster_label'] == cluster_num]['filename']
filenames = filenames.values.tolist()
cluster_details[cluster_num]['filenames'] = filenames
return cluster_details
feature_names = tfidf_vect.get_feature_names()
cluster_details = get_cluster_details(cluster_model=km_cluster, cluster_data=document_df,\
feature_names=feature_names, clusters_num=3, top_n_features=10 )
print_cluster_details(cluster_details)
def print_cluster_details(cluster_details): for cluster_num, cluster_detail in cluster_details.items(): print('####### Cluster {0}'.format(cluster_num)) print('Top features:', cluster_detail['top_features']) print('Reviews 파일명 :',cluster_detail['filenames'][:7]) print('==================================================')
08 문서 유사도
문서 유사도 측정 방법 - 코사인 유사도
문서와 문서간의 유사도 측정 : 코사인 유사도 (Cosine SImilarity)사용, 벡터와 벡터간의 유사도를 비교시, 크기보다 "벡터의 상호 방향성"이 얼마나 유사한지에 기반
-> 두 벡터 사이의 사잇각을 구해서 얼마나 유사한지를 수치로 적용
두 벡터의 사잇각
유사도 코사인 = 두 벡터의 내적 / (총 벡터 크기의 합), 즉, 내적 결과를 총 벡터 크기로 정규화 (L2Norm)한 것과 같음
코사인 유사도가 유사도에 많이 사용되는 이유
- 문서를 피처 벡터화할 경우, 차원이 매우 많은 희소 행렬이 되기 쉬움 -> 문서와 문서 벡터 간의 크기에 기반한 유사도 지표(ex.유클리도 거리)의 경우, 정확도가 떨어짐
- 문서가 매우 긴 경우, 단어의 빈도수 증가 -> 빈도수에만 기반해서는 공정한 비교를 할 수 없음.
간단한 문서에 대해서 서로간의 문서 유사도를 코사인 유사도 기반으로 구하는 실습
import sklearn
print(sklearn.__version__)
import numpy as np
def cos_similarity(v1, v2):
dot_product = np.dot(v1, v2)
l2_norm = (np.sqrt(sum(np.square(v1))) * np.sqrt(sum(np.square(v2))))
similarity = dot_product / l2_norm
return similarity
from sklearn.feature_extraction.text import TfidfVectorizer
doc_list = ['if you take the blue pill, the story ends' ,
'if you take the red pill, you stay in Wonderland',
'if you take the red pill, I show you how deep the rabbit hole goes']
tfidf_vect_simple = TfidfVectorizer()
feature_vect_simple = tfidf_vect_simple.fit_transform(doc_list)
print(feature_vect_simple.shape)
# TFidfVectorizer로 transform()한 결과는 Sparse Matrix이므로 Dense Matrix로 변환.
feature_vect_dense = feature_vect_simple.todense()
#첫번째 문장과 두번째 문장의 feature vector 추출
vect1 = np.array(feature_vect_dense[0]).reshape(-1,)
vect2 = np.array(feature_vect_dense[1]).reshape(-1,)
#첫번째 문장과 두번째 문장의 feature vector로 두개 문장의 Cosine 유사도 추출
similarity_simple = cos_similarity(vect1, vect2 )
print('문장 1, 문장 2 Cosine 유사도: {0:.3f}'.format(similarity_simple))
vect1 = np.array(feature_vect_dense[0]).reshape(-1,)
vect3 = np.array(feature_vect_dense[2]).reshape(-1,)
similarity_simple = cos_similarity(vect1, vect3 )
print('문장 1, 문장 3 Cosine 유사도: {0:.3f}'.format(similarity_simple))
vect2 = np.array(feature_vect_dense[1]).reshape(-1,)
vect3 = np.array(feature_vect_dense[2]).reshape(-1,)
similarity_simple = cos_similarity(vect2, vect3 )
print('문장 2, 문장 3 Cosine 유사도: {0:.3f}'.format(similarity_simple))
from sklearn.metrics.pairwise import cosine_similarity
similarity_simple_pair = cosine_similarity(feature_vect_simple[0] , feature_vect_simple)
print(similarity_simple_pair)
from sklearn.metrics.pairwise import cosine_similarity
similarity_simple_pair = cosine_similarity(feature_vect_simple[0] , feature_vect_simple[1:])
print(similarity_simple_pair)
similarity_simple_pair = cosine_similarity(feature_vect_simple , feature_vect_simple)
print(similarity_simple_pair)
print('shape:',similarity_simple_pair.shape)
Opinion Review 데이터 세트를 이용한 문서 유사도 측정 실습
from nltk.stem import WordNetLemmatizer
import nltk
import string
remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
lemmar = WordNetLemmatizer()
# 입력으로 들어온 token단어들에 대해서 lemmatization 어근 변환.
def LemTokens(tokens):
return [lemmar.lemmatize(token) for token in tokens]
# TfidfVectorizer 객체 생성 시 tokenizer인자로 해당 함수를 설정하여 lemmatization 적용
# 입력으로 문장을 받아서 stop words 제거-> 소문자 변환 -> 단어 토큰화 -> lemmatization 어근 변환.
def LemNormalize(text):
return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))
import pandas as pd
import glob, os
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
import warnings
warnings.filterwarnings('ignore')
path = r'OpinosisDataset1.0\topics'
all_files = glob.glob(os.path.join(path, "*.data"))
filename_list = []
opinion_text = []
for file_ in all_files:
df = pd.read_table(file_,index_col=None, header=0,encoding='latin1')
filename_ = file_.split('\\')[-1]
filename = filename_.split('.')[0]
filename_list.append(filename)
opinion_text.append(df.to_string())
document_df = pd.DataFrame({'filename':filename_list, 'opinion_text':opinion_text})
tfidf_vect = TfidfVectorizer(tokenizer=LemNormalize, stop_words='english' , \
ngram_range=(1,2), min_df=0.05, max_df=0.85 )
feature_vect = tfidf_vect.fit_transform(document_df['opinion_text'])
km_cluster = KMeans(n_clusters=3, max_iter=10000, random_state=0)
km_cluster.fit(feature_vect)
cluster_label = km_cluster.labels_
cluster_centers = km_cluster.cluster_centers_
document_df['cluster_label'] = cluster_label
from sklearn.metrics.pairwise import cosine_similarity
# cluster_label=2인 데이터는 호텔로 클러스터링된 데이터임. DataFrame에서 해당 Index를 추출
hotel_indexes = document_df[document_df['cluster_label']==2].index
print('호텔로 클러스터링 된 문서들의 DataFrame Index:', hotel_indexes)
# 호텔로 클러스터링된 데이터 중 첫번째 문서를 추출하여 파일명 표시.
comparison_docname = document_df.iloc[hotel_indexes[0]]['filename']
print('##### 비교 기준 문서명 ',comparison_docname,' 와 타 문서 유사도######')
''' document_df에서 추출한 Index 객체를 feature_vect로 입력하여 호텔 클러스터링된 feature_vect 추출
이를 이용하여 호텔로 클러스터링된 문서 중 첫번째 문서와 다른 문서간의 코사인 유사도 측정.'''
similarity_pair = cosine_similarity(feature_vect[hotel_indexes[0]] , feature_vect[hotel_indexes])
print(similarity_pair)
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# argsort()를 이용하여 앞예제의 첫번째 문서와 타 문서간 유사도가 큰 순으로 정렬한 인덱스 반환하되 자기 자신은 제외.
sorted_index = similarity_pair.argsort()[:,::-1]
sorted_index = sorted_index[:, 1:]
# 유사도가 큰 순으로 hotel_indexes를 추출하여 재 정렬.
hotel_sorted_indexes = hotel_indexes[sorted_index.reshape(-1)]
# 유사도가 큰 순으로 유사도 값을 재정렬하되 자기 자신은 제외
hotel_1_sim_value = np.sort(similarity_pair.reshape(-1))[::-1]
hotel_1_sim_value = hotel_1_sim_value[1:]
# 유사도가 큰 순으로 정렬된 Index와 유사도값을 이용하여 파일명과 유사도값을 Seaborn 막대 그래프로 시각화
hotel_1_sim_df = pd.DataFrame()
hotel_1_sim_df['filename'] = document_df.iloc[hotel_sorted_indexes]['filename']
hotel_1_sim_df['similarity'] = hotel_1_sim_value
fig1 = plt.gcf()
sns.barplot(x='similarity', y='filename',data=hotel_1_sim_df)
plt.title(comparison_docname)
fig1.savefig('p553_hotel.tif', format='tif', dpi=300, bbox_inches='tight')
09 한글 텍스트 처리 - 네이버 영화 평점 감성 분석
한글 NLP의 처리 어려움
한글언어의 언어 처리 어려움 > 영어/ 라틴어 처리
- 주된 원인은 "띄어쓰기"와 "다양한 조사" 때문.
- ex) '아버지가 방에 들어가신다'를 잘못 띄어쓰기 할 경우 '아버지 가방에 들어가신다'와 같이 의미 왜곡이 됨 <-> 영어의 경우, 잘못된 띄어쓰기는 없는 단어로 인식되어, 분석에서 제외될 수 있음
- 주어나 목적어를 위해 추가되는 조사의 경우, 경우 수가 많아 어근 추출(Stemming / Lemmatization)등의 전처리시 제거하기가 까다로움
- ex) 어근 단어 기준 : '집은' '집이', '집으로', '집에서', '집에',.. etc "너희 집은 어디 있니?"에서의 은이 조사인지 아니면 은(银)인지 구분하기 어려움
KoNLPy 소개
KoNLPy: 파이썬의 대표적인 한글 형태소('단어로서 의미를 가지는 최소 단위') 패키지
형태소 분석(Morphological analysis)라는 말뭉치를 이러한 형태소 어근 단위로 쪼개어 각 형태쇵 품사 태깅 (POS tagging)을 부착하는 작업을 일반적으로 지칭
import pandas as pd
train_df = pd.read_csv('ratings_train.txt', sep='\t')
train_df.head(3)
train_df['label'].value_counts( )
from konlpy.tag import Twitter
twitter = Twitter()
def tw_tokenizer(text):
# 입력 인자로 들어온 text 를 형태소 단어로 토큰화 하여 list 객체 반환
tokens_ko = twitter.morphs(text)
return tokens_ko
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
# Twitter 객체의 morphs( ) 객체를 이용한 tokenizer를 사용. ngram_range는 (1,2)
tfidf_vect = TfidfVectorizer(tokenizer=tw_tokenizer, ngram_range=(1,2), min_df=3, max_df=0.9)
tfidf_vect.fit(train_df['document'])
tfidf_matrix_train = tfidf_vect.transform(train_df['document'])
# Logistic Regression 을 이용하여 감성 분석 Classification 수행.
lg_clf = LogisticRegression(random_state=0, solver='liblinear')
# Parameter C 최적화를 위해 GridSearchCV 를 이용.
params = { 'C': [1 ,3.5, 4.5, 5.5, 10 ] }
grid_cv = GridSearchCV(lg_clf , param_grid=params , cv=3 ,scoring='accuracy', verbose=1 )
grid_cv.fit(tfidf_matrix_train , train_df['label'] )
print(grid_cv.best_params_ , round(grid_cv.best_score_,4))
from sklearn.metrics import accuracy_score
# 학습 데이터를 적용한 TfidfVectorizer를 이용하여 테스트 데이터를 TF-IDF 값으로 Feature 변환함.
tfidf_matrix_test = tfidf_vect.transform(test_df['document'])
# classifier 는 GridSearchCV에서 최적 파라미터로 학습된 classifier를 그대로 이용
best_estimator = grid_cv.best_estimator_
preds = best_estimator.predict(tfidf_matrix_test)
print('Logistic Regression 정확도: ',accuracy_score(test_df['label'],preds))
10 텍스트 분석 실습 - 캐글 Mercari Price Suggestion Challenge
일본의 대형 온라인 쇼핑몰 Mercari사의 제품의 가격 예측 과제
주어진 데이터 세트 : 제품에 대한 여러 속성 및, 제품 설명 등의 텍스트로 구성
데이터 전처리
from sklearn.linear_model import Ridge, LogisticRegression
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.feature_extraction.text import CountVectorizer, TfidVectorizer
import pands as pd
mercari_df=pd.read_csv('mercari_train.tsv', sep = '\t')
print(mercari_df.shape)
mercari_df.head(3)
print(mercari_df.info())
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
y_train_df = mercari_df['price']
plt.figure(figsize = (6,4))
sns.distplot(y_train_df, kde=False)
import numpy as np
y_train_df = np.log1p(y_Train_df)
sns.distplot(y_train_df, kde=False)
mercari_df['price'] = np.log1p(mercari_df['price'])
mercari_df['price'].head(3)
print('Shipping 값 유형:\n', mercari_df['shipping'].value_counts())
print('item_condition_id 값 유형: \n', mercari_df['item_condition_id'].value_counts())
boolean_cond = mercari_df['item_description'] = 'No Description yet'
mercari_df[boolean_cond]['item_description'].count()
피처 인코딩과 피처 벡터화
릿지 회귀 모델 구축 및 평가
LightGBM 회귀 모델 구축과 앙상블을 이용한 최종 예측 평가
실습의 경우 추후 업로드 예정..