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模块基础上。这里的疑问待以后做项目的时候会有明确的答案。