본문 바로가기
컴퓨터 COMPUTER/Urban Data Analytics 데이터분석

[파이썬을 이용한 토픽모델링] step3. Gensim 과 Mallet 을 활용한 LDA 모델링 (Python)

by 매실이 maesiri 2020. 2. 19.

아래 step 2 까지 성공적으로 수행했다면 자신이 분석하고 싶은 텍스트 뭉터기의 json 파일이 있을 것이다.

 

 

[파이썬을 이용한 토픽모델링] : step2. 웹크롤링 툴 (Octoparse) 을 이용해 데이터 수집하기

Octoparse 가입, Standard Free Trial (14일) 등록 후 다운로드 https://www.octoparse.com/ 위 링크에서 회원가입 후 (이메일 verification 단계 있음) Free Trial 중 Standard 형식을 선택 다운로드 받은 zip..

happy-chipmunk.tistory.com

이제 본격적으로 토픽모델링을 돌려보자.

 

 

  • 데이터 업로드

jupyter notebook 을 실행시킨 후, Upload 버튼을 눌러 토픽모델링을 할 데이터를 jupyter 폴더 내에 올린다. 

 

  • 새로운 작업 공간 만들고 사용법 익히기

우측 상단의 New → Python 3 을 눌러 새로운 작업 공간을 만든다.

 

  • Mallet 설치

아래 링크에서 Mallet 을 다운로드 받고 설치한다.

http://mallet.cs.umass.edu/dist/mallet-2.0.8.zip

 

  • nltk stopwords, spacy model 다운로드

2-1) NLTK 설치를 하려면 먼저 numpy 를 설치해야 한다. 

아래의 사이트에 접속해 numpy 파일을 다운로드 받는다. 

http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy

본인이 설치한 파이썬 버전을 확인하고 해당하는 파일을 다운 받으면 된다.

 

그 후 명령프롬프트 창을 열어 아래 pip 명령어를 통해 설치한다.

python -m pip install [numpy 파일이 다운로드된 경로/numpy-1.11.2+mkl-cp35-cp35m-win32.whl]

 

2-2) numpy 설치가 완료되었다면 NLTK 를 pip로 설치하자.

명령프롬프트 창에  pip install nltk 입력. 아래 이미지의 경우 이미 설치가 된 상태여서 세줄만 출력되었지만 첫 설치라면 텍스트가 많이 출력될 수 있다.

그 후 Python Shell 에 들어가서 아래와 같이 두 줄을 입력해보자. shell은 윈도우 검색창 (전체 화면 아래 검색창)에 idle 이라고 입력한 후  Python IDLE 을 실행하면 나온다.

아래와 같은 창이 뜨면 All packages 를 다운로드 하면 된다.

 

 

다음, 아까와 같이 Python console 에 들어가서 우선 데이터 전처리에 필요한 nltk 를 import 하자.  

‘#’으로 시작하는 문장은 설명이므로 무시한다.

그 후 명령 프롬프트 창에 들어가 아래 두번째와 명령과 같이 입력한다. 

# Run in python console
import nltk; nltk.download('stopwords')

# Run in terminal or command prompt
python3 -m spacy download en
  • 패키지 다운로드

이 매뉴얼에서는 그 외에도 re, gensim, spacy, pyLDAvis, matplotlib, numpy, pandas pip패키지를 모두 사용할 것이므로 명령 프롬프트 창에서 모두 pip 로 설치하면 된다. 아래의 이미지는 gensim 을 설치할 때의 모습이다.

> pip install {패키지 이름} 

  • 패키지와 데이터  import

그 후 아래의 코드를 jupyter 에 붙여넣어보자. 붙여 넣을 때 ‘Test_file.json’ 자리에는 데이터 파일의 이름을 넣도록 한다. 

 

Mallet 을 다운로드한 위치를 Mallet 경로설정 부분으로 수정한다.

import re

import numpy as np

import pandas as pd

from pprint import pprint



import json

import codecs



# Gensim

import gensim

import gensim.corpora as corpora

from gensim.utils import simple_preprocess

from gensim.models import CoherenceModel



import os

from gensim.models.wrappers import LdaMallet



os.environ['MALLET_HOME'] = 'C:/Users/user/Desktop/mallet-2.0.8'



# spacy for lemmatization

import spacy



# Plotting tools

