在sklearn python中处理逻辑回归分类器中的极端不平衡多类

Posted

技术标签:

【中文标题】在sklearn python中处理逻辑回归分类器中的极端不平衡多类【英文标题】:Dealing with extreme unbalanced multiclasses in logistic regression classifier in sklearn python 【发布时间】:2016-01-15 01:32:55 【问题描述】:

我想用 100000 对一些标签(10 个类别)进行分类。但数据存在极端不平衡性,例如,两个类别各占整体数据的 30%,而有些类别约为 0.01%。因此我使用 lr = LogisticRegression(class_weight="auto") 而不是 lr = LogisticRegression()。我发现我的准确率和召回率测量执行得更差(准确率:78% 召回率:64% 到准确率:62% 召回率:57%),添加 class_weight="auto" 后,这是常见的还是我做错了什么?

# coding=utf-8
import pandas as pd
from pandas import DataFrame, Series
import numpy as np
import nltk
import re
import random
from random import randint
import csv
import dask.dataframe as dd
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction import DictVectorizer
from sklearn.preprocessing import Imputer

lr = LogisticRegression(class_weight="auto")
dv = DictVectorizer()
imp = Imputer(missing_values='NaN', strategy='most_frequent', axis=0)

# Get csv file into data frame
data = pd.read_csv("file.csv", header=0, encoding="utf-8")
df = DataFrame(data)

# Random sampling a smaller dataframe for debugging
rows = random.sample(df.index, 100000)
df = df.ix[rows] # Warning!!!! overwriting original df

# Assign X and y variables
X = df.raw_name.values
y = df.ethnicity2.values

# Feature extraction functions
def feature_full_last_name(nameString):
    try:
        last_name = nameString.rsplit(None, 1)[-1]
        if len(last_name) > 1: # not accept name with only 1 character
            return last_name
        else: return '?'
    except: return '?'

# Transform format of X variables, and spit out a numpy array for all features
my_dict = ['last-name': feature_full_last_name(i) for i in X]

all_dict = my_dict

newX = dv.fit_transform(all_dict).toarray()

# Separate the training and testing data sets
half_cut = int(len(df)/2.0)*-1
X_train = newX[:half_cut]
X_test = newX[half_cut:]
y_train = y[:half_cut]
y_test = y[half_cut:]

# Fitting X and y into model, using training data
lr.fit(X_train, y_train)

# Making predictions using trained data
y_train_predictions = lr.predict(X_train)
y_test_predictions = lr.predict(X_test)

print (y_train_predictions == y_train).sum().astype(float)/(y_train.shape[0])
print (y_test_predictions == y_test).sum().astype(float)/(y_test.shape[0])

编辑输出:

Frequent label      
           w/auto   w/o auto
Error rate  0.22866 0.186724
Accuracy    0.77134 0.813276
Precision   0.921246774 0.854109238
Recall  0.511857815 0.636206455


Infrequent label    
           w/auto   w/o auto
Error rate  0.098096    0.007652
Accuracy    0.901904    0.992348
Precision   0.995609966 0.992641816
Recall  0.047821338 0.780346821

【问题讨论】:

【参考方案1】:

@Tchotchke 是对的,但我会尝试用其他方式解释这一点:

您应该考虑数据集中类的一般分布。您的数据集不平衡是因为采样错误(有人刚刚删除了包含某些类的数据集的一部分),还是因为真实世界的类发生概率?

您可以在这两种情况下更改班级权重。在第一种情况下,您可以更改权重以固定分布,但在第二种情况下,您应该知道更改类和样本频率可能会影响决策边界,因为每个估计器都会考虑每个类的发生概率。 如果你偏离真实世界的类概率分布,你通常会得到一个糟糕的分类器,因为某些类出现的概率也是数据集的一部分,它是有用的信息。因此,在大多数情况下,只有当您对数据集的公平性有疑问并且想要纠正它时,您才应该更改类/样本的权重。

但在某些情况下,即使您的数据集捕获了类之间的真实分布,您也可以更改某些类的权重。例如,如果您想对某人是否患有癌症进行分类。事实证明,如果您从某些人群中采样数据集,只有极少数人会患癌症,但在此任务中,如果分类器不确定实际标签,即此人可能健康,最好将人标记为生病,但是最好将他归类为病态并进行额外的测试以做出最终决定,而不是给他贴上健康的标签,如果它患有癌症,现在他会认为他是健康的。因此,通过更改类别权重,您将改变类别分布,癌变样本的权重总和与健康样本的权重总和相同。在此类数据集上训练的分类器将在更多情况下将健康人标记为生病(更多误报),但它会将生病的人标记为健康人很少,这更重要。分类的整体准确度下降,但谁在乎呢?我们的任务是检测所有病人,而不是检测某个人是否生病或健康。

【讨论】:

【参考方案2】:

对于您描述的多类分类问题,您将给予非常小的少数类很大的权重 - 因此该模型将有利于获得那些少数的观察结果而不是获得其他人口更多的类正确。

这种情况的副作用可能是您所看到的,整体精度和召回率下降。但是,我怀疑您在预测那些低观测类方面做得更好。

因此,如果您真的比其他类更关心预测这些低观测类,那么您只需要使用class_weight 选项。

【讨论】:

嗨,我已经运行了一些测试并添加到上面的“已编辑”部分。对于频繁和非频繁标签,添加“自动”会稍微提高精度。但他们的召回措施尤其受到非频繁标签的影响。是否有使用“自动”的经验法则?看起来没有“自动”他们做得更好。

以上是关于在sklearn python中处理逻辑回归分类器中的极端不平衡多类的主要内容,如果未能解决你的问题,请参考以下文章

逻辑回归推导

在 sklearn 逻辑回归中使用分类数据作为特征

Sklearn 逻辑分类器的 L1 和 L2 惩罚

python:如何在sklearn中使用逻辑回归系数构建决策边界

Python SKLearn 逻辑回归中的虚拟变量

具有不平衡类的sklearn逻辑回归