我开始倾向于使用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')
然后我根据本教程(https://blog.hyperiondev.com/index.php/2018/02/01/deploy-machine-learning-models-flask-api/)在与 demo_model.pkl 相同的目录中创建了 api.py 文件:
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
给了我一个错误的选择器:
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'
为什么程序的主要模块涉及我的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__)
如果有人愿意向我提供正在发生的事情的解释,那将非常感激!
答案 0 :(得分:5)
您获得的具体例外是指__main__
中的属性,但这主要是红鲱鱼。我很确定这个问题实际上与你如何转储实例有关。
Pickle不会转储实际的代码类和函数,只会转储它们的名称。它包括每个模块的名称,因此它可以再次找到它们。如果转储在作为脚本运行的模块中定义的类,它将转储名称__main__
作为模块名称,因为这是Python用作主模块的名称(如{ {1}}样板代码)。
当您将if __name__ == "__main__"
作为脚本运行并挑选其中定义的类的实例时,该类将保存为model.py
而不是__main__.NeuralNetwork
。当你运行一些其他模块并尝试加载pickle文件时,Python会在model.NeuralNetwork
模块中查找该类,因为那是pickle数据告诉它的样子。这就是为什么你得到关于__main__
的属性的例外。
要解决此问题,您可能希望更改转储数据的方式。您应该运行一些其他模块并让它执行__main__
,而不是将model.py
作为脚本运行,因此您可以使用它的正常名称获取模块。 (我想你可以在import model
块中导入model.py
,但这非常丑陋和笨拙。您可能还需要避免在导入if __name__ == "__main__"
时无条件地重新创建和转储实例,因为这需要在加载pickle文件时发生(我认为pickle的整个要点是避免重新创建实例)划痕)。
因此,从model
的底部删除转储逻辑,并添加如下新文件:
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
类)可能是一个糟糕的解决方案。从加载pickle文件获得的实例将是新类的实例,而不是原始实例。它将加载旧实例的属性,但它不会设置任何方法或其他类变量(这不是您所显示的类的问题,但可能适用于任何类型的对象更复杂)。
答案 1 :(得分:0)
如果您使用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”)