import pyLDAvis

import pyLDAvis.gensim  # don't skip this

import matplotlib.pyplot as plt

# %matplotlib inline



# Enable logging for gensim - optional

import logging

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.ERROR)



import warnings

warnings.filterwarnings("ignore",category=DeprecationWarning)



# NLTK Stop words

from nltk.corpus import stopwords

stop_words = stopwords.words('english')

stop_words.extend(['from', 're', 'edu'])



# Import data

with codecs.open('Test_file.json', 'r', 'utf-8-sig') as json_file:  

    df= pd.DataFrame(json.load(json_file))

df.head()



# Convert to list

data = df.values.tolist()

pprint(data[:1])

 

  • Stopword 설정

Stopword list 란, 데이터 전처리 과정에서 제거하려는 단어의 목록이다. NLTK 패키지에서 제공하는 Stopword list 가 기본적인 단어들은 제거해주지만, 사용자가 추가로 그 리스트를 extend 할 수 있다. 위 코드에서 ‘from’ ‘re’ ‘edu’ 는 이 매뉴얼에서 추가로 설정한 것으로, 만약 disaster 라는 단어를 추가로 제거하고 싶다면 ‘edu’ 뒤에 ‘disaster’ 를 추가로 넣어주면 된다.

 

Run 버튼 눌러 실행시키면 아래와 같이 따옴표로 분리된 문서를 볼 수 있다. 코드 마지막 줄에 pprint(data[:1]) 은 list 형태로 바뀐 데이터 중 1번째 항목을 보여주는 것으로,  업로드한 파일에서 첫번째 논문의 초록이라고 볼 수 있다. 

 

  • Tokenizing

다음, 아래 코드를 새 박스 안에 붙여넣어보자. Tokenizing은 문장들을 단어로 쪼개는 과정이다.

# Tokenize words

def sent_to_words(sentences):

    for sentence in sentences:

        yield(gensim.utils.simple_preprocess(str(sentence), deacc=True))  # deacc=True removes punctuations



data_words = list(sent_to_words(data))



print(data_words[:1])

 

Run 버튼을 눌러 실행하면 아래와 같은 출력 결과를 볼 수 있다.

print 는 단지 과정을 보기 위한 용도로, 나중에 모두 확인이 되면 지워도 된다.

 

  • Bigram, Trigram 모델 사용하기

계속 해서 다음 블록을 붙여넣고 실행한다. Bigram 은 문서에서 자주 붙여 쓰는 두개의 단어들, Trigram 은 문서에서 자주 붙여쓰는 세개의 단어들을 말한다. 

threshold 가 높을수록 단어들이 붙기 어려워진다. 이 매뉴얼에서는 디폴트 값 100으로 진행해보겠다.

# Build the bigram and trigram models

bigram = gensim.models.Phrases(data_words, min_count=5, threshold=100) # higher threshold fewer phrases.

trigram = gensim.models.Phrases(bigram[data_words], threshold=100)  



# Faster way to get a sentence clubbed as a trigram/bigram

bigram_mod = gensim.models.phrases.Phraser(bigram)

trigram_mod = gensim.models.phrases.Phraser(trigram)



# See trigram example

print(trigram_mod[bigram_mod[data_words[0]]])

 

 

  • Stopword 없애고 Lemmatize 하기

쓸데없는 단어들을 지우고, 품사에 따라 나누는 작업을 하기 위해 아래 코드를 붙여넣어 실행한다.

# Define functions for stopwords, bigrams, trigrams and lemmatization

def remove_stopwords(texts):

    return [[word for word in simple_preprocess(str(doc)) if word not in stop_words] for doc in texts]



def make_bigrams(texts):

    return [bigram_mod[doc] for doc in texts]



def make_trigrams(texts):

    return [trigram_mod[bigram_mod[doc]] for doc in texts]



def lemmatization(texts, allowed_postags=['NOUN', 'ADJ', 'VERB', 'ADV']):

    """https://spacy.io/api/annotation"""

    texts_out = []

    for sent in texts:

        doc = nlp(" ".join(sent)) 

        texts_out.append([token.lemma_ for token in doc if token.pos_ in allowed_postags])

    return texts_out



# Remove Stop Words

data_words_nostops = remove_stopwords(data_words)



# Form Bigrams

