Nltk: multiprocesamiento y nltk no funcionan bien juntos

Creado en 16 abr. 2015  ·  22Comentarios  ·  Fuente: nltk/nltk

Sinceramente, este problema no es tan grave como curioso. Descubrí que cuando se importa NLTK, hará que cualquier subproceso de Python termine prematuramente en una llamada de red. Código de ejemplo:

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)

Ejecútelo con NLTK importado y verá que la llamada urlopen () nunca se ejecuta. Comente la línea import nltk y se ejecuta bien.

¿Por qué?

* editar: esto es para Python 2. No lo he probado en 3 todavía.

admin bug inactive multithread / multiprocessing pythonic

Comentario más útil

No estoy muy familiarizado con nltk, pero investigué un poco a ciegas para ver qué causaba que la prueba pasara o fallara. Esto es lo que tuve que hacer con el paquete __init__.py para que la prueba pasara:


Detalles (haga clic para expandir)

###########################################################
# 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


Curiosamente, todas las importaciones desactivadas finalmente conducen a la importación de tkinter , que creo que es la causa principal. Si reemplazo import nltk con import tkinter en el script de prueba, obtengo un informe de fallos muy similar, ambos haciendo referencia a tkinter.

Por lo que puedo decir, estos paquetes importan directamente tkinter :

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

De los cambios anteriores al paquete principal __init__ , estas son las importaciones problemáticas y cómo se remontan a la importación 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 )

Todos 22 comentarios

¿Obtienes alguna excepción?

No. Puse una cláusula try .. except: alrededor del import urllib2; print... pero no obtuve nada de eso.

Me estoy encontrando exactamente con el mismo problema. Acabo de abrir una pregunta SO que puede ser útil para estar vinculada aquí: http://stackoverflow.com/questions/30766419/python-child-process-silently-crashes-when-issuing-an-http-request

De hecho, el proceso hijo se bloquea silenciosamente sin previo aviso.

No estoy de acuerdo contigo @ oxymor0n , esto me parece un tema bastante serio. Básicamente, esto significa que siempre que se importa nltk, no hay forma de emitir una solicitud desde un proceso secundario, lo que puede ser realmente molesto cuando se trabaja con API.

The child process is indeed crashing silently without further notice.

También estamos experimentando este problema con la combinación de: nltk, gunicorn (con nltk cargado mediante prefork) y flask.

Elimina la importación de nltk y todo funciona. Excepto nltk.

/ cc @escherba

@ninowalker , @ oxymor0n Es extraño, mis procesos funcionan bien con el código, obtengo:

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

Esa es la salida esperada, ¿verdad?

No rompe mi solicitud con esto también:

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]>

Estoy usando:

  • python 2.7.6
  • nltk 3.0.3
  • ubuntu 14.04

Me encontré con el mismo problema que tuvo @Hiestaa . Tengo un archivo auxiliar string_util.python que importa nltk, pero no se usa en el archivo principal de Python que usa el módulo de multiprocesamiento para iniciar un rastreador multiproceso. El síntoma es que el proceso hijo simplemente se atasca y no hay ningún mensaje de error (ni siquiera un mensaje de excepción).
Después de comentar las funciones e importaciones relacionadas con nltk, el problema se resolvió.

Detalles:
SO: Yosemite 10.10.5
Python: 2.7.10
Recuperar el contenido de la página: utilicé urllib2 inicialmente, luego cambié a solicitudes más tarde.

Este es un error muy grave y espero que alguien pueda intervenir y solucionarlo. ¡Gracias!

