본문 바로가기
Computer/ML·DL·NLP

[이수안컴퓨터연구소] 토픽 모델링 Topic Modeling

by injeolmialmond 2022. 1. 8.

https://www.youtube.com/watch?v=Xt607xhpF6U 

https://colab.research.google.com/drive/1mIm9o41JOa-oFodaj0j26SjLx6y1xNVD?usp=sharing 

 

_6 토픽 모델링(Topic Modeling).ipynb

Colaboratory notebook

colab.research.google.com

 

토픽 모델링(Topic Modeling)

  • 토픽 모델링은 문서 집합에서 주제를 찾아내기 위한 기술
  • 토픽 모델링은 '특정 주제에 관한 문서에서는 특정 단어가 자주 등장할 것이다'라는 직관을 기반
  • 예를 들어, 주제가 '개'인 문서에서는 개의 품종, 개의 특성을 나타내는 단어가 다른 문서에 비해 많이 등장
  • 주로 사용되는 토픽 모델링 방법은 잠재 의미 분석잠재 디리클레 할당 기법이 있음

 

a. 잠재 의미 분석(Latent Semantic Analysis)

  • 잠재 의미 분석(LSA)은 주로 문서 색인의 의미 검색에 사용
  • 잠재 의미 인덱싱(Latent Semantic Indexing, LSI)로도 알려져 있음
  • LSA의 목표는 문서와 단어의 기반이 되는 잠재적인 토픽을 발견하는 것
  • 잠재적인 토픽은 문서에 있는 단어들의 분포를 주도한다고 가정
  • LSA 방법
    • 문서 모음에서 생성한 문서-단어 행렬(Document Term Matrix)에서 단어-토픽 행렬(Term-Topic Matrix)과 토픽-중요도 행렬(Topic-Importance Matrix), 그리고 토픽-문서 행렬(Topic-Document Matrix)로 분해

 

b. 잠재 디리클레 할당(Latent Dirichlet Allocation)

  • 잠재 디레클레 할당(LDA)은 대표적인 토픽 모델링 알고리즘 중 하나
  • 잠재 디레클레 할당 방법
    1. 사용자가 토픽의 개수를 지정해 알고리즘에 전달
    2. 모든 단어들을 토픽 중 하나에 할당
    3. 모든 문서의 모든 단어에 대해 단어 w가 가정에 의거, p(t|d), p(w|t)에 따라 토픽을 재할당, 이를 반복, 이 때 가정은 자신만이 잘못된 토픽에 할당되어 있고 다른 모든 단어는 올바른 토픽에 할당된다는 것을 의미
  • p(t|d) - 문서 d의 단어들 중 토픽 t에 해당하는 비율
  • 해당 문서의 자주 등장하는 다른 단어의 토픽이 해당 단어의 토픽이 될 가능성이 높음을 의미
  • p(w|t)- 단어 w를 가지고 있는 모든 문서들 중 토픽 t가 할당된 비율
  • 다른 문서에서 단어 w에 많이 할당된 토픽이 해당 단어의 토픽이 될 가능성이 높음을 의미

데이터 준비

from gensim.utils import tokenize
from sklearn import datasets
from sklearn.datasets import fetch_20newsgroups

datasets = fetch_20newsgroups(shuffle=True, random_state=1, remove=('headers', 'footers', 'quotes'))

documents = datasets.data

print(len(documents)) # 11314

documents[0] # "Well i'm not sure about the story nad it did seem biased. What\nI disagree with is your statement that the U.S. Media is out to\nruin Israels reputation. That is rediculous. The U.S. media is\nthe most pro-israeli media in the world. Having lived in Europe\nI realize that incidences such as the one described in the\nletter have occured. The U.S. media as a whole seem to try to\nignore them. The U.S. is subsidizing Israels existance and the\nEuropeans are not (at least not to the same 
# degree). So I think\nthat might be a reason they report more clearly on the\natrocities.\n\tWhat is a shame is that in Austria, daily reports of\nthe inhuman acts commited by Israeli soldiers and 
# the blessing\nreceived from the Government makes some of the Holocaust guilt\ngo away. After all, 
# look how the Jews are treating other races\nwhen they got power. It is unfortunate.\n"

# 정제가 안 됨. 특수문자, 개행문자가 포함 -> 전처리 필요

import re
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from gensim.parsing.preprocessing import preprocess_string

nltk.download('stopwords')

def clean_text(d):
    pattern = r'[a-zA-Z\s]'
    text = re.sub(pattern, ' ', d)
    return d

def clean_stopword(d):
    stop_words = stopwords.words('english')
    return ' '.join([w.lower() for w in d.split() if w not in stop_words and len(w) > 3])

def preprocessing(d):
    return preprocess_string(d)




import pandas as pd

news_df = pd.DataFrame({'article':documents})
len(news_df) # 11314

