import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
使用线性模型参数 w=[2,−3.4]⊤ 、 b=4.2 和噪声项 ϵ 生成数据集及其标签:y=Xw+b+ϵ
def load_array(data_arrays, batch_size, is_train=True): #@save
"""构造一个PyTorch数据迭代器。"""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
torch.utils.data
class torch.utils.data.Dataset
表示Dataset的抽象类。
所有其他数据集都应该进行子类化。所有子类应该override
__len__
和__getitem__
,前者提供了数据集的大小,后者支持整数索引,范围从0到len(self)。class torch.utils.data.TensorDataset(data_tensor, target_tensor)
包装数据和目标张量的数据集。
通过沿着第一个维度索引两个张量来恢复每个样本。
参数:
- data_tensor (Tensor) - 包含样本数据
- target_tensor (Tensor) - 包含样本目标(标签)
class torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=<function default_collate>, pin_memory=False, drop_last=False)
数据加载器。组合数据集和采样器,并在数据集上提供单进程或多进程迭代器。
参数:
- dataset (Dataset) – 加载数据的数据集。
- batch_size (int, optional) – 每个batch加载多少个样本(默认: 1)。
- shuffle (bool, optional) – 设置为
True
时会在每个epoch重新打乱数据(默认: False).- sampler (Sampler, optional) – 定义从数据集中提取样本的策略。如果指定,则忽略
shuffle
参数。- num_workers (int, optional) – 用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)
- collate_fn (callable, optional) –
- pin_memory (bool, optional) –
- drop_last (bool, optional) – 如果数据集大小不能被batch size整除,则设置为True后可删除最后一个不完整的batch。如果设为False并且数据集的大小不能被batch size整除,则最后一个batch将更小。(默认: False)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
net = nn.Sequential(nn.Linear(2, 1))
第一个指定输入特征形状,即2,第二个指定输出特征形状,输出特征形状为单个标量,因此为1
class torch.nn.Sequential(* args)
一个时序容器。
Modules
会以他们传入的顺序被添加到容器中。当然,也可以传入一个OrderedDict
。为了更容易的理解如何使用
Sequential
, 下面给出了一个例子:# Example of using Sequential model = nn.Sequential( nn.Conv2d(1,20,5), nn.ReLU(), nn.Conv2d(20,64,5), nn.ReLU() ) # Example of using Sequential with OrderedDict model = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ]))
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
loss = nn.MSELoss()
计算均方误差使用的是MSELoss类,也称为平方 L2 范数。默认情况下,它返回所有样本损失的平均值。
Loss functions
基本用法:
criterion = LossCriterion() #构造函数有自己的参数 loss = criterion(x, y) #调用标准时也有参数
第一句在本例中为:loss = nn.MSELoss()
第二句在本例中为:l = loss(net(X) ,y) 以及 l = loss(net(features), labels)【见文末】
计算出来的结果已经对
mini-batch
取了平均。
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
params (iterable) – 待优化参数的iterable或者是定义了参数组的dict,lr为学习率
torch.optim
torch.optim
是一个实现了各种优化算法的库。大部分常用的方法得到支持,并且接口具备足够的通用性,使得未来能够集成更加复杂的方法。如何使用optimizer
为了使用
torch.optim
,你需要构建一个optimizer对象。这个对象能够保持当前参数状态并基于计算得到的梯度进行参数更新。构建
为了构建一个
Optimizer
,你需要给它一个包含了需要优化的参数(必须都是Variable
对象)的iterable。然后,你可以设置optimizer的参 数选项,比如学习率,权重衰减,等等。例子:
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9) optimizer = optim.Adam([var1, var2], lr = 0.0001)
为每个参数单独设置选项
Optimizer
也支持为每个参数单独设置选项。若想这么做,不要直接传入Variable
的iterable,而是传入dict
的iterable。每一个dict都分别定 义了一组参数,并且包含一个param
键,这个键对应参数的列表。其他的键应该optimizer所接受的其他参数的关键字相匹配,并且会被用于对这组参数的 优化。
注意:
你仍然能够传递选项作为关键字参数。在未重写这些选项的组中,它们会被用作默认值。当你只想改动一个参数组的选项,但其他参数组的选项不变时,这是 非常有用的。
例如,当我们想指定每一层的学习率时,这是非常有用的:
optim.SGD([ {'params': model.base.parameters()}, {'params': model.classifier.parameters(), 'lr': 1e-3} ], lr=1e-2, momentum=0.9)
这意味着
model.base
的参数将会使用1e-2
的学习率,model.classifier
的参数将会使用1e-3
的学习率,并且0.9
的momentum将会被用于所 有的参数。进行单次优化
所有的optimizer都实现了
step()
方法,这个方法会更新所有的参数。它能按两种方式来使用:
optimizer.step()
这是大多数optimizer所支持的简化版本。一旦梯度被如
backward()
之类的函数计算好后,我们就可以调用这个函数。例子
for input, target in dataset: optimizer.zero_grad() output = model(input) loss = loss_fn(output, target) loss.backward() optimizer.step()
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X) ,y) #通过调用net(X)生成预测并计算损失l
trainer.zero_grad() #将模型的参数梯度初始化为0
l.backward() #通过进行反向传播来计算梯度。
trainer.step() # 更新所有参数
根据pytorch中backward()函数的计算,当网络参量进行反馈时,梯度是累积计算而不是被替换,PyTorch会在随后的反向传播中累积梯度(有利于RNN),但在处理每一个batch时并不需要与其他batch的梯度混合起来累积计算,因此需要对每个batch调用一遍zero_grad()将参数梯度置0.
trainer.step()在参数迭代的时候是如何知道batch_size的?因为loss = nn.MSELoss(),均方误差是对样本总量平均过得到的,所以trainer.step()使用的是平均过的grad。
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')