Skip to content

Instantly share code, notes, and snippets.

@Syncrossus
Last active December 17, 2017 13:59
Show Gist options
  • Save Syncrossus/9649bf667502faeda938f9fb3586a4c9 to your computer and use it in GitHub Desktop.
Save Syncrossus/9649bf667502faeda938f9fb3586a4c9 to your computer and use it in GitHub Desktop.
Small Single-Attribute Bayesian network classifier. Sorry code is in French, intended recepient is French.
class Apprenant:
"""
Classe modelisant un apprenant bayesien
Attributs :
classes : classes identifiees dans la base d'apprentissage
valAttr : valeurs que peut prendre l'attribut des objets classables
tableau : Chaque ligne est une classe, chaque colonne une valeur d'attribut.
Chaque cellule contient l'effectif de la base d'apprentissage correspondant.
ex :
|1.6|1.7|1.8|1.9
C1| 5 | 3 | 2 | 0
C2| 0 | 2 | 3 | 5
Ce tableau signifie que la base d'apprentissage contient :
- 5 objets de classe C1 de valeur 1.6
- 3 objets de classe C1 de valeur 1.7
- ...
- 5 objets de classe C2 de valeur 1.9
Ce tableau est en realite un dictionnaire de maniere a ce que les classes
et les valeurs puissent ne pas etre des nombres entiers
probasAPriori : un tableau contenant la probabilite a priori de chaque classe
probasAPost : une matrice des probabilites a posteriori correspondant exactement a self.tableau mais avec
chaque valeur divisee par l'effectif appartenant a la classe dans la base d'apprentissage
"""
def __init__(self, baseApprentissage):
"""
:param baseApprentissage: une liste d'ObjetAClasser avec la classe deja definie
"""
self.classes = []
self.valAttr = []
for i in baseApprentissage:
if i.classe not in self.classes:
self.classes.append(i.classe)
if i.attribut not in self.valAttr:
self.valAttr.append(i.attribut)
self.tableau = {}
for i in self.classes:
for j in self.valAttr:
self.tableau[i,j]=0
for i in baseApprentissage:
self.tableau[i.classe, i.attribut] += 1
self.probasAPriori = self.calculerProbaAPriori()
self.probasAPost = self.calculerProbaAPost()
def calculerProbaAPriori(self):
"""
:return: Le tableau de probabilites a priori pour chaque classe (est appele par le constructeur)
"""
somme = sum(self.tableau.values())
effectifsClasses = [0 for i in self.classes]
classeCourante = 0
for i in self.classes:
for j in self.valAttr:
effectifsClasses[classeCourante] += self.tableau[i, j]
classeCourante += 1
return [i / somme for i in effectifsClasses]
def calculerProbaAPost(self):
"""
:return: Le tableau de probabilites a posteriori pour chaque paire (classe, valeur)
(est appele par le constructeur)
"""
probaAPost = self.tableau.copy()
effectifsAttr = [0 for i in self.valAttr]
attrCourant = 0
for j in self.valAttr:
for i in self.classes:
effectifsAttr[attrCourant] += self.tableau[i, j]
for i in self.classes:
probaAPost[i, j] /= effectifsAttr[attrCourant]
attrCourant += 1
return probaAPost
def classer(self, obj):
"""
:param obj: objet a classer de type ObjetAClasser
:return: la classe que l'apprenant determine pour l'objet
"""
#on calcule la probabilite d'appartenance a chaque classe
probasAppartenance = []
for i in self.classes:
# la probabilite d'appartenance a une classe est le produit de la probabilite a posteriori par la proba a priori
probasAppartenance.append(self.probasAPost[i, obj.attribut] * self.probasAPriori[self.classes.index(i)])
#on trouve l'indice du max dans le tableau de probabilites d'appartenance
#par construction, cet indice sera egalement l'indice de la classe correspondante dans le tableau de classes
# (par propriete de determinisme du parcours des tableaux en python, ceci ne serait pas vrai avec un dictionnaire)
return self.classes[probasAppartenance.index(max(probasAppartenance))]
class ObjetAClasser:
"""
Cette classe modelise un objet que l'apprenant doit classer.
Attributs:
attribut: Les objets a classer n'ont qu'un seul attribut, qui peut prendre n'importe quelle valeur.
Generaliser l'apprenant pour gerer n attributs aurait bien sur ete possible, mais aurait requis une
dimension de plus dans le tableau ce qui aurait ajoute beaucoup de complexite pour un benefice
pedagogique quasiment nul.
classe: La classe de l'objet. Doit etre precisee si l'objet est destine a faire partie de la base d'apprentissage.
Ce programme a ete fait avec l'hypothese que la classe est de type entier, il pourrait ne pas fonctionner
ce n'est pas le cas
"""
def __init__(self, attribut, classe=-1):
self.attribut = attribut
self.classe = classe
if __name__ == '__main__':
"""
Ici on cree un apprenant avec la base d'apprentissage presentee en exemple dans la classe Apprenant, a savoir :
|1.6|1.7|1.8|1.9 <----- valeurs pour l'attribut
1| 5 | 3 | 2 | 0 <--.-- classes
2| 0 | 2 | 3 | 5 <--'
Cet exemple est bien sur le meme exemple que celui vu en cours, a savoir que les valeurs sont des tailles (en m)
et les classes sont des sexes (masculin / feminin)
"""
app = Apprenant([ObjetAClasser(1.6, 1), ObjetAClasser(1.6, 1), ObjetAClasser(1.6, 1), ObjetAClasser(1.6, 1), ObjetAClasser(1.6, 1),
ObjetAClasser(1.7, 1), ObjetAClasser(1.7, 1), ObjetAClasser(1.7, 1), ObjetAClasser(1.7, 2), ObjetAClasser(1.7, 2),
ObjetAClasser(1.8, 1), ObjetAClasser(1.8, 1), ObjetAClasser(1.8, 2), ObjetAClasser(1.8, 2), ObjetAClasser(1.8, 2),
ObjetAClasser(1.9, 2), ObjetAClasser(1.9, 2), ObjetAClasser(1.9, 2), ObjetAClasser(1.9, 2), ObjetAClasser(1.9, 2)])
#Puis on classe quatre objets (un pour chaque valeur possible d'attribut)
#sans preciser la classe puisqu'elle ne nous interesse pas lorsqu'on classe
print(app.classer(ObjetAClasser(1.6)))
print(app.classer(ObjetAClasser(1.7)))
print(app.classer(ObjetAClasser(1.8)))
print(app.classer(ObjetAClasser(1.9)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment