토픽 모델링 알고리즘인 LSA 의 단점을 보완한 대표적인 알고리즘이 잠재 디리클레 할당(LDA) 이다. 문서들에서 발견된 단어의 빈도수를 분석하여, 해당 문서가 어떤 주제를 다루고 있을지 예측할 수 있다. LDA 를 사용하여 토픽을 몇개 얻을 것인지 사용자가 지정해야 하는데, 이 하이퍼 파라미터로부터 결과가 달라질 수 있으므로 올바른 토픽을 얻기 위한 테스트가 필요하다.

 

LDA 는 각 문서의 토픽 분포와 각 토픽의 단어 분포를 도출하고 분석하여, 해당 문서가 어떤 주제들을 함께 다루고 있을지를 예측할 수 있다.


LDA 수행과정

 

  1. 문서 빈도수 기반의 표현 방법인, DTM 이나 TF-IDF 행렬을 입력으로 한다. (단어의 순서가 중요치 않음)
  2. 사용자가 LDA 에 하이퍼 파라미터인 토픽 개수(k) 를 전달한다.
  3. 문서에 사용할 토픽의 혼합을 확률 분포에 기반하여 결정한다.
  4. 랜덤으로 할당된 단어에 잘못 할당된 토픽을 각 단어들은 P(t|d) 와 P(w|t) 작업을 반복(역공학) 하며 토픽을 재할당한다.
     - 각 문서의 토픽 분포 P(t|d) : 해당 문서 d 에 나타난 토픽 t 의 비율을 보고 단어에 토픽 할당
     - 각 토픽의 단어 분포 P(w|t) : 는 전체 문서에서 단어 w 에 할당된 토픽 t 의 비율을 보고 단어에 토픽 할당

 

아래는 뉴스 기사의 제목을 모아놓은 약 100만개의 영어 데이터로부터 gensim 과 sklearn 을 사용하여 토픽을 추출하는 예제이다.

 

 

LDA with gensim

import gensim 
from gensim import corpora 

""" 전처리 결과가 아래와 같다고 할 때 
print(tokenized_doc[:5]) 
0    [well, sure, about, story, seem, biased, what,... 
1    [yeah, expect, people, read, actually, accept,... 
2    [although, realize, that, principle, your, str... 
3    [notwithstanding, legitimate, fuss, about, thi... 
4    [well, will, have, change, scoring, playoff, p... 
Name: clean_doc, dtype: object 
"""

# 각 단어에 정수 인코딩으로 (word_id, word_frequency) 의 형태로 변환 
dictionary = corpora.Dictionary(tokenized_doc) 
corpus = [dictionary.doc2bow(text) for text in tokenized_doc]  # [[(52, 1), (55, 1), ... , (88, 1), (89, 1)]] 

# LDA 토픽 모델링 (num_topics: 토픽 수, passes: 반복훈련횟수)
ldamodel = gensim.models.ldamodel.LdaModel(corpus, num_topics=20, id2word=dictionary, passes=15)
topics = ldamodel.print_topics(num_words=4) 
for topic in topics: 
    print(topic) 

""" 토픽 별 단어 분포 
(0, '0.015*"drive" + 0.014*"thanks" + 0.012*"card" + 0.012*"system"') 
(1, '0.009*"back" + 0.009*"like" + 0.009*"time" + 0.008*"went"') 
(2, '0.012*"colorado" + 0.010*"david" + 0.006*"decenso" + 0.005*"tyre"') 
(3, '0.020*"number" + 0.018*"wire" + 0.013*"bits" + 0.013*"filename"') 
(4, '0.038*"space" + 0.013*"nasa" + 0.011*"research" + 0.010*"medical"') 
(5, '0.014*"price" + 0.010*"sale" + 0.009*"good" + 0.008*"shipping"') 
(6, '0.012*"available" + 0.009*"file" + 0.009*"information" + 0.008*"version"') 
(7, '0.021*"would" + 0.013*"think" + 0.012*"people" + 0.011*"like"') 
(8, '0.035*"window" + 0.021*"display" + 0.017*"widget" + 0.013*"application"') 
(9, '0.012*"people" + 0.010*"jesus" + 0.007*"armenian" + 0.007*"israel"') 
(10, '0.008*"government" + 0.007*"system" + 0.006*"public" + 0.006*"encryption"') 
(11, '0.013*"germany" + 0.008*"sweden" + 0.008*"switzerland" + 0.007*"gaza"') 
(12, '0.020*"game" + 0.018*"team" + 0.015*"games" + 0.013*"play"') 
(13, '0.024*"apple" + 0.014*"water" + 0.013*"ground" + 0.011*"cable"') 
(14, '0.011*"evidence" + 0.010*"believe" + 0.010*"truth" + 0.010*"church"') 
(15, '0.016*"president" + 0.010*"states" + 0.007*"united" + 0.007*"year"') 
(16, '0.047*"file" + 0.035*"output" + 0.033*"entry" + 0.021*"program"') 
(17, '0.008*"dept" + 0.008*"devils" + 0.007*"caps" + 0.007*"john"') 
(18, '0.011*"year" + 0.009*"last" + 0.007*"first" + 0.006*"runs"') 
(19, '0.013*"outlets" + 0.013*"norton" + 0.012*"quantum" + 0.008*"neck"') 
""" 

