动手学人工智能-线性神经网络1-线性回归

290 阅读6分钟

一、什么是线性回归

在日常生活和科学研究中,回归问题随处可见。简单来说,回归问题是根据已有的观察数据,来预测连续数值输出的一种任务。比如,我们希望通过了解房屋的特征(如面积、地理位置、房龄等),来预测房屋价格;或者通过分析学生的平时作业成绩、课外活动参与情况,来预测他们的最终考试成绩。

这些都是回归问题的典型例子,即输入变量与输出变量之间存在某种关系,我们希望能够学习到这个关系,以便在给出新的输入时预测输出值

1.1 线性回归在实际中的应用

现实中,线性回归 作为一种简单、易于理解的模型广泛应用于各种场景。它假设输出值与输入值之间的关系可以近似为一个线性函数,即通过 加权求和和偏移(或截距) 来预测输出。这种假设虽然简单,但在很多场景中表现不错,尤其是在关系较为直接的情况下。

1.2 以下是几个实际中的应用例子:

  • 经济学和金融学:在经济领域,线性回归常用于分析某些经济指标之间的关系。例如,通过一个地区的平均收入和消费水平来预测未来的消费需求。

  • 工程领域:在工程中,比如控制系统设计、机械结构的预测中,常常使用线性回归模型来拟合实验数据,从而理解物理系统的行为。

  • 医学与健康研究:在医学研究中,线性回归可以用来根据患者的某些体征或指标,预测他们的病情发展或疗效,例如通过病人的年龄、体重、血压等特征,预测患某种疾病的概率。

1.3 线性回归的目标

在机器学习中,我们通过一组输入变量来预测一个目标变量。具体来说,给定一些数据,我们希望能够找到一个最优的线性模型,使得该模型在给定数据的基础上准确地预测输出

在线性回归中,这个 “最优” 体现在最小化预测值与真实值之间的误差,因此我们需要定义一个损失函数来衡量这种误差,并用一定的方法不断优化模型,使得损失函数的值尽可能小。后续内容中,我们将继续探讨如何通过具体的方法来实现线性回归模型,并优化它的预测效果。

二、线性回归模型

线性回归模型 中,我们假设输入变量输出变量之间的关系可以通过一个线性函数来表示。具体来说,给定输入特征,我们想要预测输出,模型的目标是找到合适的参数,使得该线性函数尽可能贴近数据的实际分布

2.1 模型表示

对于一个样本数据点 (𝑥,y)(\mathbf{𝑥}, y),我们定义:

  • 输入特征x=(x1,x2,,xd)\mathbf{x} = (x_1, x_2, \cdots, x_d),这里的每个 xix_i 表示输入特征中的一个分量。
  • 输出yy 表示我们希望预测的目标值。

线性回归模型中,我们使用一个线性函数来表示输入与输出的关系:

y^=w1x1+w2x2++wdxd+b\hat{y} = w_1x_1 + w_2x_2 + \cdots + w_dx_d + b

其中:

  • y^\hat{y} 是模型的预测值。
  • w=(w1,w2,,wd)\mathbf{w} = (w_1, w_2, \dots, w_d)权重向量,表示每个特征 xix_i 对输出的贡献
  • bb偏置(或截距),用于调整模型的整体输出

简化表示为向量形式:

y^=xw+b\hat{y} = \mathbf{x} \cdot \mathbf{w} + b

这其中的 𝑥𝑤𝑥⋅𝑤 是向量内积,将输入特征向量与权重向量逐元素相乘并求和,得出一个标量结果,再加上偏置𝑏𝑏

2.2 线性假设的意义

在此模型中,我们假设输入特征和输出之间的关系是线性的。这种线性关系意味着:特征的变化会直接比例地影响输出,并且我们可以通过找到合适的权重和偏置来调整这种影响的大小。例如,如果我们增加一个特征值,那么输出值会相应增加或减少,具体取决于该特征对应的权重。

