天天看點

weka的參數使用

摘要: 最 常用的元件(components)是: l Instances 你的資料 l Filter 對資料的預處理 l Classifiers/Clusterer 被建立在預處理的資料上,分類/聚類 l Evaluating 評價classifier/clusterer l Attribute sel ...

最常用的元件(components)是:

l Instances 你的資料

l Filter 對資料的預處理

l Classifiers/Clusterer 被建立在預處理的資料上,分類/聚類

l Evaluating 評價classifier/clusterer

l Attribute selection 去除資料中不相關的屬性

下面将介紹如果在你自己的代碼中使用WEKA ,其中的代碼可以在上面枉址的尾部找到。

Instances

ARFF檔案

3.5.5和3.4.X版本

從ARFF檔案中讀取是一個很直接的

import weka .core.Instances;

import java.io.BufferedReader;

import java.io.FileReader;

...

Instances data = new Instances(

new BufferedReader(

new FileReader("/some/where/data.arff")));

// setting class attribute

data.setClassIndex(data.numAttributes() - 1);

Class Index是訓示用于分類的目标屬性的下标。在ARFF檔案中,它被預設為是最後一個屬性,這也就是為什麼它被設定成numAttributes-1.

你必需在使用一個Weka 函數(ex: weka .classifiers.Classifier.buildClassifier(data))之前設定Class Index。

3.5.5和更新的版本

DataSource類不僅限于讀取ARFF檔案,它同樣可以讀取CSV檔案和其它格式的檔案(基本上Weka 可以通過它的轉換器(converters)導入所有的檔案格式)。

import weka .core.converters.ConverterUtils.DataSource;

...

DataSource source = new DataSource("/some/where/data.arff");

Instances data = source.getDataSet();

// setting class attribute if the data format does not provide this

//information

// E.g. the XRFF format saves the class attribute information as well

if (data.classIndex() == -1)

data.setClassIndex(data.numAttributes() - 1);

資料庫

從資料庫中讀取資料稍微難一點,但是仍然是很簡單的,首先,你需要修改你的DatabaseUtils.props(自 己看一下原文,基本上都有連結)重組(resemble)你的資料庫連接配接。比如,你想要連接配接一個MySQL伺服器,這個伺服器運作于3306端口(默 認),MySQL JDBC驅動被稱為Connector/J(驅動類是org.gjt.mm.mysql.Driver)。假設存放你資料的資料庫是 some_database。因為你隻是讀取資料,你可以用預設使用者nobody,不設密碼。你需要添加下面兩行在你的props檔案中:

jdbcDriver=org.gjt.mm.mysql.Driver

jdbcURL=jdbc:mysql://localhost:3306/some_database

其次,你的讀取資料的Java代碼,應該寫成下面這樣:

import weka .core.Instances;

import weka .experiment.InstanceQuery;

...

InstanceQuery query = new InstanceQuery();

query.setUsername("nobody");

query.setPassword("");

query.setQuery("select * from whatsoever");

// if your data is sparse then you can say so too

// query.setSparseData(true);

Instances data = query.retrieveInstances();

注意:

l 别忘了把JDBC驅動加入你的CLASSPATH中

l 如果你要用MS Access,你需要用JDBC-ODBC-bridge,它是JDK的一部分。

參數設定(Option handling)

Weka 中實作了weka .core.OptionHandler接口,這個接口為比如classifiers,clusterers,filers等提供了設定,擷取參數的功能,函數如下:

l void setOptions(String[] Options)

l String[] getOptions()

下面伊次介紹幾種參數設定的方法:

l 手工建立一個String數組

String[] options = new String[2];

options[0] = "-R";

options[1] = "1";

l 用weka .core.Utils類中的函數splitOptions将一個指令行字元串轉換成一下數組

String[] options = weka .core.Utils.splitOptions("-R 1");

l 用OptionsToCode.java類自動将一個指令行轉換成代碼,對于指令行中包含nested classes,這些類又有它們自己的參數,如果SMO的核參數這種情況很有幫助。

java OptionsToCode weka .classifiers.functions.SMO

将産生以下輸出:

//create new instance of scheme

weka .classifiers.functions.SMO scheme = new

