2017-06-14 201 views
1

我正在尝试使用OpenNLP对发票进行分类。根据它的描述,我将它分成两类。我已经构建了一个包含20K描述的培训文件,并将其标记为正确的类。培训OpenNLP文档分类

训练数据看起来像(第一列是一个代码,即我的类中使用,而第二列是发票说明):

85171231 IPHONE 5S CINZA ESPACIAL 16GB (ME432BZA) 
85171231 Galaxy S6 SM-G920I 
85171231 motorola - MOTO G5 XT1672 
00000000 MOTONETA ITALIKA AT110 
00000000 CJ BOX UNIBOX MOLA 138X57X188 VINHO 

从OpenNLP使用DocumentCategorizer,我达到98.5%正确性。但是,为了提高效率,我选择了错误的分类文档并用它来扩展训练数据。

例如,当我第一次运行它时,“MOTONETA ITALIKA AT110”被分类为“85171231”。没关系,因为进入第一次运行时,“MOTONETA ITALIKA AT110”未被分类。所以,我教了分类器明确地把“MOTONETA ITALIKA AT110”标记为“00000000”。

但是,再次运行OpenNLP,即使训练数据包含“000000”的显式映射,OpenNLP也坚持将其归类为“85171231”。

所以我的问题是:我在教OpenNLP赖特吗?我如何提高效率?

,我正在使用的代码是:

MarkableFileInputStreamFactory dataIn = new MarkableFileInputStreamFactory("data.train"); 

ObjectStream<String> lineStream = new PlainTextByLineStream(dataIn, StandardCharsets.UTF_8); 
ObjectStream<DocumentSample> sampleStream = new DocumentSampleStream(lineStream); 

TrainingParameters params = new TrainingParameters(); 
params.put(TrainingParameters.ITERATIONS_PARAM, "100"); 
params.put(TrainingParameters.CUTOFF_PARAM, "0"); 

DoccatModel model = DocumentCategorizerME.train("pt", sampleStream, params, new DoccatFactory()); 

DocumentCategorizer doccat = new DocumentCategorizerME(model); 
double[] aProbs = doccat.categorize("MOTONETA ITALIKA AT110".replaceAll("[^A-Za-z0-9 ]", " ").split(" ")); 
doccat.getBestCategory(aProbs); 

回答

3

默认情况下,DocumentCategorizer将使用文字包。这意味着没有考虑术语的顺序。 如果在85171231组中有高频出现MOTONETA ITALIKA AT110的任何项,分类器将倾向于使用该组。

您有几种选择:

  • 您可以添加的MOTONETA ITALIKA AT110更多的变种到组000000;
  • 尝试更改特征生成器。

第二个选择是改变你的模型的创建,像这样:

int minNgramSize = 2; 
int maxNgramSize = 3; 
DoccatFactory customFactory = new DoccatFactory(
    new FeatureGenerator[]{ 
     new BagOfWordsFeatureGenerator(), 
     new NGramFeatureGenerator(minNgramSize, maxNgramSize) 
    } 
); 
DoccatModel model = DocumentCategorizerME.train("pt", sampleStream, params, customFactory); 

您可以通过删除BagOfWordsFeatureGenerator和不断变化的最小和最大的ngram尺寸与功能发挥发电机。