← 返回笔记列表
深度学习 第 1 / 49 篇

深度学习:线性回归

查看“深度学习”专题目录(1/49)
本文目录 14 个小节

线性回归

一元线性回归

如何针对一组点,找出最佳的拟合直线了。这个算法就叫做线性回归。

线性回归就是假设样本的feature和label之间满足线性关系,线性回归模型训练过程就是找到能让损失函数值最小的,斜率w的值。当然,这里我们没有训练模型,而是通过直接求解得到w的最优值。

基本概念:

  1. 收集到的真实数据叫做样本
  2. 样本里的输入叫做特征(feature)
  3. 样本里希望模型预测的值叫做标签(label)

理解:

深度学习的模型就是一个函数,这个函数的输入是样本的feature,输出是预测值。训练过程就是调节函数内部的参数(Parameter)(也可以叫做权重(weight)),来让预测值尽可能的接近label。当模型训练好之后,就可以只输入新的样本的feature,模型根据学习到的内部参数,计算出预测值了。

自己理解:

训练模型过程中会有真实值和预测值之间的误差,为了训练一个更好的模型,需要将这个误差值降到最低水平

借鉴:

预测值40和label值60之间就产生了一个误差。然后将所有点的误差都累积起来,通过调节参数w的值,能让累积误差最小的直线,就是最好拟合我们数据的直线。因为误差有正有负,为了防止直接将误差相加正负抵消,所以我们取每个点误差的平方再累加,作为最终累积误差。

样本点的feature(温度)用xx来表示,label(销量)我们用yy来表示,通过直线预测的销量用y^\hat{y}来表示。xix^i表示第ii个样本点的feature,yiy^i表示第ii个样本点的label。y^i\hat{y}^i表示第ii个预测的销量值。则针对我们收集的这7个样本的误差平方和就可以表示为: loss=∑i=17(yi−y^i)2(式5-1)loss = \sum_{i=1}^{7} (y^i - \hat{y}^i)^2 \quad (\text{式5-1})

重点:损失函数

损失函数就是衡量预测值和真实值(label)之间的差距的。模型训练的目的就是降低loss。

回归:

当你要预测的变量是连续型变量,那么这个算法就是回归算法

分类:

当你要预测的变量是可数的离散型变量,那么这个算法就是分类算法

梯度下降算法

求解参数最优解时可以联立方程组,令导数值为0,求解

只有w和b两个参数需要找出最优解。并且我们的假设函数是线性的。可以直接让损失函数对w和b求偏导,并让偏导数为0,联立方程组来求出让loss最小时w和b的最优解。

在深度学习中,求解使用梯度下降算法

原因:

  1. 高维参数空间 深度学习里需要优化的参数非常多
  2. 非线性 度学习里引入了激活函数,激活函数是非线性的变化。解方程的方法将非常复杂,以致无法实现。

在一点处x0x{_0},求出该点处的导数值,根据是否大于0判断移动的方向

根据导数的定义,导数大于0,表明当x在x0x{_0}处增大时,也就是向右变化时, f(x)是增大的;x在x0x{_0}处减小时,也就是向左变化时, f(x)是减小的。

取导数的反方向。如果导数为正,那意味着对x0x{_0}做减操作(向左运动)。如果导数为负,那意味着对x0x{_0}做加操作(向右运动)。这样就可以得到更小的x0x{_0}值。

方向判断出来后,就需要移动x,此时需要确定一个步长

直接取负的导数值

改进:

因为直接取导数值作为步长有时不是很合适,需要加上一个系数,学习率

步长系数更正式的名字叫做学习率(Learning Rate)。简写做lr。因为导数值并不是总是那么合适,有时候偏大,导致步长太大,跳过全局最小值。有时候偏小,导致训练过程很慢。这时就可以通过设置学习率来调整。一般情况下,学习率都是小于1的。比如设置为0.001。

基本概念:

  1. 学习率: 步长系数

  2. 参数:在机器学习领域,在训练过程中由算法调整的变量

    参数是通过数据学习而来的,不是人为设定的。参数决定了模型如何从输入数据映射到输出。训练好的模型会保存这些参数,以便在预测/推理时使用。比如线性回归里的权重和偏置就是参数。

  3. 超参数:模型训练前需要人为设置的变量,它们不会在训练过程中自动学习,而是由人根据实验或者经验设定的。比如学习率就是一个超参数。

