Odkrywanie wiedzy z danych
Taki tytuł może świadczyć tylko o tym, że na tapetę wchodzi data mining. Ludzie od marketingu wolą pewnie ogólniejsze określenie "business intelligence", no ale chodzi mniej więcej o to samo, czyli o użycie paru fajnych algorytmów, aby przekopać tony danych i wyciągnąć jakieś nietrywialne wnioski.
Prawdę mówiąc, jeśli chodzi o Javę, to jakichkolwiek doświadczeń z BI nie mam żadnych poza researchem, który zrobiłem ostatnio z czystej ciekawości. Muszę natomiast stwierdzić, że Microsoft podszedł do sprawy całkiem profesjonalnie i w MS SQL Serverze od paru już wersji jest sporo przydatnych rzeczy do zabawy w drążenie danych, polecam zresztą stronę SqlServerDataMining.com. Firma Oracle też się chwali swoim pakietem o wiele mówiącej nazwie Oracle Data Mining. Nie jesteśmy jednak skazani tylko i wyłącznie na dostawców baz danych, chociaż wydaje się, że jak na Javę możliwości wyboru są dość ograniczone.
Warto zacząć od tego, że mamy dwa JSR poświęcone API dla data miningu, są to JSR-73 (Data Mining API) oraz JSR-247 (Data Mining 2.0). Pierwszy zgrzyt to informacja, że prace nad wersją 2.0 APi zostały wstrzymane. Drugim zgrzytem jest to, że raczej nie istnieje implementacja nawet pierwszej wersji standardu, której źródła można obejrzeć i z której można za darmo skorzystać. Liderem obu specyfikacji jest Mark Hornick, pracujący dla Oracle i, co za niespodzianka, w produktach tejże firmy należy szukać jakiejkolwiek implementacji opisywanego właśnie standardu. Można przejrzeć nawet przykłady użycia.
Ok, w takim razie co robić, jak żyć. Zasadniczo do wyboru mamy narzędzie Weka (sponsorowane przez Pentaho) oraz Rapid Miner (kiedyś znane jako YALE). Obydwa rozwiązania możemy używać "standalone", ale możemy je też wykorzystać w naszym kodzie. Postaram się przedstawić tutaj najprostszy przypadek użycia, czyli klasyfikację na klasycznym zbiorze danych (ang. "data set") Iris, opisującym parametry irysów (to takie kwiatki, gdyby ktoś pytał). Zbiór ten zawiera długości i szerokości dwóch różnych rodzajów płatków (ang. "petal" oraz "sepal") irysa, w sumie opisuje 150 roślinek z trzech różnych gatunków. Chodzi oczywiście o rozpoznanie gatunku na podstawie zadanych parametrów.

Kwiatek z własnego ogródka, jakby nie patrzeć - irys.
// załadowanie zbioru danych
DataSource source = new DataSource("/tmp/iris.arff");
Instances data = source.getDataSet();
data.setClassIndex(data.numAttributes() - 1);
// wybór algorytmu
Classifier classifier = new NaiveBayes();
classifier.buildClassifier(data);
// uczenie modelu
Evaluation evaluation = new Evaluation(data);
evaluation.evaluateModel(classifier, data);
// classifier.classifyInstance(inst) aby użyć modelu
String summary = evaluation.toSummaryString();
out.println(summary);
Powyższy kawałek kodu przedstawia pracę z Weką. Oczywiście tak na dobrą sprawę powinniśmy podzielić posiadane dane na dwa zbiory, jeden do uczenia, drugi do testowania, no ale powiedzmy, że dla celów prostej prezentacji tak też jest OK. Więcej informacji na temat używania Weka w kodzie można znaleźć na wiki projektu. Rozszerzenie *.arff to własny format danych Weki, ale równie dobrze można podpiąć jakikolwiek inny. Nieskalibrowany w żaden sposób algorytm miał 96% skuteczności w rozpoznawaniu gatunków w zbiorze, na którym się uczył.
// inicjalizacja
RapidMiner.init();
// załadowanie zbioru danych
Operator xrffSource = OperatorService.createOperator(XrffExampleSource.class);
xrffSource.setParameter(XrffExampleSource.PARAMETER_DATA_FILE, "/temp/iris.xrff");
// przetwarzanie danych
IOContainer container = xrffSource.apply(new IOContainer());
ExampleSet exampleSet = container.get(ExampleSet.class);
// uczenie modelu
Learner learner = OperatorService.createOperator(NaiveBayes.class);
Model model = learner.learn(exampleSet);
// informacje o modelu
out.println(model.toResultString());
// stosujemy wytrenowany model na znanych już danych
Operator modelApplier = OperatorService.createOperator(ModelApplier.class);
container = modelApplier.apply(container.append(model));
// wyświetlamy dane
ExampleSet resultSet = container.get(ExampleSet.class);
int i = 0;
Iterator<Example> examples = resultSet.iterator();
while (examples.hasNext()) {
out.print( ++i + ": ");
Example reader = examples.next();
Attributes attributes = reader.getAttributes();
out.println("predicted: " + reader.getValueAsString(attributes.getPredictedLabel()));
}
Co w takim razie można powiedzieć o Rapid Miner? Jak widać, jest o wiele bardziej skomplikowany. Właściwie to trudno użyć go jako biblioteki, wpierw i tak musimy go zainicjalizować, co skutkuje potężną dawką logów. Może on korzystać z algorytmów Weki. Samo jego API bardzo mi się nie podoba, tworzenie obiektów poprzez refleksję spowodowane jest tutaj "wstrzykiwaniem" kontekstu do każdego nowo tworzonego obiektu. Tak samo "brzydka" jest metoda setParameter zamiast standardowych setterów, a to przecież jeszcze chyba nadal Java. Wedle dokumentacji w bardziej skomplikowanych przypadkach warto używać klasy Process, aczkolwiek sama dokumentacja nie wygląda na zbyt aktualną, najprostsze przykłady się nie kompilują. Abstrahując od brzydoty API, narzędzia bardzo przyjemnie używa się poprzez klikalne GUI. Warto także obejrzeć filmiki (oczywiście oparte na jakiejś o wiele starszej wersji).

Proste drzewko decyzyjne w Rapid Minerze.
Podsumowując, jest czym się bawić, aczkolwiek wypada też mieć nieco żyłki odkrywcy ;). W każdym razie zachęcam, bo data mining to całkiem fajny temat do eksploracji, zwłaszcza jeśli chodzi o samo działanie algorytmów.





