Présentation
Les réseaux bayesiens sont aujourd'hui très prisés par beaucoup de projets. Savoir si un élément est du spam, retrouver la langue d'un texte, connaître le taux de chance que certains symptômes définissent une maladie... il existe un nombre incalculable d'utilisation du théorème de Bayes
L'intérêt du théorème de Bayes, c'est non pas de trouver une probabilité de trouver un élément dans un jeu de données, mais de savoir quel catégorie de données offre le plus de chance de tomber sur notre élément. Nous fonctionnons "à posteriori", "après coup".
Nous allons vous présenter le fonctionnement du théorème avec un exemple connu, puis nous le développerons avec la classe fournie avec Copix: Bayes (dans le package "tools", module "bayes").
Bayes ? Thomas Bayes
Thomas Bayes était un pasteur britannique, et mathématicien né en 1702 àLondres - mort le 17 avril 1761. Probabiliste de renommé, il découvrit le théorème de "probabilité à posteriori" à titre postume en 1764. Ce sont en fait ces études qui ont amené au théorème qu'il découvrit durant ses recherches.
Le théorème de Bayes s'inscrit dans son essai: "Essais sur la manière de résoudre un problème dans la doctrine des risques (Essay Towards Solving a Problem in the Doctrine of Chances - 1763)" publié à titre postume. (source wikipedia).
Définition du théorème par l'Exemple
Cet exemple va vous faire découvrir l'intérêt de ce théorème, nous étudirons un autre exemple plus complexe mais plus évident quant aux résultats par la suite.
Cet exemple est simple mais démontre la différence de résultat entre la probabilité de tirage, et la probabilité de source à posteriori. Nous allons imaginer un jeu de test: 2 sacs qui contiennent des boules blanches et noires.
- Le premier sac contient 10 boule noires et 30 boules blanches;
- Le second sac contient 20 boule noires et 20 boules blanches.
Nous n'avons donc pas autant de chance de trouver une boule blanche qu'un boule noire.\\ Si nous tirons dans le premier sac, nous avons 75% de chance de trouver une boule blanche.\\ Si nous tirons dans le second sac, nous avons 50% de chance de trouver une boule blanche.
Maintenant, nous nous bandons les yeux, nous piochons au hasard une boule... nous regardons et c'est une boule blanche. Alors à votre avis, quel est la chance que cette boule provienne du sac 1 ? ou du sac 2?
La probabilité simpliste nous dit que nous avons: ( 50% + 75% ) / 2 = 62.5% de chance de trouver une boule blanche mais pas quelle est la chance que ce soit depuis le sac 1.
Là où le théorème de Bayes va entrer en jeu, c'est après le tirage... à posteriori si nous avons tiré une blanche, quel est la chance que je l'ai tiré du sac 1 ou du sac 2 ? Cette question est certainement anodine, mais si vous répondez "62.5% de chance de la tiré du sac 1" vous avez faux. Vous allez voir.
Voici le théorème de Bayes sous sa forme simple et développée (plus intéressante pour la suite) que nous allons utilisé. Je ne suis pas probabiliste, je vous assure que cette formule est simple à lire. Laissez moi vous expliquer:
- Simple: P(B|A) = (P(B)*P(A|B)) / P(A)
- Développée: P(B|A) = ( P(B)*P(A|B) ) / ( P(B)*P(A|B) + P(B2)*P(A|B2) + P(B3)*P(A|B3) ... )
Je traduis:
- A --> boule blanche
- B --> sac 1
- B2 --> sac 2
- P(B|A) --> Probabilité que A provienne de B, dans notre cas: si je tire une boule blanche, quelle est ma chance que ce soit depuis le sac 1 ?
- P(B) --> La probabilité de piocher dans B, c'est à dire la chance de piocher dans le sac 1. Nous avons 2 sacs... et nous avions les yeux bandés, ==> 50%
- P(A|B) --> la chance de tirer A dans B ==> 75% de chance que ce soit une boule blanche dans le sac 1.
- P(B2) --> La probabilité de piocher dans le sac 2 ==> encore 50%
- P(A|B2) --> probabilité de piocher une boule blanche dans le sac 2 ==> 50% (20 boules de chaque couleur)
- P(A) --> probabilité de boule blanche sur l'ensemble des boules: 75 + 50 / 2 = 62.5%
Ce qui nous donne: P(B|A) = ( 50% * 75%) / (50%*75% + 50%*50%) = 0.6 ==> 60%\\ ou en forme simple:\\ P(B|A) = ( 50*75%) / (62.5) = 0.6 ==> 60%\\
Ce qu'il faut comprendre, c'est que "après coup", après avoir tiré au hasard une boule blanche, nous savons que nous avons 60% de chance qu'elle provienne du sac 1.
L'exemple qui va suivre est plus complexe mais mets en évidence l'importance de l'utilisation avancée du théorème de Bayes.
La différence entre la forme simple et la forme développeé ?
La forme simple estime que vous ayez autant de chance de vous trouver dans n'importe quelle catégorie. Notre exemple de boules blanches et noires est un cas typique. Effectivement, nous n'avons pas plus de chance de piocher dans le sac 1 que le sac 2.
La forme simple peut donc convenir. Mais le soucis se pose dans le cas ou nous devons nous pencher sur l'évidence du tirage. Le cas suivant demande l'utilisation de la forme développé puisque nous n'aurons pas autant de chance de trouver un pourriel qu'un message sain.
Un autre fonctionnement qu'il faut prendre en compte, c'est l'exemple de la détection de langue dans des textes classés. Si par exemple nous classions des textes anglais et français puis que nous desirions tester la langue d'un nouveau texte, il faudra estimer que nous avons autant de chance que le texte soit anglais ou français. Or, nous n'aurons pas forcément autant de texte anglais dans la base de données que de texte français.
A ce moment, la forme simple est précaunisée puisque l'évidence du tirage n'est pas à prendre en compte !
Notez bien que vous pourrez utiliser la classe Bayes de Copix pour les deux manières de calculer.
Spam ou non-spam ?
Nous allons voir un cas typique du théorème de Bayes, utilisé en partie par Spamassassin. Cette utilisation du théorème va s'avèrer extrêmement fiable si nous avons un grand jeu de données.
Partons du principe que nous avons 100 messages dont 10 sont classés en "pourriel" et 90 en "ok". Nous allons pouvoir traiter nos nouveaux messages entrant en analysant le contenu.
Sans nous baser sur un ensemble de mots clefs, en classant nos éléments, le théorème de Bayes va vous donner une estimation du risque que votre message soit à classer en pourriel.
Notre nouveau message contient 60% de mots que nous trouvons dans les spams, mais 80% de mots dans "ok". Cela signifie que nous avons des mots qui se trouvent dans les deux catégories. Notre logique humaine nous permettra de savoir si le message est un spam ou non, selon le sens... mais l'ordinateur ne saura pas le faire. De plus, nous voyons bien que nous n'avons que 10% de pourriel... nous n'avons donc pas autant de chance de tomber sur un spam qu'un message sain.
Par contre, grâce au théorème de Bayes, il est fort possible de connaitre le risque en connaissant, à posteriori, la probabilité que ce message puisse se trouver dans la catégorie "pourriel"
Définissons nos données:
- P(pourriel|message) --> à trouver, c'est la chance que notre message soit du spam
- P(pourriel) --> la probabilité de messages de spams que nous avons ==> 10%
- P(ok) --> la probabilité de messages sains que nous avons ==> 90%
- P(message|pourriel) --> probabilité de mots de notre nouveau message qui se trouve dans la catégorie "pourriel" ==> 60%
- P(message|ok) --> probabilité de mots de notre nouveau message qui se trouve dans la catégorie "ok" ==> 80%
Appliquons le théorème:\\ P(pourriel|message) = ( P(pourriel) P(message|pourriel) ) / ( P(pourriel)P(message|pourriel) + P(ok)|P(message|ok) )\\ soit:\\ P(pourriel|message) = ( 10 * 60) / (10*60 + 90*80)\\ P(pourriel|message) ~= .07692\\ soit environ 7.692% de chance que notre message soit un spam.
Maintenant, imaginez que notre boite soit très spamée. Par exemple 50% de message pourriels, et donc 50% de message sains. Le calcul donnerait:\\ (50 * 60 ) /(50 *60 + 50*80) ~= .4285714285\\ soit 42% de chance que le message arrivé soit un spam.
C'est bel et bien sur l'évidence des données que se base le théorème de Bayes.
C'est donc comme cela que nous aurons tout du moins une bonne estimation pour classer nos messages. De plus, en classant ce message en non-spam ou en spam va accroitre la base de connaissance utilisé par notre théorème. De ce fait, plus vous classez vos éléments, plus l'analyse sera fine et fiable.
C'est d'ailleurs pour cela que la plupart des systèmes utilisant un réseau Bayésien vous demande d'effectuer un classement manuel pendant quelques jours afin d'affiner la recherche.
Maintenant que nous avons vu comment fonctionne le théorème, il ne reste plus qu'à se simplifier la vie avec la classe Bayes.
Utilisation de la classe
Principe de base
Il vous suffit de créer un nouvel objet "bayes" avec _ioClass('bayes|bayes') ou _Class('bayes|bayes').
Cette classe fonctionne en mode "static" ou "db". En mode "db" les données sont enregistrée en base de données. En mode "static", tout se passe en mémoire. Le mode par défaut est le mode "static".
Si vous désirez utiliser le mode "db", procédez de la sorte:
$b = _ioClass('bayes|bayes'); $b->setDataMode("db","datasetname");
où "datasetname" est le nom de votre jeux de données. Cela est nécessaire pour ne pas interférer avec d'autres utilisation de la classe. Par exemple si vous voulez gérer du spam pour le module "wiki", donner le nom de dataset: "wiki_spamcheck".
Si le dataset existe en base, il sera alors utilisé et compléter lors des appels à getProba(), getBayes() et train().
Afin de respecter les notations probabilistes, getProba(A,B) correspondant à P(A|B) demandera en premier argument la valeur à tester, alors que getBayes(B,A) correspondant à P(B|A) prend en premier argument la catégorie.
Entrer des données
La méthode pour faire un apprentissage ne nomme "train". Deux arguments son nécessaires: le nom de la catégorie, et les termes à y insérer.
Exemple d'apprentissage de langue:
$b = _ioClass('bayes|bayes'); $b->train('fr','Ceci est une phrase en français'); $b->train('fr','Encore une phrase dans la langue de Molière'); $b->train('en','This is an english sentence'); $b->train('en','Once again a sentence in Shakespear language');
Ici, deux catégories seront enregistrées: "en" et "fr".
Connaitre la valeur Bayesienne
La métode à utiliser est "getBayes" qui prend en argument le jeux à tester, et la catégorie.
$b = _ioClass('bayes|bayes'); $b->train('fr','Ceci est une phrase en français'); $b->train('fr','Encore une phrase dans la langue de Molière'); $b->train('en','This is an english sentence'); $b->train('en','Once again a sentence in Shakespear language'); //$val contient la valeur en pourcentage de chance que la phrase soit en français $val = $b->getBayes('fr','cette phrase est-elle française ?');
Par défaut, c'est la forme développée qui est utilisé. Pour utiliser le calcul simple qui ne prend pas en compte les évidences, il faut assigner le troisième paramètre à "true":
$val = $b->getBayes('fr','cette phrase est-elle française ?',true);
$val sera alors calculé en estimant que vous aviez autant de phrases anglaise que française dans vos catégories.
Connaitre la valeur de probabilité simple
Vous pouvez connaitre la valeur de probabilité simple en appelant getProba(). Cette méthode demande en argument le nom de la catégorie et le jeu à tester:
$b = _ioClass('bayes|bayes'); $b->train('fr','Ceci est une phrase en français'); $b->train('fr','Encore une phrase dans la langue de Molière'); $b->train('en','This is an english sentence'); $b->train('en','Once again a sentence in Shakespear language'); //$val contient la valeur en pourcentage de mots en français trouvés dans la phrase $val = $b->getProba('cette phrase est-elle française ?','fr');
Exemple d'utilisation avec les boules blanches et noires
Ici est présenté l'exemple expliqué en haut de cet article. Nous allons voir et vérifier que la classe Bayes retourne le résultat attendu.
Bien entendu, je vous laisse créer votre module pour tester, ici je ne vous présenterai que le code à prévoir pour l'actiongroup.
class ActionGroupDefault extends CopixActionGroup { function processDefault (){ $bayes = _Class('bayes|bayes'); //1er sac: 30 boules blanches, 10 boules noires $j=0; for ($i=0;$i<40;$i++){ if($j<30){ $bayes->train("sac1","blanche"); $j++; } else{ $bayes->train("sac1","noire"); } } //2eme sac: 20 boules blanches, 20 boules noires $j=0; for ($i=0;$i<40;$i++){ if($j<20){ $bayes->train("sac2","blanche"); $j++; } else{ $bayes->train("sac2","noire"); } } $ppo = new CopixPPO(); $ppo->MAIN = "Valeur Bayesienne pour que la boule blanche provienne du sac 1: <br />"; $ppo->MAIN .= $bayes->getBayes('sac1','blanche')."%"; return _arDirectPPO($ppo,"generictools|blank.tpl"); //affichera 60% dans une page blanche } }
A venir
Vous pouvez tester à peu près ce que vous voulez à condition de mettre un maximum de données pour un traitement fin. Sachez que nous travaillons pour utiliser cette classe dans le moteur de recherche, le traitement des spam du module blog (à venir) et le test de protection de doublon de rapport de bug dans le futur module "bugtrax".
J'espère que cette classe vous permettra de vous faciliter la vie dans votre analyse de recherche de données. Je serai très content de connaître quelles applications vous avez voué à cette classe. Les commentaires de pages vous attendent :)
