线性回归(Linear Regression)原理及手工实现_解析解法、梯度下降法求解最优解

一、什么是线性回归

直观来讲,就是 y = k x + b y = kx+b y=kx+b的形式,可以用一条直线来进行拟合。
一元线性回归:只有一个自变量和一个因变量, y = k 1 x 1 + b y = k_1x_1+b y=k1x1+b的形式表示。
多元线性回归:两个及以上的自变量和一个因变量, y = k 1 x 1 + k 2 x 2 + . . . + k n x n + b y=k_1x_1+k_2x_2+...+k_nx_n+b y=k1x1+k2x2+...+knxn+b的形式表示。
在这里可以再与多项式回归进行区分, y = k 1 x 1 + k 2 x 2 2 + . . . + k n x n n + b y=k_1x_1+k_2x_2^2+...+k_nx_n^n+b y=k1x1+k2x22+...+knxnn+b的形式表示。

二、最小二乘法与解析解

“基于均方误差(mean-square error, MSE)最小化来进行模型求解的方法称为“最小二乘法””–《机器学习》周志华
最小二乘法(又称最小平方法)可采用均方误差来定义损失函数: L ( w ) = ∑ i = 1 n ( w T x i − y i ) 2 L(w)=\sum_{i=1}^{n}(w^\mathrm{T}x_i-y_i)^2 L(w)=i=1n(wTxiyi)2若要最小化损失函数,即可转化为对其求导,令导数为0,即可得到 w w w的解析解。
因此可以定义目标函数 J ( w ) = a r g m i n L ( w ) J(w)=argminL(w) J(w)=argminL(w),找到一个 w w w,使得损失函数 L ( w ) L(w) L(w)的值最小。
最终得到的解析解: w ^ = ( X T X ) − 1 X T y \hat{w}=(X^\mathrm{T}X)^{-1}X^\mathrm{T}y w^=(XTX)1XTy详细推导过程可参考:https://www.bilibili.com/video/BV1hW41167iL
“像线性回归这样的简单问题存在解析解,但并不是所有的问题都存在解析解。解析解可以进行很好的数学分析,但解析解的限制很严格,导致它无法应用在深度学习里。” --https://www.bilibili.com/video/BV1hW41167iL
因此引入梯度下降(gradient descent)方法,是深度学习模型中常见的优化方法,可以用于求解最小二乘问题(线性非线性都可以)。

三、梯度下降法

梯度下降法的计算过程就是沿梯度下降的方向求解极小值,进行多次迭代: w = w − η ∂ ∂ w J ( w ) w=w-\eta\frac{\partial}{\partial{w}}J(w) w=wηwJ(w)其中 w w w为模型的可学习参数, J ( w ) J(w) J(w)为目标函数, η \eta η为学习率。
批量梯度下降法(Batch Gradient Descent, BGD):使用整个训练集去计算目标函数的梯度。由于每次使用全部数据来计算梯度去更新参数,速度会很慢,而且数据很难一次性全部载入内存当中进行计算。首先定义单个样本的损失函数: l ( i ) ( w ) = 1 2 ( w T x ( i ) − y ( i ) ) 2 l^{(i)}(w)=\frac{1}{2}(w^\mathrm{T}x^{(i)}-y^{(i)})^2 l(i)(w)=21(wTx(i)y(i))2采用全部训练集数据更新的批量梯度下降法如下所示,其中 N N N为训练集样本的总个数, j j j为迭代的次数: w j + 1 = w j − η N ∑ i = 1 N ∂ ∂ w j l ( i ) ( w j ) w_{j+1}=w_j-\frac{\eta}{N}\sum_{i=1}^{N}\frac{\partial}{\partial{w_j}}l^{(i)}(w_j) wj+1=wjNηi=1Nwjl(i)(wj) 随机梯度下降法(Stochastic Gradient Descent, SGD):每次只采用一个样本来计算梯度。速度快但是每次迭代方向变化大,导致不能快速收敛到最优解。 w j + 1 = w j − η ∂ ∂ w j l ( i ) ( w j ) w_{j+1}=w_j-{\eta}\frac{\partial}{\partial{w_j}}l^{(i)}(w_j) wj+1=wjηwjl(i)(wj)小批量梯度下降法(Mini-Batch Gradient Descent, MBGD):对批量梯度下降和随机梯度下降进行了折中,随机抽取固定数量的训练样本 β \beta β ( 1 < ∣ β ∣ < N ) (1<|\beta|<N) (1<β<N),与批量梯度下降法不同的是将 N N N改为了 ∣ β ∣ |\beta| β w j + 1 = w j − η ∣ β ∣ ∑ i = 1 ∣ β ∣ ∂ ∂ w j l ( i ) ( w j ) w_{j+1}=w_j-\frac{\eta}{|\beta|}\sum_{i=1}^{|\beta|}\frac{\partial}{\partial{w_j}}l^{(i)}(w_j) wj+1=wjβηi=1βwjl(i)(wj)

