Détection de la langue d’un texte (3)

Publié le mars 26, 2010

javaDans un premier article sur le sujet, je décris une méthode de détection de la langue d’un texte basée sur un calcul statistique de présence de n-gram dans le texte.  Cette distribution statistique est alors comparée à des distributions types pré-calculées sur des corpus de texte dans différentes langues. La langue du texte est alors à priori celle du corpus présentant la distribution la plus proche. Comme je l’ai expliqué dans mon premier article, j’ai utilisé NgramJ, une librairie Java open source. Après différents tests avec d’autres librairies, NgramJ reste pour moi la plus efficace.

Cependant, cette méthode n’est pas fiable à 100% et NgramJ est limitée dans sa version actuelle à 26 langues détectables : Bulgare (bg), Tchèque (cz), Danois (da), Allemand (de), Grec (el), Anglais (en) Espagnol (es), Estonien (et), Finlandais (fi), Français (fr), Hongrois (hu), Islandais (is), Italien (it), Lituanien (lt), Letton (lv), Maltais (mt) , Néerlandais (nl), Norvégien (no), Polonais (pl), Portugais (pt), Roumain (ro), Russe (ru) , Slovaque (sk), Slovène (sl), Suédois (sv) et Thaï (th).

Je voudrais proposer 2 pistes pour améliorer NGramJ : la fiabilité de la détection et le nombre de langues détectables.

La fiabilité de la détection

NgramJ utilise un algorithme complexe de calcul statistique sur les n-grams et je n’ai pas la prétention de l’améliorer. Par contre, on peut facilement fournir des informations complémentaire à NGramJ afin qu’il oriente sa détection sur un sous-ensemble restreint de langues et non pas sur toutes les langues qu’il gère (surtout si on veut en augmenter le nombre).

Que connait-on du texte que l’on veut analyser ? On connait l’encodage de ses caractères (iso-8859-1, iso-8859-5, windows-1252, … et souvent unicode ou utf-8). Chacun de ces jeux de caractères permet d’écrire un ensemble plus ou moins important de langues (voir Character encoding). Unicode est particulier car il permet de coder toutes les langues, par contre une ou plusieurs langues partagent un sous ensemble d’unicode qu’il est facile d’identifier (voir Unicode 5.2 Character Code Charts). On peut donc pour un texte donné se limiter à la détection des langues couvertes par le sous ensemble unicode qu’il utilise.

Par exemple, pour un texte codé en iso-8859-5 (caractères cyrilliques) les langues possibles sont : Bulgare (bg), Biélorusse (be), Macédonien (mk), Russe (ru), Serbe (sr) et Ukrainien (uk). La seule langue que devrait donc nous proposer NGramJ si on lui demande de se retreindre à ces langes est le Russe. Ce n’est pas satisfaisant. En effet, je ne veux pas que NgramJ me dise qu’un texte Bulgare est du Russe. Plus loin dans cet article, on verra comment « appendre » de nouvelles langues à NgramJ.

Encore mieux, on peut sans même le demander à NgramJ connaitre la langue d’une texte. En effet, certains jeux de caractères permettent de coder une seule langue : iso-8859-15 (Estonien), iso-8859-9 (Turc), iso-8859-7 (Grec), …

En conclusion, même si on parvient à apprendre à NgramJ la totalité des langues existantes (et pourquoi pas), on pourra toujours lui indiquer de travailler sur un sous-ensemble réduit de deux à une dizaine de langues en fonction de l’encodage du texte.

Un point auquel il faut porter attention est qu’un même jeux de caractères peut avoir plusieurs noms voisins. On trouve sur le site de l’INIA, l’ensemble des jeux de caractères avec leur nom préféré et leurs alias.

Pour traiter cet aspect préliminaire à l’utilisation de NGramJ, j’ai développé une classe utilitaire CharsetLanguages qui permet de :

  • Obtenir le nom préféré d’un jeux de caractères.
  • Obtenir les blocs unicode utilisé pour l’écriture d’un texte
  • Obtenir les langues associées à un jeu de caractères
  • Obtenir les langues associées aux blocs unicode détectés dans un texte

