Syndiquer le contenu
Mis à jour : il y a 15 heures 27 min

Vers une norme AFNOR pour le clavier français

10 juin, 2017 - 17:57

Suite à la 1re publication de l’AFNOR le 15 janvier 2016, annonçant son intention de travailler sur une norme pour le clavier informatique français, appuyée le 27 novembre 2016 dernier par un appel à rejoindre la commission de travail sur le projet (évoqué ici lors d’un journal), l’AFNOR invite aujourd’hui et jusqu’au 9 juillet prochain à commenter son projet de norme.

L’AFNOR, ou Association française de normalisation (abrégée AFNOR), est l’organisation française qui représente la France auprès de l’Organisation internationale de normalisation (ISO) et du Comité européen de normalisation (CEN).

Sommaire Contextualisation et présentation du projet

Partant du constat, d’une part de l’absence d’une norme française décrivant le clavier utilisé sur les différents matériels informatiques traditionnels, à la différence de la plupart de ses voisins européens, et d’autre part des difficultés dactylographiques dues à la trop grande diversité de clavier AZERTY proposés par les fabricants sur le marché français, l’AFNOR, sur l’impulsion du ministère de la Culture et de la Communication (par l’intermédiaire de la délégation générale à la Langue française et aux Langues de France) et en lien avec les industriels et les représentants des utilisateurs du secteur, présente un projet normatif pour élaborer une norme française de clavier.

En effet, la publication Vers une norme française pour les claviers informatiques de l’AFNOR introduit les problèmes les plus courants dans la plupart des variations de la disposition commune de clavier français dite AZERTY dérivée de la disposition QWERTY : diversité de placement de symboles courant comme « € » ou encore « @ », difficultés ou absence d’accès aux majuscules accentuées, absence du « Ç ». La publication signale également qu’un travail pourrait être mené pour ajouter certains symboles absents mais pouvant s’avérer utiles comme le « [[pour mille]] » (‰) ou les symboles « supérieur ou égal » (≥) et « inférieur ou égal » (≤). L’organisme évoque également la grande variété des claviers virtuels proposés sur les tablettes et téléphone intelligents, introduisant par la saisie prédictive une plus grande facilité d’utilisation des mots employant des caractères spéciaux, bien que cette compensation reste insuffisante à ses yeux. Un autre objectif est également d’homogénéiser à terme le parc de claviers informatiques en France.

Malheureusement en mentionnant les dispositions de claviers utilisées sous certains systèmes d’exploitation, la publication n’évoque que les systèmes Windows et Apple, ou Android pour les tablettes tactiles ou téléphones intelligents.

Deux configurations de clavier proposées

Là où les dispositions envisageables devraient selon l’AFNOR permettre l’utilisation aisée des différentes langues présentes sur le territoire français, l’organisme donne à titre d’exemple le clavier breton, dit C’HWERTY, ou les exigences de l’occitan.

Plus globalement, cette disposition serait envisagée pour rendre plus facile en premier lieu l’écriture des langues européennes utilisant les caractères latins, ainsi que l’écriture des caractères spécifiques des langues de nos pays voisins non basées sur le latin, comme par exemple l’eszett en allemand (ß) ou le O barré (Ø) utilisé dans certaines langues nordiques, et en second lieu rendre accessibles les caractères diacritiques spécifiques à certaines langues d’Europe centrale et de l’Est telle que la brève (Ă) ou encore la virgule souscrite (Ț).

À ce titre, deux dispositions pour les claviers bureautiques (105 ou 72 touches) utilisés en France sont proposées par la Commission de normalisation dédiée aux « interfaces utilisateur » : l’une très proche de la disposition AZERTY (jusqu’alors jamais normalisée en France) et l’autre s’appuyant sur la disposition ergonomique et libre BÉPO (CC-BY-SA).

La disposition adaptée de l’AZERTY vise à minimiser l’effort d’adaptation des utilisateurs du clavier AZERTY actuel (meilleure rétrocompatibilité avec les logiciels et notamment les pilotes de claviers existants) mais conserve les risques de troubles musculo‐squelettiques associés à cette disposition.

La disposition BÉPO a été conçue pour réduire ces risques de troubles musculo‐squelettiques associés à la disposition AZERTY et obtenir une disposition la plus ergonomique et efficace possible pour la saisie du français et d’autres langues à alphabet latin, mais elle nécessite un effort d’adaptation plus important pour les utilisateurs de l’actuel clavier AZERTY.

Carte complète de la disposition AZERTY :

Carte complète de la disposition BÉPO :

Je vous renvoie à la norme en cours de travail pour plus de précisions sur les combinaisons et utilisations de touches mortes possibles, les cartes de disposition ne permettent pas de juger de toutes les utilisations. Le document présenté permet également de suivre la démarche de l‘AFNOR dans son choix de conception d’une disposition de clavier AZERTY adaptée.

Appel à commentaires

La Commission de normalisation dédiée aux « interfaces utilisateur » lance une enquête publique visant à recueillir un maximum d’avis sur le projet. La norme elle‐même est envisagée sans aucun caractère obligatoire, mais la conformité des matériels à cette norme pourrait être exigée dans le cadre des marchés publics de l’administration et cette future norme pourrait ainsi avoir un impact non négligeable.

L’appel à commentaires concerne les points suivants :

  • les principes du clavier ;
  • l’inventaire des signes qui peuvent être saisis ;
  • le détail des dispositions.

La Commission indique que les commentaires seront traités de façon anonyme lors d’une réunion de dépouillement à laquelle tous les répondants à l’enquête pourront participer.

L’AFNOR demande spécifiquement de faire des propositions de modification. Le commentaires prend la forme d’un tableau normalisé ISO au format Office Open XML .docx (malheureusement).

Accéder aux documents et donner son avis nécessitent la création d’un compte sur le site de l’AFNOR (attention, formulaire de création non sécurisé).

Télécharger ce contenu au format Epub

Lire les commentaires

OpenRouteService : routage en ligne basé sur OpenStreetMap

9 juin, 2017 - 17:43

OpenRouteService est un service de routage basé sur les données OpenStreetMap développé par l’université de Heidelberg en Allemagne.

NdM : le logiciel est sous licence MIT.

Routage

Il est possible de demander son chemin avec un nombre assez important de moyens :

  • voiture ;
  • véhicule lourd (normal, transports de bien, bus, agricole, engin forestier ou livraison) ;
  • cycle (normal, le plus sûr, pour le paysage, c’est‐à‐dire passant proche de points d’intérêts, VTT, course ou électrique) ;
  • piéton (normal ou randonnée) ;
  • chaise roulante.

Le service de routage essaie d’utiliser une bonne partie des informations d’OSM et propose ainsi de catégoriser plusieurs éléments au sein d’un trajet :

  • la surface rencontrée ;
  • le type de voies empruntées ;
  • la pente ;
  • la pertinence des tronçons par rapport au moyen de locomotion demandé.

La pertinence peut s’avérer importante (si les données sont présentes dans OSM) lors d’un routage en chaise roulante ou en vélo, par exemple.

Isochrones

Le service vous permet de voir une estimation des isochrones à partir d’un lieu donné et avec un moyen de locomotion donné. Une isochrone est une ligne à une distance ou un temps donné du point d’origine.

Par défaut, il est possible de voir l’isochrone de 30 et 60 minutes, mais il est possible de changer cela assez facilement.

API développeurs

Tout ce service est accessible via des API en libre accès, mais avec un nombre de transactions limités pour éviter la surcharge du service. Pour cela, n’oubliez‐pas de demander une clef.

Conclusion

OpenRouteService est un service de routage qui fonctionne plutôt bien et basé sur les données d’OSM. Je suis ce service depuis quelques années et il devient meilleur de jour en jour.

Si vous ne l’avez pas essayé, jetez‐y un coup d’œil et dites ce que vous en pensez !

Télécharger ce contenu au format Epub

Lire les commentaires

Prédire la note d’un journal sur LinuxFr.org

9 juin, 2017 - 09:30

Cette dépêche traite de l’exploration de données sur des données issues de LinuxFr.org.

Ayant découvert récemment scikit-learn, une bibliothèque Python d’apprentissage statistique (machine learning). Je voulais partager ici un début d’analyse sur des contenus issus de LinuxFr.org.

Avertissement : je ne suis pas programmeur, ni statisticien. Je ne maîtrise pas encore tous les arcanes de scikit-learn et de nombreux éléments théoriques m’échappent encore. Je pense néanmoins que les éléments présentés ici pourront en intéresser plus d’un(e).

Tous les scripts sont codés en Python et l’analyse à proprement parler a été réalisée à l’aide d’un notebook Jupyter. Un dépôt contenant les données et les scripts est disponible sur GitLab.

Sommaire Prédire la note d’un journal

Il y a eu récemment une vague de journaux politiques sur DLFP. La note de la plupart de ces journaux était assez basse. Par ailleurs, on lit régulièrement ici des personnes qui se plaignent de la note de leurs articles. Bien souvent, des gens postent des contenus incendiaires, parfois en rafale. Je me suis demandé si cela est évitable.

Est-il possible de prédire la note d'un journal en fonction de son contenu? Le problème est ambitieux mais il permettrait aux auteurs d'avoir une idée de l’accueil qui sera réservé à leur prose.

Prédire un score me paraît hasardeux, c'est pourquoi j'ai préféré classer les journaux dans 4 catégories en fonction de leur note, n (en english car il est bien connu que ça improve la productivitaÿ) :

  • n < -20 : Magnificent Troll ;
  • -20 < n < 0 : Great Troll ;
  • 0 < n < 20 : Average Troll ;
  • 20 < n : Qualitaÿ Troll.

Vous l'aurez compris, tout contenu est un Troll, car je pense que nous sommes tous le troll d'un autre.

Obtenir les données

Il n'existe pas à ma connaissance de base de données de DLFP disponible pour tests. Après avoir lu deux journaux précédents, j'ai décidé de construire une moulinette afin d'aspirer une partie du contenu.

Approche 1: le flux atom

Dans un premier temps, j'ai utilisé le flux atom des journaux à l'aide de la bibliothèque feedparser. Le script fonctionne et l'approche est très simple mais malheureusement, la quantité de données est trop limitées. Par ailleurs, le score d'un contenu n'est pas disponible dans les flux. J'ai donc changé mon fusil d'épaule.

Approche 2: l'heure de la soupe

Afin d'augmenter le volume de données, il faut parcourir la page https://linuxfr.org/journaux?page=x et collecter tous les liens vers les différents journaux. Chaque journal est ensuite analysé. Dans un premier temps, les informations suivantes sont utilisées : le nom de l'auteur, le titre du journal, l'URL, le contenu du journal, sa note.

La moulinette s'appuie sur la bibliothèque Beautiful Soup4. Les données sont enregistrées dans un fichier CSV. Étant donné que le contenu des journaux est très varié, j'ai choisi les caractères µ et £ en tant que délimiteur et séparateur, respectivement.

Analyse des données

L'analyse suivante est réalisée à l'aide du fichier diaries_classification.ipynb. La lecture du fichier CSV linuxfr.csv montre qu'il contient 5921 journaux. 302 Magnificents Trolls, 460 Great Trolls, 2545 Quality Trolls et 2614 Average Trolls. Étant donné que les données sont déséquilibrées, il faudra en tenir compte dans les travaux car ces chiffres influencent les probabilités.

import matplotlib.pyplot as plt import pandas as pd import numpy as np import sys import matplotlib # Enable inline plotting %matplotlib inline filename = r'linuxfr.csv' lf_data = pd.read_csv(filename, encoding="UTF-8", sep='£', engine='python', quotechar='µ') len(lf_data) 5921 lf_data.quality_content.value_counts() Average Troll 2614 Quality Troll 2545 Great Troll 460 Magnificent Troll 302 Name: quality_content, dtype: int64 lf_data.quality_content.value_counts().plot(kind='bar') plt.ylabel('Occurences', fontsize='xx-large') plt.yticks(fontsize='xx-large') plt.xlabel('Trolls', fontsize='xx-large') plt.xticks(fontsize='xx-large')

Au passage, on observe qu'il y a beaucoup plus de contenu de qualité (pertinent), dont le score est positif que de négatif. Ou encore, qu'il y a beaucoup plus de contenu avec lequel les votants sont d'accord.

L’affaire est dans le sac (de mots)

À ce stade, j'ai suivi la documentation officielle de scikit-learn. L'analyse de texte est le plus souvent basée sur un algorithme de type "Bag of words". Chaque mot est compté dans le texte. On est alors en mesure de tracer un histogramme du nombre d’occurrence des mots en fonction de la liste des mots du dictionnaire. Dans scikit-learn, l'utilisation d'un sac de mots est très simple. Il faut faire appel à la classe CountVectorizer. Ma base de 5921 journaux contient 78879 mots différents.

import numpy as np from sklearn.feature_extraction.text import CountVectorizer count_vect = CountVectorizer() X_train_counts = count_vect.fit_transform(lf_data['content'].values) X_train_counts.shape (5921, 78879) Utiliser les fréquences d'apparition des mots

L'inconvénient du comptage de mots est qu'il entraîne un déséquilibre entre les textes de longueur différente. Il est possible de calculer les fréquences (tf) et éventuellement diminuer l'impact des mots qui apparaissent dans beaucoup de documents tels que les pronoms (tf-idf). L'utilisation de ces algorithmes est tout aussi simple :

from sklearn.feature_extraction.text import TfidfTransformer tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts) X_train_tf = tf_transformer.transform(X_train_counts) Classifier les articles Approche naïve : filtrage bayésien

La manière la plus simple d'analyser les articles est d'utiliser la classification naïve bayésienne. Wikipedia éclaire un peu plus les concepts sous-jacents :

