【问题标题】:text classifier with weka: how to correctly train a classifier issue带有 weka 的文本分类器:如何正确训练分类器问题
【发布时间】:2015-02-27 00:10:12
【问题描述】:

我正在尝试使用 Weka 构建一个文本分类器,但 distributionForInstance 的概率在一个类中是 1.0 在所有其他情况下是 0.0,所以 classifyInstance 总是返回相同的类预言。训练中的某些内容无法正常工作。

ARFF 培训

@relation test1

@attribute tweetmsg    String
@attribute classValues {politica,sport,musicatvcinema,infogeneriche,fattidelgiorno,statopersonale,checkin,conversazione}

@DATA

"Renzi Berlusconi Salvini Bersani",politica
"Allegri insulta la terna arbitrale",sport
"Bravo Garcia",sport

培训方法

public void trainClassifier(final String INPUT_FILENAME) throws Exception
{
    getTrainingDataset(INPUT_FILENAME);

    //trainingInstances consists of feature vector of every input

    for(Instance currentInstance : inputDataset)
    {           
        Instance currentFeatureVector = extractFeature(currentInstance);

        currentFeatureVector.setDataset(trainingInstances);
        trainingInstances.add(currentFeatureVector);                
    }

    classifier = new NaiveBayes();

    try {
        //classifier training code
        classifier.buildClassifier(trainingInstances);

        //storing the trained classifier to a file for future use
        weka.core.SerializationHelper.write("NaiveBayes.model",classifier);
    } catch (Exception ex) {
        System.out.println("Exception in training the classifier."+ex);
    }
}

private Instance extractFeature(Instance inputInstance) throws Exception
{       
    String tweet = inputInstance.stringValue(0);
    StringTokenizer defaultTokenizer = new StringTokenizer(tweet);
    List<String> tokens=new ArrayList<String>();
    while (defaultTokenizer.hasMoreTokens())
    {
        String t= defaultTokenizer.nextToken();
        tokens.add(t);
    }

    Iterator<String> a = tokens.iterator();
    while(a.hasNext())
    {
                String token=(String) a.next();
                String word = token.replaceAll("#","");
                if(featureWords.contains(word))
                {                                              
                    double cont=featureMap.get(featureWords.indexOf(word))+1;
                    featureMap.put(featureWords.indexOf(word),cont);
                }
                else{
                    featureWords.add(word);
                    featureMap.put(featureWords.indexOf(word), 1.0);
                }

    }
    attributeList.clear();
    for(String featureWord : featureWords)
    {
        attributeList.add(new Attribute(featureWord));   
    }
    attributeList.add(new Attribute("Class", classValues));
    int indices[] = new int[featureMap.size()+1];
    double values[] = new double[featureMap.size()+1];
    int i=0;
    for(Map.Entry<Integer,Double> entry : featureMap.entrySet())
    {
        indices[i] = entry.getKey();
        values[i] = entry.getValue();
        i++;
    }
    indices[i] = featureWords.size();
    values[i] = (double)classValues.indexOf(inputInstance.stringValue(1));
    trainingInstances = createInstances("TRAINING_INSTANCES");

    return new SparseInstance(1.0,values,indices,1000000);
}


private void getTrainingDataset(final String INPUT_FILENAME)
{
    try{
        ArffLoader trainingLoader = new ArffLoader();
        trainingLoader.setSource(new File(INPUT_FILENAME));
        inputDataset = trainingLoader.getDataSet();
    }catch(IOException ex)
    {
        System.out.println("Exception in getTrainingDataset Method");
    }
    System.out.println("dataset "+inputDataset.numAttributes());
}

private Instances createInstances(final String INSTANCES_NAME)
{
    //create an Instances object with initial capacity as zero 
    Instances instances = new Instances(INSTANCES_NAME,attributeList,0);
    //sets the class index as the last attribute
    instances.setClassIndex(instances.numAttributes()-1);

    return instances;
}

public static void main(String[] args) throws Exception
{
      Classificatore wekaTutorial = new Classificatore();
      wekaTutorial.trainClassifier("training_set_prova_tent.arff");
      wekaTutorial.testClassifier("testing.arff");
}

public Classificatore()
{
    attributeList = new ArrayList<Attribute>();
    initialize();
}    

private void initialize()
{

    featureWords= new ArrayList<String>(); 

    featureMap = new TreeMap<>();

    classValues= new ArrayList<String>();
    classValues.add("politica");
    classValues.add("sport");
    classValues.add("musicatvcinema");
    classValues.add("infogeneriche");
    classValues.add("fattidelgiorno");
    classValues.add("statopersonale");
    classValues.add("checkin");
    classValues.add("conversazione");
}

测试方法

