【pytorch】深度学习 线性回归训练 参数w,b如何进行反向传播详解(附推导公式,证明及代码)

一、线性回归训练的代码与结果

(1)代码

import random

import numpy as np
import torch


def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # 样本的读取顺序是随机的
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)])  # 最后一次可能不足一个batch
        yield features.index_select(0, j), labels.index_select(0, j)


def linreg(X, w, b):  # 本函数已保存在d2lzh包中方便以后使用
    return torch.mm(X, w) + b


def squared_loss(y_hat, y):  # 本函数已保存在pytorch_d2lzh包中方便以后使用
    return (y_hat - y.view(y_hat.size())) ** 2 / 2


def sgd(params, lr, batch_size):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    for param in params:
        param.data -= lr * param.grad / batch_size  # 注意这里更改param时用的param.data
        # print(param.grad.data)


if __name__ == '__main__':
    num_inputs = 2
    num_examples = 1000
    true_w = [2, -3.4]
    true_b = 4.2
    features = torch.randn(num_examples, num_inputs)
    labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
    labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)

    batch_size = 10
    w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float)
    b = torch.zeros(1)
    w.requires_grad_(requires_grad=True)
    b.requires_grad_(requires_grad=True)
    lr = 0.01
    num_epochs = 5
    net = linreg
    loss = squared_loss

    for epoch in range(num_epochs):  # 训练模型一共需要num_epochs个迭代周期
        # 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。X
        # 和y分别是小批量样本的特征和标签
        for X, y in data_iter(batch_size, features, labels):

            # with torch.no_grad():
                # # 测试w代码块
                # w1 = 0
                # for i in range(10):
                #     w1 += (X[i, 0] * w[0] + b - y[i]) * X[i, 0]
                # print('--------------')
                # print(w1)
                #
                # # 测试b代码块
                # b1 = 0
                # for i in range(10):
                #     b1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i])
                # print(b1)

            l = loss(net(X, w, b), y).sum()  # l是有关小批量X和y的损失
            l.backward()  # 小批量的损失对模型参数求梯度
            sgd([w, b], lr, batch_size)  # 使用小批量随机梯度下降迭代模型参数

            # 不要忘了梯度清零
            w.grad.data.zero_()
            b.grad.data.zero_()
        train_l = loss(net(features, w, b), labels)
        print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

(2)结果

epoch 1, loss 2.210815
epoch 2, loss 0.294341
epoch 3, loss 0.039363
epoch 4, loss 0.005334
epoch 5, loss 0.000763

二、w和b反向传播的计算过程

  • 在上述程序中,sgd([w, b], lr, batch_size)这一步中,代入sgd函数中有w.data -= Ir * w.grad / batch_sizeb.data -= Ir * b.grad / batch_size

下面详解如何计算w.grad和b.grad:

  • 设学习率为 η , ∣ B ∣ \eta, |\mathcal{B}| ηB为batch_size。

  • w是一个2X1的矩阵,设为 w 1 , w 2 w_{1},w_{2} w1,w2。b是一个标量。X在每一次循环中是一个 ∣ B ∣ |\mathcal{B}| BX2的矩阵,设为 x j ( i ) , i = 1 , 2... ∣ B ∣ ; j = 1 , 2 x_{j}^{(i)},i=1,2...|\mathcal{B}|;j=1,2 xj(i)i=1,2...B;j=1,2 。y是一个1X ∣ B ∣ |\mathcal{B}| B的矩阵,设为 y i , i = 1 , 2... ∣ B ∣ y_{i},i=1,2...|\mathcal{B}| yi,i=1,2...B

  • l = loss(net(X, w, b), y).sum()的计算公式:

