3-2线性回归从0实现

导入库

%matplotlib inline
import random
import torch
from d2l import torch as d2l

1.生成数据集

相关用法

1. torch.normal()

torch.normal() 的主要功能是从正态分布中抽取随机数,并将这些随机数填充到一个张量中。

它的参数主要包括均值(mean)和标准差(std),以及生成的张量的形状

参数说明

  1. mean(均值)
    • 可以是一个标量(单个数值),表示所有生成的随机数的均值。
    • 也可以是一个张量,其形状决定了生成的随机数的形状,每个位置的随机数将使用该位置的均值。
  2. std(标准差)
    • 可以是一个标量,表示所有生成的随机数的标准差。
    • 也可以是一个张量,其形状必须与 mean 一致,每个位置的随机数将使用该位置的标准差。
  3. size(生成张量的形状)
    • 如果 meanstd 是标量,可以通过 size 参数指定生成的张量的形状。
    • 如果 meanstd 是张量,则 size 参数通常不需要,因为生成的张量形状将与 meanstd 的形状一致。
  4. out(输出张量)
    • 可选参数,用于指定一个已存在的张量,将生成的随机数填充到该张量中。如果未指定,则返回一个新的张量。
  5. generator(随机数生成器)
    • 可选参数,用于指定一个随机数生成器,以控制随机数的生成过程。这在需要固定随机种子时非常有用。
  6. dtype(数据类型)
    • 可选参数,指定生成的张量的数据类型,默认为 torch.float32
  7. device(设备)
    • 可选参数,指定生成的张量存储的设备(CPU 或 GPU)。

2. torch.matmul

torch.matmul() 是 PyTorch 中用于执行矩阵乘法的函数。它可以处理多种类型的张量输入,包括一维、二维和高维张量,并且能够自动广播和处理不同形状的张量。以下是 torch.matmul 的详细用法和一些示例。

功能

torch.matmul 用于计算两个张量的矩阵乘积。它可以处理以下几种情况:

  1. 一维张量与一维张量:等价于点积。
  2. 二维张量与二维张量:标准矩阵乘法。
  3. 高维张量与高维张量:批量矩阵乘法。
  4. 高维张量与低维张量:自动广播。

参数

  • input:第一个输入张量。
  • other:第二个输入张量。
  • out(可选):输出张量,用于存储结果。

返回值

返回一个张量,表示输入张量的矩阵乘积。

def synthetic_data(w,b,num_examples):
    # 生成 y = Xw + b + 噪声
    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 = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
# 注意: features中的每一行都包含一个二维数据样本
#   labels中的每一行都包含一维标签值(一个标量)
print('features:', features[0], '\nlabel:', labels[0])
features: tensor([1.9590, 0.5966]) 
label: tensor([6.0942])

相关用法

1. d2l.set_figsize()

d2l.set_figsize() 用于设置绘图的尺寸。它是一个辅助函数,通常在绘制图形之前调用,以指定图形的宽度和高度。

参数

  • figsize:一个元组 (width, height),表示图形的宽度和高度(单位为英寸)。默认值通常是 (3.5, 2.5)

2. d2l.plt.scatter()

d2l.plt.scatter() 是一个封装了 matplotlib.pyplot.scatter() 的函数,用于绘制散点图。它可以直接使用 d2l 的绘图接口,方便与 d2l 的其他绘图功能结合使用。

参数

  • x:散点图的 x 坐标数据。
  • y:散点图的 y 坐标数据。
  • s:点的大小(可选)。
  • c:点的颜色(可选)。
  • marker:点的形状(可选)。
  • alpha:点的透明度(可选)。
  • label:图例标签(可选)。
# 通过生成第二个特征 features[:,1]和labels的散点图,观察两者之间的线性关系
d2l.set_figsize()

# features[:, (1)]:提取 features 张量的第 2 列(索引为 1)。
# detach().numpy():将张量从计算图中分离,并转换为 NumPy 数组,以便用于绘图。
# labels.detach().numpy():将 labels 张量从计算图中分离,并转换为 NumPy 数组。
# 1:表示点的大小。
d2l.plt.scatter(features[:,(1)].detach().numpy(), labels.detach().numpy(), 1);

