joblib.load __main__ 属性错误

问题描述 投票:0回答:3

我开始深入研究使用 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__)

如果有人愿意向我解释正在发生的事情,我将不胜感激!

python machine-learning flask pickle
3个回答
26
投票

您遇到的具体异常是指

__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 文件获得的实例将是新类的实例,而不是原始类的实例。它将加载旧实例的属性,但不会在其上设置任何方法或其他类变量(这对于您所显示的类来说不是问题,但可能对于任何类型的更复杂的对象)。


2
投票

如果您使用

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

0
投票

尝试使用以下解决方案:

import __main__
from model import NeuralNetwork
setattr(__main__, "NeuralNetwork", NeuralNetwork)
© www.soinside.com 2019 - 2024. All rights reserved.