Topic modeling(LSA, LDA)

정예슬·2022년 1월 28일
0

NLP

목록 보기
1/4

Topic modeling의 두 가지 알고리즘을 구현해 보자.

▪ LSA(잠재의미 분석)


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(잠재 디리클레 할당)


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
딥러닝을 이용한 자연어 처리 입문[위키독스]

profile
춘식이랑 함께하는 개발일지

0개의 댓글