線性迴歸存在問題:
在處理複雜的迴歸問題時,普通的線性迴歸問題會出現預測精度不夠的問題,如果模型中特徵之間有較強的相關關係時,即特徵之間出現嚴重的多重共線性時,用普通最小二乘法估計模型參數,往往參數估計的方差太大,求出來的模型就很不穩定。再具體取值上與真值有較大偏差。這時就需要對數據中的特徵進行提取,迴歸算法裏面的特徵選擇的方法有嶺迴歸和 Lasso 迴歸。這兩種方法都屬於正則化的特徵選擇方法,在處理複雜的數據迴歸問題中常用。
一、嶺迴歸Rdige Regression模型
嶺迴歸在平均誤差的基礎上增加正則項:
其中,,通過確定的值可以使得在方差和偏差之間達到平衡:隨着的增大,模型方差減少而偏差增大。
嶺迴歸模型的求解:
利用最小二乘法求解嶺迴歸模型的參數,對W求導並令其爲零。
二、Lasso 迴歸模型
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-BFGS 算法
這樣在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 機器學習算法》(程序)