En termes simples, un classificateur bayésien naïf suppose que l'existence d'une caractéristique pour une classe, est indépendante de l'existence d'autres caractéristiques. Un fruit peut être considéré comme une pomme s'il est rouge, arrondi, et fait une dizaine de centimètres. Même si ces caractéristiques sont liées dans la réalité, un classificateur bayésien naïf déterminera que le fruit est une pomme en considérant indépendamment ces caractéristiques de couleur, de forme et de taille.

Une fois le modèle entraîné (fonction fit, d'adéquation en français), il est possible de prédire à quelle catégorie des articles appartiennent.

from sklearn.naive_bayes import MultinomialNB classifier = MultinomialNB() classifier.fit(X_train_tfidf, targets) training_journals = ['Sécuriser son serveur avec la commande sudo rm -rf /*', 'Debian is dying', 'Windows Millenium est meilleur que Linux sur calculatrice graphique', "MultiDeskOS est 42% plus performant que Redhat 3.0.3 (Picasso)", "Pierre Tramo président !", "Des chocolatines au menu des cantines situées dans les DOM-TOM", "1515, l’année du Desktop Linux!"] X_new_counts = count_vect.transform(training_journals) X_new_tfidf = tfidf_transformer.transform(X_new_counts) predicted = classifier.predict(X_new_tfidf) for doc, category in zip(training_journals, predicted): print('%r => %s' % (doc, category)) 'Sécuriser son serveur avec la commande sudo rm -rf /*' => Quality Troll 'Debian is dying' => Quality Troll 'Windows Millenium est meilleur que Linux sur calculatrice graphique' => Quality Troll 'MultiDeskOS est 42% plus performant que Redhat 3.0.3 (Picasso)' => Average Troll 'Pierre Tramo président !' => Average Troll 'Des chocolatines au menu des cantines situées dans les DOM-TOM' => Quality Troll '1515, l’année du Desktop Linux!' => Average Troll

La commande predict_proba permet d'afficher les probabilités. Il en ressort que la marge d'erreur est énorme.

predicted_proba = classifier.predict_proba(X_new_tfidf) print(targets_names) predicted_proba ['Average Troll', 'Great Troll', 'Magnificent Troll', 'Quality Troll'] array([[ 0.38146407, 0.01242555, 0.00699732, 0.59911306], [ 0.45180296, 0.03300345, 0.01880854, 0.49638505], [ 0.37809693, 0.0190014 , 0.00917897, 0.5937227 ], [ 0.47083803, 0.0629247 , 0.02837355, 0.43786371], [ 0.54130358, 0.04642992, 0.03861831, 0.37364818], [ 0.45172753, 0.03297976, 0.01805764, 0.49723507], [ 0.59237292, 0.01164186, 0.00420374, 0.39178148]])

Mes "journaux" sont beaucoup trop courts pour être représentatifs, enfin cela dépend de la définition de "contenu de qualité". Par conséquent, il faut tester le modèle sur l'archive des contenus, dans un premier temps. Pour y arriver, je définis un pipeline qui consiste à assembler les étapes décrites précédemment dans un objet qui se comporte comme un classificateur.

from sklearn.pipeline import Pipeline text_clf = Pipeline([('vect', CountVectorizer()), ('tfidf', TfidfTransformer()), ('clf', MultinomialNB()),]) Tester le modèle avec les journaux connus

Je commence par échantillonner 20 % des journaux de la base de données et je teste le modèle sur cet ensemble, afin de voir s'il est capable de retrouver la bonne catégorie.

diaries_test = lf_data.sample(frac=0.2) predicted = text_clf.predict(diaries_test['quality_content']) from sklearn.metrics import confusion_matrix, f1_score score = f1_score(diaries_test['quality_content'], predicted, average='weighted') print('Diaries:', len(diaries_test)) print('Score:', score) Diaries: 5921 Score: 0.269979533821 /usr/lib/python3.6/site-packages/sklearn/metrics/classification.py:1113: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no predicted samples. 'precision', 'predicted', average, warn_for)

Ça ne marche pas du tout. La raison pour laquelle ce message est affiché est que le paramètre F (score F1) est indéterminé. Ce paramètre est un estimateur de la qualité d'une classification. Il dépend de la précision et du rappel. Une image vaut mieux qu'un long discours, le dessin sur la page wikipedia :

La matrice de confusion permet de comprendre pourquoi le score F est si mauvais : mis à part pour les trolls de qualité, je n'ai pas de vrai positif !

Pour lire le graphique : la prédiction parfaite aurait 100 % sur chaque case de la diagonale. C'est le cas ici des qualitaÿ trolls qui sont tous bien identifiés. Mais il y a un biais vers les qualitaÿ trolls. L'algorithme interprète ainsi erronément 100 % des average trolls comme des qualitaÿ trolls par exemple.


Au passage, j'affiche la matrice de confusion à l'aide du code de la documentation officielle.

Mon classificateur est mauvais. Il est probablement possible d'en améliorer les performances mais j'ai préféré changer d’algorithme.

Support vector machine (SVM)

D'après la documentation officielle, il s'agit de l’algorithme de classification de texte le plus performant pour le texte. SGDClassifier est basé sur un classificateur linéaire et un algorithme du gradient stochastique (abréviation SGD). Je vous avoue ne pas encore maîtriser ces subtilités. Si quelqu'un à l'aise avec ces notions veut participer à la discussion, il est le bienvenu.

from sklearn.linear_model import SGDClassifier text_clf = Pipeline([('vect', CountVectorizer()), ('tfidf', TfidfTransformer()), ('clf', SGDClassifier()),]) _ = text_clf.fit(lf_data.content, lf_data.quality_content) predicted = text_clf.predict(diaries_test.content) np.mean(predicted == diaries_test.quality_content) 0.95

Le score est très bon. Il est possible d'afficher plus d'informations à propos des prédictions :

from sklearn import metrics print(metrics.classification_report(diaries_test.quality_content, predicted, target_names=targets_names)) precision recall f1-score support Average Troll 0.99 0.93 0.96 523 Great Troll 1.00 0.94 0.97 80 Magnificent Troll 1.00 0.94 0.97 72 Quality Troll 0.92 0.99 0.96 509 avg / total 0.96 0.96 0.96 1184 # Affichage de la matrice de confusion metrics.confusion_matrix(diaries_test.quality_content, predicted) # Compute confusion matrix import itertools cnf_matrix = confusion_matrix(diaries_test['quality_content'], predicted) np.set_printoptions(precision=2) # Plot non-normalized confusion matrix plt.figure() plot_confusion_matrix(cnf_matrix, classes=targets_names, title='Confusion matrix, without normalization') # Plot normalized confusion matrix plt.figure() plot_confusion_matrix(cnf_matrix, classes=targets_names, normalize=True, title='Normalized confusion matrix') plt.show() Confusion matrix, without normalization Confusion matrix, without normalization [[489 0 0 34] [ 2 75 0 3] [ 0 0 68 4] [ 5 0 0 504]] Normalized confusion matrix [[ 0.93 0. 0. 0.07] [ 0.03 0.94 0. 0.04] [ 0. 0. 0.94 0.06] [ 0.01 0. 0. 0.99]]


Validation croisée

Ces résultats sont très intéressants mais il est important de tester la solidité du modèle. Cette étape est appelée validation croisée. scikit-learn permet de réaliser ces tests de manière automatisée. L'idée est d’échantillonner une partie des journaux (10 % dans notre cas), d'entraîner le modèle sur les 90 % restant et de tester le modèle sur ces 10 % "caché". On affiche ensuite les scores pondérés en fonction du nombre d’occurrence de journaux dans chaque catégorie.

