使用libsvm实现文本分类

文本分类,首先它是分类问题,应该对应着分类过程的两个重要的步骤,一个是使用训练数据集训练分类器,另一个就是使用测试数据集来评价分类器的分类精度。然而,作为文本分类,它还具有文本这样的约束,所以对于文本来说,需要额外的处理过程,我们结合使用libsvm从宏观上总结一下,基于libsvm实现文本分类实现的基本过程,如下所示:

  1. 选择文本训练数据集和测试数据集:训练集和测试集都是类标签已知的;
  2. 训练集文本预处理:这里主要包括分词、去停用词、建立词袋模型(倒排表);
  3. 选择文本分类使用的特征向量(词向量):最终的目标是使得最终选出的特征向量在多个类别之间具有一定的类别区分度,可以使用相关有效的技术去实现特征向量的选择,由于分词后得到大量的词,通过选择降维技术能很好地减少计算量,还能维持分类的精度;
  4. 输出libsvm支持的量化的训练样本集文件:类别名称、特征向量中每个词元素分别到数字编号的映射转换,以及基于类别和特征向量来量化文本训练集,能够满足使用libsvm训练所需要的数据格式;
  5. 测试数据集预处理:同样包括分词(需要和训练过程中使用的分词器一致)、去停用词、建立词袋模型(倒排表),但是这时需要加载训练过程中生成的特征向量,用特征向量去排除多余的不在特征向量中的词(也称为降维);
  6. 输出libsvm支持的量化的测试样本集文件:格式和训练数据集的预处理阶段的输出相同。
  7. 使用libsvm训练文本分类器:使用训练集预处理阶段输出的量化的数据集文件,这个阶段也需要做很多工作(后面会详细说明),最终输出分类模型文件
  8. 使用libsvm验证分类模型的精度:使用测试集预处理阶段输出的量化的数据集文件,和分类模型文件来验证分类的精度。
  9. 分类模型参数寻优:如果经过libsvm训练出来的分类模型精度很差,可以通过libsvm自带的交叉验证(Cross Validation)功能来实现参数的寻优,通过搜索参数取值空间来获取最佳的参数值,使分类模型的精度满足实际分类需要。

基于上面的分析,分别对上面每个步骤进行实现,最终完成一个分类任务。

数据集选择

我们选择了搜狗的语料库,可以参考后面的链接下载语料库文件。这里,需要注意的是,分别准备一个训练数据集和一个测试数据集,不要让两个数据集有交叉。例如,假设有C个类别,选择每个分类的下的N篇文档作为训练集,总共的训练集文档数量为C*N,剩下的每一类下M篇作为测试数据集使用,测试数据集总共文档数等于C*M。

数据集文本预处理

我们选择使用ICTCLAS分词器,使用该分词器可以不需要预先建立自己的词典,而且分词后已经标注了词性,可以根据词性对词进行一定程度过滤(如保留名词,删除量词、叹词等等对分类没有意义的词汇)。
下载ICTCLAS软件包,如果是在Win7 64位系统上使用Java实现分词,选择如下两个软件包:

  • 20131115123549_nlpir_ictclas2013_u20131115_release.zip
  • 20130416090323_Win-64bit-JNI-lib.zip

将第二个软件包中的NLPIR_JNI.dll文件拷贝到C:\Windows\System32目录下面,将第一个软件包中的Data目录和NLPIR.dll、NLPIR.lib、NLPIR.h、NLPIR.lib文件拷贝到Java工程根目录下面。
对于其他操作系统,可以到ICTCLAS网站(http://ictclas.nlpir.org/downloads)下载对应版本的软件包。
下面,我们使用Java实现分词,定义分词器接口,以便切换其他分词器实现时,容易扩展,如下所示:

package org.shirdrn.document.processor.common;

import java.io.File;
import java.util.Map;

public interface DocumentAnalyzer {
     Map<String, Term> analyze(File file);
}

增加一个外部的停用词表,这个我们直接封装到抽象类AbstractDocumentAnalyzer中去了,该抽象类就是从一个指定的文件或目录读取停用词文件,将停用词加载到内存中,在分词的过程中对词进行进一步的过滤。然后基于上面的实现,给出包裹ICTCLAS分词器的实现,代码如下所示:

package org.shirdrn.document.processor.analyzer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import kevin.zhang.NLPIR;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.shirdrn.document.processor.common.DocumentAnalyzer;
import org.shirdrn.document.processor.common.Term;
import org.shirdrn.document.processor.config.Configuration;

public class IctclasAnalyzer extends AbstractDocumentAnalyzer implements DocumentAnalyzer {

     private static final Log LOG = LogFactory.getLog(IctclasAnalyzer.class);
     private final NLPIR analyzer;
    
     public IctclasAnalyzer(Configuration configuration) {
          super(configuration);
          analyzer = new NLPIR();
          try {
               boolean initialized = NLPIR.NLPIR_Init(".".getBytes(charSet), 1);
               if(!initialized) {
                    throw new RuntimeException("Fail to initialize!");
               }
          } catch (Exception e) {
               throw new RuntimeException("", e);
          }
     }

     @Override
     public Map<String, Term> analyze(File file) {
          String doc = file.getAbsolutePath();
          LOG.info("Process document: file=" + doc);
          Map<String, Term> terms = new HashMap<String, Term>(0);
          BufferedReader br = null;
          try {
               br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charSet));
               String line = null;
               while((line = br.readLine()) != null) {
                    line = line.trim();
                    if(!line.isEmpty()) {
                         byte nativeBytes[] = analyzer.NLPIR_ParagraphProcess(line.getBytes(charSet), 1);
                         String content = new String(nativeBytes, 0, nativeBytes.length, charSet);
                         String[] rawWords = content.split("\\s+");
                         for(String rawWord : rawWords) {
                              String[] words = rawWord.split("/");
                              if(words.length == 2) {
                                   String word = words[0];
                                   String lexicalCategory = words[1];
                                   Term term = terms.get(word);
                                   if(term == null) {
                                        term = new Term(word);
                                        // TODO set lexical category
                                        term.setLexicalCategory(lexicalCategory);
                                        terms.put(word, term);
                                   }
                                   term.incrFreq();
                                   LOG.debug("Got word: word=" + rawWord);
                              }
                         }
                    }
               }
          } catch (IOException e) {
               e.printStackTrace();
          } finally {
               try {
                    if(br != null) {
                         br.close();
                    }
               } catch (IOException e) {
                    LOG.warn(e);
               }
          }
          return terms;
     }

}

它是对一个文件进行读取,然后进行分词,去停用词,最后返回的Map包含了的集合,此属性包括词性(Lexical Category)、词频、TF等信息。
这样,遍历数据集目录和文件,就能去将全部的文档分词,最终构建词袋模型。我们使用Java中集合来存储文档、词、类别之间的关系,如下所示:

     private int totalDocCount;
     private final List<String> labels = new ArrayList<String>();
     // Map<类别, 文档数量>
     private final Map<String, Integer> labelledTotalDocCountMap = new HashMap<String, Integer>();
     //  Map<类别, Map<文档 ,Map<词, 词信息>>>
     private final Map<String, Map<String, Map<String, Term>>> termTable =
               new HashMap<String, Map<String, Map<String, Term>>>();
     //  Map<词 ,Map<类别, Set<文档>>>
     private final Map<String, Map<String, Set<String>>> invertedTable =
               new HashMap<String, Map<String, Set<String>>>();

基于训练数据集选择特征向量

上面已经构建好词袋模型,包括相关的文档和词等的关系信息。现在我们来选择用来建立分类模型的特征词向量,首先要选择一种度量,来有效地选择出特征词向量。基于论文《A comparative study on feature selection in text categorization》,我们选择基于卡方统计量(chi-square statistic, CHI)技术来实现选择,这里根据计算公式:
chi-formula
其中,公式中各个参数的含义,说明如下:

  • N:训练数据集文档总数
  • A:在一个类别中,包含某个词的文档的数量
  • B:在一个类别中,排除该类别,其他类别包含某个词的文档的数量
  • C:在一个类别中,不包含某个词的文档的数量
  • D:在一个类别中,不包含某个词也不在该类别中的文档的数量

要想进一步了解,可以参考这篇论文。
使用卡方统计量,为每个类别下的每个词都进行计算得到一个CHI值,然后对这个类别下的所有的词基于CHI值进行排序,选择出最大的topN个词(很显然使用堆排序算法更合适);最后将多个类别下选择的多组topN个词进行合并,得到最终的特征向量。
其实,这里可以进行一下优化,每个类别下对应着topN个词,在合并的时候可以根据一定的标准,将各个类别都出现的词给出一个比例,超过指定比例的可以删除掉,这样可以使特征向量在多个类别分类过程中更具有区分度。这里,我们只是做了个简单的合并。
我们看一下,用到的存储结构,使用Java的集合来存储:

     // Map<label, Map<word, term>>
     private final Map<String, Map<String, Term>> chiLabelToWordsVectorsMap = new HashMap<String, Map<String, Term>>(0);
     // Map<word, term>, finally merged vector
     private final Map<String, Term> chiMergedTermVectorMap = new HashMap<String, Term>(0);

下面,实现特征向量选择计算的实现,代码如下所示:

package org.shirdrn.document.processor.component.train;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.shirdrn.document.processor.common.AbstractComponent;
import org.shirdrn.document.processor.common.Context;
import org.shirdrn.document.processor.common.Term;
import org.shirdrn.document.processor.utils.SortUtils;

public class FeatureTermVectorSelector extends AbstractComponent {

     private static final Log LOG = LogFactory.getLog(FeatureTermVectorSelector.class);
     private final int keptTermCountEachLabel;
     
     public FeatureTermVectorSelector(Context context) {
          super(context);
          keptTermCountEachLabel = context.getConfiguration().getInt("processor.each.label.kept.term.count", 3000);
     }

     @Override
     public void fire() {
          // compute CHI value for selecting feature terms 
          // after sorting by CHI value
          for(String label : context.getVectorMetadata().getLabels()) {
               // for each label, compute CHI vector
               LOG.info("Compute CHI for: label=" + label);
               processOneLabel(label);
          }
          
          // sort and select CHI vectors
          Iterator<Entry<String, Map<String, Term>>> chiIter = 
                    context.getVectorMetadata().chiLabelToWordsVectorsIterator();
          while(chiIter.hasNext()) {
               Entry<String, Map<String, Term>> entry = chiIter.next();
               String label = entry.getKey();
               LOG.info("Sort CHI terms for: label=" + label + ", termCount=" + entry.getValue().size());
               Entry<String, Term>[] a = sort(entry.getValue());
               for (int i = 0; i < Math.min(a.length, keptTermCountEachLabel); i++) {
                    Entry<String, Term> termEntry = a[i];
                    // merge CHI terms for all labels
                    context.getVectorMetadata().addChiMergedTerm(termEntry.getKey(), termEntry.getValue());
               }
          }
     }
     
     @SuppressWarnings("unchecked")
     private Entry<String, Term>[] sort(Map<String, Term> terms) {
          Entry<String, Term>[] a = new Entry[terms.size()];
          a = terms.entrySet().toArray(a);
          SortUtils.heapSort(a, true, keptTermCountEachLabel);
          return a;
     }

     private void processOneLabel(String label) {
          Iterator<Entry<String, Map<String, Set<String>>>> iter = 
                    context.getVectorMetadata().invertedTableIterator();
          while(iter.hasNext()) {
               Entry<String, Map<String, Set<String>>> entry = iter.next();
               String word = entry.getKey();
               Map<String, Set<String>> labelledDocs = entry.getValue();
               
               // A: doc count containing the word in this label
               int docCountContainingWordInLabel = 0;
               if(labelledDocs.get(label) != null) {
                    docCountContainingWordInLabel = labelledDocs.get(label).size();
               }
               
               // B: doc count containing the word not in this label
               int docCountContainingWordNotInLabel = 0;
               Iterator<Entry<String, Set<String>>> labelledIter = 
                         labelledDocs.entrySet().iterator();
               while(labelledIter.hasNext()) {
                    Entry<String, Set<String>> labelledEntry = labelledIter.next();
                    String tmpLabel = labelledEntry.getKey();
                    if(!label.equals(tmpLabel)) {
                         docCountContainingWordNotInLabel += entry.getValue().size();
                    }
               }
               
               // C: doc count not containing the word in this label
               int docCountNotContainingWordInLabel = 
                         getDocCountNotContainingWordInLabel(word, label);
               
               // D: doc count not containing the word not in this label
               int docCountNotContainingWordNotInLabel = 
                         getDocCountNotContainingWordNotInLabel(word, label);
               
               // compute CHI value
               int N = context.getVectorMetadata().getTotalDocCount();
               int A = docCountContainingWordInLabel;
               int B = docCountContainingWordNotInLabel;
               int C = docCountNotContainingWordInLabel;
               int D = docCountNotContainingWordNotInLabel;
               int temp = (A*D-B*C);
               double chi = (double) N*temp*temp / (A+C)*(A+B)*(B+D)*(C+D);
               Term term = new Term(word);
               term.setChi(chi);
               context.getVectorMetadata().addChiTerm(label, word, term);
          }
     }

