문서 군집화는 비슷한 텍스트 구성의 문서를 군집화(Clustering)하는 것입니다. 문서 군집화는 동일한 군집에 속하는 문서를 같은 카테고리 소속으로 분류할 수 있으므로 앞에서 소개한 텍스트 분류 기반의 문서 분류와 유사합니다. 하지만 텍스트 분류 기반의 문서 분류는 사전에 결정 카테고리 값을 가진 학습 데이터 세트가 필요한 데 반해, 문서 군집화는 학습 데이터 세트가 필요없는 비지도학습 기반으로 동작합니다.
예제는 UCI의 Opinion Review의 데이타를 이용합니다. 하기 화일을 다운로드 후 압축을 풀어도 됩니다.
import pandas as pd
import glob, os
path = r'C:\\Users\\HANA\\PycharmProjects\\HANATOUR\\NLP\\TEXT_Example\\topic'
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})
print(document_df.head())
여러 개의 파일을 DataFrame으로 읽은 후 다시 문자열로 반환한 뒤 파일 내용 리스트에 추가합니다.
문서를 TF-IDF 형태로 피처 벡터화하기전에 공통 함수를 작성합니다.
#WordNet is a semantically-oriented dictionary of English included in NLTK.
def LemTokens(tokens):
lemmer = nltk.stem.WordNetLemmatizer()
return [lemmer.lemmatize(token) for token in tokens]
# remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
def LemNormalize(text):
remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))
tokenizer는 Lemmatiation을 이용한 위의 함수를 이용하여 opinion_text칼럼을 입력하면 개별 문서 텍스트에 대해 TF-IDF변환된 피처 벡터화된 행렬을 구할 수 있습니다.
from sklearn.feature_extraction.text import TfidfVectorizer
import Common_Module.CMNLP as CMNLP
tfodf_vect = TfidfVectorizer(tokenizer=CMNLP.LemNormalize, stop_words='english', ngram_range=(1,2), min_df=0.05, max_df=0.85)
feature_vect = tfodf_vect.fit_transform(document_df['opinion_text'])
각 문서별 텍스트가 TF-IDF변환된 피처 벡터화 행렬 데이터에 대해서 군집화를 수행해 어떤 문서끼리 군집화되는지 확인해 보겠습니다. 여기서는 Kmeans를 이용합니다.
from sklearn.cluster import KMeans
km_cluster = KMeans(n_clusters=5, max_iter=1000, 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
print(document_df.head())
print(document_df[document_df['cluster_label']==0].sort_values(by='filename'))
print(document_df[document_df['cluster_label']==1].sort_values(by='filename'))
print(document_df[document_df['cluster_label']==2].sort_values(by='filename'))
print(document_df[document_df['cluster_label']==3].sort_values(by='filename'))
print(document_df[document_df['cluster_label']==4].sort_values(by='filename'))
군집별 핵심 단어 추출하기
이번에는 각 군집(Cluster)에 속한 문서는 핵심 단어를 주축으로 군집화돼 있을 것입니다. 이번에는 각 군집을 구성하는 핵심 단어가 어떤 것이 있는지 확인해 보겠습니다.
Kmeans객체는 각 군집을 구성하는 단어 피처가 군집의 중심(Centroid)을 기준으로 얼마나 가깝게 위치해 있는지 cluster_centers_라는 속성으로 제공합니다. cluster_centers_는 배열 값으로 제공되며, 행은 개별 군집을, 열은 개별 피처를 의미합니다. 각 배역 내의 값은 개별 군집 내의 상대 위치를 숫자 값으로 표현한 일종의 좌표 값입니다. 예를 들어 cluster_centers[0,1]은 0번 군집에서 두 번째 피처의 위치 값입니다.
cluster_centers = km_cluster.cluster_centers_
print('cluster centers shape :', cluster_centers.shape)
print(cluster_centers)
위의 결과를 보면 (5, 4611)로 5개의 군집, word 피처가 2409개로 구성되었음을 의미합니다. 각 행의 배열 값은 각 군집 내의 2409개 피처의 위치가 개별 중심과 얼마나 가까운가를 상대 값으로 나타낸 것입니다. 0에서 1까지의 값을 가지며 수 있으며 1에 가까울수록 중심과 가까운 값을 의미합니다.
cluster centers shape : (5, 4611)
[[0.00857792 0. 0. ... 0.01710556 0. 0. ]
[0. 0.00099499 0.00174637 ... 0. 0.00183397 0.00144581]
[0. 0.00102835 0. ... 0. 0. 0. ]
[0.01282853 0. 0. ... 0. 0. 0. ]
[0.00881133 0. 0. ... 0.00331061 0. 0. ]]
각 그룹과 과장 연관성이 높은 단어의 핵심을 뽑기 위한 함수와 명령어를 실행한다.
# 군집별 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]['filename'] = filenames
return 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('filename :', cluster_detail['filename'][:7])
print('==================================================')
feature_names = tfodf_vect.get_feature_names()
cluster_details = CMNLP.get_cluster_details(cluster_model=km_cluster, cluster_data=document_df, feature_names=feature_names, clusters_num=5, top_n_features=10)
CMNLP.print_cluster_details(cluster_details)
하기 결과를 확인하면 각 결과를 확인할 수 있다.
==================================================
####### Cluster 1
Top features: ['room', 'hotel', 'service', 'staff', 'food', 'location', 'bathroom', 'clean', 'price', 'parking']
filename : ['bathroom_bestwestern_hotel_sfo', 'food_holiday_inn_london', 'food_swissotel_chicago', 'free_bestwestern_hotel_sfo', 'location_bestwestern_hotel_sfo', 'location_holiday_inn_london', 'parking_bestwestern_hotel_sfo']
==================================================
####### Cluster 2
Top features: ['interior', 'seat', 'mileage', 'comfortable', 'gas', 'gas mileage', 'car', 'performance', 'quality', 'comfort']
filename : ['comfort_honda_accord_2008', 'comfort_toyota_camry_2007', 'gas_mileage_toyota_camry_2007', 'interior_honda_accord_2008', 'interior_toyota_camry_2007', 'mileage_honda_accord_2008', 'performance_honda_accord_2008']
==================================================
####### Cluster 3
Top features: ['battery', 'battery life', 'life', 'video', 'performance', 'sound', 'ipod', 'sound quality', 'camera', 'video camera']
filename : ['battery-life_amazon_kindle', 'battery-life_ipod_nano_8gb', 'battery-life_netbook_1005ha', 'performance_netbook_1005ha', 'sound_ipod_nano_8gb', 'video_ipod_nano_8gb']
==================================================
####### Cluster 4
Top features: ['screen', 'keyboard', 'size', 'kindle', 'page', 'button', 'font', 'voice', 'feature', 'book']
filename : ['buttons_amazon_kindle', 'eyesight-issues_amazon_kindle', 'features_windows7', 'fonts_amazon_kindle', 'keyboard_netbook_1005ha', 'navigation_amazon_kindle', 'price_amazon_kindle']
==================================================
Process finished with exit code 0
'NPL with ML' 카테고리의 다른 글
토픽 모델링 [Topic Modeling] - LDA기법 소스포함 (0) | 2020.11.17 |
---|---|
감성 분석 (Sentiment Analysis) - 비지도학습 기반, VADER (0) | 2020.11.13 |
감성 분석 (Sentiment Analysis) - 지도학습 기반 (0) | 2020.11.12 |
문서 유사도 측정 - 코사인 유사도(Cosine Similarity)와 실전 연습 코드 (0) | 2020.11.08 |
한글 텍스트 처리 위한 KoNLPy를 이용한 네이버 영화 평점 감정 분석 (0) | 2020.11.01 |
텍스트 분류 실전 예제 - 뉴스그룹 분류 (0) | 2020.11.01 |
추천시스템 구축을 위한 텍스트 분석 이해 #2 - BOW (0) | 2020.10.28 |
추천시스템 구축을 위한 텍스트 분석 이해 #1 (0) | 2020.10.25 |