1)计算 l l l w 1 , w 2 w_{1},w_{2} w1w2的偏微分

  • l o s s loss loss
    l o s s = 1 2 ∗ ( y ^ − y ) 2 = 1 2 ∗ [ ( X w + b ) − y ] 2 loss = \frac{1}{2}*(\hat{y}-y)^{2} = \frac{1}{2}*[(Xw+b)-y]^{2} loss=21(y^y)2=21[(Xw+b)y]2

  • 可得 l l l
    l = ∑ i ∈ B l o s s = 1 2 ∗ ∑ i ∈ B [ ( X i w + b ) − y i ] 2 l =\sum_{i \in \mathcal{B}} {loss} = \frac{1}{2}*\sum_{i \in \mathcal{B}} {[(X_{i}w+b)-y_{i}]^{2}} l=iBloss=21iB[(Xiw+b)yi]2

  • 利用链式法则,计算 l o s s loss loss关于 w 1 w_{1} w1的偏微分:
    ∂ l o s s ∂ w 1 = ∂ l o s s ∂ y ^ ∂ y ^ ∂ w 1 = [ ( X w + b ) − y ] x 1 ( i ) \frac{\partial{loss}}{\partial{w_{1}}} = \frac{\partial{loss}}{\partial{\hat{y}}} \frac{\partial{\hat{y}}}{\partial{w_{1}}} = [(Xw+b)-y]x_{1}^{(i)} w1loss=y^lossw1y^=[(Xw+b)y]x1(i)

  • 计算 l l l关于 w 1 w_{1} w1的偏微分:
    ∂ l ∂ w 1 = ∑ ∂ l o s s ∂ w 1 = ∑ i ∈ B ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) x 1 ( i ) \frac{\partial{l}}{\partial{w_{1}}} = \sum{\frac{\partial{loss}}{\partial{w_{1}}}} = \sum_{i \in \mathcal{B}} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) x_{1}^{(i)} w1l=w1loss=iB(x1(i)w1+x2(i)w2+by(i))x1(i)

  • 关于 w 2 w_{2} w2的偏微分计算同理:
    ∂ l ∂ w 2 = ∑ ∂ l o s s ∂ w 2 = ∑ i ∈ B ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) x 2 ( i ) \frac{\partial{l}}{\partial{w_{2}}} = \sum{\frac{\partial{loss}}{\partial{w_{2}}}} = \sum_{i \in \mathcal{B}} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) x_{2}^{(i)} w2l=w2loss=iB(x1(i)w1+x2(i)w2+by(i))x2(i)

2)计算 l l l b b b的偏微分

  • 计算 l o s s loss loss关于 b b b的偏微分:
    ∂ l o s s ∂ b = ∂ l o s s ∂ y ^ ∂ y ^ ∂ b = [ ( X w + b ) − y ] \frac{\partial{loss}}{\partial{b}} = \frac{\partial{loss}}{\partial{\hat{y}}} \frac{\partial{\hat{y}}}{\partial{b}} = [(Xw+b)-y] bloss=y^lossby^=[(Xw+b)y]

  • 计算 l l l关于 b b b的偏微分:
    ∂ l ∂ b = ∑ i ∈ B ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) \frac{\partial{l}}{\partial{b}} = \sum_{i \in \mathcal{B}} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) bl=iB(x1(i)w1+x2(i)w2+by(i))

2) w 1 , w 2 , b w_{1},w_{2},b w1w2b的更新公式

  • 得到 w 1 w_{1} w1 w 2 w_{2} w2反向传播更新后的值为(设学习率为 η , ∣ B ∣ \eta, |\mathcal{B}| ηB为batch_size):
    w 1 ← w 1 − η ∣ B ∣ ∂ l ∂ w 1 ← w 1 − η ∣ B ∣ ∑ i ∈ B ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) x 1 ( i ) , w 2 ← w 2 − η ∣ B ∣ ∂ l ∂ w 2 ← w 2 − η ∣ B ∣ ∑ i ∈ B ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) x 2 ( i ) , b ← b − η ∣ B ∣ ∂ l ∂ b ← b − η ∣ B ∣ ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) . \begin{aligned} w_1 &\leftarrow w_1 - \frac{\eta}{|\mathcal{B}|} \frac{\partial{l}}{\partial{w_{1}}} & \leftarrow w_1 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) x_{1}^{(i)},\\ w_2 &\leftarrow w_2 - \frac{\eta}{|\mathcal{B}|} \frac{\partial{l}}{\partial{w_{2}}} & \leftarrow w_2 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) x_{2}^{(i)},\\ b & \leftarrow b - \frac{\eta}{|\mathcal{B}|} \frac{\partial{l}}{\partial{b}} & \leftarrow b - \frac{\eta}{|\mathcal{B}|} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right).\qquad\qquad \end{aligned} w1w2bw1Bηw1lw2Bηw2lbBηblw1BηiB(x1(i)w1+x2(i)w2+by(i))x1(i),w2BηiB(x1(i)w1+x2(i)w2+by(i))x2(i)bBη(x1(i)w1+x2(i)w2+by(i)).

三、对理论进行证明测试

  • 根据上面的理论,分别手动计算了 w 1 w_{1} w1 b b b 反向传播的值,与w1.grad,b.grad相互对照,看结果是否一致。

(1)相关代码块

            with torch.no_grad():
                # 测试w代码块
                w1 = 0
                w2 = 0
                for i in range(10):
                    w1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i]) * X[i, 0]
                    w2 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i]) * X[i, 1]
                print('--------------')
                print(w1, w2)

                # 测试b代码块
                b1 = 0
                for i in range(10):
                    b1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i])
                print(b1)

(2)结果及分析

  • 结果解读:前三个tensor是 w 1 , w 2 w_{1},w_{2} w1w2 b b b 手动计算反向传播偏导数的值。后面三个tensor是w1.grad,w2.grad和b.grad的值。
  • 可以发现手动计算的 w 1 , w 2 w_{1} ,w_{2} w1w2 b b b 反向传播偏导数的值 -20.3094 ,21.7094 和 -46.1437 与w1.grad,w2.grad,b.grad的值-20.3094,21.7094,-46.1437完全一致,推导完全正确!可喜可乐。