     private int getDocCountNotContainingWordInLabel(String word, String label) {
          int count = 0;
          Iterator<Entry<String,Map<String,Map<String,Term>>>> iter = 
                    context.getVectorMetadata().termTableIterator();
          while(iter.hasNext()) {
               Entry<String,Map<String,Map<String,Term>>> entry = iter.next();
               String tmpLabel = entry.getKey();
               // in this label
               if(tmpLabel.equals(label)) {
                    Map<String, Map<String, Term>> labelledDocs = entry.getValue();
                    for(Entry<String, Map<String, Term>> docEntry : labelledDocs.entrySet()) {
                         // not containing this word
                         if(!docEntry.getValue().containsKey(word)) {
                              ++count;
                         }
                    }
                    break;
               }
          }
          return count;
     }
     
     private int getDocCountNotContainingWordNotInLabel(String word, String label) {
          int count = 0;
          Iterator<Entry<String,Map<String,Map<String,Term>>>> iter = 
                    context.getVectorMetadata().termTableIterator();
          while(iter.hasNext()) {
               Entry<String,Map<String,Map<String,Term>>> entry = iter.next();
               String tmpLabel = entry.getKey();
               // not in this label
               if(!tmpLabel.equals(label)) {
                    Map<String, Map<String, Term>> labelledDocs = entry.getValue();
                    for(Entry<String, Map<String, Term>> docEntry : labelledDocs.entrySet()) {
                         // not containing this word
                         if(!docEntry.getValue().containsKey(word)) {
                              ++count;
                         }
                    }
               }
          }
          return count;
     }

}

输出量化数据文件

特征向量已经从训练数据集中计算出来,接下来需要对每个词给出一个唯一的编号,从1开始,这个比较容易,输出特征向量文件,为测试验证的数据集所使用,文件格式如下所示:

认识     1
代理权     2
病理     3
死者     4
影子     5
生产国     6
容量     7
螺丝扣     8
大钱     9
壮志     10
生态圈     11
好事     12
全人类     13

由于libsvm使用的训练数据格式都是数字类型的,所以需要对训练集中的文档进行量化处理,我们使用TF-IDF度量,表示词与文档的相关性指标。
然后,需要遍历已经构建好的词袋模型,并使用已经编号的类别和特征向量,对每个文档计算TF-IDF值,每个文档对应一条记录,取出其中一条记录,输出格式如下所示:

8 9219:0.24673737883635047 453:0.09884635754820137 10322:0.21501394457319623 11947:0.27282495932970074 6459:0.41385272697452935 46:0.24041607991272138 8987:0.14897255497578704 4719:0.22296154731520754 10094:0.13116443653818177 5162:0.17050804524212404 2419:0.11831944042647048 11484:0.3501901869096251 12040:0.13267440708284894 8745:0.5320327758892881 9048:0.11445287153209653 1989:0.04677087098649205 7102:0.11308242956243426 3862:0.12007217405755069 10417:0.09796211412332205 5729:0.148037967054332 11796:0.08409157900442304 9094:0.17368658217203461 3452:0.1513474608736807 3955:0.0656773581702849 6228:0.4356889927309336 5299:0.15060439516792662 3505:0.14379243687841153 10732:0.9593462052245719 9659:0.1960034406311122 8545:0.22597403804274924 6767:0.13871522631066047 8566:0.20352452713417019 3546:0.1136541497082903 6309:0.10475466997804883 10256:0.26416957780238604 10288:0.22549409383630933

第一列的8表示类别编号,其余的每一列是词及其权重,使用冒号分隔,例如“9219:0.24673737883635047”表示编号为9219的词,对应的TF-IDF值为0.24673737883635047。如果特征向量有个N个,那么每条记录就对应着一个N维向量。
对于测试数据集中的文档,也使用类似的方法,不过首先需要加载已经输出的特征向量文件,从而构建一个支持libsvm格式的输出测试集文件。

使用libsvm训练文本分类器

前面的准备工作已经完成,现在可以使用libsvm工具包训练文本分类器。在使用libsvm的开始,需要做一个尺度变换操作(有时也称为归一化),有利于libsvm训练出更好的模型。我们已经知道前面输出的数据中,每一维向量都使用了TF-IDF的值,但是TF-IDF的值可能在一个不规范的范围之内(因为它依赖于TF和IDF的值),例如0.19872~8.3233,所以可以使用libsvm将所有的值都变换到同一个范围之内,如0~1.0,或者-1.0~1.0,可以根据实际需要选择。我们看一下命令:

F:\libsvm-3.0\windows>svm-scale.exe -l 0 -u 1 C:\\Users\\thinkpad\\Desktop\\vector\\train.txt > C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt

尺度变换后输出到文件train-scale.txt中,它可以直接作为libsvm训练的数据文件,我使用Java版本的libsvm代码,输入参数如下所示:

train -h 0 -t 0 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt

这里面,-t 0表示使用线性核函数,我发现在进行文本分类时,线性核函数比libsvm默认的-t 2非线性核函数的效果要要好一些。最后输出的是模型文件model.txt,内容示例如下所示:

svm_type c_svc
kernel_type linear
nr_class 10
total_sv 54855
rho -0.26562545584492675 -0.19596934447720876 0.24937032535471693 0.3391566771481882 -0.19541394291523667 -0.20017990510840347 -0.27349052681332664 -0.08694672836814998 -0.33057155365157015 0.06861675551386985 0.5815821822995312 0.7781870137763507 0.054722797451472065 0.07912846180263113 -0.01843419889020123 0.15110176721612528 -0.08484865489154271 0.46608205351462983 0.6643550487438468 -0.003914533674948038 -0.014576392246426623 -0.11384567944039309 0.09257404411884447 -0.16845445862600575 0.18053514069700813 -0.5510915276095857 -0.4885382860289285 -0.6057167948571457 -0.34910272249526764 -0.7066730463805829 -0.6980796972363181 -0.639435517196082 -0.8148772080348755 -0.5201121512955246 -0.9186975203736724 -0.008882360255733836 -0.0739010940085453 0.10314117392946448 -0.15342997221636115 -0.10028736061509444 0.09500443080371801 -0.16197536915675026 0.19553010464320583 -0.030005330377757263 -0.24521471309904422
label 8 4 7 5 10 9 3 2 6 1
nr_sv 6542 5926 5583 4058 5347 6509 5932 4050 6058 4850
SV
0.16456599916886336 0.22928285261208994 0.921277302054534 0.39377902901901013 0.4041207410447258 0.2561997963212561 0.0 0.0819993502684317 0.12652009525418703 9219:0.459459 453:0.031941 10322:0.27027 11947:0.0600601 6459:0.168521 46:0.0608108 8987:0.183784 4719:0.103604 10094:0.0945946 5162:0.0743243 2419:0.059744 11484:0.441441 12040:0.135135 8745:0.108108 9048:0.0440154 1989:0.036036 7102:0.0793919 3862:0.0577064 10417:0.0569106 5729:0.0972222 11796:0.0178571 9094:0.0310078 3452:0.0656566 3955:0.0248843 6228:0.333333 5299:0.031893 3505:0.0797101 10732:0.0921659 9659:0.0987654 8545:0.333333 6767:0.0555556 8566:0.375 3546:0.0853659 6309:0.0277778 10256:0.0448718 10288:0.388889
... ... 

上面,我们只是选择了非默认的核函数,还有其他参数可以选择,比如代价系数c,默认是1,表示在计算线性分类面时,可以容许一个点被分错。这时候,可以使用交叉验证来逐步优化计算,选择最合适的参数。
使用libsvm,指定交叉验证选项的时候,只输出经过交叉验证得到的分类器的精度,而不会输出模型文件,例如使用交叉验证模型运行时的参数示例如下:

-h 0 -t 0 -c 32 -v 5 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt

用-v启用交叉验证模式,参数-v 5表示将每一类下面的数据分成5份,按顺序1对2,2对3,3对4,4对5,5对1分别进行验证,从而得出交叉验证的精度。例如,下面是我们的10个类别的交叉验证运行结果:

Cross Validation Accuracy = 71.10428571428571%

在选好各个参数以后,就可以使用最优的参数来计算输出模型文件。

使用libsvm验证文本分类器精度

前面已经训练出来分类模型,就是最后输出的模型文件。现在可以使用测试数据集了,通过使用测试数据集来做对基于文本分类模型文件预测分类精度进行验证。同样,需要做尺度变换,例如:

F:\libsvm-3.0\windows>svm-scale.exe -l 0 -u 1 C:\\Users\\thinkpad\\Desktop\\vector\\test.txt > C:\\Users\\thinkpad\\Desktop\\vector\\test-scale.txt

注意,这里必须和训练集使用相同的尺度变换参数值。
我还是使用Java版本的libsvm进行预测,验证分类器精度,svm_predict类的输入参数:

C:\\Users\\thinkpad\\Desktop\\vector\\test-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt C:\\Users\\thinkpad\\Desktop\\vector\\predict.txt

这样,预测结果就在predict.txt文件中,同时输出分类精度结果,例如:

Accuracy = 66.81% (6681/10000) (classification)

如果觉得分类器精度不够,可以使用交叉验证去获取更优的参数,来训练并输出模型文件,例如,下面是几组结果:

train -h 0 -t 0 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt
Accuracy = 67.10000000000001% (6710/10000) (classification)

train -h 0 -t 0 -c 32 -v 5 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt
Cross Validation Accuracy = 71.10428571428571%
Accuracy = 66.81% (6681/10000) (classification)

train -h 0 -t 0 -c 8 -m 1024 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt 
C:\\Users\\thinkpad\\Desktop\\vector\\model.txt
Cross Validation Accuracy = 74.3240057320121%
Accuracy = 67.88% (6788/10000) (classification)

第一组是默认情况下c=1时的精度为 67.10000000000001%;
第二组使用了交叉验证模型,交叉验证精度为71.10428571428571%,获得参数c=32,使用该参数训练输出模型文件,基于该模型文件进行预测,最终的精度为66.81%,可见没有使用默认c参数值的精度高;
第三组使用交叉验证,精度比第二组高一些,输出模型后并进行预测,最终精度为67.88%,略有提高。
可以基于这种方式不断地去寻找最优的参数,从而使分类器具有更好的精度。

总结

文本分类有其特殊性,在使用libsvm分类,或者其他的工具,都不要忘记,有如下几点需要考虑到:

  1. 其实文本在预处理过程进行的很多过程对最后使用工具进行分类都会有影响。
  2. 最重要的就是文本特征向量的选择方法,如果文本特征向量选择的很差,即使再好的分类工具,可能训练得到的分类器都未必能达到更好。
  3. 文本特征向量选择的不好,在训练调优分类器的过程中,虽然找到了更好的参数,但是这本身可能会是一个不好的分类器,在实际预测中可以出现误分类的情况。
  4. 选择训练集和测试集,对整个文本预处理过程,以及使用分类工具进行训练,都有影响。

相关资源

最后,附上文章中有关文本分类预处理的实现代码,仅供参考,链接为:

这个是最早的那个版本,默认分词使用了中科院ICTCLAS分词器,不过我当时使用的现在好像因为License的问题可能用不了了。