weka .classifiers.functions.SMO();

// set options

scheme.setOptions(weka .core.Utils.splitOptions("-C 1.0 -L 0.0010 -P

1.0E-12 -N 0 -V -1 -W 1 -K \"

weka .classifiers.functions.supportVector.PolyKernel -C -E

1.0\""));

并且,OptionTree.java工具可以使你觀察一個nested參數字元串。

Filter

一個filter有兩種不同的屬性

l 監督的或是監督的(supervised or unsupervised)

是否受使用者控制

l 基于屬性的或是基于樣本的(attribute- or instance-based)

比如:删除滿足一定條件的屬性或是樣本

多數filters實作了OptionHandler接口,這意味着你可以通過String數組設定參數,而不用手工地用set-方法去伊次設定。比如你想删除資料集中的第一個屬性,你可用這個filter。

weka .

通過設定參數

-R 1

如果你有一個Instances對象,比如叫data,你可以用以下方法産生并使用filter:

import weka .core.Instances;

import weka .filters.Filter;

import weka .filters.unsupervised.attribute.Remove;

...

String[] options = new String[2];

options[0] = "-R"; // "range"

options[1] = "1"; // first attribute

Remove remove = new Remove(); // new instance of filter

remove.setOptions(options); // set options

// inform filter about dataset //**AFTER** setting options

remove.setInputFormat(data);

Instances newData = Filter.useFilter(data remove); // apply filter

運作中過濾(Filtering on-the-fly)

FilteredClassifier meta-classifier是一種運作中過濾的方式。它不需要在分類器訓練之前先對資料集過濾。并且,在預測的時候,你也不需要将測試資料集再次過濾。下面的例子中使用meta-classifier with Remove filter和J48,删除一個attribute ID為1的屬性。

import weka .core.Instances;

import weka .filters.Filter;

import weka .filters.unsupervised.attribute.Remove;

...

String[] options = new String[2];

options[0] = "-R"; // "range"

options[1] = "1"; // first attribute

Remove remove = new Remove(); // new instance of filter

remove.setOptions(options); // set options

// inform filter about dataset **AFTER** setting options

remove.setInputFormat(data);

Instances newData = Filter.useFilter(data remove); // apply filter

import weka .classifiers.meta.FilteredClassifier;

import weka .classifiers.trees.J48;

import weka .filters.unsupervised.attribute.Remove;

...

Instances train = ... // from somewhere

Instances test = ... // from somewhere

// filter

Remove rm = new Remove();

rm.setAttributeIndices("1"); // remove 1st attribute

// classifier

J48 j48 = new J48();

j48.setUnpruned(true); // using an unpruned J48

// meta-classifier

FilteredClassifier fc = new FilteredClassifier();

fc.setFilter(rm);

fc.setClassifier(j48);

// train and make predictions

fc.buildClassifier(train);

for (int i = 0; i < test.numInstances(); i++)

其它Weka 中便利的meta-schemes:

weka .clusterers.FilteredClusterer (since 3.5.4)

weka .associations.FilteredAssociator (since 3.5.6)

批過濾(Batch filtering)

在 指令行中,你可以用-b選項enable第二個input/ouput對,用對第一個資料集過濾的設定來過濾第二個資料集。如果你正使用特征選擇 (attribute selection)或是正規化(standardization),這是必要的,否則你會得到兩個不相容的資料集。其實這做起來很容易,隻需要用 setInputFormat(Instances)去初始化一個過濾器,即用training set,然後将這個過濾器伊次用于training set和test set。下面的例子将展示如何用Standardize過濾器過濾一個訓練集和測試集的。

Instances train = ... // from somewhere

Instances test = ... // from somewhere

// initializing the filter once with training set

Standardize filter = new Standardize();

filter.setInputFormat(train);

// configures the Filter based on train instances and returns filtered

//instances

Instances newTrain = Filter.useFilter(train filter);

// create new test set

Instances newTest = Filter.useFilter(test filter);

調用轉換(Calling conventions)