尽管现实中的问题往往更为复杂,非线性关系更加普遍,但线性回归仍然是一种重要的模型,它能帮助我们理解特征与输出之间的简单关系,并且在数据关系较为直接的情况下效果良好。

2.3 参数的意义

在模型中,权重 𝑤\mathbf{𝑤} 控制着各个特征对输出的影响:

  • 当权重值较大时,对应的特征对输出的影响较大;
  • 当权重值较小甚至接近 0 时,对应的特征对输出的影响较小。

偏置项 𝑏𝑏 的作用则在于:它使得模型的输出不完全依赖输入,即使所有特征 𝑥i𝑥_i 都为零时,模型仍然可以有一个非零的预测输出

2.4 预测过程

当模型训练完成后,我们就可以使用它来进行预测了。给定一个新的输入特征 x\mathbf{x},我们只需要将其代入上述公式,通过加权求和和加上偏置即可得到预测值 𝑦^\hat{𝑦}

例如,对于一个房价预测模型,假设特征是房屋的面积、房龄和位置,我们可以将这些特征的值代入训练好的线性回归公式,得到该房屋的预测价格。

三、损失函数

在线性回归模型中,我们利用输入特征 𝑥𝑥 来预测输出 y^\hat{y},但预测结果 y^\hat{y} 和真实的目标值 𝑦𝑦 之间往往会存在差距。为了量化这种差距,我们引入了损失函数

3.1 什么是损失函数?

损失函数(Loss Function) 是一个度量标准,用来计算模型预测值 y^\hat{y} 与真实值 𝑦𝑦 的差距。它的主要作用是告诉我们模型的预测结果有多好或多差。通过最小化损失函数,我们可以优化模型参数,从而让模型的预测更接近实际值

在线性回归中,常用的损失函数是均方误差(Mean Squared Error, MSE),它通过计算预测值与真实值之间的平方差来衡量预测的好坏

3.2 均方误差损失函数

对于一个样本数据点(𝑥,y)(\mathbf{𝑥}, y),线性回归模型的预测值为 y^=xw+b\hat{y} = \mathbf{x} \cdot \mathbf{w} + b。均方误差损失函数的定义如下:

MSE=1ni=1n(y(i)y^(i))2\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y^{(i)} - \hat{y}^{(i)})^2

其中:

  • nn 表示样本总数;
  • y(i)y^{(i)} 表示第 ii 个样本的真实标签;
  • y^(i)\hat{y}^{(i)} 表示第 ii 个样本的预测值。

在均方误差中,每个预测误差都被平方,这样无论预测值高估还是低估,都将作为正值被考虑。这种平方的设计有两个主要作用:

  • 惩罚大的偏差:偏差越大,平方后的误差越大,使模型更关注大的错误。
  • 平滑误差:平方后的误差具有良好的数学性质,便于优化。

3.3 损失函数的意义

损失函数值的大小反映了模型预测的准确性:

  • 损失越小:表示预测值 y^\hat{y} 越接近真实值 𝑦𝑦,模型越准确。
  • 损失越大:表示预测值与真实值的偏差越大,模型的预测越不准确。

在模型训练中,我们通过调整权重 𝑤𝑤 和偏置 𝑏𝑏 来最小化损失函数的值。这过程叫做模型优化。通过逐步调整参数,损失函数值会逐渐减小,从而让模型的预测越来越精准。

3.4 为什么选择均方误差?

均方误差是常用的回归损失函数之一,选择它的原因在于:

  • 数学性质良好:均方误差对参数是平滑且连续可导的,这对优化算法非常有利。
  • 惩罚效果适中:平方项放大了较大的误差,对异常值有一定的惩罚作用。

尽管均方误差并不适用于所有场景,但在回归问题中,其简单性和数学上的易处理性使得它成为一种常见选择。

四、解析解

我们了解了均方误差作为损失函数的作用。然而,为了找到使损失函数最小的模型参数,我们需要一个优化方法。在线性回归中,解析解提供了一个无需迭代的方法来直接计算出权重和偏置。这一方法依赖于矩阵运算,可以显著简化计算过程。