--------------
tensor([-20.3094]) tensor([21.7094])
tensor([-46.1437])
tensor([[-20.3094],
        [ 21.7094]])
tensor([-46.1437])
--------------
tensor([-26.9922]) tensor([8.3662])
tensor([-31.1934])
tensor([[-26.9922],
        [  8.3662]])
tensor([-31.1934])
--------------
tensor([-18.5115]) tensor([74.9838])
tensor([-47.7214])
tensor([[-18.5115],
        [ 74.9838]])
tensor([-47.7214])

(3)完整的测试代码

import random

import numpy as np
import torch


def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # 样本的读取顺序是随机的
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)])  # 最后一次可能不足一个batch
        yield features.index_select(0, j), labels.index_select(0, j)


def linreg(X, w, b):  # 本函数已保存在d2lzh包中方便以后使用
    return torch.mm(X, w) + b


def squared_loss(y_hat, y):  # 本函数已保存在pytorch_d2lzh包中方便以后使用
    return (y_hat - y.view(y_hat.size())) ** 2 / 2


def sgd(params, lr, batch_size):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    for param in params:
        param.data -= lr * param.grad / batch_size  # 注意这里更改param时用的param.data
        print(param.grad.data)


if __name__ == '__main__':
    num_inputs = 2
    num_examples = 1000
    true_w = [2, -3.4]
    true_b = 4.2
    features = torch.randn(num_examples, num_inputs)
    labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
    labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)

    batch_size = 10
    w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float)
    b = torch.zeros(1)
    w.requires_grad_(requires_grad=True)
    b.requires_grad_(requires_grad=True)
    lr = 0.01
    num_epochs = 5
    net = linreg
    loss = squared_loss

    for epoch in range(num_epochs):  # 训练模型一共需要num_epochs个迭代周期
        # 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。X
        # 和y分别是小批量样本的特征和标签
        for X, y in data_iter(batch_size, features, labels):

            with torch.no_grad():
                # 测试w代码块
                w1 = 0
                w2 = 0
                for i in range(10):
                    w1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i]) * X[i, 0]
                    w2 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i]) * X[i, 1]
                print('--------------')
                print(w1, w2)

                # 测试b代码块
                b1 = 0
                for i in range(10):
                    b1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i])
                print(b1)

            l = loss(net(X, w, b), y).sum()  # l是有关小批量X和y的损失
            l.backward()  # 小批量的损失对模型参数求梯度
            sgd([w, b], lr, batch_size)  # 使用小批量随机梯度下降迭代模型参数

            # 不要忘了梯度清零
            w.grad.data.zero_()
            b.grad.data.zero_()
        train_l = loss(net(features, w, b), labels)
        print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

四、参考

CNN反向传播 - 文章对反向传播方向更新w和b的值讲解的很清晰

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会为您解答关于使用PyTorch进行线性回归的问题。 在PyTorch中进行线性回归可以分为以下几个步骤: 1. 准备数据:首先,您需要准备数据,包括训练数据和验证数据。在本例中,我们将使用一个简单的数据集,其中包含一些房屋的面积和价格,以用于预测房价。 2. 定义模型:接下来,您需要定义一个模型。在本例中,我们将使用一个简单的线性模型,它包含一个线性层,该层将输入的特征映射到输出的标签。 3. 定义损失函数:您需要定义一个损失函数,以便在训练模型时计算损失。在本例中,我们将使用均方误差(MSE)损失函数。 4. 训练模型:接下来,您需要定义一个优化器,并使用训练数据对模型进行训练。在本例中,我们将使用随机梯度下降(SGD)优化器,并迭代多次来更新模型的参数。 5. 验证模型:最后,您需要使用验证数据来评估模型的性能。在本例中,我们将计算模型在验证数据上的损失,并将其与训练数据的损失进行比较,以确定模型是否过拟合。 以下是一个简单的代码示例,演示了如何使用PyTorch进行线性回归: ```python import torch import torch.nn as nn # 准备数据 x_train = torch.tensor([[1.0], [2.0], [3.0], [4.0]]) y_train = torch.tensor([[2.0], [4.0], [6.0], [8.0]]) # 定义模型 model = nn.Linear(1, 1) # 定义损失函数 criterion = nn.MSELoss() # 定义优化器 optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 训练模型 num_epochs = 1000 for epoch in range(num_epochs): # 向前传递 outputs = model(x_train) loss = criterion(outputs, y_train) # 向后传递和优化 optimizer.zero_grad() loss.backward() optimizer.step() if (epoch+1) % 100 == 0: print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, loss.item())) # 验证模型 x_val = torch.tensor([[5.0]]) y_val = model(x_val) print('预测的房价: {:.2f}'.format(y_val.item())) ``` 希望这可以帮助您理解如何使用PyTorch进行线性回归

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值