从joblib文件加载的TfidfVectorizer模型仅在相同的会话中训练时才能工作

问题描述

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',['nestling']),Tree('PP',[Tree('IN',['in']),[Tree('DT',['the']),Tree('NN',['grass'])])])])])])]),Tree(',',[',']),['winking']),Tree('CC',['and']),['glimmering']),['like']),['jewels'])])])])]),Tree('.',['.'])])]),Tree('ROOT',[Tree('NNP',['Rose']),Tree('NNS',['petals'])]),[Tree('ADVP',[Tree('RB',['perhaps'])]),['or']),['some'])]),Tree('NML',[Tree('NN',['kind'])])]),['of']),['confetti'])])])])]),['.'])])])]
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)

但是该模型是两次一次从文件加载的,并且没有全局变量(我知道),所以我们无法想到为什么它在一种情况下有效而在另一种情况下无效。

感谢您的帮助!

解决方法

好吧,我做了一些非常深入的研究,如果您检查了Tree结构隐式使用的Production类here,则看起来他们在该类存储_hash被建造。但是,Python hash函数在两次运行之间是不确定的,这意味着此值在两次运行之间可能不一致。因此,哈希是用joblib腌制的,而不是按照应有的方式重新计算。因此,这似乎是nltk中的错误。这导致模型在重新加载时看不到生产规则,因为哈希值不匹配,就好像生产规则从未存储在词汇表中。

非常棘手!

在此特定的nltk被修复之前,在运行训练脚本和测试脚本之前设置PYTHONHASHSEED将迫使每次哈希都相同。

PYTHONHASHSEED=0 python script.py