zoukankan      html  css  js  c++  java
  • 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模块基础上。这里的疑问待以后做项目的时候会有明确的答案。
  • 相关阅读:
    Problem about objc_exception_throw
    Change mime Types for mac's default apache
    重启Mac上的Apache服务
    通过无线网安装自己打包的ipa文件
    How to cancel selection for TreePanel(GXT)
    使用Jsoup获取网页内容超时设置
    HTML Meta, http-equiv, Refresh
    线上redis服务内存异常分析。
    C++/CLI 本地字符串和托管字符串之间的转换
    DWF Toolkit on Microsoft Windows
  • 原文地址:https://www.cnblogs.com/cyno/p/4149725.html
Copyright © 2011-2022 走看看