Nltk: le multitraitement et nltk ne fonctionnent pas bien ensemble

Créé le 16 avr. 2015  ·  22Commentaires  ·  Source: nltk/nltk

Honnêtement, ce problème n'est pas aussi grave que curieux. J'ai découvert que lorsque NLTK est importé, tout sous-processus Python se terminera prématurément sur un appel réseau. Exemple de code:

from multiprocessing import Process
import nltk
import time


def child_fn():
    print "Fetch URL"
    import urllib2
    print urllib2.urlopen("https://www.google.com").read()[:100]
    print "Done"


while True:
    child_process = Process(target=child_fn)
    child_process.start()
    child_process.join()
    print "Child process returned"
    time.sleep(1)

Exécutez-le avec NLTK importé, et vous verrez que l'appel urlopen () n'est jamais exécuté. Commentez la ligne import nltk , et elle s'exécute correctement.

Pourquoi?

* edit: c'est pour Python 2. Je ne l'ai pas encore testé sur 3.

admin bug inactive multithread / multiprocessing pythonic

Commentaire le plus utile

Je ne suis pas très familier avec nltk, mais j'ai fouillé un peu à l'aveugle pour voir ce qui a fait passer le test / échouer. Voici ce que j'ai dû faire avec le package __init__.py pour que le test réussisse:


Détails (cliquez pour agrandir)

###########################################################
# TOP-LEVEL MODULES
###########################################################

# Import top-level functionality into top-level namespace

from nltk.collocations import *
from nltk.decorators import decorator, memoize
# from nltk.featstruct import *
# from nltk.grammar import *
from nltk.probability import *
from nltk.text import *
# from nltk.tree import *
from nltk.util import *
from nltk.jsontags import *

# ###########################################################
# # PACKAGES
# ###########################################################

# from nltk.chunk import *
# from nltk.classify import *
# from nltk.inference import *
from nltk.metrics import *
# from nltk.parse import *
# from nltk.tag import *
from nltk.tokenize import *
from nltk.translate import *
# from nltk.sem import *
# from nltk.stem import *

# Packages which can be lazily imported
# (a) we don't import *
# (b) they're slow to import or have run-time dependencies
#     that can safely fail at run time

from nltk import lazyimport
app = lazyimport.LazyModule('nltk.app', locals(), globals())
chat = lazyimport.LazyModule('nltk.chat', locals(), globals())
corpus = lazyimport.LazyModule('nltk.corpus', locals(), globals())
draw = lazyimport.LazyModule('nltk.draw', locals(), globals())
toolbox = lazyimport.LazyModule('nltk.toolbox', locals(), globals())

# Optional loading

try:
    import numpy
except ImportError:
    pass
else:
    from nltk import cluster

# from nltk.downloader import download, download_shell
# try:
#     from six.moves import tkinter
# except ImportError:
#     pass
# else:
#     try:
#         from nltk.downloader import download_gui
#     except RuntimeError as e:
#         import warnings
#         warnings.warn("Corpus downloader GUI not loaded "
#                       "(RuntimeError during import: %s)" % str(e))

# explicitly import all top-level modules (ensuring
# they override the same names inadvertently imported
# from a subpackage)

# from nltk import ccg, chunk, classify, collocations
# from nltk import data, featstruct, grammar, help, inference, metrics
# from nltk import misc, parse, probability, sem, stem, wsd
# from nltk import tag, tbl, text, tokenize, translate, tree, treetransforms, util


Fait intéressant, toutes les importations désactivées conduisent finalement à l'importation de tkinter , ce qui, je pense, en est la cause première. Si je remplace import nltk par import tkinter dans le script de test, j'obtiens un rapport d'erreur très similaire, tous deux faisant référence à tkinter.

D'après ce que je peux dire, ces packages importent directement tkinter :

  • nltk.app
  • nltk.draw
  • nltk.sem

