[R] 6. Topic modeling

반응형
## [1] "ko_KR.UTF-8"

6. Topic modeling

  • 토픽 모델링은 클러스터링처럼 텍스트 데이터를 대상으로하는 비지도학습 분류 방법입니다.
  • 여러 토픽 모델들이 있는데 그 중 널리 사용되는 LDA(Latent Dirichlet Allocation)에 대해서 살펴보겠습니다.
  • 사전에 필요한 라이브러리는 topicmodels 라이브러리로 LDA 객체를 다루는 방법에 대해 소개하겠습니다.
library(topicmodels)



6. 1. Latent Dirichlet Allocation

  • LDA는 토픽 모델링을 위한 가장 일반적인 알고리즘 중 하나입니다.
  • 해당 포스팅에서는 모델의 수학적인 전개는 생략하고 아래 두 가지 원칙에 대해서만 정리하겠습니다.
    • 모든 문서는 토픽이 혼합되어 있다.
      • 각 문서가 특정 비율로 여러 토픽의 단어를 포함할 수 있다고 가정
      • 예를 들어, 문서 1은 토픽A 90%, 토픽B 10%이고 문서 2는 토픽A 30%, 토픽B 70%
    • 모든 토픽은 단어가 혼합되어 있다.
      • 예를 들어 하나의 토픽은 “정치”이고 또 다른 하나는 “엔터테인먼트”라고 가정했을 때
      • “정치” 토픽에서 많이 사용되는 단어는 “대통령”, “국회”, “정부” 일 수 있으며
        “엔터테인먼트” 토픽에서는 “영화”, “TV”, “배우” 등이 될 수도 있다.
      • 중요한 것은 토픽 간에 단어를 공유할 수 있다는 점
      • “예산”과 같은 단어는 두 토픽에 동등하게 나타날 수 있다.
    • LDA는 위 두 가지 가정을 기반으로 추정하는 수학적인 방법입니다.
      • 지도학습에서의 선형판별분석 LDA(Linear Discriminant Analysis)와 약자가 동일하니 해석 때 주의하셔야 합니다.
    • 각 토픽과 관련된 단어의 조합을 찾는 동시에 각 문서를 설명하는 토픽의 조합을 결정합니다.

 

  • 예시를 위해 DocumentTermMatrix 객체인 AssociatedPress 데이터를 사용하겠습니다.
    • 1988년쯤 발행된 미국 통신사의 2,246개 뉴스 기사 모음
data("AssociatedPress")

AssociatedPress
## <<DocumentTermMatrix (documents: 2246, terms: 10473)>>
## Non-/sparse entries: 302031/23220327
## Sparsity           : 99%
## Maximal term length: 18
## Weighting          : term frequency (tf)
  • 적용하는 함수는 topicmodels 라이브러리 함수인 LDA() 입니다.
# k는 분류하고자 하는 토픽의 갯수
ap_lda <- LDA(AssociatedPress, k = 2, control = list(seed = 20210720))
ap_lda
## A LDA_VEM topic model with 2 topics.



6. 1. 1. Word-topic probabilities

  • 모델에서 \(\beta\) 라고 불리우는 단어-토픽 확률을 구하기 위해서 tidy() 함수를 적용합니다.
ap_topics <- ap_lda %>% 
  tidy(matrix = "beta")

ap_topics
## # A tibble: 20,946 x 3
##    topic term           beta
##    <int> <chr>         <dbl>
##  1     1 aaron      3.31e- 5
##  2     2 aaron      6.71e- 6
##  3     1 abandon    3.23e- 5
##  4     2 abandon    3.77e- 5
##  5     1 abandoned  1.11e- 4
##  6     2 abandoned  6.05e- 5
##  7     1 abandoning 2.24e- 5
##  8     2 abandoning 3.16e-16
##  9     1 abbott     9.99e- 7
## 10     2 abbott     4.61e- 5
## # … with 20,936 more rows
  • 그 결과 모델이 토픽(topic) 당 단어(term) 당 하나의 확률(beta)값을 갖는 데이터 형태가 되었습니다.
  • 해석을 하자면, 해당 토픽에서 해당 단어가 나올 확률은 beta가 되는 것 입니다.
    • 예를 들어 “aaron” 이라는 단어는 토픽1에서 생성될 확률과 토픽2에서의 확률이 서로 다릅니다.

 

  • slice_max() 함수를 사용하여 각 토픽에서 일반적인 단어를 캐치할 수 있습니다.
ap_top10_terms <- ap_topics %>% 
  group_by(topic) %>% 
  slice_max(beta, n = 10) %>% 
  ungroup() %>% 
  arrange(topic, desc(beta))

