查看原文
其他

干货--线性回归模型与CART树的比较

2017-02-07 刘顺祥 每天进步一点点2015

在上两期的《浅谈C5.0与CART算法的比较--理论理解》和《干货--C5.0与CART算法实战》中,介绍了C5.0和CART两种树模型的理论和实战。其中,讲到CART算法既可以解决分类问题,也能够解决回归问题,那今天我们就讲讲如何使用CART算法实现回归问题的解决,同时也与线性回归做个比较。


有关CART算法的理论这里不再赘述,可参考《浅谈C5.0与CART算法的比较--理论理解》,线性回归的理论部分也不过多讲解,可以参考我之前写的文章《R语言下的线性回归模型》和《基于R语言的线性回归模型诊断》。但我需要强调的是,线性回归模型的够着需要满足几个前提假设,即:

1)残差项的期望为0,标准差为sigma的正态分布;

2)残差项之间是独立的;

3)残差项的方差应该满足齐性;

4)Y变量与X变量之间存在线性关系;

5)X变量之间不存在多重共线性;

之所以需要这些假设,是因为线性回归的参数确定是通过最小二乘法得到的。如果线性回归的这些前提假设不能得到保证,那出来的结果也是一种伪回归,故需要针对这些假设前提给予相应的解决办法。


线性回归之5个前提假设的解决办法分别是:

1)对Y变量作变换,如倒数化、对数化、开根号等,一般可通过BOX-COX变换直接处理;

2)对于非时间序列,一般认为样本间是独立的,而针对时间序列的样本,需要进行杜宾瓦森检验,如果数据不满足独立性的话,可通过Y变量进行变换,类似于1)中的方法;

3)方差齐性的检验可以使用巴特莱特检验法,如果不满足方差齐性,仍然需要对Y变量进行变换,类似于1)中的方法;

4)对于线性关系的检验,可以通过皮尔逊检验,发现Y变量与各个X变量之间的相关系数,或者通过成分残差图来判断;

5)方差膨胀因子可以鉴别X变量之间是否存在多重共线性,对于高度多重共线性的变量要进行适当的删除;


实战

