java实现lda工程小结

java做工程总结,以lda为例


声明:本篇博客只是对java做工程的总结,目的是加深java语言的学习和模块化编程的应用。以lda为例,并不涉及lda的理论部分。

LDA代码版本:http://jgibblda.sourceforge.net/

数据的结构

  • 1.数据:建模就是把问题实数化,转成计算机能处理的类型,比如数字。对问题的求解,就是把问题对象映射成数字处理,处理完成后再把数字还原成问题对象。对于批量的数字,可以选用容器和数组,数组和容器的数据结构是不同的。所以对于如何选用,这里要注意一下:
Dataset:
  - localDict : dictionary
  - docs[] :      
  - M :  number of docs
  - V : number of words, localDict.size()
Doc:
  - rawStr: original sentence
  - int word[]: word对应的id,不计数量,重复的算一个
  - length: word.size()  
Dict:
  - Map<String,Integer> word2id
  - Map<Integer,String> id2word
    • 如果调用比较多,选数组,不选容器:比如对word下的topic的迭代调用次数很多,选数组存放效率会很高。Model.doc[d].word[w]结构比,model.get(doc-d).get(word-w)要快很多,因为后者需要检索,而前者直接调用.
    • 如果是插入和检索比较多,选容器(map或set),hash表插入和删除比较快: 比如words的字典, 只是判断word有无和word对应的id,插入和检索比较多,所以要选容器。原因容器的内存是动态可变的,其次容器的结构是树,插入效率高,复杂度lgN.
  • 权限问题:因为分布需要不断迭代,几乎全部的变量都是public,为了提高调用效率,暂时忽略了安全性。
  • 2.配置:CmdOPtions包的应用
    代码:
import org.kohsuke.args4j.*;

public class LDACmdOption {

	@Option(name = "-est", usage = "Specify whether we want to estimate model from scatch")
	public boolean est = false;

        @Option(name = "-dir", usage = "Specify directory")
	public String dir = "/home/cyno/wordspace/lda/";

}

使用:

cyno@DELL $ java jgibblda.LDA -est -dir '/home/cynor/corpus'
# -dir 是声明,后面'/home/cynor/corpus'是具体目录,设置其他参数方法类似类似。
# 简单明了
  • 对象:
    • init():java里面二维数组开辟空间要new两次,切记。
    • 其他:

类之间的套嵌

  • 1.主结构:

四个模块

1.Input():

2.Estimate():

3.Inference():

4.Output():

划分模块的标准:这一点还没有弄清楚,我是根据逻辑结构分的模块,就是后一个模块需要的数据是前一个模块的结果。并且两个实现的功能有很明显的区别。

  • 2.套嵌顺序:
                                   | writeWordMap()
                      | ReadData() | InitNewDateset()
       | Estimator()  |
       
                                   | Sampling()
                      | Estimate() | computeTheta()
                                   | computephi()
main() |             
       | Inference() 
  • 3.计划顺序与实现顺序:
    • 工程量比较多,或者套嵌比较多的时候,画图是最理想的选择 。如上面的流程图,每一列是一个类,里面的主要的函数清晰明了。
    • 计划顺序: 包括两个, 第一个是模块间的计划顺序,第二个是模块内的计划顺序。模块间的计划是由果到因,也就是从最终得到的结果推需要的条件。比如inference模块需要estimate得出什么样的结果,以什么样的格式保存,然后estimate模块得出这种结果又需要input模块以什么结构储存数据。把主线画出来之后,再细节化。之后,是模块内的计划顺序,计划的顺序是由主到次的,并且要参照主结构,比如input的模块的实现是这样的。根据上面的套嵌顺序。

main() -> Estimator() -> ReadData() -> initNewDataset() & writeMap()

      • 这个图是主结构的一个分支, 每个类都要写出来,尽管有的类里面在目前实现这个模块时只有一个函数。把大概结构写好, 方便之后添加其他模块。比如对于input模块,其实只要readData类和doc类,dict类三个低层基础类就够了,高层的Estimator类和main()类用不到,但是那是框架,要先写出来,目的是为了后来添加Estimate()模块,和inference模块。也就是说,在实现具体模块之前,已经把总的结构计划出来了,lda总的结构就是上面的套嵌顺序。
    • 实现顺序:实现的顺序也包括两个,第一个是模块实现顺序,这个顺序按照逻辑顺序划分,就像盖楼,上层是建在下层基层上的。输入->操作->输出,逐层实现。第二个是模块内的实现,这个顺序非常关键,是和计划的顺序相反的,应该先写下层的,就是从doc和dict类写起,把最核心的几个函数写出来,一个类里大概就几个函数。这条线确定了之后,在根据其他上层类的需要往下层具体类里添加其他函数。用到那个函数就写那个函数,这样会很方便调试,根据调用的线顺藤摸瓜。如果一次把所以函数写出来,调试的时候思维会很乱。每添加一个功能,就是添加一条调用线,从主函数到调用类到具体类函数的一条线,这种以线为结构的调用在调试的时候会很方便。
  • 4.调试
    • 根据实现的顺序,每一个功能都是从main到调用类到具体类的一条线。哪一布出错,直接print,如果这步没问题,就找上一步,非常方便。
    • 因为核心公式都是直接抄的原文,所以出现的错误很少,调试方面以后还要再多总结。

jgibblda的实用化

这个工程只要把输入端格式放宽,输出端人性化,就直接可以实用。

  • 1.输入部分
    样本的输入端对格式比较严格,样本的词都是按照字典序排好,并且开头标好词数。优化方式有两种,第一种是对要推断的文章预处理,第二种是把预处理的代码直接写进代码里。具体采用那种方法取决于要推断的文章的格式。

  • 2.输出部分
    对文章推断的输出不要输出topic的id和概率,直接输出该topic下的words,并且数量可设置。

疑问:

  • 1.如何多人分工写java工程?对于本篇lda,如果按照模块分工,即input模块,estimate模块,inference模块,output模块,分到input模块的那个人是很方便调试的,因为他处理的是最基本的文档,但是分到estimate模块的人怎么调试?他要处理的数据是建立在input模块基础上。这里的疑问待以后做项目的时候会有明确的答案。
原文地址:https://www.cnblogs.com/cyno/p/4149725.html