from sklearn.model_selection import cross_val_score scores = cross_val_score(text_clf, # steps to convert raw messages into models lf_data.content, # training data lf_data.quality_content, # training labels cv=10, # split data randomly into 10 parts: 9 for training, 1 for scoring scoring='accuracy', # which scoring metric? n_jobs=-1, # -1 = use all cores = faster ) print(scores) print('Total diaries classified:', len(lf_data)) print('Score:', sum(scores)/len(scores)) [ 0.54 0.53 0.55 0.55 0.56 0.57 0.54 0.52 0.56 0.56] Total diaries classified: 5921 Score: 0.548226957256

Le score est égal à 0.55. Ce n'est pas terrible. Si on préfère afficher la matrice de confusion, il faut utiliser les Kfold qui reposent sur le même principe que cross_val_score et implémenter une boucle.

from sklearn.model_selection import KFold from sklearn.metrics import confusion_matrix, f1_score,precision_score k_fold = KFold(n_splits=10) scores = [] confusion = np.array([[0, 0,0,0], [0, 0,0,0], [0, 0,0,0], [0, 0,0,0]]) for train_indices, test_indices in k_fold.split(lf_data): train_text = lf_data.iloc[train_indices]['content'].values train_y = lf_data.iloc[train_indices]['quality_content'].values test_text = lf_data.iloc[test_indices]['content'].values test_y = lf_data.iloc[test_indices]['quality_content'].values text_clf.fit(train_text, train_y) predictions = text_clf.predict(test_text) confusion += confusion_matrix(test_y, predictions) score = f1_score(test_y, predictions, average='weighted') ps = precision_score(test_y, predictions, average='weighted') scores.append(score) print('Total diaries classified:', len(lf_data)) print('Score:', sum(scores)/len(scores)) print('Confusion matrix:') print(confusion) Total diaries classified: 5921 Score: 0.519244446873 Confusion matrix: [[1475 22 13 1104] [ 253 11 16 180] [ 164 15 26 97] [ 794 7 8 1736]] scores [0.48812704076867125, 0.50096444244611738, 0.53296513209879548, 0.50865953156976373, 0.53358760110311787, 0.52464153844229733, 0.53897239391380014, 0.5090212038928732, 0.5340084448235829, 0.5214971396677468]


Comme on le voit, les résultats sont très mauvais. Environ 44 % des journaux "Average Troll" sont attribués à la classe "Quality Troll" ! Si les auteurs suivent la même logique que cet algorithme, ils ont tendance à sur-estimer fortement leurs écrits. De même, 30 % des "Quality Troll" sont attribués à la classe "Average Troll". En suivant cette logique, les auteurs de contenu de qualité auraient tendance à se sous-estimer. Par ailleurs, il faut noter que ces classes sont voisines : score de 0 à 20 et de 20 à l'infini (et au delà).

Plus inquiétant : les contenus avec un score négatif sont attribués majoritairement aux classes à score positif. Un auteur de contenu moinsé qui penserait comme la machine serait persuadé que son texte est de qualité. Il ne comprendrait pas le score négatif qui en résulte.

Optimisation des paramètres

Et si nos mauvais résultats étaient dus au choix d'un mauvais jeu de paramètres de départ ? Le pipeline choisi dépend de nombreux paramètres ajustables. Scikit-learn permet d'optimiser ces paramètres facilement afin de trouver le meilleur compromis.

from sklearn.linear_model import SGDClassifier text_clf = Pipeline([('vect', CountVectorizer()), ('tfidf', TfidfTransformer()), ('clf', SGDClassifier()),])

Les paramètres ajustables sont précédés du nom de l'étape correspondante. Les explications concernant ces paramètres sont disponibles dans la documentation officielle :

sorted(text_clf.get_params().keys()) ['clf', 'clf__alpha', 'clf__average', 'clf__class_weight', 'clf__epsilon', 'clf__eta0', 'clf__fit_intercept', 'clf__l1_ratio', 'clf__learning_rate', 'clf__loss', 'clf__n_iter', 'clf__n_jobs', 'clf__penalty', 'clf__power_t', 'clf__random_state', 'clf__shuffle', 'clf__verbose', 'clf__warm_start', 'steps', 'tfidf', 'tfidf__norm', 'tfidf__smooth_idf', 'tfidf__sublinear_tf', 'tfidf__use_idf', 'vect', 'vect__analyzer', 'vect__binary', 'vect__decode_error', 'vect__dtype', 'vect__encoding', 'vect__input', 'vect__lowercase', 'vect__max_df', 'vect__max_features', 'vect__min_df', 'vect__ngram_range', 'vect__preprocessor', 'vect__stop_words', 'vect__strip_accents', 'vect__token_pattern', 'vect__tokenizer', 'vect__vocabulary']

Le code ci-dessous permet d'ajuster les paramètres suivants :

Évidemment, le temps de calcul dépend du nombre de paramètres à ajuster. Les autres paramètres sont laissés à leur valeur par défaut.

params = { 'tfidf__use_idf': (True, False), 'clf__loss':('huber', 'modified_huber', 'epsilon_insensitive', 'hinge', 'log'), 'clf__alpha':(1,0.001, 0.00001),} gs_clf = GridSearchCV(text_clf, params, n_jobs=-1, verbose=0, refit=True,scoring='accuracy',) print("Performing grid search...") print("pipeline:", [name for name, _ in text_clf.steps]) print("parameters:") print(params) t0 = time() gs_clf = gs_clf.fit(lf_data.content, targets) print("done in %0.3fs" % (time() - t0)) print() print("Best score: %0.3f" % gs_clf.best_score_) print("Best parameters set:") best_parameters = gs_clf.best_estimator_.get_params() for param_name in sorted(params.keys()): print("\t%s: %r" % (param_name, best_parameters[param_name]))

Ce qui donne :

Performing grid search... pipeline: ['vect', 'tfidf', 'clf'] parameters: {'tfidf__use_idf': (True, False), 'clf__loss': ('huber', 'modified_huber', 'epsilon_insensitive', 'hinge', 'log'), 'clf__alpha': (1, 0.001, 1e-05)} done in 108.027s Best score: 0.547 Best parameters set: clf__alpha: 0.001 clf__loss: 'modified_huber' tfidf__use_idf: True

Malheureusement, le score semble encore assez bas. Par ailleurs, le meilleur estimateur est également disponible pour utilisation future :

gs_clf.best_estimator_ Pipeline(steps=[('vect', CountVectorizer(analyzer='word', binary=False, decode_error='strict', dtype=<class 'numpy.int64'>, encoding='utf-8', input='content', lowercase=True, max_df=1.0, max_features=None, min_df=1, ngram_range=(1, 1), preprocessor=None, stop_words=None, strip... penalty='l2', power_t=0.5, random_state=None, shuffle=True, verbose=0, warm_start=False))]) Test sur un échantillon de données connues

Comme précédemment, il est possible de tester le modèle sur un échantillon de données connues. L'ajustement a été réalisé avec le meilleur jeu de paramètres grâce à l'option refit=True passée à GridSearchCV. Les résultats du score F1 sont encore une fois très bons mais l'amélioration du score est nulle : il plafonne entre 0.95 et 0.96.

print(metrics.classification_report(diaries_test.quality_content, predicted, target_names=targets_names)) precision recall f1-score support Average Troll 0.99 0.93 0.96 523 Great Troll 1.00 0.94 0.97 80 Magnificent Troll 1.00 0.94 0.97 72 Quality Troll 0.92 0.99 0.96 509 avg / total 0.96 0.96 0.96 1184

De même, la matrice de confusion est excellente :

Test sur un échantillon de données inconnues

Pour aller plus loin, j'ai testé le modèle sur de nouvelles données (des journaux plus anciens). Ces données ne font pas partie de mes journaux de base. En principe, le résultat sera similaire à ce qu'on obtient par validation croisée mais cette technique a pour avantage d'augmenter la taille de la base de journaux disponibles. Une autre possibilité consiste à relancer la validation croisée après avoir fusionné ces nouvelles données aux anciennes.

filename = r'out_of_sample.csv' lf_out = pd.read_csv(filename, encoding="UTF-8", sep='£', engine='python', quotechar='µ') lf_out = lf_out.reindex(np.random.permutation(lf_out.index)) lf_out.quality_content.value_counts().plot(kind='bar') plt.ylabel('Occurences', fontsize='xx-large') plt.yticks(fontsize='xx-large') plt.xlabel('Trolls', fontsize='xx-large') plt.xticks(fontsize='xx-large')

Ces nouvelles données sont similaires aux journaux déjà disponibles.

predicted_out = text_clf.predict(lf_out.content) np.mean(predicted_out == lf_out.quality_content) score_out = f1_score(lf_out['quality_content'], predicted_out, average='weighted') print('Diaries:', len(lf_out)) print('Score:', score_out) cnf_matrix_out = confusion_matrix(lf_out['quality_content'], predicted_out) np.set_printoptions(precision=2) # Plot non-normalized confusion matrix plt.figure() plot_confusion_matrix(cnf_matrix_out, classes=targets_names, title='Confusion matrix, without normalization') # Plot normalized confusion matrix plt.figure() plot_confusion_matrix(cnf_matrix_out, classes=targets_names, normalize=True, title='Normalized confusion matrix') plt.show() print(metrics.classification_report(lf_out.quality_content, predicted_out, target_names=targets_names)) Diaries: 1500 Score: 0.444809984556 Confusion matrix, without normalization [[452 9 10 457] [ 90 4 0 52] [ 42 5 6 26] [126 2 0 219]] Normalized confusion matrix [[ 0.49 0.01 0.01 0.49] [ 0.62 0.03 0. 0.36] [ 0.53 0.06 0.08 0.33] [ 0.36 0.01 0. 0.63]]


precision recall f1-score support Average Troll 0.64 0.49 0.55 928 Great Troll 0.20 0.03 0.05 146 Magnificent Troll 0.38 0.08 0.13 79 Quality Troll 0.29 0.63 0.40 347 avg / total 0.50 0.45 0.44 1500

Malheureusement, le résultat n'est pas bon. Encore une fois, le modèle ne peut pas s'adapter à des données inconnues. Il s'agit d'un cas assez probant de surapprentissage. L'image suivante illustre bien le problème. En cherchant à classer correctement les éléments dans la bonne catégorie, le modèle se contorsionne et ne tient pas compte de la tendance "globale".

Utiliser des propriétés multiples

Bien qu'elle soit informative, l'analyse ne permet pas de prédire la catégorie avec un score supérieur à 0,5. Pour l'instant, le classificateur se comporte comme un mauvais élève pressé d'aller jouer un match de tennis après son examen Q.C.M. : il répond la même chose (la réponse D) à toutes les questions en se disant qu'il obtiendra bien la moitié. Évidemment, cela ne fonctionne pas. L'approche "bag of words" seule ne suffit pas pour classer des journaux. Les bons journaux ne sont pas tous techniques, de même que les mauvais ne sont pas tous "politiques" (quoiqu'un journal sur l'avortement part en général très mal). Le sujet d'un journal n'est pas corrélé avec sa note finale. D'autres indicateurs doivent être pris en compte : ancienneté du compte au moment de la soumission, taille du texte (les journaux trop courts sont parfois descendu, tout comme les 'journaux fleuve parfois hallucinés' dixit oumph). scikit-learn permet de combiner plusieurs propriétés (appelées "features"), de déterminer celles qui ont le plus gros impact sur les résultats et d'ajuster un modèle en tenant compte des propriétés sélectionnées.

Extraction et préparation des données

L'analyse suivante repose sur l'utilisation des données présentes dans le fichier linuxfr_complete.csv. Elle correspond au notebook diaries_classification_2.ipynb. En plus des données présentes dans le fichier linuxfr.csv, ce document comporte les champs suivant :

  • la date de création du journal ;
  • la date de création du compte ;
  • les scores précédents de l'auteur (première page des anciennes publications) ;
  • la longueur du document.
import matplotlib.pyplot as plt import pandas as pd import numpy as np import sys import matplotlib # Enable inline plotting %matplotlib inline filename = r'linuxfr_complete.csv' lf_data = pd.read_csv(filename, encoding="UTF-8", sep='£', engine='python', quotechar='µ') Conversion des dates

Panda permet très facilement de convertir une chaîne de caractère correspondant à une date au format datetime.

lf_data['birthday'] = pd.to_datetime(lf_data['birthday']) lf_data['birthday'].head() 0 2004-08-28 1 2003-04-22 2 2004-02-14 3 2012-10-22 4 2009-10-05 Name: birthday, dtype: datetime64[ns] lf_data['datetime'] = pd.to_datetime(lf_data['datetime']) lf_data['datetime'].head() 0 2017-05-28 12:59:46 1 2017-05-28 09:57:04 2 2017-05-28 08:24:57 3 2017-05-27 14:18:10 4 2017-05-26 20:12:47 Name: datetime, dtype: datetime64[ns] Évolution du score des journaux au fil du temps score_df = lf_data[['datetime', 'score']].copy() score_df.index = score_df['datetime'] del score_df['datetime']

L'évolution des scores au fil du temps est alors facilement affichable. Je trouve personnellement qu'on n'a pas trop à se plaindre : la qualité générale des journaux est plutôt bonne.

score_df.plot(marker='o', grid=True, figsize=(15,9)) plt.ylabel('Score', fontsize='xx-large') plt.yticks(fontsize='xx-large') plt.xlabel('Date', fontsize='xx-large') plt.xticks(fontsize='xx-large')

Calcul de l’âge d’un compte

L'âge d'un compte peut facilement être calculé en soustrayant la date de création du compte à la date de création du journal. Pour une raison inconnue, cet âge est parfois négatif. Le code suivant tient compte de ce souci. Un compte qui a moins d'un jour se voit affublé de la propriété "Newbie".

lf_data['age'] = lf_data['datetime']-lf_data['birthday'] lf_data['newbie'] = False for index, line in lf_data.iterrows(): # Problem: sometimes, age << 0 if line['age'] < pd.Timedelta("0 day"): line['age'] = - line['age'] if line['age'] < pd.Timedelta("1 day"): lf_data.set_value(index, 'newbie', True) Qualité des posts des nouveaux

Il est à présent possible d'extraire les informations relatives aux nouveaux comptes (à la date de publication). Ces comptes sont à l'origine de contenu de qualité étonnante. On retrouve une grande quantité de très mauvais contenu ("magnificent troll") mais également de bons et très bons contenus ("quality troll" et "average troll").

noob = lf_data.loc[lf_data['newbie'] == True] noob.quality_content.value_counts().plot(kind='bar') plt.ylabel('Occurences', fontsize='xx-large') plt.yticks(fontsize='xx-large') plt.xlabel('Trolls', fontsize='xx-large') plt.xticks(fontsize='xx-large')

Calcul de la moyenne des scores précédents

Afin de tenir compte de l'historique d'un compte, deux colonnes sont ajoutées : la médiane et la moyenne. Les scores précédents sont conservés dans la colonne author_previous_scores. L'information, une chaîne de caractère sous la forme "[1,15,42,-12]", doit être extraite.

lf_data['median_score'] = 0 lf_data['average_score'] = 0 import statistics for index, line in lf_data.iterrows(): ps = line['author_previous_scores'] #print(ps) ps = ps.replace("[",'') ps = ps.replace("]",'') ps = ps.replace(",",'') ps = ps.split() ps = [float(x) for x in ps] median = statistics.median(ps) try: avg = statistics.mean(ps) except TypeError: avg = np.NaN lf_data.set_value(index, 'median_score', median) lf_data.set_value(index, 'average_score', avg)

La plupart du temps, la médiane et la moyenne sont très proches. Dans de rares cas, elles diffèrent beaucoup mais la moyenne est plus sévère que la médiane.

Garder l’essentiel

Les informations nécessaires pour réaliser une analyse plus complète sont à présent disponibles. Pour plus de facilité, de nouveaux dataframes sont créés en éliminant les colonnes inutiles.

lf = lf_data[['content','newbie','average_score', 'quality_content', 'score', 'count', 'author']].copy() target = lf_data[['quality_content']].copy() L’union fait la force

Maintenant que je dispose d'un dataframe contenant mes variables (lf) et un autre contenant mes catégories attendues (target), il faut que je crée une procédure permettant d'effectuer les bonnes tâches avec le bon jeu de données :

  1. Les données numériques sont utilisées telles quelles.
  2. Le corps de l'article est vectorisé et la fréquence des mots est calculée.
  3. Le nom de l'auteur est vectorisé également.

Ces trois étapes sont unies dans un object FeatureUnion dans un pipeline dont la dernière étape est un classificateur de type linéaire (SVC(kernel='linear')). Encore une fois, les fonctions .fit et .predict sont accessibles depuis le pipeline pour faciliter son utilisation.

La classe MultipleItemSelector permet d'extraire les données nécessaires à chaque étape.

Enfin, un poids est appliqué à chaque étape. Pour l'instant, il est égal sur les trois étapes mais des valeurs différentes ont donné des résultats similaires :

  • 'author': 0.8 ;
  • 'content': 0.5 ;
  • 'num_values': 1.0.
# From http://scikit-learn.org/stable/auto_examples/hetero_feature_union.html import numpy as np from sklearn.base import BaseEstimator, TransformerMixin from sklearn.datasets import fetch_20newsgroups from sklearn.datasets.twenty_newsgroups import strip_newsgroup_footer from sklearn.datasets.twenty_newsgroups import strip_newsgroup_quoting from sklearn.decomposition import TruncatedSVD from sklearn.feature_extraction import DictVectorizer from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import classification_report from sklearn.pipeline import FeatureUnion from sklearn.pipeline import Pipeline from sklearn.svm import SVC class MultipleItemSelector(BaseEstimator, TransformerMixin): def __init__(self, keys): self.keys = keys def fit(self, x, y=None): return self def transform(self, data_dict): return data_dict[self.keys] pipeline = Pipeline([ # Extract the subject & body #('subjectbody', DataExtractor()), # Use FeatureUnion to combine the features from subject and body ('union', FeatureUnion( transformer_list=[ # Pipeline for pulling features from the post's subject line ('author', Pipeline([ ('selector', MultipleItemSelector(keys='author')), ('tfidf', TfidfVectorizer(min_df=50)), ])), # Pipeline for standard bag-of-words model for body ('content', Pipeline([ ('selector', MultipleItemSelector(keys='content')), ('tfidf', TfidfVectorizer()), ('best', TruncatedSVD(n_components=50)), ])), # Pipeline dealing with numerical values stored in a dict ('num_values', Pipeline([ ('selector', MultipleItemSelector(keys=['score', 'newbie', 'average_score', 'count'])) , # list of dicts -> feature matrix ])), ], # weight components in FeatureUnion transformer_weights={ 'author': 1.0, # 0.8 'content': 1.0, # 0.5 'num_values': 1.0, # 1.0 }, )), # Use a SVC classifier on the combined features ('svc', SVC(kernel='linear')), ]) #pipeline.fit(lf, target.values.ravel()) Validation croisée

Afin de valider le comportement du classificateur, la validation croisée est effectuée avec 10 échantillons. Cette fois, les résultats sont vraiment très bons.

from sklearn.model_selection import cross_val_score scores = cross_val_score(pipeline, # steps to convert raw messages into models lf, # training data target.values.ravel(), # training labels cv=10, # split data randomly into 10 parts: 9 for training, 1 for scoring #scoring='accuracy', # which scoring metric? scoring='f1_weighted', n_jobs=-1, # -1 = use all cores = faster ) print(scores) print('Total diaries classified:', len(lf_data)) print('Score:', sum(scores)/len(scores)) [ 1. 1. 1. 1. 1. 1. 1. 0.99831646 0.99831646 1. ] Total diaries classified: 5955 Score: 0.999663292104

Afin de valider le comportement exceptionnel de ce classificateur, un test est réalisé avec des données qu'il ne connaît pas du tout : des journaux plus anciens.

Données hors échantillon.

La prédiction à l'aide de journaux anciens permet de vérifier que le modèle se comporte bien. Si c'est le cas, cela permet également d'affirmer que le type de contenu pertinent/inutile n'a pas radicalement changé ces dernières années.

Les données analysées dans cette section correspondent au fichier out_of_sample_complete.csv. La liste des journaux est sensiblement la même que celle utilisée dans le paragraphe Test sur un échantillon de données inconnues.

Les images ci-dessous montrent que la distribution temporelle de scores est similaire aux données plus récentes. La matrice de confusion et le score confirment les résultats obtenus à l'aide de la validation croisée. Le but est atteint. Il est possible de prédire la catégorie dans laquelle se trouve un journal à partir de son contenu et du nom de l'auteur. 1




Diaries: 1485 Score: 1.0 from sklearn import metrics print(metrics.classification_report(Y_out, predicted_out, target_names=targets_names)) precision recall f1-score support Average Troll 1.00 1.00 1.00 917 Great Troll 1.00 1.00 1.00 145 Magnificent Troll 1.00 1.00 1.00 77 Quality Troll 1.00 1.00 1.00 346 avg / total 1.00 1.00 1.00 1485 Pour aller plus loin

Scikit-learn dispose de nombreuses autres possibilités pour traiter des données de tout type. Je citerai la sélection des propriétés (feature selection) qui permet d’éliminer les propriétés dont la variance est inférieure à un seuil donné. Dans ce cas, la propriété est considérée comme une constante. L'intérêt est de diminuer le temps de calcul et le risque de sur-apprentissage. Un test rapide sur les données issues des journaux a montré que toutes les données sont utiles pour déterminer la catégorie d'un journal.

Choisir le bon algorithme peut être très difficile selon les informations désirées et le type de données. L'aide-mémoire suivant permet de faciliter ce choix. Une version interactive est également disponible.

Enfin, je terminerai en mentionnant la possibilité de sauver un modèle entraîné. Cela permet d'éviter de devoir repasser par l'opération d'apprentissage qui peut être consommatrice de ressources. Le fichier généré pour le modèle le plus efficace de cette dépêche fait 46 Mo.

from sklearn.externals import joblib joblib.dump(pipeline, 'linuxfr_pipeline.pkl')

Plus tard ou sur une autre machine:

from sklearn.externals import joblib pipeline = joblib.load('linuxfr_pipeline.pkl')

La documentation mentionne des considérations à prendre en compte.

  • Il existe un risque de sécurité dans le cas où on charge des données car cela pourrait mener à l'exécution de code malicieux ;
  • la comptabilité entre versions n'est pas prise en compte ;
  • il est important de laisser à disposition un jeu de données types, le score de validation du modèle associé et le code afin que la personne qui reçoit le modèle puisse vérifier les résultats obtenus.
Conclusions

Au cours de cette expérience, une moulinette a été codée afin d'aspirer le contenu des journaux.

Les données ont été analysées en deux temps. Dans la première phase, la vectorisation du contenu des journaux a été réalisée. Il a été montré que cette étape ne suffit pas à pouvoir classer correctement du contenu inconnu. Dans une seconde phase, le nom de l'auteur, la date de publication, la date de création du compte, l'historique récente des scores des publications de l'auteur ont été pris en compte et assemblés dans un pipeline d'analyse. Les résultats ont montré qu'il est possible de prédire la catégorie dans laquelle se trouve un journal à partir de son contenu et du nom de l'auteur. Le taux d'erreur est inférieur à 1/1000. Par ailleurs, l'optimisation des paramètres des classificateurs ainsi que la validation croisées ont été présentées.

L'analyse prédictive des scores permettra plusieurs grandes avancées sur le site. La première et la plus évidente sera la possibilité de renvoyer un lien vers cette dépêche chaque fois qu'un contributeur se plaindra de la note réservée à sa prose. Il s'agit là de l'argument ultime qui ne manquera pas de faire taire les trolls devant tant d'autorité. La seconde avancée sera la possibilité pour un contributeur d'améliorer ses journaux afin d'atteindre à chaque fois la catégorie visée. Provoquer un séisme de moinsage ou atteindre le summum de l'excellence ne s'improvise pas et scikit-learn permettra à chacun d'évaluer différentes variantes de ses journaux afin de poster la "meilleure". La troisième avancée concerne les journaux qui ne peuvent pas avoir été élaboré par un esprit humain. Ils sont probablement générés par une machine. Les plaisantins pourront améliorer les textes de leur programme en utilisant le modèle présenté afin de rendre la lecture du contenu plus agréable. La note finale s'en ressentira.

Pour terminer, je dirai qu'il est possible de rapidement effectuer des analyses de données avec scikit-learn. La syntaxe est très simple pour les personnes connaissant Python. Les concepts sont assez compliqués, mais la mise en œuvre est très bien faite et la documentation officielle est complète. Mais, tous ces éléments positifs ne garantissent pas des résultats probants et immédiats. Comme dans toute matière complexe, il faut comprendre ce qu'on fait pour obtenir des résultats qui ont du sens (et être en mesure de les analyser).

Perspectives

Plusieurs pistes de réflexion pourront permettre de poursuivre l'analyse :

  • Le découpage des catégories est arbitraire (bornes -20 et + 20) ; en modifiant la répartition des données, les résultats seront probablement différents (exemples : répartitions en quartiles, score strictement positifs ou négatifs, etc.) ;
  • au vu des résultats obtenus, la prédiction du score (valeur numérique) est envisageable ;
  • les catégories ne sont pas équitablement peuplées, le nombre de journaux à score négatif est beaucoup plus faible dans ce cas ; pour y remédier, nous avons besoin de plus de journaux de mauvaise qualité abondamment moinssés. À vos claviers !
  • La classification par un système d'arbre en limitant leur profondeur n'a pas été testée ;
  • les paramètres du meilleur pipeline n'ont pas été optimisés.
Rêvons un peu

Un éventail se possibilités s'ouvre à la communauté LinuxFR.org. La liste ci-dessous reprend les éléments qui me viennent en premier à l'esprit.

  • Réaliser une analyse temporelle des scores pour prédire la note d'un journal à venir en tenant compte de l'historique de publication général (comme la bourse) ;
  • prédire la note d'un commentaire (après avoir modifié la moulinette) ;
  • modifier le modèle afin de prédire le nombre de commentaires. Les contributeurs pourraient alors toucher du clavier la recette permettant de créer les trolls ultimes qui permettraient d'atteindre des sommets d'excellence, de courtoisie et de bienveillance dans une avalanche de remarques plus palpitantes les unes que les autres ;

N'hésitez pas à partager dans les commentaires vos suggestions, vos impressions et vos idées innovantes ! De même, le dépôt gitlab est accessible. Je vous invite à tester vos recettes sur les données présentes et à les exposer dans un journal ou une dépêche. Les différences entre les modèles peuvent être difficiles à appréhender. Toute explication complémentaire sera la bienvenue.

Note
  1. Les scores précédents et la date de création du score peuvent facilement être déduite à l'aide du nom de l'auteur. 

Télécharger ce contenu au format Epub

Lire les commentaires

Optimisations et corrections pour Fim 1.2.3

8 juin, 2017 - 22:41

Fim (File Integrity Manager) sort dans sa version 1.2.3 avec diverses corrections.

Fim est un gestionnaire de fichiers libre (licence GPL v3) qui permet de gérer de nombreux fichiers de n’importe quelle taille. Il peut, par exemple, gérer des musiques, des photos ou des vidéos. Il est capable de gérer des centaines de milliers de fichiers occupant une taille totale de plusieurs téraoctets. Il peut détecter les fichiers dupliqués et les effacer.

Les nouveautés de la version 1.2.3 Général
  • passage de Gson à Jackson pour avoir un sérialiseur plus efficace  ;
  • réduction de la quantité de mémoire nécessaire pour charger un State.
Corrections de bogues
  • correction du problème no 9 : Exception dans le fil d’exécution « principal » java.lang.IllegalStateException ;
  • correction de l’algorithme de comparaison d’état ;
  • lorsque la taille dépasse 1 Go, Fim n’arrondit plus au Go le plus proche ;
  • utilisation du système international d’unités (SI) pour calculer la taille d’un fichier (1 000 — kilo — au lieu de 1 024 — kibi).
Télécharger ce contenu au format Epub

Lire les commentaires

Grammalecte, correcteur grammatical [2]

8 juin, 2017 - 10:07

Grammalecte est un correcteur grammatical écrit en Python et en JavaScript, dédié à la langue française, disponible pour LibreOffice, Firefox, Thunderbird, ainsi que comme programme autonome, via une interface en ligne de commande ou un serveur. Par ailleurs, d’aimables contributeurs ont apporté leur pierre à l’édifice en concevant des greffons pour Vim et Emacs.

Grammalecte lance une deuxième campagne de financement pour améliorer la correction grammaticale et faire évoluer les intégrations aux navigateurs Web.

Cet article est très long. Plutôt que répéter ce qui a déjà été dit, je vais présumer que vous avez un souvenir à peu près clair du billet précédent sur ce sujet. Cela dit, même sans ça, ce que je vais dire devrait être intelligible.

Sommaire Ce qui a été fait Séparation du correcteur d’avec Hunspell et LibreOffice

C’était le prérequis à l’indépendance du logiciel, et c’est la première chose qui a été faite. Au lieu de consulter Hunspell, le correcteur orthographique, pour connaître l’étiquetage grammatical des mots, Grammalecte interroge dorénavant son propre dictionnaire indexable. Il s’agit d’un graphe de mots contenant toutes les formes graphiques que peuvent avoir les mots français : les pluriels, les formes féminines et masculines, et les conjugaisons.

Le graphe contient 500 966 entrées, avec 140 caractères différents, 901 codes de suffixation, 6 066 étiquettes grammaticales, et est composé de 110 796 nœuds et 236 744 arcs (liens qui vont d’un nœud à un autre). Tout ça est compressé sous forme d’un dictionnaire binaire indexable pesant environ 1,25 Mio (le fichier texte non compressé pèse environ 18,6 Mio). Mais le principal avantage de ce dernier n’est pas sa petite taille, c’est qu’on peut le parcourir très rapidement et l’interroger sans avoir à le décompresser. Il peut fournir deux sortes d’informations : un mot est‐il présent dans le dictionnaire et, si oui, quelle est sa nature grammaticale.

Grâce à cela, Grammalecte peut fonctionner de manière autonome.

Extension pour Firefox & Thunderbird

Le correcteur a été entièrement réécrit en JavaScript avec une interface pour Firefox, qui s’appuie principalement sur l’API SDK de haut niveau et quelques fonctionnalités de bas niveau. L’interface est faite en HTML/CSS.

L’API de Thunderbird étant complètement différente de celle de Firefox, cette extension est une autre réécriture en JavaScript de l’extension, hormis le cœur du moteur grammatical, bien sûr. L’interface est bâtie avec l’ancienne technologie XUL encore en vigueur sur Thunderbird. La documentation concernant Thunderbird étant en partie tombée en déshérence (liens cassés, pages manquantes, sections disparues, sites Web à l’abandon ou évaporés), j’ai parfois fait emploi de techniques trouvées dans d’autres extensions, mais ça ne concerne qu’une minorité de problèmes, et je me suis contenté autant que possible de suivre ce qui était documenté. Le fonctionnement de l’extension est assez similaire à celle pour Firefox. Votre texte est analysé dans un panneau annexe. En revanche, contrairement à Firefox, les erreurs sont listées en dessous du paragraphe analysé. Hormis cela, tout est relativement semblable, sauf l’allure générale de l’extension, qui fait plus vieux jeu, parce que XUL est d’une conception plus ancienne.

La mauvaise nouvelle, pour Firefox, c’est qu’il faudrait déjà refaire l’interface. Quand j’ai commencé le codage de l’extension, Mozilla préparait une nouvelle API pour les extensions, appelée WebExtension, qui était alors en version alpha. C’est la quatrième API pour Firefox, les trois autres étant XUL, Bootstrapped et le SDK (haut niveau et bas niveau). Il était dit que ces trois API deviendraient obsolètes d’ici quelques années, sauf le SDK de haut niveau, et qu’il fallait utiliser ça en attendant que l’API WebExtension fût finalisée. C’est donc ce que j’ai fait. Grammalecte pour Firefox est sorti à peu près au même moment que WebExtension. Quelques mois plus tard, les trois autres API ont été déclarées obsolètes, y compris le SDK de haut niveau, contrairement à ce qui avait été annoncé, et toutes les trois seront supprimées en novembre prochain avec Firefox 57.

C’est vraiment très irritant, mais l’on n’y peut pas grand‐chose. Ceci découle probablement de la volonté de Mozilla de réécrire tout le cœur de Firefox. Ce qui n’apporte pas que de mauvaises choses, puisque, par exemple, j’ai constaté que le correcteur grammatical fonctionnait plus de deux fois plus vite à partir de Firefox 55…

Le cœur de l’extension, le moteur grammatical, n’est pas à reprogrammer, il ne s’agit « que » de l’enrobage, l’interfaçage… J’ignore si ça réclamera beaucoup de travail, je ne m’y suis pas encore intéressé. L’un des autres soucis, c’est que même si WebExtension est dorénavant le modèle d’extensions conseillé, il semble qu’il ne permette pas encore autant de choses que les API précédentes. Cette API est toujours en développement, Mozilla travaillant encore à étendre les possibilités offertes.

En ce qui concerne Thunderbird, je n’ai rien vu passer… et j’ignore si nous sommes à la veille d’une révolution surprise qui va tout casser.

Mozilla n’a pas encore vérifié le code de l’extension pour Thunderbird : la liste d’attente est très longue. Heureusement, contrairement à Firefox il est possible d’installer une extension non vérifiée et non signée. Il n’y a aucun risque de confidentialité. Le correcteur n’envoie aucune donnée en ligne, absolument rien. Ce que vous écrivez n’est pas transmis, tout se passe sur votre ordinateur.

Pour ceux que ça intéresse de tester, je viens de publier la version 0.5.17 :

  • pour Firefox (il faut un Firefox capable d’installer les extensions non signées) ;
  • pour Thunderbird (Thunderbird peut installer les extensions non signées).
Captures d’écran

Boîtes de dialogue « À propos » :

Correcteur grammatical :

Conjugueur :

Formateur de texte :

Options grammaticales et orthographiques :

Interface en ligne de commande :

Révision du moteur interne du correcteur Désambiguïsation

Dans la précédente dépêche, je vous avais expliqué que Grammalecte ne possédait pas de processus de désambiguïsation permettant d’étiqueter les mots en fonction du contexte. Par exemple, un mot comme « porte » peut être un verbe ou un nom féminin, et il est utile, lors de l’analyse d’une phrase, d’avoir procédé si possible à la désambiguïsation de sa nature grammaticale, afin que les règles de contrôle n’aient pas à se poser la question à chaque fois.

Comme expliqué lors de la campagne de financement précédente, Grammalecte intègre désormais des fonctions de désambiguïsation capables d’étiqueter et baliser le texte. Ce désambiguïsateur fonctionne d’une manière similaire aux règles de contrôle et au processeur de texte. Une expression rationnelle déclenche une action de désambiguïsation si elle remplit la condition requise, de sorte que toutes les règles suivantes bénéficient d’un étiquetage plus précis. Il y a trois types d’actions de désambiguïsation : la sélection (qui ne retient que les étiquettes conformes à une expression rationnelle), le filtre (qui supprime les étiquettes grammaticales non conformes), l’étiquetage (qui impose une ou plusieurs étiquettes, quelles que soient celles que le mot possède alors).

Règles multi‐actions

Dans le précédent billet, je vous avais expliqué que Grammalecte fonctionnait selon une succession de passes, dont chacune était précédée d’une transformation du texte par ce que j’appelais le « préprocesseur de texte ». Ce qui permettait de simplifier peu à peu le texte pour faciliter le contrôle des règles grammaticales qui suivaient.

Pour simplifier, cela fonctionnait ainsi :

  1. Passe 1 (paragraphe par paragraphe) :
    • règles du préprocesseur de texte ;
    • règles de contrôle.
  2. Passe 2 (phrase par phrase) :
    • règles du préprocesseur de texte ;
    • règles de contrôle.
  3. Passe 3 (phrase par phrase) :
    • règles du préprocesseur de texte ;
    • règles de contrôle.
  4. etc.

Dans ce système, chaque règle ne pouvait faire qu’une seule chose (comme dans LanguageTool), et seulement si la condition qui contrôlait sa mise en œuvre était remplie.

Peu de temps après le début de la campagne de financement, il m’est apparu qu’il serait souhaitable de rendre plus souple ce fonctionnement. Il n’était pas prévu au programme de toucher à cela, mais j’ai préféré m’atteler tout de suite à cette tâche plutôt que de regretter plus tard de ne pas l’avoir fait. Donc, à présent, au lieu d’une succession de passes alternant transformation du texte et contrôle de la grammaire, nous sommes revenus à simplement deux passes (une pour le contrôle du paragraphe, une pour le contrôle des phrases), mais chaque règle peut dorénavant tout faire et peut accomplir autant d’actions qu’on le souhaite, chaque action se déclenchant si la condition qui y est attachée est remplie.

Donc, une règle peut dorénavant opérer plusieurs actions de contrôle, plusieurs actions de réécriture et plusieurs actions de désambiguïsation.
Au commencement, je pensais que cette modification du fonctionnement n’aurait que peu de conséquences, car elle n’était pensée que pour me faciliter la tâche dans certains cas épineux, puis il m’est apparu que ça changeait en fait de si nombreuses choses que la transition de l’ancien modèle vers le nouveau n’est toujours pas achevée à ce jour. Ça n’a pas d’importance puisque ça fonctionne aussi bien qu’auparavant, mais la souplesse de ce fonctionnement apporte tellement d’avantages que je suis loin d’en avoir profité encore pleinement.
Cela dit, depuis que Grammalecte fonctionne ainsi, des dizaines de règles complexes ont déjà été fusionnées, et cela a simplifié grandement le déroulement de nombreuses opérations.

Prenons un exemple simple. Auparavant, si vous écriviez « les ordinateur », il fallait deux règles de contrôle pour souligner « les » et suggérer « le », et souligner « ordinateur » et suggérer « ordinateurs ». À présent, une seule règle permet de faire les deux choses d’un coup.

Autre exemple. Mettons que nous rencontrons la graphie « militant(e)s ». Une même règle va permettre de faire trois choses :

  • suggérer d’écrire ceci autrement (« militants et militantes », « militantes et militants », « militant·e·s ») ;
  • étiqueter ce mot inexistant dans le dictionnaire comme « nom ou adjectif épicène pluriel » ;
  • réécrire le mot en interne pour ôter les parenthèses gênantes.
Détection des erreurs

De nombreuses règles de contrôle ont été ajoutées… notamment concernant la détection de confusions concernant les mots grammaticaux. La dernière version du correcteur (v0.5.17) contient 21 actions de désambiguïsation, 687 actions du processeur de texte, 1 437 actions de contrôle ; ces actions sont réparties dans 1 939 règles.

À titre de comparatif, la version 0.4.10, qui suivait l’ancienne logique, contenait 540 règles de transformation de texte et 933 règles de contrôle, chacune n’effectuant qu’une seule action.

Ces chiffres ne sont pas nécessairement très évocateurs, parce que rien ne comptabilise les modifications du fonctionnement des règles qui sont les plus nombreuses, ne serait‐ce, par exemple, que sur les méthodes pour faire des suggestions.

En fait, le potentiel du correcteur est encore sous‐exploité, malgré les innombrables améliorations apportées ici et là. Je n’avais pas assez insisté sur ce point lors du dernier journal, alors je vais le répéter encore une fois : les détails sont ce qui réclame le plus de temps, car il faut songer que les possibilités d’erreurs avoisinent l’infini et que les possibilités de faire des faux positifs sont aussi nombreuses. Dans les faits, améliorer le correcteur grammatical, c’est s’occuper sans cesse de micro‐problématiques. Je réécris ici ce que j’ai dit la dernière fois : « Écrire des règles, c’est assez rapide ; détecter les faux positifs, c’est beaucoup plus long ; ceux‐ci ont tendance à survenir là où l’on s’y attend le moins. C’est ce qui est le plus exigeant : maintenir un ensemble de règles, améliorer l’existant, tester, trouver de nouvelles possibilités. Lorsqu’on s’occupe d’un correcteur grammatical, on passe surtout son temps à peaufiner des détails, à ajuster le fonctionnement de l’existant, à arrondir les angles. Oubliez l’idée de concevoir l’algorithme ultime qui saura gérer tous les cas. Même quand on est à peu près sûr d’écrire une petite règle tranquille qui ne générera aucun faux positif, la réalité va très probablement nous rappeler à l’ordre et nous obliger à slalomer sur ce qui paraissait au commencement comme une belle ligne droite. S’occuper de correction grammaticale, c’est marcher sur un chemin pavé d’embûches subtiles. »

C’est pourquoi l’évolution du correcteur se fait pas à pas, en évitant autant que possible les modifications radicales.

Le correcteur fonctionne beaucoup mieux depuis que toutes ces améliorations ont été faites, et j’espère que c’est sensible pour vous, car il m’est difficile de juger si ces améliorations sont visibles pour les utilisateurs.

Parmi toutes les nouveautés apportées, il y a notamment :

  • beaucoup de règles pour détecter des confusions entre les mots homonymes (comme ce, se et ceux) ;
  • beaucoup d’améliorations pour gérer les cas particuliers, clarifier le texte interne et augmenter le taux de détection ;
  • toujours moins de faux positifs ;
  • une option pour détecter les erreurs de reconnaissance optique de caractères bien plus développée (désactivée par défaut) ;
  • beaucoup de règles de contrôle ont été améliorées.
Le moteur de suggestions

L’amélioration des suggestions est l’un des points qui ont le plus progressé depuis le précédent journal, grâce aux règles multi‐actions qui ont permis de rendre plus systématiques la possibilité de faire deux suggestions différentes lors de la constatation d’un désaccord de genre, de nombre ou de conjugaison ; mais aussi surtout grâce à la création d’une table de correspondance phonétique à partir de laquelle Grammalecte peut choisir des mots à suggérer en fonction de leur nature grammaticale.

La table de correspondance phonétique est un simple fichier qu’il est facile de compléter, c’est une succession de mots homonymes, comme :

  • appareil, appareils, appareille, appareilles, appareillent ;
  • mec, mecs, Mecque ;
  • pouce, pouces, pousse, pousses, poussent ;
  • tête, têtes, tète, tètes, tètent.

À partir de cette liste, Grammalecte construit un mini‐dictionnaire avec toutes les natures grammaticales possibles de chaque mot. Puis, au besoin, on peut demander, par exemple, de trouver un mot semblable à « tète » en spécifiant les natures grammaticales acceptables (via une expression rationnelle). Il y a aussi quelques propositions de substitutions automatisées, pas toujours opportunes, ce point reste à améliorer.

Si ça vous intéresse, cette table est consultable ici.

Cette table phonétique ne résout pas tous les problèmes, mais permet dans bien des cas de faire des suggestions de mots sans rapport grammatical avec le terme erroné. Avant cela, le correcteur se contentait de signaler l’erreur, mais ne pouvait rien suggérer. Quelques exemples :

  • « Il appareil demain. » → appareille ;
  • « La mec. » → Le | Mecque ;
  • « Une goulet » → Un | goulée ;
  • « Il pouce le bouchon trop loin. » → pousse ;
  • « J’en ai marre de ces tètes de nœud. → têtes ;
  • « Ils ne son pas idiot. » → sont.

Notez que le moteur de suggestions ne propose des mots ayant une autre racine que lorsqu’il constate une anomalie (si un verbe prend la place de ce qui devrait être un nom, par exemple) ou s’il constate une erreur de genre. S’il y a seulement une erreur de pluriel, il se contente de faire une correction de nombre. Mais il serait aussi possible dans ces cas‐là de suggérer des mots différents. Par exemple « Des homme » → hommes | ohms | heaumes.

Ça m’a semblé superflu de proposer de telles corrections, mais si vous jugez ça utile, dites‐le‐moi.

Modifications diverses
  • l’écriture des règles a été modifiée, il est plus aisé de gérer la casse et les marges des motifs des expressions rationnelles ;
  • les règles sont regroupées par option, afin de pouvoir ajouter des règles optionnelles sans gréver les performances (l’option de reconnaissance optique de caractères, par exemple) ;
  • il est possible d’utiliser pour chaque action des opérateurs logiques (__also__ et __else__) pour écrire des conditions tenant compte du résultat de la condition de l’action précédente ;
  • toutes les règles ont été nommées, afin de satisfaire aux nécessités du greffon pour Vim (désactivation paramétrable de règles spécifiques) ;
  • tout le processus de construction a été revu et des tests ont été intégrés pour éviter certaines erreurs d’écriture des règles ;
  • des marque‐pages pour s’y retrouver dans la liste des règles, qui commence à devenir très, très longue (fonctionnalité ajoutée hier).
Capture d’écran

Voici à quoi ressemble le fichier des règles (sous SublimeText) :

Tests unitaires

Auparavant, la situation sur ce point était très mauvaise, puisqu’il n’y avait aucun test unitaire sur le correcteur, seulement des tests « manuels », c’est‐à‐dire un fichier texte que j’ouvrais de temps en temps avec Writer pour vérifier que le moteur fonctionnait comme prévu. Mais c’était loin d’être exhaustif et facile à mettre en œuvre, puisque je vérifiais « à l’œil » si à chaque ligne le correcteur trouvait bien l’erreur qu’il était censé trouver. Pénible et lent au possible.

À présent, à chaque modification, il est possible lors de la construction de tester si rien n’est cassé. Plus de 6 200 tests sont lancés (dont 2 000 repris chez LanguageTool), et chaque règle de détection doit être testée au moins une fois.

Malgré cela, les tests ne sont pas encore exhaustifs (à bien y songer, il n’est sans doute pas possible d’être exhaustif), mais la situation s’améliore indubitablement, et peu à peu le socle du correcteur devient de plus en plus solide. Grâce à cela, de nombreuses erreurs dans le fonctionnement du correcteur ont été détectées et corrigées.

Beaucoup de ces tests sont là pour vérifier qu’une règle fonctionne correctement, mais une grande partie d’entre eux existent pour éviter les faux positifs constatés lors des versions précédentes. Ce sont les tests les plus nombreux, parce que les faux positifs sont légion et qu’il est très fréquent d’en provoquer de nouveaux, quelle que soit la prudence avec laquelle on écrit les règles. Pour les éviter autant que possible, quelques textes courts ont été inclus dans les tests de Grammalecte :

  • Le Horla, de Guy de Maupassant ;
  • Le double assassinat dans la rue Morgue, d’Edgar Poe ;
  • Les vers dorés, de Pythagore ;
  • L’épître du feu philosophique, de Jean Pontanus.

Avoir écrit l’extension pour Firefox présente par ailleurs un avantage considérable pour consolider la correction grammaticale : c’est beaucoup plus facile de mettre le correcteur à l’épreuve. Presque tous les jours, je lance des analyses grammaticales sur de nombreux articles de journaux et de blogs divers ; ça me permet de repérer et corriger des faux positifs, ainsi que de nouveaux mots à ajouter au dictionnaire. Auparavant, je faisais des copier‐coller dans Writer, ce qui était bien moins commode et bien plus long.

Ces tests sont faits en Python et en JavaScript. Les deux moteurs les passent tous et fonctionnent de la même manière. Cela dit, il n’est pas malgré cela pas possible de garantir (pour l’instant) que le moteur en JavaScript fonctionne aussi bien qu’en Python.

JavaScript, mon amour Hiroshima

Passer de Python à JavaScript n’a pas été de tout repos. Et si le projet a pris du retard, ce n’est pas tellement, comme je le craignais, à cause de la difficulté de concevoir une extension pour Firefox et Thunderbird. Ce point fut plus facile que je ne le pensais, tout simplement parce que la documentation de Firefox et celle Thunderbird, quoique cette dernière soit en déshérence, sont mieux tenues et plus complètes que celle pour LibreOffice… et surtout l’API est plus simple à utiliser… Quoique… l’API de bas niveau est tout aussi compliquée, de mon point de vue… Mais comme on peut s’en passer… Bref.

La syntaxe de JavaScript ne m’a pas posé de problème. J’ai utilisé toutes les nouvelles possibilités offertes par la norme ES6 : for … of, les générateurs, les paramètres par défaut, les paramètres du reste, la décomposition, les fonctions fléchées, Map, Set, les classes, let et const, et même depuis peu les nouvelles commandes async et await. Avec tout ça, JavaScript est un langage pas aussi horrible que je l’avais craint, même assez agréable parfois… En même temps, comme je démarrais un projet à zéro, je n’ai pas eu à me coltiner toutes les bizarreries possibles de ce langage. J’ai évité toute forme de syntaxe tord‐neurones.

Ce qui m’a compliqué la vie, c’est plutôt l’écosystème de JavaScript, sa difficulté à déboguer, son comportement étrange, les incohérences et sa bibliothèque standard pauvre.

Difficulté à déboguer

Ni Firefox, ni Thunderbird (ni LibreOffice d’ailleurs) ne signalent quoi que ce soit s’il y a une erreur de syntaxe dans le code. Ça ne fonctionne tout simplement pas, et c’est tout. C’est assez pénible en Python, mais c’est bien pire en JavaScript où il est fréquent d’oublier une virgule ou un point-virgule quelque part. En outre, les messages d’erreur de JavaScript sont longs et souvent abscons, sans compter que selon le contexte d’exécution dans Firefox/Thunderbird, les messages d’erreur sont parfois encore plus imprécis, voire inexistants (le module XYZ plante, mais débrouillez-vous pour le reste, car aucune info ne sera fournie, comme où et pourquoi). Je ne vais pas rentrer dans les détails, parce que j’ai maintenant oublié la plupart d’entre eux… mais à mon avis, Mozilla ferait des heureux en améliorant les rapports d’erreur.

Inutile de s’étendre sur le comportement erratique de JavaScript et son typage faible, cela a déjà été fait par tellement de monde… Quand on vient de Python, il est facile de tomber dans tous les pièges que ce langage tend, dans lesquels même les plus experts chutent encore de temps en temps, j’imagine. La traque aux “undefined” semblant sortir de nulle part m’a particulièrement marqué.

La bibliothèque standard

La bibliothèque standard est, en restant poli, très mauvaise, notamment parce qu’elle est très incomplète, mais aussi assez peu cohérente, me semble‐t‐il. Dans beaucoup de cas, ce n’est pas bien grave, il suffit d’ajouter les fonctions dont on a besoin et c’est ce que j’ai fait, mais on se demande quand même pourquoi cette bibliothèque standard est si pauvre ou si mal foutue. Avec toute la hype autour de ce langage, c’est vraiment très étonnant.

Prenons le cas des expressions rationnelles, parce que la nullité de l’objet Regexp est navrante… et parce que c’est ce qui est le plus utile à Grammalecte :

problème 1 : pas de lookbehind assertions

Les lookbehind assertions permettent de regarder ce qui précède un motif. OK, c’est pénible, mais on peut compenser avec quelques efforts.

problème 2 : les classes de métacaractères ne comprennent que l’ASCII.

Les classes de métacaractères comme \w et \b, ne comprennent que l’ASCII.
Oui, l’ASCII ! Ce qui donne (vous pouvez essayer dans l’ardoise de Firefox) :

/^\w+$/.test("étonnant") ==> false /\bvite\b/.test("évite") ==> true

Inutile de vous précipiter sur votre calendrier, nous sommes bien en 2017, et non en 1997. JavaScript est, paraît‐il, un langage moderne, mais seulement quand on n’y regarde pas de trop près.

Heureusement, il est facile de compenser l’absence d’un \w fonctionnel par [a-zA-Zà-öÀ-Ö0-9_ø-ÿØ-ßĀ-ʯ] (du moins, ce sont les plages de caractères que j’ai retenues, on pourrait en ajouter bien d’autres). Ce qui rend les expressions rationnelles un peu plus piquantes à lire…

Quant à \b, eh bien, on peut essayer de compenser avec des lookahead assertions et des lookbehind assertions. Seulement, voilà, ces dernières n’existent pas en JavaScript. Mais en magouillant, on peut simuler ça. Du moins dans presque tous les cas utiles. Tant pis pour les autres… on priera de ne pas tomber sur un caractère non ASCII.

Problème 3 : pas de position des groupes capturés

Les groupes capturés n’indiquent pas leur position. Ça, c’était vraiment la mauvaise surprise. Avant de proposer la campagne de financement, j’avais vérifié ce que JavaScript pouvait faire avec les expressions rationnelles, mais je n’avais pas vu que ce point manquait à l’appel, tout simplement parce que je n’avais pas imaginé que JavaScript serait incapable de fournir cette indication.

De quoi s’agit‐il au juste ? C’est très simple. Quand vous écrivez une expression rationnelle avec des groupes de capture, comme (\w+) (\w+) (\w+), Python vous renvoie les groupes capturés (ce qui est entre parenthèses) avec leur position dans le motif global. JavaScript ne renvoie que le contenu des groupes capturés, mais pas leur position. Débrouillez‐vous pour savoir où ça se trouve, et c’est bien plus épineux que vous pourriez le supposer, si, par exemple, vous capturez des mots comme « a », « le » ou « la », ou si vous capturez des groupes qui ne sont pas des mots entiers.

Ce point seul m’a fait perdre beaucoup de temps, sans compter que ce fut un cauchemar à déboguer. J’ai même cru pendant quelques jours que tout le projet allait tomber à l’eau à cause de ça.

Car, à ce moment du développement, alors même que je bataillais pour rendre le moteur fonctionnel, j’ai pris conscience que l’exécution du code en JavaScript était environ douze fois plus lente qu’en Python, au point que lorsqu’on lançait la correction grammaticale, Firefox se figeait pendant de longues secondes… C’était évidemment inacceptable.

Il était donc hors de question de ralentir encore l’exécution du code en essayant de calculer la bonne position des erreurs dans les motifs détectés (pour rappel, il y a des milliers d’expressions rationnelles dans Grammalecte). Du coup, j’ai oublié cette idée et procédé autrement : la position des groupes capturés est signalée dans le fichier des règles de grammaire. Cela rend le processus d’écriture des règles un peu plus brouillon, mais c’est assez simple pour être acceptable. Et ça ne ralentit pas le moteur outre mesure.

Ensuite, pour éviter que Firefox ne se fige, le cœur du correcteur grammatical, c’est‐à‐dire la vérification du texte, s’exécute dans un processus séparé… Et, bonne surprise, il s’avère que dans ce processus — inexplicablement, sans rien changer au code — le correcteur fonctionne vingt fois plus rapidement que dans le processus principal de Firefox, même quand ce dernier ne fait rien du tout !…

Un écosystème incohérent

Selon les contextes et l’application, les choses fonctionnent différemment et ça donne la désagréable impression de bâtir sur du sable. On ne sait pas toujours bien ce qui préexiste aux scripts qu’on conçoit. Notamment, il n’y a toujours pas de moyen conventionnel simple pour importer un module (ES6 en parle, mais aucun navigateur n’a encore implémenté ça).

Parfois, require existe par défaut. On peut faire :

const { Cu } = require("chrome"); const tabs = require("sdk/tabs");

Dans d’autres cas, require n’existe pas par défaut, donc :

importScripts("resource://gre/modules/workers/require.js"); const gce = require("resource://grammalecte/fr/gc_engine.js");

Ou bien encore dans un autre contexte, il faut faire :

const Cu = Components.utils; const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});