接下来我们就以实战的方式来比较这两种用于解决回归问题的算法,数据来源于Kaggle网站(https://www.kaggle.com/harlfoxem/housesalesprediction),该数据集的目的是用来预测房价。首先,我们来了解一下该数据集:

# 加载所需的第三方包

library(GGally)

library(car)

library(rpart)

# 读取数据,并进行探索性数据分析

house <- read.csv(file.choose())

# 查看数据属性

dim(house)

names(house)

该数据集一共包含了21613个观测和21个变量,这些变量包含了房子的价格、卧室数量、洗手间数量、房子的面积、楼层、建筑时间、是否重修等。这么多的变量,到底哪些变量跟房价具有更高的相关性呢


这里先删除一些对Y变量没有实质意义的自变量,如id、date、zipcode、lat、long。

# 剔除不需要的变量

house.new <- subset(house, select = -c(id,date,zipcode,lat,long))


计算房价与其余变量的相关系数,由于R自带的cor函数返回的是所有变量之间的两两相关系数,我们这里不妨自定义一个函数,仅探索Y变量与其余自变量之间的相关系数:

# 构造自定义函数

mycorr <- function(df, y.index = NULL, y.names = NULL){

  if(is.null(y.index) == TRUE)

    index = which(names(df) == y.names)

  else

    index = y.index

  # 仅提取出Y变量与自变量的相关系数

  test = cor(df)[-index, index]

  # 将test结果转化为矩阵

  res = matrix(test, dimnames = list(names(df)[-index], 

                                     names(df)[index]))

  # 将矩阵转化为数据框

  corr = data.frame(res)

  # 给数据框添加变量名,存放各个自变量

  corr$variables = row.names(corr)

  # 删除数据框的行名称

  row.names(corr) = NULL

  # 将相关系数降序排序

  corr = corr[order(corr[,1],decreasing = TRUE),]

  # 返回结果

  return(corr)

}


mycorr(df = house.new,y.names = 'price')

发现相关性较高的是sqft_living、grade、sqft_above、sqft_living15和bathrooms五个变量,接下来我们就利用这5个变量来做多元线性回归分析。


# 筛选相关的变量

house.model <- subset(house.new, 

                      select = c(price,sqft_living,

                                 grade,sqft_above,

                                 sqft_living15,bathrooms))


接下来我们先探索房价的分布情况,看看其是否服从正态分布(一个前提假设,等价于残差项服从正态分布)。

# 绘制房价的直方图

hp <- hist(house.new$price, 

           breaks = (max(house.new$price) - min(house.new$price))/100000, 

           freq = FALSE,

           col = 'steelblue', xlab = '房价', ylab = '频率',

           main = '房价的直方图')

lines(density(house.new$price), 

      col = 'red', lty = 2, lwd = 2)

x <- seq(min(house.new$price), 

         max(house.new$price), length = 1000)

y <- dnorm(x, mean = mean(house.new$price), 

           sd = sd(house.new$price))

lines(x = x, y = y, col = 'green', lty = 2, lwd = 2)


# 添加图例

legend(locator(), legend = c('核密度曲线','正态分布曲线'), 

       lty = c(2,2), lwd = c(2,2), 

       col = c('red','green'), bty = 'n')

很显然房价并不服从正态分布,后面需要对数据进行BOX-COX变换,以满足线性回归的前提假设。


接下来再来看看各个变量之间的详细情况:

ggpairs(house.model)

这个矩阵图的对角线反应了某个变量的核密度图,即实际的分布情况;下三角反应的是某两个变量间的散点图;上三角反应的是某两个变量间的相关系数。


在探索完Y变量和自变量后,我们进一步开始线性模型的搭建,具体可见下方:

# 构建多元线性回归模型,并对模型的前提假设做检验

# 将数据集拆分为训练集和测试集

set.seed(1234)

index <- sample(1:nrow(house.model), 

                size = 0.75*nrow(house.model))

train <- house.model[index,]

test <- house.model[-index,]


# 建模

fit1 <- lm(price ~ ., data = train)

summary(fit1)

针对上方的模型反馈结果,我们大致解释一下:模型中的各个自变量的参数都是通过显著性检验的,同时模型的显著性检验也满足。但并不代表模型是OK的,因为可能存在伪回归,这就需要我们对模型的假设前提作一一的检验。


# 模型变量间的多重共线性检验

vif(fit1)

这个结构能说明什么呢?根据先验知识,当0<VIF<10,不存在多重共线性;当10≤VIF<100,存在较强的多重共线性;当VIF≥100,存在严重多重共线性。所有,这5个自变量间并不存在较强的多重共线性。


# 残差的正态性检验

# 由于样本量超过5000,故我们使用K-S检验

ks.test(fit1$residuals, 'pnorm', 

        mean(fit1$residuals),

        sd(fit1$residuals))

上图也同样说明了模型的残差不服从正态性检验,那么该如何处理呢?接下来我们采用BOX-COX检验对Y变量进行变换。

powerTransform(fit1)

也许这里的结果你并不明白,我们先把核对表摆出来,你大致就可以搞清楚了:

根据变换核对表,我们发现-0.0043更接近于0,故只需对Y变量作对数变换即可。


# 变换并重新建模

fit2 <- lm(log(price) ~ ., data = train)

summary(fit2)

从上面的结果可知,模型的参数和整个模型也都通过了显著性检验,接下来我们还需要进一步做其他方便的假设检验。


# 模型的线性关系检验,绘制成分残差图

crPlots(fit2)

从图中可知,变量bathrooms与price之间并不存在明显的线性关系,而几乎是一条水平直线,故考虑删除该变量。


fit3 <- update(fit2, . ~ . - bathrooms)

summary(fit3)

模型结果如下:


# 残差同方差性检验

ncvTest(fit3)

从结果总可知,虽然p值小于0.05,但也比较解决0.05了,说明可以放宽并接受方差齐性的条件。


最后,我们通过一张诊断图来了解一下模型情况

opar <- par(no.readonly = TRUE)

par(mfrow = c(2,2))

plot(fit3)

par(opar)

左上图反应了模型的残差在0附近随机波动,且不存在某种趋势,说明残差的期望为0;

右上图反应的是模型残差的QQ图,即正态性检验图,几乎所有的点都在虚线上,说明残差服从正态分布;

左下图的红线也没有明显的趋势,说明残差的方差满足齐性的假设;

右下图则反应的数据的异常信息,有个别点属于异常值,如14号、27号等样本;


# 预测

pred <- predict(object = fit3, newdata = test[,c(-1,-6)])


使用多元线性回归模型对房价做了预测之后,我们再来使用CART算法进行房价的预测:由于CART算法对数据没有过多的要求,故我们使用所有的变量对Y变量进行预测。


# 将原始数据进行拆分,构造训练集和测试集

set.seed(1234)

index <- sample(1:nrow(house.new), 

                size = 0.75*nrow(house.new))

train2 <- house.new[index,]

test2 <- house.new[-index,]


# 构建CART算法模型

fit4 <- rpart(price ~ .,data = train2)

# 预测

pred2 <- predict(fit4,newdata = test2[,-1])


模型建完了,预测值也做出来了,如何比较CART算法和多元线性回归模型之间的优劣呢?接下来我们使用RMSE(均方根误差)来衡量。

多元线性回归模型

# 训练集上的RMSE

rmse.lm.train <- sqrt(mean(fit3$residuals^2))

# 测试集上的RMSE

rmse.lm.test <- sqrt(mean((log(test[,1])-pred)^2))


rmse.lm.train

rmse.lm.test

通过训练集和测试集的RMSE比较,并没有发现多元线性回归模型模型存在过拟合状态。


CART算法

# 训练集上的RMSE

pred3 <- predict(fit4,newdata = train2[,-1])

rmse.cart.train <- sqrt(mean((log(train2[,1])-log(pred3))^2))

# 测试集上的RMSE

rmse.cart.test <- sqrt(mean((log(test2[,1])-log(pred2))^2))

rmse.cart.train

rmse.cart.test

通过训练集和测试集的RMSE比较,也没有发现CART模型存在过拟合状态。


但通过两个模型的训练集RMSE的比较,我们发现多元线性回归模型比CART算法要更优秀一些。


今天我们就先分享到这里,如有任何问题都可以给我留言,谢谢。下一期我们将道道分类算法中的knn来龙去脉。


每天进步一点点2015

学习与分享,取长补短,关注小号!


长按识别二维码 马上关注

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存