Collapsed Gibbs Sampling For LDA

King LJ發表於2020-11-22

一、分佈介紹

1.多項分佈

重複進行n次獨立隨機試驗,每次實驗結果有 k k k種,第 i i i種結果出現的概率為 p i p_i pi,第 i i i種結果出現的次數為 n i n_i ni,則:
P ( X 1 = n 1 , X 2 = n 2 , . . . , X k = n k ) P(X_1=n_1,X_2=n_2,...,X_k=n_k) P(X1=n1,X2=n2,...,Xk=nk) = ( n n 1 ) p 1 n 1 ( n − n 1 n 2 ) p 2 n 2 . . . ( n − n 1 − n 2 − . . . − n k − 1 n k ) p k n k = n ! ∏ i = 1 n n i ! ∏ i = 1 k p i n i =\dbinom{n}{n_1}p_1^{n_1}\dbinom{n-n_1}{n_2}p_2^{n_2}...\dbinom{n-n_1-n_2-...-n_{k-1}}{n_k}p_k^{n_k}=\frac{n!}{\prod_{i=1}^{n}{n_i}!}\prod\limits_{i=1}^{k}p_i^{n_i} =(n1n)p1n1(n2nn1)p2n2...(nknn1n2...nk1)pknk=i=1nni!n!i=1kpini
記作 X ∼ M u l t i ( p ) X \thicksim Multi(p) XMulti(p),其中 X = ( X 1 , X 2 , … , X k ) X=(X_1,X_2,\dots,X_k) X=(X1,X2,,Xk) p = ( p 1 , p 2 , … , p k ) p=(p_1,p_2,\dots,p_k) p=(p1,p2,,pk) ∑ i = 1 k p i = 1 \displaystyle\sum_{i=1}^k p_i=1 i=1kpi=1 ∑ i = 1 k n i = n \displaystyle\sum_{i=1}^k n_i=n i=1kni=n

2. D i r i c h l e t Dirichlet Dirichlet分佈

f ( x ; α ) = Γ ( ∏ i = 1 k α i ) ∏ i = 1 k Γ ( α i ) ∏ i = 1 k x i α i − 1 f(x;\alpha)=\frac{\Gamma(\prod\limits_{i=1}^{k}\alpha_i)}{\prod\limits_{i=1}^{k}\Gamma(\alpha_i)}\prod\limits_{i=1}^{k}x_i^{\alpha_i-1} f(x;α)=i=1kΓ(αi)Γ(i=1kαi)i=1kxiαi1
記作 x ∼ D i r ( α ) x \thicksim Dir(\alpha) xDir(α),其中 x = ( x 1 , x 2 , … , x k ) x=(x_1,x_2,\dots,x_k) x=(x1,x2,,xk) ∑ i = 1 k x i = 1 \displaystyle\sum_{i=1}^kx_i=1 i=1kxi=1 α = ( α 1 , α 2 , … , α k ) \alpha=(\alpha_1,\alpha_2,\dots,\alpha_k) α=(α1,α2,,αk)
分佈聯絡

二、模型介紹

1.LDA模型(Latent Dirichlet Allocation)

(1)模型定義(三個集合)

  • 單詞集合: W = { w 1 , w 2 , … , w V } W=\{w_1,w_2,\dots,w_V\} W={w1,w2,,wV},其中 V V V是單詞個數
  • 文字集合: D = { d 1 , d 2 , … , d M } D=\{d_1,d_2,\dots,d_M\} D={d1,d2,,dM},其中 M M M是文字個數, d m = ( w m 1 , w m 2 , … , w m n , … , w m N m ) d_m=(w_{m1},w_{m2},\dots,w_{mn},\dots,w_{mN_m}) dm=(wm1,wm2,,wmn,,wmNm),其中 w m n w_{mn} wmn代表第m個文字的第n個單詞, N m N_{m} Nm代表文字m的單詞數
  • 主題集合: Z = { z 1 , z 2 , … , z K } Z=\{z_1,z_2,\dots,z_K\} Z={z1,z2,,zK},其中 K K K是主題個數(隱變數)