setInputFormat(Instances) 方法總是必需是應用過濾器時晤後一個調用,比如用Filter.useFilter(InstancesFilter)。為什麼?首先,它是使用過濾器的 轉換,其實,很多過濾器在setInputFormat(Instances)方法中用目前的設定參數産生輸出格式(output format)(在這個調用後設定參數不再有任何作用)。

分類(classification)

一些必要的類可以在下面的包中找到:

weka .

建立一個分類器(Build a classifier)

批(Batch)

在一個給定的資料集上訓練一個Weka

分類器是非常簡單的事。例如,我們可以訓練一個C4.5樹在一個給定的資料集data上。訓練是通過buildClassifier(Instances)來完成的。

import weka .classifiers.trees.J48;

...

String[] options = new String[1];

options[0] = "-U"; // unpruned tree

J48 tree = new J48(); // new instance of tree

tree.setOptions(options); // set the options

tree.buildClassifier(data); // build classifier

增量式(Incremental)

實作了weka .classifiers.UpdateabeClassifier接口的分類器可以增量式的訓練,它可以節約記憶體,因為你不需要半冽據一次全部讀入記憶體。你可以查一下文檔,看哪些分類器實作了這個接口。

真正學習一個增量式的分類器是很簡單的:

l 調用buildClassifier(Instances),其中Instances包話這種資料集的結構,其中Instances可以有資料,也可以沒有。

l 順序調用updateClassifier(Instances)方法,通過一個新的weka .core.Instances,更新分類器。

這裡有一個用weka .core.converters.ArffLoader讀取資料,并用weka .classifiers.bayes.NaiveBayesUpdateable訓練分類器的例子。

// load data

ArffLoader loader = new ArffLoader();

loader.setFile(new File("/some/where/data.arff"));

Instances structure = loader.getStructure();

structure.setClassIndex(structure.numAttributes() - 1);

// train NaiveBayes

NaiveBayesUpdateable nb = new NaiveBayesUpdateable();

nb.buildClassifier(structure);

Instance current;

while ((current = loader.getNextInstance(structure)) != null)

nb.updateClassifier(current);

Evaluating

交叉檢驗

如果你一個訓練集并且沒有測試集,你也話想用十次交叉檢驗的方法來評價分類器。這可以很容易地通過Evaluation類來實作。這勒醅我們用1作為随機種子進行随機選擇,檢視Evaluation類,可以看到更多它輸出的統計結果。

import weka .classifiers.Evaluation;

import java.util.Random;

...

Evaluation eval = new Evaluation(newData);

eval.crossValidateModel(tree newData 10 new Random(1));

注 意:分類器(在這個例子中是tree)不應該在作為crossValidateModel參數之前訓練,為什麼?因為每當buildClassifier 方法被調用時,一個分類器必需被重新初始化(換句話說:接下來調用buildClassifier 方法總是傳回相同的結果),你将得到不一緻,沒有任何意義的結果。crossValidateModel方法處理分類器的training和 evaluation(每一次cross-validation,它産生一個你作為參數的原分類器的複本(copy))。

Train/Set set

如果你有一個專用的測試集,你可以在訓練集上訓練一個分類器,再在測試集上測試。在下面的例子中,一個J48被執行個體化,訓練,然後評價。在控制台輸出一些統計值。

import weka .core.Instances;

import weka .classifiers.Evaluation;

import weka .classifiers.trees.J48;

...

Instances train = ... // from somewhere

Instances test = ... // from somewhere

// train classifier

Classifier cls = new J48();

cls.buildClassifier(train);

// evaluate classifier and print some statistics

Evaluation eval = new Evaluation(train);

eval.evaluateModel(cls test);

System.out.println(eval.toSummaryString("\nResults\n======\n"

false));

統計(statistics)

下面是一些擷取評價結果的方法

l 數值型類别

? Correct() 分類正确的樣本數 (還有incorrect() )

? pctCorrect() 分類正确的百分比 (還有pctIncorrect())

? kappa() Kappa statistics

l 離散型類别

? correlationCoefficient() 相關系數

l 通用

? meanAbsoluteError() 平均絕對誤差

? rootMeanSquaredError() 均方根誤差

? unclassified() 未被分類的樣本數

? pctUnclassified() 未被分類的樣本百分比

