霞と側杖を食らう

ほしいものです。なにかいただけるとしあわせです。[https://www.amazon.jp/hz/wishlist/ls/2EIEFTV4IKSIJ?ref_=wl_share]

トピックモデル(ユニグラムモデル)の学習記録

 

【学習動機】

以前、テキストマイニングをRで使って遊んでみたとき, トピックモデルが出てきたが、理論を全く知らないのも悲しいので、岩田『トピックモデル』

www.amazon.co.jp

で学ぶことにした。まずはユニグラムモデルについて学ぶ。

【学習内容】

ユニグラムモデル(unigram model)はBOW(bag of words)表現された文書集合の最も簡単な生成モデル。このモデルの理論を学んだので、Rで人工データを作成し、最尤推定とMAP推定でパラメータの推定を行う。

ライブラリの読み込みと表示桁数の指定。

library(tidyverse)
library(ggplot2)
options(digits=3)
set.seed(536329)

単語分布の設定をする。語彙の種類とそれに付与される確率を決定。

df_word_dist <- data.frame(
  words = c("あじ","いくら","うに","えんがわ","かれい","こはだ","さわら","さんま","しゃこ","すずき"),
  prob = c(0.2,0.1,0.075,0.075,0.05,0.075,0.1,0.2,0.075,0.05)
)
numV <- nrow(df_word_dist)    #単語分布のボキャブラリーの種類(number of Vocabulary)
allword <- df_word_dist$words %>% as.vector()   #全単語(あとでファクタ型にしたときの並べ替えで使う)

文書数と各文書の単語数を指定する。

numD <- 12    #文書の数(number of Documents)
vnumW <- c(20,70,10,80,20,80,10,80,20,80,40,60)    #各文書に含まれる単語数(number of words)のベクトル
numW <- sum(vnumW)    #単語の総数

シミュレーションデータ作成関数が以下のようになる。引数には文書の数と各文書の単語数ベクトル、単語分布のデータフレームをとる。

sim_unigram <- function(numD, vnumW, df_word_dist){
  list_word <- vector("list", numD)
  for(d in 1:numD){
    list_word[[d]] <- sample(df_word_dist$words, vnumW[d], replace=TRUE, prob = df_word_dist$prob)
  }
  return(list_word)
}

これにより人工データを作成し、生成された文書1の単語を見てみる。

DW <- sim_unigram(numD=numD, vnumW = vnumW, df_word_dist = df_word_dist)
DW[[1]]
##  [1] あじ     こはだ   さんま   すずき   えんがわ さんま   うに    
##  [8] さわら   さんま   かれい   すずき   しゃこ   さんま   あじ    
## [15] さわら   あじ     さんま   すずき   あじ     さんま  
## 10 Levels: あじ いくら うに えんがわ かれい こはだ さわら ... すずき

各語彙の頻度テーブルを作成し、生成された文書1のテーブルを見てみる。

DWtab <- DW %>% purrr::map(factor,levels = allword) %>% purrr::map(table)
DWtab[[1]]
## 
##     あじ   いくら     うに えんがわ   かれい   こはだ   さわら   さんま 
##        4        0        1        1        1        1        2        6 
##   しゃこ   すずき 
##        1        3

ユニグラムモデルでは文書によらず単語分布が一緒なので、語彙の頻度テーブルを統一してしまって問題ない。

allWtab <- DW %>% unlist() %>% factor(levels = allword) %>% table()

最尤推定(phiv = Nv/N)を行う。

MLphi <- allWtab / numW
MLphi
## .
##     あじ   いくら     うに えんがわ   かれい   こはだ   さわら   さんま 
##   0.2035   0.1035   0.0842   0.0930   0.0526   0.0579   0.0860   0.1930 
##   しゃこ   すずき 
##   0.0807   0.0456

事前分布にディリクレ分布を設定し、パラメータを(beta,beta,beta,…,beta)として、MAP推定(phiv = (Nv+beta-1)/(N+(beta-1)V) beta:paramofDirichlet)を行う。

beta <- 2    #観測がない単語の推定値を負にしないためにはbetaが1以上とする必要がある
MAPphi <- (allWtab + beta - 1) / (numW + (beta - 1)*numV)
MAPphi
## .
##     あじ   いくら     うに えんがわ   かれい   こはだ   さわら   さんま 
##   0.2017   0.1034   0.0845   0.0931   0.0534   0.0586   0.0862   0.1914 
##   しゃこ   すずき 
##   0.0810   0.0466

ちなみに、ベイズ推定では事前分布にディリクレ分布として、パラメータを(beta,beta,beta,…,beta)とすると、事後分布はDirichlet(phi|beta+N1,beta+N2,…,beta+Nv)が得られるが、ここでは求めない。

MAP推定と最尤推定(ML)と真のパラメータを棒グラフにして並べる。赤がMAP推定値、緑が最尤推定値、青が真の値を示している。

df_est <- data.frame(
    voc = df_word_dist$words,
    phi = df_word_dist$prob,
    MLphi = MLphi %>% as.vector(),
    MAPphi = MAPphi %>% as.vector()
)
df_est_wide <- gather(df_est, key = "estimator",value,-voc)
df_est_wide$voc <- df_est_wide$voc %>% factor(levels = df_word_dist$words)

ggplot(df_est_wide, aes(x=estimator, y=value, fill=estimator)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~voc)

サンプルサイズを10%にすると当然ながら推定精度が落ちる。

set.seed(536329)
numD <- 12    #文書の数(number of Documents)
vnumW <- c(20,70,10,80,20,80,10,80,20,80,40,60)*0.1   #各文書に含まれる単語数(number of words)のベクトル

【学習予定】

次は混合ユニグラムモデルを学ぶ。

moratoriamuo.hatenablog.com