Topic modeling의 두 가지 알고리즘을 구현해 보자.
LSA(Latent Semantic Analysis)
는 DTM이나 TF-IDF 행렬에 Truncated SVD를 사용하여 차원을 축소시키고, 단어들의 잠재 의미를 끌어낸다.
✅ 영어 버전 구현(fetch_20newsgroups dataset)
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_20newsgroups
import nltk
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD
import warnings
warnings.filterwarnings('ignore')
# *-- 샘플 수 확인 --*
dataset = fetch_20newsgroups(shuffle=True, random_state=1,
remove=('headers', 'footers', 'quotes'))
documents = dataset.data
print(f'샘플의 수 : {len(documents)}')
# 데이터 카테고리(20개) 확인
print(dataset.target_names)
# *-- 텍스트 전처리 --*
df = pd.DataFrame({'documents' : documents})
# 정규식으로 특수문자 제거
df['cleaned_doc'] = df['documents'].str.replace('[^a-zA-z]', ' ')
# 3자 미만 제거
df['cleaned_doc'] = df['cleaned_doc'].apply(lambda x : ' '.join([w for w in
x.split() if len(x)>3]))
# 소문자로 변환
df['cleaned_doc'] = df['cleaned_doc'].apply(lambda x : x.lower())
# 불용어 처리
stop_words = stopwords.words('english')
tokenized_doc = df['cleaned_doc'].apply(lambda x : x.split())
tokenized_doc = tokenized_doc.apply(lambda x : [item for item in x
if item not in stop_words])
print(tokenized_doc[1])
# *-- TF-IDF 행렬 --*
detokenized_doc = [] # 역토큰화
for i in range(len(df)) :
t = ' '.join(tokenized_doc[i])
detokenized_doc.append(t)
>
df['clean_doc'] = detokenized_doc
# 상위 1000개 단어 보존
vectorizer = TfidfVectorizer(stop_words='english', max_features = 1000,
max_df=0.5, smooth_idf=True)
X = vectorizer.fit_transform(df['clean_doc'])
print(f'TF-IDF 행렬 크기 : {X.shape}')
# *-- TOPIC MODELING --*
svd = TruncatedSVD(n_components=20, algorithm='randomized', n_iter=100,
random_state=0)
svd.fit(X)
len(svd.components_)
print(np.shape(svd.components_))
terms = vectorizer.get_feature_names()
def get_topics(components, feature_names, n=5) :
for idx, topic in enumerate(components) : # 각 토픽에 대한 주요 단어 n개 출력
print('Topic %d:' %(idx+1), [(feature_names[i], topic[i].round(5))
for i in topic.argsort()[:-n - 1 : -1]])
get_topics(svd.components_, terms)
LDA(Latent Dirichlet Allocation)
은 문서 집합에서 존재하는 토픽을 찾는 알고리즘으로,
단어가 특정 토픽에 존재할 확률
과문서에 특정 토픽이 존재할 확률
을 결합확률로 추정하여 토픽을 추출한다.
✅ 영어 버전 구현(fetch_20newsgroups dataset)
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_20newsgroups
import nltk
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD
import warnings
warnings.filterwarnings('ignore')
# -- 샘플 데이터는 LSA와 같은 방법으로 가져온다 -- #
# *-- 샘플 수 확인 --*
dataset = fetch_20newsgroups(shuffle=True, random_state=1,
remove=('headers', 'footers', 'quotes'))
documents = dataset.data
print(f'샘플의 수 : {len(documents)}')
# 데이터 카테고리(20개) 확인
print(dataset.target_names)
# *-- 텍스트 전처리 --*
df = pd.DataFrame({'documents' : documents})
df['cleaned_doc'] = df['documents'].str.replace('[^a-zA-z]', ' ')
df['cleaned_doc'] = df['cleaned_doc'].apply(lambda x : ' '.join([w for w in
x.split() if len(x)>3]))
df['cleaned_doc'] = df['cleaned_doc'].apply(lambda x : x.lower())
# 불용어 처리
stop_words = stopwords.words('english')
tokenized_doc = df['cleaned_doc'].apply(lambda x : x.split())
tokenized_doc = tokenized_doc.apply(lambda x : [item for item in x
if item not in stop_words])
print(tokenized_doc[:5]) # tokenized data check
# 말뭉치 단어 사전 생성
from gensim import corpora
dic = corpora.Dictionary(tokenized_doc)
corpus = [dic.doc2bow(text) for text in tokenized_doc]
print(corpus[1])
# *-- LDA 모델링 --*
import gensim
num_topics = 20
lda = gensim.models.ldamodel.LdaModel(corpus, num_topics=num_topics,
id2word=dic, passes=15)
topics = lda.print_topics(num_words=4)
for topic in topics :
print(topic) # --> 20개의 토픽과 해당 토픽에 대한 각각의 기여도를 확인
# *-- 시각화 --*
import pyLDAvis.gensim_models # pip install pyLDAvis
pyLDAvis.enable_notebook()
vis = pyLDAvis.gensim_models.prepare(lda, corpus, dic)
pyLDAvis.display(vis)
✅ 한글 버전 구현(customed-data)
1:1 문의내역 데이터
를 dataset으로 설정하여 분석을 진행했다.
*-- 데이터 전처리 --*
import re
data['inqry'] = data['inqry'].apply(lambda x : re.sub('[^ A-Za-z0-9가-힣]', '', x))
data = data[data['inqry']!='']
from tqdm import tqdm
tqdm.pandas()
# word-tokenizing
data['inqry_tokens'] = data['inqry'].progress_apply(nltk.word_tokenize)
# 한국어 불용어 사전 정의 (txt 파일 출처 https://bab2min.tistory.com/544)
f = open('./data/한국어불용어100.txt', encoding='utf8')
# print(f.readlines())
stopwords = f.readlines()
f.close()
# 불러온 불용어 파일 리스트로 저장
stopwords = [re.sub('[^ 가-힣]', '', text) for text in stopwords ]
# 불용어 새로 지정
temp = ['도', '는', '의', '가', '이', '은', '한', '에', '하', '고',
'을', '를', '인', '듯', '과', '와', '네', '들', '지', '임',
'게','로', '요','안', '으로', '데', '하는', '하고','인데', '야',
'이고', '에게', '것', '거', '좀', '다', '왜', '더', '여기', '아마',
'합니다', '입니다','습니다', '너무','제가','전', '안', '때', '가요',
'이거','이건','무슨','있는데','하는데','는데','해서','라서','해야','너무']
# 전처리
data['inqry_tokens'] = data['inqry_tokens'].progress_apply(lambda x :
[item for item in x if item not in stopwords and len(item) > 1 ])
data.reset_index(drop=True, inplace=True)
data.head(3)
# *-- 단어 사전 생성 --*
from gensim import corpora
inqry_token = data['inqry_tokens']
dic = corpora.Dictionary(inqry_token)
corpus = [dic.doc2bow(text) for text in inqry_token]
print(corpus[1])
# *-- LDA 모델링 결과 시각화 --*
import pyLDAvis.gensim_models
pyLDAvis.enable_notebook()
vis = pyLDAvis.gensim_models.prepare(lda, corpus, dic)
pyLDAvis.display(vis)
# *-- 문서별 토픽 분포 확인 --*
for i, topic_list in enumerate(lda[corpus]) :
if i == 10 :
break
print(i+1, '번째 문서의 topic 비율 :', topic_list)
# *-- dataframe으로 생성 --*
def make_topictable_per_doc(lda, corpus) :
topic_table = pd.DataFrame()
for i, topic_list in enumerate(lda[corpus]) :
doc = topic_list[0] if lda.per_word_topics else topic_list
doc = sorted(doc, key=lambda x : (x[1]), reverse=True) # 비중 높은 순으로 정렬
for j, (topic_num, prop_topic) in enumerate(doc) :
if j == 0 :
topic_table = topic_table.append(pd.Series([int(topic_num),
round(prop_topic,4), topic_list]), ignore_index=True)
else :
break
return topic_table
# 출력
df = make_topictable_per_doc(lda, corpus)
df.reset_index(inplace=True)
df.columns = ['문서 번호', '가장 비중이 높은 토픽', '가장 높은 토픽의 비중', '각 토픽의 비중']
df[:10]
◾ Reference
딥러닝을 이용한 자연어 처리 입문[위키독스]