我开始深入研究使用 Flask 将预测模型部署到 Web 应用程序,不幸的是,我陷入了起步阶段。
我做了什么:
我在我的 model.py 程序中腌制了我的模型:
import numpy as np
from sklearn.externals import joblib
class NeuralNetwork():
"""
Two (hidden) layer neural network model.
First and second layer contain the same number of hidden units
"""
def __init__(self, input_dim, units, std=0.0001):
self.params = {}
self.input_dim = input_dim
self.params['W1'] = np.random.rand(self.input_dim, units)
self.params['W1'] *= std
self.params['b1'] = np.zeros((units))
self.params['W2'] = np.random.rand(units, units)
self.params['W2'] *= std * 10 # Compensate for vanishing gradients
self.params['b2'] = np.zeros((units))
self.params['W3'] = np.random.rand(units, 1)
self.params['b3'] = np.zeros((1,))
model = NeuralNetwork(input_dim=12, units=64)
#####THIS RIGHT HERE ##############
joblib.dump(model, 'demo_model.pkl')
然后我根据本教程在与我的 demo_model.pkl 相同的目录中创建了一个 api.py 文件(https://blog.hyperiondev.com/index.php/2018/02/01/deploy-机器学习模型-flask-api/):
import flask
from flask import Flask, render_template, request
from sklearn.externals import joblib
app = Flask(__name__)
@app.route("/")
@app.route("/index")
def index():
return flask.render_template('index.html')
# create endpoint for the predictions (HTTP POST requests)
@app.route('/predict', methods=['POST'])
def make_prediction():
if request.method == 'POST':
return render_template('index.html', label='3')
if __name__ == '__main__':
# LOAD MODEL WHEN APP RUNS ####
model = joblib.load('demo_model.pkl')
app.run(host='0.0.0.0', port=8000, debug=True)
我还在同一目录中使用以下信息创建了 templates/index.html 文件:
<html>
<head>
<title>NN Model as Flask API</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Boston Housing Price Predictor</h1>
<form action="/predict" method="post" enctype="multipart/form-data">
<input type="file" name="image" value="Upload">
<input type="submit" value="Predict"> {% if label %} {{ label }} {% endif %}
</form>
</body>
</html>
跑步:
>> python api.py
给我一个pickler错误:
Traceback (most recent call last):
File "api.py", line 22, in <module>
model = joblib.load('model.pkl')
File "C:\Users\joshu\Anaconda3\lib\site-packages\sklearn\externals\joblib\numpy_pickle.py", line 578, in load
obj = _unpickle(fobj, filename, mmap_mode)
File "C:\Users\joshu\Anaconda3\lib\site-packages\sklearn\externals\joblib\numpy_pickle.py", line 508, in _unpickle
obj = unpickler.load()
File "C:\Users\joshu\Anaconda3\lib\pickle.py", line 1043, in load
dispatch[key[0]](self)
File "C:\Users\joshu\Anaconda3\lib\pickle.py", line 1342, in load_global
klass = self.find_class(module, name)
File "C:\Users\joshu\Anaconda3\lib\pickle.py", line 1396, in find_class
return getattr(sys.modules[module], name)
AttributeError: module '__main__' has no attribute 'NeuralNetwork'
为什么程序的主模块会涉及我的神经网络模型?我现在很困惑...任何建议将不胜感激。
更新:
向我的
api.py程序添加类定义
class NeuralNetwork(object): pass
修复了该错误。
import flask
from flask import Flask, render_template, request
from sklearn.externals import joblib
class NeuralNetwork(object):
pass
app = Flask(__name__)
如果有人愿意向我解释正在发生的事情,我将不胜感激!
您遇到的具体异常是指
__main__
中的属性,但这主要是转移注意力。我很确定这个问题实际上与您转储实例的方式有关。
Pickle 不会转储实际的代码类和函数,仅转储它们的名称。它包括定义每个模块的名称,以便它可以再次找到它们。如果您转储在作为脚本运行的模块中定义的类,它将转储名称
__main__
作为模块名称,因为这是 Python 用作主模块的名称(如 if __name__ == "__main__"
中所示)样板代码)。
当您将
model.py
作为脚本运行并腌制其中定义的类的实例时,该类将被保存为 __main__.NeuralNetwork
而不是 model.NeuralNetwork
。当您运行其他模块并尝试加载 pickle 文件时,Python 将在 __main__
模块中查找该类,因为 pickle 数据告诉它要查找的位置。这就是为什么您会收到关于 __main__
属性的异常。
为了解决这个问题,您可能需要更改转储数据的方式。您可能应该运行一些其他模块并让它执行
model.py
,而不是作为脚本运行 import model
,这样您就可以使用正常名称获得该模块。 (我想你可以将 model.py
导入到 if __name__ == "__main__"
块中,但这非常丑陋和尴尬)。您可能还需要避免在导入 model
时无条件地重新创建和转储实例,因为在加载 pickle 文件时需要发生这种情况(我认为 pickle 的全部目的是避免从头开始重新创建实例) .
因此从
model.py
底部删除转储逻辑,并添加一个新文件,如下所示:
# new script, dump_model.py, does the creation and dumping of the NeuralNetwork
from sklearn.externals import joblib
from model import NeuralNetwork
if __name__ == "__main__":
model = NeuralNetwork(input_dim=12, units=64)
joblib.dump(model, 'demo_model.pkl')
当您使用此脚本转储
NeuralNetwork
时,它将正确地将 model
识别为定义类的模块,因此加载代码将能够导入该模块并正确创建该类的实例。
您当前对该问题的“修复”(在加载对象时在
NeuralNetwork
模块中定义一个空的 __main__
类)可能是一个糟糕的解决方案。通过加载 pickle 文件获得的实例将是新类的实例,而不是原始类的实例。它将加载旧实例的属性,但不会在其上设置任何方法或其他类变量(这对于您所显示的类来说不是问题,但可能对于任何类型的更复杂的对象)。
如果您使用
Keras
库来构建神经网络,那么 pickle
将不起作用。 pickle
仅适用于使用 scikit
库构建的模型。使用 json
. 保存您的神经网络模型
Keras
提供了使用 JSON
格式和 to_json()
函数来描述任何模型的能力。可以将其保存到文件中,然后通过 model_from_json()
函数加载,该函数将根据 JSON 规范创建新模型。
# serialize model to JSON
model_json = model.to_json()
with open(“model.json”, “w”) as json_file:
json_file.write(model_json)
# serialize weights to HDF5
model.save_weights(“model.h5”)
print(“Saved model to disk”)
# later…
# load json and create model
json_file = open(‘model.json’, ‘r’)
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
# load weights into new model
loaded_model.load_weights(“model.h5”)
print(“Loaded model from disk”)
尝试使用以下解决方案:
import __main__
from model import NeuralNetwork
setattr(__main__, "NeuralNetwork", NeuralNetwork)