À partir des modifications ci-dessus apportées au package principal __init__ , voici les importations problématiques, et comment elles remontent à l'importation de tkinter

  • nltk.featstruct ( sem )
  • nltk.grammar ( featstruct )
  • nltk.tree ( grammar )
  • nltk.chunk ( chunk.named_entity > tree )
  • nltk.parse ( parse.bllip > tree )
  • nltk.tag ( tag.stanford > parse )
  • nltk.classify ( classify.senna > tag )
  • nltk.inference ( inference.discourse > sem , tag )
  • nltk.stem ( stem.snowball > corpus > corpus.reader.timit > tree )

Tous les 22 commentaires

Avez-vous des exceptions?

non. J'ai mis une clause try .. except: autour du import urllib2; print... mais je n'en ai rien tiré.

Je rencontre exactement le même problème. Je viens d'ouvrir une question SO qui peut être utile pour être liée ici: http://stackoverflow.com/questions/30766419/python-child-process-silently-crashes-when-issuing-an-http-request

Le processus enfant plante en effet silencieusement sans autre préavis.

Je ne suis pas d'accord avec vous @ oxymor0n , cela

The child process is indeed crashing silently without further notice.

Nous rencontrons également ce problème avec la combinaison de: nltk, gunicorn (avec nltk chargé via prefork) et flask.

Supprimez l'importation nltk et tout fonctionne. Sauf nltk.

/ cc @escherba

@ninowalker , @ oxymor0n C'est étrange, mes processus fonctionnent bien avec le code, j'obtiens:

Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="de"><head><meta content
Done
Child process returned

C'est le résultat attendu, non?

Cela ne rompt pas ma demande avec ceci aussi:

alvas<strong i="13">@ubi</strong>:~$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from multiprocessing import Process
>>> import requests
>>> from pprint import pprint
>>> Process(target=lambda: pprint(
...         requests.get('https://api.github.com'))).start()
>>> <Response [200]>

>>> import nltk
>>> Process(target=lambda: pprint(
...         requests.get('https://api.github.com'))).start()
>>> <Response [200]>

J'utilise:

  • python 2.7.6
  • nltk 3.0.3
  • ubuntu 14.04

J'ai rencontré le même problème que @Hiestaa . J'ai un fichier d'aide string_util.python qui importe nltk, mais il n'est pas utilisé dans le fichier python principal qui utilise le module multitraitement pour démarrer un robot d'exploration multi-processus. Le symptôme est que le processus enfant est bloqué et il n'y a pas de message d'erreur (pas même de message d'exception).
Après avoir commenté les importations et les fonctions liées à nltk, le problème a été résolu.

Détails:
Système d'exploitation: Yosemite 10.10.5
Python: 2.7.10
Récupérer le contenu de la page: j'ai utilisé urllib2 au départ, puis je suis passé aux requêtes plus tard.

C'est un bug très sérieux, et j'espère que quelqu'un pourra intervenir et le corriger. Merci!

Je pense que c'est un problème sérieux si vous faites de la PNL de niveau de production. Nous utilisons des workers Rq (http://python-rq.org/), pour exécuter plusieurs pipelines NLP, qui sont tués silencieusement lors d'appels réseau. J'espère qu'il y aura bientôt un correctif. Merci!

@sasinda : Vous voudrez peut-être lancer un appel sur la liste de diffusion nltk-dev pour voir si vous pouvez attirer l'attention sur ce problème.

@sasinda Je ne sais pas comment Rq fonctionne exactement mais dans mon projet PNL de niveau production, j'ai réussi à contourner ce problème en démarrant chaque processus dans un interpréteur python séparé et isolé, en utilisant un script shell pour les générer au démarrage. Dans ce cas, python n'a jamais besoin de fork et le crash silencieux de nltk ne se produit jamais. Peut-être que cela peut aider en attendant.

J'ai constaté que l'exécution de l'importation au niveau de la fonction évite le problème.

En d'autres termes, cela fonctionne:

def split(words):
    import nltk
    return nltk.word_tokenize(words)

et ce n'est pas:

import nltk
def split(words):
    return nltk.word_tokenize(words)

Merci @mpenkov. Cela résout-il le problème?

@stevenbird, je ne pense pas. C'est une solution de contournement, mais ce n'est pas une solution.

