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

时间:2015-10-16 10:20:55

标签: python-2.7 machine-learning scikit-learn sampling logistic-regression

我想使用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

2 个答案:

答案 0 :(得分:3)

由于你所描述的多类别分类问题,你将会给极少数类别的少数群体带来很大的影响 - 因此该模型有利于将这些少数观察权交给另一个,人口更多,课程正确。

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

因此,如果您真的关心预测那些低观察类别而不是其他类别,那么您只想使用class_weight选项。

答案 1 :(得分:0)

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

您应该考虑数据集中类的一般分布。您的数据集是否由于采样不良而导致数据集不平衡(有人刚刚删除了某些类的数据集的一部分),还是因为实际发生类的概率?

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

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