多元线性回归与梯度下降法原理及公式推导(附Python代码)

本文介绍如何使用梯度下降法求解线性回归问题,详细解释了损失函数的概念及其实现,并提供了Python实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以线性方程为例,设有线性方程:

ytrue=θ0+θ1x1+θ2x2…+θnxny_{true} = \theta_0 + \theta_1x_1 + \theta_2x_2…+ \theta_nx_nytrue=θ0+θ1x1+θ2x2+θnxn

如果现在我们手头上已经有了N组数据,其中第iii组数据为[xi,1xi,2xi,3…xi,n][x_{i,1} x_{i,2} x_{i,3}…x_{i,n}][xi,1xi,2xi,3xi,n]和其对应的yi,truey_{i,true}yi,true

我们想要通过这N组数据,估计出上述方程中的θ0\theta_0θ0θn\theta_nθn

现在问题转化为:

我们想求出一组θ^\hat{\theta}θ^,使得这组θ^\hat{\theta}θ^无限逼近上述方程中的θ\thetaθ,又因为真实的θ\thetaθ是未知的,我们无法直接比较两者的误差关系,所以我们引入Lose Function即误差函数来描述我们求得的θ^\hat{\theta}θ^到底准确与否。

有:

Lose=J=1N∑i=1N(yi,ture−yi^)2Lose = J = \frac{1}{N}\sum^{N}_{i = 1}(y_{i, ture} - \hat{y_i})^2Lose=J=N1i=1N(yi,tureyi^)2

又因为,若设x0=1x_0 = 1x0=1有:

y^=θ0^x0+θ1^x1+θ2^x2…+θn^xn\hat{y} = \hat{\theta_0}x_0 + \hat{\theta_1}x_1 + \hat{\theta_2}x_2 … + \hat{\theta_n}x_ny^=θ0^x0+θ1^x1+θ2^x2+θn^xn

LoseFunctionLose FunctionLoseFunction可改写为:

J=1N∑i=1N(yi,ture−θ0^x0−θ1^x1−θ2^x2…−θn^xn)2J = \frac{1}{N}\sum^{N}_{i = 1}(y_{i, ture} - \hat{\theta_0}x_0 - \hat{\theta_1}x_1 - \hat{\theta_2}x_2 … - \hat{\theta_n}x_n)^2J=N1i=1N(yi,tureθ0^x0θ1^x1θ2^x2θn^xn)2

可知上述误差函数JJJ有极小值,且使得JJJ最小的那一组θ^\hat{\theta}θ^最接近真实的θ\thetaθ

形象理解梯度下降

我们把问题简化一下若要求的线性方程为:

y=θxy = \theta xy=θx

则根据上述LoseFunctionLose FunctionLoseFunction可得出以下关系:
在这里插入图片描述
要想让误差值即JJJ的值最小,目前估计的点P就需要朝着蓝色箭头的方向移动,直到找到这个二次曲线的极小值即最小值点。在程序中,若想让P向目标点移动,需要加上−ηdJdθ,θ=θp-\eta\frac{dJ}{d\theta}, \theta = \theta_pηdθdJ,θ=θp。这里的η\etaη就是学习率

通过不断的移动P点,使得该次计算的JJJ值与上一次计算的JJJ值只差小于一个阀值,我们就认为找到了这个极小值点,且当前的θ^\hat{\theta}θ^接近真实的θ\thetaθ

而这个过程就是对LoseFunctionLose FunctionLoseFunction优化的过程。

梯度下降解决线性回归的原理

回到之前我们讨论的线性方程:

ytrue=θ0+θ1x1+θ2x2…+θnxny_{true} = \theta_0 + \theta_1x_1 + \theta_2x_2…+ \theta_nx_nytrue=θ0+θ1x1+θ2x2+θnxn

对于多元线性方程的优化过程,由于参数由一个变成多个,所以这里的导数也上升为梯度,即JJJ在其所在点要对当前组中θ0\theta_0θ0θn\theta_nθn均求偏导,而这样一组求偏导结果构成该点的梯度。

我们令x0=1x_0 = 1x0=1,则原方程可以改写为:

ytrue=θ0x0+θ1x1+θ2x2…+θnxny_{true} = \theta_0x_0 + \theta_1x_1 + \theta_2x_2…+ \theta_nx_nytrue=θ0x0+θ1x1+θ2x2+θnxn

LoseFunctionLose FunctionLoseFunction为:

J=1N∑i=1N(yi,ture−θ0^x0−θ1^x1−θ2^x2…−θn^xn)2J = \frac{1}{N}\sum^{N}_{i = 1}(y_{i, ture} - \hat{\theta_0}x_0 - \hat{\theta_1}x_1 - \hat{\theta_2}x_2 … - \hat{\theta_n}x_n)^2J=N1i=1N(yi,tureθ0^x0θ1^x1θ2^x2θn^xn)2

上式JJJθ0\theta _0θ0求偏导的结果为:

∂J∂θ0=2N∑i=1N(yi,true−θ0^x0−θ1^x1−θ2^x2…−θn^xn)(−x0)\frac{\partial J}{\partial \theta_0} = \frac{2}{N}\sum^{N}_{i = 1}(y_{i, true} - \hat{\theta_0}x_0 - \hat{\theta_1}x_1 - \hat{\theta_2}x_2 … - \hat{\theta_n}x_n)(-x_0)θ0J=N2i=1N(yi,trueθ0^x0θ1^x1θ2^x2θn^xn)(x0)

即:

∂J∂θ0=2N∑i=1N(yi,true−θ^x)(−x0)\frac{\partial J}{\partial \theta_0} = \frac{2}{N}\sum^{N}_{i = 1}(y_{i, true} - \hat{\theta}x)(-x_0)θ0J=N2i=1N(yi,trueθ^x)(x0)

=>∂J∂θ0=2N∑i=1N(θ^x−yi,true)(x0)\frac{\partial J}{\partial \theta_0} = \frac{2}{N}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_0)θ0J=N2i=1N(θ^xyi,true)(x0),左式中θ^\hat{\theta}θ^为行向量,xxx为列向量。

JJJθi\theta _iθi求偏导的结果为:

∂J∂θ0=2N∑i=1N(θ^x−yi,true)(xi)\frac{\partial J}{\partial \theta_0} = \frac{2}{N}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_i)θ0J=N2i=1N(θ^xyi,true)(xi)

这样求完所有θ\thetaθ的偏导,构成该点的梯度:

∇J∇θ=[∂J∂θ0=2N∑i=1N(θ^x−yi,true)(x0)∂J∂θ1=2N∑i=1N(θ^x−yi,true)(x1)∂J∂θ2=2N∑i=1N(θ^x−yi,true)(x2)……∂J∂θn=2N∑i=1N(θ^x−yi,true)(xn)]\frac{\nabla J}{\nabla \theta} =\left[ \begin {matrix}\frac{\partial J}{\partial \theta_0} = \frac{2}{N}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_0) \\ \frac{\partial J}{\partial \theta_1} = \frac{2}{N}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_1) \\ \frac{\partial J}{\partial \theta_2} = \frac{2}{N}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_2) \\ … \\ … \\ \frac{\partial J}{\partial \theta_n} = \frac{2}{N}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_n)\end {matrix} \right]θJ=θ0J=N2i=1N(θ^xyi,true)(x0)θ1J=N2i=1N(θ^xyi,true)(x1)θ2J=N2i=1N(θ^xyi,true)(x2)θnJ=N2i=1N(θ^xyi,true)(xn)

若此时估计的θ\thetaθ为:

θ^now=[θ0^θ1^...θn^]\hat{\theta }_{now} = \left[ \begin {matrix} \hat{\theta_0} \\ \hat{\theta_1} \\ . \\ . \\ . \\\hat{\theta_n}\end {matrix}\right]θ^now=θ0^θ1^...θn^

若学习率为η\etaη,则下一次迭代的θ^next\hat{\theta}_{next}θ^next为:

θ^next=θ^now−η∇J∇θ=[θ0^θ1^θ2^..θn^]−2ηN[∑i=1N(θ^x−yi,true)(x0)∑i=1N(θ^x−yi,true)(x1)∑i=1N(θ^x−yi,true)(x2)……∑i=1N(θ^x−yi,true)(xn)]\hat{\theta}_{next} = \hat{\theta }_{now} - \eta\frac{\nabla J}{\nabla \theta} = \left[ \begin {matrix} \hat{\theta_0} \\ \hat{\theta_1} \\ \hat{\theta_2} \\ . \\ . \\\hat{\theta_n}\end {matrix}\right] - \frac{2\eta}{N}\left[ \begin {matrix}\sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_0) \\ \sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_1) \\ \sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_2) \\ … \\ … \\ \sum^{N}_{i = 1}(\hat{\theta}x - y_{i, true} )(x_n)\end {matrix} \right]θ^next=θ^nowηθJ=θ0^θ1^θ2^..θn^N2ηi=1N(θ^xyi,true)(x0)i=1N(θ^xyi,true)(x1)i=1N(θ^xyi,true)(x2)i=1N(θ^xyi,true)(xn)

