M@XCode

Personal blog dedicated to computer science

Machine Learning : comment fonctionne la classification naïve Bayesienne

Un classifieur rapide et économe en puissance de calcul

Qu’est ce qu’un classifieur ?

Un classifieur est un algorithme permettant de définir la classe d’un objet suivant certaines de ses propriétés. Pour bâtir notre classifieur nous avons à notre disposition un ensemble d’objets dont on connait la classe par avance. Ce set d’objet est appelé set d’entrainement.

Par exemple, nous souhaitons classé des textes selon deux classes : écrit scientifique OU écrit romanesque (classification appelée binaire car composée de deux classes). Nous aurons donc à notre disposition un ensemble composé de 20 textes dont 12 classés “écrit romanesque” et les autres classés “écrit scientifique”.

Notre problème va donc consister à classer un nouveau texte en fonction de son contenu dans ces deux classes.

Utilisation de la théorie des probabilités

Notre algorithme aura la lourde tâche de fournir une classe à notre nouveau texte. Notre approche dans le cas de la classification naïve Bayesienne va être probabiliste. Nous allons chercher à déterminer la probabilité que notre objet appartient à la classe “écrit romanesque” à la probabilité que notre écrit appartient à l’autre classe. Nous allons ensuite comparer ces deux probabilités pour sélectionner la plus grande des deux.

Trouver la classe revient donc à calculer deux grandeurs et les comparer. Ce qui si on compare à l’algorithme des K voisins les plus proches (voir l’article ici) est très économe en calculs. Avant d’étudier plus en détail d’algorithme nous nous devons de faire un rappel sur les probabilités conditionnelles, qui constituent le fer de lance de cet algorithme.

Un passage obligé par les probabilités conditionnelles

La probabilité d’un événement A est un nombre variant entre 0 et 1 qui traduit le degrés de chance qu’un évènement A se produise. Plus on s’approche de la valeur 1 plus cet événement aura de chance de se dérouler.

Par exemple si on prend l’événement A “une élection va se dérouler en 2017”. La probabilité de cet événement est de 1 car il s’agit d’un événement certain.

Une probabilité conditionnelle est légèrement plus complète dans le sens où elle intègre dans son calcul un autre évènement. Ainsi une probabilité conditionnelle traduit le degrés de chance qu’un élément se produise sachant qu’un autre évènement s’est produit.

Par exemple, si on prend l’évènement A “réussite d’un test de maths”. L’évènement B “être mathématicien” et l’évènement C “avoir suivi un cursus scolaire littéraire”. Alors la probabilité de A sachant l’évènement B sera plus importante que la probabilité de A sachant l’événement C.

Mathématiquement on note une probabilité conditionnelle de deux façons :

$P_{B}(A) = P(A | B)$

La formule des probabilités conditionnelles se calcul ainsi :

$P_{B}(A) = P(A | B) = \frac{P(A \cap B)}{P(B)}$

La formule de Bayes

Fonctionnement théorique

Prenons un exemple afin de comprendre de quoi il en retourne. Imaginons un hôpital dans lequel on fait passer un test de dépistage de la tuberculose à certains patients. Les médecins dispose de données permettant d’établir que la fiabilité de ce test est de 90%, c’est à dire que 90% des tests sont fiables (les patients sont vraiment malades), il reste que 10% des tests sont des faux-positifs ou des faux-négatifs (le patient est bien malade mais dispose d’un test lui indiquant le contraire).

La formule de Bayes pourra nous donner la réponse à la question suivante : “sachant que le test de M.X est positif quel est la probabilité que celui-ci ne soit pas malade ?” On cherche donc à remonter dans le temps…

La formule de Bayes est définie ainsi :
$P_{B}(A) = \frac{P_{A}(B).P(A)}{P(B)}$

Si on n’applique la formule à notre exemple on aura :

  • A : Ne pas être malade
  • B : Avoir un résultat de test positif

Ainsi la probabilité de ne pas être malade sachant qu’on a un test de dépistage positif est le rapport entre la probabilité d’avoir un résultat positif sachant qu’on est malade multiplié par la probabilité de ne pas être malade et la probabilité d’avoir un résultat de test positif.

Application à un problème de classification

On va chercher dans notre cas la probabilité qu’un objet appartient à la classe i sachant qu’il a les propriétés x et y. Soit :


$P_{x,y}(C_i)$

Si on a deux classes (1 et 2) dans notre problème nous allons donc comparer deux grandeurs :


$P_{x,y}(C_1) et P_{x,y}(C_2)$

Dans tous les cas nous devrons isoler des features, des propriétés relatives aux objets que nous souhaitons classer. Si nous souhaitons donc classer un document nous prendons par exemple 1000 propriétés correspondant chacune à la présence ou non d’un mot courant dans le texte.

Pourquoi cet algorithme est-il naïf ?

Nous faisons deux assomptions un peu naïves sur notre données :

  • Les features ou les propriétés sont indépendantes les unes des autres. Or c’est rarement le cas, si on prend les classifications de textes la présence d’un mot peut être lié à la présence d’un autre mot !
  • On fait aussi le postulat que les features sont d’importances équivalentes dans la tâche de classification, ce qui n’est encore pas vraiment le cas. Pour certains textes la présence d’un mot ou d’un groupe de mot est très signifiant dans leur classe.

Application en NodeJS via le module “bayes”

Nous allons créer un script permettant de classer des phrases selon deux classes : les phrases provenant de romans et ceux d’écrits scientifiques.

  • Créez un nouveau dossier nommé bayes_classifier

    1
    $ mkdir bayes_classifier
  • Placez vous dans ce dossier

    1
    $ cd bayes_classifier
  • Initialisez npm

1
$ npm init
  • Après avoir répondu à l’ensemble des questions installez le module bayes