(2)LDA生成過程

在LDA生成過程中,作出了4個假設:

  • 文字-主題分佈服從多項分佈,即 p ( z ∣ d m ) ∼ M u l t i ( θ m ) p(z|d_m) \thicksim Multi(\theta_m) p(zdm)Multi(θm),其中 z = ( z 1 , z 2 , … , z K ) z=(z_1,z_2,\dots,z_K) z=(z1,z2,,zK)表示各主題在文字 d m d_m dm中出現的次數, θ m = ( θ 1 , θ 2 , … , θ K ) \theta_m=(\theta_1,\theta_2,\dots,\theta_K) θm=(θ1,θ2,,θK)表示各主題在文字 d m d_m dm中出現的概率
  • 主題-單詞分佈服從多項分佈,即 p ( w ∣ z k ) ∼ M u l t i ( φ k ) p(w|z_k) \thicksim Multi(\varphi_k) p(wzk)Multi(φk),其中 w = ( w 1 , w 2 , … , w V ) w=(w_1,w_2,\dots,w_V) w=(w1,w2,,wV)表示各單詞在主題 z k z_k zk中出現的次數, φ k = ( φ 1 , φ 2 , … , φ V ) \varphi_k=(\varphi_1,\varphi_2,\dots,\varphi_V) φk=(φ1,φ2,,φV)表示各單詞在主題 z k z_k zk中出現的概率
  • θ m \theta_m θm的先驗分佈為 D i r ( α ) Dir(\alpha) Dir(α),即 θ m ∼ D i r ( α ) \theta_m \thicksim Dir(\alpha) θmDir(α),其中 θ m \theta_m θm是一個 K K K維向量,即 θ m = ( θ 1 , θ 2 , … , θ K ) \theta_m=(\theta_1,\theta_2,\dots,\theta_K) θm=(θ1,θ2,,θK),而 α = ( α 1 , α 2 , … , α K ) \alpha=(\alpha_1,\alpha_2,\dots,\alpha_K) α=(α1,α2,,αK)為先驗分佈的超引數
  • φ k \varphi_k φk的先驗分佈為 D i r ( β ) Dir(\beta) Dir(β),即 φ k ∼ D i r ( β ) \varphi_k \thicksim Dir(\beta) φkDir(β),其中 φ k \varphi_k φk是一個 V V V維向量,即 φ k = ( φ 1 , φ 2 , … , φ V ) \varphi_k=(\varphi_1,\varphi_2,\dots,\varphi_V) φk=(φ1,φ2,,φV),而 β = ( β 1 , β 2 , … , β V ) \beta=(\beta_1,\beta_2,\dots,\beta_V) β=(β1,β2,,βV)為先驗分佈的超引數
    #LDA示意圖#
    LDA示意圖
    其中 w w w已知, z , θ , φ z,\theta,\varphi z,θ,φ未知,而超引數 α , β \alpha,\beta α,β事先給定。LDA模型的生成過程如下:
  • 按照概率 p ( d m ) p(d_m) p(dm)選擇一個文字 d m d_m dm
  • 從先驗分佈 D i r ( α ) Dir(\alpha) Dir(α)中抽樣生成文字 d m d_m dm的主題分佈 θ m \theta_m θm
  • 從主題分佈 θ m \theta_m θm中抽樣生成文件 d m d_m dm n n n個單詞的主題 z m , n z_{m,n} zm,n
  • 從先驗分佈 D i r ( β ) Dir(\beta) Dir(β)中抽樣生成主題 z m , n z_{m,n} zm,n的單詞分佈 φ k \varphi_k φk
  • 從單詞分佈 φ k \varphi_k φk中抽樣生成單詞 w m , n w_{m,n} wm,n

(3)LDA模型目標

