我刚开始学习机器学习,目前正在尝试在Python 3.4中实现ROC曲线,并将其应用于朴素贝叶斯分类器。分类器的实际代码如下所示:
from __future__ import divisionfrom collections import defaultdictfrom math import logdef train(samples): classes, freq = defaultdict(lambda:0), defaultdict(lambda:0) for feats, label in samples: classes[label] += 1 # 统计类别频率 for feat in feats: freq[label, feat] += 1 # 统计特征频率 for label, feat in freq: # 归一化特征频率 freq[label, feat] /= classes[label] for c in classes: # 归一化类别频率 classes[c] /= len(samples) return classes, freq # 返回P(C)和P(O|C)def classify(classifier, feats): classes, prob = classifier return min(classes.keys(), # 计算argmin(-log(C|O)) key = lambda cl: -log(classes[cl]) + \ sum(-log(prob.get((cl,feat), 10**(-7))) for feat in feats))
例如,我有一些包含姓名和性别关联的数据,我希望将我的分类器应用于这种数据,以预测给定姓名的性别。以下是更多代码:
def get_features(sample): return (sample[-1],) # 获取最后一个字母samples = (line.split() for line in open('names.txt'))features = [(get_features(feat), label) for feat, label in samples]classifier = train(features)print 'gender: ', classify(classifier, get_features('Mary'))
好的,所以我在构建ROC曲线时遇到了困难。可能是因为我对分类器的一些基本概念理解错误,实际上我感到非常失望。使用我的分类器,我可以预测给定姓名的’类别’,作为上述代码中所写的(-log((C|O)))的值的argmin,因此classify
函数在调用时会搜索对于给定姓名的所有特征,使得对数值最小的那一类——这正是朴素贝叶斯分类器的定义中所指定的。
接下来,我想为这个分类器构建ROC曲线,但问题是我的classify
函数返回一个二进制值,实际上是通过计算我之前提到的argmin
来显示给定人的预测性别。我需要一种threshold
值,必须将其与classify
函数的结果进行比较,以便绘制ROC曲线,这是一个可以进行+/-
调整以获得多个(TPR, FPR)点的阈值。
请帮助我解决这种不幸的误解,以便我能够构建我的ROC曲线。
回答:
接收者操作特征(ROC)用于展示二元(两类)分类器的性能。因此,您需要将自己限制在两个类别(X vs. Y或X vs.非X)以生成单一曲线(但您可以重复为其他类别对生成曲线)。
与其寻找-log prob(C|O)最小的那一类C,您可以使用值prob(C1|O)(假设它们在数据样本的行之间以相同的方式归一化)。
然后,您可以扫描阈值t,并决定如果prob(C1|O) >= t,则将一行分类为属于类别C1。
对于每个t,您可以计算
- 真正率:实际上属于类别C1且因为prob(C1|O) >= t而被分类为属于C1的行的比例
- 假正率:实际上不属于类别C1但因为prob(C1|O) >= t而被分类为属于C1的行的比例
在实践中,您只需在数据样本的行上测试您得到的prob(C1|O)值作为t(在您的例子中,我预计会得到类似2^(特征数量)的不同值)。