IMHO, si l'importation d'une bibliothèque tierce casse un composant de bibliothèque standard Python, quelque chose d'impie se produit quelque part et doit être corrigé.

@mpenkov Je ne suis pas tout à fait sûr de savoir pourquoi cela fonctionne, mais voici une autre solution de contournement que j'ai trouvée. Construire un ouvreur dans le processus parent semble résoudre ce problème. Modification du code original de @ oxymor0n :

from multiprocessing import Process
import nltk
import time
import urllib2

# HACK
urllib2.build_opener(urllib2.HTTPHandler())

def child_fn():
    print "Fetch URL"
    import urllib2
    print urllib2.urlopen("https://www.google.com").read()[:100]
    print "Done"


while True:
    child_process = Process(target=child_fn)
    child_process.start()
    child_process.join()
    print "Child process returned"
    time.sleep(1)

@mpenkov @ninowalker , @ oxymor0n @sasinda @wenbowang Êtes-vous tous confrontés au même problème?

Je n'ai pas pu reproduire le problème sur ma machine:

from multiprocessing import Process
import nltk
import time

def child_fn():
    print "Fetch URL"
    import urllib2
    print urllib2.urlopen("https://www.google.com").read()[:100]
    print "Done"


while True:
    child_process = Process(target=child_fn)
    child_process.start()
    child_process.join()
    print "Child process returned"
    time.sleep(1)

Donne moi:

Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-SG"><head><meta cont
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-SG"><head><meta cont
Done
Child process returned
Fetch URL
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-SG"><head><meta cont
Done

Je suis dessus:

  • Python 2.7.12
  • NLTK 3.2.5
  • Ubuntu 16.04.3

@alvations Cela fait longtemps que je n'ai pas trouvé ce problème.
J'ai même oublié quelle base de projet avait ce problème, donc je ne pourrais pas vous dire si j'ai toujours le problème ou non.
Pardon!

@alvations Je ne me souviens pas non plus lequel de mes projets a souffert de ces problèmes spécifiques.

J'ai exécuté votre code sur ma machine et je n'ai pas pu reproduire le problème.

Python 2.7.12
nltk 3.2.1
macOS 10.12.6

@alvations Moi aussi, je ne travaille plus sur ce projet. Mais utilisé l'une de ces solutions de contournement.
J'ai essayé votre code mais le processus enfant se termine toujours avec une erreur de segment (code de sortie 11) pour moi (quitte à la ligne: urllib2.urlopen ("https://www.google.com") .read () [: 100])