Pour simplifier l’utilisation de NgramJ, j’ai également développé une classe LanguageRecognizerCngram qui permet de :

  • Initialiser un contexte de détection en spécifiant le répertoire ou sont les fichiers de définitions des langues (fichiers modèles) et la liste des langues que l’on veut pouvoir détecter (si on sait à l’avance que les documents que l’on va traiter se limitent à ces langues)
  • lancer la reconnaissance d’une langue dans un texte

Le code source de ces classes est fournie sous la forme d’un projet Eclipse. je fournie également la version compilée de NGramJ modifié. Ces modifications ont été transmises à l’auteur qui s’est proposé de les intégrer à la version officielle.

Voici un exemple de code pour l’utilisation de ces classes :

public static String getLanguage (String content, String charSet)
{
 
String language = null;
 
// Define the maximum length for the text to be analyzed
int languageDetectionLength = 2048;
 
// Define a list of candidate languages for the text
String languageDetectionList = "bg, ru, sr, uk";
 
content = content.substring(0, Math.min(content.length(), languageDetectionLength));
 
// Create the language detector
LanguageRecognizerCngram lrCnGram = new LanguageRecognizerCngram();
if (lrCnGram!=null)
{
    // Set the directory for the language models
    lrCnGram.setLanguageModelsDir("/Data/ngramj/ngp/ngp");
    if (languageDetectionList!=null && !"".equals(languageDetectionList))
    {
        // Create a list for the candidate languages
        String[] tab = languageDetectionList.split(",");
        ArrayList<String> arr = new ArrayList<String>(Arrays.asList(tab));
        for (int i=0;i<arr.size();i++) {
           arr.set(i, arr.get(i).trim());
        }
 
        // Set the candidate languages
        lrCnGram.setCandidateLanguages(arr);
    }
    else
    {
        // Try to get a candidate languages list according to the charset
        ArrayList<String> charSetCanditateLanguages;
        CharsetLanguages csu = new CharsetLanguages();
 
        if (charSet == null || "".equals(charSet))
            charSet ="utf-8";
        else
            charSet = csu.getCharsetCanonicalName(charSet);
 
        if ("utf-8".equals(charSet))
        {
            String s = csu.getUnicodeBlockDistribution (content);
            charSetCanditateLanguages = csu.getUnicodeBlockLanguages(s);
        }
        else
        {
            charSetCanditateLanguages = csu.getCharsetLanguages(charSet);
        }
 
        // Only one possible language so nothing else to do
        if (charSetCanditateLanguages!=null && charSetCanditateLanguages.size()==1)
            return charSetCanditateLanguages.get(0);
 
        // Set the candidate languages
        lrCnGram.setCandidateLanguages(charSetCanditateLanguages);
    }
 
    // Process the recognition
    language = lrCnGram.RecognizeLanguage( content, 0.5, null);
    return language;
}

Ajouter des langues à la liste de celles détectables par NGramJ

Pour ce travail, je me suis inspiré de l’article suivant « Generating a Plain Text Corpus from Wikipedia« .

Pour ajouter une nouvelle langue, les étapes sont :

  • Récupérer un corpus significatif de texte écrit dans cette langue
  • Nettoyer le texte
  • Créer le fichier modèle avec NGramJ

Obtenir un corpus

Pour obtenir un corpus dans une langue donnée, il est possible de télécharger la totalité des articles de wikipedia dans cette langue. L’adresse dépend du code ISO de la langue :

http://download.wikimedia.org/<code iso>wiki/latest/<code iso>wiki-latest-pages-articles.xml.bz2

Par exemple, pour l’arabe, on récupère une archive et on la décompresse comme ceci :

curl -O http://download.wikimedia.org/arwiki/latest/arwiki-latest-pages-articles.xml.bz2
bunzip2 arwiki-latest-pages-articles.xml.bz2

Nettoyer le corpus

Pour nettoyer le fichier xml, je me suis inspiré de ce script.

Ma version est plus violent mais ne supprime pas ce qui n’est pas iso-8859-1.

wikicat2.sh :

#!/bin/sh
# http://wiki.apertium.org/wiki/Talk:Calculating_coverage#wikicat2.sh
# clean up wiki for running through apertium-destxt
# Use $ ./wikicat2.sh blah-pages-articles.xml.bz2
 
# awk prints full lines, make sure each html element has one
#bzcat "$@" | sed 's/&gt;/&gt;n/g' | sed 's/<!--n</g' |
cat "$@" | sed 's/-->/&gt;n/g' | sed 's/<!--n</g' |
# want only stuff between <text...--> and
awk '
//,/&lt;/text&gt;/ { print $0 }
' |
 
sed 's/[[([a-z]{2,3}):[^]]+]]//g' |
# Drop all transwiki links
 
sed 's/[[[^]|]*|//g' | sed 's/]]//g' | sed 's/[[//g' |
# wiki markup, retain bar and fie from [[foo|bar]] [[fie]]
 
sed 's/[http[^ ]*([^]]*)]/1/g' |
# wiki markup, retain `bar fie' from [http://foo bar fie]
 
sed 's/&amp;.*;/ /g' |
# remove entities greedily, so as to get rid of hidden html too
 
# Keep only lines starting with a capital letter, removing tables with style info etc.
# grep '^[ t]*[A-ZÆØÅ]' # Your alphabet here
grep -vE '^([|{=}!]|[a-z]{2,3}:)' |
 
# some other cleanup
grep -vE '^([a-z]{1,4}-[a-z]{1,4}:)' |
grep -vE '^([a-z]{1,4}-[a-z]{1,4}-[a-z]{1,4}:)' |
grep -vE '^s*[a-z]{0,2}s*$' |
grep -vE '^s*$' |
grep -vE "([|]|'{3})" |
 
# keep a raisonnable corpus size
head -n 2000

Et donc, pour générer un fichier nettoyé la commande est :

wikicat2.sh arwiki-latest-pages-articles.xml &gt;ar.txt

Ce script peut être à améliorer en fonction de résultat obtenu. Quoi qu’il en soit, il faut tout faire pour nettoyer au mieux le texte et même terminer le travail à la main si nécessaire.

Créer le fichier model

Le fichier modèle est généré à partir du texte nettoyé avec la commande suivante :

java -jar cngram.jar -create ar ar.txt "UTF-8"

On peut enchainer automatiquement toutes ces taches en prenant soin de ne télécharger le corpus de wikipedia que si on ne l’a pas déjà. Voici comment préparer un environnement de travail et un script shell qui va enchainer toutes les opérations automatiquement pour une liste de langues.

1. dans un répertoire, on place cngram.jar et wikicat2.sh.

2. on y créer buildngp.sh

#!/bin/bash
 
mkdir data &gt; /dev/null 2&gt;&amp;1
cd  data
mkdir ngp &gt; /dev/null 2&gt;&amp;1
 
# for all these languages
for i in fr fa ar be mk sr uk; do
    echo $i
 
    # download file from wikipedia only if necessary
    if [ -f ${i}wiki-latest-pages-articles.xml ];
    then
        echo "${i}wiki-latest-pages-articles.xml exists in local"
    else
        # download
        curl -O http://download.wikimedia.org/${i}wiki/latest/${i}wiki-latest-pages-articles.xml.bz2
        # unzip the file
        bunzip2 ${i}wiki-latest-pages-articles.xml.bz2
    fi
 
    # cleanup the xml file to text file
    ../wikicat2.sh ${i}wiki-latest-pages-articles.xml &gt; ${i}.txt
 
    # create cngram ngp file
    java -jar ../cngram.jar -create ${i} ${i}.txt "UTF-8"
    mv ${i}.ngp ngp/${i}.ngp
done

On lance le script ainsi en se positionnant dans le répertoire de travail :

./buildngp.sh

Les données sont téléchargées dans le sous-répertoire data et les fichiers ngp créés dans data/ngp. Dans le script, la ligne suivante permet de définir la liste des langues pour lesquels ont va générer un fichier NPG.

for i in fr fa ar be mk sr uk; do

Téléchargements

Télécharger le projet Eclipse CharsetLanguages

Télécharger les scripts de génération de fichier NGP pour NgramJ BuildNGP


Tags: , ,

Laissez un commentaire

Navigation

Categories
  •  Technique (35)
  •  Lucene / Solr (21)
  •  Moteur de recherche (19)
  •  Sites à découvrir (12)
  •  Web 2.0 (12)
  •  Productivité (10)
  •  Debian (6)
  •  Flux RSS (6)
  •  Web (6)
  •  Hébergement (3)
  •  Non classé (3)
  •  Wordpress (3)
  •  Freelance (2)
  •  Référencement (2)
  •  vmware (2)
  •  Mac OS (1)
  •  Réseaux (1)
  • Tags
  •  Solr (14)
  •  Lucene / Solr (11)
  •  RSS (5)
  •  Wordpress (5)
  •  java (5)
  •  tomcat (4)
  •  vmware (4)
  •  Crawler (3)
  •  Debian (3)
  •  OPML (3)
  •  Crossfeeds (2)
  •  PHP (2)
  •  Plugins (2)
  •  apt (2)
  •  mercurial (2)
  •  moteur de recherche (2)
  •  mysql (2)
  •  Agrégateur (1)
  •  Bitbucket (1)
  •  CAS (1)
  •  Debugbar (1)
  •  ESXi (1)
  •  Emilie Ogez (1)
  •  Fast (1)
  •  Firebug (1)
  •  Firefox (1)
  •  Flux RSS (1)
  •  Freelance (1)
  •  Google Chrome (1)
  •  Huridocs (1)
  •  Hurisearch (1)
  •  IE (1)
  •  IETester (1)
  •  Migration Day 2008 (1)
  •  Ogez (1)
  •  Poll (1)
  •  Référencement (1)
  •  SEO (1)
  •  Savoirs en réseau (1)
  •  Sondage (1)
  •  SurveyGizmo (1)
  •  WP-Poll (1)
  •  aiderss (1)
  •  ant (1)
  •  apache (1)
  •  aptitude (1)
  •  backup (1)
  •  bande passante (1)
  •  base de registre (1)
  •  catégorie (1)
  •  configuration (1)
  •  curl (1)
  •  dell (1)
  •  detection langue (1)
  •  dojo (1)
  •  débit (1)
  •  etch (1)
  •  extjs (1)
  •  framwork (1)
  •  gateway (1)
  •  git (1)
  •  google (1)
  •  hg (1)
  •  hosted (1)
  •  hosting (1)
  •  iis (1)
  •  indeed (1)
  •  javascript (1)
  •  jdk (1)
  •  jquery (1)
  •  log (1)
  •  magpie (1)
  •  mg4j (1)
  •  mod_auth_cas (1)
  •  mod_cas (1)
  •  mootools (1)
  •  morphologique (1)
  •  n-gram (1)
  •  ngp (1)
  •  ngram (1)
  •  ngramj (1)
  •  nightly build (1)
  •  nuage de tags (1)
  •  openSSL (1)
  •  openSUSE (1)
  •  optimisation (1)
  •  pagerank (1)
  •  patch (1)
  •  performances (1)
  •  perl (1)
  •  phpCAS (1)
  •  ping (1)
  •  pipeline (1)
  •  podcast (1)
  •  presse-papier (1)
  •  prototype (1)
  •  saas (1)
  •  scriptaculous (1)
  •  serveur (1)
  •  shrink (1)