【问题标题】:Unable to unpickle an object while predicting using gunicorn使用 gunicorn 进行预测时无法解开对象
【发布时间】:2019-12-30 10:41:05
【问题描述】:

目前我正在使用 API 提供下一个单词预测模型。该模型在使用烧瓶时成功运行,但在使用 gunicorn 进行部署时解开对象存在问题。 Pickeled 对象依赖于类定义,我在需要时明确地提供类定义。

class LanguageModel(nn.Module):
    def __init__(self, vocab_size, embedding_size, hidden_size, n_layers=1, dropout_p=0.5):
        # Defining layers
        super(LanguageModel, self).__init__()
        self.n_layers = n_layers
        self.hidden_size = hidden_size
        self.embed = nn.Embedding(vocab_size, embedding_size)
        self.rnn = nn.LSTM(embedding_size, hidden_size, n_layers, batch_first=True)
        self.linear = nn.Linear(hidden_size, vocab_size)
        self.dropout = nn.Dropout(dropout_p)

    def init_weight(self):
        # self.embed.weight = nn.init.xavier_uniform(self.embed.weight)
        self.embed.weight.data.copy_(torch.from_numpy(new_w))
        self.linear.weight = nn.init.xavier_uniform(self.linear.weight)
        self.linear.bias.data.fill_(0)

# importing word indexes
with open(w2i, "rb") as f1:
    word2index = pickle.load(f1)

with open(i2w, "rb") as f2:
    index2word = pickle.load(f2)
# loading model
model = torch.load(wordModel)


def getNextWords(words):
    results = []
    data = [words]
    data = flatten([co.strip().split() + ['</s>'] for co in data])
    x = prepare_sequence(data, word2index)
    x = x.unsqueeze(1)
    x = batchify(x, 1)

    with torch.no_grad():
        hidden = model.init_hidden(1)
        for batch in getBatch(x, 1):
            inputs, targets = batch
            output, hidden = model(inputs, hidden)
            prob = output.exp()

            word_id = torch.multinomial(prob, num_samples=1).item()
            # word_probs = torch.multinomial(prob, num_samples=1).probs()
            word = index2word[word_id]
            results.append(word)
    return [res for res in results if res.isalpha()][:4]  # return results

app = Flask(__name__)

@app.route('/')
def home():
    return "Home"

@app.route('/getPredictions', methods=["POST"])
def getPredictions():
    #...... code .........
    resultJSON = {'inputPhrase': inputPhrase,
                  'predictions': predictions}  # predictions [nextPhrase]
    print('result: ', predictions)
    return jsonify(resultJSON)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3001, debug=True)  # 10.2.1.29

Gunicorn wsgi.py 文件:

from m_api import app
import torch
import torch.nn as nn
from torch.autograd import Variable

if __name__ == "__main__":
    class LanguageModel(nn.Module):
        def __init__(self, vocab_size, embedding_size, hidden_size, n_layers=1, dropout_p=0.5):
            # Defining layers
            super(LanguageModel, self).__init__()
            self.n_layers = n_layers
            self.hidden_size = hidden_size
            self.embed = nn.Embedding(vocab_size, embedding_size)
            self.rnn = nn.LSTM(embedding_size, hidden_size, n_layers, batch_first=True)
            self.linear = nn.Linear(hidden_size, vocab_size)
            self.dropout = nn.Dropout(dropout_p)
        def init_weight(self):
            # self.embed.weight = nn.init.xavier_uniform(self.embed.weight)
            self.embed.weight.data.copy_(torch.from_numpy(new_w))
            self.linear.weight = nn.init.xavier_uniform(self.linear.weight)
            self.linear.bias.data.fill_(0)

    app.run()

这个应用程序在由烧瓶提供服务时运行得非常好,但是当我使用 gunicorn 时会抛出一个错误:

    model = torch.load(wordModel)
  File "/home/.conda/envs/sppy36/lib/python3.6/site-packages/torch/serialization.py", line 426, in load
    return _load(f, map_location, pickle_module, **pickle_load_args)
  File "/home/.conda/envs/sppy36/lib/python3.6/site-packages/torch/serialization.py", line 613, in _load
    result = unpickler.load()
AttributeError: Can't get attribute 'LanguageModel' on <module '__main__' from '/home/.conda/envs/sppy36/bin/gunicorn'>

为了解决这个问题,我也在 wsgi.py 文件中包含了类定义,但在加载 pickeled 文件时仍然无法获取类定义。我需要在哪里指定类定义仍然未知。