有的地方将除以NNN凑成除以2N2N2N这样后边的因数化简为−ηN-\frac{\eta}{N}Nη

以上便是梯度下降解决线性回归的原理,大家可以自行对公式进行推导加深理解,附上Python仿真代码。

Python仿真代码

import matplotlib.pyplot as plt
import numpy as np


# 根据当前的theta求Y的估计值
# 传入的data_x的最左侧列为全1,即设X_0 = 1
def return_Y_estimate(theta_now, data_x):
    # 确保theta_now为列向量
    theta_now = theta_now.reshape(-1, 1)
    _Y_estimate = np.dot(data_x, theta_now)

    return _Y_estimate


# 求当前theta的梯度
# 传入的data_x的最左侧列为全1,即设X_0 = 1
def return_dJ(theta_now, data_x, y_true):
    y_estimate = return_Y_estimate(theta_now, data_x)
    # 共有_N组数据
    _N = data_x.shape[0]
    # 求解的theta个数
    _num_of_features = data_x.shape[1]
    # 构建
    _dJ = np.zeros([_num_of_features, 1])
    
    for i in range(_num_of_features):
        _dJ[i, 0] = 2 * np.dot((y_estimate - y_true).T, data_x[:, i]) / _N
    
    return _dJ


# 计算J的值
# 传入的data_x的最左侧列为全1,即设X_0 = 1
def return_J(theta_now, data_x, y_true):
    # 共有N组数据
    N = data_x.shape[0]
    temp = y_true - np.dot(data_x, theta_now)
    _J = np.dot(temp.T, temp) / N
    
    return _J


# 梯度下降法求解线性回归
# data_x的一行为一组数据
# data_y为列向量,每一行对应data_x一行的计算结果
# 学习率默认为0.3
# 误差默认为1e-8
# 默认最大迭代次数为1e4
def gradient_descent(data_x, data_y, Learning_rate = 0.01, ER = 1e-10, MAX_LOOP = 1e5):
    # 样本个数为
    _num_of_samples = data_x.shape[0]
    # 在data_x的最左侧拼接全1列
    X_0 = np.ones([_num_of_samples, 1])
    new_x = np.column_stack((X_0, data_x))
    # 确保data_y为列向量
    new_y = data_y.reshape(-1, 1)
    # 求解的未知元个数为
    _num_of_features = new_x.shape[1]
    # 初始化theta向量
    theta = np.zeros([_num_of_features, 1]) * 0.3
    flag = 0  	# 定义跳出标志位
    last_J = 0  # 用来存放上一次的Lose Function的值
    ct = 0  	# 用来计算迭代次数
    
    while flag == 0 and ct < MAX_LOOP:
        last_theta = theta
        # 更新theta
        gradient =  return_dJ(theta, new_x, new_y)
        theta = theta - Learning_rate * gradient
        er = abs(return_J(last_theta, new_x, new_y) - return_J(theta, new_x, new_y))
        
        # 误差达到阀值则刷新跳出标志位
        if er < ER :
            flag = 1
        
        # 叠加迭代次数
        ct += 1
        
    return theta
            

def main():
    # =================== 样本数据生成 =======================
    # 生成数据以1元为例,要估计的theta数为2个
    num_of_features = 1
    num_of_samples = 2000
    # 设置噪声系数
    rate = 0
    X = []
    
    for i in range(num_of_features):
        X.append(np.random.random([1, num_of_samples]) * 10)
        
    X = np.array(X).reshape(num_of_samples, num_of_features)
    print("X的数据规模为 : ", X.shape)
    
    # 利用方程生成X对应的Y
    Y = []

    for i in range(num_of_samples):
        Y.append(3 + 3.27 * X[i][0] + np.random.rand() * rate)

    Y = np.array(Y).reshape(-1, 1)
    print("Y的数据规模为 : ", Y.shape)
    # ======================================================
    
   	# 计算并打印结果
    print(gradient_descent(X, Y))
    

if __name__ == '__main__':
    main()
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值