原理
Ridge Regression,Lasso Regression和Elastic-Net Regression都是通过增加一个惩罚项让模型less sensitive。
以该图为例,红色的点为training data, 绿色的点为test data。虽然通过两个红色的点能得到一条完全拟合的线,但是这条红线对绿色的点预测较差差。
加入惩罚项之后,得到蓝色的线,与红色点拟合度降低的同时,对绿色的点预测更准确。
Ridge Regression,Lasso Regression和Elastic-Net Regression原理如下:
三种原理相似,区别在于Ridge Regression适用于变量对模型都有贡献,分析过程中不排除变量;Lasso Regression适用于存在大量干扰变量,分析过程中可以排除无关变量使其贡献为0;Elastic-Net Regression适用于存在大量变量但是不知道哪些有用哪些没用。
建立数据集
在没有数据的情况下建立一个数据集来模拟分析吧
先建立一个1000个容量,有5000个参数,但是实际上只有15个有用参数的数据集
set.seed(42)
#1000个样本
n <- 1000
#5000个参数
p <- 5000
#15个有用的参数
real_p <- 15
x <- matrix(rnorm(n*p), nrow=n, ncol=p)
##y的结果是x前15列的和,加上一些干扰项
y <- apply(x[,1:real_p], 1, sum) + rnorm(n))
做好了数据集,我们来尝试用x预测y,可以看出这个数据集存在大量没用参数,理论上来讲Lasso Regression使最合适的
分析
调用包
在R中,Ridge Regression,Lasso Regression和Elastic-Net Regression都可以用同一个包glmnet
library(glmnet)
区分好训练集和测试集
#将数据分为training和testing
#设定某些行为训练集
#在1:n之间随意选一个属,选中所有0.66*n的数,三分之二的数为train dataset, 行名保存在train_rows里面
train_rows <- sample(1:n, .66*n)
#根据train_rows作为索引,创建新的matrix,只含training data
x.train <- x[train_rows,]
#test data就是排除train data的
x.test <- x[-train_rows,]
#同样,将y也中的数据也分为训练和测试
y.train <- y[train_rows]
y.test <- y[-train_rows]
Ridge regression
#先用训练集建立模型, cv(corss validation),默认为10-Fold Cross Validation #
#前两个参数确定训练集,在本例中,我们想用x.train去预测y.train
#liner regression中type.measure="mse" mean squared error = suquared ressiduals/ sample size(如果要用logistic Regression,参数为deciance)
#ridge regression,所以alpha为0
#liner regression中family="gaussian" ; (logistic regression set "binomial")
#整个函数的意思是用cv.glmnet(),10-Fold Cross Validation, 拟合一个带有ridge regression惩罚项的linear regression,从而得到最优λ
alpha0.fit <- cv.glmnet(x.train,y.train, type.measure="mse",
alpha=0, family="gaussian")
#现在将预测函数在testing中得到验证,预测数据保存
#第一个参数是预测模型,第二个参数是λ的值,能得到最简模型,虽然ridgere gression中可用lambda.min替代(不需要排除参数),为了比较三个模型,在这里统一设置lambda.1se
#news=x.test,
alpha0.predicted <-
predict(alpha0.fit, s=alpha0.fit$lambda.1se, newx=x.test)
#比较真实值和预测值
mean((y.test - alpha0.predicted)^2)
lasso regression
alpha1.fit <- cv.glmnet(x.train,y.train, type.measure="mse",
alpha=1, family="gaussian")
alpha1.predicted <-
predict(alpha1.fit, s=alpha1.fit$lambda.1se, newx=x.test)
mean((y.test - alpha1.predicted)^2)
Elastic-Net Regression
先随便指定一下
α=0.5时
alpha0.5.fit <- cv.glmnet(x.train,y.train, type.measure="mse",
alpha=0.5, family="gaussian")
alpha0.5.predicted <-
predict(alpha0.5.fit, s=alpha0.5.fit$lambda.1se, newx=x.test)
mean((y.test - alpha0.5.predicted)^2)
用循环实现α=0.1, 0.2, ..., 1
来看三种方法到底哪个算的mean更小
list.of.fits <- list()
#用for来尝试不同的alpha
for(i in 0:10:{
fit.name <- paste0("alpha", i/10)
list.of.fits[[fit.name]] <-
cv.glmnet(x.train,y.train, type.measure="mse",
alpha=i/10, family="gaussian")
}
#alpha 取值0.1, 0.2, ..., 1
results <- data.frame()
for(i in 0:10){
fit.name <- paste0("alpha", i/10)
predicted <-
predict(list.of.fits[[fit.name]],
s=list.of.fits[[fit.name]],
s=list.of.fits[[fit.name]]$lambda.1se, newx=x.test)
mse <- mean((y.test - predicted)^2)
temp <- data.frame(alpha=i/10, mse=mse, fit.name=fit.name)
results <- rbind(results, temp)
}
results
结果
可以看出当alpha=1 mse最小,这时实际上是Lasso Regression。
也证实了在有大量无用参数时,Lasson Regression比较合适。