Cela fonctionnait avec urllib3 (https://urllib3.readthedocs.io/en/latest/).

  • nltk (3.2.5)
  • urllib3 (1,22)
  • Mac OSX 10.12.16
  • Python 2.7.13 | Continuum Analytics, Inc. | (par défaut, 20 décembre 2016, 23:05:08)
    [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] sur darwin

Pour autant que je sache, ce problème semble affecter macOS. En utilisant Python 3.6 jusqu'à présent,

  • macOS 10.13 (échoue)
  • Centos 7.2 (réussit)
  • Ubuntu 16.04 (réussit)

Script d'OP modifié pour python3:

from multiprocessing import Process
import nltk
import time


def child_fn():
    from urllib.request import urlopen
    print("Fetch URL")
    print(urlopen("https://www.google.com").read()[:100])
    print("Done")


child_process = Process(target=child_fn)
child_process.start()
child_process.join()
print("Child process returned")
time.sleep(1)

Production:

Fetch URL
Child process returned

Le sous-processus se ferme de manière inattendue, recevant une sortie similaire à ce qui est vu dans cette publication Stack Overflow .

Je pense que c'est assez époustouflant. Cela pourrait avoir quelque chose à voir avec la gestion des threads sur MacOS.

Je ne suis pas très familier avec nltk, mais j'ai fouillé un peu à l'aveugle pour voir ce qui a fait passer le test / échouer. Voici ce que j'ai dû faire avec le package __init__.py pour que le test réussisse:


Détails (cliquez pour agrandir)

###########################################################
# TOP-LEVEL MODULES
###########################################################

# Import top-level functionality into top-level namespace

from nltk.collocations import *
from nltk.decorators import decorator, memoize
# from nltk.featstruct import *
# from nltk.grammar import *
from nltk.probability import *
from nltk.text import *
# from nltk.tree import *
from nltk.util import *
from nltk.jsontags import *

# ###########################################################
# # PACKAGES
# ###########################################################

# from nltk.chunk import *
# from nltk.classify import *
# from nltk.inference import *
from nltk.metrics import *
# from nltk.parse import *
# from nltk.tag import *
from nltk.tokenize import *
from nltk.translate import *
# from nltk.sem import *
# from nltk.stem import *

# Packages which can be lazily imported
# (a) we don't import *
# (b) they're slow to import or have run-time dependencies
#     that can safely fail at run time

from nltk import lazyimport
app = lazyimport.LazyModule('nltk.app', locals(), globals())
chat = lazyimport.LazyModule('nltk.chat', locals(), globals())
corpus = lazyimport.LazyModule('nltk.corpus', locals(), globals())
draw = lazyimport.LazyModule('nltk.draw', locals(), globals())
toolbox = lazyimport.LazyModule('nltk.toolbox', locals(), globals())

# Optional loading

try:
    import numpy
except ImportError:
    pass
else:
    from nltk import cluster

# from nltk.downloader import download, download_shell
# try:
#     from six.moves import tkinter
# except ImportError:
#     pass
# else:
#     try:
#         from nltk.downloader import download_gui
#     except RuntimeError as e:
#         import warnings
#         warnings.warn("Corpus downloader GUI not loaded "
#                       "(RuntimeError during import: %s)" % str(e))

# explicitly import all top-level modules (ensuring
# they override the same names inadvertently imported
# from a subpackage)

# from nltk import ccg, chunk, classify, collocations
# from nltk import data, featstruct, grammar, help, inference, metrics
# from nltk import misc, parse, probability, sem, stem, wsd
# from nltk import tag, tbl, text, tokenize, translate, tree, treetransforms, util


Fait intéressant, toutes les importations désactivées conduisent finalement à l'importation de tkinter , ce qui, je pense, en est la cause première. Si je remplace import nltk par import tkinter dans le script de test, j'obtiens un rapport d'erreur très similaire, tous deux faisant référence à tkinter.

D'après ce que je peux dire, ces packages importent directement tkinter :

  • nltk.app
  • nltk.draw
  • nltk.sem

À partir des modifications ci-dessus apportées au package principal __init__ , voici les importations problématiques, et comment elles remontent à l'importation de tkinter

  • nltk.featstruct ( sem )
  • nltk.grammar ( featstruct )
  • nltk.tree ( grammar )
  • nltk.chunk ( chunk.named_entity > tree )
  • nltk.parse ( parse.bllip > tree )
  • nltk.tag ( tag.stanford > parse )
  • nltk.classify ( classify.senna > tag )
  • nltk.inference ( inference.discourse > sem , tag )
  • nltk.stem ( stem.snowball > corpus > corpus.reader.timit > tree )

Merci @rpkilby , c'est très utile!

Cela ressemble à ce problème https://stackoverflow.com/questions/16745507/tkinter-how-to-use-threads-to-preventing-main-event-loop-from-freezing

Je pense que le bricolage est un problème pour nous depuis un certain temps. Peut-être que ce sera bien si nous pouvons trouver une alternative.

Je suis d'accord. Une solution à plus court terme serait d'enterrer les importations tkinter dans les classes et les méthodes qui ont besoin de tkinter, et d'éviter de l'importer par des programmes qui n'en ont pas besoin. Nous avons déjà fait quelque chose de similaire pour numpy.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

mwess picture mwess  ·  5Commentaires

zdog234 picture zdog234  ·  3Commentaires

stevenbird picture stevenbird  ·  3Commentaires

talbaumel picture talbaumel  ·  4Commentaires

DavidNemeskey picture DavidNemeskey  ·  4Commentaires