四、线性回归从零实现

假设我们有1000个样本的训练集,每个样本 x ( i ) x^{(i)} x(i)是3维的,也就是有三个特征, x 1 ( i ) x^{(i)}_1 x1(i) x 2 ( i ) x^{(i)}_2 x2(i) x 3 ( i ) x^{(i)}_3 x3(i)。有要拟合的直线: Y = w 1 x 1 + w 2 x 2 + w 3 x 3 + b = W T X + b Y=w_1x_1+w_2x_2+w_3x_3+b=W^\mathrm{T}X+b Y=w1x1+w2x2+w3x3+b=WTX+b假设 Y = 2 x 1 − 3 x 2 + 4 x 3 − 5 Y=2x_1-3x_2+4x_3-5 Y=2x13x2+4x35,如何使用线性回归模型和梯度下降法来不断迭代求得参数呢?

1、首先生成数据集

对于 Y = 2 x 1 − 3 x 2 + 4 x 3 − 5 Y=2x_1-3x_2+4x_3-5 Y=2x13x2+4x35,训练集中 X ∈ R 1000 × 3 X\in{R}^{1000\times3} XR1000×3 Y ∈ R 1000 × 1 Y\in{R}^{1000\times1} YR1000×1

import torch
def generate_data(w, b, num_examples):  
    """ Y = 2a - 3b + 4c - 5 + noise """
    X = torch.normal(0, 1, (num_examples, len(w)))
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X, y.reshape((-1, 1))

true_w = torch.tensor([2., -3., 4.])
true_b = 5.
features, labels = generate_data(true_w, true_b, 1000)

2、小批量读取数据集

1000个数据中,batch_size=8,整除后共125组。batch_X[125,8,3],batch_y[125,8,1]。
这里先将1000个样本数据打乱,然后按照顺序、无放回地8个8个取样本组成一个batch。因此,在后面训练模型的时候,每一个epoch,都会按组(125组)来遍历,每次会遍历完所有组。

