UserWarning:标签不是:NUMBER:出现在所有培训示例中

时间:2017-03-15 21:48:18

标签: python scikit-learn classification text-classification multilabel-classification

我正在进行多标签分类,我尝试为每个文档预测正确的标签,这是我的代码:

mlb = MultiLabelBinarizer()
X = dataframe['body'].values 
y = mlb.fit_transform(dataframe['tag'].values)

classifier = Pipeline([
    ('vectorizer', CountVectorizer(lowercase=True, 
                                   stop_words='english', 
                                   max_df = 0.8, 
                                   min_df = 10)),
    ('tfidf', TfidfTransformer()),
    ('clf', OneVsRestClassifier(LinearSVC()))])

predicted = cross_val_predict(classifier, X, y)

运行我的代码时,我收到多个警告:

UserWarning: Label not :NUMBER: is present in all training examples.

当我打印出预测标签和真实标签时,所有文件的一半都有标签为空的预测。

为什么会发生这种情况,是否与培训运行期间打印出的警告有关?我怎样才能避免那些空洞的预测呢?

<小时/> 的 EDIT01: 使用除LinearSVC()以外的其他估算工具时也会发生这种情况。

我已经尝试了RandomForestClassifier(),它也提供了空预测。奇怪的是,当我使用cross_val_predict(classifier, X, y, method='predict_proba')来预测每个标签的概率而不是二元决策0/1时,每个预测集总是至少有一个标签,其概率大于1。给定文件为0。所以我不知道为什么这个标签没有选择二元决策?或者是以不同于概率的方式评估二元决策?

EDIT02: 我找到了一个旧的post,其中OP处理类似的问题。这是同样的情况吗?

1 个答案:

答案 0 :(得分:7)

  

为什么会发生这种情况,是否与培训运行时打印出的警告有关?

问题可能是某些标签仅出现在少数文档中(有关详细信息,请查看this thread)。将数据集拆分为train并进行测试以验证模型时,可能会发生训练数据中缺少某些标记的情况。设train_indices为具有训练样本索引的数组。如果训练样本中未出现特定标记(索引k),则指标矩阵k的{​​{1}}列中的所有元素都为零。

  

如何避免这些空洞的预测?

在上述场景中,分类器将无法可靠地预测测试文档中的y[train_indices]标记(下一段中将详细介绍)。因此,您无法信任k所做的预测,您需要自己实现预测功能,例如使用clf.predict中建议的clf.decision_function返回的决策值。

  

所以我不知道为什么这个标签没有选择二元决策?或者是以不同于概率的方式评估二元决策?

在包含许多标签的数据集中,大多数标签的出现频率相当低。如果将这些低值提供给二元分类器(即进行0-1预测的分类器),则分类器极有可能为所有文档上的所有标记选择0。

  

我找到了一个老职位,其中OP处理类似的问题。这是同样的情况吗?

是的,绝对的。那家伙面临着与你完全相同的问题,他的代码与你的代码非常相似。

<强> 演示

为了进一步解释这个问题,我使用模拟数据详细阐述了一个简单的玩具示例。

Q = {'What does the "yield" keyword do in Python?': ['python'],
     'What is a metaclass in Python?': ['oop'],
     'How do I check whether a file exists using Python?': ['python'],
     'How to make a chain of function decorators?': ['python', 'decorator'],
     'Using i and j as variables in Matlab': ['matlab', 'naming-conventions'],
     'MATLAB: get variable type': ['matlab'],
     'Why is MATLAB so fast in matrix multiplication?': ['performance'],
     'Is MATLAB OOP slow or am I doing something wrong?': ['matlab-oop'],
    }
dataframe = pd.DataFrame({'body': Q.keys(), 'tag': Q.values()})    

mlb = MultiLabelBinarizer()
X = dataframe['body'].values 
y = mlb.fit_transform(dataframe['tag'].values)

classifier = Pipeline([
    ('vectorizer', CountVectorizer(lowercase=True, 
                                   stop_words='english', 
                                   max_df=0.8, 
                                   min_df=1)),
    ('tfidf', TfidfTransformer()),
    ('clf', OneVsRestClassifier(LinearSVC()))])

请注意,我已设置min_df=1,因为我的数据集比您的数据集小得多。当我运行以下句子时:

predicted = cross_val_predict(classifier, X, y)

我收到一堆警告

C:\...\multiclass.py:76: UserWarning: Label not 4 is present in all training examples.
  str(classes[c]))
C:\\multiclass.py:76: UserWarning: Label not 0 is present in all training examples.
  str(classes[c]))
C:\...\multiclass.py:76: UserWarning: Label not 3 is present in all training examples.
  str(classes[c]))
C:\...\multiclass.py:76: UserWarning: Label not 5 is present in all training examples.
  str(classes[c]))
C:\...\multiclass.py:76: UserWarning: Label not 2 is present in all training examples.
  str(classes[c]))

以及以下预测:

In [5]: np.set_printoptions(precision=2, threshold=1000)    

In [6]: predicted
Out[6]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])

条目全部为0的那些行表示没有预测相应文档的标记。

<强> 解决方法

为了便于分析,让我们手动验证模型,而不是通过cross_val_predict

import warnings
from sklearn.model_selection import ShuffleSplit

rs = ShuffleSplit(n_splits=1, test_size=.5, random_state=0)
train_indices, test_indices = rs.split(X).next()

with warnings.catch_warnings(record=True) as received_warnings:
    warnings.simplefilter("always")
    X_train, y_train = X[train_indices], y[train_indices]
    X_test, y_test = X[test_indices], y[test_indices]
    classifier.fit(X_train, y_train)
    predicted_test = classifier.predict(X_test)
    for w in received_warnings:
        print w.message

当执行上面的代码片段时,会发出两个警告(我使用上下文管理器确保警告被捕获):

Label not 2 is present in all training examples.
Label not 4 is present in all training examples.

这与训练样本中缺少指数24的标签的事实一致:

In [40]: y_train
Out[40]: 
array([[0, 0, 0, 0, 0, 1, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 1, 0, 1, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 1]])

对于某些文档,预测为空(那些文档对应于predicted_test中全零的行):

In [42]: predicted_test
Out[42]: 
array([[0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 1, 0, 0, 0]])

要解决这个问题,您可以像这样实现自己的预测函数:

def get_best_tags(clf, X, lb, n_tags=3):
    decfun = clf.decision_function(X)
    best_tags = np.argsort(decfun)[:, :-(n_tags+1): -1]
    return lb.classes_[best_tags]

通过这样做,始终为每个文档分配具有最高置信度分数的n_tag标记:

In [59]: mlb.inverse_transform(predicted_test)
Out[59]: [('matlab',), (), (), ('matlab', 'naming-conventions')]

In [60]: get_best_tags(classifier, X_test, mlb)
Out[60]: 
array([['matlab', 'oop', 'matlab-oop'],
       ['oop', 'matlab-oop', 'matlab'],
       ['oop', 'matlab-oop', 'matlab'],
       ['matlab', 'naming-conventions', 'oop']], dtype=object)