Parfois, on importe Components.utils (Cu) grâce à require. Parfois, on importe require à partir de Components.utils (Cu) ou bien d’autre chose.
On peut aussi importer des modules via XUL ou le HTML, avec des espaces de noms partagés. Il y a aussi les déclarations de ressources dans un manifeste. Bref, tout ça est contre‐intuitif au possible.

Cela dit, alors que j’écrivais ce texte, je découvre que import et export arrivent enfin dans les navigateurs ! On va peut‐être en finir avec tout ça bientôt.

Mais, ça résoudra pas tout. Comme je l’ai dit, selon les applications et les contextes, certains objets ou fonctions existent ou n’existent pas. Dans Firefox, on peut appeler directement XMLHttpRequest(), mais pas dans Thunderbird. Pour ce dernier, il « suffit » donc de faire :

let { Cc, Ci } = require("chrome"); let xRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); xRequest.QueryInterface(Ci.nsIXMLHttpRequest);

Même pour faire un print, il n’y a pas de solution uniforme. Là encore, selon le contexte, il y a : console.log(), Service.console.logStringMessage() ou dump() ou que sais‐je encore. Il y a des contextes où rien de tout ça ne fonctionne.

Une dernière étrangeté : pour compenser la pauvreté des objets standard, j’ai ajouté des fonctions personnelles à String, Regexp et Map. Mais ce qui est curieux, c’est que dans Firefox, il faut que ces nouvelles fonctions soient déclarées dans le module où les objets sont instanciés… tandis que dans Thunderbird, il faut que ces nouvelles fonctions soient déclarées dans le module où ces fonctions sont utilisées. C’est pourtant le même moteur JavaScript.