如果你想通過指令行獲得相同的結果,使用以下方法:

import weka .classifiers.trees.J48;

import weka .classifiers.Evaluation;

...

String[] options = new String[2];

options[0] = "-t";

options[1] = "/some/where/somefile.arff";

System.out.println(Evaluation.evaluateModel(new J48() options));

ROC 曲線/AUC(ROC curves/AUC)

從Weka 3.5.1開始,你可以在測試中産生ROC曲線/AUC。你可以調用Evaluation類中的predictions()方法去做。你可從Generating Roc curve這篇文章中找到許多産生ROC曲線的例子。

分類樣本(classifying instances)

如 果你想用你新訓練的分類器去分類一個未标記資料集(unlabeled dataset),你可以使用下面的代碼段,它從/some/where/unlabeled.arff中讀取資料,并用先前訓練的分類器tree去标記 樣本,并儲存标記樣本在/some/where/labeled.arff中

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileReader;

import java.io.FileWriter;

import weka .core.Instances;

...

// load unlabeled data

Instances unlabeled = new Instances(

new BufferedReader(

new FileReader("/some/where/unlabeled.arff")));

// set class attribute

unlabeled.setClassIndex(unlabeled.numAttributes() - 1);

// create copy

Instances labeled = new Instances(unlabeled);

// label instances

for (int i = 0; i < unlabeled.numInstances(); i++)

// save labeled data

BufferedWriter writer = new BufferedWriter(

new FileWriter("/some/where/labeled.arff"));

writer.write(labeled.toString());

writer.newLine();

writer.flush();

writer.close();

數值型類别注意事項

l 如果你對所有類别在分布感興趣,那麼使用distributionForInstance(Instance)。這個方法傳回一個針對每個類别機率的double數組。

l classifyInstance傳回的是一個double值(或者是distributionForInstance傳回的數組中的下标),它僅僅是屬性的下标,例如,如果你想用字元串形式來表現傳回的類别結果clsLabel,你可以這樣輸出:

System.out.println(clsLabel + " -> " +

unlabeled.classAttribute().value((int) clsLabel));

聚類(Clustering)

聚類與分類相似,必要的類可以在下面的包中找到

weka .clusterers

建立一個Clusterer

批(Batch)

一 個clusterer建立與建立一個分類器的方式相似,隻是不是使用buildClassifier(Instances)方法,它使用 buildClusterer(Instances),下面的代碼段展示了如何用EM clusterer使用最多100次疊代的方法。

import weka .clusterers.EM;

...

String[] options = new String[2];

options[0] = "-I"; // max. iterations

options[1] = "100";

EM clusterer = new EM(); // new instance of clusterer

clusterer.setOptions(options); // set the options

clusterer.buildClusterer(data); // build the clusterer

增量式

實作了weka .clusterers.UpdateableClusterer接口的Clusterers可以增量式的被訓練(從3.5.4版開始)。它可以節省記憶體,因為它不需要一次性将資料全部讀入記憶體。檢視文檔,看哪些clusterers實作了這個接口。

真正訓練一個增量式的clusterer是很簡單的:

l 調用buildClusterer(Instances) 其中Instances包話這種資料集的結構,其中Instances可以有資料,也可以沒有。

l 順序調用updateClusterer(Instances)方法,通過一個新的weka .core.Instances,更新clusterer。

l 當全部樣本被處理完之後,調用updateFinished(),因為clusterer還要進行額外的計算。

下面是一個用weka .core.converters.ArffLoader讀取資料,并訓練weka .clusterers.Cobweb的代碼:

//load data

ArffLoader loader = new ArffLoader();

loader.setFile(new File("/some/where/data.arff"));

Instances structure = loader.getStructure();

// train Cobweb

Cobweb cw = new Cobweb();

cw.buildClusterer(structure);

Instance current;

while ((current = loader.getNextInstance(structure)) != null)

cw.updateClusterer(current);

cw.updateFinished();

評價(Evaluating)

評價一個clusterer,你可用ClusterEvaluation類,例如,輸出聚了幾個類:

import weka .clusterers.ClusterEvaluation;

import weka .clusterers.Clusterer;

...

