线性回归存在问题:
在处理复杂的回归问题时,普通的线性回归问题会出现预测精度不够的问题,如果模型中特征之间有较强的相关关系时,即特征之间出现严重的多重共线性时,用普通最小二乘法估计模型参数,往往参数估计的方差太大,求出来的模型就很不稳定。再具体取值上与真值有较大偏差。这时就需要对数据中的特征进行提取,回归算法里面的特征选择的方法有岭回归和 Lasso 回归。这两种方法都属于正则化的特征选择方法,在处理复杂的数据回归问题中常用。
岭回归在平均误差的基础上增加正则项:
其中,,通过确定的值可以使得在方差和偏差之间达到平衡:随着的增大,模型方差减少而偏差增大。
岭回归模型的求解:
利用最小二乘法求解岭回归模型的参数,对W求导并令其为零。
Lasso 采用的则是 L1正则,即 Lasso是在平方误差的基础上增加 L1 正则:
与基于 L2 回归的岭回归不同的是,上述的损失函数在 处不可导,因此传统的基于梯度的方法不能直接用来求解损失函数。问了解决这个问题,采用近似的优化算法,或者采用一些简单的方法来近似这样的优化算法。
BFGS 算法是使用较多的一种拟牛顿方法,是由 Broyde、Fletcher、Goidfarb和Shanno 四人提出,所以称为 BFGS。(莫名想到TFBOYS,哈哈哈哈哈)
对于拟牛顿方程:
令,则可得:
在BFGS校正方法中假设:
BFGS校正公式的推导:
BFGS校正的算法流程:
设 对称正定,由上述的 BFGS 校正公式确定,那么 对称正定的充要条件是 。
在利用 Armijo 搜索准则时,并不是都满足上述的充要条件,此时可以对BFGS校正公式做些改变:
BFGS 算法流程如下
利用Sherman-Morrison公式可对上式进行变换,得:
蓝色部分表示实数
在BFGS算法中,每次都要储存近似Hesse 矩阵 ,在高维数据中,储存浪费很多的储存空间,在实际的应用中,我们需要搜索方向,因此出现L-BFGS算法,只保存最近的m次迭代信息,以降低数据的储存空间。
这样在L-BPFS算法中不需要保存完整的Hk,而是储存向量序列{sk}和{yk},而向量序列{sk}和{yk}也不是都要保存,只要保存最新的m不向量即可。
L-BFGS 算法中确定新的下降方向的具体过程是:
在岭回归模型中,我们分别使用最小二乘法、拟牛顿法BFGS和拟牛顿法L-BFGS对其回归系数进行求解。
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 24 19:04:05 2019
@author: 2018061801
"""
import numpy as np
def load_data(file_path):
'''导入训练数据
input: file_path(string):训练数据
output: feature(mat):特征
label(mat):标签
'''
f = open(file_path)
feature = []
label = []
for line in f.readlines():
feature_tmp = []
lines = line.strip().split("\t")
feature_tmp.append(1) # x0
for i in range(len(lines) - 1):
feature_tmp.append(float(lines[i]))
feature.append(feature_tmp)
label.append(float(lines[-1]))
f.close()
return np.mat(feature), np.mat(label).T
def ridge_regression(feature, label, lam):
'''最小二乘的求解方法
input: feature(mat):特征
label(mat):标签
output: w(mat):回归系数
'''
n = np.shape(feature)[1]
w = (feature.T * feature + lam * np.mat(np.eye(n))).I * feature.T * label
return w
def get_gradient(feature, label, w, lam):
'''计算导函数的值
input: feature(mat):特征
label(mat):标签
output: w(mat):回归系数
'''
err = (label - feature * w).T
left = err*(-1)*feature
return left.T + lam * w
def get_result(feature, label, w, lam):
'''
input: feature(mat):特征
label(mat):标签
output: w(mat):回归系数
'''
left = (label - feature * w).T * (label - feature * w)
right = lam * w.T * w
return (left + right) / 2
def get_error(feature, label, w):
'''
input: feature(mat):特征
label(mat):标签
output: w(mat):回归系数
'''
m = np.shape(feature)[0]
left = (label - feature * w).T * (label - feature * w)
return (left / (2 * m))[0, 0]
def bfgs(feature, label, lam, maxCycle):
'''利用bfgs训练Ridge Regression模型
input: feature(mat):特征
label(mat):标签
lam(float):正则化参数
maxCycle(int):最大迭代次数
output: w(mat):回归系数
'''
n = np.shape(feature)[1]
# 1、初始化
w0 = np.mat(np.zeros((n, 1)))
rho = 0.55
sigma = 0.4
Bk = np.eye(n)
k = 1
while (k < maxCycle):
print ("\titer: ", k, "\terror: ", get_error(feature, label, w0))
gk = get_gradient(feature, label, w0, lam) # 计算梯度
dk = np.mat(-np.linalg.solve(Bk, gk))
m = 0
mk = 0
while (m < 20):
newf = get_result(feature, label, (w0 + rho ** m * dk), lam)
oldf = get_result(feature, label, w0, lam)
if (newf < oldf + sigma * (rho ** m) * (gk.T * dk)[0, 0]):
mk = m
break
m = m + 1
# BFGS校正
w = w0 + rho ** mk * dk
sk = w - w0
yk = get_gradient(feature, label, w, lam) - gk
if (yk.T * sk > 0):
Bk = Bk - (Bk * sk * sk.T * Bk) / (sk.T * Bk * sk) + (yk * yk.T) / (yk.T * sk)
k = k + 20
w0 = w
return w0
def lbfgs(feature, label, lam, maxCycle, m=10):
'''利用lbfgs训练Ridge Regression模型
input: feature(mat):特征
label(mat):标签
lam(float):正则化参数
maxCycle(int):最大迭代次数
m(int):lbfgs中选择保留的个数
output: w(mat):回归系数
'''
n = np.shape(feature)[1]
# 1、初始化
w0 = np.mat(np.zeros((n, 1)))
rho = 0.55
sigma = 0.4
H0 = np.eye(n)
s = []
y = []
k = 1
gk = get_gradient(feature, label, w0, lam) # 3X1
print (gk)
dk = -H0 * gk
# 2、迭代
while (k < maxCycle):
print ("iter: ", k, "\terror: ", get_error(feature, label, w0))
m = 0
mk = 0
gk = get_gradient(feature, label, w0, lam)
# 2.1、Armijo线搜索
while (m < 20):
newf = get_result(feature, label, (w0 + rho ** m * dk), lam)
oldf = get_result(feature, label, w0, lam)
if newf < oldf + sigma * (rho ** m) * (gk.T * dk)[0, 0]:
mk = m
break
m = m + 1
# 2.2、LBFGS校正
w = w0 + rho ** mk * dk
# 保留m个
if k > m:
s.pop(0)
y.pop(0)
# 保留最新的
sk = w - w0
qk = get_gradient(feature, label, w, lam) # 3X1
yk = qk - gk
s.append(sk)
y.append(yk)
# two-loop
t = len(s)
a = []
for i in range(t):
alpha = (s[t - i - 1].T * qk) / (y[t - i - 1].T * s[t - i - 1])
qk = qk - alpha[0, 0] * y[t - i - 1]
a.append(alpha[0, 0])
r = H0 * qk
for i in range(t):
beta = (y[i].T * r) / (y[i].T * s[i])
r = r + s[i] * (a[t - i - 1] - beta[0, 0])
if yk.T * sk > 0:
'''print ("update OK!!!!")'''
dk = -r
k = k + 1
w0 = w
return w0
def save_weights(file_name, w0):
'''保存最终的结果
input: file_name(string):需要保存的文件
w0(mat):权重
'''
f_result = open("weights", "w")
m, n = np.shape(w0)
for i in range(m):
w_tmp = []
for j in range(n):
w_tmp.append(str(w0[i, j]))
f_result.write("\t".join(w_tmp) + "\n")
f_result.close()
if __name__ == "__main__":
# 1、导入数据
print ("----------1.load data ------------")
feature, label = load_data("D:/anaconda4.3/spyder_work/data3.txt")
# 2、训练模型
print ("----------2.training ridge_regression ------------")
method = "ridge_regression" # 选择的方法
if method == "bfgs": # 选择BFGS训练模型
w0 = bfgs(feature, label, 0.5, 1000)
elif method == "lbfgs": # 选择L-BFGS训练模型
w0 = lbfgs(feature, label, 0.5, 1000, m=10)
else: # 使用最小二乘的方法
w0 = ridge_regression(feature, label, 0.5)
# 3、保存最终的模型
print ("----------3.save model ------------")
save_weights("weights", w0)
BFGS算法结果
----------1.load data ------------
----------2.training ridge_regression ------------
iter: 1 error: 5165.1938312549155
iter: 21 error: 140.3163820505484
iter: 41 error: 137.40907856263846
iter: 61 error: 76.74290451504852
iter: 81 error: 76.6807677020955
iter: 101 error: 76.68074468245393
.
.
.
iter: 901 error: 76.68074448016529
iter: 921 error: 76.68074448016529
iter: 941 error: 76.68074448016529
iter: 961 error: 76.68074448016529
iter: 981 error: 76.68074448016529
----------3.save model ------------
L-BFGS算法结果
----------1.load data ------------
----------2.training ridge_regression ------------
.
.
.
iter: 992 error: 76.68074448016529
iter: 993 error: 76.68074448016529
iter: 994 error: 76.68074448016529
iter: 995 error: 76.68074448016529
iter: 996 error: 76.68074448016529
iter: 997 error: 76.68074448016529
iter: 998 error: 76.68074448016529
iter: 999 error: 76.68074448016529
----------3.save model ------------
结果一致,
w0=11.527469640915967
w1=1.9657311810802856
w2=5.194374781097597
训练数据
参考文献:赵志勇《python 机器学习算法》(程序)