J’arrête ici de lister les incohérences, on n’en finirait pas.

Chaque problème n’est pas dramatique en soi, mais l’accumulation de ces contretemps finit par rendre la programmation en JavaScript vraiment casse‐pieds par moments.

Et, là, on ne parle que de Mozilla. C’est encore différent ailleurs, puisque l’implémentation des nouvelles fonctionnalités du langage n’avance pas au même rythme selon les moteurs.

En résumé, JavaScript, c’est le bordel et on se demande pourquoi c’est si incohérent. À mon avis, ce qui manque particulièrement à ce langage, c’est une implémentation standard avec un comportement logique et uniforme, des rapports d’erreurs plus clairs et une bibliothèque par défaut mieux faite et plus complète. On perd un temps considérable avec ces histoires.

Je ne voulais pas particulièrement écrire un long chapitre sur ce langage, mais ça fait déjà pas mal…

De Writer à Firefox (ou de la littérature au Web) : le choc culturel

L’un des points auxquels je n’avais pas beaucoup réfléchi et qui m’a le plus surpris, bien que j’avais conscience que les textes du Web n’égalaient pas en qualité et normativité ceux sur lesquels j’avais travaillé sur LibreOffice, c’est combien l’écrit sur le Web est chaotique et exubérant, et foisonne de néologismes, d’anglicismes, de noms propres (compagnies, marques, produits, people), de sigles, de codes divers, de vocabulaire technique, de smileys, d’erreurs de syntaxe ou de formatage, sans compter les habituelles erreurs typographiques, orthographiques et grammaticales… Si bien que même lorsque Grammalecte commençait à fonctionner comme il le devait sur Firefox, il m’est vite apparu que c’était difficilement utilisable en l’état, et qu’il fallait revoir son fonctionnement pour éviter trop de signalements intempestifs, pour éviter que tous les textes soient barbouillés de toutes les couleurs. Pourtant, je n’avais pas fait faire les premières armes du correcteur sur les logorrhées de Facebook, mais sur des textes d’acteurs « sérieux », comme les journaux et les blogs grand public ou plus confidentiels, mais a priori bien tenus. Quelle déconvenue !… Entre le charabia, les hashtags et autres bidules, le formatage indigent et la novlangue du Web, Grammalecte tirait tout le temps la gueule, et il a fallu revoir beaucoup de choses pour que le correcteur se comporte moins comme un casse‐pieds permanent. J’ai passé un temps considérable à cette adaptation culturelle. J’ai fait passer au correcteur des centaines de textes tirés de blogs et de journaux. J’ai corrigé des tas de faux positifs et j’ai ajouté des centaines de mots dans le dictionnaire. Et beaucoup d’entre vous trouvent sans doute le correcteur encore trop rigide. À vous de me le dire, j’ai eu très peu de retour sur ce point…

Quoi qu’il en soit, pour s’acclimater à Internet, il a fallu assouplir grandement la politique d’intégration des néologismes, des anglicismes, des marques, améliorer l’analyseur lexical pour ignorer des tas de trucs. Par exemple, si l’on peut se passer du verbe « tweeter » quand on ne s’occupe que de LibreOffice, sur le Web, non, ce ne peut être compris des utilisateurs. Trop fréquent. Idem pour pas mal de marques et d’autres néologismes très courants.

Cela dit, je suis toujours réticent à ajouter trop de nouveautés et de bizarreries, les noms de produits sont toujours interdits (tant pis pour les Freebox, les iBidule, et autres gadgets dans l’air du temps…), et je me suis arrêté au moment où j’ai estimé que j’avais suffisamment assoupli les choses… Faites‐moi part de votre avis, en bien ou en mal. Il reste sans doute encore des rugosités à polir.

Lexique du correcteur

La graphie d’un mot français ne permet pas de déterminer sa nature. Un mot finissant par « -ent » peut être un nom, un adjectif, un adverbe ou la forme conjuguée d’un verbe. C’est pourquoi un correcteur grammatical ne peut souvent pas grand‐chose sans un lexique étiqueté référençant tous les mots d’une langue. Cet étiquetage, c’est la base de la connaissance du correcteur.

Ce lexique est généré à partir du dictionnaire orthographique pour Hunspell.

Quelques données sur le dictionnaire :

  • plus de 82 300 entrées (+ 5 300 depuis le précédent journal) ;
  • toutes les entrées sont grammaticalement étiquetées ;
  • environ 20 % d’entre elles sont sémantiquement étiquetées (médecine, informatique, botanique, etc.), mais cet étiquetage ne sert pas encore.

Améliorer la base lexicale et son étiquetage, c’est l’une des tâches les plus importantes de la conception d’un correcteur grammatical. Tout le travail sur le dictionnaire se fait sur Dicollecte, où sont collectées les propositions des utilisateurs.

C’est toujours perfectible

Beaucoup de choses ont été faites, parfois plus que ce qui était demandé, parfois le strict minimum. Évidemment, beaucoup de choses sont encore améliorables, c’est le propre des projets informatiques. Un correcteur grammatical, ce n’est jamais fini.

Si j’ai oublié de mentionner quelque chose d’important, n’hésitez pas à m’en faire part.

Malheureusement pour certains, je n’ai pas réécrit le correcteur en OCaml, Haskell, Lisp, Cobol… [insérez ici votre langage à la mode favori]. Firefox et Thunderbird s’obstinent à ne comprendre que le JavaScript, LibreOffice le Python, le Basic et le Java. C’est barbare ou vintage si on est indulgent, mais c’est ainsi. :-)

Pour l’instant, attendu que Mozilla n’a toujours pas implémenté l’interface de programmation qui permettra de souligner les erreurs directement dans les zones de texte, la correction se fait dans un panneau annexe. Mais, dès que cette interface sera disponible, c’est bien sûr ainsi que les corrections se feront. Au cas où ceci intéresserait quelqu’un parmi vous, sachez qu’il existe une prime pour la réalisation de cette tâche sur Bountysource.

Il existe également une prime pour apporter la colorisation des erreurs dans LibreOffice. Idéalement, j’aimerais qu’on passe des vaguelettes bleues à peine visibles à des traits pleins, épais et colorés. Par exemple :

L’un des problèmes qui m’ennuie assez en ce moment, c’est que la validation des extensions proposées sur le site de Mozilla est devenue vraiment longue depuis le début de l’année. Les contrôleurs sont probablement submergés de nouvelles extensions à cause du passage obligatoire à WebExtension. Du coup, la version 0.5.16 n’est pas encore validée. La version 0.5.17 n’a pas encore été proposée, attendu que toute nouvelle version nous ramène en fin de liste d’attente.

L’avenir Améliorer la correction grammaticale

Après le passage délicat que constituait la désimbrication de Grammalecte de LibreOffice, je vais dorénavant me concentrer sur l’amélioration du correcteur lui‐même. Avec toutes les nouvelles fonctionnalités apportées dernièrement, il est possible de faire bien mieux que ce qu’on a maintenant.

Pour l’instant, le désambiguïsateur, quoique déjà fort utile, est particulièrement sous‐utilisé, pas parce qu’il est difficile d’emploi, mais parce que cette affaire est vraiment plus délicate que je ne l’avais supposé de prime abord, et aussi parce que le processeur de texte fait en vérité déjà beaucoup de travail sur ce point (cf. le journal précédent)… Encore une fois, le diable est dans les détails, les détails, les détails…
En revanche, il m’apparaît de plus en plus évident qu’il serait utile d’adjoindre au désambiguïsateur un système d’annotation du texte, parce que tout ne dépend pas de la désambiguïsation grammaticale, et qu’il est souvent appréciable de connaître le contexte.

Le fait que les règles puissent lancer plusieurs actions va permettre de faire des examens plus complets sur des détails pour l’instant ignorés, notamment sur le contrôle des locutions adverbiales ou les syntagmes nominaux les plus fréquents. Rien de difficile à première vue. Juste un travail de tâcheron qui demande du temps.

L’autre point central à mettre en œuvre, c’est la fusion et le réajustement des règles existantes. Le fait que les règles peuvent dorénavant appliquer autant d’actions que voulu change grandement la donne. Par exemple, je vous avais expliqué dans le précédent journal que la vérification des verbes se faisait après avoir vérifié tout le reste. À présent, même s’il est encore utile de fonctionner ainsi dans bon nombre de cas, il serait appréciable de fusionner certaines de ces vérifications avec le désambiguïsateur qui, lui, est surtout utilisé dans les premières phases de traitement.
Ce travail de fusion et de réajustement des règles ne présente pas de difficultés particulières, c’est surtout une question de stratégie globale qui ne nécessite cependant qu’un travail minutieux et attentif, plutôt ennuyeux et probablement un peu répétitif. Une fois de plus, avec plusieurs centaines de règles concernées, ça ne peut pas se faire en un tour de main.

Autre objectif : bétonner les tests, bétonner les tests, bétonner les tests. Parce que c’est possible et que c’est indispensable pour éviter les bogues inattendus que la correction grammaticale peut susciter. Mais il faudra aussi consolider radicalement les tests par l’ajout systématique des erreurs les plus fréquentes.

Améliorer les suggestions… Un casse‐tête qui s’est beaucoup amélioré ces derniers temps, mais il faudrait quand même réussir à filtrer les absurdités que le correcteur sort parfois. Son défaut est d’être un peu trop ingénieux par moments… mais bon, ça peut faire rire. :-)

Parmi les suggestions, il y a particulièrement les suggestions orthographiques. Pour le moment, dans Firefox et Thunderbird, Grammalecte utilise Hunspell pour avoir des suggestions orthographiques, même s’il n’a plus besoin de lui pour savoir si un mot est présent dans le dictionnaire et récupérer les données grammaticales. Il serait utile d’adjoindre un module de suggestion orthographique en parcourant le graphe de mots à la recherche de possibles mots correspondant suffisamment à la graphie inconnue. Un domaine de recherche à part entière. À mon avis, il serait utile d’implémenter une suggestion basée sur des substitutions phonétiques.

Plus loin, plus fort ?

Ce n’est qu’une idée, pour l’instant mal formée et un peu extravagante, mais je réfléchis à la possibilité de bâtir un graphe de la grammaire française comme il a été possible de bâtir un graphe de l’ensemble des mots du français. La différence, c’est que la totalité des mots français recensés peuvent s’écrire selon un graphe acyclique, avec un début et une fin connus, tandis que si on schématise la grammaire française les boucles et les récurrences sont possibles, les embranchements innombrables, les fantaisies ne sont pas rares, la longueur de la chaîne de tokens est théoriquement sans limite… Et on ne parle là que de phrases faites avec une grammaire correcte. Comment gérer les erreurs rencontrées ? Faut‐il les intégrer dans ce même graphe en créant des branches d’erreur ?
C’est une pensée embryonnaire qui n’aboutira peut‐être à rien d’utile, mais si l’on trouve un moyen de bâtir ce graphe, il devrait être possible de gérer des cas très complexes. Ça nous permettrait de sortir du schéma [motif potentiel d’erreur → condition d’erreur → suggestions → message d’erreur] qu’on peut heureusement combiner avec diverses magouilles plus ou moins subtiles élaborées avec le processeur de texte et le désambiguïsateur.

Ce qui est sûr, c’est que le tokeniseur va prendre une place une place plus importante dans la nouvelle stratégie pour gérer la complexité.

Créer une extension pour Chrome

Pas grand‐chose de spécial à dire sur ce sujet. Il est à espérer que produire l’extension pour Chrome prenne un temps raisonnablement court, attendu que Mozilla a essayé de faire en sorte que la nouvelle API soit proche de celle de Chrome. Mais dans l’univers de JavaScript, y a‐t‐il quelque chose de stable et bien pensé ? Ce serait une bonne surprise. J’ironise et je médis, mais c’est un peu mérité, n’est‐ce pas ?
Quoi qu’il en soit, même si ça se fait plus rapidement qu’attendu – il n’est pas interdit de rêver –, le temps prévu à cette tâche sera utilisé pour renforcer le correcteur, ce ne sera donc pas perdu. En plus, moins je fais de JavaScript, plus je suis heureux… C’est donc avec bonheur que je me livrerai à une tâche plus essentielle que deviner ce que peut vouloir me dire le débogueur de Chrome.

Outils annexes

L’amélioration du lexicographe (qui donne des informations sur les mots) et l’assistant lexical (permettant l’ajout simplifié de mots au lexique) ont pour but de revisiter la manière d’améliorer la base lexicale, dont Dicollecte, le site Web existant pour cette tâche, commence à se faire vieux. J’espère inciter les utilisateurs à participer à la conception de la base en rendant l’interface plus simple et plus séduisante, et surtout directement accessible dans son logiciel. Ça a l’air insignifiant, mais c’est une question essentielle.

Le détecteur de répétitions est proposé parce que c’est un outil qui intéresse potentiellement beaucoup les écrivains soucieux de varier leur vocabulaire.

Le mot de la fin

Bon sang ! Difficile de rendre une campagne de financement attrayante quand on parle de grammaire. Au final, on veut juste de petits liserés sous les mots erronés et un menu contextuel qui explique pourquoi c’est faux et qui suggère quelque chose de crédible. Rien de palpitant à la réflexion. On peut espérer susciter l’intérêt des technophiles en parlant de l’arrière‐cuisine et du système de tuyauterie, mais j’avais déjà presque tout dit la première fois, je ne peux que rendre compte de ce qui a été fait et amélioré.

Si la première campagne vous a satisfait, si vous jugez que la correction grammaticale est trop importante pour être négligée et qu’il faut en finir avec la maltraitance de la langue française, si vous voulez peser sur les priorités du correcteur grammatical ou ajouter des mots spécifiques ordinairement interdits dans le dictionnaire, je vous invite à consulter la page de financement participatif sur Ulule.

Télécharger ce contenu au format Epub

Lire les commentaires

Les journaux LinuxFr.org les mieux notés du mois de mai 2017

7 juin, 2017 - 19:59

LinuxFr.org propose des dépêches et articles, soumis par tout un chacun, puis revus et corrigés par l’équipe de modération avant publication. C’est la partie la plus visible de LinuxFr.org, ce sont les dépêches qui sont le plus lues et suivies, sur le site, via Atom/RSS, ou bien via partage par messagerie instantanée, par courriel, ou encore via médias sociaux.

Ce que l’on sait moins, c’est que LinuxFr.org vous propose également à tous de tenir vos propres articles directement publiables, sans validation a priori des modérateurs. Ceux‐ci s’appellent des journaux. Voici un florilège d’une dizaine de ces journaux parmi les mieux notés par les utilisateurs… qui notent. Lumière sur ceux du mois de mai passé :

Télécharger ce contenu au format Epub

Lire les commentaires

Métriques libres et analyse des développements : rencontre de présentation des enjeux et outils

6 juin, 2017 - 15:04

L’analyse des développements libres basée sur des métriques intéresse à la fois les utilisateurs des projets et leurs mainteneurs, lorsqu’il s’agit d’évaluer la vitalité de ceux‐ci ou les détails de leur fonctionnement. Pour les entreprises ayant adopté des pratiques d’innersourcing, ces méthodes et outils fournissent des indicateurs précieux dans la conduite de leurs projets internes. Inno³ et Bitergia organisent le 14 juin 2017 à Paris une soirée sur le thème des métriques libres couvrant l’ensemble de ces aspects, illustrés avec les outils libres de Bitergia.

Depuis plus de dix ans, Bitergia développe des outils libres permettant de collecter, traiter et restituer une large variété de métriques sur les éléments générés par les projets libres : dépôts de code, système de suivi de bogues, listes de diffusion, etc. La nouvelle génération de ces outils est regroupée au sein du projet Grimoirelab. Le site Cauldron.io permet d’en tester le fonctionnement directement pour les projets GitHub qui vous intéressent.

  • adresse : Inno³, 137 boulevard de Magenta, 75010 Paris
  • métros : Gare du Nord, Barbès‐Rochechouart ou Poissonnière
  • date et heure : mercredi 14 juin 2017, à partir de 19 h
Télécharger ce contenu au format Epub

Lire les commentaires

Revue de presse de l’April pour la semaine 22 de l’année 2017

6 juin, 2017 - 10:04

La revue de presse de l’April est régulièrement éditée par les membres de l’association. Elle couvre l’actualité de la presse en ligne, liée au logiciel libre. Il s’agit donc d’une sélection d’articles de presse et non de prises de position de l’association de promotion et de défense du logiciel libre.

Sommaire

[Nouvelle République] Les « Geek Faëries » reviennent en force
Par Laurence Texier, le dimanche 4 juin 2017. Extrait :

« Malgré la pluie, les geeks ont afflué au château de Selles‐sur‐Cher. Le festival enfile ses plus beaux costumes pour une dernière partie ce dimanche. »

Et aussi :

Voir aussi :

[Silicon] Les Gafam dans les écoles : une menace pour la filière française des ENT ?
Par Reynald Fléchaux_, le jeudi 1er juin 2017. Extrait :

« L’Éducation nationale ouvre ses portes aux solutions des Apple, Google et autre Microsoft. Une volte‐face qui fait bondir trois patrons d’éditeurs spécialisés, les ENT, qui critiquent une forme de deux poids, deux mesures. »

[ZDNet France] L’ARCEP publie son premier rapport sur l’état de l’Internet en France
Par Pierre Col, le mercredi 31 mai 2017. Extrait :

« Ce premier rapport de l’ARCEP fait un point exhaustif sur l’état de l’Internet en France, tant du point de vue du fonctionnement technique du réseau qu’en matière d’accessibilité pour la population et de neutralité des acteurs : un travail utile ! »

[Next INpact] Législatives 2017 : près de cent candidats ont déjà signé le Pacte du logiciel libre
Par Marc Rees, le mardi 30 mai 2017. Extrait :

« À l’occasion des législatives pour 2017, l’association pour la promotion du Libre, l’April, relance sa campagne autour du « Pacte du logiciel libre ». Une initiative qui fête cette année ses dix ans. »

Et aussi :

Voir aussi :

[Resources Solidaires] Les données des élèves et les « GAFAM » : si c’est gratuit, c’est toi le produit !
Par la FCPE, le mardi 30 mai 2017. Extrait :

« La FCPE a pris connaissance d’une communication de la direction nationale du numérique — DNE — indiquant qu’il était désormais possible de fournir aux « GAFAM », grands groupes fournisseurs de services Web, les annuaires des établissements, ceux‐ci ayant des conditions générales d’utilisation — CGU — « éducation » couvrant cette utilisation. »

Voir aussi :

Télécharger ce contenu au format Epub

Lire les commentaires

Outil d’évaluation des aptitudes au développement

6 juin, 2017 - 08:58

Afin d’évaluer les aptitudes à la programmation de candidats à une formation de développeur (déjà présentée sur LinuxFr.org), un langage de programmation très simple a été conçu, ainsi qu’une plate‐forme Web qui permet d’écrire des programmes et de visualiser leur exécution de la façon la plus claire et explicite possible.

Tous les éléments de la plate‐forme sont en anglais et elle comporte également un tutoriel, l’idée étant de la mettre à disposition d’un candidat une dizaine de jours avant le recrutement et de voir s’il sera capable d’auto‐apprentissage dans un environnement nouveau, documenté en anglais. Le jour du recrutement, un test sera effectué, dans lequel des exercices de complexité croissante seront proposés : simples enchaînements d’instructions, boucles basées sur des algorithmes élémentaires, manipulation de structures plus complexes en mémoire telles que des chaînes de caractères, etc.

La seconde partie décrit le système.

L’utilisateur dispose d’un ruban mémoire qui ne peut contenir que des nombres, entiers ou réels. Il dispose de huit instructions, qui peuvent comporter des expressions arithmétiques et qui vont lire et/ou écrire dans le ruban mémoire, ou effectuer des entrées‐sorties. À ce stade, le langage est proche d’un assembleur, mais avec un typage plus large (entier/réel) et des expressions arithmétiques dont la complexité n’est pas bornée. Ce niveau de langage assez bas permet d’enlever toute ambiguïté aux instructions, et de visualiser tous les effets de leur exécution. Pour rendre l’activité ludique, des instructions de dessin à la logo permettent de déplacer un stylo virtuel et de tracer des segments de droite comme on peut le voir sur l’image.

La plate‐forme est écrite en VueJS et s’exécute exclusivement sur le client sans aucune partie serveur. Son code est disponible sur GitHub et les contributions au langage et au développement sont les bienvenues.

Télécharger ce contenu au format Epub

Lire les commentaires

Nouvelles versions logicielles du projet GNU avril et mai 2017

5 juin, 2017 - 00:08

Le projet GNU publie tous les mois une liste de versions logicielles publiées. Jetons‐y un coup d’œil pour découvrir de nouveaux logiciels inconnus (de moi), des infâmes bogues disparus ou les promesses de solutions à tous nos besoins : soit 33 nouvelles versions annoncées allant de la corrective mineure à la version attendue depuis des années ; et l’on va donc parler de acct, artanis, bc, diffutils, emacs, emms, freedink-data, gcc, global, gnubik, gnupg, gnutls, grub, guile, guile-cv, guile-ncurses, icecat, kawa, less, libcdio-paranoia, libidn2, libmicrohttpd, linux-libre, nano, ocrad, orgadoc et parallel.

Sommaire acct-6.6.3 (avril)

Il s’agit d’une mise à jour mineure de cet outil d’enregistrement des actions sur le système (nom d’utilisateur et processus), pour incorporer des correctifs venant de SUSE et Red Hat.

artanis-0.2.1 (mai)

On y trouve quelques corrections de bogues et un peu plus de robustesse pour cette boîte à outils pour applications Web (WAF) en Guile Scheme.

bc-1.07.1 (avril)

La version apporte principalement des compléments documentaires et quelques corrections de bogues pour cette calculatrice à précision arbitraire.

diffutils-3.6 (mai)

Cet ensemble d’outils pour comparer des fichiers reçoit une nouvelle fonctionnalité (si un fichier comparé est le début de l’autre), des corrections de bogues et une amélioration de performance sur les gros fichiers.

emacs-25.2 (avril)

Cet éditeur polyvalent a connu une version mineure corrective.

emms-4.3 (mai)

Ce logiciel utilisé pour gérer les fichiers multimédia dans emacs reçoit quelques corrections, affiche plus de métadonnées à l’exécution et moins d’avertissements à la compilation.

freedink-data-1.08.20170409 (avril)

Les données de ce jeu d’aventure et de rôle à la Zelda sont complétées par deux nouveaux sons, une traduction en suédois et des mises à jour des traductions catalane, espagnole et allemande, ainsi qu’une construction reproductible.

gcc-7.1.0 (mai)

Cette suite de compilateurs a ou aura sa propre dépêche pour détailler les nouveautés.

global-6.5.7 (mai)

Cet outil pour étiqueter du code source reçoit une nouvelle option --nearness, des nouveaux alias GTAGSOBJDIR et GTAGSOBJDIRPREFIX, une nouvelle commande --print, la prise en charge des espaces de noms et traits de PHP 5 et supérieur, et des corrections de bogues.

gnubik-2.4.3 (avril)

Il s’agit d’une mise à jour des traductions et la correction de bogues mineurs pour ce jeu de puzzle de type Rubik’s cube.

gnupg-2.1.21 (mai)

Cette suite d’outils autour d’OpenPGP corrige principalement un bogue important introduit par la version précédente, la suppression du squelette de configuration par défaut (remplacé par celui du système), l’installation sans être administrateur sous Windows et divers bogues.

gnutls-3.5.12 (mai)

Cette bibliothèque pour gérer les protocoles SSL, TLS et DTLS connaît une version corrigeant divers bogues (sans changement d’API ou d’ABI).

grub-2.02 (avril)

La précédente version 2.00 de ce chargeur d’amorçage datait de 2012. Cette version apporte notamment des améliorations sur l’interface graphique et la prise en charge de nouvelles plates‐formes comme ARM, ARM64 et Xen, ainsi qu’une meilleure prise en charge de coreboot (en pratique les distributions GNU/Linux utilisaient déjà des pré‐versions de la 2.02 depuis longtemps).
(source de l’image)

guile-2.2.2 (et 2.2.1) (avril)

Ce langage de programmation reçoit deux versions correctives (la dernière corrigeant la précédente). Citons notamment l’ajout d’une fonction de bac à sable pour tester du code d’utilisateurs inconnus et l’interdiction sous peine d’exception de modifier des constantes à la compilation ou à l’exécution.

guile-cv-0.1.4 (mai)

Il s’agit de la première version publique de cette bibliothèque de vision par ordinateur pour Guile Scheme, et première version incluse dans le projet GNU.

guile-ncurses-2.2 (avril)

Cette bibliothèque ncurses (interfaces textuelles) pour Guile vise la prise en charge de Guile 2.2.

icecat-52.0.2-gnu1 (avril) et 52.1.0-gnu1 (mai)

Il s’agit d’une version démarquée de Firefox, sans greffon ou extension non libre, qui suit donc les versions produites chez Mozilla.

kawa-2.4 (mai)

kawa implémente du Scheme en Java, et cette version est une corrective mineure.

less-487 (avril)

less permet de visualiser un fichier texte page par page. Cette version apporte des nouvelles commandes ESC-{ et ESC-} pour aller au début et à la fin des lignes affichées, une mise en valeur des recherches qui gère l’option « sans tenir compte de la casse » -i, le passage à Unicode 9.0.0, une option -Da sous Windows pour le mode SGR et des corrections de bogues.

libcdio-paranoia-10.2+0.94+1 (avril)

Cette bibliothèque pour gérer les images CD (mais si, vous savez, les galettes en polycarbonate et alu) reçoit quelques corrections de bogues (la précédente version 10.2+0.93+1 datant de 2014).

libidn2-2.0.1 (avril) et 2.0.2 (mai)

Cette bibliothèque gère le codage et le décodage des noms de domaine internationalisés suivant les spécifications IDNA 2008 et TR 46 (RFC 5890, 5891, 5892, 5893 et TR 46). Ces versions amènent la prise en charge de IDNA 2008 et TR 46 par défaut, et des corrections de bogues.

libmicrohttpd-0.9.53 (avril) puis 0.9.54 et 0.9.55 (mai)

Cette bibliothèque qui évolue visiblement assez vite fournit un micro‐serveur Web en C. Ces versions amènent une meilleure prise en charge de l’en‐tête Upgrade, des options de compilation pour choisir la fonction de polling suivant la plate‐forme, et diverses corrections.

linux-libre-4.10.12-gnu (avril) et 4.11.2-gnu (mai)

Le projet vise à publier et maintenir le noyau Linux 100 % libre. Les principaux blocs binaires (blobs) sont présents dans les pilotes graphiques, mais aussi pour l’accélération cryptographique, l’Ethernet ou l’écran tactile, et chaque nouvelle version du noyau amène en général son lot de nouveaux blocs binaires.

nano-2.8.1 (avril) à 2.8.4 (mai)

L’éditeur de texte nano a connu quatre versions, notamment pour corriger des bogues, des plantages, améliorer les traductions, accélérer les recherches arrières, améliorer la coloration syntaxique en PHP, éviter d’introduire des blancs intempestifs, mieux gérer les caractères de largeur double, etc.

::: The iLE88Dj. :jD88888Dj: .LGitE888D.f8GjjjL8888E; .d8888b. 888b 888 888 888 iE :8888Et. .G8888. d88P Y88b 8888b 888 888 888 ;i E888, ,8888, 888 888 88888b 888 888 888 D888, :8888: 888 888Y88b 888 888 888 D888, :8888: 888 88888 888 Y88b888 888 888 D888, :8888: 888 888 888 Y88888 888 888 D888, :8888: Y88b d88P 888 Y8888 Y88b. .d88P 888W, :8888: "Y8888P88 888 Y888 "Y88888P" W88W, :8888: W88W: :8888: 88888b. 8888b. 88888b. .d88b. DGGD: :8888: 888 "88b "88b 888 "88b d88""88b :8888: 888 888 .d888888 888 888 888 888 :W888: 888 888 888 888 888 888 Y88..88P :8888: 888 888 "Y888888 888 888 "Y88P" E888i tW88D Text Editor Homepage ocrad-0.26 (avril)

Ce logiciel de reconnaissance optique de caractères (OCR) a connu une nouvelle version de pure maintenance (la dernière publication datant de 2015).

orgadoc-0.9 (mai)

Le logiciel permet de copier et gérer un ensemble de documents sur plusieurs ordinateurs. La précédente version datait de 2004, et il s’agit surtout de mettre à jour les scripts et la documentation pour l’utilisation et l’installation.

parallel-20170422 et 20170522

Cet outil shell permet d’exécuter des tâches en parallèle sur un ou plusieurs ordinateurs. Ces versions sont nommées respectivement Санкт-Петербу́рг (Saint‐Pétersbourg) et Macron (les précédentes étant baptisées TRAPPIST-1, 13769 et George Michael). Санкт‐Петербу́рг abandonne la prise en charge de Perl 5.6 sur IRIX, --halt prend désormais en charge done en plus de success et fail, et parset initialise les variables en Bash. Et Macron n’amène que peu de nouveautés (--timeout accepte s=second, m=minute, h=hour et d=day, tandis que l’alias --dr est ajouté pour --dry-run) et sert donc de version stable.

Conclusion

Y a‐t‐il un intérêt à écrire une telle dépêche ? Bonne question, je vous remercie de l’avoir posée.

Pour LinuxFr.org, ça fait une dépêche publiée de plus, ce qui est plutôt bien (mais ça fait encore une longue dépêche diront certains, j’aurais dû en faire 33. ;)

Pour moi, ça m’a fait découvrir divers logiciels, donc j’ai trouvé ça plutôt intéressant, même si j’ai un peu de mal avec l’hétérogénéité du projet GNU, avec des annonces par courriel ou non, sur le site du projet ou non, dans les fichiers Changelog ou NEWS de l’archive, etc. Et probablement pas au point de la refaire tous les mois tout seul, parce que ça reste un poil long à rédiger. Des volontaires ?

Pour vous, je ne sais pas, mais si déjà vous lisez ça, c’est que vous êtes allés beaucoup plus loin que la plupart. Bravo, vous gagnez un niveau !

Ah oui, toute bonne conclusion doit terminer sur une ouverture : à voir les écarts de publication entre les versions, on peut se dire que le projet GNU manque de contributeurs (sur le code ou sur les sites des projets visiblement), qu’il est plus facile de pondre un nouveau logiciel que de le maintenir sur des dizaines d’années, qu’il s’attaque à des chantiers immenses (virer les blobs, par exemple). Ou plus positivement, on peut rester admiratif devant ces projets maintenus sur des dizaines d’années, ces très ambitieux projets lancés pour garantir les quatre libertés, la diversité des projets existants, la variété des langues prises en charge et le fait que des gens lisent une telle dépêche.

Télécharger ce contenu au format Epub

Lire les commentaires