TD N°1 - Nicolas Durand
Transcription
TD N°1 - Nicolas Durand
Polytech Marseille – Département Informatique 4ème année Fouille de données Annexe Programmation avec WEKA 1. Programmer avec Weka En travaux pratiques, nous avons utilisé les algorithmes de classification implémentés par Weka pour expérimenter des méthodes, résoudre des problèmes, en nous concentrant sur l'utilisation et les résultats fournis par ces implémentations, sans avoir à réécrire à chaque fois les algorithmes. Utiliser Weka nous permet par exemple de définir un protocole ou un programme JAVA générique d'apprentissage où il nous suffira de changer une ligne dans le programme pour pouvoir utiliser un classifieur à la place d'un autre. Pour pouvoir utiliser les algorithmes dans nos programmes, il nous faut : – Pouvoir définir ou charger à partir d'un fichier un ensemble d'exemples d'apprentissage. – Connaître les quelques méthodes qui permettent de définir, initialiser et utiliser un classifieur. – Connaître la méthode qui permet d'utiliser un classifieur pour trouver la classe d'un nouvel exemple. – Afficher, lire, interpréter les classifications obtenues. Référez vous à la documentation en ligne de Weka pour connaître les packages et les classes disponibles. http://weka.sourceforge.net/doc/ Compilation, exécution Pour pouvoir compiler et exécuter les programmes utilisant Weka, il faut demander à javac et java d'aller chercher les classes de Weka. Celles-ci sont à l'intérieur du fichier weka.jar. Compiler : javac -classpath "/usr/local/weka-3-6-7/weka.jar" Exemple.java Exécuter : java -classpath ".:/usr/local/weka-3-6-7/weka.jar" Exemple Sous Eclipse, il suffit de configurer le projet comme indiqué ci-dessus, il n'y a plus rien à faire… S'il y a besoin de plus de mémoire, utiliser l'option –Xmx512m pour indiquer, par exemple, qu'on veut utiliser 512Mo. Rappels Le cadre dans lequel nous travaillons, et les algorithmes que nous étudions et utilisons se basent sur le schéma de fonctionnement suivant : – On dispose d'un ensemble d'exemples (instances), chaque exemple étant défini par : – Sa description : c'est un ensemble de valeurs définissant cet exemple (par exemple ses dimensions, sa couleur . . .) – La classe qu'on lui a associée (avec l'aide d'un expert humain, par exemple. . .) – On fournit cet ensemble d'exemples à un programme qui va générer un classifieur. Un classifieur est un programme qui, quand on lui fournira un exemple, essaiera de deviner sa classe. Dit autrement, le programme prédit la classe d'un exemple à partir de sa description. Dit encore autrement, le programme cherche la relation qui lie la description à la classe. – Si tout s'est bien passé, on dispose maintenant d'un classifieur qui va agir un peu comme un "oracle", pour prédire la classe d'un exemple. 1 Polytech Marseille – Département Informatique 4ème année Fouille de données Définir un ensemble d'apprentissage Un ensemble d'apprentissage est défini dans Weka par la classe Instances (au pluriel !). Un objet de cette classe contient : – Une description de la structure des exemples (liste des attributs, type de chaque attribut, indice de l'attribut qui sert de classe). – La liste des exemples. Charger un ensemble d'exemples On peut charger un ensemble d'exemples à partir d'un fichier de suffixe ".arff". On utilise alors le constructeur : Instances(java.io.Reader reader) Exemple : reader = new FileReader(filename); instances = new Instances(reader); Définir la classe C'est la méthode : public final void setClass(Attribute att) où att est l'objet de type attribut qui servira de classe. On peut aussi fixer la classe en donnant son rang parmi les attributs. Usuellement, la classe est le dernier attribut de la liste, mais ca n'a rien d'obligatoire : // Indique que la classe est le dernier attribut de la description instances.setClassIndex(instances.numAttributes() - 1); Construire un classifieur Les arbres de décision correspondent à la classe weka.classifiers.trees.J48, et sont une sousclasse de weka.classifiers.Classifier. Dans le TP, on désire juste instancier un classifieur, l'entraîner sur un jeu de données, et l'utiliser pour classer de nouveaux exemples. On a donc besoin des méthodes : – buildClassifier(Instances data) – classifyInstance(Instance instance) Utiliser le classifieur Une fois que le classifieur est construit, on peut l'utiliser pour classer de nouveaux exemples. La méthode public double classifyInstance(Instance instance) de la classe Classifier retourne un réel qui correspond à la classe attribuée par le classifieur à l'exemple passé en paramètre. Dans le cas où la classe est discrète, comme par exemple quand le classifieur est un arbre de décision, il faut encore reconvertir ce réel pour retrouver la valeur nominale de l'attribut classe. Les concepteurs de Weka ont préféré coder en interne la valeur de chaque attribut par un réel, mais dans le cas des attributs nominaux, ce réel est en fait un entier, qui est l'indice de la valeur de l'attribut. 2 Polytech Marseille – Département Informatique 4ème année Fouille de données Par exemple, si la classe ne peut prendre que les trois valeurs first, second, third, et que l'on construit un classifieur, la réponse de ce classifieur pour un exemple quelconque sera une des trois valeurs réelles 0.0, 1.0, 2.0. La méthode String value(int j) de la classe Attribute retransformera ce réel en first, second ou third Exemples La figure 1 présente un exemple de programme construisant un arbre de décision avec des données d'apprentissage (train), et l'évaluant sur d'autres données (test). public class ClassifJ48 { public static void main(String[] args) { if (args.length < 2) { System.out.println("Usage: J48 <arff_train_file> <arff_test_file>"); System.exit(1); } String trainFile = args[0]; String testFile = args[1]; DataSource sourceTrain; DataSource sourceTest; Instances instancesTrain = null; Instances instancesTest = null; try { // donnees pour l'apprentissage sourceTrain = new DataSource(trainFile); instancesTrain = sourceTrain.getDataSet(); instancesTrain.setClassIndex(instancesTrain.numAttributes()- 1); // donnees pour l'evaluation sourceTest = new DataSource(testFile); instancesTest = sourceTest.getDataSet(); instancesTest.setClassIndex(instancesTest.numAttributes()- 1); //definir les options String[] options = { new String("-t"), trainFile, new String("-T"), testFile, // si pas de testFile // alors cross validation // parametres specifiques a J48 new String("-C"), new String("0.25"), new String("-M"), new String("2") }; // instancier le classifieur J48 classifieur = new J48(); // construire le classifieur avec donnees train classifieur.buildClassifier(instancesTrain); // evaluer avec donnees test String result = Evaluation.evaluateModel(classifieur, options); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } } Figure 1 – Exemple 1. 3 Polytech Marseille – Département Informatique 4ème année Fouille de données La figure 2 présente un programme construisant un classifieur bayésien aves des données d'apprentissage (train), l'évaluant par cross-validation, et l'utilisant pour classer de nouvelles instances public class Champignons { public static void main(String[] args) { String file = args[0]; String newFile = args[1]; DataSource sourceTrain; Instances instancesTrain = null; try { // donnees pour l'apprentissage (train) sourceTrain = new DataSource(file); instancesTrain = sourceTrain.getDataSet(); instancesTrain.setClassIndex(instancesTrain.numAttributes()- 1); // definir les options String[] options = { new String("-t"), file, new String("-x"), "4", new String("-i") }; // instancier le classifieur NaiveBayes classifieur = new NaiveBayes(); System.out.println("Construction du classifieur bayesien naif."); // construire le classifieur avec donnees train classifieur.buildClassifier(instancesTrain); System.out.println("\nEvaluation."); // evaluer par cross-validation String result = Evaluation.evaluateModel(classifieur, options); System.out.println(result); // classement de nouveaux champignons DataSource source = new DataSource(newFile); Instances dataset = source.getDataSet(); dataset.setClassIndex(dataset.numAttributes()- 1); System.out.println("\nClassement de nouveaux champignons :"); @SuppressWarnings("unchecked") Enumeration<Instance> e = dataset.enumerateInstances(); while(e.hasMoreElements()){ Instance i = (Instance)e.nextElement(); double prediction = classifieur.classifyInstance(i); System.out.println("Instance <"+i+">\n-> classe = "+prediction+" (" + dataset.classAttribute().value((int) prediction)+")"); // Attention, il faut afficher la valeur (symbolique) de la classe // et non le valeur "double" correspondant ! } } catch (Exception e) { e.printStackTrace(); } } } Figure 2 – Exemple 2. 4