# 불필요한 것 빼기
news_df.replace("", float("NaN"), inplace=True) # inplace=True : 데이터 업데이트. 변수에 다시 지정 필요없음
news_df.dropna(inplace=True)
print(len(news_df)) # 11096

# 함수 적용하기
news_df['article'] = news_df['article'].apply(clean_text)
news_df['article'] = news_df['article'].apply(clean_stopword)
tokenized_news = news_df['article'].apply(preprocessing)
tokenized_news = tokenized_news.to_list()

import numpy as np

drop_news = [index for index, sentence in enumerate(tokenized_news) if len(sentence) <= 1]
news_texts = np.delete(tokenized_news, drop_news, axis=0)
print(len(news_texts)) # 10936

 

Gensim을 이용한 토픽 모델링

from gensim import corpora

dictionary = corpora.Dictionary(news_texts)
corpus = [dictionary.doc2bow(text) for text in news_texts]

print(corpus[1]) #BoW 형태로 만든 딕셔너리
#[(51, 1), (52, 1), (53, 1), (54, 1), (55, 1), (56, 1), (57, 1), (58, 2), (59, 1), (60, 1), (61, 1), (62, 2), (63, 1), (64, 1), (65, 1), (66, 1), (67, 1), (68, 2), (69, 3), (70, 1), (71, 1), (72, 1), (73, 1), (74, 1), (75, 2), 
#(76, 1), (77, 1), (78, 1), (79, 1), (80, 1), (81, 1), (82, 2), (83, 1), (84, 1), (85, 1), (86, 1)]

 

잠재 의미 분석을 위한 LsiModel

from gensim.models import LsiModel

lsi_model = LsiModel(corpus, num_topics=20, id2word=dictionary) # 문서가 20개니까 토픽 20개로 가정
topics = lsi_model.print_topics()
topics # 20개로 토픽 나눠줌




# coherence score : 몇 개 토픽이 적절한지 알 수 있는 지표

from gensim.models.coherencemodel import CoherenceModel

min_topics, max_topics = 20, 25
coherence_scores = []

for num_topics in range(min_topics, max_topics):
    model = LsiModel(corpus, num_topics=num_topics, id2word=dictionary)
    coherence = CoherenceModel(model = model, texts=news_texts,dictionary=dictionary)
    coherence_scores.append(coherence.get_coherence())

print(coherence_scores) # [0.46576455332579475, 0.5016853459132843, 0.4949544575536571, 0.4741464942175256, 0.46472790660377855]


# 시각화해서 나타내보자 

import matplotlib.pyplot as plt
plt.style.use('seaborn-white')

x = [i for i in range(min_topics, max_topics)]

plt.figure(figsize=(10,6))
plt.plot(x, coherence_scores)
plt.xlabel('Number of Topics')
plt.ylabel('Coherence Scores')
plt.show()

21일 때 가장 높고, 24일 때 가장 낮게 나타남 -> 24로 출력~

lsi_model = LsiModel(corpus, num_topics=24, id2word=dictionary) 
topics = lsi_model.print_topics(num_topics=24)
topics # 다시 24개 토픽으로 출력됨

잠재 디리클레 할당을 위한 LdaModel

from gensim.models import LdaModel

lda_model = LdaModel(corpus, num_topics=20, id2word=dictionary)
topics = lda_model.print_topics()
topics



# 여기서도 coherence score 써볼게~

from gensim.models.coherencemodel import CoherenceModel

min_topics, max_topics = 20, 25
coherence_scores = []

for num_topics in range(min_topics, max_topics):
    model = LdaModel(corpus, num_topics=num_topics, id2word=dictionary)
    coherence = CoherenceModel(model = model, texts=news_texts,dictionary=dictionary)
    coherence_scores.append(coherence.get_coherence())

print(coherence_scores)  #[0.5173430261178437, 0.4852693815776289, 0.5046177943900898, 0.49921437467166074, 0.525714592171359]


import matplotlib.pyplot as plt
plt.style.use('seaborn-white')

x = [i for i in range(min_topics, max_topics)]

plt.figure(figsize=(10,6))
plt.plot(x, coherence_scores)
plt.xlabel('Number of Topics')
plt.ylabel('Coherence Scores')
plt.show()

21에서 가장 낮게 나왔다! -> 21개 토픽으로 설정

lda_model = LdaModel(corpus, num_topics=21, id2word=dictionary) 
topics = lda_model.print_topics(num_topics=21)
topics

토픽 모델링 시각화

# pip install pyLDAvis
# pyLDAvis 설치
# 이후 내용은 주피터에서 다시 해보깅..
# 이수안씨는 코랩에서 아주 멋찌게 시각화를 해놓앗네룡..
# 주제끼리 많이 겹치지 않게 잘 시각화가 됩니다!

import pyLDAvis.gensim

pyLDAvis.enable_notebook()
vis=pyLDAvis.gensim.prepare(lda_model, corpus, dictionary)
pyLDAvis.display(vis)

댓글