topic model本质上就一个套路,在doc-word user-url user-doc等关系中增加topic层,扩充为2层结构,一方面可以降维,另一方面挖掘深层次的关系,用户doc word user url的聚类。
LDA的理论知识不介绍太多,基本就讲了原理以及推导两个内容,原理比较简单,推导过程貌似很简单,就一个变分加上一些参数估计的方法就搞定了,但是具体的细节还没明白,以后慢慢研究。
简单介绍下基本原理以及有意义的几个公式:

plsa作为topic-model ,每篇文档对应一系列topics,每个topic对应一批terms,有如下问题:
1.每篇文档及其在topic上的分布都是模型参数,也就是模型参数随着文档的数目增加而增加,这样容易导致overfitting
2.对于new doc,如何确定其topic 分布

LDA解决这个问题,没必要把每个doc-topic分布作为模型参数,为doc-topic分布增加一个先验概率,限制整体上文档的topic分布,具体先验分布的作用,之前已经介绍过。
doc-topic分布服从多项分布,狄利克雷分布是其共轭先验。
这样参数的个数就变成K + N*K, N为词个数,K为topic个数,与文档个数无关。
如果我想知道一个文档的topic分布怎么办?下面介绍下train以及predic的方法。
作者采用了varitional inference进行推导,过程就免了,列出来几个重要的公式:

论文中几个重要公式:
概率模型:
D 表示文档集合,最后就是保证P(D|α,β)最大。


phi的迭代公式,表示文档中单词n在topic i上的分布:

gamma的迭代公式,文档在topic上的分布

Beta的迭代公式,model中topic-word分布:

alpha的迭代公式,model中文档-topic分布的先验参数,利用梯度下降法即可求解:





LDA最核心的迭代公式,针对每一篇文档,计算每个词的topic分布,从而计算文档的topic分布:

变分后,计算出来的似然函数,其似然值用户判断迭代的收敛程度:





基本逻辑:
1.初始模型参数,开始迭代,执行2,3,4,直至收敛
2.针对每一篇文档,初始gamma以及phi参数,迭代该参数,直至收敛
     2.1.计算文档中每个单词在topic上的分布,利用model中beta以及文档-topic分布(2.2)
     2.2.计算文档-topic分布,利用模型参数alpha以及word-topic分布(2.1结果)
3.update模型参数beta,利用word-topic分布。
4.update模型参数alpha,利用2.2的结果gamma


源码介绍

模型参数:
var_gamma:文档-topic分布,每篇文档都会计算其topic分布
phi:              word-topic分布,针对每篇文档,计算文档中每个word的topic分布
lda_mode:    lad的模型参数,里面包括beta以及alpha
lda_suffstats: 记录统计信息,比如每个topic上每个word出现的次数,这是为了计算lda_model而存在
corpus:        全部文档信息
document:    文档的具体信息,包括word信息

corpus_initialize_ss:初始化 ss,随机选择一些文档,利用文档词信息update ss 里面topic上term的频度

    for (k = 0; k < num_topics; k++)

    {

        for (i = 0; i < NUM_INIT; i++)

        {

            d = floor(myrand() * c->num_docs);

            printf("initialized with document %d\n", d);

            doc = &(c->docs[d]);

            for (n = 0; n < doc->length; n++)

            {

                ss->class_word[k][doc->words[n]] += doc->counts[n];

            }

        }

        for (n = 0; n < model->num_terms; n++)

        {

            ss->class_word[k][n] += 1.0;

            ss->class_total[k] = ss->class_total[k] + ss->class_word[k][n];

        }

    }

random_initialize_ss:随机选取一些值初始化ss

run_em:执行EM算法,针对每篇文档,利用其单词以及初始化的β和α信息 更新模型,直至收敛。
该函数是一个框架性质的,具体的见下。
void run_em(char* start, char* directory, corpus* corpus)

