R分类器性能评价:图形方法

  1. 几个基本概念 对于二元分类器,我们可以把分类样本的真实值记为1(positive,正例/阳性),-1(或0,negative,负例/阴性)分类结果记作1(success)和-1(或0,failure)。分类器分类正确,为真(true);分类器分类错误为假(false) 那么分类结果会有四种可能: TP,真正例/真阳性,预测为1且预测正确 TN,真反例/真阴性,预测为-1且预测正确 FP,假正例/假阳性,预测为1且预测错误 FN,假反例/假阴性,预测为-1且预测错误 把上面的这四种结果构造列联表,就得到混淆矩阵(Confusion Matrix) 例:使用caret包的GermanCredit数据。信用卡的评分,包括多个预测变量,其中多数为0-1属性变量。分类为Good和Bad两类。采用logistic回归作为分类器,输出结果是分类为正例的概率。对这个概率取一个阈值(这里取的是1/2),大于阈值的判为正例小于阈值的判为反例。 library(caret) ## Loading required package: lattice ## Loading required package: ggplot2 data(GermanCredit) GermanCredit$Class <- factor(GermanCredit$Class, levels = c("Bad", "Good", 0, 1)) class <- GermanCredit$Class GermanCredit$Class[which(class == "Bad")] <- 0 GermanCredit$Class[which(class == "Good")] <- 1 GermanCredit$Class <- factor(GermanCredit$Class, levels = c(0, 1)) # remove near-zero variance predictors then get rid of a few predictors that # duplicate values GermanCredit <- GermanCredit[, -nearZeroVar(GermanCredit)] GermanCredit$CheckingAccountStatus.lt.0 <- NULL GermanCredit$SavingsAccountBonds.lt.100 <- NULL GermanCredit$EmploymentDuration.lt.1 <- NULL GermanCredit$EmploymentDuration.Unemployed <- NULL GermanCredit$Personal.Male.Married.Widowed <- NULL GermanCredit$Property.Unknown <- NULL GermanCredit$Housing.ForFree <- NULL # create training amd prediction datasets set.seed(1) train <- sample(1:1000, 900) credit.train <- GermanCredit[train, ] credit.test <- GermanCredit[-train, ] credit.glm <- glm(Class ~ ., family = binomial, data = credit.train) # Now to prediction credit.p <- predict(credit.glm, newdata = credit.test, type = "response") credit.n <- data.frame(credit.test$Class, credit.p) head(credit.n) ## credit.test.Class credit.p ## 16 0 0.4874 ## 18 1 0.1227 ## 19 0 0.5552 ## 26 1 0.8185 ## 27 1 0.8539 ## 43 1 0.8482 # set cutoff=1/2 cut = 1/2 Class.p <- floor(credit.p + cut) confusion <- table(credit.test$Class, Class.p) # confunsion matrix confusion ## Class.p ## 0 1 ## 0 15 13 ## 1 10 62 记全部样本个数为T,正例个数为P,负例个数为N。 可以计算各种比率,其中TPR=TP/P称为灵敏度(sensitivity)/召回率(recall), TNR=TN/N称为特指度(specicity),FPR=FP/P称误警率(Fallout),FNR=FP/N称为漏查率(miss). 分类器预测正确的比例称正确率(accuracy):(TP+TN)/T 分类器预测错误的比例称错误率(error rate):(FP+FN)/T TP <- confusion[4] TN <- confusion[3] FP <- confusion[2] FN <- confusion[1] N <- sum(credit.test$Class == 0) P <- sum(credit.test$Class == 1) TPR <- TP/P TNR <- TN/N FPR <- FP/P FNR <- FP/N ACC <- (sum(TN) + sum(TP))/sum(confusion) ERR <- (sum(FN) + sum(FP))/sum(confusion) 很多时候,我们更关心正例的分类精确与否,因此可以定义下面两个比率: TPR又可称为查全率,表示正确分类的正例占实际正例(TP/(TP+FN))的比例,用于衡量分类器预测正例的可信程度。 相对应的概念有查准率(precision),表示正确分类的正例占全部预测正例的比例(TP/(TP+FP))。 ROC和AUC 对于更关注于正例的情况,ROC(Receiver Operating Characteristic)是很常用的一种图形评价方法。 ROC曲线使用了上面定义的两种比率,灵敏度和误警率。 sensitivity(TPR):正确分类的正例数占实际正例的比例; specicity(TNR):正确分类的反例数占实际反例的比例; 1-specicity=FPR,错误分类的反例(“报警”)占实际反例的比例。 ROC曲线描述的是二元分类器TPR和FPR的相对变化情况。 如果二元分类模型输出的是分类为正例的概率,那么设置分类一个阈值就可以计算相应阈值下的sensitivity和1-specicity。如果取一组阈值,把对每个阈值计算得到的sensitivity和1-specicity绘制在图中,就得到ROC曲线。ROC曲线表示在尽量少的误判的基础上,尽可能多的判出正例的个体。 roc <- function(p, y, n) { y <- factor(y) l <- length(p) criteria <- p > matrix(rep(seq(1, 0, length = n), l), ncol = n, byrow = T) fpr <- colSums((y == levels(y)[1]) * criteria)/sum(y == levels(y)[1]) tpr <- colSums((y == levels(y)[2]) * criteria)/sum(y == levels(y)[2]) roc <- data.frame(tpr, fpr) } roc.result <- roc(credit.p, credit.test$Class, 100) library(ggplot2) ggplot(roc.result, aes(x = fpr, y = tpr)) + geom_line(color = "blue") + geom_segment(aes(x = 0, y = 0, xend = 1, yend = 1), color = "grey", lty = 2)