public void testClassifier(final String INPUT_FILENAME) throws Exception
{
    getTrainingDataset(INPUT_FILENAME);

    //trainingInstances consists of feature vector of every input
    Instances testingInstances = createInstances("TESTING_INSTANCES");

    for(Instance currentInstance : inputDataset)
    {

        //extractFeature method returns the feature vector for the current input
        Instance currentFeatureVector = extractFeature(currentInstance);
        //Make the currentFeatureVector to be added to the trainingInstances
        currentFeatureVector.setDataset(testingInstances);
        testingInstances.add(currentFeatureVector);

    }


    try {
        //Classifier deserialization
        classifier = (Classifier) weka.core.SerializationHelper.read("NaiveBayes.model");

        //classifier testing code
        for(Instance testInstance : testingInstances)
        {

            double score = classifier.classifyInstance(testInstance);
            double[] vv= classifier.distributionForInstance(testInstance);
            for(int k=0;k<vv.length;k++){
            System.out.println("distribution "+vv[k]); //this are the probabilities of the classes and as result i get 1.0 in one and 0.0 in all the others
            }
            System.out.println(testingInstances.attribute("Class").value((int)score));
        }
    } catch (Exception ex) {
        System.out.println("Exception in testing the classifier."+ex);
    }
}

我想为短信创建一个文本分类器,此代码基于本教程http://preciselyconcise.com/apis_and_installations/training_a_weka_classifier_in_java.php。问题是分类器为 testing.arff 中的几乎每条消息预测错误的类,因为类的概率不正确。 training_set_prova_tent.arff 每个类的消息数量相同。 我正在遵循的示例使用 featureWords.dat 并将 1.0 与消息中存在的单词相关联,而不是我想创建自己的字典,其中包含 training_set_prova_tent 中存在的单词加上测试中存在的单词并与每个单词相关联出现次数。

附言 我知道这正是我可以使用过滤器 StringToWordVector 执行的操作,但我还没有找到任何示例来说明如何将此过滤器与两个文件一起使用:一个用于训练集,一个用于测试集。所以改编我找到的代码似乎更容易。

非常感谢

【问题讨论】:

  • 我想我知道你想做什么,我可能会有答案。但可以肯定的是:您想根据字数将推文(短文本)分类为多个类别(您的classValues),对吧?
  • 正确!!我真的希望你能帮助我
  • 可以添加getTrainingDataset(...)的代码吗?没有它我无法运行代码。另外:您能否在您的代码示例之后改写文本?我不完全理解,可能有重要的细节。
  • 你看过StringToWordVector了吗?
  • 我把所有的方法都加了,试着写的更全面,希望现在更清楚了。

标签: java weka text-classification categorization


【解决方案1】:

您似乎在某些关键点更改了 website you referenced 的代码,但不是很好。我会尝试起草你想要做什么以及我发现了什么错误。

你(可能)想在extractFeature 中做的是

  • 将每条推文拆分为单词(标记化)
  • 统计这些词出现的次数
  • 创建一个表示这些字数和类别的特征向量

你在那个方法中忽略了什么

  1. 您永远不会重置您的featureMap。线

    Map<Integer,Double> featureMap = new TreeMap<>();
    

    最初是extractFeatures,但您将其移至initialize。这意味着您总是将字数加起来,但从不重置它们。对于每条新推文,您的字数还包括所有先前推文的字数。我确定这不是您想要的。

  2. 您不会使用您想要作为特征的单词来初始化featureWords。是的,你创建了一个空列表,但是你用每条推文迭代地填充它。原始代码在 initialize 方法中对其进行了一次初始化,之后就再也没有改变过。这样做有两个问题:

    • 每条新推文都会添加新特征(词),因此您的特征向量会随着每条推文而增长。这不会是一个大问题(SparseInstance),但这意味着
    • 您的class 属性始终位于另一个位置。这两行代码适用于原始代码,因为 featureWords.size() 基本上是一个常量,但在您的代码中,类标签将位于索引 5、8、12 等处,但它必须 对每个实例都一样。
    indices[i] = featureWords.size();
    values[i] = (double) classValues.indexOf(inputInstance.stringValue(1));
    
  3. 这也体现在您为每条新推文构建一个新的attributeList,而不是在initialize 中只构建一次,由于已经解释的原因,这很糟糕。

可能还有更多的东西,但是——事实上——你的代码是相当不可修复的。您想要的比您的版本更接近您修改的教程源代码。

另外,您应该查看StringToWordVector,因为这似乎正是您想要做的:

将字符串属性转换为一组属性,这些属性表示来自字符串中包含的文本的单词出现(取决于标记器)信息。单词(属性)的集合由过滤的第一批(通常是训练数据)决定。

【讨论】:

  • 非常感谢您的解释非常好和清晰..好的,所以我会尝试使用 StringToWordVector 来制作我的代码。再次感谢
猜你喜欢
  • 2016-09-25
  • 2015-01-26
  • 2014-04-21
  • 2014-11-25
  • 2012-03-05
  • 2013-08-30
  • 2013-04-11
  • 2012-07-27
  • 2016-04-04
相关资源
最近更新 更多