Recherche efficace en Chinois avec Elasticsearch

Si vous jouez avec Elasticsearch, vous le savez déjà (à moins que vous ayez raté mon article sur le site de l’Afsy l’année dernière), l’analyse et la tokenisation sont les étapes les plus importantes de l’indexation et sans elles, votre recherche sera inefficace et imprécise.

Rien qu’avec un texte en français, une mauvaise racinisation (stemming en anglais) vous fera perdre beaucoup de précision, l’élision est fortement recommandée et se passer d’asciifolding serait une erreur, sans parler de la pertinence : les paramètres par défaut ne conviennent pas (ils sont fait pour des textes anglais).

En Chinois, tout est plus complexe, et on ne parlera ici que du Mandarin qui est la langue officielle de la Chine et qui, accessoirement est la plus parlée au monde, car il existe une multitude de dialectes

Le défis de la recherche en chinois

L’écriture du Chinois se fait via des sinogrammes représentant des mots ou des concepts, qui assemblés, peuvent former d’autres mots. La difficulté est que rien n’est séparé par des espaces et il est donc très difficile de savoir où un mot s’arrête et où il commence.

Par exemple, le mot volcan (火山) est en fait la combinaison de :

  • 火 : le feu ;
  • 山 : la montagne.

Notre tokenisation doit être suffisamment maline pour ne pas séparer 火 et 山, car collé, ils ont une toute autre signification que séparé.

De plus, il existe trois grandes variantes d’écriture :

  • chinois dit simplifié : 书法 ;
  • chinois traditionnel, plus complexe et comportant plus de caractères : 書法 ;
  • et le pinyin, une forme romanisée du mandarin : shū fǎ.

Analyser du chinois efficacement

Il existe – pour Elasticsearch – différentes solutions :

  • l’analyzer par défaut chinese, qui repose sur des classes dépréciées dans Lucene 4, version actuellement utilisée ;
  • le plugin paoding, qui n’est plus maintenu mais qui repose sur des dictionnaires très complets ;
  • l’analyzer cjk, qui fait des bi-gram de vos contenus ;
  • l’analyer smart chinese distribué sous forme de plugin ;
  • et enfin le plugin ICU et son tokenizer.

Ces analyzers sont tous différents et il est donc intéressant de comparer leurs comportements face à un mot composé.

Nous allons prendre le texte « 手机 » comme exemple, il signifie « téléphone cellulaire ». C’est un mot très simple composé de seulement deux sinogrammes mais si nous le décomposons nous obtenons deux autres mots à la signification différente : « 手 » veut dire main et « 机 » veut dire machine.

Machine compose de nombreux autres mots :

  • 机票 : ticket d’avion ;
  • 机器人 : robot ;
  • 机枪 : mitrailleuse ;
  • 机遇 : une opportunité…

Notre tokenisation ne doit donc pas séparer ces deux signes, car si je recherche « téléphone cellulaire » je ne souhaite pas obtenir mes documents parlant de Rambo et sa mitrailleuse lourde.

Rambo

Nous allons tester ce texte avec toutes les solutions connues grâce à l’API _analyze :

curl -XGET 'http://localhost:9200/chinese_test/_analyze?analyzer=paoding_analyzer1' -d '手机'

J’en profite pour vous rappeler notre cheat sheet Elasticsearch, avec les end-points d’API bien utiles tel que ce dernier.

L’analyzer chinese par défaut

Installé par défaut avec votre instance d’Elasticsearch, il utilise la classe ChineseTokenizer de Lucene, qui ne fait que séparer tous les signes. Nous obtenons donc les tokens « 手 » et « 机 ».

L’analyzer standard d’Elasticsearch produit exactement le même résultat, pour cette raison, chinese va être remplacé par ce dernier avec l’arrivée de Lucene 5. À éviter donc.

Le plugin paoding

Paoding est reconnu dans l’industrie pour être une solution très élégante, malheureusement le plugin Elasticsearch n’est pas maintenu et je n’ai pu le faire fonctionner qu’avec la version 1.0.1 de l’index, après quelques modifications. L’installation doit se faire à la main avec maven :

git clone git@github.com:damienalexandre/elasticsearch-analysis-paoding.git /tmp/elasticsearch-analysis-paoding
cd /tmp/elasticsearch-analysis-paoding
mvn clean package
sudo /usr/share/elasticsearch/bin/plugin -url file:/tmp/elasticsearch-analysis-paoding/target/releases/elasticsearch-analysis-paoding-1.2.2.zip -install elasticsearch-analysis-paoding

# Copy all the dic config files to the ES config path - make sure to set the permissions rights, ES needs to write in /etc/elasticsearch/config/paoding!
sudo cp -r config/paoding /etc/elasticsearch/config/

Malgré cette installation qui ne plaira pas à votre infogéreur (surtout qu’il faut la faire sur tous les nodes !), le plugin expose un nouveau tokenizer paoding qui fonctionne avec deux collecteurs : max_word_len et most_word.

Pour l’utiliser, il faut déclarer un analyzer car le tokenizer n’est pas exposé globalement.

