在scikit-learn中保存和重复使用TfidfVectorizer

27

我正在使用scikit learn中的TfidfVectorizer来从文本数据创建矩阵。现在我需要保存这个对象以便以后重复使用它。我尝试使用pickle,但它给出了以下错误:

loc=open('vectorizer.obj','w')
pickle.dump(self.vectorizer,loc)
*** TypeError: can't pickle instancemethod objects

我尝试在sklearn.externals中使用joblib,但是出现了类似的错误。有没有办法可以保存这个对象,以便以后重复使用?
这是我的完整对象:
class changeToMatrix(object):
def __init__(self,ngram_range=(1,1),tokenizer=StemTokenizer()):
    from sklearn.feature_extraction.text import TfidfVectorizer
    self.vectorizer = TfidfVectorizer(ngram_range=ngram_range,analyzer='word',lowercase=True,\
                                          token_pattern='[a-zA-Z0-9]+',strip_accents='unicode',tokenizer=tokenizer)

def load_ref_text(self,text_file):
    textfile = open(text_file,'r')
    lines=textfile.readlines()
    textfile.close()
    lines = ' '.join(lines)
    sent_tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
    sentences = [ sent_tokenizer.tokenize(lines.strip()) ]
    sentences1 = [item.strip().strip('.') for sublist in sentences for item in sublist]      
    chk2=pd.DataFrame(self.vectorizer.fit_transform(sentences1).toarray()) #vectorizer is transformed in this step 
    return sentences1,[chk2]

def get_processed_data(self,data_loc):
    ref_sentences,ref_dataframes=self.load_ref_text(data_loc)
    loc=open("indexedData/vectorizer.obj","w")
    pickle.dump(self.vectorizer,loc) #getting error here
    loc.close()
    return ref_sentences,ref_dataframes
2个回答

17

首先,在你的代码顶部留下导入语句比在类内部定义要更好:

from sklearn.feature_extraction.text import TfidfVectorizer
class changeToMatrix(object):
  def __init__(self,ngram_range=(1,1),tokenizer=StemTokenizer()):
    ...

下一个 StemTokenizer 看起来不像是一个正式的类。可能你从http://sahandsaba.com/visualizing-philosophers-and-scientists-by-the-words-they-used-with-d3js-and-python.html或其他地方得到了它,所以我们假设它返回一个字符串列表

class StemTokenizer(object):
    def __init__(self):
        self.ignore_set = {'footnote', 'nietzsche', 'plato', 'mr.'}

    def __call__(self, doc):
        words = []
        for word in word_tokenize(doc):
            word = word.lower()
            w = wn.morphy(word)
            if w and len(w) > 1 and w not in self.ignore_set:
                words.append(w)
        return words

现在回答你的实际问题,有可能你需要在将pickle转储之前以字节模式打开文件,即:

>>> from sklearn.feature_extraction.text import TfidfVectorizer
>>> from nltk import word_tokenize
>>> import cPickle as pickle
>>> vectorizer = TfidfVectorizer(ngram_range=(0,2),analyzer='word',lowercase=True, token_pattern='[a-zA-Z0-9]+',strip_accents='unicode',tokenizer=word_tokenize)
>>> vectorizer
TfidfVectorizer(analyzer='word', binary=False, decode_error=u'strict',
        dtype=<type 'numpy.int64'>, encoding=u'utf-8', input=u'content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
        ngram_range=(0, 2), norm=u'l2', preprocessor=None, smooth_idf=True,
        stop_words=None, strip_accents='unicode', sublinear_tf=False,
        token_pattern='[a-zA-Z0-9]+',
        tokenizer=<function word_tokenize at 0x7f5ea68e88c0>, use_idf=True,
        vocabulary=None)
>>> with open('vectorizer.pk', 'wb') as fin:
...     pickle.dump(vectorizer, fin)
... 
>>> exit()
alvas@ubi:~$ ls -lah vectorizer.pk 
-rw-rw-r-- 1 alvas alvas 763 Jun 15 14:18 vectorizer.pk

注意:使用with习语进行I/O文件访问会在离开with作用域时自动关闭文件。

关于SnowballStemmer()的问题,请注意,SnowballStemmer('english')是一个对象,而词干提取函数是SnowballStemmer('english').stem

重要提示

  • TfidfVectorizer的tokenizer参数期望接受一个字符串并返回一个字符串列表。
  • 但是Snowball词干提取器不接受字符串作为输入并返回一个字符串列表。

所以你需要这样做:

>>> from nltk.stem import SnowballStemmer
>>> from nltk import word_tokenize
>>> stemmer = SnowballStemmer('english').stem
>>> def stem_tokenize(text):
...     return [stemmer(i) for i in word_tokenize(text)]
... 
>>> vectorizer = TfidfVectorizer(ngram_range=(0,2),analyzer='word',lowercase=True, token_pattern='[a-zA-Z0-9]+',strip_accents='unicode',tokenizer=stem_tokenize)
>>> with open('vectorizer.pk', 'wb') as fin:
...     pickle.dump(vectorizer, fin)
...
>>> exit()
alvas@ubi:~$ ls -lah vectorizer.pk 
-rw-rw-r-- 1 alvas alvas 758 Jun 15 15:55 vectorizer.pk

1
以字节模式打开文件没有起作用。但我找到了问题所在。是StemTokenizer类造成的问题。在初始化该类时,我给出了“self.snowball_stemmer = SnowballStemmer('english')”。当我将这部分改为__call__部分时,它就起作用了。虽然我不确定为什么它能起作用。 - Joswin K J
你需要确保无论令牌化函数是什么,它都返回一个字符串列表。 - alvas
它仅返回一个字符串列表。当我将self.snowball_stemmer = SnowballStemmer('english')更改为snowball_stemmer = SnowballStemmer('english')时,错误被修复了。基本上,我从类的属性中删除了这个,并且错误已经解决了。 - Joswin K J
1
啊。这是因为SnowballStemmer('english')是一个对象,你需要使用SnowballStemmer('english').stem来获取可迭代对象。 - alvas
你好!我正在尝试保存一个Pickle文件,用于使用TfidfVectorizer转换文本,它的大小为76MB,我需要将其缩小到10MB。参数dtype=<type 'numpy.int64'>是否有助于减小文件大小? - anitasp

4
如果您来到这个问答页面是为了了解如何将一个向量化器进行压缩以节省磁盘空间,您可以使用scikit-learn自带的joblib并设置compress=True,或者使用内置的gzip模块和pickle。下面是一个可行的示例,它可以将文件压缩至原大小的至少2倍小,适用于我的使用场景。
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.datasets import fetch_20newsgroups
import joblib
import pickle
import gzip

data = fetch_20newsgroups().data
tvec = TfidfVectorizer()
tvec.fit(data)

# option #1
joblib.dump(tvec, 'vectorizer.pkl', compress=True)

# option #2
with gzip.open('vectorizer.pkl', 'wb') as f:
    pickle.dump(tvec, f)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接