4163 3

[问答] 请问SCAD惩罚函数是怎么把参数估计为0的?我这里有Python代码 [推广有奖]

  • 1关注
  • 0粉丝

大专生

65%

还不是VIP/贵宾

-

威望
0
论坛币
10 个
通用积分
6.1353
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
1873 点
帖子
15
精华
0
在线时间
97 小时
注册时间
2015-5-11
最后登录
2023-8-24

楼主
阿卜拉克萨斯 发表于 2019-1-3 10:49:12 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
我用Python按照:https://ask.hellobi.com/blog/datasciencemeditation/7540,http://www.doc88.com/p-2089722981617.html。这两个链接实现门槛回归(没办法,没看到Python有什么库提供门槛回归方法),由于第2个链接里的论文根本没提到SCAD惩罚函数的样子,我就去搜索到了第一个链接。总而言之,现在我的确跑出来结果了,但是门槛值 c 的估计值竟然比实际存在的门槛变量的取值还大好多倍,这肯定不正确。。。
所以我怀疑是不是我对SCAD惩罚函数的理解有错误,总之我今天又谷歌,搜索到了http://vlambda.com/wz_wRtUGSimSJ.html。这篇文章,里面的SCAD惩罚函数又和第一个链接看到的不一样,我已经迷糊了

我的意思是,现在我搞不清楚SCAD是不是函数表达式弄错了,但不论是否弄错,我发现我不能理解SCAD如何把参数估计为0。参数估计为0就会将某段的回归全部系数估计为0,从而能直接告知这个分段不需要,这是第二个链接论文告诉我的。。。但反正,现在门槛 c 值就不对。。。门槛自变量的数据最大也才1W出头,而估计出的C竟然数倍于它们
以下是代码,其中pay_data_x为189*9的矩阵自变量数据,pay_data_y为因变量。M_c是分段,这里分了2段,先分别用pay_data_x的第2列数据的标准差和均值做分段门槛值的初始值(即第2列数据为门槛变量)
另外就是,是否有可能我对门槛值c的求导有错误??

  1. # 定义损失函数:光滑SCAD正则,参考《门槛回归模型中门槛值和回归参数的估计》
  2. def loss_SCAD(beta_matrix, M_c):
  3.     first_M_MSE = np.dot(pay_data_x, beta_matrix[:lt]) - pay_data_y.values.reshape((-1, 1))
  4.     z = pay_data_x.iloc[:, 1].values.reshape((-1, 1))
  5.     other_M_MSE = 0
  6.     scad = 0

  7.     for idx, c in enumerate(M_c):
  8.         nor_distri_x = (z - c) / h
  9.         nor_distri_CDF = st.norm.cdf(nor_distri_x)  # 光滑示性函数
  10.         other_M_MSE += np.dot(pay_data_x, beta_matrix[lt * (idx + 1):lt * (idx + 2)]) * nor_distri_CDF
  11.         for beta_param in beta_matrix[lt * (idx + 1):lt * (idx + 2)]:
  12.             if abs(beta_param) < lam:
  13.                 scad += lam ** 2 - (abs(beta_param) - lam) ** 2
  14.             else:
  15.                 scad += lam ** 2

  16.     MSE = 0.5 * np.average((first_M_MSE + other_M_MSE) ** 2) + scad

  17.     return MSE


  18. # 更新梯度
  19. def loss_derivative(beta_matrix, M_c):
  20.     deriv_list = []
  21.     other_M_MSE = 0
  22.     nor_distri_CDF = 0

  23.     first_M_MSE = np.dot(pay_data_x, beta_matrix[:lt]) - pay_data_y.values.reshape((-1, 1))
  24.     z = pay_data_x.iloc[:, 1].values.reshape((-1, 1))

  25.     for idx, c in enumerate(M_c):
  26.         nor_distri_x = (z - c) / h
  27.         nor_distri_CDF = st.norm.cdf(nor_distri_x)  # 光滑示性函数
  28.         other_M_MSE += np.dot(pay_data_x, beta_matrix[lt * (idx + 1):lt * (idx + 2)]) * nor_distri_CDF

  29.     square_part = first_M_MSE + other_M_MSE

  30.     for idx, beta_param in enumerate(beta_matrix[:lt]):  # 更新梯度:第一段
  31.         deriv_list.append(np.average(square_part * pay_data_x.iloc[:, idx].values.reshape((-1, 1))))

  32.     for idx, c in enumerate(M_c):
  33.         for i, beta_param in enumerate(beta_matrix[lt * (idx + 1):lt * (idx + 2)]):  # 更新梯度:分段
  34.             if abs(beta_param) > lam:  # 更新梯度:SCAD函数
  35.                 if a * lam > beta_param:
  36.                     scad = (lam + (a * lam - beta_param) / ((a - 1) * lam))
  37.                 else:
  38.                     scad = 0
  39.             else:
  40.                 scad = lam

  41.             deriv_list.append(np.average(square_part * pay_data_x.iloc[:, i].values.reshape((-1, 1)) * nor_distri_CDF) + scad)

  42.     for idx, c in enumerate(M_c):  # 更新梯度:门槛参数
  43.         nor_distri_PDF = st.norm.pdf((z - c) / h)
  44.         deriv_list.append(np.average(square_part * np.dot(pay_data_x, beta_matrix[lt * (idx + 1):lt * (idx + 2)]) * nor_distri_PDF / -h))

  45.     return np.array(deriv_list).reshape((-1, 1))


  46. # DFP迭代
  47. def DFP(M_c, t=0.5, epsilon=1e-5, max_iter=1000, sigma=0.4):
  48.     c = np.array(M_c).reshape((-1, 1))
  49.     tmp_beta = np.ones((pay_data_x.shape[1] * (len(M_c) + 1), 1))
  50.     H = np.eye(pay_data_x.shape[1] * (len(M_c) + 1) + len(M_c))  # 初始化伪黑塞矩阵的逆
  51.     beta_c_last = np.concatenate((tmp_beta, c))
  52.     iter = 1
  53.     tmp_beta_idx = tmp_beta.shape[0]

  54.     while iter < max_iter:
  55.         iter += 1

  56.         beta_last = beta_c_last[:tmp_beta_idx]
  57.         c_last = beta_c_last[tmp_beta_idx:]
  58.         deriv_last = loss_derivative(beta_last, c_last)

  59.         if np.linalg.norm(deriv_last) <= epsilon or iter >= max_iter:
  60.             return beta_last.reshape((-1, len(M_c) + 1)), c_last, iter, loss_SCAD(beta_last, c_last)

  61.         dk = -np.dot(H, deriv_last)

  62.         m, mk = 0, 0
  63.         loss_Armijo_1 = loss_SCAD(beta_last, c_last)
  64.         while m < 20:  # 搜索最佳学习率
  65.             if loss_SCAD(beta_last + t ** m * dk[:tmp_beta_idx], c_last + t ** m * dk[tmp_beta_idx:]) \
  66.                     < loss_Armijo_1 + sigma * t ** m * np.dot(deriv_last.T, dk):
  67.                 mk = m
  68.                 break
  69.             m += 1

  70.         beta_c_new = beta_c_last + t ** mk * dk
  71.         beta_new = beta_c_new[:tmp_beta_idx]
  72.         c_new = beta_c_new[tmp_beta_idx:]

  73.         deriv_new = loss_derivative(beta_new, c_new)
  74.         s = beta_c_new - np.concatenate((beta_last, c_last))  # 参数一阶差分
  75.         y = deriv_new - deriv_last  # 梯度一阶差分
  76.         h_y = np.dot(H, y)
  77.         H += np.dot(s, s.T) / np.dot(s.T, y) - np.dot(h_y, h_y.T) / np.dot(np.dot(y.T, H), y)  # 更新伪黑塞矩阵的逆

  78.         beta_c_last = beta_c_new


  79. s = DFP(M_c=(pay_data_y.std(), pay_data_y.mean()))
  80. print(s[0], s[1], s[2], s[3], sep='\n')