PUT /chinese_test
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "analysis": {
      "tokenizer": {
        "paoding1": {
          "type": "paoding",
          "collector": "most_word"
        },
        "paoding2": {
          "type": "paoding",
          "collector": "max_word_len"
        }
      },
      "analyzer": {
        "paoding_analyzer1": {
          "type": "custom",
          "tokenizer": "paoding1",
          "filter": ["standard"]
        },
        "paoding_analyzer2": {
          "type": "custom",
          "tokenizer": "paoding2",
          "filter": ["standard"]
        }
      }
    }
  }
}

Dans les deux configurations, l’analyse est bonne, nous obtenons un unique token « 手机 », et le comportement est identique au sein d’une phrase : la signification est conservée.

L’analyzer cjk

Son fonctionnement est plutôt simple : il transforme des textes en bi-gram. « Batman » devient donc : Ba, at, tm, ma, an. La collecte dans les langues asiatiques est souvent bien meilleure au prix d’un index plus gros et d’une pertinence aléatoire.

Dans notre cas, seul le token « 手机 » est indexé, ce qui semble être juste, mais si nous prenons le mot plus long « 元宵节 » (Fête des lanternes), deux tokens sont générés : « 元宵 » et « 宵节 » qui correspondent respectivement à yuánxiāo et festival de Xiao.

Le plugin SmartCn

Distribué sous forme de plugin officiellement maintenu par Elasticsearch, il s’installe comme tout plugin :

bin/plugin -install elasticsearch/elasticsearch-analysis-smartcn/2.3.0

Il expose un nouvel analyzer smartcn ainsi qu’un tokenizer smartcn_tokenizer, s’interfaçant avec le SmartChineseAnalyzer de Lucene.

Son fonctionnement est basé sur les probabilités pour trouver une séparation optimale des mots, la segmentation utilise le modèle Hidden Markov et un très grand nombre de textes a été utilisé pour entraîner le tokenizer. Un dictionnaire est donc embarqué, et il est performant sur de nombreux textes courants.

Le plugin ICU

On termine ce test avec ICU, un plugin Elasticsearch qui intègre les librairies « International Components for Unicode » au sein du moteur de recherche. Ce plugin est aussi maintenu par Elasticsearch et est recommandé dans le guide officiel pour travailler avec toutes les langues autre que l’anglais (je l’utilise pour le français aussi).

Il expose un tokenizer icu_tokenizer qui nous intéresse ici mais aussi de nombreux outils de manipulation comme le icu_normalizer, icu_folding, icu_collation

La séparation en mot se fait via un dictionnaire (unique pour le japonais et le chinois) qui contient des informations sur la fréquence des mots, pour en déduire les groupes de sinogrammes les plus courants au sein de notre contenu.

Sur « 手机 », aucun problème, mais sur « 元宵节 », deux tokens sont générés, « 元宵 » et « 节 » – il se trouve que « lanterne » et « festival » sont plus courants que « Fête des lanternes ».

Comparaison des résultats

Analyzer 手机 (téléphone cellulaire) 元宵节 (Fête des lanternes) 元宵節 (Fête des lanternes version traditionnel)
chinese [手] [机] [元] [宵] [节] [元] [宵] [節]
paoding most_word [手机] [元宵] [元宵节] [元宵] [節]
paoding max_word_len [手机] [元宵节] [元宵] [節]
cjk [手机] [元宵] [宵节] [元宵] [宵節]
smartcn [手机] [元宵节] [元宵] [節]
icu_tokenizer [手机] [元宵] [节] [元宵節]

Ces tests ont été effectué sous ES 1.3.2 à l’exception des tests Paoding sous ES 1.0.1.

Les résultats sur paoding et smartcn sont les meilleurs selon moi. L’analyzer chinese détruit toute pertinence et icu_tokenizer est un peu décevant sur « 元宵节 » mais reste tout à fait honnête surtout pour le chinois traditionnel.

Supporter le chinois traditionnel

Comme précisé dans l’introduction, si vous avez des documents en chinois traditionnel et en chinois simplifié, il vous faudra passer par une étape de normalisation, dans un sens ou dans l’autre. En effet smartcn, par exemple, ne sait pas manipuler du chinois traditionnel, tout comme paoding.

La première solution serait de traduire les tokens traditionnels en leur version simplifiée. Vous pouvez le faire avec le plugin elasticsearch-analysis-stconvert mais il n’est pas maintenu et il faudra le compiler manuellement pour l’installer (comme pour paoding).

C’est là que faire une analyse cjk s’avère intéressant : quelle que soit la version, nous avons de bonnes chances de remonter des documents, couplé à icu_tokenizer pour améliorer la pertinence.

Aller plus loin avec le chinois ?

Il n’existe pas d’analyse parfaite à appliquer partout, comme avec le Français, c’est à vous de composer avec les informations que vous avez. Il serait par exemple intéressant d’analyser les textes chinois avec cjk pour améliorer la collecte et avec smartcn pour améliorer la pertinence.

Si le Chinois vous intéresse plus que la simple recherche, je vous recommande chaudement Chineasy dont quelques exemples de l’article proviennent, chaque sinogramme y est représenté de façon simple et didactique. Et si vous voulez apprendre le chinois, lisez cet article avant快乐编码

Concernant Elasticsearch, sachez que nous pouvons vous aider aussi bien sur vos problématiques d’implémentation que de formation ;-)

blog comments powered by Disqus