LibSvm流程及java代碼測試

使用libSvm實現文本分類的基本過程,此文參考 使用libsvm實現文本分類 對前期數據準備及後續的分類測試進行了驗證,同時對文中做者的分詞組件修改爲hanLP分詞,對數字進行過濾,僅保留長度大於1的詞進行處理。html

轉上文做者寫的分類流程:java

  1. 選擇文本訓練數據集和測試數據集:訓練集和測試集都是類標籤已知的;
  2. 訓練集文本預處理:這裏主要包括分詞、去停用詞、創建詞袋模型(倒排表);
  3. 選擇文本分類使用的特徵向量(詞向量):最終的目標是使得最終選出的特徵向量在多個類別之間具備必定的類別區分度,可使用相關有效的技術去實現特徵向量的選擇,因爲分詞後獲得大量的詞,經過選擇降維技術能很好地減小計算量,還能維持分類的精度;
  4. 輸出libsvm支持的量化的訓練樣本集文件:類別名稱、特徵向量中每一個詞元素分別到數字編號的映射轉換,以及基於類別和特徵向量來量化文本訓練集,可以知足使用libsvm訓練所須要的數據格式;
  5. 測試數據集預處理:一樣包括分詞(須要和訓練過程當中使用的分詞器一致)、去停用詞、創建詞袋模型(倒排表),可是這時須要加載訓練過程當中生成的特徵向量,用特徵向量去排除多餘的不在特徵向量中的詞(也稱爲降維);
  6. 輸出libsvm支持的量化的測試樣本集文件:格式和訓練數據集的預處理階段的輸出相同;
  7. 使用libsvm訓練文本分類器:使用訓練集預處理階段輸出的量化的數據集文件,這個階段也須要作不少工做(後面會詳細說明),最終輸出分類模型文件;
  8. 使用libsvm驗證分類模型的精度:使用測試集預處理階段輸出的量化的數據集文件,和分類模型文件來驗證分類的精度;
  9. 分類模型參數尋優:若是通過libsvm訓練出來的分類模型精度不好,能夠經過libsvm自帶的交叉驗證(Cross Validation)功能來實現參數的尋優,經過搜索參數取值空間來獲取最佳的參數值,使分類模型的精度知足實際分類須要。

文本預處理階段,增長了基於hanLP的分詞,代碼以下:maven

/**
 * 使用hanlp進行分詞
 * Created by zhouyh on 2018/5/30.
 */
public class HanLPDocumentAnalyzer extends AbstractDocumentAnalyzer implements DocumentAnalyzer {

    private static final Log LOG = LogFactory.getLog(HanLPDocumentAnalyzer.class);

    public HanLPDocumentAnalyzer(ConfigReadable configuration) {
        super(configuration);
    }

    @Override
    public Map<String, Term> analyze(File file) {
        String doc = file.getAbsolutePath();
        LOG.debug("Process document: file=" + doc);
        Map<String, Term> terms = Maps.newHashMap();
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charSet));
            String line = null;
            while((line = br.readLine()) != null) {
                LOG.debug("Process line: " + line);
                List<com.hankcs.hanlp.seg.common.Term> termList = HanLP.segment(line);
                if (termList!=null && termList.size()>0){
                    for (com.hankcs.hanlp.seg.common.Term hanLPTerm : termList){
                        String word = hanLPTerm.word;
                        if (!word.isEmpty() && !super.isStopword(word)){
                            if (word.trim().length()>1){
                                Pattern compile = Pattern.compile("(\\d+\\.\\d+)|(\\d+)|([\\uFF10-\\uFF19]+)");
                                Matcher matcher = compile.matcher(word);
                                if (!matcher.find()){
                                    Term term = terms.get(word);
                                    if (term == null){
                                        term = new TermImpl(word);
                                        terms.put(word, term);
                                    }
                                    term.incrFreq();
                                }
                            }
                        } else {
                            LOG.debug("Filter out stop word: file=" + file + ", word=" + word);
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("", e);
        } finally {
            try {
                if(br != null) {
                    br.close();
                }
            } catch (IOException e) {
                LOG.warn(e);
            }
            LOG.debug("Done: file=" + file + ", termCount=" + terms.size());
        }
        return terms;
    }

    public static void main(String[] args){
        String filePath = "/Users/zhouyh/work/yanfa/xunlianji/UTF8/train/ClassFile/C000008/0.txt";
        HanLPDocumentAnalyzer hanLPDocumentAnalyzer = new HanLPDocumentAnalyzer(new Configuration());
        hanLPDocumentAnalyzer.analyze(new File(filePath));
        String str = "測試hanLP分詞";
        System.out.println(str);
//        Pattern compile = Pattern.compile("(\\d+\\.\\d+)|(\\d+)|([\\uFF10-\\uFF19]+)");
//        Matcher matcher = compile.matcher("9402");
//        if (matcher.find()){
//            System.out.println(matcher.group());
//        }
    }
}
View Code

這裏對原做者提供的訓練集資源作了合併,將訓練集擴大到10個類別,每一個類別的8000文本中,前6000文本做爲訓練集,後2000文本做爲測試集,文本結構以下圖所示:ide

 測試集中是一樣的結構。測試

生成的特徵向量與libsvm須要的訓練集格式以下面所示:spa

libsvm訓練集格式文檔:debug

針對測試集也經過上述方式處理。3d

使用libSvm訓練分類文本code

文本轉換:htm

./svm-scale -l 0 -u 1 /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/train.txt > /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/train-scale.txt

測試集也作一樣轉換:

./svm-scale -l 0 -u 1 /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/test.txt > /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/test-scale.txt

進行模型訓練,此部分耗時較長:

./svm-train -h 0 -t 0 /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/train-scale.txt /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/model.txt

訓練過程以下圖所示:

訓練完成會生成model文件

採用預先處理好的測試文本進行分類測試:

./svm-predict /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/test-scale.txt /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/model.txt /Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/predict.txt

獲得結果爲:Accuracy = 81.6568% (16333/20002) (classification) 

總體流程作完,獲得文件以下圖所列:

 

至此,仿照原做者的思路,對libsvm的分類流程作了一次實踐。

JAVA代碼測試

創建相關java項目,引入libsvm的jar包,我這裏採用maven搭建,引入jar包:

<!-- https://mvnrepository.com/artifact/tw.edu.ntu.csie/libsvm -->
      <!-- libsvm jar包 -->
      <dependency>
          <groupId>tw.edu.ntu.csie</groupId>
          <artifactId>libsvm</artifactId>
          <version>3.17</version>
      </dependency>

同時要把libsvm包中的svm_predict.java及svm_train.java引入,並對svm_predict.java的類作簡單改動,將預測的結果值返回,測試代碼以下:

public class LibSvmAlgorithm {

    public static void main(String[] args){
        String[] testArgs = {"/Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/test-scale.txt", "/Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/model.txt", "/Users/zhouyh/work/yanfa/xunlianji/UTF8/heji/predict1.txt"};
        try {
            Double accuracy = svm_predict.main(testArgs);
            System.out.println(accuracy);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
相關文章
相關標籤/搜索