1.引入
本文是我學習《資料採礦與機器學習–WEKA應用技術與實踐》的筆記。該書電子版的連結是:http://download.csdn.net/detail/fhb292262794/8759397
前一篇博文總結了用Weka示範機器學習的演算法處理,主要是通過Weka3.8的用戶端軟體操作。
本文通過Java API調用來處理,這樣就可以在編程中應用Weka的機器學習演算法處理資料。
本書的執行個體是用weka3.7,我下載使用的是最新版的weka3.8,更新代碼以適應了weka3.8後,整理記錄如下。 1.分類(手把手教你寫代碼) 1.1 線性迴歸
預測樓價
樓價資料:
@RELATION house@ATTRIBUTE houseSize NUMERIC@ATTRIBUTE lotSize NUMERIC@ATTRIBUTE bedrooms NUMERIC@ATTRIBUTE granite NUMERIC@ATTRIBUTE bathroom NUMERIC@ATTRIBUTE sellingPrice NUMERIC@DATA3529,9191,6,0,0,205000 3247,10061,5,1,1,224900 4032,10150,5,0,1,197900 2397,14156,4,1,0,189900 2200,9600,4,0,1,195000 3536,19994,6,1,1,325000 2983,9365,5,0,1,230000
需求:根據該地區附近的房子資訊及樓價出售價格,預測新的房子的售價。該房子資訊是:houseSize:3198;lotSize:9669;bedrooms:5;granite:3;bathroom:1;請預測樓價。
由需求可知,需要處理的邏輯是:
1.載入樓價資料。
2.設定屬性資訊。
3.構建分類器並計算係數。
4.使用迴歸係數預測未知樓價。
代碼如下:
public static final String WEKA_PATH = "data/weka/"; public static final String WEATHER_NOMINAL_PATH = "data/weka/weather.nominal.arff"; public static final String WEATHER_NUMERIC_PATH = "data/weka/weather.numeric.arff"; public static final String SEGMENT_CHALLENGE_PATH = "data/weka/segment-challenge.arff"; public static final String SEGMENT_TEST_PATH = "data/weka/segment-test.arff"; public static final String IONOSPHERE_PATH = "data/weka/ionosphere.arff"; public static void pln(String str) { System.out.println(str); } @Test public void testLinearRegression() throws Exception { Instances dataset = ConverterUtils.DataSource.read(WEKA_PATH + "houses.arff"); dataset.setClassIndex(dataset.numAttributes() - 1); LinearRegression linearRegression = new LinearRegression(); try { linearRegression.buildClassifier(dataset); } catch (Exception e) { e.printStackTrace(); } double[] coef = linearRegression.coefficients(); double myHouseValue = (coef[0] * 3198) + (coef[1] * 9669) + (coef[2] * 5) + (coef[3] * 3) + (coef[4] * 1) + coef[6]; System.out.println(myHouseValue); }1.2 隨機森林
代碼:
@Test public void testRandomForestClassifier() throws Exception { ArffLoader loader = new ArffLoader(); loader.setFile(new File(WEKA_PATH + "segment-challenge.arff")); Instances instances = loader.getDataSet(); instances.setClassIndex(instances.numAttributes() - 1); System.out.println(instances); System.out.println("------------"); RandomForest rf = new RandomForest(); rf.buildClassifier(instances); System.out.println(rf); }1.3 元分類器
// 元分類器 @Test public void testMetaClassifier() throws Exception { Instances data = ConverterUtils.DataSource.read(WEATHER_NUMERIC_PATH); if (data.classIndex() == -1) data.setClassIndex(data.numAttributes() - 1); AttributeSelectedClassifier classifier = new AttributeSelectedClassifier(); CfsSubsetEval eval = new CfsSubsetEval(); GreedyStepwise stepwise = new GreedyStepwise(); stepwise.setSearchBackwards(true); J48 base = new J48(); classifier.setClassifier(base); classifier.setEvaluator(eval); classifier.setSearch(stepwise); Evaluation evaluation = new Evaluation(data); evaluation.crossValidateModel(classifier, data, 10, new Random(1234)); pln(evaluation.toSummaryString()); }1.4 預測分類結果(批量處理)
代碼:
/** * 利用訓練集預測測試集的分類,批量處理 */ @Test public void testOutputClassDistribution() throws Exception { ArffLoader loader = new ArffLoader(); loader.setFile(new File(SEGMENT_CHALLENGE_PATH)); Instances train = loader.getDataSet(); train.setClassIndex(train.numAttributes() - 1); ArffLoader loader1 = new ArffLoader(); loader1.setFile(new File(SEGMENT_TEST_PATH)); Instances test = loader1.getDataSet(); test.setClassIndex(test.numAttributes() - 1); J48 classifier = new J48(); classifier.buildClassifier(train); System.out.println("num\t-\tfact\t-\tpred\t-\terr\t-\tdistribution"); for (int i = 0; i < test.numInstances(); i++) { double pred = classifier.classifyInstance(test.instance(i)); double[] dist = classifier.distributionForInstance(test.instance(i)); StringBuilder sb = new StringBuilder(); sb.append(i + 1) .append(" - ") .append(test.instance(i).toString(test.classIndex())) .append(" - ") .append(test.classAttribute().value((int) pred)) .append(" - "); if (pred != test.instance(i).classValue()) sb.append("yes"); else sb.append("no"); sb.append(" - "); sb.append(Utils.arrayToString(dist)); System.out.println(sb.toString()); } }
這裡指定的是J48,是決策樹分類器,可以用其他更好的分類器替代,請比較效果選用分類器。 1.5 交叉驗證
代碼:
// 交叉驗證並預測 @Test public void testOnceCVAndPrediction() throws Exception { Instances data = ConverterUtils.DataSource.read(IONOSPHERE_PATH); data.setClassIndex(data.numAttributes() - 1); Classifier classifier = new J48(); int seed = 1234; int folds = 10; Debug.Random random = new Debug.Random(seed); Instances newData = new Instances(data); newData.randomize(random); if (newData.classAttribute().isNominal()) newData.stratify(folds); // 執行交叉驗證,並添加預測 Instances predictedData = null; Evaluation eval = new Evaluation(newData); for (int i = 0; i < folds; i++) { Instances train = newData.trainCV(folds, i); Instances test = newData.testCV(folds, i); Classifier clsCopy = AbstractClassifier.makeCopy(classifier); clsCopy.buildClassifier(train); eval.evaluateModel(clsCopy, test); // add prediction AddClassification filter = new AddClassification(); filter.setClassifier(classifier); filter.setOutputClassification(true); filter.setOutputDistribution(true); filter.setOutputErrorFlag(true); filter.setInputFormat(train); Filter.useFilter(train, filter); Instances pred = Filter.useFilter(test, filter); if (predictedData == null) predictedData = new Instances(pred, 0); for (int j = 0; j < pred.numInstances(); j++) predictedData.add(pred.instance(j)); } pln("classifier:" + classifier.getClass().getName() + " " + Utils.joinOptions(((OptionHandler) classifier).getOptions())); pln("data:" + data.relationName()); pln("seed:" + seed); pln(eval.toSummaryString("=== " + folds + " test ===", false)); // write data ConverterUtils.DataSink.write(WEKA_PATH + "predictions.arff", predictedData); }2.聚類(手把手教你寫代碼) 2.1 EM
@Test public void testEM() throws Exception { Instances instances = ConverterUtils.DataSource.read(WEKA_PATH + "contact-lenses.arff"); EM cluster = new EM(); cluster.setOptions(new String[]{"-I", "100"}); cluster.buildClusterer(instances); pln(cluster.toString()); }2.2 估計聚類器
// 評估聚類器的方式 3種 @Test public void testEvaluation() throws Exception { String filePath = WEKA_PATH + "contact-lenses.arff"; Instances instances = ConverterUtils.DataSource.read(filePath); // 第1種 String[] options = new String[]{"-t", filePath}; String output = ClusterEvaluation.evaluateClusterer(new EM(), options); pln(output); // 第2種 DensityBasedClusterer dbc = new EM(); dbc.buildClusterer(instances); ClusterEvaluation clusterEvaluation = new ClusterEvaluation(); clusterEvaluation.setClusterer(dbc); clusterEvaluation.evaluateClusterer(new Instances(instances)); pln(clusterEvaluation.clusterResultsToString()); // 第3種 // 基於密度的聚類器交叉驗證 DensityBasedClusterer newdbc = new EM(); double logLikelyhood = ClusterEvaluation.crossValidateModel(newdbc, instances, 10, instances.getRandomNumberGenerator(1234)); pln("logLikelyhood: " + logLikelyhood); }2.3 聚類並評估
@Test public void testClassesToClusters() throws Exception { String filePath = WEKA_PATH + "contact-lenses.arff"; Instances data = ConverterUtils.DataSource.read(filePath); data.setClassIndex(data.numAttributes() - 1); Remove remove = new Remove(); remove.setAttributeIndices("" + (data.classIndex() + 1)); remove.setInputFormat(data); Instances dataCluster = Filter.useFilter(data, remove); Clusterer cluster = new EM(); cluster.buildClusterer(dataCluster); ClusterEvaluation eval = new ClusterEvaluation(); eval.setClusterer(cluster); eval.evaluateClusterer(data); pln(eval.clusterResultsToString()); }2.4 輸出聚類點
@Test public void testOutputClusterDistribution() throws Exception { Instances train = ConverterUtils.DataSource.read(SEGMENT_CHALLENGE_PATH); Instances test = ConverterUtils.DataSource.read(SEGMENT_TEST_PATH); if (!train.equalHeaders(test)) throw new Exception("train data and test data not the same headers."); EM clusterer = new EM(); clusterer.buildClusterer(train); pln("id - cluster - distribution"); for (int i = 0; i < test.numInstances(); i++) { int cluster = clusterer.clusterInstance(test.instance(i)); double[] dists = clusterer.distributionForInstance(test.instance(i)); StringBuilder sb = new StringBuilder(); sb.append(i + 1).append(" - ").append(cluster).append(" - ").append(Utils.arrayToString(dists)); pln(sb.toString()); } }3.屬性選擇(手把手教你寫代碼) 自動屬性選擇
應用CfsSubsetEval及GreedyStepwise處理:
// 底層API屬性選擇 @Test public void testUseLowApi() throws Exception { ConverterUtils.DataSource source = new ConverterUtils.DataSource(WEATHER_NOMINAL_PATH); Instances data = source.getDataSet(); if(data.classIndex() == -1) data.setClassIndex(data.numAttributes() -1); AttributeSelection attributeSelection = new AttributeSelection(); CfsSubsetEval eval = new CfsSubsetEval(); GreedyStepwise search = new GreedyStepwise(); search.setSearchBackwards(true); attributeSelection.setEvaluator(eval); attributeSelection.setSearch(search); attributeSelection.SelectAttributes(data); int[] indices = attributeSelection.selectedAttributes(); pln(Utils.arrayToString(indices)); }4.其他 4.1 資料庫表操作
@Test public void testSaveCSV() throws Exception { DatabaseLoader loader = new DatabaseLoader(); loader.setUrl(SqlUtil.URL); loader.setUser(SqlUtil.USER); loader.setPassword(SqlUtil.PASSWORD); loader.setQuery("select question from question"); Instances data1 = loader.getDataSet(); if (data1.classIndex() == -1) data1.setClassIndex(data1.numAttributes() - 1); System.out.println(data1); CSVSaver saver = new CSVSaver(); saver.setInstances(data1); saver.setFile(new File("data/weka/baidubook-csvsaver.csv")); saver.writeBatch(); }4.2 過濾器
過濾
@Test public void testFilter() throws Exception { Instances instances = ConverterUtils.DataSource.read("data/weka/houses.arff"); instances.setClassIndex(instances.numAttributes() - 1); System.out.println(instances); String[] options = new String[2]; options[0] = "-R"; options[1] = "1"; Remove remove = new Remove(); remove.setOptions(options); remove.setInputFormat(instances); Instances newData = Filter.useFilter(instances,remove); System.out.println(newData); }
過濾並分類
@Test public void testFilterOnTheFly() throws Exception { Instances instances = ConverterUtils.DataSource.read("data/weka/weather.nominal.arff"); instances.setClassIndex(instances.numAttributes() - 1); System.out.println(instances); Remove remove = new Remove(); remove