ROC曲线:
在逻辑回归里面,我们会设一个阈值,大于这个值的为正类,小于这个值为负类。如果我们减小这个阀值,那么更多的样本会被识别为正类。这会提高正类的识别率,但同时也会使得更多的负类被错误识别为正类。为了形象化这一变化,在此引入 ROC ,ROC 曲线可以用于评价一个分类器好坏。
ROC.png
如何绘制的呢
横坐标: false positive rate (TPR = TP/(TP+FN)) 表示(实际负样本预测为正样本个数)/(实际样本中负样本个数)
例如:3个样本(0.4,0)(0.2,0)(0.8,1) 0 代表负样本 1正样本, 如果设置阈值0.4,则(0.4,0) 实际负样本预测为正样本,(0.2,0)实际负样本预测为负样本 ,(0.8,1)实际正样本预测为正样本,false positive rate=1/2=0.5
纵坐标:true positive rate (FPR = FP/(TN+FP))表示(实际正样本预测为正样本个数)/(实际样本中正样本个数)
上面例子中 true positive rate = 1/1 = 1
详细计算过程
例给出的数据如下
y = np.array([0, 0, 1, 1])
scores = np.array([0.1, 0.4, 0.35, 0.8])
score即各个样本属于正例的概率。
针对score,将数据排序
样本 预测属于P的概率(score) 真实类别
y[0] 0.1 N
y[2] 0.3 P
y[1] 0.4 N
y[3] 0.8 P
阈值依次取score值
依次取值为0.1,0.3,0.4,0.8时,计算ROC曲线横纵坐标的结果。
阈值为0.1
只要score>=0.1,它的预测类别就是正例。
此时,因为4个样本的score都大于等于0.1,所以,所有样本的预测类别都为P。
TPR = TP/(TP+FN) = 1
FPR = FP/(TN+FP) = 1
横纵坐标均为1
阈值为0.3
只要score>=0.3,它的预测类别就是P。
此时,因为4个样本的score有3个大于等于0.3。所以,所有样本的预测类有3个为P(2个预测正确,1一个预测错误),1个样本被预测为N(预测正确)。
TPR = TP/(TP+FN) = 1
FPR = FP/(TN+FP) = 0.5
阈值为0.4
只要score>=0.4,它的预测类别就是P。
此时,因为4个样本的score有2个大于等于0.4。所以,所有样本的预测类有2个为P(1个预测正确,1一个预测错误);2个样本被预测为N(1个预测正确,1一个预测错误)。
TPR = TP/(TP+FN) = 0.5
FPR = FP/(TN+FP) = 0.5
阈值为0.8
说明只要score>=0.8,它的预测类别就是P。所以,所有样本的预测类有1个为P(1个预测正确);3个样本被预测为N(2个预测正确,1一个预测错误)
TPR = TP/(TP+FN) = 0.5
FPR = FP/(TN+FP) = 0
python 实现
#读数据,用db保存带正负标签数据,pos统计正样本个数,neg统计负样本个数
#举例 line 格式 [0.3,0] 则db处理成[0.3,1,0] 第db[1],db[2]位分别代表是否正负样本,为后面get_roc计算准备
def read_file(file_path, accuracy=2):
db = [] #(score,nonclk,clk)
pos, neg = 0, 0 #正负样本数量
#读取数据
with open(file_path,'r') as fs:
for line in fs:
temp = eval(line)
#精度可控
#score = '%.1f' % float(temp[0])
score = float(temp[0])
trueLabel = int(temp[1])
sample = [score, 0, 1] if trueLabel == 1 else [score, 1, 0]
score,nonclk,clk = sample
pos += clk #正样本
neg += nonclk #负样本
db.append(sample)
return db, pos, neg
def get_roc(db, pos, neg):
#按照输出概率,从大到小排序
db = sorted(db, key=lambda x:x[0], reverse=True)
file=open('data.txt','w')
file.write(str(db))
file.close()
#计算ROC坐标点
xy_arr = []
tp, fp = 0., 0.
#已经从大到小排序 i表示以第i个数作为阈值, tp表示第i个数前面实际为正样本预测为正样本个数,fp表示第i个数前面实际为负样本预测为正样本个数
for i in range(len(db)):
tp += db[i][2]
fp += db[i][1]
xy_arr.append([fp/neg,tp/pos])#计算出横纵坐标
return xy_arr
def get_AUC(xy_arr):
#计算曲线下面积
auc = 0.
prev_x = 0
for x,y in xy_arr:
if x != prev_x:
auc += (x - prev_x) * y
prev_x = x
return auc
def draw_ROC(xy_arr):
x = [_v[0] for _v in xy_arr]
y = [_v[1] for _v in xy_arr]
pl.title("ROC curve of %s (AUC = %.4f)" % ('clk',auc))
pl.xlabel("False Positive Rate")
pl.ylabel("True Positive Rate")
pl.plot(x, y)# use pylab to plot x and y
pl.show()# show the plot on the screen
image.png
网友评论