如何为 scikit-learn 分类器获得最具信息性的特性?

机器学习包中的分类器,比如 lilinear 和 nltk,提供了一种方法 show_most_informative_features(),这对于调试特性非常有帮助:

viagra = None          ok : spam     =      4.5 : 1.0
hello = True           ok : spam     =      4.5 : 1.0
hello = None           spam : ok     =      3.3 : 1.0
viagra = True          spam : ok     =      3.3 : 1.0
casino = True          spam : ok     =      2.0 : 1.0
casino = None          ok : spam     =      1.5 : 1.0

我的问题是,在 scikit-learn 中是否实现了类似的分类器。我查了文件,但没找到类似的东西。

如果还没有这样的函数,是否有人知道如何得到这些值的变通方法?

59372 次浏览

分类器本身不记录特性名称,它们只看到数字数组。然而,如果你使用 Vectorizer/CountVectorizer/TfidfVectorizer/DictVectorizer还有来提取你的特征,你就是在使用线性模型(例如 LinearSVC或朴素贝叶斯) ,那么你可以应用 文档分类的例子使用的同样的技巧。示例(未经测试,可能包含一两个 bug) :

def print_top10(vectorizer, clf, class_labels):
"""Prints features with the highest coefficient values, per class"""
feature_names = vectorizer.get_feature_names()
for i, class_label in enumerate(class_labels):
top10 = np.argsort(clf.coef_[i])[-10:]
print("%s: %s" % (class_label,
" ".join(feature_names[j] for j in top10)))

这是针对多元分类的,对于二进制情况,我认为应该只使用 clf.coef_[0]。您可能需要对 class_labels进行排序。

在 Larsmans 代码的帮助下,我想出了这个二进制代码:

def show_most_informative_features(vectorizer, clf, n=20):
feature_names = vectorizer.get_feature_names()
coefs_with_fns = sorted(zip(clf.coef_[0], feature_names))
top = zip(coefs_with_fns[:n], coefs_with_fns[:-(n + 1):-1])
for (coef_1, fn_1), (coef_2, fn_2) in top:
print "\t%.4f\t%-15s\t\t%.4f\t%-15s" % (coef_1, fn_1, coef_2, fn_2)

RandomForestClassifier还没有 coef_的属性,但是我认为它会在0.17版本中发布。但是,请参阅 基于 scikit-learn 的随机森林递归特征消除中的 RandomForestClassifierWithCoef类。这可能会给你一些想法来绕过上面的限制。

你也可以这样做,按顺序创建一个重要特征的图表:

importances = clf.feature_importances_
std = np.std([tree.feature_importances_ for tree in clf.estimators_],
axis=0)
indices = np.argsort(importances)[::-1]


# Print the feature ranking
#print("Feature ranking:")




# Plot the feature importances of the forest
plt.figure()
plt.title("Feature importances")
plt.bar(range(train[features].shape[1]), importances[indices],
color="r", yerr=std[indices], align="center")
plt.xticks(range(train[features].shape[1]), indices)
plt.xlim([-1, train[features].shape[1]])
plt.show()

要添加更新,RandomForestClassifier现在支持 .feature_importances_属性。这个 属性告诉你有多少观察到的方差是由这个特征解释的。显然,所有这些值的总和必须是 < = 1。

我发现这个属性在执行特性工程时非常有用。

感谢 scikit-learn 团队和贡献者实现了这一点!

编辑: 这适用于随机森林和梯度增强。所以 RandomForestClassifierRandomForestRegressorGradientBoostingClassifierGradientBoostingRegressor都支持这个。

我们最近发布了一个库(https://github.com/TeamHG-Memex/eli5) ,它允许这样做: 它处理 scikit-learn 中的多种分类器、二进制/多类情况、允许根据特征值突出显示文本、与 IPython 集成等等。

实际上,我必须在我的奈维贝叶斯分类器上找到特征重要性,尽管我使用了上面的函数,但是我不能根据类得到特征重要性。我浏览了 scikit-learn 的文档,稍微调整了一下上面的函数,发现它可以解决我的问题。希望对你也有帮助!

def important_features(vectorizer,classifier,n=20):
class_labels = classifier.classes_
feature_names =vectorizer.get_feature_names()


topn_class1 = sorted(zip(classifier.feature_count_[0], feature_names),reverse=True)[:n]
topn_class2 = sorted(zip(classifier.feature_count_[1], feature_names),reverse=True)[:n]


print("Important words in negative reviews")


for coef, feat in topn_class1:
print(class_labels[0], coef, feat)


print("-----------------------------------------")
print("Important words in positive reviews")


for coef, feat in topn_class2:
print(class_labels[1], coef, feat)


请注意,您的分类器(在我的例子中是奈维贝叶斯)必须具有 Feature _ count _ 属性才能正常工作。

这并不是你想要的,而是一种获得最大幅度系数的快速方法(假设熊猫数据框列是你的特征名称) :

你把模特训练成这样:

lr = LinearRegression()
X_train, X_test, y_train, y_test = train_test_split(df, Y, test_size=0.25)
lr.fit(X_train, y_train)

获取10个最大的负系数值(或者对最大的正值改为反向 = 真) ,如下所示:

sorted(list(zip(feature_df.columns, lr.coef_)), key=lambda x: x[1],
reverse=False)[:10]

首先制作一个列表,我给这个列表的名称标签。然后提取所有特性名称和列名称,添加到标签列表中。这里我使用天真的贝叶斯模型。在朴素贝叶斯模型中,特征 _ log _ prob _ 给出了特征的概率。

def top20(model,label):


feature_prob=(abs(model.feature_log_prob_))


for i in range(len(feature_prob)):


print ('top 20 features for {} class'.format(i))


clas = feature_prob[i,:]


dictonary={}


for count,ele in enumerate(clas,0):


dictonary[count]=ele


dictonary=dict(sorted(dictonary.items(), key=lambda x: x[1], reverse=True)[:20])


keys=list(dictonary.keys())


for i in keys:


print(label[i])


print('*'*1000)