data_words_bigrams = make_bigrams(data_words_nostops)



# Initialize spacy 'en' model, keeping only tagger component (for efficiency)

# python3 -m spacy download en

nlp = spacy.load('en', disable=['parser', 'ner'])



# Do lemmatization keeping only noun, adj, vb, adv

data_lemmatized = lemmatization(data_words_bigrams, allowed_postags=['NOUN', 'ADJ', 'VERB', 'ADV'])



print(data_lemmatized[:1])

 

 

  • Dictionary 와 Corpus 만들기 

토픽 모델에 사용될 dictionary(id2word)와 corpus 를 만들기 위해 아래 코드를 붙여넣고 실행한다. 

# Create Dictionary

id2word = corpora.Dictionary(data_lemmatized)



# Create Corpus

texts = data_lemmatized



# Term Document Frequency

corpus = [id2word.doc2bow(text) for text in texts]



# View

print(corpus[:1])

 

아래와 같이 숫자쌍이 출력될텐데, 이는 문서에서 나오는 단어마다 id를 달고 frequency를 체크한 것이다. 

+) 궁금하다면  id2word[0] 를 출력해서 아이디에 해당하는 단어가 무엇인지 알 수 있다. (0 자리에 아이디 바꿔가면서 체크할 수 있음)

 

 

  • LDA 모델을 통해 생성된 주제 출력하고 해석해보기

아래 코드를 붙여넣고, Mallet 을 다운로드 한 경로를 ‘’ 안에 넣어준다. 토픽의 갯수는 우선 20으로 정해두고 이후에 최적화 시켜보겠다.

mallet_path = 'C:/Users/user/Desktop/mallet-2.0.8/bin/mallet'

ldamallet = gensim.models.wrappers.LdaMallet(mallet_path, corpus=corpus, num_topics=20, id2word=id2word)

# Show Topics

pprint(ldamallet.show_topics(formatted=False))

 

실행버튼을 누르면 아래와 같이 명령프롬프트 창에 바쁘게 모델이 돌아가는 모습을 볼 수 있다. 

실행이 끝나면 Jupyter 에 20개의 토픽이 출력된 것을 볼 수 있다. 

앞에 나오는 숫자는 토픽의 id, 그 뒤의 단어와 숫자는 각각 그 토픽을 대표하는 keyword와 그 토픽에서의 비중이다. 

 

  • 모델 Coherence Score 출력해보기

아래 코드를 실행시키면 모델의 Coherence Score 가 나온다. 이 지수는 높을수록 좋다. 

이 매뉴얼에서의 예시는 Coherence 가 아직 낮지만, 위에서 숫자를 조정하면서 높여보거나 Mallet 가 아닌 Gensim 의 내장 LDA 모델을 이용해 결과를 비교하면서 높일 수 있다.

 

# Compute Coherence Score

coherence_model_ldamallet = CoherenceModel(model=ldamallet, texts=data_lemmatized, dictionary=id2word, coherence='c_v')

coherence_ldamallet = coherence_model_ldamallet.get_coherence()

print('\nCoherence Score: ', coherence_ldamallet)

 

  • 토픽 데이터 시각화 하기

이제 pyLDAvis 를 이용해 모델링 결과를 시각화해보겠다.

아래 코드를 돌리면 몇가지 warning 이 발생하지만 무시해도 괜찮다.

model = gensim.models.wrappers.ldamallet.malletmodel2ldamodel(ldamallet)

pyLDAvis.enable_notebook()

vis = pyLDAvis.gensim.prepare(model, corpus, id2word)

vis

pyLDAvis.save_html(vis, 'LDAMallet_Optimal_disaster.html')

 

실행 후 jupyter notebook 작업공간이 모두 보이는 Home 에 가보면 내가 설정한 이름의 html 파일이 생긴 것을 볼 수 있다. 이를 열어보자.

 

아래와 같이 시각화된 데이터를 볼 수 있다. 좌측 상단에 토픽의 id 를 넣거나 마우스로 동그라미 위를 hover 하면 우측의 단어 비중 그래프가 바뀌는 것을 볼 수 있다.



 

다음 스텝으로 토픽 갯수를 최적화하고 시각화하는 방법을 더 알아보겟슘당

 

* Reference

https://www.machinelearningplus.com/nlp/topic-modeling-gensim-python/

반응형

댓글