梯度下降算法要素:

  1. 初始化:对于每个要优化的参数,随机初始化一个值。
  2. 计算梯度:根据当前参数值,对每个参数求偏导,得到梯度值。
  3. 迭代更新参数:让当前参数向量沿着梯度负方向,用学习率乘以梯度的值前进。然后再计算梯度。
  4. 停止条件:
    • 可以设置迭代次数,当到达迭代次数后则停止更新。
    • 或者可以设置loss连续多个迭代不减小,则认为到达loss最小处,停止更新。

损失函数除以样本数

为了让训练稳定,不同的样本数也有差不多大小的偏导数值。一般在loss函数都会除以计算loss的样本数.

回归问题损失函数 :

均方误差(Mean Squared Error,MSE)。其公式为:

​ MSE=1n∑i=1n(yi−y^i)2MSE = \frac{1}{n}\sum_{i=1}^{n} \left(y_i - \hat{y}_i\right)^2

多元线性回归

相对于一元线性回归有了更过的特征

线性回归的一般表达式

hw(x)=w0+w1x1+w2x2+⋯+wnxnh_w(x) = w_0 + w_1 x_1 + w_2 x_2 + \dots + w_n x_n

其中:

  • w0w_0 是模型参数:偏置。
  • w1,w2,…,wnw_1, w_2, \dots, w_n是模型参数:特征系数。
  • x1,x2,…,xnx_1, x_2, \dots, x_n是特征。

线性回归拟合曲线

泰勒公式告诉我们,任何一个光滑的、n阶可导的函数,在某一点附近都可以用一个多项式函数来近似。这意味着,即使我们面对的函数关系非常复杂,只要我们选择足够高的多项式次数,就可以用线性回归模型在局部范围内很好地拟合它。

如果模型有多个特征,还可以构造出多个特征之间相乘的高次项。

实现多元线性回归:

已知条件

数据
温度价格 (元)销量 (个)
10360
20385
253100
282.5120
302140
352.5145
402.5163

多元线性回归公式

我们用x1x_1表示温度,x2x_2表示价格,yy表示销量。 w0w_0表示截距,w1w_1表示温度对应的权重,w2w_2表示价格对应的权重。 则预测的销量为:

y^=w0+w1x1+w2x2\hat{y} = w_0 + w_1x_1 + w_2x_2

损失函数

损失函数我们用MSE,我们有7个数据,所以:

loss=17∑i=17(y^i−yi)2loss = \frac{1}{7}\sum_{i=1}^{7}(\hat{y}^i - y^i)^2

带入线性回归方程有:

loss=17∑i=17(w0+w1x1i+w2x2i−yi)2loss = \frac{1}{7}\sum_{i=1}^{7}(w_0 + w_1x_1^i + w_2x_2^i - y^i)^2

用梯度下降算法更新参数

我们用梯度下降算法逐步来更新参数w0,w1,w2w_0,w_1,w_2。

梯度计算

利用损失函数对每个参数求偏导:

  • w0w_0的偏导数: ∂loss∂w0=27∑i=17(w0+w1x1i+w2x2i−yi)\frac{\partial loss}{\partial w_0} = \frac{2}{7}\sum_{i=1}^{7}(w_0 + w_1x_1^i + w_2x_2^i - y^i)
  • w1w_1的偏导数: ∂loss∂w1=27∑i=17(w0+w1x1i+w2x2i−yi)⋅x1i\frac{\partial loss}{\partial w_1} = \frac{2}{7}\sum_{i=1}^{7}(w_0 + w_1x_1^i + w_2x_2^i - y^i) \cdot x_1^i
  • w2w_2的偏导数: ∂loss∂w2=27∑i=17(w0+w1x1i+w2x2i−yi)⋅x2i\frac{\partial loss}{\partial w_2} = \frac{2}{7}\sum_{i=1}^{7}(w_0 + w_1x_1^i + w_2x_2^i - y^i) \cdot x_2^i