ClusterEvaluation eval = new ClusterEvaluation();

// new clusterer instance default options

Clusterer clusterer = new EM();

clusterer.buildClusterer(data); // build clusterer

eval.setClusterer(clusterer); // the cluster to evaluate

// data to evaluate the clusterer on

eval.evaluateClusterer(newData);

// output # of clusters

System.out.println("# of clusters: " + eval.getNumClusters());

在density based clusters這種情況下,你可用交叉檢驗的方法去做(注意:用MakeDensitybasedClusterer你可将任何clusterer轉換成一下基于密度(density based)的clusterer)。

import weka .clusterers.ClusterEvaluation;

import weka .clusterers.DensitybasedClusterer;

import java.util.Random;

...

ClusterEvaluation eval = new ClusterEvaluation();

eval.setClusterer(clusterer); // the clusterer to evaluate

eval.crossValidateModel( // cross-validate

clusterer newData 10 // with 10 folds

new Random(1)); // and random number generator with seed 1

如果你想用指令行方式得到相同的結果,用以下方法:

import weka .clusterers.EM;

import weka .clusterers.ClusterEvaluation;

...

String[] options = new String[2];

options[0] = "-t";

options[1] = "/some/where/somefile.arff";

System.out.println(ClusterEvaluation.evaluateClusterer(new EM()

options));

聚類資料集(Clustering instances)

與分類唯一不同是名字不同。它不是用classifyInstances(Instance),而是用clusterInstance(Instance)。獲得分布的方法仍然是distributionForInstance(Instance)。

Classes to cluster evaluation

如果你的資料包含一個類别屬性,并且你想檢查一下産生的clusters與類别吻合程度,你可進行所謂的classes to clusters evaluation。Weka Exporer提供了這個功能,并用它也很容易實作,下面是一些必要的步驟。

l 讀取資料,設定類别屬性下标

Instances data = new Instances(new BufferedReader(new

FileReader("/some/where/file.arff")));

data.setClassIndex(data.numAttributes() - 1);

l 産生無類别的資料,并用下面代碼訓練

weka .filters.unsupervised.attribute.Remove filter = new

eka.filters.unsupervised.attribute.Remove();

filter.setAttributeIndices("" + (data.classIndex() + 1));

filter.setInputFormat(data);

Instances dataClusterer = Filter.useFilter(data filter);

l 學習一個clusterer,比如EM

EM clusterer = new EM();

// set further options for EM if necessary...

clusterer.buildClusterer(dataClusterer);

l 用仍然包含類别屬性的資料集評價這個clusterer

ClusterEvaluation eval = new ClusterEvaluation();

eval.setClusterer(clusterer);

eval.evaluateClusterer(data)

l 輸出評價結果

System.out.println(eval.clusterResultsToString());

屬性選擇(Attribute selection)

其實沒有必要在你的代碼中直接使用屬性選擇類,因為已經有meta-classifier和filter可以進行屬性選擇,但是為了完整性,底層的方法仍然被列出來了。下面就是用CfsSubsetEVal和GreedStepwise方法的例子。

meta-Classifier

下面的meta-classifier在資料在傳給classifier之前,進行了一個預外理的步驟:

Instances data = ... // from somewhere

AttributeSelectedClassifier classifier = new

AttributeSelectedClassifier();

CfsSubsetEval eval = new CfsSubsetEval();

GreedyStepwise search = new GreedyStepwise();

search.setSearchBackwards(true);

J48 base = new J48();

classifier.setClassifier(base);

classifier.setEvaluator(eval);

classifier.setSearch(search);

// 10-fold cross-validation

Evaluation evaluation = new Evaluation(data);

evaluation.crossValidateModel(classifier data 10 new Random(1));

System.out.println(evaluation.toSummaryString());

Filter

過濾器方法是很直接的,在設定過濾器之後,你就可以通過過濾器過濾并得到過濾後的資料集。

Instances data = ... // from somewhere

AttributeSelection filter = new AttributeSelection();

// package weka .filters.supervised.attribute!

CfsSubsetEval eval = new CfsSubsetEval();

GreedyStepwise search = new GreedyStepwise();

search.setSearchBackwards(true);