如果ROC曲线可以经过点(0,1),即特指度和灵敏度都是1,那么就是一个最优的分类器。但是绝大多数分类器做不到这一点。因此,引入AUC:ROC曲线下的面积来度量不同分类器的表现。AUC越大,则分类性能越好。

3.提升度和提升曲线 分类器分类为正例的比例称为深度(depth):(TP+FP)/T,T是全部待判样本数量。 提升度(lift)等于TPR/depth 以深度为横轴,以提升度为纵轴绘制曲线,得到提升曲线。 绘制提升曲线的思路和ROC类似。当阈值为0的时候,所有的样本都会判为正例,此时深度为1,提升为1;随阈值增大,深度减小,提升随之逐渐增大。一个好的模型要在大的深度下得到尽量大的提升。 lift.c <- function(p, y, n) { y <- factor(y) l <- length(p) criteria <- p > matrix(rep(seq(1, 0, length = n), l), ncol = n, byrow = T) fp <- colSums((y == levels(y)[1]) * criteria) tp <- colSums((y == levels(y)[2]) * criteria) fpr <- fp/sum(y == levels(y)[1]) tpr <- tp/sum(y == levels(y)[2]) depth <- (tp + fp)/length(y) tpr <- tpr[depth != 0] depth <- depth[depth != 0] lift <- tpr/depth lift.c <- data.frame(depth, lift) } lift.result <- lift.c(credit.p, credit.test$Class, 100) ggplot(lift.result, aes(x = depth, y = lift)) + geom_line(color = "blue")

提升曲线常用于市场营销。一个经常在各种书籍中被举出的例子是邮寄广告促销(现在是短信?)。只有很少的人(潜在客户)会响应这种促销,因此对大量人群的进行邮寄广告促销会有较高的成本。因此通过分类器寻找可能会积极响应的客户,其中的潜在客户的比例提升了。 其他图形化方法还有 precision/recall曲线,洛伦兹曲线等。 4.ROCR包 图形方法(特别是ROC)是在机器学习/数据挖掘中用来评价模型的重要方法。在R当中,有多个package可用来绘制相应的图形。其中最常用的一个当属ROCR包,可用于绘制ROC曲线和提升曲线。 使用ROCR包来绘制上面的ROC曲线,并计算AUC值 library(ROCR) pre <- prediction(credit.p, credit.test$Class) plot(performance(pre, "tpr", "fpr"), main = "ROC CURVE", colorize = T) performance(pre, measure = "auc")@y.values ## [1] ## [1] 0.7902

其中prediction函数产生预测对象,performance函数可以计算对预测对象的各种评价方法(结果是个S4类)。使用plot函数可以绘制ROC曲线,colorize=T表示可以按颜色在图形上表示出阈值的分布。 绘制提升曲线 plot(performance(pre, "lift", "rpp"), main = "LIFT CURVE", colorize = T)

R的Daim包和pROC包也可以绘制ROC曲线

(原文:http://site.douban.com/182577/widget/notes/10567212/note/348006411/)

原文发布于微信公众号 - 大数据挖掘DT数据分析(datadw)

原文发表时间:2016-02-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

objective-C 的OOP(上)--类定义、继承及方法调用

上一篇展示了如何用传统的“面向过程编程方法”,实现画“矩形”、“圆”、“椭圆”,这一篇看下如何改用OOP的方法来实现: ? 因为要用到“颜色”以及“矩形区域”二...

1568
来自专栏Golang语言社区

【Go 语言社区】Golang 语言再谈接口

Go编程提供所谓的接口是另一种数据类型,代表了一组方法签名。结构数据类型实现这些接口对接口的方法签名,并其实现方法具体定义。 Syntax /* define ...

33315
来自专栏mukekeheart的iOS之旅

iOS学习——布局利器Masonry框架源码深度剖析

  iOS开发过程中很大一部分内容就是界面布局和跳转,iOS的布局方式也经历了 显式坐标定位方式 --> autoresizingMask --> iOS 6....

3989
来自专栏一“技”之长

Masonry源码解析 原

    Masonry的核心依然是使用原生的NSLayoutConstraint类来进行添加约束,通过统一的封装和链式函数式编程的方式让开发者添加约束布局更加方...

844
来自专栏移动开发面面观

iOS自动布局——Masonry详解

1452
来自专栏海天一树

小朋友学C++(10):子类构造函数调用父类构造函数

从哲学层面来看,子类会继承父类除private以外的所有成员。 因为构造函数是公有的,所以理所当然地会被子类继承。 程序1: #include <iostrea...

2686
来自专栏鹅厂优文

iOS自动布局——Masonry详解

UI布局是整个前端体系里不可或缺的一环。代码的布局是设计语言与用户视觉感受沟通的桥梁,不论它看起来多么简单或是琐碎,但不得不承认,绝大部分软件开发的问题,都是界...

8088
来自专栏菩提树下的杨过

java中的tuple实现

java中没有类似c#、scala中的tuple元组类,只能自己动手,丰衣足食了,见下面的代码: Tuple 抽象类 1 import java.util.O...

1785
来自专栏上善若水

014 linux 命令行工具jq

795
来自专栏一个会写诗的程序员的博客

On the Rise of Kotlin

It’s rare when a highly structured language with fairly strict syntax sparks emo...

542

扫码关注云+社区