首页 > 技术文章 > 2、线性回归(Linear Regression)算法 —— 监督、回归

ai-learning-blogs 2019-08-13 11:13 原文

1、线性回归(Linear Regression)模型

线性回归是利用数理统计中回归分析,来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法,运用十分广泛。回归分析中,只包括一个自变量和一个因变量,且二者的关系可用一条直线近似表示,这种回归分析称为一元线性回归分析。如果回归分析中包括两个或两个以上的自变量,且因变量和自变量之间是线性关系,则称为多元线性回归分析。许多功能更为强大的非线性模型(nonlinear model)可在线性模型的基础上通过高维映射可得。

适用条件:主要面向数值型或标称型数据。(1)自变量与因变量是否呈直线关系;(2)因变量是否符合正态分布;(3)因变量数值之间是否独立;(4)方差是否齐性。

2、系统模型

线性回归遇到的问题一般是这样的。我们有m个样本,每个样本对应于n维特征1个结果输出

训练数据的形式:

$(x_1^{(0)}, x_2^{(0)}, ...x_n^{(0)}, y_0), (x_1^{(1)}, x_2^{(1)},...x_n^{(1)},y_1), ... (x_1^{(m)}, x_2^{(m)}, ...x_n{(m)}, y_m)$

我们主要做的是通过找到参数$\left( {b{\rm{,}}{w_1}{\rm{,}} \cdots {\rm{,}}{w_n}} \right)$

线性回归模型如下:

$y= \sum\limits_{j = 1}^n {{w_j}} {x_j} + b$

其中$w$为回归系数,$b$为偏置。对于上式,令${x_0} = 1$,则上式可表示为:

$y = \sum\limits_{j = 0}^n {{w_j}} {x_j}$

矩阵化如下:

$Y = XW$

其中,标签$Y$为$m \times 1$的矩阵,训练特征$X$为$m \times n$的矩阵,回归系数$W$为$n \times 1$的矩阵。

3、构造损失函数 (平方损失函数/均方误差)

在线性回归模型中,其目标是求出线性回归方程。而线性回归的评价是指如何度量预测值与标签之间的接近程度。线性回归模型的损失函数常用平方损失函数/均方误差(Squared Loss),主要在于其处处可导。对于上述模型,其平方损失函数为:
$\begin{array}{l}
{l_w} = \frac{1}{2}{\sum\limits_{i = 1}^m {\left( {{y^{\left( i \right)}} - \sum\limits_j^n {{w_j}x_j^{\left( i \right)}} } \right)} ^2}\\
 = {\left( {Y - XW} \right)^T}\left( {Y - XW} \right)
\end{array}$

为此,可以得出如下最小化问题:

$\mathop {{\rm{min}}}\limits_w {\rm{ }}{l_w}$

4、线性回归问题的解法

1) 最小二乘法(Least squares,LS)

对于线性回归模型,其预测函数为:

$Y = XW$

其损失函数为:

${\left( {Y - XW} \right)^T}\left( {Y - XW} \right)$

对$W$求导,即

$\frac{\partial }{{\partial W}}{\left( {Y - XW} \right)^T}\left( {Y - XW} \right) = {X^T}\left( {Y - XW} \right)$

令其为0,可得

$\widehat W = {\left( {{X^T}X} \right)^{ - 1}}{X^T}Y$

2)批量梯度下降法(Batch Gradient Descent,BGD)

根据梯度下降时使用数据量的不同,梯度下降可以分为3类:批量梯度下降(Batch Gradient Descent,BGD)、随机梯度下降(Stochastic Gradient Descent, SGD)和小批量梯度下降(Mini-Batch Gradient Descent, MBGD)。选用批量梯度下降进行最优化问题的求解。

批量梯度下降的详细过程如下:

  • 随机选取一个超参数初始点${w_0}$;
  • 重复以下过程:
    • 确定梯度下降的方向:

${\nabla _{{W_j}}}\left( {{l_W}} \right) = \frac{{\partial {l_W}}}{{\partial {W_j}}} = \frac{\partial }{{\partial {W_j}}}{\left( {Y - X{W_j}} \right)^T}\left( {Y - X{W_j}} \right) = {X^T}\left( {Y - X{W_j}} \right)$

    • 选择步长$\alpha$
    • 更新权值:${W_{j + 1}} = {W_j} - \alpha  \cdot {\nabla _{{W_j}}}\left( {{l_W}} \right)$
  • 直到满足终止条件

通过若干次迭代后,即可得$W$的最终结果。

3)牛顿法

除了最小二乘法梯度下降法牛顿法也是机器学习中用的比较多的一种优化算法。其基本思想是利用迭代点${x_k}$处的一阶导数(梯度)和二阶导数(Hessen矩阵)对目标函数进行二次函数近似,然后把二次函数的极小点作为新的迭代点,并不断重复这一过程,直到求得满足精度的近似极小值。牛顿法下降的速度比梯度下降得快,而且能高度逼近最优值。牛顿法分为基本牛顿法全局牛顿法