def data_iterater(X, y, batch_size):
    num = len(X)
    indices = list(range(num))
    random.shuffle(indices)  # 将顺序打乱
    batch_X = torch.zeros([num//batch_size, batch_size, len(true_w)])
    batch_y = torch.zeros([num//batch_size, batch_size, 1])
    for id, i in enumerate(range(0, num, batch_size)):
        batch_indices = torch.tensor(indices[i: min(i + batch_size, num)])
        batch_X[id,:,:] = features[batch_indices]
        batch_y[id,:,:] = labels[batch_indices]
    return batch_X, batch_y

batch_size = 8
batch_X, batch_y = data_iterater(features, labels, batch_size)

3、定义模型

# 定义线性模型
def Linear_Model(X, w, b):
    return torch.matmul(X, w) + b

4、定义损失函数、优化方法

# 定义均方损失函数
def loss_sq(predict, y):
    return (predict - y) ** 2 / 2
    
# 定义小批量梯度下降优化方法
def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

5、训练

训练前初始化模型参数 w , b w,b w,b。以及相应超参数学习率 l r lr lr,模型迭代次数 e p o c h s epochs epochs等等。

w = torch.normal(0, 0.01, size=(3,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
lr = 1e-3
epochs = 100
for epoch in range(epochs):
    """在每个epoch中,小批量梯度遍历整个数据集,也就是每次batch_size大小取样本
    并将训练数据集中所有样本都使用一次(假设样本数能够被批量大小整除)。"""
    for (X, y) in zip(batch_X, batch_y):
        predict = Linear_Model(X, w, b)
        loss = loss_sq(predict, y)
        loss.sum().backward()  # 计算梯度
        sgd([w, b], lr, batch_size)  # 更新参数值
    with torch.no_grad():
        train_l = loss_sq(Linear_Model(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
print(f'true_w {true_w}, true_b {true_b}')
print(f'pred_w {w}, pred_b {b}')

6、结果

epoch 1, loss 21.021652
epoch 2, loss 16.366686
epoch 3, loss 12.744938
epoch 4, loss 9.926495
epoch 5, loss 7.732749
.......................
epoch 99, loss 0.000052
epoch 100, loss 0.000052
true_w tensor([ 2., -3.,  4.]), true_b 5.0
pred_w tensor([[ 2.0000],
        [-2.9998],
        [ 3.9995]], requires_grad=True), pred_b tensor([5.0002], requires_grad=True)

五、参考

[1]https://zh-v2.d2l.ai/chapter_linear-networks/linear-regression-scratch.html
[2]https://www.bilibili.com/video/BV1hW41167iL

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用梯度下降实现线性回归模型的Python代码: ```python import numpy as np import pandas as pd import matplotlib.pyplot as plt # 加载数据集 data = pd.read_csv('linearRegression_data.txt', header=None, names=['x', 'y']) # 特征缩放 data['x'] = (data['x'] - data['x'].mean()) / data['x'].std() # 添加一列全为1的向量作为截距 data.insert(0, 'Ones', 1) # 将数据集分为特征和目标变量 X = np.matrix(data.iloc[:, :-1].values) y = np.matrix(data.iloc[:, -1:].values) # 初始化theta theta = np.matrix(np.zeros((1, X.shape[1]))) # 定义代价函数 def computeCost(X, y, theta): inner = np.power(((X * theta.T) - y), 2) return np.sum(inner) / (2 * len(X)) # 定义梯度下降函数 def gradientDescent(X, y, theta, alpha, iters): temp = np.matrix(np.zeros(theta.shape)) parameters = int(theta.ravel().shape[1]) cost = np.zeros(iters) for i in range(iters): error = (X * theta.T) - y for j in range(parameters): term = np.multiply(error, X[:, j]) temp[0, j] = theta[0, j] - ((alpha / len(X)) * np.sum(term)) theta = temp cost[i] = computeCost(X, y, theta) return theta, cost # 设置学习率和迭代次数 alpha = 0.01 iters = 1000 # 运行梯度下降 theta, cost = gradientDescent(X, y, theta, alpha, iters) # 输出最终的theta值 print(theta) # 绘制代价函数随迭代次数的变化图 fig, ax = plt.subplots() ax.plot(np.arange(iters), cost, 'r') ax.set_xlabel('Iterations') ax.set_ylabel('Cost') ax.set_title('Error vs. Training Epoch') plt.show() ``` 上述代码中,我们首先加载数据集,并进行特征缩放,然后添加一列全为1的向量作为截距。接着,我们将数据集分为特征和目标变量,并初始化theta。然后,我们定义代价函数和梯度下降函数。在梯度下降函数中,我们迭代地更新theta,并计算代价函数的值。最后,我们设置学习率和迭代次数,并运行梯度下降。最终,输出得到的theta值,并绘制代价函数随迭代次数的变化图。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值