3-2线性回归从0实现
导入库
%matplotlib inline
import random
import torch
from d2l import torch as d2l
1.生成数据集
相关用法
1. torch.normal()
torch.normal()
的主要功能是从正态分布中抽取随机数,并将这些随机数填充到一个张量中。
它的参数主要包括均值(mean)和标准差(std),以及生成的张量的形状
参数说明
mean
(均值)- 可以是一个标量(单个数值),表示所有生成的随机数的均值。
- 也可以是一个张量,其形状决定了生成的随机数的形状,每个位置的随机数将使用该位置的均值。
std
(标准差)- 可以是一个标量,表示所有生成的随机数的标准差。
- 也可以是一个张量,其形状必须与
mean
一致,每个位置的随机数将使用该位置的标准差。
size
(生成张量的形状)- 如果
mean
和std
是标量,可以通过size
参数指定生成的张量的形状。 - 如果
mean
和std
是张量,则size
参数通常不需要,因为生成的张量形状将与mean
和std
的形状一致。
- 如果
out
(输出张量)- 可选参数,用于指定一个已存在的张量,将生成的随机数填充到该张量中。如果未指定,则返回一个新的张量。
generator
(随机数生成器)- 可选参数,用于指定一个随机数生成器,以控制随机数的生成过程。这在需要固定随机种子时非常有用。
dtype
(数据类型)- 可选参数,指定生成的张量的数据类型,默认为
torch.float32
。
- 可选参数,指定生成的张量的数据类型,默认为
device
(设备)- 可选参数,指定生成的张量存储的设备(CPU 或 GPU)。
2. torch.matmul
torch.matmul()
是 PyTorch 中用于执行矩阵乘法的函数。它可以处理多种类型的张量输入,包括一维、二维和高维张量,并且能够自动广播和处理不同形状的张量。以下是 torch.matmul
的详细用法和一些示例。
功能
torch.matmul
用于计算两个张量的矩阵乘积。它可以处理以下几种情况:
- 一维张量与一维张量:等价于点积。
- 二维张量与二维张量:标准矩阵乘法。
- 高维张量与高维张量:批量矩阵乘法。
- 高维张量与低维张量:自动广播。
参数
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);
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.定义优化算法
参数说明
params
:- 一个包含模型参数的列表(通常是张量列表)。这些参数需要是可训练的,并且已经计算了梯度(即
param.grad
是有效的)。
- 一个包含模型参数的列表(通常是张量列表)。这些参数需要是可训练的,并且已经计算了梯度(即
lr
:- 学习率(Learning Rate),是一个标量值,控制参数更新的步长。学习率越大,每次更新的步长越大。
batch_size
:- 小批量的大小。在每次更新中,梯度是基于一个小批量的数据计算的。
batch_size
用于对梯度进行归一化,确保梯度的大小与批量大小无关。
- 小批量的大小。在每次更新中,梯度是基于一个小批量的数据计算的。
代码逻辑
-
with torch.no_grad():
- 这是一个上下文管理器,用于禁用梯度计算。在参数更新过程中,不需要计算梯度,因此使用
torch.no_grad()
可以节省内存和计算资源。
- 这是一个上下文管理器,用于禁用梯度计算。在参数更新过程中,不需要计算梯度,因此使用
-
param -= lr \* param.grad / batch_size
-
这是参数更新的核心公式。它将每个参数
param
减去学习率lr
乘以梯度param.grad
,并除以批量大小batch_size
。 -
除以
batch_size
是为了对梯度进行归一化,确保无论批量大小如何,每次更新的步长都保持一致。
-
-
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>)