joblib.load __main__ 属性错误
joblib.load __main__ AttributeError
我开始深入研究使用 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-machine-learning-models-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 定义 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 不会转储实际代码 classes 和函数,只会转储它们的名称。它包括每个定义所在的模块的名称,因此它可以再次找到它们。如果转储模块中定义的 class 作为脚本,它会将名称 __main__
转储为模块名称,因为这就是 Python 使用的名称作为主模块的名称(如 if __name__ == "__main__"
样板代码所示)。
当您 运行 model.py
作为脚本并 pickle 一个在其中定义的 class 的实例时,class 将被保存为 __main__.NeuralNetwork
而不是 model.NeuralNetwork
。当您 运行 一些其他模块并尝试加载 pickle 文件时,Python 将在 __main__
模块中查找 class,因为 pickle 数据告诉它看。这就是为什么您会收到有关 __main__
.
属性的异常的原因
要解决这个问题,您可能需要更改转储数据的方式。而不是 运行ning 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
识别为定义 class 的模块,因此加载代码将能够导入该模块并正确创建 class 的实例。
您当前的 "fix" 问题(加载对象时在 __main__
模块中定义一个空的 NeuralNetwork
class)可能是一个糟糕的解决方案。您从加载 pickle 文件中获得的实例将是新 class 的实例,而不是原始实例。它将加载旧实例的属性,但不会在其上设置任何方法或其他 class 变量(这不是您显示的 class 的问题,但可能适用于更复杂的任何类型的对象)。
如果您使用 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”)
我开始深入研究使用 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-machine-learning-models-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 定义 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 不会转储实际代码 classes 和函数,只会转储它们的名称。它包括每个定义所在的模块的名称,因此它可以再次找到它们。如果转储模块中定义的 class 作为脚本,它会将名称 __main__
转储为模块名称,因为这就是 Python 使用的名称作为主模块的名称(如 if __name__ == "__main__"
样板代码所示)。
当您 运行 model.py
作为脚本并 pickle 一个在其中定义的 class 的实例时,class 将被保存为 __main__.NeuralNetwork
而不是 model.NeuralNetwork
。当您 运行 一些其他模块并尝试加载 pickle 文件时,Python 将在 __main__
模块中查找 class,因为 pickle 数据告诉它看。这就是为什么您会收到有关 __main__
.
要解决这个问题,您可能需要更改转储数据的方式。而不是 运行ning 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
识别为定义 class 的模块,因此加载代码将能够导入该模块并正确创建 class 的实例。
您当前的 "fix" 问题(加载对象时在 __main__
模块中定义一个空的 NeuralNetwork
class)可能是一个糟糕的解决方案。您从加载 pickle 文件中获得的实例将是新 class 的实例,而不是原始实例。它将加载旧实例的属性,但不会在其上设置任何方法或其他 class 变量(这不是您显示的 class 的问题,但可能适用于更复杂的任何类型的对象)。
如果您使用 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”)