joblib.load __main__ AttributeError

时间:2018-04-03 02:02:56

标签: python machine-learning flask pickle

我开始倾向于使用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__)

如果有人愿意向我提供正在发生的事情的解释,那将非常感激!

2 个答案:

答案 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”)