Nltk: multiprocessamento e nltk não funcionam bem juntos

Criado em 16 abr. 2015  ·  22Comentários  ·  Fonte: nltk/nltk

Honestamente, esse assunto não é tão sério quanto é curioso. Descobri que, quando o NLTK é importado, fará com que qualquer subprocesso Python termine prematuramente em uma chamada de rede. Código de exemplo:

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)

Execute-o com o NLTK importado e você verá que a chamada urlopen () nunca é executada. Comente a linha import nltk e ela será executada corretamente.

Por quê?

* editar: isso é para Python 2. Eu não testei no 3 ainda.

admin bug inactive multithread / multiprocessing pythonic

Comentários muito úteis

Não estou muito familiarizado com o nltk, mas dei uma espiada às cegas para ver o que fez o teste passar / falhar. Aqui está o que eu tive que fazer com o pacote __init__.py para passar no teste:


Detalhes (clique 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 as importações desativadas acabam levando de volta à importação de tkinter , que eu acho que é a causa raiz. Se eu substituir import nltk por import tkinter no script de teste, obtenho um relatório de falha muito semelhante, ambos fazendo referência ao tkinter.

Pelo que eu posso dizer, esses pacotes importam diretamente tkinter :

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

Das mudanças acima para o pacote principal __init__ , essas são as importações problemáticas e como elas remontam à importação do 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 comentários

Você consegue alguma exceção?

não. coloquei uma cláusula try .. except: torno de import urllib2; print... mas não obtive nada dela.

Estou tendo exatamente o mesmo problema. Acabei de abrir uma pergunta do SO que pode ser útil para um link aqui: http://stackoverflow.com/questions/30766419/python-child-process-silently-crashes-when-issuing-an-http-request

O processo filho está de fato travando silenciosamente sem aviso prévio.

Eu discordo de você @ oxymor0n , isso me parece um problema muito sério. Isso basicamente significa que sempre que nltk é importado, não há como emitir uma solicitação de um processo filho, o que pode ser realmente irritante ao trabalhar com APIs.

The child process is indeed crashing silently without further notice.

Também estamos enfrentando esse problema com a combinação de: nltk, gunicorn (com nltk carregado via prefork) e flask.

Remova a importação nltk e tudo funcionará. Exceto nltk.

/ cc @escherba

@ninowalker , @ oxymor0n É estranho, meus processos funcionam bem com o código, eu entendo:

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

Essa é a saída esperada, certo?

Isso também não interrompe meu pedido:

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

Estou a usar:

  • python 2.7.6
  • nltk 3.0.3
  • ubuntu 14.04

Encontrei o mesmo problema que o @Hiestaa teve. Tenho o arquivo auxiliar string_util.python que importa nltk, mas não é usado no arquivo python principal que usa o módulo de multiprocessamento para iniciar um rastreador de multiprocessos. O sintoma é que o processo filho simplesmente fica travado e não há mensagem de erro (nem mesmo mensagem de exceção).
Depois de comentar as importações e funções relacionadas ao nltk, o problema foi resolvido.

Detalhes:
OS: Yosemite 10.10.5
Python: 2.7.10
Recuperar o conteúdo da página: usei o urllib2 inicialmente, depois mudei para as solicitações.

Este é um bug muito sério e espero que alguém possa intervir e corrigi-lo. Obrigado!

Eu acho que este é um problema sério se você está fazendo PNL em nível de produção. Estamos usando workers Rq (http://python-rq.org/), para executar vários pipelines de PNL, que são silenciados silenciosamente ao fazer chamadas de rede. Espero que haja uma correção em breve. Obrigado!

@sasinda : Você pode querer fazer uma chamada na lista de e-mail nltk-dev para ver se consegue obter alguma atenção para este problema.

@sasinda Não tenho certeza de como o Rq funciona exatamente, mas no meu projeto de PNL de nível de produção, consegui contornar esse problema iniciando cada processo em um interpretador python separado e isolado, usando um script de shell para gerá-los na inicialização. Neste caso, o python nunca precisa se ramificar e a falha silenciosa do nltk nunca acontece. Talvez isso possa ajudar nesse meio tempo.

Descobri que executar a importação no nível da função evita o problema.

Em outras palavras, isso funciona:

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

e isso não:

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

Obrigado @mpenkov. Isso resolve o problema?

@stevenbird Acho que não. É uma solução alternativa, mas não é uma solução.

IMHO, se importar uma biblioteca de terceiros quebrar um componente de biblioteca padrão do Python, algo profano está acontecendo em algum lugar e precisa ser consertado.

@mpenkov Não tenho certeza de por que isso funciona, mas aqui está outra solução alternativa que descobri que funciona. Construir um abridor no processo pai parece consertá-lo. Modificando o 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 Todos vocês ainda enfrentam o mesmo problema?

Não consegui replicar o problema na minha 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 dá:

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

Estou dentro:

  • Python 2.7.12
  • NLTK 3.2.5
  • Ubuntu 16.04.3

@alvations Já se passou muito tempo desde que descobri esse problema.
Até esqueci qual base de projeto estava tendo esse problema, então não posso dizer se ainda tenho o problema ou não.
Desculpe!

@alvations Eu também não me lembro qual dos meus projetos sofreu com esses problemas específicos.

Executei seu código na minha máquina e não consegui replicar o problema.

Python 2.7.12
nltk 3.2.1
macOS 10.12.6

@alvations Eu também não estou mais trabalhando nesse projeto. Mas usou uma dessas soluções alternativas.
Tentei seu código, mas ainda assim o processo filho sai com falha de segmento (código de saída 11) para mim (sai na linha: urllib2.urlopen ("https://www.google.com") .read () [: 100])

No entanto, funcionou com 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. | (padrão, 20 de dezembro de 2016, 23:05:08)
    [GCC 4.2.1 compatível com Apple LLVM 6.0 (clang-600.0.57)] em darwin

Até onde eu sei, esse problema parece afetar o macOS. Usando Python 3.6 até agora,

  • macOS 10.13 (falha)
  • Centos 7.2 (bem-sucedido)
  • Ubuntu 16.04 (bem-sucedido)

Script do 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)

Resultado:

Fetch URL
Child process returned

O subprocesso fecha inesperadamente, recebendo uma saída semelhante ao que é visto nesta postagem do Stack Overflow .

Eu acho que isso é incompreensível. Pode ter algo a ver com o tratamento de threads no MacOS.

Não estou muito familiarizado com o nltk, mas dei uma espiada às cegas para ver o que fez o teste passar / falhar. Aqui está o que eu tive que fazer com o pacote __init__.py para passar no teste:


Detalhes (clique 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 as importações desativadas acabam levando de volta à importação de tkinter , que eu acho que é a causa raiz. Se eu substituir import nltk por import tkinter no script de teste, obtenho um relatório de falha muito semelhante, ambos fazendo referência ao tkinter.

Pelo que eu posso dizer, esses pacotes importam diretamente tkinter :

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

Das mudanças acima para o pacote principal __init__ , essas são as importações problemáticas e como elas remontam à importação do 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 )

Obrigado @rpkilby , isso é muito útil!

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

Eu acho que mexer tem sido um problema para nós há algum tempo. Talvez seja bom se encontrarmos uma alternativa para isso.

Concordo. Uma solução de curto prazo seria enterrar as importações do tkinter dentro das classes e métodos que precisam do tkinter e evitar importá-lo por programas que não precisam dele. Já fizemos algo semelhante para o numpy.

Esta página foi útil?
0 / 5 - 0 avaliações