# 문서 별 토픽 분포 
for i, topic_list in enumerate(ldamodel[corpus]): 
    if i==5:  # 상위 5개 
        break 
    print(i,'번째 문서의 topic 비율은',topic_list) 

""" 
0 번째 문서의 topic 비율은 [(7, 0.3050222), (9, 0.5070568), (11, 0.1319604), (18, 0.042834017)] 
1 번째 문서의 topic 비율은 [(0, 0.031606797), (7, 0.7529218), (13, 0.02924682), (14, 0.12861845), (17, 0.037851967)] 
2 번째 문서의 topic 비율은 [(7, 0.52241164), (9, 0.36602455), (16, 0.09760969)] 
3 번째 문서의 topic 비율은 [(1, 0.16926806), (5, 0.04912094), (6, 0.04034211), (7, 0.11710636), (10, 0.5854137), (15, 0.02776434)] 
4 번째 문서의 topic 비율은 [(7, 0.42152268), (12, 0.21917087), (17, 0.32781804)] 
""" 



LDA with sklearn

 

from sklearn.feature_extraction.text import TfidfVectorizer 
from sklearn.decomposition import LatentDirichletAllocation

""" 전처리 결과가 아래와 같다고 할 때 
print(tokenized_doc[:5]) 
0       decide community broadcast licence 
1       fire witness must aware defamation 
2    call infrastructure protection summit 
3                   staff aust strike rise 
4      strike affect australian travellers 
Name: headline_text, dtype: object 
""" 

# TF-IDF 행렬 만들기 
vectorizer = TfidfVectorizer(stop_words='english', max_features= 1000)  # 상위 1,000개의 단어로 제한 
X = vectorizer.fit_transform(tokenized_doc) 

# LDA 토픽 모델링 
lda_model = LatentDirichletAllocation(n_components=10, learning_method='online', random_state=777, max_iter=1) 
lda_top = lda_model.fit_transform(X) 

terms = vectorizer.get_feature_names() # 단어 집합 

def get_topics(components, feature_names, n=5): 
    for idx, topic in enumerate(components): 
        print("Topic %d:" % (idx+1), [(feature_names[i], topic[i].round(2)) for i in topic.argsort()[:-n - 1:-1]]) 
get_topics(lda_model.components_,terms) 

""" 토픽 별 단어 분포 
Topic 1: [('government', 8725.19), ('sydney', 8393.29), ('queensland', 7720.12), ('change', 5874.27), ('home', 5674.38)] 
Topic 2: [('australia', 13691.08), ('australian', 11088.95), ('melbourne', 7528.43), ('world', 6707.7), ('south', 6677.03)] 
Topic 3: [('death', 5935.06), ('interview', 5924.98), ('kill', 5851.6), ('jail', 4632.85), ('life', 4275.27)] 
Topic 4: [('house', 6113.49), ('2016', 5488.19), ('state', 4923.41), ('brisbane', 4857.21), ('tasmania', 4610.97)] 
Topic 5: [('court', 7542.74), ('attack', 6959.64), ('open', 5663.0), ('face', 5193.63), ('warn', 5115.01)] 
Topic 6: [('market', 5545.86), ('rural', 5502.89), ('plan', 4828.71), ('indigenous', 4223.4), ('power', 3968.26)] 
Topic 7: [('charge', 8428.8), ('election', 7561.63), ('adelaide', 6758.36), ('make', 5658.99), ('test', 5062.69)] 
Topic 8: [('police', 12092.44), ('crash', 5281.14), ('drug', 4290.87), ('beat', 3257.58), ('rise', 2934.92)] 
Topic 9: [('fund', 4693.03), ('labor', 4047.69), ('national', 4038.68), ('council', 4006.62), ('claim', 3604.75)] 
Topic 10: [('trump', 11966.41), ('perth', 6456.53), ('report', 5611.33), ('school', 5465.06), ('woman', 5456.76)] 
""" 

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret