Projet - Détecteur de langue d’un texte
Présentation
Comment un navigateur web sait-il qu’il doit vous proposer de traduire une page ? Comment un correcteur orthographique choisit-il son dictionnaire ? Il doit d’abord reconnaître la langue du texte.
L’objectif de ce projet est de produire un programme qui détecte la langue dans laquelle un texte est écrit, en s’appuyant sur l’analyse fréquentielle des lettres. En effet, les lettres de l’alphabet n’ont pas la même fréquence d’apparition dans toutes les langues : le « e » domine en français, en anglais ou en allemand, mais le « a » domine en espéranto, et le « w » est très rare en français alors qu’il est fréquent en polonais.
Votre programme apprendra à reconnaître chaque langue à partir de textes d’exemple, puis sera évalué sur des textes qu’il n’a jamais vus. C’est le principe, très simplifié, de l’apprentissage automatique.
Mode de travail
- Le travail est réalisé en binômes.
- Durée indicative : 4 à 5 séances en classe.
- Les étapes 1 et 2 sont guidées : les noms et les rôles des fonctions sont imposés, et un fichier de tests vous dit si vous êtes dans la bonne direction.
- Les étapes 3 et 4 sont plus ouvertes : c’est à vous de concevoir et de justifier vos choix. Des coups de pouce repliés sont disponibles, mais essayez d’abord seul.
- Vous êtes libre d’organiser votre code (fichiers, modules, style de programmation) à condition de respecter les noms de fonctions imposés. Vous pouvez effectuer des recherches documentaires.
- Des compléments facultatifs sont proposés pour aller plus loin.
Ressources
Téléchargez l’archive ressources_eleves.zip et décompressez-la dans le dossier de votre projet. Elle contient :
| Élément | Contenu |
|---|---|
textes/apprentissage/ |
un long texte par langue (fr.txt, en.txt, de.txt, es.txt, it.txt, pt.txt, eo.txt, pl.txt, nl.txt) : c’est avec eux que votre programme apprend |
textes/test/ |
un texte complet par langue (fr_piliers.txt, …) pour tester le programme |
textes/courts/ |
des extraits de 60, 150 et 400 caractères (fr_60.txt, fr_150.txt, …) pour tester sur des textes courts |
textes/inconnues/ |
des textes en suédois, danois, roumain et turc (utiles pour un complément) |
textes/mixte/ |
un texte écrit en plusieurs langues (utile pour un complément) |
test_projet.py |
un programme qui teste automatiquement vos fonctions |
Le nom de chaque fichier de test commence par le code de sa langue (fr, en…) : le programme peut ainsi connaître la bonne réponse. Tous les textes proviennent de Wikipédia (licence CC BY-SA 4.0).
Les langues étudiées sont : Français, Anglais, Allemand, Espagnol, Italien, Portugais, Espéranto, Polonais et Néerlandais. Recopiez en début de programme les deux constantes suivantes :
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
NOMS = {"fr": "Français", "en": "Anglais", "de": "Allemand", "es": "Espagnol",
"it": "Italien", "pt": "Portugais", "eo": "Espéranto", "pl": "Polonais",
"nl": "Néerlandais"}Vocabulaire
- Le profil d’un texte est le dictionnaire donnant la fréquence de chacune des 26 lettres, c’est-à-dire la part de cette lettre parmi toutes les lettres du texte.
- La signature d’un profil est la liste de ses
nlettres les plus fréquentes (par défautn = 10), de la plus fréquente à la moins fréquente.
Évaluation
| Critère | Points |
|---|---|
| Fonctionnement du programme (tests, étapes 1 à 3) | 8 |
Qualité du code (découpage en fonctions, noms, commentaires et docstring, lisibilité) |
4 |
| Évaluation du détecteur et analyse des résultats (étape 4) | 5 |
| Autonomie, organisation du travail, honnêteté du compte rendu | 3 |
| Compléments | bonus |
Étape 1 : compter les lettres (guidée)
Créer un fichier detecte_langue.py contenant les fonctions suivantes. Écrivez-les dans l’ordre et vérifiez-les avec python test_projet.py au fur et à mesure.
| Fonction | Rôle |
|---|---|
lit_fichier(chemin) |
retourne le contenu d’un fichier texte encodé en UTF-8 sous forme d’une chaîne |
compte_lettres(texte) |
retourne un dictionnaire dont les clés sont les 26 lettres de « a » à « z » et dont les valeurs sont leur nombre d’apparitions dans texte. Les majuscules sont comptées comme les minuscules. Les autres caractères (chiffres, ponctuation, lettres accentuées…) sont ignorés |
frequences(compteurs) |
prend un dictionnaire tel que celui retourné par compte_lettres et retourne un dictionnaire de mêmes clés avec la fréquence de chaque lettre, c’est-à-dire sa part parmi les lettres comptées (la somme des valeurs vaut 1). Si aucune lettre n’a été comptée, toutes les fréquences valent 0 |
profil(texte) |
retourne le profil de texte (enchaînement des deux fonctions précédentes) |
histogramme(un_profil) |
affiche le diagramme en barres d’un profil à l’aide de la bibliothèque matplotlib |
Remarques
- Pour
histogramme, recherchez dans la documentation dematplotlibdes exemples de diagrammes en barres (bar). Soignez le rendu : titre, légendes des axes. - Affichez le profil du texte
textes/apprentissage/fr.txt, puis celui detextes/apprentissage/pl.txt. Que remarquez-vous ?
Dans cette étape, les lettres accentuées et spécifiques (é, è, ç, ñ, ł, ß…) sont ignorées. Citez au moins deux conséquences possibles de ce choix sur le profil d’un texte français ou polonais. Conservez votre réponse : vous y reviendrez à l’étape 4.
Étape 2 : apprendre les langues (guidée)
L’idée est de ne plus recopier à la main un tableau de fréquences trouvé sur Internet, mais de calculer les profils des langues à partir des textes du dossier textes/apprentissage/.
| Fonction | Rôle |
|---|---|
signature(un_profil, n=10) |
retourne la liste des n lettres les plus fréquentes du profil, de la plus fréquente à la moins fréquente |
apprend(dossier) |
lit tous les fichiers xx.txt du dossier et retourne un dictionnaire profils dont les clés sont les noms des langues ("Français"…) et les valeurs leurs profils |
Par exemple, après profils = apprend("textes/apprentissage"), l’appel signature(profils["Français"])[0] retourne "e".
import os
for nom_fichier in os.listdir("textes/apprentissage"):
chemin = os.path.join("textes/apprentissage", nom_fichier)
code = nom_fichier.split(".")[0] # "fr.txt" -> "fr"Pour programmer signature
- Programmez-la d’abord sans utiliser
sortednisort: en vous inspirant du tri par sélection vu en classe de première, cherchez à chaque tour la lettre la plus fréquente parmi celles qui restent. - Programmez ensuite une seconde version avec la fonction
sortedet son paramètrekey(documentation Python : Guide pour le tri). - Vérifiez que vos deux versions donnent le même résultat sur les neuf langues. Quel est l’avantage de chacune ?
Affichez les signatures apprises pour chaque langue et comparez-les avec le tableau de Wikipédia. Où sont les différences ? Comment les expliquez-vous (nature des textes, taille, lettres accentuées…) ?
Quelles langues ont des signatures très proches ? Quelles difficultés cela laisse-t-il prévoir pour la détection ?
Étape 3 : comparer un texte aux langues connues (ouverte)
Pour deviner la langue d’un texte, on calcule son profil, puis on cherche la langue dont le profil est le plus proche. Il faut donc définir précisément ce que « proche » veut dire : c’est le rôle d’une distance entre deux profils.
Vous allez programmer deux distances très différentes, puis les comparer.
3.1. Distance entre signatures (guidée)
| Fonction | Rôle |
|---|---|
distance_rang(p1, p2, n=10) |
calcule les signatures de p1 et p2 (à n lettres), puis retourne la somme, pour chaque lettre figurant dans au moins une des deux signatures, de l’écart entre son rang dans la première et son rang dans la seconde. Si une lettre est absente d’une signature, son rang dans celle-ci vaut n |
Exemple avec n = 3 : les signatures sont ["a", "b", "c"] et ["b", "a", "d"].
| Lettre | a | b | c | d |
|---|---|---|---|---|
| Rang dans la première | 0 | 1 | 2 | 3 (absente) |
| Rang dans la seconde | 1 | 0 | 3 (absente) | 2 |
| Écart | 1 | 1 | 1 | 1 |
La distance vaut donc 1 + 1 + 1 + 1 = 4.
3.2. Distance entre profils (ouverte)
La distance précédente n’utilise que l’ordre des dix lettres les plus fréquentes : elle ignore les fréquences elles-mêmes, ainsi que les seize autres lettres.
Programmez une fonction distance_frequences(p1, p2) qui retourne un nombre mesurant l’écart entre les fréquences des 26 lettres de deux profils. Vous devez la concevoir vous-même et justifier votre choix. Elle doit vérifier :
distance_frequences(p, p)vaut 0 ;distance_frequences(p1, p2)est égale àdistance_frequences(p2, p1)et n’est jamais négative ;- plus les profils se ressemblent, plus la valeur est petite.
Comment mesure-t-on l’écart entre deux nombres ? Et si on a vingt-six nombres à comparer deux à deux, comment obtenir un seul résultat ?
Comment mesure-t-on la distance euclidienne entre deux points dans un plan ? Et dans l’espace à trois dimensions ? Et dans un espace à 26 dimensions ?
3.3. Le détecteur (ouverte)
Programmez la fonction detecte_langue(texte, profils, distance) qui retourne le nom de la langue dont le profil (dans le dictionnaire profils retourné par apprend) est le plus proche du profil de texte, au sens de la fonction distance donnée en paramètre.
En Python, une fonction est un objet comme un autre : on peut la passer en argument, puis l’appeler (cf. le cours sur la programmation fonctionnelle).
def double(x):
return 2 * x
def applique(f, valeur):
return f(valeur)
print(applique(double, 5)) # affiche 10Ici, distance est une fonction qui prend deux profils et retourne un nombre. On l’utilise donc ainsi : distance(profil_du_texte, profil_de_la_langue).
Testez votre détecteur avec chacune des deux distances sur les textes du dossier textes/test/. Utilisez test_projet.py pour valider votre travail.
Étape 4 : évaluer le détecteur (ouverte)
Un programme qui réussit sur quelques exemples n’est pas forcément fiable. Comment le mesurer ?
Programmez une fonction evalue(profils, dossier, distance) qui teste le détecteur sur tous les fichiers d’un dossier de test (le nom de chaque fichier commence par le code de sa langue) et retourne un couple (taux, erreurs) :
tauxest la proportion de textes dont la langue est correctement détectée (un nombre entre 0 et 1) ;erreursest la liste des cas d’échec, chacun étant décrit par le nom du fichier, la langue attendue et la langue détectée.
Puis réalisez et rédigez une petite étude, en un compte rendu d’une page (document texte ou notebook) :
Comparaison des distances. Complétez un tableau donnant le taux de réussite de
distance_ranget dedistance_frequencessurtextes/test/et surtextes/courts/.Distance Textes complets Textes courts distance_rangdistance_frequencesAnalyse des erreurs. Pour chaque erreur, quelles langues sont confondues ? Pourquoi, à votre avis ? Votre réponse à la question 3 est-elle confirmée ?
Influence de la longueur du texte. Évaluez séparément les textes de 60, de 150 et de 400 caractères, puis tracez avec
matplotlible taux de réussite en fonction de la longueur. À partir de quelle longueur le détecteur est-il fiable ?Influence de la taille de l’apprentissage. Que se passe-t-il si l’on apprend seulement avec les 500, 2 000 ou 5 000 premiers caractères de chaque texte d’apprentissage ?
Pourquoi ne pas tester sur les textes d’apprentissage ? Quel résultat obtiendrait-on, et pourquoi ne serait-il pas significatif ?
Bilan. Reprenez la question 1 : les lettres accentuées auraient-elles aidé ? Proposez au moins deux améliorations concrètes et justifiées de votre détecteur.
Compléments facultatifs
Choisissez ceux qui vous intéressent, dans n’importe quel ordre. Les étoiles indiquent la difficulté.
A. Les bigrammes (★★)
Au lieu des lettres seules, comptez les paires de lettres consécutives (« th », « qu », « ch », « e␣ »…) : le profil d’une langue devient un dictionnaire dont les clés sont ces paires. Adaptez la comparaison et mesurez le gain sur les textes courts de l’étape 4. Que dire du coût du calcul ?
B. Les accents et les alphabets étendus (★★)
Étendez votre profil aux lettres accentuées ou propres à certaines langues (é, ñ, ç, ä, ß, ł, ĉ…). Vous pouvez aussi essayer de normaliser le texte (é devient e) avec le module unicodedata. Dans les deux cas, mesurez l’effet sur les taux de réussite. L’effet est-il celui que vous attendiez ? Pourquoi ?
C. Page web (★★)
Programmez une fonction detecte_langue_url(url, profils) qui retourne la langue dans laquelle une page web est écrite. La bibliothèque BeautifulSoup permet d’extraire le texte présent dans les balises HTML (voir la documentation). Attention : l’accès à Internet peut être filtré au lycée. Essayez avec des pages de Wikipédia dans différentes langues.
D. Langue inconnue (★★)
Que répond votre détecteur pour un texte écrit en suédois, en danois, en roumain ou en turc (dossier textes/inconnues/) ? Modifiez-le pour qu’il réponde "Indéterminée" lorsque le texte est trop éloigné de toutes les langues connues. Comment choisir le seuil ? Que constatez-vous pour le roumain ?
E. Texte en plusieurs langues (★★★)
Le fichier textes/mixte/melange.txt contient successivement des paragraphes en français, anglais, allemand, espagnol et italien. Programmez un détecteur qui indique la langue au fil du texte.
On utilisera une fenêtre glissante : une file contenant les N dernières lettres lues (par exemple N = 150). Chaque nouvelle lettre est enfilée et, si la file est pleine, la plus ancienne est défilée. Vous pouvez utiliser collections.deque. Les compteurs de lettres sont mis à jour en ne regardant que la lettre entrante et la lettre sortante, sans recompter toute la fenêtre. Quelle est la complexité de cette mise à jour, comparée à un recomptage complet ? Comment choisir N ?
Rendu
À la fin du projet, déposez un fichier zip contenant :
- votre fichier
detecte_langue.py(et les éventuels autres modules) ; - votre compte rendu de l’étape 4 ;
- éventuellement vos compléments.
Le programme doit pouvoir s’exécuter dans un dossier contenant uniquement vos fichiers et le dossier textes.