Creo que este es un problema grave si estás haciendo PNL a nivel de producción. Estamos usando trabajadores de Rq (http://python-rq.org/), para ejecutar múltiples canalizaciones de NLP, que se eliminan silenciosamente cuando se realizan llamadas de red. Espero que haya una solución pronto. ¡Gracias!

@sasinda : le gustaría hacer una llamada en la lista de correo nltk-dev para ver si puede llamar la atención sobre este problema.

@sasinda No estoy seguro de cómo funciona exactamente Rq, pero en mi proyecto de PNL de nivel de producción logré solucionar este problema iniciando cada proceso en un intérprete de Python separado y aislado, usando un script de shell para generarlos en el inicio. En este caso, Python nunca tiene que bifurcarse y el bloqueo silencioso de nltk nunca ocurre. Quizás esto pueda ayudar mientras tanto.

Descubrí que realizar la importación a nivel de función evita el problema.

En otras palabras, esto funciona:

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

y esto no:

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

Gracias @mpenkov. ¿Resuelve esto el problema?

@stevenbird No lo creo. Es una solución alternativa, pero no es una solución.

En mi humilde opinión, si la importación de una biblioteca de terceros rompe un componente de la biblioteca estándar de Python, algo profano está sucediendo en alguna parte y debe arreglarse.

@mpenkov No estoy del todo seguro de por qué funciona esto, pero aquí hay otra solución que encontré que funciona. La construcción de un abridor en el proceso principal parece solucionarlo. Modificando el código 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 ¿Aún enfrentan el mismo problema?

No pude replicar el problema en mi máquina:

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)

me da:

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

Estoy en:

  • Python 2.7.12
  • NLTK 3.2.5
  • Ubuntu 16.04.3

@alvations Ha pasado mucho tiempo desde que encontré este problema.
Incluso olvidé qué base de proyecto estaba teniendo este problema, por lo que no podría decirle si todavía tengo el problema o no.
¡Lo siento!

@alvations Yo tampoco recuerdo cuál de mis proyectos sufrió estos problemas específicos.

Ejecuté su código en mi máquina y no pude replicar el problema.

Python 2.7.12
nltk 3.2.1
macOS 10.12.6

@alvations Yo tampoco estoy trabajando más en ese proyecto. Pero usó una de esas soluciones.
Probé su código, pero aún así el proceso secundario se cierra con un error de segmento (código de salida 11) para mí (sale en la línea: urllib2.urlopen ("https://www.google.com") .read () [: 100])

Sin embargo, funcionó con 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. | (predeterminado, 20 de diciembre de 2016, 23:05:08)
    [GCC 4.2.1 Compatible con Apple LLVM 6.0 (clang-600.0.57)] en darwin

Por lo que puedo decir, este problema parece afectar a macOS. Usando Python 3.6 hasta ahora,

  • macOS 10.13 (falla)
  • Centos 7.2 (tiene éxito)
  • Ubuntu 16.04 (tiene éxito)

Script de OP modificado para 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)

Salida:

Fetch URL
Child process returned

El subproceso se cierra inesperadamente y recibe un resultado similar al que se ve en esta publicación de Stack Overflow .

Creo que esto es bastante alucinante. Puede que tenga algo que ver con el manejo de subprocesos en MacOS.

No estoy muy familiarizado con nltk, pero investigué un poco a ciegas para ver qué causaba que la prueba pasara o fallara. Esto es lo que tuve que hacer con el paquete __init__.py para que la prueba pasara:


Detalles (haga clic para expandir)

###########################################################
# 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


Curiosamente, todas las importaciones desactivadas finalmente conducen a la importación de tkinter , que creo que es la causa principal. Si reemplazo import nltk con import tkinter en el script de prueba, obtengo un informe de fallos muy similar, ambos haciendo referencia a tkinter.

Por lo que puedo decir, estos paquetes importan directamente tkinter :

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

De los cambios anteriores al paquete principal __init__ , estas son las importaciones problemáticas y cómo se remontan a la importación 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 )

Gracias @rpkilby , ¡eso es muy útil!

Parece que este problema https://stackoverflow.com/questions/16745507/tkinter-how-to-use-threads-to-preventing-main-event-loop-from-freezing

Creo que tinkter ha sido un punto de dolor para nosotros durante bastante tiempo. Quizás, sea bueno si podemos encontrar una alternativa.

Estoy de acuerdo. Una solución a corto plazo sería enterrar las importaciones de tkinter dentro de las clases y métodos que necesitan tkinter, y evitar importarlo por programas que no lo necesiten. Ya hicimos algo similar para numpy.

¿Fue útil esta página
0 / 5 - 0 calificaciones