Weka,是一個用Java編寫的數(shù)據(jù)挖掘軟件。數(shù)據(jù)挖掘,從字面上來看,它是一個從數(shù)據(jù)中找尋有用信息的過程,不過,它涉及的內(nèi)容很多,所以,這里借用“分類”這一面來說事。
分類,從名稱上來看,再簡單不過了,給你一樣?xùn)|西,給它分個類。你如何知道怎么分類呢?顯然,這是基于你已有的經(jīng)驗。對于計算機而言,這種經(jīng)驗從何而來呢?只有讓人來告訴它,也就是說,我們要拿一批數(shù)據(jù)訓(xùn)練計算機,經(jīng)過訓(xùn)練的計算機,便具備了一定的識別能力,就可以完成一些簡單的分類工作。現(xiàn)實中,可以用到分類的機會有很多,比如我之前,曾經(jīng)參與過的一個項目就是用這種方法來做車輛的識別。
下面便是一段使用Weka完成一段分類程序。
import weka.classifiers.Classifier;
import weka.classifiers.bayes.NaiveBayesMultinomial;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;
public class Main {
private static final String GOOD = "G";
private static final String BAD = "B";
private static final String CATEGORY = "category";
private static final String TEXT = "text";
private static final int INIT_CAPACITY = 100;
private static final String[][] TRAINING_DATA = {
{"Good", GOOD},
{"Wonderful", GOOD},
{"Cool", GOOD},
{"Bad", BAD},
{"Disaster", BAD},
{"Terrible", BAD}
};
private static final String TEST_DATA = "Good";
private static Filter filter = new StringToWordVector();
private static Classifier classifier = new NaiveBayesMultinomial();
public static void main(String[] args) throws Exception {
FastVector categories = new FastVector();
categories.addElement(GOOD);
categories.addElement(BAD);
FastVector attributes = new FastVector();
attributes.addElement(new Attribute(TEXT, (FastVector)null));
attributes.addElement(new Attribute(CATEGORY, categories));
Instances instances = new Instances("Weka", attributes, INIT_CAPACITY);
instances.setClassIndex(instances.numAttributes() - 1);
for (String[] pair : TRAINING_DATA) {
String text = pair[0];
String category = pair[1];
Instance instance = createInstanceByText(instances, text);
instance.setClassValue(category);
instances.add(instance);
}
filter.setInputFormat(instances);
Instances filteredInstances = Filter.useFilter(instances, filter);
classifier.buildClassifier(filteredInstances);
// Test
String testText = TEST_DATA;
Instance testInstance = createTestInstance(instances.stringFreeStructure(), testText);
double predicted = classifier.classifyInstance(testInstance);
String category = instances.classAttribute().value((int)predicted);
System.out.println(category);
}
private static Instance createInstanceByText(Instances data, String text) {
Attribute textAtt = data.attribute(TEXT);
int index = textAtt.addStringValue(text);
Instance instance = new Instance(2);
instance.setValue(textAtt, index);
instance.setDataset(data);
return instance;
}
private static Instance createTestInstance(Instances data, String text) throws Exception {
Instance testInstance = createInstanceByText(data, text);
filter.input(testInstance);
return filter.output();
}
}
這個程序分成兩個大部分,前半部分用以訓(xùn)練分類器,后半部分則是測試這個分類器。
訓(xùn)練分類器,我們要做的包括,選擇分類算法和準備訓(xùn)練數(shù)據(jù)。在Weka中,每一種分類算法都是Classifier的一個子類,這樣的話,就可以在不改變其它部分的情況下,很容易的修改分類算法。
其實,稍微了解一下這方面的知識的人,都會知道,分類算法固然重要,但真正決定一個分類器本事大小的,是用以訓(xùn)練的數(shù)據(jù)。想要得到一個好的分類器,少不了不斷調(diào)整訓(xùn)練數(shù)據(jù)和不斷的訓(xùn)練。這同人類認識問題是一樣的,經(jīng)得多,見得廣,才有更好的分辨能力。
在Weka中,用以訓(xùn)練的數(shù)據(jù)就是Instances,顧名思義,這是Instance的復(fù)數(shù),顯而易見,單獨的一個訓(xùn)練數(shù)據(jù)就是Instance,而Instances這個類的存在,可以把Instance的一些公共的屬性放到一起。在這里,我們可以看到,為了用文本作為訓(xùn)練數(shù)據(jù),我們會把文本轉(zhuǎn)換為Instance。同樣,測試分類器的時候,我們也會把文本轉(zhuǎn)換為一個Instance,然后再進行分類。
除此之外,這里還有一個Filter的概念,同常見的filter概念類似,它給了我們一個進行正式處理之前,對數(shù)據(jù)進行處理的機會。在這里,主要是對Instance做一些相關(guān)的變換。
當我們得到一個分類器之后,就可以利用這個分類器進行分類了,其中,最關(guān)鍵的代碼是
classifier.classifyInstance(testInstance);
這段代碼返回的是根據(jù)分類算法計算結(jié)果得到的一個相似度,我們可以利用這個值來估計我們測試用的數(shù)據(jù)應(yīng)該屬于哪個分類。
從代碼上來說,這段代碼本身并不復(fù)雜。正如前面所說,一個好的分類器是需要讓數(shù)據(jù)幫忙的。所以,換幾個測試數(shù)據(jù),你就會發(fā)現(xiàn),這段代碼中實現(xiàn)的分類器一點都不強大。如果希望它強大起來,擴展訓(xùn)練數(shù)據(jù)是一個必然的結(jié)果。不過,對于這篇blog而言,這不重要,因為我們只是要和Weka問個好,進一步的工作,還需要進一步的努力。