4.1 矩阵形式的表示

假设我们有 nn 个样本,每个样本包含 dd 个特征。我们可以使用矩阵 XRn×dX \in \mathbb{R}^{n \times d} 来表示所有特征,yRny \in \mathbb{R}^{n} 表示标签向量,权重向量为 wRdw \in \mathbb{R}^{d},偏置为 bRb \in \mathbb{R}。模型预测可以表示为:

y^=Xw+b\hat{y} = Xw + b

其中,y^\hat{y} 是预测值的向量形式。

4.2 损失函数的矩阵形式

均方误差损失函数可以用矩阵表示简化为:

L(w,b)=12nXw+by2\mathbf{L}(w, b)=\frac{1}{2n}\|Xw+b-y\|^2

这里,2\| \cdot \|^2 表示向量的 L2L_2 范数。通过最小化 L(w,b)L(w, b),我们可以找到最佳的 wwbb

4.3 求解解析解

为了得到解析解,我们对 wwbb 求偏导并令偏导数为 0。首先,我们将偏置项 bb 合并到权重向量中。为此,我们定义扩展矩阵 X~Rn×(d+1)\tilde{X} \in \mathbb{R}^{n \times (d+1)},其中最后一列为全 1。然后将权重扩展为 w~=[w b]\tilde{w} = \begin{bmatrix} w \ b \end{bmatrix}。因此,模型变为:

y^=X~w~\hat{y} = \tilde{X}\tilde{w}

均方误差损失函数现在可以重写为:

L(w~)=12nX~w~y2L(\tilde{w})=\frac{1}{2n}\|\tilde{X}\tilde{w}-y\|^2

接下来,我们对 w~\tilde{w} 求导:

L(w~)w~=1nXT(Xw~y)\frac{\partial L(\tilde{w})}{\partial \tilde{w}} = \frac{1}{n} X^T (X \tilde{w} - y)

令该导数为 0,得到正常方程:

XTXw~=XTyX^T X\tilde{w} = X^T y

X~TX~\tilde{X}^T \tilde{X} 可逆的情况下,解析解为:

w~=(XTX)1XTy\tilde{w} = (X^T X)^{-1} X^T y

4.4 实际应用中的注意事项

解析解的计算时间复杂度为 O(d3)O(d^3),这意味着当特征数 dd 较大时,计算会非常耗时。在这种情况下,使用梯度下降等迭代优化方法可能更为实际。

五、小批量随机梯度下降

小批量随机梯度下降(Stochastic Gradient Descent, SGD)是优化模型参数的常用方法。与标准的批量梯度下降相比,小批量随机梯度下降能在每次迭代中处理较小数据集,既提高计算效率,也有助于模型的泛化能力。

5.1 梯度下降法的基本原理

梯度下降法是一种通过反向传播损失函数的梯度,逐步减小模型损失的优化方法。在梯度下降法中,损失函数对模型参数(例如权重 𝑤𝑤 和偏置 𝑏𝑏)的导数用于调整这些参数,从而逐渐逼近最优解。标准的更新公式如下:

w:=wηL(w,b)ww := w - \eta \cdot \frac{\partial L(w, b)}{\partial w}
b:=bηL(w,b)bb := b - \eta \cdot \frac{\partial L(w, b)}{\partial b}

其中,η\eta 为学习率,用于控制每次参数更新的步长。

5.2 小批量随机梯度下降的实现步骤

在小批量随机梯度下降中,我们将数据集分为多个小批量(batch),并在每次迭代中从中随机选取一个小批量进行计算。假设数据集为 {(x1,y1),,(xn,yn)}\{ (x_1, y_1), \ldots, (x_n, y_n) \},我们在每次迭代中使用一个大小为 mm 的小批量 BB,计算该小批量上的损失函数梯度,更新公式如下:

w:=wη1miBL(w,b;xi,yi)ww := w - \eta \cdot \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial w}
b:=bη1miBL(w,b;xi,yi)bb := b - \eta \cdot \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial b}

