多元线性回归分析--推导过程及python、numpy和python实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/EngineerHe/article/details/99343551

多元线性回归分析

在这里插入图片描述

什么是线性回归?

线性回归,如上图所示(这里用二维的例子比较好理解),我们知道许多的 ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) (x_1, y_1), (x_2, y_2), ..., (x_n, y_n) ,即图中红色的点,通过某种方法,得到图中蓝色的线( y = w × x + b y = w\times x + b ),即求 w b w, b 的值;然后可以使得未知数据 x n e w x_{new} 输入方程,使得结果 y n e w y_{new} 尽可能的接近真实值。

具体的关于线性回归的定义,这里是引用西瓜书上定义

定义:

​ 给定由 d d 个属性描述的示例 x = ( x 1 ; x 1 ; . . . ; x d ) x = (x_1; x_1; ... ; x_d) ,其中 x i x_i x \textbf{x} 在第 i i 个属性上的取值,线性模型(linear model)试图学得一个通过属性的组合来进行预测的函数,即
f ( x ) = w 1 x 1 + w 2 x 2 + . . . + w d x d + b f(x) = w_1x_1 + w_2x_2 + ... + w_dx_d + b
一般用向量形式写成
f ( x ) = w T x + b f(x) = w^Tx + b
其中 w = ( w 1 ; w 2 ; . . . ; w 1 ; ) w = (w_1;w_2;...;w_1;) w w b b 学习到以后,模型就得以确定

通常回归分为线性回归分析和非线性回归分析,判读二者最好的方法是看每一个变量的指数,如都是1次的就是线性,否则是非线性。

基于矩阵求解(解析解法或者公式法)

推导过程

假如我们的多元线性方程有n项,目标方程为
f ( x i ) = w 1 x 1 + w 2 x 2 + + w n x n + b f\left( {{x_i}} \right) = {w_1}{x_1} + {w_2}{x_2} + \cdots + {w_n}{x_n} + b
有对应的关于数据D( ( x 1 , x 2 , . . . , x n , y ) (x_1,x_2,...,x_n,y) ),需要根据输入的数据来求解( w i , b w_i,b )。这里对于最后的偏置项 b b ,可以认为是 w 0 x 0 w_0x_0 ,其中 x 0 x_0 是一个恒为1的数,求解 w 0 w_0 相当于求解 b b ,即
f ( x i ) = w 0 x 0 + w 1 x 1 + w 2 x 2 + + w n x n f\left( {{x_i}} \right) = {w_0}{x_0} + {w_1}{x_1} + {w_2}{x_2} + \cdots + {w_n}{x_n}
这样写成矩阵的形式为
f ( x ) = w T x f\left( x \right) = {w^T}{x}
其中 x x 是一个列向量, w w 是一个列向量

在数据D中有多组数据,就可以写出矩阵的形式

w T [ x 1 , x 2 , , x n ] = [ y 1 , y 2 , , y n ] {w^T}{ \left[ \begin{array}{c} {{x^1}} ,{{x^2}},\dots ,{{x^n}} \end{array} \right ]} ={ \left[ \begin{array}{ccc} {{y^1}},{{y^2}}, \dots ,{{y^n}} \end{array} \right ]}

如何确定 w w ,就是在于如何可以使得 y i y^i (目标值)和 y ^ i {{\hat y}^i} (预测值)直接最小,这里使用均方差来使得误差最小化,即定义下面的目标方程
w = arg min w i = 1 m ( y i y ^ i ) 2 = arg min w i = 1 m ( y i w T x i ) 2 \begin{array}{l} {w^*} &= \mathop {\arg \min }\limits_w \sum\limits_{i = 1}^m {({y^i} - } {{\hat y}^i}{)^2} \\ &= \mathop {\arg \min }\limits_w \sum\limits_{i = 1}^m {({y^i} - } {w^T}{x^i}{)^2} \end{array}
其中 w {w^*} 就是我们要找的最优的参数, m m 表示有m组数据。根据上式可以知道,目标函数是一个关于 w w 的凸函数,对于凸函数而言,一阶导数等于零对应的 w w 就是目标函数的最优值;为了求导方便,我们再把式子变换一下,式子中 x i x^i 是一个列向量,如果有多组数据的话就可以把他写成一个矩阵 X = ( x 1 , x 2 , . . . , x m ) X=(x^1,x^2,...,x^m) ,相应的真实值 y = ( y 1 , y 2 , . . . , y n ) T y=({y}^1,{y}^2,...,{y}^n)^T 是一个列向量,然后目标函数就可以转换为
E = ( y X T w ) T ( y X T w ) E = {(y - {X^T}w)^T}(y - {X^T}w)
w w 求导
E w = 2 X ( X T w y ) \frac{{\partial E}}{{\partial w}} = 2X({X^T}w - y)
E w = 0 \frac{{\partial E}}{{\partial w}} =0 ,就可以求的
w = ( X X T ) 1 X T y w = {(X{X^T})^{ - 1}}{X^T}y

代码实现

import numpy as np

def getParams(x, y):
    n = len(y)
    x_0 = np.ones(n)
    x = np.matrix(np.vstack([x_0, x]).T)
    y = np.matrix(y)
    params = ((x.T*x).I*x.T*(y.T))
    b = params[0]
    w = params[1:]
    return w, b
    
    
if __name__ == "__main__":
    n_samples = 10000
    x_1, x_2 = np.random.random(n_samples),np.random.random(n_samples)
    theta_1, theta_2, b = map(float, input().split())
    
    x, y= np.vstack([x_1, x_2]), theta_1*x_1 + theta_2*x_2 + b
    params = getParams(x, y)

梯度下降逼近法