这个是我最近抽时间将原来document-processor代码进行重构,使用Maven进行构建,分为多个模块,并做了部分优化:

  1. 默认使用Lucene的SmartChineseAnalyzer进行分词,默认没有使用词典,分词效果可能没有那么理想。
  2. 增加了api层,对于某些可以自定义实现的,预留出了接口,并可以通过配置来注入自定义实现功能,主要是接口:FeatureTermSelector。
  3. 分词过程中,各个类别采用并行处理,处理时间大大减少:DocumentWordsCollector。
  4. 将train阶段和test阶段通用的配置提出来,放在配置文件config.properties中。
  5. 默认给予CHI卡方统计量方法选择特征向量,实现了各个类别之间并行处理的逻辑,可以查看实现类ChiFeatureTermSelector。

另外,使用的语料库文件,我放在百度网盘上了,可以下载:http://pan.baidu.com/s/1pJv6AIR

参考链接

Creative Commons License

本文基于署名-非商业性使用-相同方式共享 4.0许可协议发布,欢迎转载、使用、重新发布,但务必保留文章署名时延军(包含链接:http://shiyanjun.cn),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

评论(260): “使用libsvm实现文本分类

  1. 赞~介绍的很详细!我刚开始学数据挖掘,对于特征选择那块看得还不是很明白,照着你的代码试了试发现有不少问题,不知道您可不可以给我邮箱发该程序的源码让我学学?谢谢啦~

  2. processor.dataset.chi.term.vector.file=C:\\Users\\Shirdrn\\Desktop\\vector\\terms.txt
    processor.dataset.file.extension=.txt
    processor.dataset.label.vector.file=C:\\Users\\Shirdrn\\Desktop\\vector\\labels.txt
    请问一下config_test.properties里面这三项是做什么的?

    • 第一个和第三个都是经过训练阶段以后,产出的结果。processor.dataset.label.vector.file是包含了类标签的文件(类标签名称+编号),你在测试阶段肯定要对应上;processor.dataset.chi.term.vector.file是包含经过训练节点选择的特征向量文件(词+词编号)。

  3. 膜拜下楼主,楼主的功力十分深厚啊,佩服!!
    想请问下楼主配置文件中的processor.document.filter.kept.lexical.categories=n,这个意思是特征向量只保留了名词?请问楼主这样做有什么依据呢?是否这样选择能提高分类精度?非常感谢。

    呵呵,还有个问题呢,楼主您最后将所有的特征项都合并放到一个map中,而Term中并没有label的属性,请问如何标记他们的类别的呢?

    谢谢楼主作出的卓有成效的工作,为我们指明了方向,再次感谢。

    • 1、n表示名词,根据数据的特点来选择,当然也可以不使用词性来过滤。
      2、这个其实就是选择能够标识文档类别的特征词,决定这个“特征”的也未必就是根据词性。
      3、选择名词作为特征向量,肯定比你选择介词、助词等更能表达语义,精度自然会好,因为名词相对来说更有区分度。
      4、举个简单的例子:假如有3个类别,C={体育,农业 , 军事},合并得到一个8维特征词向量为T(体育, 篮球, 跑步, 玉米, 花生, 大豆, 作物, 军舰),那么T(1, 3, 5, 0, 0, 1, 0, 0)会被认为属于体育这个类别,而T(0, 0, 0, 0, 1, 0, 0, 5)会被认为属于军事这个类别,T(0, 0, 0, 0, 4, 1, 7, 0)属于农业这个类别,讲这个例子你能明白了吧。

      • 非常感谢楼主的回复,您讲解的非常清楚,我了解楼主的意思了。

        只是对于第一个问题,呵呵,我只是有点好奇呢,名词确实包含的意义更多,只是奇怪楼主如何想到只保留名词,而将动词、形容词去掉,这样做是否提高了精度,或者说是只在某个领域的分类应用提高了精度呢?

        望楼主不吝赐教。

  4. 非常感谢。讲解得很详细,我是刚接触svm,从楼主的文章获益颇多。
    代码也跑通了,只是得到的输出文件还不是直接符合libsvm要求的,libsvm要求每条数据特征向量index按升序排列。
    再次感谢楼主的无私!

    • 您好!请教下,最后这个index排序的数据预处理怎么做的?能把你的脚本发我一份吗?邮箱:659324338@qq.com

        • 楼主您好,请问您这代码跑通了没,请问停用词去除了没有,停用词文件夹下是不是有多个停用词表文件,不然干嘛要用dir.fileList()方法呢?

  5. 还在运行这个程序,发现了一个问题,如果我测试集合里面类别下面只有一个文件,算出来的tf-idf值为-0.125,我看了一下代码,测试的时候idf值是重新计算的,标准情况下,idf的值是不是应该使用训练集得出的值?

  6. 由衷的感谢楼主的帮助,这是我第一个算是数据挖掘案例的实现吧,之前一直在找文本分类的资料,都很零碎,而这次通过楼主的源代码,一句句分析下来,觉得很清晰,除此之外,楼主的代码真的写的很漂亮。再次感谢楼主。
    此外,我发现楼主的代码里有两个问题,可能正是这两个导致了测试正确率低吧,一个是对CHI堆排序的算法,楼主没有写全。另一个是计算TF时,应该是除以该篇文档中所有词的总数,就是说是词*词频,而楼主可能疏忽少乘了词频。这样我测出来的正确率竟然有95%,发现提高好多哦。

      • 还有一个问题,希望楼主解答下,可能是我新手不懂,看到很多小白也在提,就是如果我现在想测试一篇未知分类的文本,用tf-idf进行向量化就会出现问题了,对于这个我们该怎么做呢?

        • 如果对单个文档进行分类,实际上就没有IDF的概念了,可以直接使用TF计算看看,直观上看应该可以,模型只不过是间接地给了一个加权而已。单个文档这种情况我还真没试过,你可以试试。
          或者,我们可以选择其他一种度量去计算呢。

    • 堆排序选出特征向量的逻辑,已经修正了,详细可以查看代码文件:https://github.com/shirdrn/document-processor/blob/master/src/main/java/org/shirdrn/document/processor/utils/SortUtils.java

    • 您好!请教一下,你是怎么做到95%的准确率的,能把您的源码发我一份吗?刚接触文本分类,还望大神指教!

    • 我按照博文中的方法获得的特测征向量只有2000+维(训练样本8000*10),预测准确度也很低,只有35%,不知道是哪里有问题导致这么大的差异?

      • 可以看看文章后面,我总结的那几点,根据你实际的情况进行相关参数的调整可能也会改善分类精度。

        • 我基本上是按照博文中描述进行的,包括语料库等,所以预想会有接近的结果。我后来将词性进行了一点扩展(加入了v,vt等)后,特征向量维数增加到了近3000,不过实际预测的准确度还是很低。

          想问博主,你当时的测试向量的维数是多少?猜想维数太小可能是一个原因。

    • 请问,你说的第二个问题,关于计算TF的问题是什么意思?“另一个是计算TF时,应该是除以该篇文档中所有词的总数,就是说是词*词频,而楼主可能疏忽少乘了词频。这样我测出来的正确率竟然有95%,发现提高好多哦”

    • 您好,问一下95%的正确率怎么得到的?
      还有,为什么正确率一直在60%几呢?楼主的测试也都是60%几,原因是什么啊

  7. 您好,我是小白,刚接触文本分类,对JAVA也不是很熟悉,能不能提供源码给我下载,或者发到我邮箱?不胜感谢!

  8. 楼主,你的计算CHI值有问题,“N*temp*temp / (A+C)*(A+B)*(B+D)*(C+D)” 其中整个分母(A+C)*(A+B)*(B+D)*(C+D)外应该加个括号,要不然计算顺序会出错,就成了乘乘除乘乘乘,你看是不是?

      • 我发现如果数据量太大的时候,按照楼主的代码计算出的CHI=N*temp*temp / (A+C)*(A+B)*(B+D)*(C+D)值有负值,应该是超出了double的显示范围,用BigDecimal也没得到很好的解决,请问楼主有没有比较好的办法?

        • BigDecimal肯定可以解决的吧,你试试下面的计算逻辑:
          BigDecimal bd = new BigDecimal(“4318747498174987189479874983127978.82777193″);
          System.out.println(bd.multiply(bd));
          System.out.println(bd.divide(new BigDecimal(“88.431424317″), 10, RoundingMode.HALF_UP));

  9. 运行TrainDocumentProcessorDriver出错:
    Exception in thread “main” java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:43)
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:30)
    at org.shirdrn.document.processor.component.DocumentWordsCollector.(DocumentWordsCollector.java:30)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:42)
    at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:24)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)
    Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.shirdrn.document.processor.utils.ReflectionUtils.construct(ReflectionUtils.java:76)
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:41)
    … 5 more
    Caused by: java.lang.RuntimeException:
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:33)
    … 11 more
    Caused by: java.lang.RuntimeException: Fail to initialize!
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:30)
    … 11 more
    不明白这个该怎么解决,麻烦楼主赐教!!

    • 问题已经解决,是因为NLPIR初始化失败造成的,用ictcals2014下面的Data文件夹把ictcals2013的Data替换掉,就好了

    • Exception in thread “main” java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
      at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:43)
      at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:30)
      at org.shirdrn.document.processor.component.DocumentWordsCollector.(DocumentWordsCollector.java:30)
      at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:42)
      at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:24)
      at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)
      Caused by: java.lang.reflect.InvocationTargetException
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
      at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
      at org.shirdrn.document.processor.utils.ReflectionUtils.construct(ReflectionUtils.java:76)
      at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:41)
      … 5 more
      Caused by: java.lang.UnsatisfiedLinkError: kevin.zhang.NLPIR.NLPIR_Init([BI)Z
      at kevin.zhang.NLPIR.NLPIR_Init(Native Method)
      at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:28)
      … 11 more
      我的也是这个问题,按着你说的方法试了,可还是不行。能帮帮忙吗?谢了

  10. 选择文本训练数据集和测试数据集:训练集和测试集都是类标签已知的;对于这里我有一个疑问,请问博主,你的测试集类别都是已知的,你通过训练集训练出来的模型,对于类别已知的数据分类还有什么意义?

    • 这你就应该理解,为什么分类要使用测试集了。测试集是用来验证分类器精度的,倘若你不知道测试集的类标签,那你怎么评估你的分类器精度呢?当你认为精度达到要求了,才可以用它处理未知的数据,这些未知的数据就相当于未知类标签的测试集数据。

  11. 楼主你好,有一个地方有疑问,测试数据的特征向量文件是如何得到的,需要加载训练过程中所生成的特征向量,然后得到测试数据的所属类别,特征项及其权重,生成test.txt文件以供测试使用,我想问的是:在得到测试文件中某一个文档的向量类标签时楼主是如何得到的呢? 我看楼主的意思是从AbstractOutputtingQuantizedData类的Integer labelId = getLabelId(label);这一行来得到类别,查看其具体方法实现可知最终是由VectorMetadata类的getlabelId()方法来实现它返回globalLabelToIdMap.get(label)这个值可以得到类别labelId,可是在这个VectorMetadata类中只定义了globalLabelToIdMap这个集合,并没有在他里边加入元素,所以获得的labelId是空值,我想问楼主您是不是后来又加了往globalLabelToIdMap这个集合中添加元素的方法实现呢,具体怎么实现的呢?或者楼主是从哪里得到的这个值呢?因为怎么加载训练向量来得到测试文件的相应类别标签的逻辑我不太明白,所以不知道此处应该怎么实现这个方法,请楼主给予指点

    • 测试阶段文档的类标签,包含在测试文件路径中,和训练阶段的类似,测试文件所在的上级目录名称就是类标签,例如F:\SogouC-UTF8\UTF8\test\ClassFile\C000007。

  12. 您好博主我就是做文本分类的 我想问下你是否有爬虫代码。有的话能否发我一份供我研究

  13. 楼主,我看了您的源码,但在我的MacBook上运行不了ictclas这个分词系统,我改用了ansj,但是那个chi值计算我看不懂楼主的逻辑,楼主能否留个QQ交流下,项目还有一个月,时间非常赶,期待楼主回复。这方面我也是刚刚接触不久。

    • 你就根据那个公式N*(A*D-B*C)*(A*D-B*C)/((A+C)*(A+B)*(B+D)*(C+D)),把对应的量计算出来,套公式计算即可,代码实现在这个类里面:org/shirdrn/document/processor/component/train/FeatureTermVectorSelector.java

  14. 请问楼主,import org.shirdrn.document.processor.utils.SortUtils.Result;出现错误,是不是少了一个类哇??

  15. 请问楼主,Configuration类中的private static final String DEFAULT_CONFIG = “config.properties”;
    config.properties 文件在哪?作用是什么?你的源码中没有呢?

  16. 请问楼主,VectorMetadata类中的方法
    public void addChiTerm(String label, String word, Term term) {
    Map words = chiLabelToWordsVectorsMap.get(label);
    if(words == null) {
    words = new HashMap(1);
    chiLabelToWordsVectorsMap.put(label, words);
    }
    words.put(word, term);
    }
    其中,words.put(word, term);这一语句的作用是什么呢?局部变量方法调用完,不就失效了吗?
    自己JAVA语言不太熟悉,还望楼主抽空恢复下,万分感谢!!

  17. 楼主,您好。刚接触这些东西有很多不懂的,调程序的时候出现2015-04-09 22:45:47.086 [main] INFO processor.config.Configuration – Load properties file: prop=config.properties是什么原因?还有你的语料库是哪个?方便加QQ吗?

  18. 请问楼主,如果用TFIDF计算特征权重,需要被测试或分类的文档有多个,如果是单独来对一个文档分类,可以考虑什么度量方法计算特征权重?

  19. Map<类别, Map<类别, Map<文档 ,Map>>>
    这个结构从表面来看是一个类别只能对应一个key,也就是Map<文档 ,Map>>,但是事实是每个类别都会有很多文档,这究竟是如何实现统计的?

  20. 楼主好,请问:train 出来的term.txt label.txt 有什么用处? 他和tain.txt是什么关系?
    又怎么和测试阶段对应起来呢?
    非常感谢

    • term.txt是特征向量文件,包括对应的数字化编号,label.txt是类标签,包括第一营的类标签数字化编号,这个主要是根据libsvm训练阶段输入的数据格式对应的。如果还不明白,你可以了解一下libsvm在训练的时候,需要什么样的输入数据格式,就知道是怎么样的对应关系了。

  21. 博主我搭好工程了,有个实例化的错误,也定位到具体出错位置是在ReflectionUtils.java文件,但是不知道怎么解决,楼主能帮帮忙吗?这边很着急啊~谢谢了!附上具体出错的函数位置和异常名称。
    @SuppressWarnings(“unchecked”)
    private static T construct(Class clazz, Class baseClass, Object[] parameters)throws InstantiationException, IllegalAccessException,InvocationTargetException
    {
    T instance = null;
    Constructor[] constructors = (Constructor[])clazz.getConstructors();
    for (Constructor c : constructors) {
    if (c.getParameterTypes().length == parameters.length) {
    instance = c.newInstance(parameters); //java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    break;
    }
    }
    return instance;
    }

    • 估计是中科院那个ICTCLAS初始化的问题(License失效),要不你就实现一个自己的,然后不用Java反射,直接在代码中创建一个实例看看。

  22. 楼主,我想问一下,你的语料库是哪个文件,其他的文件在配置文件中都有哪些作用,需要我添加那些文件

      • 请问楼主,这些语料资源是需要我们自己添加的吗?还是不需要添加的?然后,我运行了TrainDocumentProcessorDriver.java 文件显示如下错误,不知道是什么原因,已经将Data的文件替换为2014年的了,谢谢楼主了。
        Exception in thread “main” java.lang.NullPointerException
        at org.shirdrn.document.processor.component.BasicInformationCollector.fire(BasicInformationCollector.java:23)
        at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.run(AbstractDocumentProcessorDriver.java:18)
        at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:47)
        at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:24)
        at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)

  23. 请问楼主,为什么测试的时候,得到的两类文档集的词汇个数总是一样多?
    Sort CHI terms for: label=com, termCount=28956
    Sort CHI terms for: label=adv, termCount=28956
    并且
    for (int i = result.getStartIndex(); i <= result.getEndIndex(); i++) {
    Entry termEntry = result.get(i);
    // merge CHI terms for all labels
    context.getVectorMetadata().addChiMergedTerm(termEntry.getKey(),
    termEntry.getValue());
    }
    此处的开始索引和结束索引也都一样,一般不会这样吧??

    • 是每一类文档集的词汇集合根据卡方排序后得到的开始索引和结束索引都一样

  24. Pingback: 文本挖掘初步 – 对自由文本进行信息抽取 | JetMufffin - 梦想追逐者

  25. 请教一下楼主,我的理解是否正确?

    卡方统计量(CHI) 是为了从每一个类别,所有分词中挑选出来topN。然后合并所有类别的的topN分词,作为最终的词袋模型。

    然后对词袋模型的每一个分词进行svm训练(TF-IDF值)。

    svm阶段(训练-分类)是不需要CHI值的。

  26. 为什么我归一化的特征值全变成1和-1,而且是1很多很多,我是不是搞错了,求解答

  27. 数据格式变成了这样
    1:1 2:1 3:1 4:1 5:1 6:1 7:1 8:1 9:1 10:1 11:1 12:1 13:1 14:1 15:1 16:1 17:1 18:1 19:1 20:1 21:1 22:1 23:1 24:1 25:1 26:1 27:1 28:1 29:1 30:1 31:1 32:1 33:1 34:1 35:1 36:1 37:1 38:1 39:1 40:1 41:1 42:1 43:1 44:1 45:1 46:1 47:1 48:1 49:1 50:1 51:1 52:1 53:1 54:1 55:1 56:1 57:1 58:1 59:1 60:1 61:1 62:1 63:1 64:1 65:1 66:1 67:1 68:1 69:1 70:1 71:1 72:1 73:1 74:1 75:1 76:1 77:1 78:1 79:1 80:1 81:1 82:1 83:1 84:1 85:1 86:1 87:1 88:1 89:1 90:1 91:1 92:1 93:1 94:1 95:1 96:1 97:1 98:1 99:1 100:1 101:1 102:1 103:1 104:1 105:1 106:1 107:1 108:1 109:1 110:1 111:1 112:1 113:1 114:1 115:1 116:1 117:1 118:1 119:1 120:1 121:1 122:1 123:1 124:1 125:1 126:1 127:1 128:1 129:1 130:1 131:1 132:1 133:1 134:1 135:1 136:1 137:1 138:1 139:1 140:1 141:1 142:1 143:1 144:1 145:1 146:1 147:1 148:1 149:1 150:1 151:1 152:1 153:1 154:1 155:1 156:1 157:1 158:1 159:1 160:1 161:1 162:1 163:1 164:1 165:1 166:1 167:1 168:1 169:1 170:1 171:1 172:1 173:1 174:1 175:1 176:1 177:1 178:1 179:1 180:1 181:1 182:1 183:1 184:1 185:1 186:1 187:1 188:1 189:1 190:1 191:1 192:1 193:1 194:1 195:1 196:1 197:1 198:1 199:1 200:1 201:1 202:1 203:1 204:1 205:1 206:1 207:1 208:1 209:1 210:1 211:1 212:1 213:1 214:1 215:1 216:1 217:1 218:1 219:1 220:1 221:1 222:1 223:1 224:1 225:1 226:1 227:1 228:1 229:1 230:1 231:1 232:1 233:1 234:1 235:1 236:1 237:1 238:1 239:1 240:1 241:1 242:1 243:1 244:1 245:1 246:1 247:1 248:1 249:1 250:1 251:1 252:1 253:1 254:1 255:1 256:1 257:1 258:1 259:1 260:1 261:1 262:1 263:1 264:1 265:1 266:1 267:1 268:1 269:1 270:1 271:1 272:1 273:1 274:1 275:1 276:1 277:1 278:1 279:1 280:1 281:1 282:1 283:1 284:1 285:1 286:1 287:1 288:1 289:1 290:1 291:1 292:1 293:1 294:1 295:1 296:1 297:1 298:1 299:1 300:1 301:1 302:1 303:1 304:1 305:1 306:1 307:1 308:1 309:1 310:1 311:1 312:1 313:1 314:1 315:1 316:1 317:1 318:1 319:1 320:1 321:1 322:1 323:1 324:1 325:1 326:1 327:1 328:1 329:1 330:1 331:1 332:1 333:1 334:1 335:1 336:1 337:1 338:1 339:1 340:1 341:1 342:1 343:1 344:1 345:1 346:1 347:1 348:1 349:1 350:1 351:1 352:1 353:1 354:1 355:1 356:1 357:1 358:1 359:1 360:1 361:1 362:1 363:1 364:1 365:1 366:1 367:1 368:1 369:1 370:1 371:1 372:1 373:1 374:1 375:1 376:1 377:1 378:1 379:1 380:1 381:1 382:1 383:1 384:1 385:1 386:1 387:1 388:1 389:1 390:1 391:1 392:1 393:1 394:1 395:1 396:1 397:1 398:1 399:1 400:1 401:1 402:1 403:1 404:1 405:1 406:1 407:1 408:1 409:1 410:1 411:1 412:1 413:1 414:1 415:1 416:1 417:1 418:1 419:1 420:1 421:1 422:1 423:1 424:1 425:1 426:1 427:1 428:1 429:1 430:1 431:1 432:1 433:1 434:1 435:1 436:1 437:1 438:1 439:1 440:1 441:1 442:1 443:1 444:1 445:1 446:1 447:1 448:1 449:1 450:1 451:1 452:1 453:1 454:1 455:1 456:1 457:1 458:1 459:1 460:1 461:1 462:1 463:1 464:1 465:1 466:1 467:1 468:1 469:1 470:1 471:1 472:1 473:1 474:1 475:1 476:1 477:1 478:1 479:1 480:1 481:1 482:1 483:1 484:1 485:1 486:1 487:1 488:1 489:1 490:1 491:1 492:1 493:1 494:1 495:1 496:1 497:1 498:1 499:1 500:1 501:1 502:1 503:1 504:1 505:1 506:1 507:1 508:1 509:1 510:1 511:1 512:1 513:1 514:1 515:1 516:1 517:1 518:1 519:1 520:1 521:1 522:1 523:1 524:1 525:1 526:1 527:1 528:1 529:1 530:1 531:1 532:1 533:1 534:1 535:1 536:1 537:1 538:1 539:1 540:1 541:1 542:1 543:1 544:1 545:1 546:1 547:1 548:1 549:1 550:1 551:1 552:1 553:1 554:1 555:1 556:1 557:1 558:1 559:1 560:1 561:1 562:1 563:1 564:1 565:1 566:1 567:1 568:1 569:1 570:1 571:1 572:1 573:1 574:1 575:1 576:1 577:1 578:1 579:1 580:1 581:1 582:1 583:1 584:1 585:1 586:1 587:1 588:1 589:1 590:1 591:1 592:1 593:1 594:1 595:1 596:1 597:1 598:1 599:1 600:1 601:1 602:1 603:1 604:1 605:1 606:1 607:1 608:1 609:1 610:1 611:1 612:1 613:1 614:1 615:1 616:1 617:1 618:1 619:1 620:1 621:1 622:1 623:1 624:1 625:1 626:1 627:1 628:1 629:1 630:1 631:1 632:1 633:1 634:1 635:1 636:1 637:1 638:1 639:1 640:1 641:1 642:1 643:1 644:1 645:1 646:1 647:1 648:1 649:1 650:1 651:1 652:1 653:1 654:1 655:1 656:1 657:1 658:1 659:1 660:1 661:1 662:1 663:1 664:1 665:1 666:1 667:1 668:1 669:1 670:1 671:1 672:1 673:1 674:1 675:1 676:1 677:1 678:1 679:1 680:1 681:1 682:1 683:1 684:1 685:1 686:1 687:1 688:1 689:1 690:1 691:1 692:1 693:1 694:1 695:1 696:1 697:1 698:1 699:1 700:1 701:1 702:1 703:1 704:1 705:1 706:1 707:1 708:1 709:1 710:1 711:1 712:1 713:1 714:1 715:1 716:1 717:1 718:1 719:1 720:1 721:1 722:1 723:1 724:1 725:1 726:1 727:1 728:1 729:1 730:1 731:1 732:1 733:1 734:1 735:1 736:1 737:1 738:1 739:1 740:1 741:1 742:1 743:1 744:1 745:1 746:1 747:1 748:1 749:1 750:1 751:1 752:1 753:1 754:1 755:1 756:1 757:1 758:1 759:1 760:1 761:1 762:1 763:1 764:1 765:1 766:1 767:1 768:1 769:1 770:1 771:1 772:1 773:1 774:1

  28. 博主您好!我在运行您的代码时也遇到这样的问题:“得到的输出文件还不是直接符合libsvm要求的,libsvm要求每条数据特征向量index按升序排列“,比如:
    7 399:1.3564509446378685 88:1.7009310552655554
    7
    7 108:1.2702909150035386 39:0.7771082550830171 332:1.1277802615663919 56:0.6442579729778579 191:0.9722587458336817
    7 6:2.1920460811942317 56:0.8053224662223224 88:0.8504655276327777 411:2.1920460811942317
    而且有的会好像上面第2行那样只有标签号。

    希望博主能抽空解答,谢谢!

      • 谢谢博主回答!我使用linsvm-3.20,可能版本真的是版本问题。但是我理解的是,为什么得到的train文件是这样的(二分类):
        1
        1 31:8.380821783940931
        1
        1
        1
        1 20:9.965784284662087
        1
        1
        1
        1
        1
        1
        1
        1
        1
        1 46:8.643856189774725
        1
        1 17:7.265344566520995
        中间很多语句都没有提取出特征向量,而且我不明白为什么得到的trem文件里面的词并不是我训练文本中的:
        液态 1
        毒品 2
        石头 3
        委员 4
        趾头 5
        拷纱 6
        雌黄 7
        水源 8
        妇女 9
        志士 10
        水漂 11
        水平 12
        水牛 13
        全胜 14
        同志 15
        实证 16
        食品 17
        传说 18
        模式 19
        猕猴 20
        请问博主该如何解决这两个问题?

  29. 请教博主一个问题,我在二元分类的时候训练出来的模型对负样本识别准确率90%+,但对正样本识别准确率只有20%几,请问这是什么原因?因为是选择的特征空间不均匀吗?

      • 楼主,能帮我答疑一下吗,同一个数据集,用贝叶斯分类器能有80%的正确率,用这个SVM就只有60几,是什么原因呢?是特征选择的问题吗

        • 影响分类准确率的因素很多,我在5折交叉验证时准确率78%。但另外找测试集效果差一些,目前找到得原因是因为我计算tf的方法和博主的不一样。

      • 博主,您在计算tf的时候貌似疏忽了,计算词频应该除以文档词的总数,但是您把词放入一个map中就会把重复的词的个数算少了,这样计算出来的词频和会大于1.可以在getTermCount那里修改一下。

  30. 楼主,这个地方是不是错了
    FeatureTermVectorSelector类下:
    计算B时:docCountContainingWordNotInLabel += entry.getValue().size();
    应该是:docCountContainingWordNotInLabel += labelledEntry.getValue().size();

    请确认,多谢!

    • 我看了下,代码中计算是不对,多谢指正。
      抽时间,我把代码更正一下,文章也更新一下。
      大家有问题可以在评论这留言,我收集大家的意见,修正代码、改正文章,或者考虑对libsvm不同版本,代码也提供相应的版本。

  31. 博主,还有一个问题请教,对计算出来的CHI值堆排序的时候,result.setStartIndex(array.length – i); 这个StartIndex为什么只有一个,如果分析完文档后总得词少于3000个,这时候这个StartIndex是不是应该等于0

    麻烦,博主

  32. 这种归一化在tf*idf的情况下不对吧,按照0-1归一化不就是变成去掉idf了吗,其实只有tf了,比如一个常见词idf很低,一个不常见词idf很高,但是如果一个文档中出现了这两个词,并且出现的频次都是单文档出现的最高频次,那么这两个词对应的特征值都是1,没有idf的区分度体现了吧。
    我理解应该是对单个特征向量进行l2归一化。

    • 有没有区分度是在选特征的时候进行的工作,这里归一化只是让量纲统一吧

    • 这里面使用TF-IDF不是为了选择特征词向量,而只是量化的手段,如果还有其他更好的方式,当然也可以使用。

  33. 楼主,你好,请问一下是不是少了org.shirdrn.document.processor.utils.SortUtils.Result这个类啊?

      • 楼主,你好,为什么我用libsvm结果只有10%,之前的步骤都是对的,不知道用java怎么调用libsvm?

        • libsvm包里面默认的package下自带了几个Java类:svm_train,svm_scale,svm_predict,你可以完全使用这几个类去操作,而不用使用命令行,在Windows下使用命令可能会出现字符集问题,所以我上面混用了命令和Java代码的方式去处理的。

  34. 求博主指点,我自己实例化了ICTALAS,可是一直出现初始化错误,花了好长时间也没能解决怎么回事。不知道您方便加QQ吗?646358868

    Exception in thread “main” java.lang.UnsatisfiedLinkError: kevin.zhang.NLPIR.NLPIR_Init([BI)Z
    at kevin.zhang.NLPIR.NLPIR_Init(Native Method)
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:29)

    • 代码里面默认使用的ICTCLAS的licence已经过期了,就不要使用默认这个了,可以到官网下载一个最新的尝试一下。或者,将Analyzer换成其他的,比如Lucene内置支持的一些Analyzer都可以进行分词。

  35. [main] INFO processor.config.Configuration – Load properties file: prop=config.properties
    [main] INFO processor.analyzer.IctclasAnalyzer – Process document: file=E:\语料库\SogouC.reduced\Reduced\C000010\10.txt
    [word=鄹, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=撅, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=皆, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=800, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=站, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=的, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=桔, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=硷, freq=3, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=讯, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=HINK, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=秸, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=记, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=沂, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=拷, freq=81, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=讹, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=轿, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=涧, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=劫, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=品, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=�4, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=节, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=拇, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=(, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=津, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=), freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=★, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=匡, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=竭, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=到, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=1, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=斤, freq=67, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=洹, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=瞥, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=呓, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=·, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=高, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=绞, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=系, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=街, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=T, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=达, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=角, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=憋, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=THINKPAD, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=全, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=谋, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=2%, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=凤, freq=2, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=X, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=Z, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=絋, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=锟, freq=83, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=元, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=�, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=伙, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=不, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=г, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    [word=锏, freq=1, tf=0.0, idf=0.0, tf-idf=0.0, chi=0.0]
    楼主 我是刚刚接触文本分类 不太会java 能不能请教楼主下 QQ:1358981527

  36. 博主 请问 计算tf值时 getTermCount这里怎么改 计算TF值那部分需要改吗 我测试的结果只有29% 应该从哪改呢

  37. #
    # An unexpected error has been detected by Java Runtime Environment:
    #
    # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x5bef7415, pid=7720, tid=7816
    #
    # Java VM: Java HotSpot(TM) Client VM (11.3-b02 mixed mode windows-x86)
    # Problematic frame:
    # C [NLPIR.DLL+0x7415]
    #
    # An error report file with more information is saved as:
    # D:\Graduate study\Myeclipse\myeclipse_workplace\document-processor\hs_err_pid7720.log
    #
    # If you would like to submit a bug report, please visit:
    # http://java.sun.com/webapps/bugreport/crash.jsp
    # The crash happened outside the Java Virtual Machine in native code.
    # See problematic frame for where to report the bug.
    博主,不知道为什么会出现这个问题,这个该怎么解决啊?

    • 你这个是JVM崩溃了,可能是调用ICTCLAS造成了非法访问内存地址,应该是ICTCLAS的版本与操作系统/JVM不兼容造成的。找一个兼容的版本的ICTCLAS,或者不使用这个分词工具。

      • 2015-07-29 15:49:00.984 [main] INFO processor.config.Configuration – Load properties file: prop=config-train.properties
        2015-07-29 15:49:00.986 [main] INFO processor.common.FDMetadata – Train dataset file extension: name=.txt
        2015-07-29 15:49:00.986 [main] INFO processor.common.FDMetadata – Vector input root directory: outputDir=F:\SogouC-UTF8\UTF8\train\ClassFile
        2015-07-29 15:49:00.986 [main] INFO processor.common.FDMetadata – Vector output directory: outputDir=C:\Users\Administrator\Desktop\vector
        2015-07-29 15:49:00.986 [main] INFO processor.common.FDMetadata – Vector output file: outputFile=train.txt
        2015-07-29 15:49:00.989 [main] INFO processor.component.DocumentWordsCollector – Analyzer class name: class=org.shirdrn.document.processor.analyzer.IctclasAnalyzer
        2015-07-29 15:49:01.094 [main] INFO processor.component.DocumentWordsCollector – Load filter classes: classNames=org.shirdrn.document.processor.filter.LexicalCategoryFilter,org.shirdrn.document.processor.filter.SingleWordTermFilter,org.shirdrn.document.processor.filter.StopwordsTermFilter
        2015-07-29 15:49:01.095 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.LexicalCategoryFilter@a1d1f4
        2015-07-29 15:49:01.096 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.SingleWordTermFilter@1be0f0a
        2015-07-29 15:49:01.096 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.StopwordsTermFilter@157aa53
        2015-07-29 15:49:01.100 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000008
        2015-07-29 15:49:01.106 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000008, docCount=1990
        2015-07-29 15:49:01.106 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000010
        2015-07-29 15:49:01.109 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000010, docCount=1990
        2015-07-29 15:49:01.109 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000013
        2015-07-29 15:49:01.112 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000013, docCount=1990
        2015-07-29 15:49:01.112 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000014
        2015-07-29 15:49:01.114 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000014, docCount=1990
        2015-07-29 15:49:01.114 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000016
        2015-07-29 15:49:01.117 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000016, docCount=1990
        2015-07-29 15:49:01.117 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000020
        2015-07-29 15:49:01.120 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000020, docCount=1990
        2015-07-29 15:49:01.120 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000022
        2015-07-29 15:49:01.122 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000022, docCount=1990
        2015-07-29 15:49:01.123 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000023
        2015-07-29 15:49:01.125 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000023, docCount=1990
        2015-07-29 15:49:01.125 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000024
        2015-07-29 15:49:01.128 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000024, docCount=1990
        2015-07-29 15:49:01.128 [main] INFO processor.component.BasicInformationCollector – Total documents: totalCount= 17910
        2015-07-29 15:49:01.128 [main] INFO processor.component.DocumentWordsCollector – Collect words for: label=C000008
        2015-07-29 15:49:01.131 [main] INFO processor.component.DocumentWordsCollector – Prepare to analyze 1990 files.
        2015-07-29 15:49:01.131 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\10.txt
        2015-07-29 15:49:01.159 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\100.txt
        2015-07-29 15:49:01.163 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\1000.txt
        2015-07-29 15:49:01.167 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\1001.txt
        2015-07-29 15:49:01.170 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\1002.txt
        2015-07-29 15:49:01.173 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\1003.txt
        2015-07-29 15:49:01.176 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\1004.txt
        2015-07-29 15:49:01.184 [main] INFO processor.analyzer.NlpirTest – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\1005.txt ——一直到这都执行了啊
        #
        # An unexpected error has been detected by Java Runtime Environment:
        #
        # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0×61547415, pid=14628, tid=13152
        #
        # Java VM: Java HotSpot(TM) Client VM (11.3-b02 mixed mode windows-x86)
        # Problematic frame:
        # C [NLPIR.DLL+0x7415]
        #
        # An error report file with more information is saved as:
        # D:\Graduate study\Myeclipse\myeclipse_workplace\document-processor\hs_err_pid14628.log
        #
        # If you would like to submit a bug report, please visit:
        # http://java.sun.com/webapps/bugreport/crash.jsp
        # The crash happened outside the Java Virtual Machine in native code.
        # See problematic frame for where to report the bug.
        #
        可是博主,,前面的输出都没有问题啊

  38. 非常感谢博主的分享,这里有一个关于libsvm-dp的问题,跑libsvm-dp-driver的TrainDocumentProcessorDriver程序得到train文件在固定的一些位置的结果是空,比如我在config-train.properties的参数feature.each.label.kept.term.percent=0.99后得到train文件7、19、30为空,我在删除相应文档后,这几个位置依然为空。这是为什么呢?谢谢!

    • 程序生成的数据文件你最好不要手动去改,而且,你说的这个为空,可能是因为一行太长,你感觉它是空的,实际上不是空的

  39. 楼主,请问这个maven构建的工程怎么运行啊,训练文本和测试文本分别放在哪里啊。对maven不了解。

  40. 博主,您有已标记的情感微博语料吗?一直找不到合适的语料,也不知道您有这方面的语料没,有的话可以发给我一份吗?谢谢了~15319562912@qq.com

  41. 膜拜。。虽然有很多问题也没搞懂,有种相见恨晚的感脚。。感谢大神!

  42. 楼主好,有两个问题,麻烦解答:
    1、LibSVM Document Preprocessing Driver 子项目中资源库没有这个jar:libsvm-dp-analysis-lucene 2、train -h 0 -t 0 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt
    这一步是训练什么?我理解是用训练集预料 对文本进行分类了,但是待分类的文本在哪?

    3、测试集在这里是什么概念?因为做预测时候的语料已经发生了变化?预测带分类文章不变?如果是这样文章在哪呢?

    • 楼主好,有三个问题,麻烦解答:
      1、LibSVM Document Preprocessing Driver 子项目中资源库没有这个jar:libsvm-dp-analysis-lucene

      2、train -h 0 -t 0 C:\\Users\\thinkpad\\Desktop\\vector\\train-scale.txt C:\\Users\\thinkpad\\Desktop\\vector\\model.txt
      这一步是训练什么?我理解是用训练集预料 对文本进行分类了,但是待分类的文本在哪?

      3、测试集在这里是什么概念?因为做预测时候的语料已经发生了变化?预测带分类文章不变?如果是这样文章在哪呢?

      • 1、你可能要熟悉一下Maven,libsvm-dp-analysis-lucene是整个project的一个module,是可以进行统一构建的;
        2、这个是基于训练集(已知分类的语料)进行训练,得到分类模型;
        3、测试集是对分类模型进行验证的。

  43. 楼主您好,我想问问,既然libsvm是可以用来做文本分类,那我可以不可以这样用:我要在一个文件中识别出机构名,那么分词完成以后,我就可以把它分为机构名和非机构名是吗?我还有挺多的问题的,希望您不要嫌麻烦,多给我指导一下!我的QQ:使用训练数据集训练分类器

  44. 博主好,首先谢谢您的CHI算法,帮助我节省了很多时间,现在我用您的算法挑选出来的词,在SPARK上实现了文本分类,调用了SPARK上的朴素贝叶斯算法,现在正确率在78左右徘徊,怎么也上不去了,是不是,对于CHI+TFIDF算法+朴素贝叶斯算法,这个正确率就到极限了,是不是应考虑换个分类算法了,求博主指教

    • 你可以替换一下CHI选择特征向量的算法,CHI算法中,一些低频词会具有很高的权重,尤其是当你不能很好地去噪的时候,使用CHI选择的特征向量就会掺杂很多噪声词,自然会影响分类模型的准确性。

  45. 请问语料库的样本和测试集放到什么目录,没有在代码中找到读取样本文件目录的语句

  46. processor.dataset.chi.term.vector.file=C:\\Users\\Shirdrn\\Desktop\\vector\\terms.txt
    processor.dataset.file.extension=.txt
    processor.dataset.label.vector.file=C:\\Users\\Shirdrn\\Desktop\\vector\\labels.txt
    请问一下config_test.properties里面这三项是做什么的?
    第二个要怎么改

    报错了:
    2015-11-10 21:29:02.511 [main] INFO processor.config.Configuration – Load properties file: prop=config-train.properties
    2015-11-10 21:29:02.515 [main] INFO processor.common.FDMetadata – Train dataset file extension: name=
    Exception in thread “main” java.lang.RuntimeException: File has been existed: C:\Users\cccc\Desktop\vector\terms.txt
    at org.shirdrn.document.processor.utils.CheckUtils.checkFile(CheckUtils.java:24)
    at org.shirdrn.document.processor.common.FDMetadata.(FDMetadata.java:40)
    at org.shirdrn.document.processor.common.Context.(Context.java:16)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:38)
    at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:24)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)

    • Train时,会重新生成terms.txt文件,你删除这个文件,再重新运行即可,这个文件是特征词文件,后续在Test阶段会用到。

  47. 2015-11-11 00:23:40.436 [main] INFO processor.config.Configuration – Load properties file: prop=config-train.properties
    2015-11-11 00:23:40.440 [main] INFO processor.common.FDMetadata – Train dataset file extension: name=
    2015-11-11 00:23:40.441 [main] INFO processor.common.FDMetadata – Vector input root directory: outputDir=F:\SogouC\Reduced\test\ClassFile
    2015-11-11 00:23:40.442 [main] INFO processor.common.FDMetadata – Vector output directory: outputDir=C:\Users\cccc\Desktop\vector
    2015-11-11 00:23:40.442 [main] INFO processor.common.FDMetadata – Vector output file: outputFile=train.txt
    2015-11-11 00:23:40.446 [main] INFO processor.component.DocumentWordsCollector – Analyzer class name: class=org.shirdrn.document.processor.analyzer.IctclasAnalyzer
    null
    2015-11-11 00:23:40.450 [main] INFO processor.component.DocumentWordsCollector – Load filter classes: classNames=org.shirdrn.document.processor.filter.LexicalCategoryFilter,org.shirdrn.document.processor.filter.SingleWordTermFilter,org.shirdrn.document.processor.filter.StopwordsTermFilter
    2015-11-11 00:23:40.455 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.LexicalCategoryFilter@506e4e82
    2015-11-11 00:23:40.456 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.SingleWordTermFilter@7894b886
    2015-11-11 00:23:40.458 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.StopwordsTermFilter@82a5e75
    2015-11-11 00:23:40.465 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000008
    2015-11-11 00:23:40.482 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000008, docCount=1990
    2015-11-11 00:23:40.482 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000010
    2015-11-11 00:23:40.497 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000010, docCount=1990
    2015-11-11 00:23:40.498 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000013
    2015-11-11 00:23:40.512 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000013, docCount=1990
    2015-11-11 00:23:40.512 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000014
    2015-11-11 00:23:40.527 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000014, docCount=1990
    2015-11-11 00:23:40.528 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000016
    2015-11-11 00:23:40.542 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000016, docCount=1990
    2015-11-11 00:23:40.543 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000020
    2015-11-11 00:23:40.553 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000020, docCount=1990
    2015-11-11 00:23:40.553 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000022
    2015-11-11 00:23:40.560 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000022, docCount=1990
    2015-11-11 00:23:40.561 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000023
    2015-11-11 00:23:40.565 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000023, docCount=1990
    2015-11-11 00:23:40.565 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000024
    2015-11-11 00:23:40.570 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000024, docCount=1990
    2015-11-11 00:23:40.570 [main] INFO processor.component.BasicInformationCollector – Total documents: totalCount= 17910
    2015-11-11 00:23:40.571 [main] INFO processor.component.DocumentWordsCollector – Collect words for: label=C000008
    2015-11-11 00:23:40.576 [main] INFO processor.component.DocumentWordsCollector – Prepare to analyze 1990 files.
    Exception in thread “main” java.lang.NullPointerException
    at org.shirdrn.document.processor.component.DocumentWordsCollector.analyze(DocumentWordsCollector.java:75)
    at org.shirdrn.document.processor.component.DocumentWordsCollector.fire(DocumentWordsCollector.java:63)
    at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.run(AbstractDocumentProcessorDriver.java:18)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:47)
    at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:24)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)
    博主,这个空指针指向哪里

  48. 2015-11-11 14:06:17.231 [main] INFO processor.config.Configuration – Load properties file: prop=config-train.properties
    2015-11-11 14:06:17.257 [main] INFO processor.common.FDMetadata – Train dataset file extension: name=
    2015-11-11 14:06:17.259 [main] INFO processor.common.FDMetadata – Vector input root directory: outputDir=F:\SogouC\Reduced\test\ClassFile
    2015-11-11 14:06:17.259 [main] INFO processor.common.FDMetadata – Vector output directory: outputDir=C:\Users\cccc\Desktop\vector
    2015-11-11 14:06:17.259 [main] INFO processor.common.FDMetadata – Vector output file: outputFile=train.txt
    2015-11-11 14:06:17.262 [main] INFO processor.component.DocumentWordsCollector – Analyzer class name: class=org.shirdrn.document.processor.analyzer.IctclasAnalyzer
    null
    2015-11-11 14:06:17.268 [main] INFO processor.component.DocumentWordsCollector – Load filter classes: classNames=org.shirdrn.document.processor.filter.LexicalCategoryFilter,org.shirdrn.document.processor.filter.SingleWordTermFilter,org.shirdrn.document.processor.filter.StopwordsTermFilter
    2015-11-11 14:06:17.273 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.LexicalCategoryFilter@e1c3a7
    2015-11-11 14:06:17.274 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.SingleWordTermFilter@65b07961
    2015-11-11 14:06:17.275 [main] INFO processor.component.DocumentWordsCollector – Added filter instance: filter=org.shirdrn.document.processor.filter.StopwordsTermFilter@102a648c
    2015-11-11 14:06:17.283 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000008
    2015-11-11 14:06:17.303 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000008, docCount=1990
    2015-11-11 14:06:17.304 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000010
    2015-11-11 14:06:17.320 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000010, docCount=1990
    2015-11-11 14:06:17.320 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000013
    2015-11-11 14:06:17.335 [main] INFO processor.component.BasicInformationCollector – Put document count: label= C000013, docCount=1990
    2015-11-11 14:06:17.335 [main] INFO processor.component.BasicInformationCollector – Add label: label=C000014
    中间的NULL是怎么回事,为什么获取不到

    • 那个分词用的是中科院的ICTCLAS分词器,在Windows下需要使用动态链接库,如果得到NULL,很有可能你用的ICTCLAS已经不能使用了,换换别的版本的试试,注意要能够兼容的。

  49. C:\Users\cccc\Desktop\svm-test>svm-train.exe -c 128 -g 0.5 train-scale.txt model.model
    Wrong input format at line 2
    已经运行了svm_scale也就是归一化了,但是一运行svm_train来生成模型就说输入格式不对,train-scale.txt的内容:

    7 748:0.0666667 791:0.0266667 1139:0.666667
    7 791:0.06 806:0.0227273 1182:0.142857 283:0.0357143
    7 336:0.00988701 728:0.0119048 791:0.0133333 806:0.181818 573:0.166667 283:0.0238095
    7 615:0.0555556 903:0.666667 336:0.00988701 186:0.0833333 791:0.0266667 875:0.0392157
    7 38:0.0222222 674:0.111111 177:0.222222 791:0.00888889 336:0.0131827 975:0.0444444 1044:0.166667 1132:0.111111 1182:0.031746
    7 50:0.25 283:0.0714286
    7

  50. 你使用的中科院的ICTCLAS分词器为什么一直是初始化失败呢…..
    费解,而且我下载的张华平博士新版的分词器也是报“初始化失败”,快无语了

    • 郁闷了一天,昨天晚上把代码翻来覆去的看了几次也没看出来那里有毛病,早上在文件夹里点了点,原来如此,分词包里有一个错误日志,里面的内容为“Not valid license or your license expired! Please feel free to contact pipy_zhang@msn.com!”,我的个去,原来如此,下载最新的NLPIR2015,将data文件夹换掉,搞定…….

  51. 首先非常感谢您提供的分析和代码,对这一块内容有了比较清晰的了解。
    有几个问题想向您请教:
    (1)训练后的train.txt文本确实出现有些文本只有标签,但是没有提取到任何特征词,这是正常现象吗?
    (2)采用同样的语料库,进行参数修改后,文本的交叉验证效果仍然很低(38%),是否和特征选择的数目有关(实验提取到的特征为1940个)?

    • 1. 估计是分词器没有把实际的词分出来,完全过滤掉了吧,可能需要调整分词器;
      2. 主要应该不是和特征词的数目有关,看看质量如何,好的特征词能够在多类别之间具有很强的区分度。

  52. 我用的是2015ICTCLAS,但是NLPIR_JNI.dll我只找到了2013版本的。运行TestIctclasAnalyzer时,case一直不过,报如下错误:
    java.lang.UnsatisfiedLinkError: kevin.zhang.NLPIR.NLPIR_Init([BI)Z
    at kevin.zhang.NLPIR.NLPIR_Init(Native Method)
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:28)
    at org.shirdrn.document.processor.analyzer.TestIctclasAnalyzer.analyze(TestIctclasAnalyzer.java:18)

    • 估计是ICTCLAS这个的问题,可能是需要授权才能使用,例如出现这样的提示:“Not valid license or your license expired! Please feel free to contact pipy_zhang@msn.com!”,可能看看前面评论,有个朋友ir解决了,尝试一下吧

  53. 楼主,你好!我是用svm做图片分类的,麻烦咨询你一个问题,我的测试集数据是下面这个样子的
    1 1:14913.957092798 2:7896.04045379089 3:-1061.1491363413 4:-382.138812764711 5:1446.28707691428 6:-31.1531150939927 7:3694.87928741976 8:-6591.34581945891 9:-644.815277795345 10:-3218.21981020529 11:559.539457140553 12:-4558.88017159254 13:-12503.0973322583 14:794.209852897899 15:-726.798770543774 16:1289.80216918397 17:-166.238253753923 18:136.619321560288 19:-33.1199994731981 20:181.084421425542 21:-82.473834395538 22:107.704295654048 23:-128.380880391139 24:-112.522487486805 25:63.9735167923671 26:10.4105935497987 27:-126.896635793304 28:57.1986821218359 29:28.7311584978883 30:-41.818549569894 31:-8.12033709456071 32:-1.8244968749806 33:37.0552464005184 34:70.2325090626897 35:24.7054532025709 36:-3.09889237523263 37:-9.18186295889286 38:-12.9067449010003 39:-4.20910390175767 40:-4.73241780833192 41:-11.2236692319955 42:-7.91589609633522 43:-.571026628884907 44:-3.32950350507436 45:-944.33398992693
    1 1:-17024.0646232293 2:2435.49967364808 3:4993.26829787777 4:1729.93773167277 5:-1448.26658030495 6:4299.45298253814 7:19391.2015919613 8:10258.5951102556 9:-4371.98793143573 10:-6177.90257001439 11:-4478.96527271122 12:403.604712769 13:42.8488564114565 14:781.112162214088 15:183.217214004622 16:-113.72718481702 17:-183.053007727818 18:24.3397103756684 19:131.554177413808 20:27.1651802135822 21:-164.125947631425 22:-3.56475328541521 23:-46.2428488872647 24:38.4793287052369 25:-90.5042744631281 26:-3.50606086141062 27:-4.21249411331253 28:-7.50009648091712 29:-5.39346423339145 30:-3.3042479516165 31:14.1760532817828 32:-23.9865371478939 33:-3.19803731258065 34:9.87503620630448 35:3.22094219165754 36:7.70993162544045 37:-9.71267102097492 38:-5.56511402203097 39:-.288692244055353 40:-1.48385245647067 41:1.44292066152859 42:.537700212347334 43:-1.59526285670102 44:-1.23694346292359 45:-902.729407094353
    然后在用checkdata.py检查数据格式的时候老是会出现乱码的现象,譬如:label1 is not a number之类,麻烦您帮忙看下为什么会出错?

    • 估计是文件的字符集问题,你用程序生成的这个数据存到文件中时,指定UTF-8编;或者拷贝到记事本里面,保存的时候选择“另存为”,不要使用默认的ANSI,选择UTF-8字符集,然后再调用checkdata.py试试。

  54. 您好,我现在正在研究这方面,能不能把您的测试集。、训练集发我一份,刚接触,不太懂,需要学习

  55. 我想请问一下楼主,你的语料库用的是你给的链接 搜狗语料库 中的全网新闻数据吗?如果不是的话,是哪一个呢?你用的是完整版的语料库吗?完整版的下载不成功啊。。。刚接触文本分类,在慢慢摸索,希望能得到楼主的回答,感谢。

  56. 博主您好,非常感谢您的无私分享,学习了。
    有个问题,关于计算word TF值,您写的DocumentTFIDFComutation.java文件,
    int termCount = context.getVectorMetadata().getTermCount(label, doc);
    double tf = MetricUtils.tf(freq, termCount);
    这两行代码是计算tf值的,word的词频freq我没有疑问,但是termCount似乎不太合理,我跟了这个方法context.getVectorMetadata().getTermCount(label, doc),
    public int getTermCount(String label, String doc) {
    int size = 0;
    // avoid empty file
    if(termTable.get(label) != null
    && termTable.get(label).get(doc) != null) {
    size = termTable.get(label).get(doc).size();
    }
    return size;
    }
    您这个方法得到的结果应该是文档的不同特征词的个数吧,并非是该文档包含的特征词总数,比如一篇文档经过降维得到的词是(“微笑”,“微笑”,“微笑”,“太阳”),那么“微笑”的freq=3,termCount按博主的方法得到的却是2,而实际是不是应该等于4 ?

      • while(termsIter.hasNext()) {
        Entry termEntry = termsIter.next();
        String word = termEntry.getKey();
        // check whether word is contained in CHI vector
        if(context.getVectorMetadata().containsChiWord(word)) {
        GeiRiTerm term = termEntry.getValue();
        int freq = term.getFreq();
        int termCount = context.getVectorMetadata().getTermCount(label, doc);

        double tf = MetricUtils.tf(freq, termCount);
        int totalDocCount = context.getVectorMetadata().getTotalDocCount();
        int docCountContainingTerm = context.getVectorMetadata().getDocCount(term);

        double idf = MetricUtils.idf(totalDocCount, docCountContainingTerm);
        termEntry.getValue().setIdf(idf);
        termEntry.getValue().setTf(tf);
        termEntry.getValue().setTfidf(MetricUtils.tfidf(tf, idf));
        LOG.debug(“Term detail: label=” + label + “, doc=” + doc + “, term=” + term);
        } else {
        // remove term not contained in CHI vector
        // termsIter.remove();
        LOG.debug(“Not in CHI vector: word=” + word);
        }
        把else{
        }中的termsIter.remove()注销掉

  57. 2015-12-28 14:22:30.263 [main] INFO processor.analyzer.IctclasAnalyzer – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\998.txt
    2015-12-28 14:22:30.263 [main] INFO processor.analyzer.IctclasAnalyzer – Process document: file=F:\SogouC-UTF8\UTF8\train\ClassFile\C000008\999.txt
    2015-12-28 14:22:30.263 [main] INFO processor.component.DocumentWordsCollector – Analyzed files: count=8000
    2015-12-28 14:22:30.263 [main] INFO processor.component.DocumentWordsCollector – STAT: totalDocCount=16000
    2015-12-28 14:22:30.263 [main] INFO processor.component.DocumentWordsCollector – STAT: labelCount=2
    2015-12-28 14:22:30.263 [main] INFO processor.component.DocumentWordsCollector – STAT: label=C000008, docCount=8000
    2015-12-28 14:22:30.263 [main] INFO processor.component.DocumentWordsCollector – STAT: label=C000007, docCount=8000
    2015-12-28 14:22:30.263 [main] INFO component.train.FeatureTermVectorSelector – Compute CHI for: label=C000007
    2015-12-28 14:28:29.529 [main] INFO component.train.FeatureTermVectorSelector – Compute CHI for: label=C000008
    您好,我想请问一下我的代码跑完就是这个没有出terms.txt 和label.txt的文件,请问是怎么回事呢?

  58. 我使用的是用maven重构的版本,可以正确生成train.txt 但是生成test.txt时发现文本为空,查询控制台日志发现 2016-02-25 16:25:03.550 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000024, labelId=null
    断点跟踪源码后发现,语料库/UTF8/train/ClassFile下是C000007、C000008 而/UTF8/test/ClassFile是C0000010、C0000013之类的。程序里面是根据文件名来定义标签,所以根据train的label是无法找到test里面的label的 所以无法生成labelid 这个时候我是要把test语料里面的文件夹都命名为c000007和c000008吗 初学svm 还请指导下

  59. 老师您讲的很清楚,我还想问一个问题就是在用libsvm的时候交叉验证精度和model实际预测精度有什么关系么?交叉验证精度是在训练集上的平均精度,用来寻找最优参数的,所以应该跟验证机预测精度没什么关系吧?交叉训练精度很高预测精度也有可能很低对吧?

    • 为了预测既然要进行训练,那么训练集数据一定程度上是有一定代表性的,如果没有代表性还拿它训练模型干什么。交验验证就是为了防止一些噪声数据对模型精度的影响,而取一个平均值,降低这种干扰。模型精度是对基于训练集训练得到结果的准确程度(训练之前假装不知道分类结果),如果在训练集上预测准确率很高,我们其实也希望模型对于一些未知类标签的数据进行预测,也能很准确。也就是说,在对未知类标签进行预测之前,你是不知到底能不能预测正确。

      • 老师您好,非常感谢您的回答,我的毕设中要使用到libsvm这个分类器,您的讲解让我对libsvm有了一个初步的了解,但是我还有一个问题想请教您一下,还是交叉验证精度这块儿,我听学长学姐说一般情况下交叉验证精度是用做训练的终止条件防止过拟合的,那在这里我们做文本分类的时候这个交叉验证精度也是做这个的吗?我之前一直以为这个交叉验证精度也是作为一个标准来判断模型的准确度的,感觉好困惑啊!

          • 哦,soga,确实是的。我还想问一下老师processor.each.label.kept.term.count这个参数是在选特征向量的时候计算完每一类的CHI值后用到的限制选择词频到哪个界限以上的词是么?这个参数设置不同的值最后会选出不同的term值,那如果每一个文本都很短短到只有一句话的时候这个参数值是不是设的小一点比较好?这样计算出的特征向量比较多我做实验的时候?

  60. 您好!最近在做毕业设计 在您这里学习了很多。
    想请教一下您 shirdrn/libsvm-dp这个项目应该如何使用或者构建?

  61. 您好,我在阅读您的代码的时候,在分词,去停用词这一块,没有发现去停用词的代码。还有就是,假设我有一个不知道类型的文档,需要预测它的分类,怎么在您的这个基础上实现?

  62. 我按楼主的流程搭好工程,没问题,感谢楼主无私奉献。但是我的特征数量总是非常少,一般在1000以下,而且感觉特征选取不够准确,很多高频词汇没有选择进来,分类精度一直在50%,我想让CHI值结合词频把高频词汇加进来,请问楼主这个要怎么解决呢

    • 你使用的是libsvm-dp的代码,可以替换libsvm-dp-measure这个module里面的默认实现ChiFeatureTermSelector,实现自己的选择特征词的逻辑。

    • 您好 不知道可不可以讨论一下? 我也在跑这个代码
      qq 309170603
      谢谢您啦

  63. 请问测试集的结果应该是什么样呀?为什么有的只有类别编号没有词语权重呢?像这样的:
    4
    4
    4
    4
    6 830:0.01588171552850254 292:0.020773027277325152 272:0.031159540915987726 239:0.012164142874509424 710:0.016974494119913267 269:0.01705271759512024 361:0.01729177438383688 60:0.01753762899592935 700:0.01483560579723779 164:0.017621141514957583 753:0.02967121159447558 786:0.01483560579723779 838:0.01770545319684733 851:0.07082181278738932 583:0.0178765203774932 398:0.01796329960262666 163:0.016660305975507675 885:0.025575393798427205 630:0.013140948194771988 167:0.023636616954412694 363:0.020672847587380402 36:0.047654818555431036 408:0.01888168010557537 612:0.02327320577447228 797:0.01731051101372495 816:0.030493738759663203 834:0.010900254603468539 313:0.02372076742398136 934:0.040199706031224976 517:0.10521193707043217 227:0.047889495770209324 306:0.018123756497591223 815:0.02624425332014959 908:0.022095583102269046 873:0.022095583102269046 161:0.01942762387151865 474:0.05441823114913371 688:0.0200032571714155 1036:0.040915753305168064 972:0.05692984181755527 701:0.02960351774512874 683:0.030836997651175767 6:0.1914020543866082 520:0.3895199703306413 577:0.027570594844424208 590:0.03274725414284153 606:0.06549450828568305 620:0.03303964034054546 639:0.03303964034054546 655:0.10001188427408358 672:0.06667458951605572 887:0.13456144429603972 475:0.10184696471947961 459:0.06789797647965307 448:0.06916709753534751 986:0.31418827795537574 1041:0.050951692795114946 1020:0.029391294503961655 1078:0.06731636669586029 1056:0.10190338559022989 858:0.17634776702376995 842:0.05878258900792331 840:0.05878258900792331 1005:0.24669598120940614 238:0.027004397181410924 266:0.04805765867715704
    还有特征向量选择完后怎么进行使用libsvm工具包训练文本分类器呢?下面讲得好简单而且是用命令做的,能不能想前面那部分详细一点呢?谢谢楼主!

  64. 博主你好,我在测试的时候,遇到个问题,如果test文件是单一输入的话,分类结果跟实际相差很大;test文件越多,分类就越准确,为什么会与输入也有关系呢?谢谢

  65. 楼上各位。是不是这样的过程,先使用训练样本训练生成分类器,然后再将测试样本输入分类器就可以分类了?那么,使用训练样本训练生成分类器这个过程是怎样的呢?

    • 我就是想问,训练阶段利用各种分类算法对转化后的文本向量估计模型,这样一个过程是怎样的??

  66. Exception in thread “main” java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:43)
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:30)
    at org.shirdrn.document.processor.component.DocumentWordsCollector.(DocumentWordsCollector.java:30)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:42)
    at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:24)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)
    Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.shirdrn.document.processor.utils.ReflectionUtils.construct(ReflectionUtils.java:76)
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:41)
    … 5 more
    Caused by: java.lang.NullPointerException
    at org.shirdrn.document.processor.analyzer.AbstractDocumentAnalyzer.(AbstractDocumentAnalyzer.java:49)
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:25)
    … 11 more

    有人会解决这个方法么,求教QQ1398384979,重谢

  67. 感谢大神的分享,仔细阅读了您的代码,我先运行了train在运行test,运行train成功了,生成了label.txt,train.txt,term.txt,但是运行test的时候,生成的test.txt,为什么是空的呢,控制台给出的信息是:6-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000013, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000024, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000023, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000014, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000020, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000022, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000010, labelId=null
    2016-06-01 22:15:18.666 [main] WARN AbstractOutputtingQuantizedData – Label ID can not be found: label=C000016, labelId=null
    2016-06-01 22:15:18.667 [main] INFO AbstractOutputtingQuantizedData – Finished: outputVectorFile=test.txt

    原因是map里面没有labelid,我查看了源代码的确是没有添加C000013等的id,请问,这个问题如何解决,拜谢

    • 看一下train阶段生成的文件是否正确,肯定是这里有问题,导致执行test阶段的处理时没有读取到label信息。

    • 你好,我也遇到了这个问题,感觉是因为test时候读取train得到的labels文件,但是labels文件中没有对应的test的label。请问你解决了吗?

    • 我觉得有两个办法可以解决:一是test中的文件夹名字和train中一样,二是将test的文件夹名称加入到labels.txt中。不知道对不对,我打算用第一种办法解决。

      • 这个写的比较早了,可能并不是很通用,你可以根据自己的情况,修改相应的代码或者配置,这里只是给你一种思路,作为参考。

  68. 楼主,为什么我在生成model文件时会一直出现Exception in thread “main” java.lang.NumberFormatException: For input string: “x0.000000000000000″这个问题呢

  69. 楼主写的,最后在tain.xtx的输出向量,里面数据只与VectorMetadata.java里面的termTable有关,有用上卡方统计量吗?

  70. 楼主,运行出来的train.txt在libsvm训练的时候,报 格式错误是什么原因啊,“Wrong input format at line 1“,这是错误,

  71. 求问各路大神,我为什么一直没有办法成功生成train.txt、lable.txt、terms.txt呢?一直抓取错误Should Not Null,我不知道到底哪里出了问题

  72. 博主你好,时间过那么久不知道是否还愿意回答我的问题,电脑在跑测试集的文件时总是提示java.lang.OutOfMemoryError: Java heap space,虚拟内存已经调最大了。该如何解决,是需要换一台内存更大的电脑吗

    • 这个预处理程序是单机版吧,如果正确将内存调至最大还报OOM错误,要么你减少样本数据,要么就需要考虑一些分布式处理方案。

  73. 想请教楼主:
    1.输出量化数据文件是单个文件还是每个文档一个文件。
    2.train.txt是文档集合处理完生成的吗?
    3.libsvm训练的是label.txt还是train.txt
    麻烦能不能把配置项里的每个文件格式说明一下

  74. 楼主,libsvm生成的train.txt 序号不是按照从小到大的顺序排列的,这样无法生成model.txt
    2 33:0.121622 42:0.41736 158:0.114865 159:0.114865 191:0.317568
    2 17:0.387879 42:0.0850816 134:0.166667 158:0.772727 159:0.772727
    2 14:0.504065 121:0.19187 24:0.200542 134:0.98374 36:0.200542 42:0.182614 47:0.188618 158:0.691057 159:0.691057 165:0.132114 171:0.558266 176:0.804878 91:0.353659 194:0.98374 199:0.401084
    2 134:0.293333 42:0.0748718 158:0.68 159:0.68

    • 您好,我是个新手,想请问您:
      在用tf-idf表示文档的特征数值时,对于特征词典的一些词,当前文档未出现过这个词,在表示此文档的特征向量时,特征属性对应的值是不是就为0了?在libsvm的输入中此项是不用表示出来的?

    • 我没有试过新版本的libsvm是否有这个限制,我用的应该是3.0版本的,那个应该是没有必须排序这个要求的。

  75. 楼主,想问一下,我用maven工程的编译的时候报了这个错误,是什么愿意,那resource应该去哪里下载呢
    skip non existing resourceDirectory D:\workspace\libsvm-dp-master\libsvm-dp-measure\src\main\resources
    skip non existing resourceDirectory D:\workspace\libsvm-dp-master\libsvm-dp-core\src\main\resources

      • 博主,你好。请问你用的是maven的哪个版本?我用新版本在运行的时候总是提示jar包导入出错的问题,maven的lib中没有一些jar

  76. 楼主,不知你还会不会回答我的问题,我行问下processor.dataset.label.vector.file=C:\\Users\\KINKOO\\Desktop\\vector\\labels.txt这个label是用来干嘛的?期待你的回复。

  77. 大神你好,新手有几个问题想请教一下。
    分词、选择特征,用tf-idf我都理解,使用tf-idf之后的文件就可以直接作为libsvm训练集文件的格式吗?
    我看了一下libsvm他的训练集是
    +1 5:1 11:1 15:1 32:1 39:1 40:1 52:1 63:1 67:1 73:1 74:1 76:1 78:1 83:1
    -1 5:1 16:1 30:1 35:1 41:1 64:1 67:1 73:1 74:1 76:1 80:1 83:1
    -1 5:1 6:1 15:1 20:1 37:1 40:1 50:1 63:1 67:1 73:1 75:1 76:1 80:1 83:1
    -1 5:1 7:1 16:1 29:1 39:1 40:1 48:1 63:1 67:1 73:1 74:1 76:1 78:1 83:1
    -1 1:1 11:1 18:1 20:1 37:1 42:1 59:1 62:1 71:1 72:1 74:1 76:1 80:1 83:1
    +1 5:1 18:1 19:1 39:1 40:1 63:1 67:1 73:1 74:1 76:1 80:1 83:1
    -1 2:1 11:1 18:1 20:1 37:1 40:1 54:1 63:1 67:1 73:1 74:1 76:1 80:1 83:1
    类似这样的(官网测试例子) 貌似和你的训练集不一样,不是很懂望解答。
    我的理解你的livsvmt的train.txt训练不是应该这样的吗
    8 9219:0.24673737883635047 453:0.09884635754820137
    8 10322:0.21501394457319623 11947:0.27282495932970074
    3 6459:0.41385272697452935 46:0.24041607991272138
    2 6459:0.41385272697452935 46:0.24041607991272138
    1 6459:0.41385272697452935 46:0.24041607991272138
    1 6459:0.41385272697452935 46:0.24041607991272138

  78. Pingback: 使用sklearn进行文本分类(以搜狗用户画像为例)[未完] – 我想做一个科学家

  79. 请问 F盘 这两个文件里存放的是什么?
    public class UTF8Encoding {

    public static void main(String[] args) {

    String src = “F:\\数据挖掘\\SogouC\\SogouC\\ClassFile”;

    String dst = “F:\\数据挖掘\\SogouC\\SogouC\\UTF8\\ClassFile”;

    File dstRoot = new File(dst);

  80. 楼主好,有个问题请教下。训练完后做测试的时候,是不是也需要对测试集中的文本的特征向量计算tf-idf?如果测试集中只包含一篇文章,idf是不是就为0了?所以tf-idf中每个特征向量的值都为0?谢谢。

  81. 您好 我一直报错 缺少这个怎么解决?Missing artifact org.shirdrn:libsvm-dp-analysis-lucene:jar:0.0.1-SNAPSHOT

  82. Exception in thread “main” java.lang.Error: 无法解析的编译问题:
    String 无法解析为类型
    类型 AbstractDocumentProcessorDriver 中的方法 start(Class)引用缺少的类型 Class

    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:50)

    请问有人遇到这个问题吗?我用2014的DATA代替以后就报这个错误

  83. Exception in thread “main” java.lang.NullPointerException
    at org.shirdrn.document.preprocessing.component.BasicInformationCollector.fire(BasicInformationCollector.java:23)
    at org.shirdrn.document.preprocessing.driver.common.AbstractDocumentProcessorDriver.run(AbstractDocumentProcessorDriver.java:21)
    at org.shirdrn.document.preprocessing.driver.TrainDocumentPreprocessingDriver.preprocess(TrainDocumentPreprocessingDriver.java:53)
    at org.shirdrn.document.preprocessing.driver.common.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:31)
    at org.shirdrn.document.preprocessing.driver.TrainDocumentPreprocessingDriver.main(TrainDocumentPreprocessingDriver.java:57)
    调用重构的代码出现这个问题?大神们有遇到的吗?

        • 用train数据训练时,每次训练到4000左右提示内存不足,但是我把运行eclipse的运行内存和jdk的内存都调大仍然没有用。
          把数据换成2000就可以正常运行了,谢谢笔者的源码。请问谁有遇到同样的情况吗?是怎么解决的?

          • 不好意思说啊,这个是由于JVM内存太小 导致的,吧JVM内存修改成Xms512,Xms1024就能正常用了

  84. 2018-07-28 11:04:24.698 [main] INFO processor.config.Configuration – Load properties file: prop=config-train.properties
    2018-07-28 11:04:24.789 [main] INFO processor.common.FDMetadata – Train dataset file extension: name=
    2018-07-28 11:04:24.823 [main] INFO processor.common.FDMetadata – Vector input root directory: outputDir=F:\SogouC-UTF8\UTF8\train\ClassFile
    2018-07-28 11:04:24.823 [main] INFO processor.common.FDMetadata – Vector output directory: outputDir=C:\Users\Shirdrn\Desktop\vector
    2018-07-28 11:04:24.823 [main] INFO processor.common.FDMetadata – Vector output file: outputFile=train.txt
    2018-07-28 11:04:24.873 [main] INFO processor.component.DocumentWordsCollector – Analyzer class name: class=org.shirdrn.document.processor.analyzer.IctclasAnalyzer
    Exception in thread “main” java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:43)
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:30)
    at org.shirdrn.document.processor.component.DocumentWordsCollector.(DocumentWordsCollector.java:30)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.process(TrainDocumentProcessorDriver.java:42)
    at org.shirdrn.document.processor.AbstractDocumentProcessorDriver.start(AbstractDocumentProcessorDriver.java:29)
    at org.shirdrn.document.processor.TrainDocumentProcessorDriver.main(TrainDocumentProcessorDriver.java:51)
    Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.shirdrn.document.processor.utils.ReflectionUtils.construct(ReflectionUtils.java:77)
    at org.shirdrn.document.processor.utils.ReflectionUtils.getInstance(ReflectionUtils.java:41)
    … 5 more
    Caused by: java.lang.RuntimeException:
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:33)
    … 11 more
    Caused by: java.lang.RuntimeException: Fail to initialize!
    at org.shirdrn.document.processor.analyzer.IctclasAnalyzer.(IctclasAnalyzer.java:30)
    … 11 more
    我一直出现这个问题,博主的两个源代码,我都是出现这样的问题,但是我不知道问题出在哪里?跪求大神帮忙,谢谢

  85. String train = configuration.get(“processor.dataset.train.svm.vector.file”);
    得到的是C:\Users\Shirdrn\Desktop\vector
    为什么啊?这个可以自己改吗?
    processor.dataset.train.svm.vector.file在哪里啊?
    烦请大神指教,不胜感激

  86. 楼主,为什么我用你的代买去分类短文本,类似于微博文本时,发现得到的terms特别少,是因为短文本数据太少的原因吗?还是程序没调对?

  87. Pingback: Python爬虫爬取知乎用户信息+寻找潜在客户 - 内存网

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>