image

2.读取数据集

定义一个data_iter函数,该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。每个小批量包含一组特征和标签。

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):
        batch_indices = torch.tensor(indices[i:min(i+batch_size, num_examples)])
        yield features[batch_indices], labels[batch_indices]

batch_size = 10

for x,y in data_iter(batch_size, features, labels):
    print(x, '\n', y)
    break
tensor([[-0.7075,  0.5018],
        [ 0.1850, -0.1162],
        [-1.3421,  0.1540],
        [ 0.8673,  2.4672],
        [ 2.5221, -0.0242],
        [ 0.1540, -0.3132],
        [ 0.2965,  0.1625],
        [-0.8754, -0.3662],
        [ 0.6320, -1.0065],
        [ 0.5371, -0.8378]]) 
 tensor([[ 1.0757],
        [ 4.9367],
        [ 1.0051],
        [-2.4504],
        [ 9.3295],
        [ 5.5908],
        [ 4.2589],
        [ 3.6872],
        [ 8.8749],
        [ 8.1356]])

3.初始化模型参数

我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重,并将偏置初始化为0

requires_grad=True 的作用

当你将张量的 requires_grad 属性设置为 True 时,PyTorch 会自动跟踪对该张量的所有操作,并构建一个计算图(Computational Graph)。

这个计算图用于在反向传播时计算梯度。

如果 requires_grad=False(默认值),则不会跟踪该张量的操作,也不会计算梯度。

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

4.定义模型

def linreg(x, w, b):
    '''线性回归模型'''
    return torch.matmul(x,w) + b

5.定义损失函数

def squared_loss(y_hat, y):
    '''均方损失'''
    return (y_hat-y.reshape(y_hat.shape))**2/2

6.定义优化算法

参数说明

  1. params:
    • 一个包含模型参数的列表(通常是张量列表)。这些参数需要是可训练的,并且已经计算了梯度(即 param.grad 是有效的)。
  2. lr:
    • 学习率(Learning Rate),是一个标量值,控制参数更新的步长。学习率越大,每次更新的步长越大。
  3. batch_size:
    • 小批量的大小。在每次更新中,梯度是基于一个小批量的数据计算的。batch_size 用于对梯度进行归一化,确保梯度的大小与批量大小无关。

代码逻辑

  1. with torch.no_grad():

    • 这是一个上下文管理器,用于禁用梯度计算。在参数更新过程中,不需要计算梯度,因此使用 torch.no_grad() 可以节省内存和计算资源。
  2. param -= lr \* param.grad / batch_size

    • 这是参数更新的核心公式。它将每个参数 param 减去学习率 lr 乘以梯度 param.grad,并除以批量大小 batch_size

    • 除以 batch_size 是为了对梯度进行归一化,确保无论批量大小如何,每次更新的步长都保持一致。

  3. param.grad.zero_()

    • 在更新参数后,需要将梯度清零。这是因为 PyTorch 会累积梯度(即不会自动清零梯度),如果不手动清零,梯度会不断累加,导致错误的更新。
def sgd(params, lr, batch_size):
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

7.训练

lr = 0.03 #学习率
num_epochs = 3 #迭代周期个数
net = linreg  #线性回归模型
loss = squared_loss #损失函数
for epoch in range(num_epochs):  # epoch:迭代周期
    for x, y in data_iter(batch_size, features, labels):
        l = loss(net(x,w,b), y)  # x和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()
        sgd([w,b],lr,batch_size)  #使用参数的梯度更新参数
    with torch.no_grad():
        train_1 = loss(net(features,w,b),labels)
        print(f'epoch{epoch+1},loss{float(train_1.mean()):f}')
        
epoch1,loss0.034982
epoch2,loss0.000127
epoch3,loss0.000048
print(f'w的估计误差:{true_w-w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b-b}')
w的估计误差:tensor([ 7.9513e-05, -4.2915e-04], grad_fn=<SubBackward0>)
b的估计误差: tensor([0.0004], grad_fn=<RsubBackward1>)

posted @ 2025-06-17 19:21  小西贝の博客  阅读(13)  评论(0)    收藏  举报