参数更新

在每次迭代中,参数按照以下规则更新:

w0=w0−lr⋅∂loss∂w0w_0 = w_0 - lr \cdot \frac{\partial loss}{\partial w_0} w1=w1−lr⋅∂loss∂w1w_1 = w_1 - lr \cdot \frac{\partial loss}{\partial w_1} w2=w2−lr⋅∂loss∂w2w_2 = w_2 - lr \cdot \frac{\partial loss}{\partial w_2}

其中lrlr为学习率。

# Feature 数据
X = [[10, 3], [20, 3], [25, 3], [28, 2.5], [30, 2], [35, 2.5], [40, 2.5]]
y = [60, 85, 100, 120, 140, 145, 163]  # Label 数据
# 初始化参数
w = [0.0, 0.0, 0.0]  # w0, w1, w2
lr = 0.0001  # 学习率
num_iterations = 10000  # 迭代次数
# 梯度下降
for i in range(num_iterations):
    # 预测值 列表形式
    y_pred = [w[0] + w[1] * x[0] + w[2] * x[1] for x in X]
    # 计算损失
    loss = sum((y_pred[j] - y[j]) ** 2 for j in range(len(y))) / len(y)
    # 计算梯度
    grad_w0 = 2 * sum(y_pred[j] - y[j] for j in range(len(y))) / len(y)
    grad_w1 = 2 * sum((y_pred[j] - y[j]) * X[j][0] for j in range(len(y))) / len(y)
    grad_w2 = 2 * sum((y_pred[j] - y[j]) * X[j][1] for j in range(len(y))) / len(y)
    # 更新参数
    w[0] -= lr * grad_w0
    w[1] -= lr * grad_w1
    w[2] -= lr * grad_w2
    # 打印损失
    if i % 100 == 0:
        print(f"Iteration {i}: Loss = {loss}")
# 输出最终参数
print(f"Final parameters: w0 = {w[0]}, w1 = {w[1]}, w2 = {w[2]}")
# ============================================================
# 多元线性回归 — 梯度下降手写实现(MSE 损失)
# 模型公式: y_hat = w0 + w1 * x1 + w2 * x2
# ============================================================

# --- 1. 数据 ---
# X: 7 个样本,每个样本有 2 个特征(例如 x1=面积, x2=房间数)
#    每个 x 是一个 [特征1, 特征2] 的列表
X = [[10, 3], [20, 3], [25, 3], [28, 2.5], [30, 2], [35, 2.5], [40, 2.5]]
y = [60, 85, 100, 120, 140, 145, 163]  # 真实标签(房价,单位:万)

# --- 2. 初始化参数 ---
w = [0.0, 0.0, 0.0]   # [w0(偏置/截距), w1(特征1权重), w2(特征2权重)]
lr = 0.0001            # 学习率:控制每次参数更新的步长,太大会震荡,太小收敛慢
num_iterations = 10000 # 迭代次数:梯度下降走多少步

# --- 3. 梯度下降循环 ---
for i in range(num_iterations):

    # 3.1 计算所有样本的预测值(列表推导式)
    #     等价于:
    #     y_pred = []
    #     for x in X:
    #         pred = w[0] + w[1] * x[0] + w[2] * x[1]
    #         y_pred.append(pred)
    #
    #     x[0] = 样本的第1个特征, x[1] = 样本的第2个特征
    #     这里必须用列表 [...],不能用生成器 (...),因为后面要多次遍历并做索引取值
    y_pred = [w[0] + w[1] * x[0] + w[2] * x[1] for x in X]

    # 3.2 计算均方误差损失: MSE = (1/n) * Σ(预测值 - 真实值)²
    loss = sum((y_pred[j] - y[j]) ** 2 for j in range(len(y))) / len(y)

    # 3.3 计算梯度(MSE 对每个参数的偏导数)
    #     grad_w0 = ∂loss/∂w0 = (2/n) * Σ(预测值 - 真实值)
    #     grad_w1 = ∂loss/∂w1 = (2/n) * Σ(预测值 - 真实值) * x1
    #     grad_w2 = ∂loss/∂w2 = (2/n) * Σ(预测值 - 真实值) * x2
    #
    #     梯度的含义: 损失函数在当前参数处的"坡度",
    #     指向损失上升最快的方向,所以更新时往反方向(负梯度)走
    grad_w0 = 2 * sum(y_pred[j] - y[j] for j in range(len(y))) / len(y)
    grad_w1 = 2 * sum((y_pred[j] - y[j]) * X[j][0] for j in range(len(y))) / len(y)
    grad_w2 = 2 * sum((y_pred[j] - y[j]) * X[j][1] for j in range(len(y))) / len(y)

    # 3.4 参数更新: w_new = w_old - lr * grad
    #     沿着梯度的反方向走一小步(步长 = 学习率 × 梯度)
    w[0] -= lr * grad_w0
    w[1] -= lr * grad_w1
    w[2] -= lr * grad_w2

    # 3.5 每 100 次打印一次损失,观察是否在下降
    if i % 100 == 0:
        print(f"Iteration {i}: Loss = {loss}")