【问题讨论】:

  • 不确定您的项目结构是什么。您是否在不同的脚本中定义了两次 LanguageModel 类?不要那样做。当您在主脚本中创建它时,它是__main__.LanguageModel。但是,当您从另一个文件导入它时,它是another_file.LanguageModel。因此,可能的解决方案是使用一个文件,在其中定义 LanguageModel 类并在使用 pickle 保存或加载数据时将其导入。
  • 该类已在 wsgi.py 文件中定义为试用以消除错误。我最初尝试从单个文件中导入类,而不是写两次,但这不是解决问题的方法。然后将类定义写入两个文件是第二次尝试,但也失败了。我仍在寻找定义类的正确方法。
  • 我猜你运行代码的方式不同。你运行你的脚本my_script.py 并用pickle 保存模型——你有__main__.LanguageModel。然后你运行你的应用程序。为此,您需要导入您的脚本 - 这样您就有了 my_script.LanguageModel。那就是问题所在。尝试按照我之前评论中的建议进行操作。再次建议:创建一个定义LanguageModel 类的文件,例如my_model.py。然后导入文件import my_model 并在两种情况下使用它my_model.LanguageModel - 当您保存和加载模型时使用pickle。
  • 它还没有解决我的问题。现在,我已将 LanguageModel 的类定义与训练和预测文件分开,并将其放在单独的文件中,并且两次都从同一个文件中导入类。但是抛出的错误是一样的。看看我们是否有其他解决方案。

标签: python python-3.x flask pickle gunicorn


【解决方案1】:

问题是因为 gunicorn 查找 Class 定义 int 的 main 方法,即 gunicorn 可执行文件。这就是为什么即使在两个 .py 文件中明确定义类在 gunicorn 上运行时也没有完成预期的工作,但在使用烧瓶时却做到了。 为了克服这个问题,我在 gunicorn 可执行文件中明确定义了该类并且它起作用了。目前,我发现这是可行的解决方案。

gunicorn.py

#!/home/user/anaconda3/envs/envName/bin/python

import re
import sys

from gunicorn.app.wsgiapp import run

import torch
import torch.nn as nn
from torch.autograd import Variable

USE_CUDA = torch.cuda.is_available()

if __name__ == '__main__':
    # defining model class
    class LanguageModel(nn.Module):
        def __init__(self, vocab_size, embedding_size, hidden_size, n_layers=1, dropout_p=0.5):
            # Defining layers
            super(LanguageModel, self).__init__()
            self.n_layers = n_layers
            self.hidden_size = hidden_size
            self.embed = nn.Embedding(vocab_size, embedding_size)
            self.rnn = nn.LSTM(embedding_size, hidden_size, n_layers, batch_first=True)
            self.linear = nn.Linear(hidden_size, vocab_size)
            self.dropout = nn.Dropout(dropout_p)

        def init_weight(self):
            # self.embed.weight = nn.init.xavier_uniform(self.embed.weight)
            self.embed.weight.data.copy_(torch.from_numpy(new_w))
            self.linear.weight = nn.init.xavier_uniform(self.linear.weight)
            self.linear.bias.data.fill_(0)

        def init_hidden(self, batch_size):
            hidden = Variable(torch.zeros(self.n_layers, batch_size, self.hidden_size))
            context = Variable(torch.zeros(self.n_layers, batch_size, self.hidden_size))
            return (hidden.cuda(), context.cuda()) if USE_CUDA else (hidden, context)

        def detach_hidden(self, hiddens):
            return tuple([hidden.detach() for hidden in hiddens])

        def forward(self, inputs, hidden, is_training=False):
            embeds = self.embed(inputs)
            if is_training:
                embeds = self.dropout(embeds)
            out, hidden = self.rnn(embeds, hidden)
            return self.linear(out.contiguous().view(out.size(0) * out.size(1), -1)), hidden


    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(run())

【讨论】:

  • 嗨,我也遇到了同样的问题,你把 gunicorn.py 文件放在哪里了,在 app 目录中使用这个文件,没有解决我的问题,它只是忽略这个文件并去执行我的应用程序。
猜你喜欢
  • 1970-01-01
  • 2018-12-31
  • 1970-01-01
  • 2020-08-29
  • 2020-12-08
  • 2010-11-18
  • 2022-01-14
  • 2018-03-04
  • 1970-01-01
相关资源
最近更新 更多