如何正确转换和“拟合”文本分类器的值?

Posted

技术标签:

【中文标题】如何正确转换和“拟合”文本分类器的值?【英文标题】:How do I tfidf transform and "fit" the values for my text classifier correctly? 【发布时间】:2016-09-08 22:01:16 【问题描述】:

我正在编写一个朴素贝叶斯分类器,因为我有大量文本文档需要分类。但是,当我尝试测试我的预测时,我收到以下错误

sklearn.utils.validation.NotFittedError: TfidfVectorizer - 词汇不适合。

在这里提问之前我做了什么

我知道朴素贝叶斯分类的工作原理。

                         P(B|A)*P(A)
  P(A|B) =           ____________________

           P(B|A)*P(A) + P(B|C)*P(C) +...+P(B|n)*P(n)

其中 A 到 n 是您要分类的不同类别,P(B|A) 是 B 发生的概率,假设 A 已经发生,P(A) 是 A 发生的概率。应该注意的是,我专门使用多项式朴素贝叶斯。

我也发现了这个问题:

SciPy and scikit-learn - ValueError: Dimension mismatch

还有这个问题

cannot cast array data when a saved classifier is called

但是,当我尝试做出预测或测试我的预测时,我仍然遇到问题。

我编写了以下用于创建训练和测试集的函数

def split_data_set(original_data_set, percentage):
    test_set = []
    train_set = []

    forbidden = set()

    split_sets = 

    if is_float(percentage):
        stop_len = int(percentage * len(original_data_set))

    while len(train_set) < stop_len:
        random_selection = randrange(0, len(original_data_set))
        if random_selection not in forbidden:
            forbidden.add(random_selection)
            train_set.append(original_data_set[random_selection])

    for j in range(0, len(original_data_set)-1):
        if j not in forbidden:
            test_set.append(original_data_set[j])

    split_sets.update('testing set': test_set)
    split_sets.update('training set': train_set)
    split_sets.update('forbidden': forbidden)

    return split_sets

创建和训练模型

def create_and_fit_baes_model(data_set):

    train = []
    expect = []

    for data in data_set['training set']:
        train.append(data[1])
        expect.append(data[0])

    vectorizer = TfidfVectorizer(min_df=1)

    # I think this is one of the places where I'm doing something 
    # incorrectly
    vectorized_training_data = vectorizer.fit_transform(train)


    model = MultinomialNB()


    model.fit(vectorized_training_data, expect)

    return model

并测试我的模型

def test_nb_model(data_set, model):

    test = []
    expect = []

    for data in data_set['testing set']:
        test.append(data[1])
        expect.append(data[0])

    #This is the other section where I think that 
    # I'm doing something incorrectly
    vectorizer = TfidfVectorizer(min_df=1)
    vectorized_testing_data = vectorizer.transform(test)
    fitted_vectorized_testing_data = vectorizer.fit(vectorized_testing_data)

    predicted = model.predict(fitted_vectorized_testing_data)

    print(metrics.confusion_matrix(expect,predicted))
    print(metrics.classification_report(expect, predicted))

我认为我在转换/拟合阶段遇到了问题。

我知道 tfidf 矢量化的工作原理如下

这将是一个由具有不同术语计数的文档组成的常规矩阵。

     _term1____term2____term3____termn____________
doc1|   5  |    0   |     13   |   1
doc2|   0  |    8   |     2    |   0
doc3|   1  |    5   |     5    |   10
.   |   .  |    .   |     .    |   .
.   |   .  |    .   |     .    |   .
.   |   .  |    .   |     .    |   .
docn|   10 |    0   |     0    |   0

从这里您可以应用加权方案来确定特定单词对您的语料库的重要性。

我知道所有这些在理论上是如何工作的,并且我可以在纸上进行数学计算,但是当我尝试阅读 the documentation for sklearn 时,我仍然对我应该如何编写所有代码感到有些困惑。

过去两天我一直在为此苦苦挣扎。如果有人能提供一些关于我做错了什么以及如何充分训练和运行我的模型的见解,我将不胜感激。

【问题讨论】:

你也需要保存 tfidf vectorizer,并在训练阶段使用同一个 @lejlot 你的意思是使用完全相同的矢量化器吗?就像我需要将它从一种方法传递给另一种方法一样? 是的,我的意思是你也要保存它 【参考方案1】:

我认为最干净的选择是使用Pipeline 将矢量化器与分类器打包;那么如果你调用model.fit,这将适合你的矢量化器的词汇和词频,并使它们可用于以后的函数。这样你仍然可以从你的训练函数中返回一个“模型”,如果你需要保存你的模型,你也可以腌制它。

from sklearn.pipeline import Pipeline

def create_and_fit_model(data):
    # ... get your train and expect data
    vectorizer = TfidfVectorizer(min_df=1)
    nb = MultinomialNB()
    model = Pipeline([('vectorizer', vectorizer), ('nb', nb)])
    model.fit(train, expect)
    return model

顺便说一句,您不需要为训练/测试拆分编写自己的代码,您可以使用sklearn.cross_validation.train_test_split。此外,您应该考虑使用 pandas 来存储数据而不是普通列表;这将使提取列更容易。

【讨论】:

以上是关于如何正确转换和“拟合”文本分类器的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决基于 NLP 的 CNN 模型中的过度拟合问题,以使用词嵌入进行多类文本分类?

拟合多标签文本分类模型时的错误

如何评估我自己的文本分类器

文本分类 CNN 过拟合训练

文本分类:多标签文本分类与多类文本分类

基于朴素贝叶斯分类器的文本分类