线性回归梯度下降法,这里的梯度下降是指计算损失函数(loss function)的梯度;这个损失函数通常是均方差函数
E ( w ) = 1 2 m i = 1 m ( y ^ i y i ) 2 = 1 2 m i = 1 m ( w T x i + b y i ) 2 \begin{array}{l} E(w) &= \frac{1}{{2m}}\sum\limits_{i = 1}^m {({{\hat y}^i} - {y^i}} {)^2}\\ &= \frac{1}{{2m}}\sum\limits_{i = 1}^m {({w^T}{x^i} + b - {y^i}} {)^2} \end{array}
然后通过求解目标函数对于 w w 的梯度和对于 b b 的梯度,来不断的迭代更新他们的值,使得结果更加逼近真实值
E w = 1 m i = 1 m ( w T x i + b y i ) x i = 1 m i = 1 m ( y ^ i y i ) x i E b = 1 m i = 1 m ( w T x i + b y i ) x i = 1 m i = 1 m ( y ^ i y i ) \begin{array}{l} \frac{{\partial E}}{{\partial w}} &= \frac{1}{m}\sum\limits_{i = 1}^m {({w^T}{x^i} + b - {y^i}} ){x^i}\\ &= \frac{1}{m}\sum\limits_{i = 1}^m {({{\hat y}^i} - {y^i}} ){x^i} \end{array} \\ \begin{array}{l} \frac{{\partial E}}{{\partial b}}& = \frac{1}{m}\sum\limits_{i = 1}^m {({w^T}{x^i} + b - {y^i}} ){x^i}\\ &= \frac{1}{m}\sum\limits_{i = 1}^m {({{\hat y}^i} - {y^i}} ) \end{array}
迭代更新:
w = w α E w b = b α E b \begin{array}{l} w{\rm{ = }}w{\rm{ - }}\alpha \frac{{\partial E}}{{\partial w}}\\ \\ b = b - \alpha \frac{{\partial E}}{{\partial b}} \end{array}
其中 α \alpha 表示学习率(learning rate),用于控制每一步下降的步长(步长要选择适中,步长太小下降的速度比较慢,太大则可能造成不收敛;通常也可以选择动态步长的方法,即前期使用大步长用于快速下降,后期采用小步长使用收敛精度),这里由于使用的是逼近的方法,所以我们可以设置通过循环的次数来结束求解过程,也可以使用设置阈值的方法结束求解。

扫描二维码关注公众号,回复: 7205272 查看本文章

代码实现

这里代码实现,分为使用numpy的方法和使用机器学习框架(pytorch)的方法

使用numpy


import numpy as np

def gradient_descent(X, y, lr=0.01, threshold=1e-3):
    y = y[:, np.newaxis]
    
    W =np.ones(shape=(1,X.shape[1]))
    b =np.array([[1]])
    n = y.shape[0]
    
    while True:
        pred_y = np.dot(X, W.T) + b
        loss = np.dot((y-pred_y).T, (y-pred_y))/(2*n)
        if np.abs(loss) < threshold:
            break
        
        w_gradient = -(1.0/n)*np.dot((y-pred_y).T, X)
        b_gradient = -(1.0/n)*np.dot((y-pred_y).T, np.ones(shape=[n, 1]))

        W = W - w_gradient
        b = b - b_gradient
        print(loss)
    
    return W[0][0], W[0][1], b[0][0]


    
if __name__ == "__main__":
    n_samples = 10000
    x_0 = np.ones(n_samples)
    x_1, x_2 = np.random.random(n_samples),np.random.random(n_samples)
    theta_1, theta_2, b = map(float, input().split())
    X, y= np.vstack([x_1, x_2]).T, theta_1*x_1 + theta_2*x_2 + b
    
    params = gradient_descent(X, y)
    result = []
    for param in params:
        result.append('{:.02f}'.format(param))
    print(' '.join(result))

使用pytorch

import torch
import torch.nn as nn
import numpy as np

# 线性回归
class LinerRegression(nn.Module):
    def __init__(self):
        super(LinerRegression, self).__init__()
        self.pred = torch.nn.Linear(2, 1)
    
    def forward(self, x):
        x = self.pred(x)
        return x

# 产生10000个数据
n_samples = 10000
x_1, x_2 = np.random.random(n_samples),np.random.random(n_samples)
x_train = np.vstack([x_1, x_2]).T
# 设置想要的权重,产生y
theta_1, theta_2, b = 8.69, -20.66, 76.12
y_train = theta_1*x_1 + theta_2*x_2 + b

# 把numpy array转为为tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)
# 为数据添加一个维度,和预测结果相匹配
y_train = y_train.unsqueeze(1)

# 调用网络
net = LinerRegression().double()

# 采用随机梯度下降法优化函数
optimizer = torch.optim.SGD(net.parameters(), lr = 0.01)
# 使用均方差作为 loss function
criterion = nn.MSELoss()

# 迭代循环,当loss小于一定的值时,结束
num = 0
while True:
    pred_y = net(x_train)
    loss = criterion(pred_y, y_train)
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    if (num +1)%20==0:
        print('Loss: {:.6f}'.format(loss.data))
    
    if loss < 1e-3:
        break
    num += 1

# 保存参数
torch.save(net.state_dict(), 'params.pth')

# 加载参数
net.load_state_dict(torch.load('params.pth'))
# 输出每一个参数的值
for name, param in net.named_parameters():
    if name == 'pred.weight':
        print('W1:{:.02f}, W2:{:.02f}'.format(param.data[0][0].numpy(), param.data[0][1].numpy()))
    else:
        print('b:{:.02f}'.format(param.data[0].numpy()))

注: 文中有写错的地方,欢迎大家不吝指正!!!

作者:黑暗主宰

邮箱:[email protected]

猜你喜欢

转载自blog.csdn.net/EngineerHe/article/details/99343551
今日推荐