今天可以接着前天的话题继续写了
在完成了使用现成函数lm.ridge做出的岭回归之后,来尝试下整理这个东西的内部逻辑
本来想直接使用之前已经完成的结果来做后续对照的,但在指定optim函数的初始变量时发现一个很内伤的问题,那就是整个系数组中的常数值偏离太大,导致有限次数内总是收敛不到最佳解,不得已只能祭出大招,把所有数据先做一遍z-score标准化
以下是前期准备步骤,包括了导入数据,标准化,和使用两个现成的函数产生的线性回归系数
代码部分:
#导入数据 Data <- read.csv("StasticData.csv") str(Data) #z-score标准化 Z.score <- function (x) { (x - mean(x))/sd(x) } Data1 <- as.data.frame(apply(Data, 2, Z.score)) #一般线性拟合 LR1 <- as.vector(coef(lm(ValueSold ~. ,Data1))) #岭回归,预先指定了参数lambda = 0.065 library("MASS") LR2 <- as.vector(coef(lm.ridge(ValueSold ~. ,Data1, lambda = 0.065)))
输出结果:
就知道标准化能有点用处,至少这样就可以先把所有初始输入值设成0了
之前研究一般的线性回归时,咱用过optim函数最小化残差平方和,也就是最通俗的最小二乘法,形式是这样的:
#最小二乘法 Array.x <- as.matrix(cbind(c(1),Data1[,-5])) SMQ <- function(x) { CanCha <- Data1$ValueSold - Array.x %*% x; SQCanCha <- sum(CanCha^2); return(SQCanCha) } CoefOrgin <- c(0,0,0,0,0) Op1 <- optim(CoefOrgin,SMQ)
运行结果和上边已经做出来的LR1比较,稍微有一点差异,但所幸不至于差得坍台
而岭回归作为线性回归的一种衍生算法,其最重要的部分是在最小二乘法的过程中增加了一个部分,参数lambda乘以二次正则化项
那啥叫正则化项呢?我就按上面代码里的部分来解释吧,请稍稍仔细观察下做最小二乘法时的代码结构,里面为了计算出残差平方和至少用到了三个部分
目标列实际值(Data1$ValueSold)、自变量矩阵(Array.x)和系数向量x
就是这两行:
CanCha <- Data1$ValueSold - Array.x %*% x; SQCanCha <- sum(CanCha^2)
而正则化项以本人粗劣的表达方式来讲,就是上边那个系数向量x的和
二次正则化项,为所有系数x的平方加和
所以,把一个普通的线性回归,改编成一个自带流星雨特效的岭回归的要点就是——在SQCancha那句话里加一截
SQCR <- sum(CanCha^2) + lambda * sum(x^2)
由于废话啰嗦了太多导致没精神再多捯饬一段取最佳lambda的算法,所以这里还是使用和上边自动版一样的参数0.065
#岭回归手动不完全版 Array.x <- as.matrix(cbind(c(1),Data1[,-5])) Ridge <- function(x) { CanCha <- Data1$ValueSold - Array.x %*% x; SQCR <- sum(CanCha^2) + 0.065 * sum(x^2); return(SQCR) } CoefOrgin <- c(0,0,0,0,0) Op2 <- optim(CoefOrgin,Ridge)
结果如下:
验证下来虽然依旧有点跑偏,但能咋办咧,当然是原谅它啊,不然这么纠结下去我还睡不睡觉了
至于在岭回归的各种不同版本的介绍里津津乐道的一个问题,为啥要取正则化项的二次,虽然比较官方的解释是请直接联想初中数学中的y=x2的曲线特征,但我还是得说,这个世界是很大滴,其实在这位置加一次方的版本也有(Lasso),一次二次都加上的版本也有(弹性网络),只不过每换种加法,算法就换了个名字,这我就不延伸出去了
------------------End----------------