估計引數矩陣 Θ \varTheta Θ Φ \varPhi Φ,其中 Θ \varTheta Θ M ∗ K M*K MK維矩陣, Φ \varPhi Φ K ∗ V K*V KV維矩陣
Θ = [ θ 1 θ 2 ⋮ θ M ] = [ θ 11 θ 12 … θ 1 K θ 21 θ 22 … θ 2 K ⋮ ⋮ ⋮ ⋮ θ M 1 θ M 2 … θ M K ] M ∗ K \varTheta=\begin{bmatrix} \theta_1 \\ \theta_2 \\ \vdots \\ \theta_M \end{bmatrix}=\begin{bmatrix} \theta_{11} & \theta_{12} & \dots & \theta_{1K} \\ \theta_{21} & \theta_{22} & \dots & \theta_{2K} \\ \vdots & \vdots & \vdots & \vdots\\ \theta_{M1} & \theta_{M2} & \dots & \theta_{MK} \end{bmatrix}_{M*K} Θ=θ1θ2θM=θ11θ21θM1θ12θ22θM2θ1Kθ2KθMKMK Φ = [ φ 1 φ 2 ⋮ φ K ] = [ φ 11 φ 12 … φ 1 V φ 21 φ 22 … φ 2 V ⋮ ⋮ ⋮ ⋮ φ K 1 φ K 2 … φ K V ] K ∗ V \varPhi=\begin{bmatrix} \varphi_1 \\ \varphi_2 \\ \vdots \\ \varphi_K\end{bmatrix}=\begin{bmatrix} \varphi_{11} & \varphi_{12} & \dots & \varphi_{1V} \\ \varphi_{21} & \varphi_{22} & \dots & \varphi_{2V} \\ \vdots & \vdots & \vdots & \vdots\\ \varphi_{K1} & \varphi_{K2} & \dots & \varphi_{KV} \end{bmatrix}_{K*V} Φ=φ1φ2φK=φ11φ21φK1φ12φ22φK2φ1Vφ2VφKVKV
且對 ∀ i , ∑ k = 1 K θ i k = 1 \forall i,\displaystyle\sum_{k=1}^K\theta_{ik}=1 i,k=1Kθik=1 ∀ j , ∑ v = 1 V φ j v = 1 \forall j,\displaystyle\sum_{v=1}^V \varphi_{jv}=1 j,v=1Vφjv=1

2.TTM模型(Tensor Topic Model )

三、Collapsed Gibbs Sampling For LDA

1.Gibbs Sampling

(1)原理

Gibbs抽樣是馬爾可夫鏈蒙特卡洛理論(MCMC)的一個特例,適用於聯合分佈未知而(全)條件分佈已知的高維變數(2維及以上)抽樣,它交替選擇某一維度,通過其他維度的值來抽樣該維度的值。
基本過程為:隨機初始化一個 n n n維向量,依次選擇第 i ( i = 1 , 2 , . . . , n ) i(i=1,2,...,n) i(i=1,2,...,n)個變數,通過固定剩餘 n − 1 n-1 n1個變數,抽取第 i i i個變數的新值(由於條件分佈已知),其樣本序列構成了馬爾可夫鏈,利用該馬爾可夫鏈的靜態分佈計算聯合分佈

(2)演算法

Gibbs演算法

(3)example(二元正態的Gibbs抽樣)

Gibbs抽樣案例1
Gibbs抽樣案例2

###Gibbs Sampling
N <- 5000 #length of chain
burn <- 1000 #burn-in length
X <- matrix(0,N,2) #the chain
rho <- -0.75 #correlation
mu1 <- 0;mu2 <- 2
sigma1 <- 1;sigma2 <- 0.5
s1 <- sqrt(1-rho^2)*sigma1;s2 <- sqrt(1-rho^2)*sigma2
###generate the chain
X[1,] <- c(mu1,mu2) #initialize
for(i in 2:N){
	x2 <- X[i-1,2]
	m1 <- mu1+rho*(x2-mu2)*sigma1/sigma2
	X[i,1] <- rnorm(1,m1,s1)
	x1 <- X[i,1]
	m2 <- mu2+rho*(x1-mu1)*sigma2/sigma1
	X[i,2] <- rnorm(1,m2,s2)
}
b <- burn+1
x <- X[b:N,]