# --- 4. 输出最终训练结果 ---
print(f"Final parameters: w0 = {w[0]}, w1 = {w[1]}, w2 = {w[2]}")
# 最终模型: y ≈ 2.11 + 3.91 * x1 + 3.22 * x2
Iteration 0: Loss = 14631.285714285714
Iteration 100: Loss = 76.144943048227
Iteration 200: Loss = 75.34515908380763
Iteration 300: Loss = 74.58501440666258
Iteration 400: Loss = 73.86251347394905
Iteration 500: Loss = 73.17576121076728
Iteration 600: Loss = 72.52295795197715
Iteration 700: Loss = 71.90239463868159
Iteration 800: Loss = 71.31244825654672
Iteration 900: Loss = 70.75157750378692
Iteration 1000: Loss = 70.21831867724987
Iteration 1100: Loss = 69.71128176562073
Iteration 1200: Loss = 69.22914673931989
Iteration 1300: Loss = 68.7706600271886
Iteration 1400: Loss = 68.33463117056202
Iteration 1500: Loss = 67.91992964579549
Iteration 1600: Loss = 67.52548184676445
Iteration 1700: Loss = 67.15026821928475
Iteration 1800: Loss = 66.79332053980403
Iteration 1900: Loss = 66.45371933109946
Iteration 2000: Loss = 66.13059140808637
Iteration 2100: Loss = 65.8231075471858
Iteration 2200: Loss = 65.53048027302972
Iteration 2300: Loss = 65.25196175659896
Iteration 2400: Loss = 64.98684181917966
Iteration 2500: Loss = 64.7344460368146
Iteration 2600: Loss = 64.49413394018758
Iteration 2700: Loss = 64.2652973051365
Iteration 2800: Loss = 64.04735852923301
Iteration 2900: Loss = 63.83976909009523
Iteration 3000: Loss = 63.642008081317805
Iteration 3100: Loss = 63.45358082211327
Iteration 3200: Loss = 63.27401753695216
Iteration 3300: Loss = 63.10287210167807
Iteration 3400: Loss = 62.93972085275144
Iteration 3500: Loss = 62.784161456443094
Iteration 3600: Loss = 62.635811834959654
Iteration 3700: Loss = 62.49430914663436
Iteration 3800: Loss = 62.35930881746174
Iteration 3900: Loss = 62.23048362139032
Iteration 4000: Loss = 62.10752280691907
Iteration 4100: Loss = 61.99013126766744
Iteration 4200: Loss = 61.87802875470216
Iteration 4300: Loss = 61.770949128522616
Iteration 4400: Loss = 61.66863964870333
Iteration 4500: Loss = 61.570860299303284
Iteration 4600: Loss = 61.47738314823518
Iteration 4700: Loss = 61.387991738890285
Iteration 4800: Loss = 61.302480512391995
Iteration 4900: Loss = 61.22065425893586
Iteration 5000: Loss = 61.14232759675425
Iteration 5100: Loss = 61.067324477311374
Iteration 5200: Loss = 60.99547771541081
Iteration 5300: Loss = 60.92662854296058
Iteration 5400: Loss = 60.86062618520397
Iteration 5500: Loss = 60.79732745828501
Iteration 5600: Loss = 60.73659638707648
Iteration 5700: Loss = 60.67830384224716
Iteration 5800: Loss = 60.622327195602246
Iteration 5900: Loss = 60.56854999277664
Iteration 6000: Loss = 60.51686164240532
Iteration 6100: Loss = 60.46715712094362
Iteration 6200: Loss = 60.41933669234987
Iteration 6300: Loss = 60.37330564187844
Iteration 6400: Loss = 60.32897402327734
Iteration 6500: Loss = 60.286256418712604
Iteration 6600: Loss = 60.24507171077988
Iteration 6700: Loss = 60.205342865993806
Iteration 6800: Loss = 60.16699672917894
Iteration 6900: Loss = 60.12996382821093
Iteration 7000: Loss = 60.094178188589275
Iteration 7100: Loss = 60.05957715734553
Iteration 7200: Loss = 60.0261012358164
Iteration 7300: Loss = 59.99369392083711
Iteration 7400: Loss = 59.96230155392858
Iteration 7500: Loss = 59.93187317807916
Iteration 7600: Loss = 59.90236040173598
Iteration 7700: Loss = 59.87371726964342
Iteration 7800: Loss = 59.84590014018527
Iteration 7900: Loss = 59.818867568901666
Iteration 8000: Loss = 59.79258019787044
Iteration 8100: Loss = 59.76700065065866
Iteration 8200: Loss = 59.74209343256111
Iteration 8300: Loss = 59.717824835863375
Iteration 8400: Loss = 59.694162849873486
Iteration 8500: Loss = 59.67107707548274
Iteration 8600: Loss = 59.64853864402952
Iteration 8700: Loss = 59.62652014024667
Iteration 8800: Loss = 59.60499552908839
Iteration 8900: Loss = 59.583940086242016
Iteration 9000: Loss = 59.56333033213691
Iteration 9100: Loss = 59.54314396927699
Iteration 9200: Loss = 59.523359822727635
Iteration 9300: Loss = 59.50395778359869
Iteration 9400: Loss = 59.48491875537359
Iteration 9500: Loss = 59.46622460294042
Iteration 9600: Loss = 59.447858104188015
Iteration 9700: Loss = 59.42980290404065
Iteration 9800: Loss = 59.412043470806005
Iteration 9900: Loss = 59.394565054720495
Final parameters: w0 = 2.111728802228473, w1 = 3.9059202091756244, w2 = 3.21719404307381