复制代码
分3段,故有9*3=27个参数,可以看到第2和第3段参数估计一模一样

[[ 2.21651758e+02  1.24799988e+04  1.24799988e+04]

[-2.70472227e-02  1.26663922e+08  1.26663922e+08]

[ 2.84548849e-02  3.16436052e+07  3.16436052e+07]

[ 3.82476892e-02  3.05133523e+07  3.05133523e+07]

[ 1.13002044e-02  2.92317240e+07  2.92317240e+07]

[ 8.44805488e-02  3.13574382e+07  3.13574382e+07]

[-1.01630661e-01  2.93887077e+07  2.93887077e+07]

[ 3.42105205e-02  3.08086230e+07  3.08086230e+07]

[ 7.84099476e+00  2.79222208e+07  2.79222208e+07]]

门槛值c:[[ 26632.39914284], [113303.41470849]]
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝


沙发
屈草待君芳1 发表于 2019-2-17 18:27:08
我最近也在看这个方法。不想调程序,想自己编一个。你现在搞明白了吗?

藤椅
SCKB 发表于 2020-4-1 13:23:12 来自手机
阿卜拉克萨斯 发表于 2019-1-3 10:49
我用Python按照:https://ask.hellobi.com/blog/datasciencemeditation/7540,http://www.doc88.com/p-2089 ...
楼主看范老师的原文吧  原文里面写的挺清楚的   2001年的JASA

板凳
刘艳萍 发表于 2021-3-18 15:42:43
画一下惩罚函数导数的图像,可以看到在小于lam那一段是赋予大的权重,这样就能达到收缩系数了

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注jltj
拉您入交流群
GMT+8, 2026-2-7 16:22