在每次迭代中,我们更新参数 𝑤𝑤𝑏𝑏,直至损失函数达到最小值或达到设定的最大迭代次数。

5.3 更新参数的公式推导

对于每个小批量 𝐵𝐵,我们通过以下步骤更新参数:

  1. 计算小批量损失函数的梯度:对于每个样本 (xi,yi)(x_i, y_i) 属于小批量 𝐵𝐵,计算损失函数 L(w,b;xi,yi)L(w,b;x_i,y_i)的偏导数,并取平均值:
1miBL(w,b;xi,yi)w1miBL(w,b;xi,yi)b \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial w} \quad \text{和} \quad \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial b}
  1. 更新参数:使用上一步中得到的梯度值更新模型参数 𝑤𝑤𝑏𝑏
w:=wη1miBL(w,b;xi,yi)ww := w - \eta \cdot \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial w}
b:=bη1miBL(w,b;xi,yi)bb := b - \eta \cdot \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial b}

通过以上推导,我们可以看到小批量随机梯度下降如何实现逐步更新模型参数。

5.4 小批量随机梯度下降的优点

  • 计算效率更高:由于每次更新只需计算一个小批量的梯度,因此计算开销较小,更适合大型数据集的处理。
  • 更好的泛化能力:在小批量更新的过程中,梯度会带有随机性,有助于模型跳出局部最优解。
  • 支持并行计算:每个小批量可以在不同的计算单元上并行处理,从而加速计算。

5.5 学习率的影响

在小批量随机梯度下降中,学习率 η\eta 的选择十分关键。如果学习率过大,可能导致模型参数在更新过程中震荡,无法收敛;如果学习率过小,则会导致收敛速度缓慢。因此,选择合适的学习率是保证模型有效训练的重要因素。

5.6 伪代码

for epoch in range(num_epochs):
    for B in data_loader:  # 遍历每个小批量 B
        # 计算小批量梯度
        w_grad = (1 / len(B)) * sum([grad_L(w, b, x_i, y_i) for x_i, y_i in B])
        b_grad = (1 / len(B)) * sum([grad_L(w, b, x_i, y_i) for x_i, y_i in B])
        
        # 更新参数
        w -= eta * w_grad
        b -= eta * b_grad

六、线性回归的代码实现

我们将实现一个简易的线性回归模型,通过生成数据、定义模型、计算损失、使用小批量随机梯度下降来优化参数,并在训练过程中进行性能评估。以下是逐步的实现代码及相关说明。

6.1 生成数据集

我们首先创建一个简单的数据集,使其满足线性关系。假设我们有如下线性模型:

y=Xw+b+ϵy = Xw + b + \epsilon

其中,XX 是输入特征矩阵,ww 是权重向量,bb 是偏置项,ϵ\epsilon 是随机噪声。

import torch

# 设置随机种子
torch.manual_seed(49)

# 生成输入数据 X 和真实权重、偏置
num_samples, num_features = 1000, 2

true_w = torch.tensor([2.0, -3.4])
true_b = torch.tensor(4.2)

X = torch.normal(0, 1, [num_samples, num_features])

y = X @ true_w + true_b

y += torch.normal(0, 0.01, y.shape)  # # 添加噪声

6.2 初始化模型参数

为了开始训练,我们需要初始化模型的参数 𝑤𝑤𝑏𝑏,它们将会在训练过程中更新。

# 初始化模型参数
w = torch.randn(num_features, requires_grad=True)
b = torch.zeros(1, requires_grad=True)

6.3 定义模型的正向传播计算

我们定义线性模型的正向传播计算公式,计算预测值 y^\hat{y}

y^=Xw+b \hat{y} = Xw + b
# 定义线性模型的正向传播
def linear_regression(X, w, b):
    return X @ w + b

6.4 定义损失函数

我们使用均方误差(Mean Squared Error, MSE)作为损失函数,公式如下:

L(w,b)=12ni=1n(y^iyi)2L(w, b) = \frac{1}{2n} \sum_{i=1}^n (\hat{y}_i - y_i)^2
# 定义均方误差损失函数
def mse_loss(y_pred, y):
    return ((y_pred - y) ** 2).mean() / 2

6.5 定义优化算法

我们使用小批量随机梯度下降法(SGD)来更新参数。更新公式如下:

w:=wη1miBL(w,b;xi,yi)ww := w - \eta \cdot \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial w}
b:=bη1miBL(w,b;xi,yi)bb := b - \eta \cdot \frac{1}{m} \sum_{i \in B} \frac{\partial L(w, b; x_i, y_i)}{\partial b}
# 定义随机梯度下降优化算法
def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

6.6 模型训练及参数优化过程

我们使用生成的数据集,定义小批量随机梯度下降的训练循环,不断更新参数 𝑤𝑤𝑏𝑏 以最小化损失函数。

# 模型训练
def train(num_epochs, lr, batch_size):
    loss = None
    for epoch in range(num_epochs):
        # 将数据划分为小批量
        idx = torch.randperm(num_samples)
        for i in range(0, num_samples, batch_size):
            batch_indices = idx[i:i + batch_size]
            x_batch, y_batch = X[batch_indices], y[batch_indices]
            # 前向传播计算损失
            y_pred = linear_regression(x_batch, w, b)
            loss = mse_loss(y_pred, y_batch)
            # 反向传播
            loss.backward()
            # 更新参数
            sgd([w, b], lr, batch_size)

        # 每隔一定周期输出当前损失
        if (epoch + 1) % 10 == 0:
            print(f'Epoch {epoch + 1}, Loss: {loss.item():.4f}')

6.7 模型训练过程中的性能评估

最后,我们评估训练后得到的模型,比较预测值和真实值的差距,以判断模型是否成功拟合数据。

# 训练模型
train(num_epochs=100, lr=0.03, batch_size=10)

print(f'误差 - 真实权重:{true_w.numpy()}, 学习到的权重:{w.detach().numpy()}')
print(f'误差 - 真实偏置:{true_b.item()}, 学习到的偏置:{b.item()}')
Epoch 10, Loss: 0.0668
Epoch 20, Loss: 0.0004
Epoch 30, Loss: 0.0000
Epoch 40, Loss: 0.0001
Epoch 50, Loss: 0.0000
Epoch 60, Loss: 0.0000
Epoch 70, Loss: 0.0001
Epoch 80, Loss: 0.0000
Epoch 90, Loss: 0.0000
Epoch 100, Loss: 0.0000
误差 - 真实权重:[ 2.  -3.4], 学习到的权重:[ 2.0001948 -3.3993635]
误差 - 真实偏置:4.199999809265137, 学习到的偏置:4.199973106384277

通过这个训练过程,模型的参数 www 和 bbb 将逐步收敛到真实值。

七、总结

在本篇文章中,我们深入探讨了线性回归的基本原理、损失函数的定义、以及梯度下降等优化算法,并通过小批量随机梯度下降的实现演示了如何在代码中优化模型参数。此外,我们还详细介绍了如何在 PyTorch 中构建线性回归模型并生成一个简单的人工数据集用于训练和评估。

通过本文,你应该对以下几个关键点有了更加清晰的认识:

  • 线性回归模型:理解了线性关系如何通过权重和偏置来拟合数据。
  • 损失函数:掌握了如何定义并使用均方误差(MSE)作为损失函数来衡量预测值与真实值之间的差距。
  • 梯度下降:学习了梯度下降的原理以及如何实现小批量随机梯度下降,以更高效地训练模型。
  • 代码实现:通过 PyTorch 实现了一个完整的线性回归模型,涵盖数据生成、参数初始化、前向传播、损失计算以及优化的全过程。

掌握这些基础概念和实现技巧将为你进一步学习深度学习的复杂模型打下坚实的基础。在接下来的学习中,我们会逐步接触更多层次的神经网络结构和优化方法,带你走向更加丰富的深度学习应用场景。