{



    int d, n;

    lda_model *model = NULL;

    double **var_gamma, **phi;



    // allocate variational parameters



    var_gamma = malloc(sizeof(double*)*(corpus->num_docs));

    for (d = 0; d < corpus->num_docs; d++)

     var_gamma[d] = malloc(sizeof(double) * NTOPICS);



    int max_length = max_corpus_length(corpus);

    phi = malloc(sizeof(double*)*max_length);

    for (n = 0; n < max_length; n++)

     phi[n] = malloc(sizeof(double) * NTOPICS);



    // initialize model



    char filename[100];



    lda_suffstats* ss = NULL;

    if (strcmp(start, "seeded")==0)

    {

        model = new_lda_model(corpus->num_terms, NTOPICS);

        ss = new_lda_suffstats(model);

        corpus_initialize_ss(ss, model, corpus);

        lda_mle(model, ss, 0);

        model->alpha = INITIAL_ALPHA;

    }

    else if (strcmp(start, "random")==0)

    {

        model = new_lda_model(corpus->num_terms, NTOPICS);

        ss = new_lda_suffstats(model);

        random_initialize_ss(ss, model);

        lda_mle(model, ss, 0);

        model->alpha = INITIAL_ALPHA;

    }

    else

    {

        model = load_lda_model(start);

        ss = new_lda_suffstats(model);

    }



    sprintf(filename,"%s/000",directory);

    save_lda_model(model, filename);



    // run expectation maximization



    int i = 0;

    double likelihood, likelihood_old = 0, converged = 1;

    sprintf(filename, "%s/likelihood.dat", directory);

    FILE* likelihood_file = fopen(filename, "w");



    while (((converged < 0) || (converged > EM_CONVERGED) || (i <= 2)) && (i <= EM_MAX_ITER))

    {

        i++; 

        likelihood = 0;

        zero_initialize_ss(ss, model);



        // e-step



//这里是核心,针对每篇文档计算相关模型参数

        for (d = 0; d < corpus->num_docs; d++)

        {

   

            likelihood += doc_e_step(&(corpus->docs[d]),

                                     var_gamma[d],

                                     phi,

                                     model,

                                     ss);

        }



        // m-step



        lda_mle(model, ss, ESTIMATE_ALPHA);




        // check for convergence



        converged = (likelihood_old - likelihood) / (likelihood_old);

        if (converged < 0) VAR_MAX_ITER = VAR_MAX_ITER * 2;

        likelihood_old = likelihood;


doc_e_step:执行EM中的E-step,干了两件事情
1.基于文档的单词,update φ以及γ,得到doc-topic 分布以及 word-topic分布(lda_inference)
2.利用γ信息,更新α,这里的α只有一个参数,所以计算方式不同。


本来应该是αi = αi + ,但是实际α只有一个,所以作者通过在所有topic上的分布计算出α。
每篇文档迭代一次,遍历完所有文档后,就计算出最后的α。

double doc_e_step(document* doc, double* gamma, double** phi,

                  lda_model* model, lda_suffstats* ss)

{

    double likelihood;

    int n, k;



    // posterior inference



    likelihood = lda_inference(doc, model, gamma, phi);



    // update sufficient statistics



    double gamma_sum = 0;

    for (k = 0; k < model->num_topics; k++)

    {

        gamma_sum += gamma[k];

        ss->alpha_suffstats += digamma(gamma[k]);

    }

    ss->alpha_suffstats -= model->num_topics * digamma(gamma_sum);



    for (n = 0; n < doc->length; n++)

    {

        for (k = 0; k < model->num_topics; k++)

        {

            ss->class_word[k][doc->words[n]] += doc->counts[n]*phi[n][k];

            ss->class_total[k] += doc->counts[n]*phi[n][k];

        }

    }




    ss->num_docs = ss->num_docs + 1;



    return(likelihood);

}

lad_inference:这是最核心的code,基于每个doc,计算对应γ和φ,也就是doc-topic以及word-topic分布。也就是如下代码:
利用topic个数以及word个数,初始化γ以及φ。
严格实现这个过程,工程做了优化,对φ取了对数logφ,这样降低计算复杂度,同时利用log_sum接口,计算log(φ1) log(φ2)...log(φk)计算出log(φ1+φ2.....+φk),这样利用(logφ1)-log(φ1+φ2.....+φk)即可完成归一化。
利用:

解释下代码:
     针对文档中的每一个词 n:
          针对每个topic i:
               单词n在topic i上的分布为:φni = topic i在word n上的分布 × exp(Digamma(该文档在toic i上的分布))
         归一化 φni
         文档在topic上的分布γi = 其先验分布αi + 文档内所有词在topic i上的分布值和

lda-infernce,不只是在train的时候使用,对于一片新的文档,同样是通过该函数/方法计算文档在tpoic上的分布,只依赖于模型参数α以及β
利用:
double lda_inference(document* doc, lda_model* model, double* var_gamma, double** phi)

{

    double converged = 1;

    double phisum = 0, likelihood = 0;

    double likelihood_old = 0, oldphi[model->num_topics];

    int k, n, var_iter;

    double digamma_gam[model->num_topics];



    // compute posterior dirichlet

     //init gama and php

    for (k = 0; k < model->num_topics; k++)

    {

//初始化 γ以及φ

        var_gamma[k] = model->alpha + (doc->total/((double) model->num_topics));

        digamma_gam[k] = digamma(var_gamma[k]);

        for (n = 0; n < doc->length; n++)

            phi[n][k] = 1.0/model->num_topics;


    }

    var_iter = 0;



    while ((converged > VAR_CONVERGED) &&

           ((var_iter < VAR_MAX_ITER) || (VAR_MAX_ITER == -1)))

    {

     var_iter++;

     for (n = 0; n < doc->length; n++)

     {

            phisum = 0;

            for (k = 0; k < model->num_topics; k++)

            {

                oldphi[k] = phi[n][k];

               //对于每个word,更新对应的topic,也就是公式中的对数结果

               

                phi[n][k] = digamma_gam[k] + model->log_prob_w[k][doc->words[n]];

                   

                if (k > 0)

              //为归一化做准备,通过log(a) +log(b)计算log(a+b)

                    phisum = log_sum(phisum, phi[n][k]);


                else

                    phisum = phi[n][k]; // note, phi is in log space

            }

              

            for (k = 0; k < model->num_topics; k++)

            {

                phi[n][k] = exp(phi[n][k] - phisum);//归一化



//update γ,这里面没有用到α,原始公式不同

                var_gamma[k] =var_gamma[k] + doc->counts[n]*(phi[n][k] - oldphi[k]);

                // !!! a lot of extra digamma's here because of how we're computing it

                // !!! but its more automatically updated too.

                digamma_gam[k] = digamma(var_gamma[k]);

                    printf("%d:%d: gmama: %f php: %f\n", n, k, var_gmama[k], php[n][k]);

            }

        }

//计算似然结果,观察是否收敛,计算采用公式

        likelihood = compute_likelihood(doc, model, phi, var_gamma);

        assert(!isnan(likelihood));

        converged = (likelihood_old - likelihood) / likelihood_old;

        likelihood_old = likelihood;



        // printf("[LDA INF] %8.5f %1.3e\n", likelihood, converged);

    }

    return(likelihood);

}

compute likehood:这个函数实现的是blei LDA 公式15,也就是定义的似然函数,比较复杂,但是严格按照这个实现。
需要注意的是,blei的代码,k个α值相同,计算时 包含α的计算进行了简化。
利用:

double

compute_likelihood(document* doc, lda_model* model, double** phi, double* var_gamma)

{

    double likelihood = 0, digsum = 0, var_gamma_sum = 0, dig[model->num_topics];

    int k, n;



    for (k = 0; k < model->num_topics; k++)

    {

     dig[k] = digamma(var_gamma[k]);

     var_gamma_sum += var_gamma[k];


    }

    digsum = digamma(var_gamma_sum);



lgamma(α*k) - k*lgamma(alpha)

    likelihood =

     lgamma(model->alpha * model -> num_topics)

     - model -> num_topics * lgamma(model->alpha)

     - (lgamma(var_gamma_sum));



    for (k = 0; k < model->num_topics; k++)

    {

     likelihood +=

         (model->alpha - 1)*(dig[k] - digsum) + lgamma(var_gamma[k])

         - (var_gamma[k] - 1)*(dig[k] - digsum);



     for (n = 0; n < doc->length; n++)

     {

            if (phi[n][k] > 0)

            {

                likelihood += doc->counts[n]*

                    (phi[n][k]*((dig[k] - digsum) - log(phi[n][k])

                                + model->log_prob_w[k][doc->words[n]]));

            }

        }

    }

    return(likelihood);

}


lda_mle:针对每个文档执行do_e_step后,更新了ss,也就是计算模型所需要数据,topic-word对的次数

void lda_mle(lda_model* model, lda_suffstats* ss, int estimate_alpha)

{

    int k; int w;



    for (k = 0; k < model->num_topics; k++)

    {

        for (w = 0; w < model->num_terms; w++)

        {

            if (ss->class_word[k][w] > 0)

            {

                model->log_prob_w[k][w] =

                    log(ss->class_word[k][w]) -

                    log(ss->class_total[k]);

            }

            else

                model->log_prob_w[k][w] = -100;

        }

    }

    if (estimate_alpha == 1)

    {

        model->alpha = opt_alpha(ss->alpha_suffstats,

                                 ss->num_docs,

                                 model->num_topics);



        printf("new alpha = %5.5f\n", model->alpha);

    }

}

LDA实现的更多相关文章

  1. 用scikit-learn进行LDA降维

    在线性判别分析LDA原理总结中,我们对LDA降维的原理做了总结,这里我们就对scikit-learn中LDA的降维使用做一个总结. 1. 对scikit-learn中LDA类概述 在scikit-le ...

  2. 线性判别分析LDA原理总结

    在主成分分析(PCA)原理总结中,我们对降维算法PCA做了总结.这里我们就对另外一种经典的降维方法线性判别分析(Linear Discriminant Analysis, 以下简称LDA)做一个总结. ...

  3. word2vec参数调整 及lda调参

     一.word2vec调参   ./word2vec -train resultbig.txt -output vectors.bin -cbow 0 -size 200 -window 5 -neg ...

  4. PCA与LDA的区别与联系

    由于涉及内容较多,这里转载别人的博客: http://blog.csdn.net/sunmenggmail/article/details/8071502 其实主要在于:PCA与LDA的变换矩阵不同, ...

  5. 计算LDA模型困惑度

    http://www.52nlp.cn/lda-math-lda-%E6%96%87%E6%9C%AC%E5%BB%BA%E6%A8%A1 LDA主题模型评估方法--Perplexity http:/ ...

  6. LDA的Python实现源码

    #-*- coding:utf-8 -*- import logging import logging.config import ConfigParser import numpy as np im ...

  7. LDA( Latent Dirichlet Allocation)主题模型 学习报告

    1     问题描述 LDA由Blei, David M..Ng, Andrew Y..Jordan于2003年提出,是一种主题模型,它可以将文档集中每篇文档的主题以概率分布的形式给出,从而通过分析一 ...

  8. 关于LDA的几何表示——MATLAB实现

    承接这个PCA的练习,还有一个关于LDA的几何表示. 题目如下: 代码实现LDA如下:LDA.m clear clc % 生成training sample MU1 = [6 10]'; MU2 = ...

  9. Gensim LDA主题模型实验

    本文利用gensim进行LDA主题模型实验,第一部分是基于前文的wiki语料,第二部分是基于Sogou新闻语料. 1. 基于wiki语料的LDA实验 上一文得到了wiki纯文本已分词语料 wiki.z ...

  10. [综] Latent Dirichlet Allocation(LDA)主题模型算法

    多项分布 http://szjc.math168.com/book/ebookdetail.aspx?cateid=1&&sectionid=983 二项分布和多项分布 http:// ...

随机推荐

  1. ORACLE异常(整理网上资料)

    一.oracle预定义异常 命名的系统异常 产生原因 Oracle Error SQLCODE Value ACCESS_INTO_NULL 未定义对象 ORA-06530  -6530 CASE_N ...

  2. 乐观的并发策略——基于CAS的自旋

    悲观者与乐观者的做事方式完全不一样,悲观者的人生观是一件事情我必须要百分之百完全控制才会去做,否则就认为这件事情一定会出问题:而乐观者的人生观则相反,凡事不管最终结果如何,他都会先尝试去做,大不了最后 ...

  3. XMPP(一)-openfire服务端的安装和搭建

    XMPP全称:可扩展通讯和表示协议 简介:可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯.表示和需求响应服务中的XML数据元流式传输.XMPP以Jabber协议为基础,而Jabber是即时通 ...

  4. Google Dremel数据模型详解(上)

    首先简单介绍一下Dremel是什么,能解决什么问题.第二部分着重讲Dremel的数据模型,即数据结构.第三部分将谈一下在此数据结构上设计的算法. 1 起源 Dremel的数据模型起源于分布式系统的应用 ...

  5. (一二七)NSURLSession的基本用法 下载与数据获取

    简介 NSURLSession是苹果官方提供的一系列网络接口库,使用他们可以轻松实现下载和数据获取等任务.在上一篇文章中,我们介绍了使用NSURLConnection下载文件和断点续传的功能,实现起来 ...

  6. SSH深度历险(三) EJB Session Bean有状态和无状态的区别与联系

    刚开始对两种sessionbean存在误解,认为有状态是实例一直存在,保存每次调用后的状态,并对下一次调用起作用,而认为无状态是每次调用实例化一次,不保留用户信息.仔细分析并用实践检验后,会发现,事实 ...

  7. Error running app: Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled.

    废了半天劲才解决... 就三步:菜单栏,Tools -> Adnroid -> enable ADB integration

  8. Android的oom详解

    Android的oom原因 1.资源对象没关闭造成的内存泄露,try catch finally中将资源回收放到finally语句可以有效避免OOM.资源性对象比如: 1-1,Cursor 1-2,调 ...

  9. 【一天一道LeetCode】#237. Delete Node in a Linked List

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...

  10. Oracle EBS SLA 详解

    SLA概述 SLA :子分类账(Subledger Accounting),这个在R12中大力宣扬的内容,我们通常的认为总账就是对Journal的汇总,但是在实际的操作中我们会发现,对于Sub sys ...