京公网安备 11010802034615号
经营许可证编号:京B2-20210330
变量选择之SCAD算法
本文提出了一种用于同时达到选择变量和预测模型系数的目的的方法——SCAD。这种方法的罚函数是对称且非凹的,并且可处理奇异阵以产生稀疏解。此外,本文提出了一种算法用于优化对应的带惩罚项的似然函数。这种方法具有广泛的适用性,可以应用于广义线性模型,强健的回归模型。借助于波和样条,还可用于非参数模型。更进一步地,本文证明该方法具有Oracle性质。模拟的结果显示该方法相比主流的变量选择模型具有优势。并且,模型的预测误差公式显示,该方法实用性较强。
SCAD的理论理解
在总结了现有模型的一些缺点之后,本文提出构造罚函数的一些目标:
罚函数是奇异的(singular)
连续地压缩系数
对较大的系数产生无偏的估计
SCAD模型的Oracle性质,使得它的预测效果跟真实模型别无二致。
并且,这种方法可以应用于高维非参数建模。
SCAD的目标函数如下:
SCAD的罚函数与$theta$的(近似)关系如下图所示。
可见,罚函数可以用二阶泰勒展开逼近。

Hard Penality,lasso,SCAD的系数压缩情况VS系数真实值的情况如下图所示。
可以看到,lasso压缩系数是始终有偏的,Hard penality是无偏的,但压缩系数不连续。而SCAD既能连续的压缩系数,也能在较大的系数取得渐近无偏的估计。
这使得SCAD具有Oracle性质。
SCAD的缺点
模型形式过于复杂
迭代算法运行速度较慢
在low noise level的情况下表现较优,但在high noise level的情况下表现较差。
SCAD的实现
SCAD迭代公式
SCAD的目标函数如下:
时,罚函数可以用二阶泰勒展开逼近。
从而,有如下迭代公式:

根据以上公式,代入迭代步骤,即可实现算法。
SCAD的R实现
##------数据模拟--------
library(MASS)
##mvrnorm()
##定义一个产生多元正态分布的随机向量协方差矩阵
Simu_Multi_Norm<-function(x_len, sd = 1, pho = 0.5){
#初始化协方差矩阵
V <- matrix(data = NA, nrow = x_len, ncol = x_len)
#mean及sd分别为随机向量x的均值和方差
#对协方差矩阵进行赋值pho(i,j) = pho^|i-j|
for(i in 1:x_len){ ##遍历每一行
for(j in 1:x_len){ ##遍历每一列
V[i,j] <- pho^abs(i-j)
}
}
V<-(sd^2) * V
return(V)
}
##产生模拟数值自变量X
set.seed(123)
X<-mvrnorm(n = 200, mu = rep(0,10), Simu_Multi_Norm(x_len = 10,sd = 1, pho = 0.5))
##产生模拟数值:响应变量y
beta<-c(1,2,0,0,3,0,0,0,-2,0)
#alpha<-0
#prob<-exp(alpha + X %*% beta)/(1+exp(alpha + X %*% beta))
prob<-exp( X %*% beta)/(1+exp( X %*% beta))
y<-rbinom(n = 200, size = 1,p = prob)
##产生model matrix
mydata<-data.frame(X = X, y = y)
#X<-model.matrix(y~., data = mydata)
##包含截矩项的系数
#b_real<-c(alpha,beta)
b_real<-beta
########----定义惩罚项相关的函数-----------------
##定义惩罚项
####运行发现,若lambda设置为2,则系数全被压缩为0.
####本程序根据rcvreg用CV选出来的lambda设置一个较为合理的lambda。
p_lambda<-function(theta,lambda = 0.025){
p_lambda<-sapply(theta, function(x){
if(abs(x)< lambda){
return(lambda^2 - (abs(x) - lambda)^2)
}else{
return(lambda^2)
}
}
)
return(p_lambda)
}
##定义惩罚项导数
p_lambda_d<-function(theta,a = 3.7,lambda = 0.025){
if(abs(theta) > lambda){
if(a * lambda > theta){
return((a * lambda - theta)/(a - 1))
}else{
return(0)
}
}else{
return(lambda)
}
}
# ##当beta_j0不等于0,定义惩罚项导数近似
# p_lambda_d_apro<-function(beta_j0,beta_j,a = 3.7, lambda = 2){
# return(beta_j * p_lambda_d(beta = beta_j0,a = a, lambda = lambda)/abs(beta_j0))
# }
#
#
# ##当beta_j0 不等于0,指定近似惩罚项,使用泰勒展开逼近
# p_lambda_apro<-function(beta_j0,beta_j,a = 3.7, lambda = 2){
# if(abs(beta_j0)< 1e-16){
# return(0)
# }else{
# p_lambda<-p_lambda(theta = beta_j0, lambda = lambda) +
# 0.5 * (beta_j^2 - beta_j0^2) * p_lambda_d(theta = beta_j0, a = a, lambda = lambda)/abs(beta_j0)
# }
# }
#define the log-likelihood function
loglikelihood_SCAD<-function(X, y, b){
linear_comb<-as.vector(X %*% b)
ll<-sum(y*linear_comb) + sum(log(1/(1+exp(linear_comb)))) - nrow(X)*sum(p_lambda(theta = b))
return (ll)
}
##初始化系数
#b0<-rep(0,length(b_real))
#b0<- b_real+rnorm(length(b_real), mean = 0, sd = 0.1)
##将无惩罚时的优化结果作为初始值
b.best_GS<-b.best
b0<-b.best_GS
##b1用于记录更新系数
b1<-b0
##b.best用于存放历史最大似然值对应系数
b.best_SCAD<-b0
# the initial value of loglikelihood
ll.old<-loglikelihood_SCAD(X = X,y = y, b = b0)
# initialize the difference between the two steps of theta
diff<-1
#record the number of iterations
iter<-0
#set the threshold to stop iterations
epsi<-1e-10
#the maximum iterations
max_iter<-100000
#初始化一个列表用于存放每一次迭代的系数结果
b_history<-list(data.frame(b0))
#初始化列表用于存放似然值
ll_list<-list(ll.old)
#######-------SCAD迭代---------
while(diff > epsi & iter < max_iter){
for(j in 1:length(b_real)){
if(abs(b0[j]) < 1e-06){
next()
}else{
#线性部分
linear_comb<-as.vector(X %*% b0)
#分子
nominator<-sum(y*X[,j] - X[,j] * exp(linear_comb)/(1+exp(linear_comb))) +
nrow(X)*b0[j]*p_lambda_d(theta = b0[j])/abs(b0[j])
#分母,即二阶导部分
denominator<- -sum(X[,j]^2 * exp(linear_comb)/(1+exp(linear_comb))^2) +
nrow(X)*p_lambda_d(theta = b0[j])/abs(b0[j])
#2-(3) :更新b0[j]
b0[j]<-b0[j] - nominator/denominator
#2-(4)
if(abs(b0[j]) < 1e-06){
b0[j] <- 0
}
# #更新似然值
# ll.new<- loglikelihood_SCAD(X = X, y = y, b = b0)
#
#
#
# #若似然值有所增加,则将当前系数保存
# if(ll.new > ll.old){
# #更新系数
# b.best_SCAD[j]<-b0[j]
# }
#
# #求差异
# diff<- abs((ll.new - ll.old)/ll.old)
# ll.old <- ll.new
# iter<- iter+1
# b_history[[iter]]<-data.frame(b0)
# ll_list[[iter]]<-ll.old
# ##当达到停止条件时,跳出循环
# if(diff < epsi){
# break
# }
#
}
}
#更新似然值
ll.new<- loglikelihood_SCAD(X = X, y = y, b = b0)
#若似然值有所增加,则将当前系数保存
if(ll.new > ll.old){
#更新系数
b.best_SCAD<-b0
}
#求差异
diff<- abs((ll.new - ll.old)/ll.old)
ll.old <- ll.new
iter<- iter+1
b_history[[iter]]<-data.frame(b0)
ll_list[[iter]]<-ll.old
}
b_hist<-do.call(rbind,b_history)
#b_hist
ll_hist<-do.call(rbind,ll_list)
#ll_hist
#
iter
##
ll.best<-max(ll_hist)
ll.best
##
b.best_SCAD
##对比
cbind(coeff_glm,b.best,b.best_SCAD,b_real)
##----------ncvreg验证-----------
library(ncvreg)
my_ncvreg<-ncvreg(X,y,family = c("binomial"),penalty = c("SCAD"),lambda = 2)
my_ncvreg$beta
my_ncvreg<-ncvreg(X,y,family = c("binomial"),penalty = c("SCAD"))
summary(my_ncvreg)
my_ncvreg$beta
###用cv找最优的lambda
scad_cv<-cv.ncvreg(X,y,family = c("binomial"),penalty='SCAD')
scad_cv$lambda.min
mySCAD=ncvreg(X,y,family = c("binomial"),penalty='SCAD',lambda=scad_cv$lambda.min)
summary(mySCAD)
ncv_SCAD<-mySCAD$beta[-1]
##对比
myFinalResults<-cbind(无惩罚项回归=coeff_glm, GS迭代 = b.best,
GS_SCAD迭代 = b.best_SCAD, ncvreg = ncv_SCAD,真实值 = b_real)
save(myFinalResults,file = "myFinalResults.rda")
想深入学习统计学知识,为数据分析筑牢根基?那快来看看统计学极简入门课程!
学习入口:https://edu.cda.cn/goods/show/3386?targetId=5647&preview=0
课程由专业数据分析师打造,完全免费,60 天有效期且随到随学。它用独特思路讲重点,从数据种类到统计学体系,内容通俗易懂。学完它,能让你轻松入门统计学,还能提升数据分析能力。赶紧点击链接开启学习,让自己在数据领域更上一层楼!
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
【核心关键词】贷款、报表、课程、专业、建模、缺失值、营销、互联网、银行、办公自动化、数据分析、数据预处理、特征工程、贷 ...
2026-06-05在数据库数据查询、业务报表统计、多表关联分析中,LEFT JOIN左连接是使用率最高的SQL关联查询语句。其核心特性是保留左表全部数 ...
2026-06-05 很多数据分析师能熟练地写SQL、做透视表、算描述性统计,但当被问到“如何预测用户流失概率”“如何归因销量下滑的关键因素 ...
2026-06-05任何一款产品从诞生、普及到最终退出市场,都会遵循一套固定的发展规律,这就是产品生命周期理论。在市场竞争日益激烈、产品迭代 ...
2026-06-04在Excel数据分析、办公统计、业务报表制作场景中,数据透视表是数据汇总、分类统计、快速复盘的核心工具,能够高效完成海量原始 ...
2026-06-04 很多数据分析师拿到数据就开始清洗、建模,但当被问到“这批数据属于什么类型——结构化还是非结构化?分类变量还是数值变量 ...
2026-06-04在问卷调查与社会科学数据分析中,卡方检验是最常用、最基础的非参数检验方法,广泛应用于市场调研、用户分析、行为统计、满意度 ...
2026-06-03【核心关键词】贷款、报表、课程、专业、建模、缺失值、营销、互联网、银行、办公自动化、数据分析、数据预处理、特征工程、贷 ...
2026-06-03 很多数据分析师画过趋势图、做过业绩预测,但当被问到“这个月销售额增长20%,到底是长期趋势自然增长,还是促销活动的短期 ...
2026-06-03逻辑回归是数据分析、机器学习、统计建模中应用最广泛的二分类预测模型,常用于风险判断、行为预测、归因分析等场景。在SPSS、Py ...
2026-06-02数字经济时代,市场竞争日趋同质化,用户消费需求愈发个性化、多元化,传统依托经验、粗放式、广撒网的营销模式弊端日益凸显。长 ...
2026-06-02 很多数据分析师做过按月份的销售额趋势图,画过按天的流量折线图,但当被问到“时间序列和普通数据有什么本质区别”“季节性 ...
2026-06-02在市场竞争日趋饱和、用户需求不断细分的当下,企业创业创新、产品迭代与市场拓展不再依赖经验决策,而是需要系统化、工具化的商 ...
2026-06-01【核心关键词】调度、岗位、数据库、企业、报表、培训、程序、数据分析、数据加工、业务部门、企业数据、调度工具、业务指标、 ...
2026-06-01 很多数据分析师能熟练地计算指标、搭建标签体系,但当被问到“画像到底在解决什么问题”“画像和标签是什么关系”“画像如何 ...
2026-06-01在数据统计分析、数据清洗、异常值识别与数据分布研究中,箱型图是最直观、高效、专业的可视化分析工具。相较于柱状图、折线图仅 ...
2026-05-29Tkinter是Python内置的标准GUI图形界面库,具备无需额外安装、调用简单、兼容性强、轻量化高效等优势,是Python快速开发桌面小程 ...
2026-05-29 很多分析师在设计标签时思路清晰,但真到落地环节却面临“数据在手,不知如何转化为可用标签”的困境:或因加工方式选择不当 ...
2026-05-29【核心关键词】大数据、经理、专业、金融、客户、传统、建模、数据产品、互联网金融、产品经理、数据分析、金融行业、数据模型 ...
2026-05-28 很多分析师每天和数据打交道,但当被问到“标签是什么”“标签和指标有什么区别”“标签体系如何设计”时,却常常答不上来。 ...
2026-05-28