ap_top10_terms
## # A tibble: 20 x 3
##    topic term          beta
##    <int> <chr>        <dbl>
##  1     1 i          0.00716
##  2     1 people     0.00504
##  3     1 two        0.00445
##  4     1 president  0.00426
##  5     1 police     0.00404
##  6     1 government 0.00402
##  7     1 soviet     0.00364
##  8     1 bush       0.00343
##  9     1 new        0.00338
## 10     1 years      0.00323
## 11     2 percent    0.0108 
## 12     2 million    0.00767
## 13     2 new        0.00661
## 14     2 year       0.00647
## 15     2 billion    0.00507
## 16     2 last       0.00404
## 17     2 company    0.00369
## 18     2 market     0.00358
## 19     2 federal    0.00345
## 20     2 years      0.00284
ap_top10_terms %>% 
  ggplot(aes(x = reorder_within(term, beta, within = topic), y = beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip() +
  scale_x_reordered()

  • 위 결과를 통해 두 가지 주제에 대해서 대략적인 감을 찾아볼 수 있습니다.
  • 토픽1은 “people”, “president”, “police”, “government” 등 정치와 관련된 뉴스 기사임을 대략적으로 알 수 있으며
    토픽2는 “percent”, “million”, “company”등 경제 관련 뉴스 기사임을 확인할 수 있습니다.
  • 또한 공통적으로 “years”와 같이 하나의 단어가 두 개의 토픽(클러스터)에서 높은 확률 값을 나타내고 있습니다.
  • 이는 기존에 우리가 익숙히 알고 있는 하드한 클러스터링과 다르게 소프트하게 클러스터링을 해볼 수 있다는 장점도 있다는 점을 알려줍니다.

 

  • 다음으로는 두 토픽 간의 가장 큰 차이를 보이는 항을 고려할 수 있습니다.
  • 이 차이를 계산하는 방법은 A 대비 B의 방식과 같이 로그비(log-ratio)를 고려합니다.
ap_topics_wider <- ap_topics %>% 
  mutate(topic = paste0("topic", topic)) %>% 
  pivot_wider(
    names_from = "topic",
    values_from = beta,
    values_fill = 0
  ) %>% 
  filter(topic1 > 0.001 | topic2 > 0.001) %>% 
  mutate(log_ratio = log2(topic2/topic1))

ap_topics_wider
## # A tibble: 209 x 4
##    term             topic1    topic2 log_ratio
##    <chr>             <dbl>     <dbl>     <dbl>
##  1 administration 1.13e- 3 0.000767     -0.562
##  2 agreement      6.29e- 4 0.00130       1.05 
##  3 aid            1.01e- 3 0.0000431    -4.54 
##  4 air            8.71e- 4 0.00134       0.628
##  5 american       1.67e- 3 0.00207       0.309
##  6 analysts       2.37e- 6 0.00116       8.94 
##  7 army           1.17e- 3 0.0000110    -6.74 
##  8 asked          1.41e- 3 0.000327     -2.11 
##  9 authorities    1.11e- 3 0.000138     -3.01 
## 10 average        7.39e-12 0.00144      27.5  
## # … with 199 more rows
ap_topics_wider %>% 
  group_by(group = ifelse(log_ratio >= 0, "+", "-")) %>% 
  slice_max(abs(log_ratio), n = 10) %>% 
  ungroup() %>% 
  ggplot(aes(x = reorder(term, log_ratio), y = log_ratio)) +
  geom_bar(stat = "identity", width = 0.8) +
  coord_flip() +
  labs(x = NULL, y = "Log2 ratio of beta in topic2 / topic1")

  • 위 결과를 통해 확인해볼 수 있는 것은 토픽2는 “stock”, “dollar” 등의 단어와 같이 상대적으로 경제 관련 기사임을 확인해볼 수 있습니다.



6. 1. 2. Document-topic probabilities

  • 모델에서 \(\gamma\) 라고 불리우는 문서-토픽 확률을 구하기 위해서 마찬가지로 tidy() 함수를 적용합니다.
ap_documents <- ap_lda %>% 
  tidy(matrix = "gamma")

ap_documents
## # A tibble: 4,492 x 3
##    document topic  gamma
##       <int> <int>  <dbl>
##  1        1     1 0.999 
##  2        2     1 0.612 
##  3        3     1 0.959 
##  4        4     1 0.797 
##  5        5     1 0.997 
##  6        6     1 1.00  
##  7        7     1 0.193 
##  8        8     1 0.997 
##  9        9     1 0.0261
## 10       10     1 0.926 
## # … with 4,482 more rows
  • 그 결과 모델이 문서(document) 당 토픽(topic) 당 하나의 확률(gamma)값을 갖는 데이터 형태가 되었습니다.
  • 해석을 하자면, 해당 문서에서 해당 토픽에 대한 단어의 추청 확률이 gamma가 되는 것 입니다.
    • 예를 들어 문서2에 있는 단어의 약 61%만이 토픽1에서 생성된 것으로 추정합니다. (두번째 줄)
  • 위 결과로 보면 문서6은 토픽1에 있는 단어의 거의 100%를 가져왔습니다.
  • 따라서 해당 문서에서 가장 빈도가 높은 단어들이 어떤 것인지 확인해볼 수 있습니다.
AssociatedPress %>% 
  tidy() %>% 
  filter(document == 6) %>% 
  arrange(desc(count))
## # A tibble: 287 x 3
##    document term           count
##       <int> <chr>          <dbl>
##  1        6 noriega           16
##  2        6 panama            12
##  3        6 jackson            6
##  4        6 powell             6
##  5        6 administration     5
##  6        6 economic           5
##  7        6 general            5
##  8        6 i                  5
##  9        6 panamanian         5
## 10        6 american           4
## # … with 277 more rows



6. 2. Example: the great library heist

  • 해당 예시를 똑같이 따라해보려고 했는데… 이상하게 파일을 불러올 수가 없네요 ㅠㅠ
  • 원인을 확인해보고 확인되는대로 다시 재업로드 하겠습니다.
  • 꼭 아래 링크를 통해서 한번쯤은 따라해보고 공부해보시길 권장드립니다. 꼭!
  • 필요하신 분들은 여기를 참고해주시면 되어요!
반응형
TAGS.

Comments