岭回归和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,哈哈哈哈哈)

岭回归和Lasso 回归_第1张图片

对于拟牛顿方程:

\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校正公式的推导:

岭回归和Lasso 回归_第2张图片

BFGS校正的算法流程:

B_{k} 对称正定,B_{k+1}由上述的 BFGS 校正公式确定,那么 B_{k+1} 对称正定的充要条件是 y_{k}^{T}s_{k}> 0

在利用 Armijo 搜索准则时,并不是都满足上述的充要条件,此时可以对BFGS校正公式做些改变:

BFGS 算法流程如下

岭回归和Lasso 回归_第3张图片

利用Sherman-Morrison公式可对上式进行变换,得:

岭回归和Lasso 回归_第4张图片

蓝色部分表示实数

在BFGS算法中,每次都要储存近似Hesse 矩阵 B_{k}^{-1},在高维数据中,储存B_{k}^{-1}浪费很多的储存空间,在实际的应用中,我们需要搜索方向,因此出现L-BFGS算法,只保存最近的m次迭代信息,以降低数据的储存空间。

四、L-BFGS 算法

岭回归和Lasso 回归_第5张图片

这样在L-BPFS算法中不需要保存完整的Hk,而是储存向量序列{sk}和{yk},而向量序列{sk}和{yk}也不是都要保存,只要保存最新的m不向量即可。

L-BFGS 算法中确定新的下降方向的具体过程是:

岭回归和Lasso 回归_第6张图片

五、岭回归对数据的训练

在岭回归模型中,我们分别使用最小二乘法、拟牛顿法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 机器学习算法》(程序)


 

你可能感兴趣的:(机器学习)