2.Collapsed Gibbs Sampling For LDA

LDA並沒有在三個引數( z , θ , φ z,\theta,\varphi z,θ,φ)中都進行抽樣,因為只要得到了 z z z,就可以直接根據 z z z得到 θ \theta θ φ \varphi φ。因此在LDA模型中使用了收縮的吉布斯抽樣(Collapsed Gibbs Sampling),即僅對 z z z進行了Gibbs抽樣。

(1)條件分佈推導 p ( z m v ∣ − z m v , w , α , β ) p(z_{mv}|-z_{mv},w,\alpha,\beta) p(zmvzmv,w,α,β)

在這裡插入圖片描述
在這裡插入圖片描述

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

(2)估計未知引數矩陣 Θ \varTheta Θ Φ \varPhi Φ

在這裡插入圖片描述
在這裡插入圖片描述

四、Python實現

1.LDA實現

將LDA的Gibbs取樣演算法流程總結如下:

  • 選擇合適的主題數 K K K,選擇合適的超引數向量 α , β \alpha,\beta α,β
  • 對應語料庫中每一篇文件的每一個單詞,隨機的賦予一個主題編號 z z z
  • 重新掃描語料庫,對於每一個單詞,利用Gibbs取樣公式更新它的主題編號,並更新語料中該單詞的編號
  • 重複取樣過程直至收斂
  • 統計語料庫中的各個文件各個單詞的主題數,得到文件-主題分佈 Θ \varTheta Θ;統計語料庫中各個主題的哥哥單詞數,得到主題-單詞分佈 Φ \varPhi Φ
import numpy as np


