Python 从joblib文件加载的TfidfVectorizer模型仅在同一会话中训练时有效

Python 从joblib文件加载的TfidfVectorizer模型仅在同一会话中训练时有效,python,scikit-learn,nltk,joblib,tfidfvectorizer,Python,Scikit Learn,Nltk,Joblib,Tfidfvectorizer,sklearn…TfidfVectorizer仅当分析仪返回nltk.tree.tree对象列表时,才在训练后应用它时工作。这是一个谜,因为模型在应用之前总是从文件加载。当模型文件在其自己的会话开始时加载并应用时,与在该会话中接受培训时相比,调试显示模型文件没有任何错误或不同之处。在这两种情况下,分析仪也能正常使用和工作 下面是一个帮助重现神秘行为的脚本: import joblib import numpy as np from nltk import Tree from sklearn.fe

sklearn…TfidfVectorizer
仅当分析仪返回
nltk.tree.tree
对象列表时,才在训练后应用它时工作。这是一个谜,因为模型在应用之前总是从文件加载。当模型文件在其自己的会话开始时加载并应用时,与在该会话中接受培训时相比,调试显示模型文件没有任何错误或不同之处。在这两种情况下,分析仪也能正常使用和工作

下面是一个帮助重现神秘行为的脚本:

import joblib
import numpy as np
from nltk import Tree
from sklearn.feature_extraction.text import TfidfVectorizer

def lexicalized_production_analyzer(sentence_trees):
    productions_per_sentence = [tree.productions() for tree in sentence_trees]
    return np.concatenate(productions_per_sentence)

def train(corpus):
    model = TfidfVectorizer(analyzer=lexicalized_production_analyzer)
    model.fit(corpus)
    joblib.dump(model, "model.joblib")

def apply(corpus):
    model = joblib.load("model.joblib")
    result = model.transform(corpus)
    return result

# exmaple data
trees = [Tree('ROOT', [Tree('FRAG', [Tree('S', [Tree('VP', [Tree('VBG', ['arkling']), Tree('NP', [Tree('NP', [Tree('NNS', ['dots'])]), Tree('VP', [Tree('VBG', ['nestling']), Tree('PP', [Tree('IN', ['in']), Tree('NP', [Tree('DT', ['the']), Tree('NN', ['grass'])])])])])])]), Tree(',', [',']), Tree('VP', [Tree('VBG', ['winking']), Tree('CC', ['and']), Tree('VP', [Tree('VBG', ['glimmering']), Tree('PP', [Tree('IN', ['like']), Tree('NP', [Tree('NNS', ['jewels'])])])])]), Tree('.', ['.'])])]),
 Tree('ROOT', [Tree('FRAG', [Tree('NP', [Tree('NP', [Tree('NNP', ['Rose']), Tree('NNS', ['petals'])]), Tree('NP', [Tree('NP', [Tree('ADVP', [Tree('RB', ['perhaps'])]), Tree(',', [',']), Tree('CC', ['or']), Tree('NP', [Tree('DT', ['some'])]), Tree('NML', [Tree('NN', ['kind'])])]), Tree('PP', [Tree('IN', ['of']), Tree('NP', [Tree('NN', ['confetti'])])])])]), Tree('.', ['.'])])])]
corpus = [trees, trees, trees]
首先训练模型并保存
model.joblib
文件

train(corpus)
result = apply(corpus)
print("number of elements in results: " + str(result.getnnz()))
print("shape of results: " + str(result.shape))
我们打印结果的数量
.getnnz()
,以显示模型正在使用120个已计数的元素:

number of elements in results: 120
shape of results: (3, 40)
然后重新启动python并将模型重新应用于相同的语料库,无需培训

result = apply(corpus)
print("number of elements in results: " + str(result.getnnz()))
print("shape of results: " + str(result.shape))
您将看到这次保存了零个元素

number of elements in results: 0
shape of results: (3, 40)
但是这个模型是从一个文件中加载的,两次加载都没有全局变量(我知道),所以我们不知道为什么它在一种情况下有效,在另一种情况下无效


谢谢你的帮助

好的,我做了一些非常深入的挖掘,如果您检查您隐式使用的
结构的生产类,它看起来像是在创建类时存储
\u hash
。但是,Python
hash
函数在运行之间是不确定的,这意味着该值在运行之间可能不一致。因此,使用
joblib
对哈希进行pickle处理,而不是按原样重新计算。所以这看起来像是
nltk
中的一个bug。这会导致模型在重新加载时看不到生产规则,因为哈希不匹配,所以就好像生产规则从未存储在vocab中一样

相当狡猾

在修复此特定的
nltk
之前,在运行培训和测试脚本之前设置
pythonhasheed
,将强制每次哈希相同

PYTHONHASHSEED=0 python script.py

每次创建
sklearn…TfidfVectorizer
并在预测之前手动重新加载词汇表和权重都不起作用-因为有问题的散列在
nltk
树中的一个级别以下,所以我做了一个PR:Brilliant!你的公关是解决这个问题的好办法。