Vérification de la version¶
In [1]:
#documentation -> https://fracpete.github.io/python-weka-wrapper/
#version du package "weka wrapper" installé
from importlib_metadata import version
ver = version("python-weka-wrapper3")
print(f"Version du package : {ver}")
Version du package : 0.3.2
Démarrage de la JVM¶
In [2]:
#démarrer la machine virtuelle
import weka.core.jvm as jvm
jvm.start(packages=True, max_heap_size="512m",
class_path=[
"C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/weka/lib/weka.jar",
"C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/org.jpype.jar"
])
DEBUG:weka.core.jvm:Adding user-supplied classpath=C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/weka/lib/weka.jar DEBUG:weka.core.jvm:Adding user-supplied classpath=C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/org.jpype.jar DEBUG:weka.core.jvm:Adding bundled jars DEBUG:weka.core.jvm:Classpath=['C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/weka/lib/weka.jar', 'C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/org.jpype.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\arpack_combined.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\core.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\mtj.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\python-weka-wrapper.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\weka.jar'] DEBUG:weka.core.jvm:MaxHeapSize=512m DEBUG:weka.core.jvm:Package support enabled DEBUG:weka.core.jvm:Adding user-supplied classpath=C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/org.jpype.jar DEBUG:weka.core.jvm:Adding bundled jars DEBUG:weka.core.jvm:Classpath=['C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/weka/lib/weka.jar', 'C:/Users/ricco/anaconda3/envs/env_weka/Lib/site-packages/org.jpype.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\arpack_combined.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\core.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\mtj.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\python-weka-wrapper.jar', 'c:\\Users\\ricco\\anaconda3\\envs\\env_weka\\Lib\\site-packages\\weka\\lib\\weka.jar'] DEBUG:weka.core.jvm:MaxHeapSize=512m DEBUG:weka.core.jvm:Package support enabled
Chargement et préparation des données¶
Chargement, inspection¶
In [3]:
#changement du dossier par défaut
import os
os.chdir("C:/Users/ricco/Desktop/demo")
In [4]:
#chargement du fichier "arff"
from weka.core.converters import Loader
loader = Loader(classname="weka.core.converters.ArffLoader")
data = loader.load_file("working_conditions.arff")
#https://fracpete.github.io/python-weka-wrapper/weka.core.html#module-weka.core.dataset
print(type(data))
<class 'weka.core.dataset.Instances'>
In [5]:
#liste des méthodes et propriétés
#dir(data)
In [6]:
#nombre d'observations
data.num_instances
Out[6]:
864
In [7]:
#nombre de variables
data.num_attributes
Out[7]:
7
In [8]:
#nom des variables
data.attribute_names()
Out[8]:
['profdev', 'conflict', 'regulat', 'jobvar', 'workgrp', 'standrds', 'working']
In [9]:
#afficher les 5 premières lignes
print(data[:5])
@relation working_conditions
@attribute profdev numeric
@attribute conflict numeric
@attribute regulat numeric
@attribute jobvar numeric
@attribute workgrp numeric
@attribute standrds numeric
@attribute working {good,poor}
@data
4.6,3.6,3,4.6,5.2,3.8,good
4.8,5,3.6,5.6,5,5.8,poor
6.4,5.2,4,4.8,5.8,5.8,good
5.6,6.4,1,6,6.4,6.4,good
6.4,6.6,3,6.4,6.2,4,good
Spécification de la variable cible¶
In [10]:
#est-ce qu'une variable cible a été définie ?
data.class_index
Out[10]:
-1
In [11]:
#ou encore
data.has_class()
Out[11]:
False
In [12]:
#la variable cible est la dernière dorénavant
data.class_is_last()
In [13]:
#son numéro
data.class_index
Out[13]:
6
In [14]:
#ou le nom de la variable corresp.
data.class_attribute
Out[14]:
@attribute working {good,poor}
Partition train-test¶
Mélanger au hasard les lignes tout d'abord (on ne sait pas comment les données ont été triées - ou pas - initialement).
In [15]:
#générateur de valeurs aléatoires
#avec une graine de départ
from weka.core.classes import Random
alea = Random(10112025)
#et mélanger les lignes
data.randomize(alea)
#5 premières lignes maintenant
data[:5]
Out[15]:
@relation working_conditions
@attribute profdev numeric
@attribute conflict numeric
@attribute regulat numeric
@attribute jobvar numeric
@attribute workgrp numeric
@attribute standrds numeric
@attribute working {good,poor}
@data
4.4,4.2,4.2,4.2,4.8,6.4,good
4.4,4.4,4.8,3.8,4,4.6,poor
7,7,1.6,5.2,7,5.4,good
3.2,4.8,4.2,5.8,6.2,4.4,good
5,5,2.6,5,5.4,5.2,good
Echantillon d'apprentissage (70%)
In [16]:
#70% pour le train - création d'un filtre
#attention, très bizarre, on retire les derniers (100-70) = 30% en fait
#pour définir le train !!!
from weka.filters import Filter
train_filter = Filter(
classname="weka.filters.unsupervised.instance.RemovePercentage",
options=["-P", str(100-70)])
#puis partition
train_filter.inputformat(data)
train = train_filter.filter(data)
#nombre d'instances
print(f"Nombre d'obs : {train.num_instances}")
#5 premières lignes
train[:5]
Nombre d'obs : 605
Out[16]:
@relation working_conditions-weka.filters.unsupervised.instance.RemovePercentage-P30.0
@attribute profdev numeric
@attribute conflict numeric
@attribute regulat numeric
@attribute jobvar numeric
@attribute workgrp numeric
@attribute standrds numeric
@attribute working {good,poor}
@data
4,4.6,4.4,5.2,5.4,4.2,good
3.2,4.2,4.9,4.8,6.2,6.4,good
3.2,4.8,4.2,5.8,6.2,4.4,good
4.8,5.6,4.4,4.6,3,4.8,good
5.2,5.6,3.6,5.8,6.4,6.2,good
Echantillon test (30% -- autre que les 70% en réalité)
In [17]:
#autre "-V" c.-à-d. inverser la sélection
#on conserve les derniers 30% pour le test
test_filter = Filter(
classname="weka.filters.unsupervised.instance.RemovePercentage",
options=["-P", str(100-70), "-V"])
#appliquer le filtre
test_filter.inputformat(data)
test = test_filter.filter(data)
#nombre d'instances
print(f"Nombre d'obs : {test.num_instances}")
#premières lignes
test[:5]
Nombre d'obs : 259
Out[17]:
@relation working_conditions-weka.filters.unsupervised.instance.RemovePercentage-P30.0-V
@attribute profdev numeric
@attribute conflict numeric
@attribute regulat numeric
@attribute jobvar numeric
@attribute workgrp numeric
@attribute standrds numeric
@attribute working {good,poor}
@data
4.4,4.2,4.2,4.2,4.8,6.4,good
4.4,4.4,4.8,3.8,4,4.6,poor
7,7,1.6,5.2,7,5.4,good
3.2,4.8,4.2,5.8,6.2,4.4,good
5,5,2.6,5,5.4,5.2,good
Modélisation - Evaluation (SVM Linéaire)(Explorer)¶
Apprentissage¶
In [18]:
#importation et instanciation du classifieur
#voir le paramétrage - noyau polynomial de degré 1
from weka.classifiers import Classifier
svm = Classifier(
classname="weka.classifiers.functions.SMO",
options=["-K", "weka.classifiers.functions.supportVector.PolyKernel -E 1.0"])
In [19]:
#modélisation
svm.build_classifier(train)
#comme SVM linéaire, on peut obtenir
#un modèle linéaire explicite
print(svm)
SMO
Kernel used:
Linear Kernel: K(x,y) = <x,y>
Classifier for classes: good, poor
BinarySMO
Machine linear: showing attribute weights, not support vectors.
-3.3445 * (normalized) profdev
+ -0.7607 * (normalized) conflict
+ 0.7921 * (normalized) regulat
+ 0.2263 * (normalized) jobvar
+ -3.2851 * (normalized) workgrp
+ 1.5302 * (normalized) standrds
+ 2.7034
Number of kernel evaluations: 9137 (69.219% cached)
Test¶
In [20]:
# évaluation sur l'échantillon test
from weka.classifiers import Evaluation
#sert simplement à indiquer la structure des données (attributs, classe)
#il n'y a pas de réapprentissage ici
#on aurait pu passer data en paramètre, ou encore test
evl = Evaluation(train)
#appliquer le svm sur l'échantillon test
evl.test_model(svm, test)
Out[20]:
array([1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 0., 0.,
1., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0.,
0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1.,
0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0.,
1., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0.,
0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 1., 0., 0., 1., 0.,
0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
1., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 1., 0., 1., 0.,
0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 1.,
0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.,
1., 0., 0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 1., 0., 1., 0., 0.,
0., 1., 0., 0., 1., 1., 0., 0., 1., 0., 0., 0., 1., 0., 1., 1., 1.,
0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
1., 1., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 1., 1., 0., 1.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0.,
0., 0., 0., 0.])
In [21]:
#résumé
print(evl.summary())
Correctly Classified Instances 199 76.834 % Incorrectly Classified Instances 60 23.166 % Kappa statistic 0.4439 Mean absolute error 0.2317 Root mean squared error 0.4813 Relative absolute error 50.577 % Root relative squared error 102.0835 % Total Number of Instances 259
In [22]:
#matrice de confusion
print(evl.confusion_matrix)
[[153. 22.] [ 38. 46.]]
GridSearch (paramètre de régularisation "cost") (Experiments)¶
In [23]:
#instancier la grille de recherche en validation croisée
#indiquer les options à tester
#si, plutôt qu'une plage de "cost", on veut éprouver des valeurs spécifiques
#il faut passer par une boucle
grid = Classifier(classname="weka.classifiers.meta.CVParameterSelection",
options=[
"-X", "5", # 5-fold cross-validation
"-P", "C 0.1 4.1 1", #varier C de 0.1 à 4 avec un pas de 1
"-W", "weka.classifiers.functions.SMO", # nom du classifieur
"--", "-K", "weka.classifiers.functions.supportVector.PolyKernel -E 1.0" #choix du noyau
])
In [24]:
#entraînement sur "train" (en validation croisée)
grid.build_classifier(train)
In [25]:
#paramètres optimaux indentifiés
print(grid.jobject.getBestClassifierOptions())
['-C', '0.1', '', '', '-L', '0.001', '-P', '1.0E-12', '-N', '0', '-V', '-1', '-W', '1', '-K', 'weka.classifiers.functions.supportVector.PolyKernel -E 1.0 -C 250007', '-calibrator', 'weka.classifiers.functions.Logistic -R 1.0E-8 -M -1 -num-decimal-places 4']
In [26]:
#évaluation sur le test du "meilleur modèle"
evl.test_model(grid,test)
#summary
print(evl.summary())
Correctly Classified Instances 390 75.2896 % Incorrectly Classified Instances 128 24.7104 % Kappa statistic 0.372 Mean absolute error 0.2471 Root mean squared error 0.4971 Relative absolute error 53.9488 % Root relative squared error 105.4314 % Total Number of Instances 518
Comparaison de modèles (Experiments)¶
Définir la liste des algos.
In [27]:
#liste des algos à tester-- tous avec leurs paramètres par défaut
#très compliqué dès lors qu'on souhaite introduire des paramètres spécifiques
algos = {}
#SMO linéaire
algos["SMO"] = {'classname':'weka.classifiers.functions.SMO'}
#J48 (ersatz de C4.5)
algos["J48"] = {'classname':'weka.classifiers.trees.J48'}
#régression logistique
algos["LR"] = {'classname':'weka.classifiers.functions.Logistic'}
#KNN
algos["KNN"] = {'classname':'weka.classifiers.lazy.IBk'}
#vérif.
for item,detail in algos.items():
print(item,detail)
SMO {'classname': 'weka.classifiers.functions.SMO'}
J48 {'classname': 'weka.classifiers.trees.J48'}
LR {'classname': 'weka.classifiers.functions.Logistic'}
KNN {'classname': 'weka.classifiers.lazy.IBk'}
In [28]:
#évaluation avec le schéma app/test
#pour disposer de la structure des données
eval = Evaluation(train)
#stockage des résultats
result = {}
#pour chaque méthode
for name,method in algos.items():
#instanciation
modele = Classifier(classname=method['classname'])
#entraînement
modele.build_classifier(train)
#évaluation
eval.test_model(modele,test)
#récupération de l'accuracy
result[name] = eval.percent_correct
#affichage des résultats
print(result)
{'SMO': 76.83397683397683, 'J48': 88.22393822393822, 'LR': 83.26898326898326, 'KNN': 87.45173745173746}
Arrêt de la JVM¶
In [29]:
#stop
jvm.stop()