嶺迴歸和Lasso 迴歸

線性迴歸存在問題:

    在處理複雜的迴歸問題時,普通的線性迴歸問題會出現預測精度不夠的問題,如果模型中特徵之間有較強的相關關係時,即特徵之間出現嚴重的多重共線性時,用普通最小二乘法估計模型參數,往往參數估計的方差太大,求出來的模型就很不穩定。再具體取值上與真值有較大偏差。這時就需要對數據中的特徵進行提取,迴歸算法裏面的特徵選擇的方法有嶺迴歸和 Lasso 迴歸。這兩種方法都屬於正則化的特徵選擇方法,在處理複雜的數據迴歸問題中常用。

一、嶺迴歸Rdige Regression模型

嶺迴歸在平均誤差的基礎上增加正則項:

l=\sum_{i=1}^{m}(y^{(i)}-\sum_{j=0}^{n}w_{j}x_{j}^{(i)})^{2}+\lambda \sum_{j=0}^{n}w_{j}^{2}

其中,\lambda > 0,通過確定\lambda的值可以使得在方差和偏差之間達到平衡:隨着\lambda的增大,模型方差減少而偏差增大。

嶺迴歸模型的求解:

利用最小二乘法求解嶺迴歸模型的參數,對W求導並令其爲零。

2X^{T}\left ( Y-XW \right )-2\lambda W\Rightarrow \hat{W}=\left ( X^{T} X+\lambda I\right )^{-1}X^{T}Y

二、Lasso 迴歸模型

Lasso 採用的則是 L1正則,即 Lasso是在平方誤差的基礎上增加 L1 正則:

l=\sum_{i=1}^{m}(y^{(i)}-\sum_{j=0}^{n}w_{j}x_{j}^{(i)})^{2}+\lambda \sum_{j=0}^{n}\left | w_{j} \right |

與基於 L2 迴歸的嶺迴歸不同的是,上述的損失函數在 w_{j}=0 處不可導,因此傳統的基於梯度的方法不能直接用來求解損失函數。問了解決這個問題,採用近似的優化算法,或者採用一些簡單的方法來近似這樣的優化算法。

三、擬牛頓法

BFGS 算法是使用較多的一種擬牛頓方法,是由 Broyde、Fletcher、Goidfarb和Shanno 四人提出,所以稱爲 BFGS。(莫名想到TFBOYS,哈哈哈哈哈)

對於擬牛頓方程:

\bigtriangledown f\left ( x_{k} \right )=\bigtriangledown f\left ( x_{k+1} \right )+G_{k+1}(x_{k}-x_{k+1})

B_{k+1}\doteq G_{k+1},則可得:

B_{k+1}(x_{k}-x_{k+1})=\bigtriangledown f\left ( x_{k+1} \right )-\bigtriangledown f\left ( x_{k} \right )

在BFGS校正方法中假設:

B_{k+1}=B_{k}+E_{k}

BFGS校正公式的推導:

BFGS校正的算法流程:

B_{k} 對稱正定,B_{k+1}由上述的 BFGS 校正公式確定,那麼 B_{k+1} 對稱正定的充要條件是 y_{k}^{T}s_{k}> 0

在利用 Armijo 搜索準則時,並不是都滿足上述的充要條件,此時可以對BFGS校正公式做些改變:

BFGS 算法流程如下

利用Sherman-Morrison公式可對上式進行變換,得:

藍色部分表示實數

在BFGS算法中,每次都要儲存近似Hesse 矩陣 B_{k}^{-1},在高維數據中,儲存B_{k}^{-1}浪費很多的儲存空間,在實際的應用中,我們需要搜索方向,因此出現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

 

 

 

訓練數據鏈接:https://github.com/zhaozhiyong19890102/Python-Machine-Learning-Algorithm/blob/master/Chapter_8%20RidgeRegression/data.txt

 

參考文獻:趙志勇《python 機器學習算法》(程序)


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章