用PyTorch实现线性回归:

生成训练数据

import torch

# 确保CUDA可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 生成数据
inputs = torch.rand(100, 3) # 随机生成shape为(100,3)的tensor,里边每个元素的值都是0-1之间
weights = torch.tensor([[1.1], [2.2], [3.3]]) #预设的权重
bias = torch.tensor(4.4) #预设的bias
targets = inputs @ weights + bias + 0.1*torch.randn(100, 1) #增加一些误差,模拟真实情况

首先随机生成100条数据,每条数据都有3个feature。具体值是随机产生的0-1之间的值。接着通过我们人为设定的weight和bias,对随机生成的inputs经过线性变化,再加上一些小的随机误差。生成对应的targets作为label值。

# 初始化参数时直接放在CUDA上,并启用梯度追踪
w = torch.rand((3, 1), requires_grad=True, device=device)
b = torch.rand((1,), requires_grad=True, device=device)

用随机值初始化线性回归模型的参数。

# 将数据移至相同设备
inputs = inputs.to(device)
targets = targets.to(device)

#设置超参数
epoch = 10000
lr = 0.003

for i in range(epoch):
    outputs = inputs @ w + b
    loss = torch.mean(torch.square(outputs - targets))
    print("loss:", loss.item())

    loss.backward()

    with torch.no_grad(): #下边的计算不需要跟踪梯度
        w -= lr * w.grad
        b -= lr * b.grad

    # 清零梯度
    w.grad.zero_()
    b.grad.zero_()

print("训练后的权重 w:", w)
print("训练后的偏置 b:", b)

首先必须将inputs和targets也移动到和参数同样的设备上,不同设备上的tensor是无法进行计算的。

