Utiliser NLTK sur Heroku avec Python

Sur le principe du billet « Extraire le texte de PDF avec Python« , je vais créer un service qui utilise le package NLTK. NLTK est un ensemble d’outils pour construire en Python des programmes de traitement des langues. Il nécessite donc d’utiliser Python.

Phases de base

Je résume les étapes détaillées dans le billet mentionné ci-dessus:

  • créer un dossier pour ce service
  • créer un environnement virtuel pour le développement local avec la commande
  • créer un fichier requirements.txt avec la liste des dépendances (dont nltk, voir plus bas)
  • créer un dossier nltk_service
  • dans ce dossier, créer deux fichiers: __init__.py and resources.py (vides pour l'instant)
  • démarrer un serveur local (en remplaçant pdf_service par nltk_service dans runserver.py)
  • créer un dépot git
  • ajouter les fichiers du projet au dépot git local
  • mettre à jour le dépot git local
  • se connecter à Heroku
  • créer un nouveau service sur Heroku
  • pousser le développement local vers heroku
  • lancer une instance du service
  • tester en ligne

Là, on a créé un service vide. Nous allons le compléter avec une mise en oeuvre d’une des possibilités offertes par NLTK.

Un service avec NLTK

Je veux créer un service qui trouve les mots et autres éléments qui composent un texte, puis élimine les mots creux (stopwords) -ces mots qui ne contribuent pas fortement à certaines analyses du contenu d’un texte, comme ‘le’, ‘la’, ‘les’, ‘à’ en français- et enfin, compte le nombre de mots du texte et le nombre d’occurrences de chaque mot conservé.

Pour éliminer les stopwords -ainsi que pour de nombreux autres traitements- NLTK utilise des fichiers de données définis sous la dénomination ‘NLTK Data‘. Des fichiers listant  les mots creux d’un ensemble de langues sont disponibles. Mais Heroku n’est pas très adapté à l’utilisation de gros volumes de fichiers statiques; Heroku est fait pour héberger et exécuter des programmes.

Hébergement de fichiers statiques pour Heroku

Dans l’article « Using AWS S3 to Store Static Assets and File Uploads« , il est suggéré qu’une bonne scalabilité peut être obtenue en déployant sur Amazon S3 les fichiers statiques utilisés par une application. En effet, les fichiers hébergés sur Heroku peuvent être déchargés à chaque mise en veille d’une application, puis rechargés pour une nouvelle utilisation. S’il s’agit d’un gros volume de fichiers statiques, cela peut pénaliser les temps de réponse d’une application. Le système de fichier proposé par Heroku est dit ‘éphémère’.

Normalement, les données associées à NLTK sont téléchargées avec un programme interactif qui permet de sélectionner ce qu’on va télécharger et s’assure que les données chargées seront trouvées par les outils NLTK. Dans le cas d’Heroku, nous ne pouvons pas procéder ainsi, mais plutôt chercher à utiliser un chargement en ligne de commande.

C’est pourtant la première méthode que j’ai exploré. La première idée est de charger les données en local, puis de les pousser sur Heroku, mais cela chargerait le dépot GIT qui nous sert dans nos échanges avec Heroku avec toutes les données statiques de nltk-data. Une solution est proposée ici: http://stackoverflow.com/questions/13965823/resource-corpora-wordnet-not-found-on-heroku/37558445#37558445. C’est la solution que j’ai retenu en première approche. Un essai avec toutes les données de nltk_data échoue (all). Avec juste les corpus stopwords (python -m nltk.downloader stopwords) et wordnet (python -m nltk.downloader wordnet) et le tokenizer punkt (python -m nltk.downloader punkt), le déploiement se déroule correctement.

Une autre idée est d’utiliser AWS Simple Storage Service, service de stockage sur le Cloud d’Amazon. J’explorerai cette possibilité dans un prochain billet.

Le service

from flask import request, abort
from flask.ext import restful
from flask.ext.restful import reqparse
from nk_service import app
import urllib2
import nltk.data
import io
import os
import collections
import json
from nltk.tokenize import word_tokenize
from nltk.corpus import wordnet
from nltk.corpus import stopwords

english_stops = set(stopwords.words('english'))
french_stops = set(stopwords.words('french'))
stops = english_stops.union(french_stops)

def words_counting(wordslist):
    wordscounter = {}
    wordscounter["cnt"] = collections.Counter()
    wordscounter["wordscount"] = 0
    for w in wordslist:
        word = w.lower()
        if word not in stops:
            syn = wordnet.synsets(word)
            if syn:
                lemmas = syn[0].lemmas()
                res = lemmas[0].name()
                wordscounter["cnt"][res] += 1
                wordscounter["wordscount"] += 1
    wordscounter["cnt"] = wordscounter["cnt"].most_common()
    return wordscounter

def filewords(path):
    text = urllib2.urlopen(path).read().decode('utf-8')
    wordslist = word_tokenize(text)
    jsonwords = words_counting(wordslist)
    return json.dumps(jsonwords)

@app.route('/words/<path:url>', methods=['GET'])
def get_words(url):
    return filewords(url)

L’appel de <mondomaine>/words/<url d’un fichier texte> renvoie une structure json avec un tableau des mots associés à leur nombre d’occurrence (dans le tableau cnt) et le nombre total de mots pris en compte (dans wordscount »). ceci permettra d’agréger facilement les résultats de plusieurs fichiers et de calculer des fréquences d’occurrences de mots.

Conclusion

L’utilisation de NLTK avec Heroku est validée. Il faut maintenant définir la ou les structures (a priori json) qui vont nous permettre de transporter et d’enrichir progressivement les données associées à une source ou à un ensemble de sources.

A propos Moissinac

Maitre de conférence à Télécom Paris, Département Image, Données, Signal - Groupe Multimédia Jean-Claude Moissinac a mené des recherches sur les techniques avancées pour la production, le transport, la représentation et l’utilisation des documents multimédia. Ces travaux d'abord ont évolué vers la représentation sémantique de données liées au multimédia (process de traitement de médias, description d'adaptations de média, description formelle d'interactions utilisateurs). Aujourd'hui, les travaux portent sur la constitution de graphes de connaissances. Principaux axes de recherche actuel : représentations sémantiques de connaissances, constitution de graphes de connaissances, techniques d'apprentissage automatique sur ces graphes
Ce contenu a été publié dans Cuisine traitement de textes, SemBib. Vous pouvez le mettre en favoris avec ce permalien.