且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

凯拉斯预测芹菜任务不归队

更新时间:2022-10-15 15:19:11

我碰到了这个完全相同的问题,而那真是个兔子洞.想要在这里发布我的解决方案,因为这可能会节省某人一天的工作:

TensorFlow线程特定的数据结构

在TensorFlow中,当您调用model.predict(或keras.models.load_modelkeras.backend.clear_session或几乎任何其他与TensorFlow后端交互的函数)时,有两种关键的数据结构在幕后工作:>

在文档中没有明确挖掘就不清楚的是,会话和图形都是当前线程的属性.请在此处 from keras.models import load_model MY_MODEL = load_model('path/to/model/file') def some_worker_function(inputs): return MY_MODEL.predict(inputs)

在像Celery这样的Web服务器或工作器池上下文中,这意味着您在导入包含load_model行的模块时将加载模型,然后另一个线程将执行some_worker_function,并在全局包含Keras模型的变量.但是,尝试在装入不同线程的模型上运行预测会产生张量不是此图的元素"错误.感谢有关此主题的几篇SO帖子,例如 from keras.models import load_model import tensorflow as tf MY_MODEL = load_model('path/to/model/file') MY_GRAPH = tf.get_default_graph() def some_worker_function(inputs): with MY_GRAPH.as_default(): return MY_MODEL.predict(inputs)

这里有些令人惊讶的转折是:如果您使用Thread,上面的代码就足够了,但是如果您使用Process es,则可以无限期地挂起.并且默认情况下,Celery使用进程管理其所有工人池.因此,此时,仍然在Celery上无法正常工作.

为什么这仅在Thread上有效?

在Python中,Thread与父进程共享相同的全局执行上下文.从 Python _thread文档:

此模块提供了用于处理多个线程(也称为轻量级进程或任务)的低级原语-多个控件线程共享其全局数据空间.

由于线程不是实际的独立进程,因此它们使用相同的python解释器,因此要受到臭名昭著的Global Interpeter Lock(GIL)的约束.对于这次调查而言,也许更重要的是,它们与父级共享全局数据空间.

与此相反,Process es是程序产生的 actual 新进程.这意味着:

  • 新的Python解释器实例(没有GIL)
  • 全局地址空间已重复

请注意此处的区别.虽然Thread可以访问共享的单个全局Session变量(内部存储在Keras的tensorflow_backend模块中),但是Process es具有Session变量的重复项.

我对这个问题的***理解是,Session变量应该表示客户机(进程)与TensorFlow运行时之间的唯一连接,但是由于在派生过程中被复制,因此该连接信息不正确调整.这会导致TensorFlow在尝试使用以其他过程创建的Session时挂起.如果有人对TensorFlow的工作原理有更深入的了解,我很乐意听到!

解决方案/解决方法

我调整了Celery,以便它使用Thread s而不是Process es进行池化.这种方法有一些缺点(请参见上面的GIL注释),但这使我们只能加载一次模型.无论如何,由于TensorFlow运行时会最大化所有CPU内核,因此我们并不是真正受CPU约束的地方(因为它不是用Python编写的,因此可以避开GIL).您必须为Celery提供一个单独的库才能进行基于线程的池化.该文档建议了两个选项: gevent 解决方案