1
$ npm install bayes --save
  • Créez un nouveau fichier index.js

    1
    $ touch index.js
  • Dans ce fichier faites un require du module bayes.

    1
    2
    var bayes = require('bayes')
    var classifier = bayes()
  • Nous allons ensuite entrainer notre modèle pour reconnaître les écrits romanesques (fiction):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Make the algorithm learn the training data
    // The Great Gatsby
    classifier.learn('Il me sourit avec une sorte de complicité - qui allait au-delà de la complicité.', 'fiction')
    // 1984 (Orwell)
    classifier.learn('Celui qui a le contrôle du passé, disait le slogan du Parti, a le contrôle du futur. Celui qui a le contrôle du présent a le contrôle du passé.', 'fiction')
    // Le dernier Jour d'un Condamné (Hugo)
    classifier.learn('Maintenant je suis captif. Mon corps est aux fers dans un cachot, mon esprit est en prison dans une idée.','fiction')
    // Guerre et Paix (Tolstoï)
    classifier.learn('Oui, ils m ont accablé de reproches là-bas, et pour la guerre et pour la paix...','fiction')
    // Les Caractères (La Bruyères)
    classifier.learn('on n aime qu une seule fois : c est la première ; les amours qui suivent sont moins involontaires.','fiction')
    // O.Wilde
    classifier.learn("S'aimer soi-même, c'est l'assurance d'une longue histoire d'amour.","fiction")
  • Entrainons le maintenant pour reconnaître des écrits scientifiques (science):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* MAKE IT LEARN SCIENCE...
*/

//La sensibilité de l'activité mathématique aux ostensifs, BOSCH M. ; CHEVALLARD Y. ;
classifier.learn('Les écritures, symboles, mots, discours, graphismes et gestes mobilisés dans l activité mathématique - soit ce que nous appelons, pour leur caractère matériel et perceptible, les objets ostensifs','science')
// http://www.pourlascience.fr/ewb_pages/a/actu-des-panaches-de-vapeur-d-eau-observes-sur-europe-37639.php
classifier.learn("De nouvelles observations suggèrent que des geysers de vapeur d’eau jaillissent du pôle Sud de la lune glacée de Jupiter.","science")
// pourlascience
classifier.learn("La pédagogie haptique rend l'élève acteur de son enseignement. Il est alors plus réceptif aux connaissances qu'on veut lui transmettre.","science")
// http://cat.inist.fr/?aModele=afficheN&cpsidt=207222
classifier.learn("La méthode est fondée sur une approche distributionnelle de la sémantique. Les classes sémantiques qu'il est possible d'apprendre à partir d'un corpus analysé","science")
// https://hal.archives-ouvertes.fr/tel-00145147/
classifier.learn("Le panorama du Traitement Automatique des Langues est dominé par deux familles d'approches~: dans la première, la connaissance linguistique s'exprime sous forme de règles (grammaticales pour le traitement syntaxique, d'inférence pour le traitement sémantique, etc.)","science")
// https://pdfs.semanticscholar.org/57d0/2d681a479094bbd570b7992952281c39e146.pdf
classifier.learn(" Les outils disponibles de recherche d’information sur le Web ont une approche généraliste, ne prenant pas en compte les caractéristiques de l’utilisateur, ce qui limite la qualité des résultats","science")
  • Maintenant nous allons tester notre modèle avec deux exemples non classés.
1
2
3
4
5
6

// test the classifier
var f = classifier.categorize("Je la contemplai avec une haine intense, celle qu'un cheveu seul sépare de l'amour ardent.")
var s = classifier.categorize(" Par ailleurs la réalisation d’un système de ce type exige un assemblage de plusieurs techniques utilisées en apprentissage ou en recherche d’information ")
console.log("fiction",f);
console.log("science",s);

La console affiche :

1
2
fiction fiction
science science

On a un résultat concordant ! Pour les impatients voici le repo contenant ce morceau de code : https://github.com/maximilienandile/bayes_classifier

L'algorithme des K voisins les plus proches et son implémentation en NodeJs

Un algorithme simple mais gourmand en mémoire

L’algorithme des K voisins les plus proches repose sur la notion de distance entre des éléments classifiés et des nouveaux éléments à classer. La tâche principale de cet algorithme est de pouvoir prévoir une catégorisation d’un objet à partir d’objets dont on connait la déjà la catégorie.

L’idée est de décortiquer nos objets suivants des propriétés nominales ou numériques et pour classer un nouvel objet, il suffit d’identifier les éléments les plus proches, les plus semblables. C’est pourquoi on parle de voisins. La lettre K fait quand à elle référence au nombre de voisins que l’on doit étudier pour déterminer la classe.

Si in prends par exemple la classification de détection de la musique électronique, dont le but serait d’identifier les morceaux de musique électronique et ceux qui ne le sont pas. On pourrait imaginer un algorithme étudiant le tempo et le nombre de mots chantés par l’artiste. On aurait déjà une centaine de morceaux étudiés et classé. Lorsqu’on veut classer un nouveau morceau il suffirait de regarder ces deux indicateurs pour déterminer une ressemblance avec l’ensemble des morceaux déjà catégorisés…

Un exemple !

Pour bien comprendre notre algo nous allons prendre un exemple réel, celui de l’immobilier. Imaginons une agence pourvue d’un jeune stagiaire qui souhaiterait prédire le temps de vente d’un bien.

Toute chose égales par ailleurs il pourrait considérer deux facteurs : le nombre de parcs publics présents à moins de 20 min à pied du logement et le nombre de pièces.

Donnée d’entrainement

Ce dernier va donc constituer un set de données d’entrainement avec l’ensemble des transactions de son agence. Chaque bien est donc classé en deux catégories : ceux qui se sont vendus rapidement et ceux qui ont nécessité un temps plus important pour être cédés.

1
2
3
4
5
6
7

Bien 1 : Pièces : 1 - Parcs : 2 - Catégorie : vendu rapidement
Bien 2 : Pièces : 2 - Parcs : 3 - Catégorie : vendu rapidement
Bien 3 : Pièces : 1 - Parcs : 1 - Catégorie : vendu rapidement
Bien 4 : Pièces : 3 - Parcs : 0 - Catégorie : vendu rapidement
Bien 5 : Pièces : 1 - Parcs : 1 - Catégorie : vendu rapidement
...

Nouvel exemple à classer

Tout le bureau est excité, on rentre un nouvel appartement (2 pièces, et 2 parcs à proximité) ! Chacun y va de son pronostique, mais notre stagiaire a une méthode secrète :

Il va donc déterminer la distance de ce nouveau bien avec chacun des biens déjà vendus et il va déduire des trois biens les plus proches pour savoir ce qu’il en sera de ce bien.

L’algorithme va donc consister à un calcul de distance puis on va sélectionner la classe dominante parmi les 3 classes des 3 voisins les plus proches.

Mise en pratique avec Nodejs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// nous ferons grand usage du module
// lodash qui est bien pratique lorsqu'on
// travaille sur des tableaux avec nodejs (https://lodash.com/)
var _ = require('lodash')

// On crée une variable data contenant un Array de l'ensemble de nos
// appartements déjas vendus dans le passé (cahcun des apprt est dans un sous tableau,
// le premier chiffre présentant le nombre de pièces, le second le nombre de parcs)
var data_training = [[1,2],[2,3],[1,1],[3,0],[1,1]];
// Pour chaque élément de notre tableau on associe une étiquette
var labels = ["vendu", "non_vendu","non_vendu","vendu","vendu"]

//Partie 2 : Notre élément à classer
// 2 pièces, 2 parcs à proximité (l'ordre est capital !)
var element_to_class = [2,2]

// on crée un array vide
// pour contenir l'ensemble de nos résultats de cacul de distance
var array_of_results =[]

//Partie 3 : calcul de la distance
// on itère sur notre set d'entrainement
_.forEach(data_training, function(element_of_array, key) {
// Pour le premier passage dans la boucle
// element_of_array= [1,2]
// calcul ( Xa0 - Xb0 )^2
var difference_square_first_feature = (element_to_class[0]-element_of_array[0])*(element_to_class[0]-element_of_array[0])
// calcul ( Xa1 - Xb1 )^2
var difference_square_second_feature = (element_to_class[1]-element_of_array[1])*(element_to_class[1]-element_of_array[1])
// Calcul de la différence euclidienne
var euclidean_distance = Math.sqrt(difference_square_first_feature + difference_square_second_feature)

// Création d'un objet contenant le résultat du calcul de la distance
var result = {}
result.euclidean_distance = euclidean_distance
result.label = labels[key]

// on pousse cet objet dans notre tableau
array_of_results.push(result)
});

// nous devons maintenant trouver les trois classes dont la distance est la plus faible
// pour trouver la classe de notre élément à classe (element_to_class)
// Nous classons donc le tableau array_of_results par la propriété euclidean_distance
// de manières ascendate (asc)
var array_ordened = _.orderBy(array_of_results, ['euclidean_distance'], ['asc']);

// nous prenons les trois premiers voisins
var three_first = _.take(array_ordened, 3);

//three first sera égale à
//[ { euclidean_distance: 33, label: 'non_vendu' },
// { euclidean_distance: 36.013886210738214, label: 'non_vendu' },
// { euclidean_distance: 37, label: 'vendu' } ]

var vote_for_label_vendu = 0;
var vote_for_label_non_vendu = 0;

// pour chcun des trois premiers voisins nous comptons les votes
_.forEach(three_first, function(element,key){
if(element.label == "vendu"){
vote_for_label_vendu ++;
}else {
vote_for_label_non_vendu ++;
}
});

if(vote_for_label_vendu>vote_for_label_non_vendu){
console.log("Ce bien sera vendu rapidement");
} else {
console.log("Ce bien ne sera pas vendu rapidement");

}

Pour pouvoir exécuter ce code :

  1. Créez un dossier sur votre ordinateur (p.ex immobilier)
  2. Ouvres un terminal et déplacez vous avec la commande cd (change directory)

    1
    $ cd immobilier
  3. Installez lodash (via npm qu’il faut d’abord initialiser)

    1
    2
    $ npm init
    $ npm install lodash --save
  4. Créez votre fichier index.js (soit directement via le terminal ou bien avec votre éditeur de code préféré). Ce dernier contiendra le code

    1
    $ touch index.js
  5. Copiez le code précédent.

  6. Lancez l’algorithme !
    1
    $ node index.js

