【GuidedLDA】代码分析

初始化

  • 先为各个文档里的单词随机分配主题
  • guidedLDA在初始化阶段改变了[文档:主题]的随机分布
  • seed_topics-字典格式{在词袋中的位置:种子词的列表索引}
# 这是有种子词的初始化
# 遍历所有单词
for i in range(N):
    # WS[k] 包含语料库中的第k个单词
    # DS[k] 包含第k个单词的文档索引
    w, d = WS[i], DS[i]
    if w not in seed_topics:
        continue
    # check if seeded initialization
    # 判断是否在种子词出现过
    # 初始化新的主题
    if w in seed_topics and random.random() < seed_confidence:
        # 使用自定义的主题编号
        z_new = seed_topics[w]
    else:
        # 否则,随机分配
        z_new = i % n_topics
    ZS[i] = z_new
    # 矩阵对应元素+1
    ndz_[d, z_new] += 1
    nzw_[z_new, w] += 1
    nz_[z_new] += 1

开始迭代

核心公式,这里源码是用cpython搞的
_guidedlda.cpython-36m-darwin.so

log p(w,z) = log p(w|z) + log p(z)

nzw_: 记录最终迭代中主题词分配的计数矩阵
ndz_: 记录最终迭代中文档主题分配的计数矩阵
nz_: 主题赋值数组在最终迭代中计数
z:主题
d:文档
w:单词

    def _fit(self, X, seed_topics, seed_confidence):
        """Fit the model to the data X

        Parameters
        ----------
        X: array-like, shape (n_samples, n_features)
            Training vector, where n_samples in the number of samples and
            n_features is the number of features. Sparse matrix allowed.
        """
        random_state = guidedlda.utils.check_random_state(self.random_state)
        rands = self._rands.copy()

        self._initialize(X, seed_topics, seed_confidence)
        # 迭代
        for it in range(self.n_iter):
            # FIXME: using numpy.roll with a random shift might be faster
            random_state.shuffle(rands)
            if it % self.refresh == 0:
                ll = self.loglikelihood()
                logger.info("<{}> log likelihood: {:.0f}".format(it, ll))
                # keep track of loglikelihoods for monitoring convergence
                self.loglikelihoods_.append(ll)
            self._sample_topics(rands)
        # 这里python代码是看不出返回的值是啥玩意
        # 用c写的,编译成so文件的
        # 计算可能性
        ll = self.loglikelihood()
        logger.info("<{}> log likelihood: {:.0f}".format(self.n_iter - 1, ll))
        # eta: Dirichlet parameter for distribution over words 词分布
        # alpha: Dirichlet parameter for distribution over topics 主题分布
        self.components_ = (self.nzw_ + self.eta).astype(float)
        # sum之后再增加一维得到形状(主题数,1)
        self.components_ /= np.sum(self.components_, axis=1)[:, np.newaxis]
        # 主题t生成V中第i个单词的概率
        self.topic_word_ = self.components_

        self.word_topic_ = (self.nzw_ + self.eta).astype(float)
        self.word_topic_ /= np.sum(self.word_topic_, axis=0)[np.newaxis, :]
        self.word_topic_ = self.word_topic_.T
        # 文档d对应主题T中第i个主题的概率
        self.doc_topic_ = (self.ndz_ + self.alpha).astype(float)
        self.doc_topic_ /= np.sum(self.doc_topic_, axis=1)[:, np.newaxis]

        # delete attributes no longer needed after fitting to save memory and reduce clutter
        del self.WS
        del self.DS
        del self.ZS
        return self

总结

  • 对于lDA来说,p(w|d) = p(w|t)p(t|d),词在文档的分布 = 词在主题的分布*主题在文档的分布,有两个超参数,θd和αt,分别表示d文档对于主题的概率分布和t主题生成单词的概率分布
  • 首先随机初始化θd和αt,然后枚举主题T,对于每一个主题,都可以计算出每一篇文档d和文档d对应所有单词w的p(w|d),取最大值,此时的主题t就是对应d文档w单词
  • 然后不断更新超参,最后收敛
  • python包源码不涉及LDA公式的实现,或者说不涉及串联一些变量,核心变量的更新在py代码里没有体现,是用的c(或者C++)实现的
  • 目前已经掌握变量的定义、形状、数据预处理
  • 目前能看到的是guidedlda在有种子词的情况下会影响初始化的主题分布和词分布的矩阵,这个原本在lda中是完全随机的,我在代码上做了注释
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章