浅谈LDA

  原创内容,转载注明出处

 

  LDA是导师10月初布置的内容,每次拿起来《LDA数学八卦》看前面的公式推导都觉得这是个很难的问题,一直拖到10月末。这周末用了两天时间终于把LDA弄懂了,其实LDA是一个很简单的模型,不要被前面的数学公式吓到。当然,作为一个初学者,如果有什么理解不对的,欢迎大家批评指正。

  和《LDA数学八卦》不同,我想先从这个模型说起。

  现在我有M篇文章,这些文章由V个单词组成。每个单词可能属于不同的主题,主题总个数是K(我们并不知道每个单词是什么主题)。现在要根据现有的语料得出一篇新文章的主题分布,这篇文章也是由V个单词中的某些组成的。

  根据机器学习的思路,我们要先得到一个模型,通过现有的语料确定模型中的参数,然后通过模型生成新的主题分布。

  这是一个非监督学习,估计模型中参数的方法是马尔科夫链的平稳分布。马尔科夫链的概率只和它的前一个状态有关,不管初始状态如何,乘上一个转移矩阵,若干步后都会收敛到一个状态,称这个状态为马氏链的平稳分布。如果能让这个分布收敛到p(x),那么当马氏链收敛之后得到的转移序列即为分布p(x)的样本。这就是著名的MCMC。Gibbs Sampling是对MCMC方法的优化,将MCMC中的接受概率α变为1,将状态转移变为延坐标轴的转移,在k维坐标轴中沿着k个坐标轴轮换采样,收敛后得到的样本即为p(x1,x2,...,xn)的样本。

  LDA用的就是这种Gibbs Sampling采样方法,首先将每篇文章的主题分布和每个主题的词汇分布设置成随机值,按照上述方法不断采样,直到收敛后,每个主题的词汇分布就可以用来生成新的文章的主题分布了!(主题的词汇分布是公用的) 有了模型参数后,将新的文章的主题分布设置成随机的初值,这一次主题的词汇分布固定,按照上述方法不断采样,Gibbs Sampling收敛后,得到的就是新的主题分布。

  以上就是LDA的全部内容!当然可能还是很迷惑,到底是如何采样的。这就要说到Gamma函数,Beta分布,Dirichlet分布,二项分布,多项分布,以及他们的共轭分布。

  生成文章中的主题分布和主题中的词汇分布都是dirichlet分布,有了主题分布,从主题分布中取样生成第j个词语的主题和从词语分布中采样生成最终的词语都是多项式分布。后面的是多项式分布是显而易见的,至于前面为什么是dirchlet分布,就是因为他们俩是共轭分布。。。

  知道这些有什么用呢?就是为了推导出最后参数的公式,这里就不列出推到过程了,最终参数的采样在代码中体现为

   for (int k = 0; k < K; k++) {
            for (int t = 0; t < V; t++) {
                phi[k][t] = (nkt[k][t] + beta) / (nktSum[k] + V * beta);
            }
        }

        for (int m = 0; m < M; m++) {
            for (int k = 0; k < K; k++) {
                theta[m][k] = (nmk[m][k] + alpha) / (nmkSum[m] + K * alpha);
            }
        }

 其中phi是主题的词汇分布,theta是文档的主题分布。nmk是文章m中主题为k的单词总个数,nkt是主题k中单词t的总个数。nmksum是文章m中所有主题单词总个数,nktsum是主题k中所有单词总个数。每个单词的主题每次更新为

 private int sampleTopicZ(int m, int n) {
        // TODO Auto-generated method stub
        // Sample from p(z_i|z_-i, w) using Gibbs upde rule

        // Remove topic label for w_{m,n}
        int oldTopic = z[m][n];
        nmk[m][oldTopic]--;
        nkt[oldTopic][doc[m][n]]--;
        nmkSum[m]--;
        nktSum[oldTopic]--;

        // Compute p(z_i = k|z_-i, w)
        double[] p = new double[K];
        for (int k = 0; k < K; k++) {
            p[k] = (nkt[k][doc[m][n]] + beta) / (nktSum[k] + V * beta)* (nmk[m][k] + alpha) / (nmkSum[m] + K * alpha);
        }

        // Sample a new topic label for w_{m, n} like roulette
        // Compute cumulated probability for p
        for (int k = 1; k < K; k++) {
            p[k] += p[k - 1];
        }
        double u = Math.random() * p[K - 1]; // p[] is unnormalised
        int newTopic;
        for (newTopic = 0; newTopic < K; newTopic++) {
            if (u < p[newTopic]) {
                break;
            }
        }

        // Add new topic label for w_{m, n}
        nmk[m][newTopic]++;
        nkt[newTopic][doc[m][n]]++;
        nmkSum[m]++;
        nktSum[newTopic]++;
        return newTopic;
    }

 最后收敛后就得到了文档的主题分布,主题的词汇分布,和文章中每个单词属于什么主题。

  LDA最浅显的应用就是信息检索中,训练好参数后对每个检索的文档计算主题分布,根据检索内容的主题分布和已知文档主题分布的距离返回相似的文档。更高级的应用还在探索中。

  LDA中一些具体的公式推到可以详见参考文献。有了整体理解后,看懂所有的推导就不成问题了!

 

  参考文献:《LDA数学八卦》

       《Parameter estimation for text analysis》

                 LDAGibbsSampling-master代码 by liuyang

 

posted @ 2016-10-30 22:49  LC_Ruc  阅读(1613)  评论(1编辑  收藏  举报