Pour bien comprendre je vous invite à reprendre ligne par ligne et d’essayer de le réécrire à votre sauce (l’utilisation de lodash (https://lodash.com/) n’est pas obligatoire mais très conseillée)

Pourquoi utiliser RAML pour designer une API RESTful

Qu’est ce qu’un API

On a souvent tendance à limiter l’API aux applications web, mais ces dernières sont définies et utilisées au sein de presque tous les langages, applications elles supportent même la programmation et le design d’interface pour le hardware.

API signifie “Application programming interface”, littéralement il s’agit d’une boite à outils contenants l’ensemble du matériel nécessaire pour bâtir un logiciel. Il s’agit de l’ensemble des blocs de construction permettant aux développeurs de bâtir un nouveau logiciel sur une base existante, cette base existante de fonctionnalités étant mise à la disposition du développeur via l’API.

Les API sont protéiformes, elles sont mises à disposition par les langages de programmation. Par exemple NodeJS fournit au développeur un ensemble de méthodes et de fonctions pré codées lui permettant de bâtir des programmes sans avoir à réinventer la roue à chaque reprise.

Prenons l’exemple simple d’un programme que nous souhaiterions construire dont l’unique but est d’ouvrir un fichier sur le disque et d’afficher son contenu. Afin d’utiliser l’API de Nodejs nous allons donc regarder sa documentation et chercher l’outil nous permettant d’ouvrir un fichier et de le lire. Nous trouvons la documentation de cette api sur cette page https://nodejs.org/api/fs.html.

Example de la documentation de l'API de NOdejs

Au passage, nous abordons ici un point fondamental, la documentation. Une bonne API est bien documentée afin de permettre au développeur de bâtir avec rapidité.

Les API Web

Nous avons abordé les API fournies par les langages de programmations et les programmes développés à partir de ces derniers mais ce qui va surtout nous intéresser dans cet article ce sont les API Web RESTful.

Une API web est là aussi un ensemble de ressources mis à la disposition des développeurs par d’autres développeurs d’application.

Dans la plupart des cas ou nous faisons usage des API Web il s’agit en effet de récupérer de la données d’autres applications. Par exemple nous pouvons utiliser l’API de Twitter pour récupérer l’ensemble des tweets d’un utilisateur. La récupération de données est usage mais pas le seul des API Web, Twitter ainsi mets à la disposition des développeurs des API d’authentification leur permettant de laisser gérer par Twitter l’authentification de leur site par Twitter ! C’est aussi le cas de Facebook par exemple avec le Facebook login button.

La tendance est grande et stratégiquement intéressantes pour les entreprises de mettre à la disposition leurs données à d’autres entreprises afin que ces dernières créent de la valeur par leur biais. Valeur qui est ensuite récupéré soit directement par un paiement à l’appel (au call) ou indirectement par la croissance de données récupérées sur l’utilisateur final…

REST

Derrière cette notion d’API Web on rajoute souvent le mot RESTful. Ce dernier terrorise pas mal de développeurs car il sous entend de nombreuses contraintes dans le développement d’une API. Au passage REST signifie (REpresentational State Transfer).

REST est un style architectural pensé dans les années 60 par Roy Fielding dans sa thése de doctorat (plus précisément chapitre 5 :)
(Pour les curieux voici une traduction française : http://opikanoba.org/tr/fielding/rest/).

Il va donc s’agir pour le créateur de l’API (vous par exemple) de suivre scrupuleusement les principes architecturaux de Roy afin de pouvoir vous vanter d’avoir bâti une API RESTful. Ces principes étant forgés pour une bonne raison, elles assurent que votre API se comportera de manière identique pour 5 utilisateurs comme pour 5 millions (notion de scalabilité). Elle assurera aussi que votre API sera compréhensible par tous le monde car tous le monde utilise le même modèle, (pas forcément le même langage de programmation mais du moins le même mode de fonctionnement)

Nous allons passer brièvement sur les contraintes (qui feront surement l’objet d’un autre article de ma part afin de creuser un peu plus le sujet)

Des contraintes architecturales pensées pour notre bien…

Une API RESTFul: :

  1. Est sans états (Stateless) : lorsque vous appelez l’API a plusieurs reprises et vous faites la même demande l’API ne se souviendra pas de vos demandes précédentes car elle n’a pas de mémoire. On ne stocke pas d’informations entre les requêtes. Cette contraintes est directement lié au protocole HTTP qui lui même ne conserve pas d’états.

  2. A la possibilité de mettre en “cache” certaines informations. Par exemple imaginons qu’un nombre important de développeur utilise l’API Open Graph de Facebook pour retrouver le profil de Zuck. On peut parier que cette requête est faite de nombreuses reprises. Facebook (le producteur de l’API) va sans aucun doute mettre en mémoire la réponse à cette requête afin d’économiser le temps de recherche dans ces bases de données. La seule contrainte imposée au développeur RESTful est de préciser à l’utilisateur que l’information est en cache. (Afin de ne pas afficher à nos propres utilisateurs des informations non à jour, si jamais Zuck décide de divorcer par exemple)

  3. Doit et peut avoir un système composé de serveurs multiples ou l’information est répliqué. Facebook par exemple n’a pas qu’un seul serveur, mais dispose d’une armada de machines qui peuvent répondre à la même requête. Cette caractéristique couplée à la première permet d’avoir un système qui va monter en charge sans embûche, car si il y a un pic de demande on peut rajouter des serveurs à la volée dans le système.

  4. Une interface uniforme : cette contrainte est un peu plus difficile à comprendre car elle regroupe elle même 4 contraintes. Ce qu’il est important de retenir est que chaque ressource (données mise à disposition : par exemple le profil Facebook, la liste d’amis, la liste des likes…) est correctement identifiée dans les call que l’on va faire à l’API. Je zappe volontairement la contrainte de manipulation des ressources via leur représentation ainsi que la contrainte HATEOAS, traité dans un prochain article. Vient ensuite la notion des messages de retour de l’API. Ces derniers doivent être auto-descriptifs, c’est à dire que le développeur doit comprendre immédiatement ce que l’API revoit. Par exemple en cas d’erreur le développeur doit prévenir le client de son API de la cause de cette dernière.

    Bâtir une API RESTful

    Tout cela est bel et bon, mais comment donc bâtir avec ces contraintes ? Le défi est important si l’on ne part pas d’une base. Eh bien figurez vous que vous n’êtes pas seul à avoir embrassé la difficulté du REST car des Frameworks existent et permettent de démarrer très rapidement sans avoir à trop transpirer sur vos claviers.

    Le sauveur est nommé RAML. RAML (RESTful API Modeling Language) est un langage vous permettant de désigné une API très rapidement respectant les principes architecturaux de REST. RAML permet de respecter les contraintes tout en étant très facile à lire (il repose sur le langage YAML, format de présentation de données très facile à lire développé par Clark Evans en 2001).

    Sa facilité de lecture lui permet d’être compréhensible par l’ensemble de la communauté des développeurs. En fait une API grâce à RAML est définit dans un simple fichier. Dans ce dernier on peut voir immédiatement ce que cette dernière fait. Procédé ainsi permet d’avoir un système maintenable sur le long terme.

    Unfortunately, people are fairly good at short-term design, and usually awful at long-term design (Roy Fielding)

RAML permet d’avoir une rédaction de spécification claire précise efficace et maintenable sur le long terme même si les équipes évoluent. Il est fini le temps ou un seul élément détient le savoir sur le fonctionnement d’une machine.

En guise d’introduction voici un exemple de fichier RAMML définissant une API simple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#%RAML 0.8
---
title: Facebook API
baseUri: http://api.fbbbb.com/{version}
version: v1

/users:
/{username}
get:
queryParameters:
first_name:
displayName: First Name
type: string
description: The First Name of an user
example: Joe
required: false
last_name:
displayName: Last Name
type: string
description: The Lasr Name of an user
example: Doe
required: false
put:
queryParameters:
access_token:
displayName: Access Token
type: string
description: Token giving you permission t put info
required: true

Ce fichier RAML définit une API imaginaire de Facebook (qui existe bel et bien) permettant de retrouver des informations sur un utilisateur en particulier (son prenom et nom).

Par exemple pour recevoir les informations concernant l’utilisateur “jonhdoe75” nous allons juste devoir faire un call sur l’URL suivant :

1
http://api.fbbbb.com/v1/users/jonhdoe75

La seconde étape consiste à construire l’ensemble de la logic derrière cahque call. Cela peut être fait grâce à Sosprey avec NodeJS (http://www.raml.org/blogs/osprey-out).

Voici un article à lire pour finir :

Comment installer PHPUnit sur macOs sur un projet PHP existant et écrire son premier cas de test

PHPUnit est un Framework puissant permettant d’automatiser des tests unitaires sur une application écrite en PHP.

Qu’est ce qu’un test unitaire

Usuellement il s’agit d’un morceau de code source permettant de tester si une fonction (ou une méthode d’une classe en POO) a le fonctionnement attendu.

Ainsi dans l’idéal, chaque fonction de notre application doit avoir un test associé. Le rapport entre le nombre de fonctions étant testés effectivement par un test unitaire ET le nombre de fonctions non testés donne une indication de la couverture du code (code coverage)

Chaque test unitaire doit être en général indépendant car associé à une seule et même fonction. Il se peut par contre que l’exécution de la suite de tests nécessite l’exécution d’un script de démarrage.

Par exemple si vous voulez tester que la fonction f renvoi bien le nombre de point qu’un de vos utilisateurs a récolté, il faudra bien qu’un script exécuté avant toute chose crée cette utilisateur et lui associé un nombre de points, afin de vérifier si le résultat est bien celui attendu.

Ecrire des tests unitaires permet d’avoir un code modulaire et évolutif qui sépare correctement l’interface de l’implémentation. Le module qui est suffisamment testé peut être réutilisé dans un développement ultérieur.

Avantages des tests unitaires

Avoir une application avec des tests unitaires permet de livrer en production des versions de votre application fonctionnelle en toute confiance. Par exemple, votre équipe a développé une grosse brique fonctionnelle susceptible d’impacter d’autres fonctionnalités du site. Livrer sur l’environnement de production une telle modification peut être catastrophique. Le passage en revue de tous les tests permettra de s’assurer que cette nouvelle fonctionnalité fonctionne de manière attendu sans détériorer l’existant (on appelle ce cas une régression fonctionnelle).

Rédiger des tests pour chaque fonction nécessite un temps additionnel pour le développeur mais en rédigeant des tests de qualité, cela améliorera intrinsèquement la documentation du code. Un nouvel arrivant pourra se référer aux tests pour comprendre le fonctionnement de telle fonction.

Installation de PHPUnit sur macOs

  1. Ouvrez votre terminal

  2. Dans ce dernier tapez les commandes suivantes

1
2
3
$ curl https://phar.phpunit.de/phpunit.phar -L -o phpunit.phar
$ chmod +x phpunit.phar
$ mv phpunit.phar /usr/local/bin/phpunit

NB : Ne pas recopier le symbole dollar ($), il s’agit ici d’une convention d’écriture dans le terminal.

Ces 3 commandes permettent de télécharger phpunit.phar. The chmodcommand va modifier les droits d’accès du fichier phpunit.phar. Plus précisément cela va donner le droit de lecture et d’exécution du fichier à tous les utilisateurs, les groupes et les autres parties du système.

La commande mvpermet quand à elle de changer de place ce fichier, de l’emplacement actuel à l’emplacement /usr/local/bin/phpunit

  1. Afin de vérifier si l’installation s’est bien déroulée veillez à exécuter cette commande :
1
$ phpunit -version

Si l’installation est OK vous devriez voir s’afficher dans votre terminal la ligne suivante :

1
PHPUnit 5.5.0 by Sebastian Bergmann and contributors.

(La version peut changer)

Configuration d’un projet existant et rédaction du premier test

  1. Sur un projet existant la première étape va consister à créer un dossier nommé tests à la racine de votre dossier.

Dans ce dernier vous allez placer tous vos tests. La règle étant que pour chaque classe de votre projet (si vous utilisez la POO) un fichier devra être créé.

La convention de nommage que j’utilise personnellement est la suivante :

1
NomDeVotreClasseTests.php

ou en camel case :

1
nom_de_la_classe_tests.php

  1. Ainsi si vous avez une classe Utilisateur définie au sein du fichier Utilisateur.php vous trouverez en miroir un fichier de test de cette classe nommé UtilisateurTests.php

Ce fichier aura toujours une composition identique :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//fichier : tests/UtilisateurTests.php
<?php
// Réglage de la time zone
date_default_timezone_set('America/Los_Angeles');
// Si vous utilisez composer (le gestionnaire de package, ajoutez cette ligne)
require_once __DIR__ .'/../vendor/autoload.php';

class UtilisateurTests extends TestCase
{

// il s'agit ici de la structure d'un test unitaire pour la méthode
// function_number_one de notre Utilisateur
public function test_function_number_one(){


}



}

Nous avons donc ici la classe permettant de tester la classe Utilisateur.
Cette classe se nomme UtilisateurTests et il s’agit d’une extension de la classe TestCase. Cette dernière est une classe interne à PHPUnit et elle nous permet d’utiliser des méthodes faciles pour écrire nos tests.

Ici un seul test unitaire est définit. En fait un test unitaire est une méthode de la classe de test (UtilisateurTests). Chaque test va donc tester une méthode de notre classe Utilisateur.

  1. L’étape suivante consiste à écrire effectivement notre test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
// fichier : tests/UtilisateurTests.php
// Réglage de la timezone
date_default_timezone_set('America/Los_Angeles');
// Si vous utilisez composer (le gestionnaire de package, ajoutez cette ligne)
require_once __DIR__ .'/../vendor/autoload.php';

class UtilisateurTests extends TestCase
{

// il s'agit ici de la structure d'un test unitaire pour la méthode
// function_number_one de notre Utilisateur
public function test_function_number_one(){

// instanciation de notre classe Utilisateur
$instance_utilisateur = new Utilisateur();
// execution de la méthode à tester
$resultat_function_number_one = $instance_utilisateur -> function_number_one(123);
// TEST du résultat attendu
$this->assertEquals("resultat attendu", $resultat_function_number_one);


}



}

Le cas de test consiste simplement à lancer la méthode function_number_one avec le paramètre 123.
Nous allons ensuite tester si le résultat d’exécution de cette méthode est bien égale au résultat attendu grâce à la ligne $this->assertEquals("resultat attendu", $resultat_function_number_one);

  1. Exécuter le test

Afin de lancer votre test il suffit d’ouvrir votre terminal et de taper :

1
2
$ cd le_dossier_contenant_votre_application
$ phpunit tests

La commande numéro 2 va dire à PHPUnit de lancer l’exécution des tests définit dans le dossier tests

Et voilà ! Bon tests

Pourquoi et comment créer des modules sur NodeJs

Qu’est ce qu’un module ?

Un module est une partie d’un programme informatique développé par le programmeur afin d’effectuer une série de tâches précises. Chaque module contient en général plusieurs fonctionnalités. On croise aussi l’appellation package qui recouvre exactement la même notion : une brique de fonctionnalité que l’on peut utiliser et réutiliser dans n’importe quel projet.

La notion de développement par module (ou modulaire) est née dans les années 1960 lors de l’émergence du paradigme de la programmation orientée objet et la création de langages plus structurés qui pouvait en faire l’usage.

On pourrait imaginer par exemple un module dont le but serait d’envoyer un email de bienvenue à nos nouveaux inscrits. Ce dernier pourrait être utilisé à nouveau dans tous les programmes suivants que nous allons créer qui nécessiteront un email de bienvenue.

Vous ne saisissez peut-être pas immédiatement l’intérêt de créer des modules sur nodejs, car lorsqu’on débute il est plus facile d’écrire directement le bout de code qui permet l’envoi de l’email de bienvenue directement après l’enregistrement du visiteur. Mais au rythme des développements on se fait souvent la réflexion que l’on a au final déjà écrit ce bout de code plusieurs fois… D’où l’intérêt de modulariser son développement.

Pourquoi écrire des modules

Le développement modulaire permet de diviser un projet extrêmement complexe en micros brique fonctionnelles très simples rendant le travail du développeur moins titanesque et plus facilement quantifiable.

Prenons un exemple : bâtir un site e-Commerce. Il s’agit d’un projet d’envergure mais au final le site e-Commerce n’est que l’addition de fonctionnalités simples : sauvegarde et mise à jour des produits dans une base de données, gestion de la connexion des utilisateurs, envoie des emails de confirmations… En pensant le projet de façon modulaire on peut aussi se servir dans l’incroyable bibliothèque open source disponible. En particulier sur Nodejs où les modules sont nombreux et tous regroupés au sein de npm. (https://www.npmjs.com/)

Le développement par module permet aussi une meilleure couverture du code via des tests unitaires. Ce n’est probablement pas le focus de nombre de développeurs lors de l’initialisation d’un projet, mais plus le nombre de contributeurs augmentent et plus la base de code augmente plus chaque mise en production sans suite de tests unitaires assurera des sueurs froides à l’équipe.

Le développement modulaire permet aussi de casser la fameuse loi de Brooks, qui assure qu’ajouter des développeurs à un projet subissant du retard ne fait qu’aggraver ce dernier. Mais pourquoi ça ? En raison du fait qu’un code modulaire est plus compréhensible et donc que les nouveaux arrivants sur le projet n’ont pas autant besoin de formations de leurs pairs pour être opérationnels.

Créer son propre module en Nodejs

  1. Dans le dossier de votre programme créez un dossier que vous nommerez de la même manière que votre module.

Concernant le nommage une règle est de mise : il est conseiller d’être le plus explicite possible et d’appeler son module suivant sa tâche principal. Nous allons ici écrire un module qui permettant de vérifier qu’une chaine de caractère est bien un email au bon format. Nous pouvons donc appeler notre module email_checker par exemple.

  1. Dans ce dossier vous allez créer un fichier nommé index.js qui va contenir le code de notre module.

  2. Dans ce fichier index.js, localisé dans /mon_projet/email_checker/index.js nous allons définir une fonctionnalité (ce sera donc une fonction) permettant d’effectuer notre tâche : vérifier si une chaine de caractère est bien un email.

1
2
3
4
5
6
7
8
9
10
11
//  fichier : index.js

var check_that_email= function(email_to_check){

// définition d'une expression régulière matchant les emails
var regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

// on retourne le test de notre chaine de caractère (email_to_check)
return regex.test(email_to_check);

}

Notez ici la construction étrage de notre module. Nous avons en fait défnit une variable nommée check_that_email qui contient une fonction.

Cette syntaxe est très courante et il vaut mieux vous y faire, mais elle est troublantes je vous l’accorde. Reste qu’on peut bien mettre une fonction dans une variable !

  1. Il suffit maintenant d’exposer cette fonction afin de pouvoir l’utiliser dans notre programme. Pour cela nous allons utiliser la syntaxe d’exportation propre à nodejs.
1
2
3
4
5
6
7
//  fichier : index.js
//
//....
//
module.exports = {
check_that_email: check_that_email
}

Dans ce morceau de code nous exportons notre fonction check_that_email sous le nom externe check_that_email afin de pouvoir nous en servir par ailleurs.
On aurait pu l’appeler autrement :

1
2
3
4
5
6
7
//  fichier : index.js
//
//....
//
module.exports = {
can_you_check_that_email_please: check_that_email
}

Le nom public de cette fonction (ou méthode) sera maintenant can_you_check_that_email_please !

Votre module est fini ! Bravo. Utilisons le maintenant.

Utilisation de notre module

Dans n’importe quel script de notre application nous pouvons appeler notre module ainsi :

1
var email_checker = require("chemin/vers/le/dossier/email_checker");

Et nous pouvons appeler une méthode ainsi

1
var result_of_email_checking = email_checker.check_that_email("john.doe@gmail.com");

Easy !

How to use tokenization, stopwords and synsets with NLTK (python)

This is my next article about NLTK (The natural language processing toolkit that can be used with Python). In this blog post I will highlight some of the key features of NLTK that can be useful for any developers having to treat and understand text programmatically.

Tokenization : the transformation of text into understandable chunks

In Natural Language processing a token is a small piece of text. There are different tokenization techniques, but you have to keep in mind that the process consists in cutting an amount of words into smaller bags of word. This is usually the very first task of a complex NLP algorithm.

Sentence tokenization

If you want to analyze all the sentences of a given text you can use the punkt tokenizer.

Let’s say that you have stored into the data variable the text you want to tokenize:

1
2
3
4
data= "This is my text. I want to tokenize it. But what is exactly tokenization ?"
import nltk.data
tokenizer = nltk.data.load('tokenizers/punkt/PY3/english.pickle')
tokenizer.tokenize(data)

The first line will import the necessary classes we will need to create a new tokenizer. On the second line we create a new variable that loads the English Punkt tokenize. Note that NLTK provides tokenizer for different languages!

The output of the following code chunk will be :

1
['This is my text.','I want to tokenize it.','But what is exactly tokenization ?']

Note that the punctuation is included into the list.

Word tokenization

The sentence scope might not be precise enough if you want to pursue a text analysis. You will probably need to go down to the word level:

1
2
3
from nltk.tokenize import TreebankWordTokenizer
tokenizer = TreebankWordTokenizer()
tokenizer.tokenize('This is my text')

Again we import the TreebankWordTokenizer class from nltk.tokenize. You have the to instantiate the class : TreebankWordTokenizer(). tokenizer will be an instance of that class. You can then call the method tokenize and provide it with your string of data.

The output will be :

1
['This','is','my','text']

Stop words

Stop words can be really interesting. In a text you have many of them, those stop words do not give vital information in the understanding of a text. Hence they can be removed in order to perform a better analysis of a corpus.

NLTK provides a list of usual stop words that you can use to filter a text.

1
2
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))

This list is exposed inside nltk.corpus. The first line will import the stopwords class. We will then access to the list by invoking the wordsmethod and providing 'english' as parameter to load only the English corpus.

How to filter a text from it’s stopwords.

1
2
words_list=["baltimore","you","love","hate","can"]
[w for w in words_list if w not in stop_words]

You first have a list of tokenized words. Then create another one with the following method :

  1. We take each element of the first list
  2. If the word is in the stop_words list we do not include it in the newly created list
  3. Else we include it.

Synset : how to get the definition of a word token

Wordnet is an English dictionary that gives you the ability to lookup for definition and synonyms of a word.
With a Synsets instance you can ask for the definition of the word :

1
2
3
from nltk.corpus import wordnet
s = wordnet.synsets('computer')[0]
s.definition()

The code is pretty simple !

In my next blog post I will talk about hypernyms and how to use it. I will also try to find a way to use NLTK with npm and provide a tutorial for installing it with npm. Thanks for reading!

How to install NLTK and compute basic statistics on a text

NLTK is a free library for NLP

NLTK (Natural Language Toolkit) is a free python library that is really helpful to execute NLP (Natural Language processing) tasks. The main challenge of NLP is to give the ability to an anlgorithm to understand the meaning of a text written by the human brain. The first application og NLP began during the 1950’s with the Georgetow Experiment (that was organised by IBM). The objective of that experiment was to show to the government the capabilities of machines in the translation field.

How to install it

For Windows Users :

  • Go to https://pypi.python.org/pypi/nltk and download the nltk-3.2.1.win32.exe file
  • After the downloading is complete double click on it and follow the instalation process
  • In order to check your installation, launch python. (Start menu > Python)
    and type on the black window :
1
>>> import nltk

If you have no errors you are good to go !

For MAC Users

The installation procedure is easier. Open your terminal and just type :

1
$ sudo pip install -U nltk

Import your own text from the filesystem

We will use the filesystem capability of python. The function open(filename,mode) will help us to import from the filesystem our file, in order to use NLTK against it.

1
2
f= open("my_file.txt","r")
text=f.read()

The first parameter is the name of the file (placed at the root of your project). The second argument is the treatment option of the file. For this application we only need to read the file. Thus we will use the r option.

The second line will store the raw input in the text variable.

Compute basic statistics

Words that appear in the same contexts

With this method you can find other words that appear in a similar context. The words are arranged in a way that the most similar words will appear first in the output list.

1
text.similar('banana')

Counting words

NLTK provides an easy method to count how many time a word appears in a text

1
text.count('alexa')

It will output an integer.

Get the number of words and punctuation

You can use the len function that will output an integer. Be careful, this function returns the number of words but also the number of punctuation signs in a corpus

1
len(text)

In the next blog article I will try to focus on text classifiers. (Bayesian text classifiers). Nltk gives you the power of building one really easily.

How to use promises with NodeJS

Like many others i started to develop algorithm in synchronous languages. The first language I learned was TI BASIC on my calculator in high school. With that language and like many others the instructions are processed line by line. If you write a bunch of lines your sure that the line 8 will be executed after the line 7.

When developping applications with Nodejs I faced the problematic of asynchronous execution. With this fabulous tool instructions are not blocking. If you write on line 10 a task that is pretty long to execute (opening and reading a file for instance), the system will not wait till the end of this task. It will go directly to the next one on line 11. The execution is much faster but it can become a nightmare for developers that have the habbit to develop in synchronous languages. You might for instance use a variable that is not existing because it’s affectation has not be done yet !

But there is an interesting tool that is not so complicated to use : promises.

Origine of promises

Promises where implemented in Javascript as of ECMAScript 6. Originally the term was first used in 1976 in a paper proposed by Daniel Friedman and David Wise (The impact of applicative programming on multiprocessing). I want to find and read this paper, but i did’nt found it. So if somebody is looking at this article and has a PDF version i’m highly interested.

How to use a promise

The better way to understand the way promises are working is to take a simple example.
Imagine that you have to perform a database lookup, and you want to send back the result to your user when it’s done.

Let’s take as example a route in the Express framework :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// this route will be fired when an user visit the url product/1
router.get('product/:product_id', function(req, res) {
// Put the parameter in a variable for more convenience
var product_id_in_param=req.params.product_id
// Let's do the request (we use a sequelize model)
products.findOne({
where: {product_id: product_id_in_param}

}).then(function(product_data) {
// This will be executed only when the database lookup is done

res.render('product_page', {product:product_data});
});
});

How to design a function that use a promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function my_function_with_a_promise(){

// We will return a promise object
return new Promise(function (resolve, reject) {
// We will for instance write information in a file
fs.writeFile("info.txt", "information to write into the file", function(err) {
// If there is an error
if(err) {
// We use the reject function with the error as parameter
// To tell our program that there was an error
reject("There was an error saving your file")
}
// Else, there was no error :
// We use the resolve function to tell our program that
// The instruction was successfully executed
// You can pass a greeting message
resolve("The file was saved!");

});

}

Use our function inside our main program

Let’s imagine we want to fire this function when the user of our application visit the url /execute :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
router.get('/execute', function(req, res) {

my_function_with_a_promise().then(function(greeting_message){

// The promise executed correctly !
// Let's render the success view
// With the parameter m that will represents the greeting message
res.render('success', {m:greeting_message});

}).catch(function(error_message){

// There was an error
// Let's warn our user by renderring the error view
// along with the error message
res.render('success', {m:error_message});



});

});

How to create a model with Sequelize for Nodejs

What is Sequelize

Sequelize is a SQL ORM for Nodejs. ORM means Object-Relational Mapping. This type of interface offers you to take some distance from the database administration hassle. It will create the tables, the indexes and auto generate and execute the queries directly for you. You don’t have to execute those very painful and repetitive tasks.

Sequelize is an ORM that is developed for database using SQL as a query language. MySQL for instance or Postgresql.

Installation

Go to the directory of your nodejs project :

1
$ cd my_project_forlder

Then use npm to install sequelize ad all it’s dependencies.
We will use the --saveoption to write in our package.json file that the project needs this module.
It will be more easier when you will deploy it, or update your packages.

1
$ npm install sequelize —-save

Configuration

Prerequesite having a running SQL database. For demonstration purpose we will use a MySQL one.

Create a new file in your project folder named sequelize.js with the following content :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Let's require our module
var Sequelize=require('sequelize');
// Let's create a new sequelize instance
// And connect tou our database
var sequelize = new Sequelize('database_name', 'username', 'password', {
host: 'XXX.us-west-2.rds.amazonaws.com',
dialect: 'mysql',

pool: {
max: 5,
min: 0,
idle: 10000
},
});

That’s it ! You have now a brand new connection to your server.
By default sequelize will use a pool connection. We give it in the last lines some additional options to configure the pooling system. Basically a pool of connection is a cache of database connections that can be used again and again in case of multiple queries to perform.

Creation of the model

You can see the model as an abstract representation of data that will be stored in your system. By definig a model you will warn Sequelize that you want to be able to insert, select, delete or update rows of data that will be stored and retrieved following the instruction you specified when you define your model.

Do not be affraid by this abstraction step. I have experienced with many of my students that being able to fully understand what is a model and what it brings to developer can be really hard. Just keep in mind that you are just defining the structure of a traditional database table with indexes, constraints and types.

1
2
3
4
5
6
7
8
// Creation of the model "client"
var client = sequelize.define('client', {
// Here are the columns of the table
family_name: {type: Sequelize.STRING},
surname: {type: Sequelize.STRING},
title: {type: Sequelize.STRING},
email: {type: Sequelize.STRING}
});

In this code we have created a model client, (that will be stored in the table client) with the columns family_name, surname …

Synchronization with the database

Creating a model is cool, but now you have to synchronize it with your physic database. If you forget this step you will get errors because the table will not be created and it will be impossible to insert or select data for a non existing table.

1
2
3
4
5
6
7
8
9
10

client.sync().then(function () {
// Table created
return client.create({
family_name: 'Jean',
surname: 'Dupont',
title : "Mr",
email : "jean.dupont@gmail.com"
});
});

This code will create the tables and the column defined before. Bonus : Sequelize will add automatically an id , createdAtand updatedAt column !

After the creatin is done it will create a client with the information provided in the create method of our model object (client).

Enjoy and use without moderation but becareful ! Never use (unless you know what you are doing) the method sync with the option force:true. It will drop the table if one exist with the same name and create a new one ! It can be a real disaster in a production environment !