基本牛顿法

对函数$f\left( x \right)$进行二阶泰勒,得到

$f\left( x \right) = f\left( {{x_k}} \right) + f{\rm{'}}\left( {{x_k}} \right)\left( {x - {x_k}} \right) + \frac{1}{2}f{\rm{''}}\left( {{x_k}} \right){\left( {x - {x_k}} \right)^2}$

对上式求导并令为0,则为

$f{\rm{'}}\left( x \right) = f{\rm{'}}\left( {{x_k}} \right) + f{\rm{''}}\left( {{x_k}} \right)\left( {x - {x_k}} \right) = 0$

即可得牛顿法的更新公式:

$x = {x_k} - \frac{{f{\rm{'}}\left( {{x_k}} \right)}}{{f{\rm{''}}\left( {{x_k}} \right)}}$

  • 基本牛顿法的流程
  1. 给定终止误差值$0 \le \varepsilon  \le 1$,初始点${x_0} \in {{\rm{R}}^n}$,令$k = 0$;
  2. 计算${g_k} = \nabla f\left( {{x_k}} \right)$,若$\left\| {{g_k}} \right\| \le \varepsilon $,则停止,输出${x^ * } \approx {x_k}$;
  3. 计算${G_k} = {\nabla ^2}f\left( {{x_k}} \right)$,并求解${d_k} =  - \frac{{{g_k}}}{{{G_k}}}$;
  4. 令${x_{k + 1}} = {x_k} + {d_k}$,$k = k + 1$,并转2。

全局牛顿法

牛顿法最突出的优点是收敛速度快,具有局部二阶收敛性,但是,基本牛顿法初始点需要足够“靠近”极小点,否则,有可能导致算法不收敛,此时便引入全局牛顿法。

  • 全局牛顿法的流程
  1. 给定终止误差值$0 \le \varepsilon  \le 1$,$\delta  \in \left( {0,1} \right)$,$\sigma  \in \left( {0{\rm{,}}0.5} \right)$,初始点${x_0} \in {{\rm{R}}^n}$,令$k = 0$;
  2. 计算${g_k} = \nabla f\left( {{x_k}} \right)$,若$\left\| {{g_k}} \right\| \le \varepsilon $,则停止,输出${x^ * } \approx {x_k}$;
  3. 计算${G_k} = {\nabla ^2}f\left( {{x_k}} \right)$,并求解${d_k} =  - \frac{{{g_k}}}{{{G_k}}}$;
  4. 设${m_k}$是不满足下列不等式(Armijo准则)的最小非负整数$m$:
    $f\left( {{x_k} + {\delta ^m}{d_k}} \right) \le f\left( {{x_k}} \right) + \sigma {\delta ^m}g_k^T{d_k}$
  5. 令${\alpha _k} = {\delta ^{{m_k}}}$,${x_{k + 1}} = {x_k} + {\alpha _k}{d_k}$,并转2。

损失函数一阶和二阶导函数

损失函数一阶导函数:

$\frac{{\partial l}}{{\partial {w_j}}} =  - \sum\limits_{i = 1}^m {\left\{ {\left( {{y^{\left( i \right)}} - \sum\limits_{j = 0}^n {{w_j} \cdot x_j^{\left( i \right)}} } \right) \cdot x_j^{\left( i \right)}} \right\}} $

损失函数二阶导函数:

$\frac{{\partial l}}{{\partial {w_j}\partial {w_k}}} = \sum\limits_{i = 1}^m {\left\{ {x_j^{\left( i \right)} \cdot x_k^{\left( i \right)}} \right\}} $

4、线性回归模型算法Python实践

针对本数据集(见附录),主要分为两个步骤来训练线性回归模型:

1)利用训练样本训练模型

# coding:UTF-8

import numpy as np
from math import pow
import matplotlib.pyplot as plt

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 least_square(feature, label):
    '''最小二乘法
    input:  feature(mat):特征
            label(mat):标签
    output: w(mat):回归系数
    '''
    w = (feature.T * feature).I * feature.T * label
    return w

def first_derivativ(feature, label, w):
    '''计算一阶导函数的值
    input:  feature(mat):特征
            label(mat):标签
    output: g(mat):一阶导数值
    '''
    m, n = np.shape(feature)
    g = np.mat(np.zeros((n, 1)))
    for i in range(m):
        err = label[i, 0] - feature[i, ] * w
        for j in range(n):
            g[j, ] -= err * feature[i, j]
    return g     

def second_derivative(feature):
    '''计算二阶导函数的值
    input:  feature(mat):特征
    output: G(mat):二阶导数值
    '''
    m, n = np.shape(feature)
    G = np.mat(np.zeros((n, n)))
    for i in range(m):
        x_left = feature[i, ].T
        x_right = feature[i, ]
        G += x_left * x_right
    return G

def get_error(feature, label, w):
    '''计算误差
    input:  feature(mat):特征
            label(mat):标签
            w(mat):线性回归模型的参数
    output: 损失函数值
    '''
    return (label - feature * w).T * (label - feature * w) / 2

def get_min_m(feature, label, sigma, delta, d, w, g):
    '''计算步长中最小的值m
    input:  feature(mat):特征
            label(mat):标签
            sigma(float),delta(float):全局牛顿法的参数
            d(mat):负的一阶导数除以二阶导数值
            g(mat):一阶导数值
    output: m(int):最小m值
    '''
    m = 0
    while True:
        w_new = w + pow(sigma, m) * d
        left = get_error(feature, label , w_new)
        right = get_error(feature, label , w) + delta * pow(sigma, m) * g.T * d
        if left <= right:
            break
        else:
            m += 1
    return m           

def newton(feature, label, iterMax, sigma, delta):
    '''牛顿法
    input:  feature(mat):特征
            label(mat):标签
            iterMax(int):最大迭代次数
            sigma(float), delta(float):牛顿法中的参数
    output: w(mat):回归系数
    '''
    n = np.shape(feature)[1]
    w = np.mat(np.zeros((n, 1)))
    it = 0
    while it <= iterMax:
        # print it
        g = first_derivativ(feature, label, w)  # 一阶导数
        G = second_derivative(feature)  # 二阶导数
        d = -G.I * g
        m = get_min_m(feature, label, sigma, delta, d, w, g)  # 得到最小的m
        w = w + pow(sigma, m) * d
        if it % 10 == 0:
            print ("\t---- itration: ", it, " , error: ", get_error(feature, label , w)[0, 0])
        it += 1       
    return w

def save_model(file_name, w):
    '''保存最终的模型
    input:  file_name(string):要保存的文件的名称
            w(mat):训练好的线性回归模型
    '''
    f_result = open(file_name, "w")
    m, n = np.shape(w)
    for i in range(m):
        w_tmp = []
        for j in range(n):
            w_tmp.append(str(w[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("data.txt")
    # 2.1、最小二乘求解
    print ("----------- 2.training ----------")
    # print "\t ---------- least_square ----------"
    # w_ls = least_square(feature, label)
    # 2.2、牛顿法
    print ("\t ---------- newton ----------")
    w_newton = newton(feature, label, 50, 0.1, 0.5)
    # 3、保存最终的结果
    print ("----------- 3.save result ----------")
    save_model("weights", w_newton)
    
    #作图
    xMat = feature
    yMat = label
    fig = plt.figure()
    ax = fig.add_subplot(111)
    #作原始数据散点图
    ax.scatter(xMat[:,1].flatten().A[0],yMat[:,0].flatten().A[0])

    #绘制最佳拟合直线,对数据按照升序排列
    xCopy = xMat.copy()
    xCopy.sort(0)
    yHat = xCopy*w_newton
    #作拟合曲线图
#    ax.plot(xCopy[:,1],yHat,'r')
    plt.show()
    
View Code

2)利用已训练好的模型对新样本进行预测

#coding:UTF-8

import numpy as np

def load_data(file_path):
    '''导入测试数据
    input:  file_path(string):训练数据
    output: feature(mat):特征
    '''
    f = open(file_path)
    feature = []
    for line in f.readlines():
        feature_tmp = []
        lines = line.strip().split("\t")
        feature_tmp.append(1)  # x0
        for i in range(len(lines)):
            feature_tmp.append(float(lines[i]))
        feature.append(feature_tmp)
    f.close()
    return np.mat(feature)

def load_model(model_file):
    '''导入模型
    input:  model_file(string):线性回归模型
    output: w(mat):权重值
    '''
    w = []
    f = open(model_file)
    for line in f.readlines():
        w.append(float(line.strip()))
    f.close()
    return np.mat(w).T
    
def get_prediction(data, w):
    '''得到预测值
    input:  data(mat):测试数据
            w(mat):权重值
    output: 最终的预测
    '''
    return data * w

def save_predict(file_name, predict):
    '''保存最终的预测值
    input:  file_name(string):需要保存的文件名
            predict(mat):对测试数据的预测值
    '''
    m = np.shape(predict)[0]
    result = []
    for i in range(m):
        result.append(str(predict[i,0]))
    f = open(file_name, "w")
    f.write("\n".join(result))
    f.close()    

if __name__ == "__main__":
    # 1、导入测试数据
    testData = load_data("data_test.txt")
    # 2、导入线性回归模型
    w = load_model("weights")
    # 3、得到预测结果
    predict = get_prediction(testData, w)
    # 4、保存最终的结果
    save_predict("predict_result", predict)
    
View Code

参考文献 

[1] 赵志勇. Python机器学习算法[M]. 北京:电子工业出版社,2017.

[2] 李航. 统计学习方法[M]. 北京:清华大学出版社,2012.

[3] 周志华. 机器学习[M]. 北京:清华大学出版社,2016.

附录

数据集下载

链接:https://pan.baidu.com/s/1cYsl0cAzb5na8IICFsqwsA

提取码:ymth

推荐阅读