然后我们设置超参数迭代次数epoch,和学习率lr。

然后进入训练循环,我们只需要定义前向传播过程就可以,

最后调用loss的backward方法。

PyTorch会根据自动生成的计算图进行反向传播和梯度计算。后边我们就可以用w.grad来获取loss对w的偏导数了。 然后对参数进行梯度更新,需要注意这个更新计算不记录梯度,所以放在torch.no_grad()的上下文里。 还需要注意的是,需要对更新后的参数的梯度进行清零,不然梯度值会一直累积。 经过多次迭代后,可以打印出模型训练后的参数值,看看和我们生成数据时预设的参数值是否接近。

loss: 0.00798078253865242
loss: 0.007973769679665565
loss: 0.007967247627675533
loss: 0.007961045019328594
loss: 0.00795525498688221
loss: 0.007949751801788807
loss: 0.007944579236209393
loss: 0.007939732633531094
loss: 0.00793509092181921
loss: 0.007930785417556763
loss: 0.007926697842776775
loss: 0.00792284868657589
loss: 0.007919237948954105
loss: 0.007915777154266834
loss: 0.007912578992545605
loss: 0.007909541949629784
loss: 0.007906647399067879
loss: 0.00790394376963377
loss: 0.007901404052972794
loss: 0.007898983545601368
loss: 0.007896698080003262
loss: 0.007894572801887989
loss: 0.007892566733062267
loss: 0.007890615612268448
loss: 0.007888817228376865
loss: 0.00788713525980711
loss: 0.007885534316301346
loss: 0.007883996702730656
loss: 0.007882567122578621
loss: 0.007881245575845242
loss: 0.007879978977143764
loss: 0.007878750562667847
loss: 0.007877606898546219
loss: 0.007876557298004627
loss: 0.00787555705755949
loss: 0.007874603383243084
loss: 0.007873696275055408
loss: 0.007872821763157845
loss: 0.007872038520872593
loss: 0.007871287874877453
loss: 0.007870581932365894
loss: 0.00786990113556385
loss: 0.00786924920976162
loss: 0.007868632674217224
loss: 0.007868078537285328
loss: 0.007867562584578991
loss: 0.00786706991493702
loss: 0.007866604253649712
loss: 0.007866144180297852
loss: 0.007865707390010357
loss: 0.007865309715270996
loss: 0.007864919491112232
loss: 0.007864566519856453
loss: 0.007864242419600487
loss: 0.007863922975957394
loss: 0.007863622158765793
loss: 0.007863331586122513
loss: 0.007863050326704979
loss: 0.007862775586545467
loss: 0.007862534373998642
loss: 0.007862314581871033
loss: 0.007862094789743423
loss: 0.007861899212002754
loss: 0.007861709222197533
loss: 0.00786153506487608
loss: 0.007861362770199776
loss: 0.007861210964620113
loss: 0.007861032150685787
loss: 0.007860896177589893
loss: 0.007860749959945679
loss: 0.007860606536269188
loss: 0.007860483601689339
loss: 0.007860367186367512
loss: 0.007860265672206879
loss: 0.007860166020691395
loss: 0.00786008220165968
loss: 0.007859998382627964
loss: 0.007859913632273674
loss: 0.007859831675887108
loss: 0.00785975530743599
loss: 0.007859687320888042
loss: 0.007859594188630581
loss: 0.007859534583985806
loss: 0.007859465666115284
loss: 0.007859409786760807
loss: 0.007859354838728905
loss: 0.007859282195568085
loss: 0.00785923469811678
loss: 0.00785918440669775
loss: 0.007859136909246445
loss: 0.00785910990089178
loss: 0.007859070785343647
loss: 0.007859029807150364
loss: 0.007858989760279655
loss: 0.007858961820602417
loss: 0.007858935743570328
loss: 0.007858901284635067
loss: 0.007858878001570702
loss: 0.007858848199248314
loss: 0.007858818396925926
训练后的权重 w: tensor([[1.1334],
        [2.1737],
        [3.2831]], device='cuda:0', requires_grad=True)
训练后的偏置 b: tensor([4.3959], device='cuda:0', requires_grad=True)
继续学习本专题