背景:TextCnn模型用tensorflow+flask+gunicorn搭建模型预测并发API生产环境,模型调用抛异常。
报错代码:
#模型代码处:
# Misc Parameters
tf.flags.DEFINE_boolean("allow_soft_placement", True, "Allow device soft device placement")
tf.flags.DEFINE_boolean("log_device_placement", False, "Log placement of ops on devices")
FLAGS = tf.flags.FLAGS
FLAGS.flag_values_dict()
FLAGS.set_default("checkpoint_dir",checkpoint_dir)
# Map data into vocabulary
vocab_path = os.path.join(FLAGS.checkpoint_dir, "..", "vocab")
self.vocab_processor = learn.preprocessing.VocabularyProcessor.restore(vocab_path)
#tensorflow的flags.py代码处:
class _FlagValuesWrapper(object):
"""Wrapper class for absl.flags.FLAGS.
The difference is that tf.flags.FLAGS implicitly parses flags with sys.argv
when accessing the FLAGS values before it's explicitly parsed,
while absl.flags.FLAGS raises an exception.
"""
def __init__(self, flags_object):
self.__dict__['__wrapped'] = flags_object
def __getattribute__(self, name):
if name == '__dict__':
return super(_FlagValuesWrapper, self).__getattribute__(name)
return self.__dict__['__wrapped'].__getattribute__(name)
def __getattr__(self, name):
wrapped = self.__dict__['__wrapped']
# To maintain backwards compatibility, implicitly parse flags when reading
# a flag.
if not wrapped.is_parsed():
wrapped(_sys.argv)
return wrapped.__getattr__(name)
报错信息:
File "/usr/local/lib/python3.6/site-packages/tensorflow/python/platform/flags.py", line 86, in __getattr__
wrapped(_sys.argv)
File "/usr/local/lib/python3.6/site-packages/absl/flags/_flagvalues.py", line 633, in __call__
name, value, suggestions=suggestions)
absl.flags._exceptions.UnrecognizedFlagError: Unknown command line flag 'c'
问题原因:
gunicorn的如下两种命令行启动方式都会通过命令行带配置参数(gunicorn自己用的),被flask文件带到了模型调用处,tensorflow解析sys.argv的时候不认识了,对于这种情况tensorflow代码里是有注释说明的。
#gunicorn -c gun.py evalute:app
#gunicorn -w 4 -b 127.0.0.1:5000 evalute:app
#tensorflow的代码注释说明:
"""Wrapper class for absl.flags.FLAGS.
The difference is that tf.flags.FLAGS implicitly parses flags with sys.argv
when accessing the FLAGS values before it's explicitly parsed,
while absl.flags.FLAGS raises an exception.
"""
解决办法:
1. 修改tensorflow的flags.py代码
参考 https://blog.csdn.net/qq_35240640/article/details/103632902 加两行代码,不让tensorflow解读sys.argv。
百度的时候也有推荐修改tensorflow版本的,太麻烦,没试。
def __getattr__(self, name):
wrapped = self.__dict__['__wrapped']
# To maintain backwards compatibility, implicitly parse flags when reading
# a flag.
if not wrapped.is_parsed():
while len(sys.argv) > 1:
sys.argv.pop()
wrapped(_sys.argv)
return wrapped.__getattr__(name)
2. 修改工程模型代码
提前定义,让tensorflow认识,虽然它也用不到。
#debug Parameters
tf.flags.DEFINE_integer("w", 3, "gunicorn workers' number")
tf.flags.DEFINE_string("b", "", "gunicorn workers' ip and port")
tf.flags.DEFINE_string("c", "", "gunicorn workers' config address")
FLAGS = tf.flags.FLAGS
显然第2中方式更合理。
详细追查过程:
其实在定位到上面的报错之前还有一步。
第一步:
flask的启用入口我涉及到两种,一种是开发环境main函数入口;一种是生产环境gunicorn入口。
我最初模型的实例化放到了main函数里,所以在通过gunicorn调用的时候就找不到模型了,通过给flask的app入口文件打log看报错信息,定位了问题。
解决办法就是把实例化代码放到flask app文件的公用代码处。
第二步:
在哪查看报错信息:
1. 在通过配置文件启动gunicorn的时候报错信息查看debug文件。
2. 在通过-w -b直接启动gunicorn的时候报错信息在控制台。
走的弯路:
在第二步排查的时候,我一度以为是因为gunicorn多进程实例化了多个模型而不该实例化多个模型所以报错了。
于是试图通过gunicorn提供的preload功能或flask的@app.before_request的注解功能提前实例化一个模型实例,最后通过报错信息才知道问题症结所在。