class LDAGibbs(object):
    def __init__(self, K=3):
        """
        利用collapsed gibbs sampling方法實現LDA
        :param K: 主題個數,預設值為3
        :param V: 總共單詞個數
        :param M: 文字個數
        :param tockens: 單詞tockens
        :param alpha: theta的超引數,維度(K,)
        :param theta: 文字主題矩陣,服從狄利克雷分佈,維度(M,K)
        :param beta: varphi的超引數,維度(V,)
        :param varphi: 單詞主題矩陣,服從狄利克雷分佈,維度(K,V)
        :param z: 話題集合,維度(M,Nm),Nm表示第m個文字的單詞個數
        """
        self.K = K

        self.V = None
        self.M = None
        self.tokens = None

        self.params = {
            'alpha': None,
            'theta': None,
            'beta': None,
            'varphi': None,
            'z': None
        }

    def _init_params(self, texts):
        """
        初始化引數,除了上面定義的引數,公式中統計的引數也在這兒初始化
        :param texts: 輸入文字
        :return:
        """
        alpha = np.ones(self.K)
        # 文件主題引數先佔位,最後再進行更新
        theta = np.ones((self.M ,self.K))
        beta = np.ones(self.V)
        # 單詞主題引數也先佔位,最後再進行更新
        varphi = np.ones((self.K, self.V))
        z = []
        for m in range(self.M):
            N = len(texts[m])
            z.append(np.zeros(N))

        # 代表第k個主題的第v個單詞個數
        self.n_kv = np.zeros((self.K, self.V))
        # 代表第k個主題有多少個單詞
        self.n_k = np.zeros(self.K)
        # 代表第m個文件第k個主題的單詞個數
        self.n_mk = np.zeros((self.M, self.K))
        # 每一個元素代表第m個文件有多少個單詞
        self.n_m = np.zeros(self.M)

        z = np.array(z)

        # 初始化中做一次統計,且對z隨機初始化
        for m in range(self.M):
            N = len(texts[m])
            for v in range(N):
                rand_topic = int(np.random.randint(0, self.K))
                z[m][v] = rand_topic
                self.n_kv[rand_topic][int(texts[m][v])] += 1
                self.n_k[rand_topic] += 1
                self.n_mk[m][rand_topic] += 1
            self.n_m[m] = N

        self.params = {
            'alpha': alpha,
            'theta': theta,
            'beta': beta,
            'varphi': varphi,
            'z': z
        }


    def _sample_topic(self, m, v, texts):
        """
        計算z_mv對應主題的概率值,並通過概率值進行多項分佈取樣,得到當前的主題
        math:
        p(z):
        $$p(z_{mv} | z_{-mv}, w , alpha, beta) =  frac {n_{kv} +beta_v -1} {sum_{v=1}^V (n_{kv} + beta_v) -1} .
        frac {n_{mk} + alpha_k - 1} {sum_{k=1}^K (n_{mk} + alpha_k) - 1}$$

        :param m: 表示呼叫的時候,上層函式計算的是第m個文件
        :param v: 第v個單詞
        :param texts: 語料
        :return:
        """
        # 首先是排除當前的單詞z[m][v]
        old_topic = int(self.params['z'][m][v])
        self.n_kv[old_topic][int(texts[m][v])] -= 1
        self.n_k[old_topic] -= 1
        self.n_mk[m][old_topic] -= 1
        self.n_m[m] -= 1

        # 依次計算該單詞的p(z_mv=k | *)
        p = np.zeros(self.K)
        for k in range(self.K):
            p[k] = (self.n_mk[m][k] + self.params['alpha'][k]) / (self.n_k[k] + np.sum(self.params['beta'])) * \
                   (self.n_kv[k][int(texts[m][v])] + self.params['beta'][int(texts[m][v])]) \
                   / (self.n_k[k] + np.sum(self.params['beta']))

        # 對概率進行歸一化處理
        p = p / np.sum(p)

        # 抽樣新的主題
        new_topic = np.argmax(np.random.multinomial(1, p))

        # 更新統計值
        self.n_kv[new_topic][int(texts[m][v])] += 1
        self.n_k[new_topic] += 1
        self.n_mk[m][new_topic] += 1
        self.n_m[m] += 1

        return new_topic


    def collapse_gibbs_sampling(self, texts, max_iter):
        """
        吉布斯取樣的迴圈入口
        :param texts: 語料
        :param max_iter: 最大迴圈次數
        :return:
        """
        for iter in range(max_iter):
            print('iter: ', iter + 1)
            for m in range(self.M):
                N = len(texts[m])
                for v in range(N):
                    topic = self._sample_topic(m, v, texts)
                    self.params['z'][m][v] = topic

    def _update_params(self):
        """
        更新引數
        math:
        theta:
        $$theta_{mk} = frac {n_{mk} + alpha_k - 1} {sum_{k=1}^K (n_{mk} + alpha_k) - 1}$$
        varphi:
        $$varphi_{kv} = frac {n_{kv} + beta_v -1} {sum_{v=1}^V (n_{kv} + beta_v) -1} $$

        但是一般都沒有減一,是因為在統計過程中,提前減一了
        :return:
        """
        # 依據統計值,更新單詞主題矩陣和文件主題矩陣
        for k in range(self.K):
            for v in range(self.V):
                self.params['varphi'][k][v] = \
                    (self.n_kv[k][v] + self.params['beta'][v]) / \
                    (self.n_k[k] + np.sum(self.params['beta']))

        for m in range(self.M):
            for k in range(self.K):
                self.params['theta'][m][k] = \
                    (self.n_mk[m][k] + self.params['alpha'][k]) / \
                    (self.n_m[m] + np.sum(self.params['alpha']))


    def fit(self, texts, tokens, max_iter=10):
        """
        模型入口
        :param texts: 語料
        :param tokens: 單詞tokens
        :param max_iter: 最大迭代次數
        :return:
        """
        self.M = len(texts)
        self.V = len(tokens)
        self.tokens = tokens

        # 初始化引數
        self._init_params(texts)

        # 執行程式,統計迭代統計值
        self.collapse_gibbs_sampling(texts, max_iter)

        # 由於統計值一直迭代,最後賦值到模型引數中即可
        self._update_params()

2.TTM實現

相關文章