import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn # nn 是神经网络(Neural Networks)的缩写
true_w = torch.tensor([2, -3.4]) # 与上一节类似 生成数据集
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
# 调用现有 API 读取数据,其中 is_train 表示是否打乱数据
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)
batch_size = 10
data_iter = load_array((features, labels), batch_size) # 构造迭代器
next(iter(data_iter)) # 调用 next 函数从迭代器获取第一项
[tensor([[-0.5500, 0.3406],
[-0.1213, 0.0385],
[ 1.0384, -0.4336],
[-0.6411, -0.5833],
[ 0.9083, -0.2935],
[-1.5141, -1.2149],
[ 3.8181, -0.4858],
[ 1.4873, 0.4429],
[-0.9654, -1.1355],
[ 1.2120, -0.8451]]),
tensor([[ 1.9365],
[ 3.8387],
[ 7.7771],
[ 4.8915],
[ 7.0209],
[ 5.2958],
[13.4798],
[ 5.6838],
[ 6.1303],
[ 9.5189]])]
标准的深度学习模型可以试图框架预先定义好的层,我们可以只关注模型的构建而非层的实现细节。
我们定义的模型 net 是 Sequential 类的实例,Sequential 类可以将多个层串在一起,即将数据传入第一层,再将第一层的输出作为第二层输入,以此类推。本模型只用了一层,实际上使用不到 Sequential 类,在此只是熟悉以下标准流程。
net = nn.Sequential(nn.Linear(2, 1))
通过 net[0] 选择网络中的第一层,然后使用 weight.data 和 bias.data 访问参数,直接使用预定义的方法 normal_ 和 fill_ 重写参数。
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
tensor([0.])
均方误差使用的是 MSELoss 类,也称为平方 L 2 L_2 L2 范数。默认情况下,它返回所以样本损失的平均值。
loss = nn.MSELoss()
PyTorch 在 optim 模块中实现了小批量随机梯度下降算法的许多变体。可通过 net.parameters() 从模型获得优化参数以实例化一个 SGD 实例。
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X), y)
trainer.zero_grad() # 梯度归零
l.backward() # 反向传播
trainer.step() # 更新模型参数
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
epoch 1, loss 0.000116
epoch 2, loss 0.000115
epoch 3, loss 0.000115
w = net[0].weight.data
print('w 的误差估计:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b 的估计误差:', true_b - b)
w 的误差估计: tensor([-5.7220e-06, -6.5851e-04])
b 的估计误差: tensor([-0.0003])
(1)如果将小批量的总损失替换为小批量损失的平均值,需要如何更改学习率?
不是默认就是损失的均值吗?
如果是均值,则梯度会多一个系数 1 b a t c h _ s i z e \frac{1}{batch\_size} batch_size1,所以为了抵消该系数应该使 l r × b a t c h _ s i z e lr\times batch\_size lr×batch_size。
ps:所以说上一节那个 sgd 除一个 batch_size 可能是因为最后传的是总损失,在优化器里除个 batch_size 模拟框架实现的 SGD 默认传均值的样子。感觉多此一举,怪怪的。
(2)查看深度学习框架文档,它们提供了哪些损失函数和初始化方法?用胡伯尔损失代替原损失,即
l ( y , y ′ ) = { ∣ y − y ′ ∣ − σ 2 , 若 ∣ y − y ′ ∣ > σ 1 2 σ ( y − y ′ ) 2 , 其他情况 \begin{equation} l(y,y')=\left\{ \begin{aligned} & |y-y'|-\frac{\sigma}{2} &&, 若|y-y'|>\sigma\\ & \frac{1}{2\sigma}(y-y')^2 &&, 其他情况 \end{aligned} \right. \end{equation} l(y,y′)=⎩ ⎨ ⎧∣y−y′∣−2σ2σ1(y−y′)2,若∣y−y′∣>σ,其他情况
dir(nn)
['AdaptiveAvgPool1d',
'AdaptiveAvgPool2d',
'AdaptiveAvgPool3d',
'AdaptiveLogSoftmaxWithLoss',
'AdaptiveMaxPool1d',
'AdaptiveMaxPool2d',
'AdaptiveMaxPool3d',
'AlphaDropout',
'AvgPool1d',
'AvgPool2d',
'AvgPool3d',
'BCELoss',
'BCEWithLogitsLoss',
'BatchNorm1d',
'BatchNorm2d',
'BatchNorm3d',
'Bilinear',
'CELU',
'CTCLoss',
'ChannelShuffle',
'ConstantPad1d',
'ConstantPad2d',
'ConstantPad3d',
'Container',
'Conv1d',
'Conv2d',
'Conv3d',
'ConvTranspose1d',
'ConvTranspose2d',
'ConvTranspose3d',
'CosineEmbeddingLoss',
'CosineSimilarity',
'CrossEntropyLoss',
'CrossMapLRN2d',
'DataParallel',
'Dropout',
'Dropout1d',
'Dropout2d',
'Dropout3d',
'ELU',
'Embedding',
'EmbeddingBag',
'FeatureAlphaDropout',
'Flatten',
'Fold',
'FractionalMaxPool2d',
'FractionalMaxPool3d',
'GELU',
'GLU',
'GRU',
'GRUCell',
'GaussianNLLLoss',
'GroupNorm',
'Hardshrink',
'Hardsigmoid',
'Hardswish',
'Hardtanh',
'HingeEmbeddingLoss',
'HuberLoss',
'Identity',
'InstanceNorm1d',
'InstanceNorm2d',
'InstanceNorm3d',
'KLDivLoss',
'L1Loss',
'LPPool1d',
'LPPool2d',
'LSTM',
'LSTMCell',
'LayerNorm',
'LazyBatchNorm1d',
'LazyBatchNorm2d',
'LazyBatchNorm3d',
'LazyConv1d',
'LazyConv2d',
'LazyConv3d',
'LazyConvTranspose1d',
'LazyConvTranspose2d',
'LazyConvTranspose3d',
'LazyInstanceNorm1d',
'LazyInstanceNorm2d',
'LazyInstanceNorm3d',
'LazyLinear',
'LeakyReLU',
'Linear',
'LocalResponseNorm',
'LogSigmoid',
'LogSoftmax',
'MSELoss',
'MarginRankingLoss',
'MaxPool1d',
'MaxPool2d',
'MaxPool3d',
'MaxUnpool1d',
'MaxUnpool2d',
'MaxUnpool3d',
'Mish',
'Module',
'ModuleDict',
'ModuleList',
'MultiLabelMarginLoss',
'MultiLabelSoftMarginLoss',
'MultiMarginLoss',
'MultiheadAttention',
'NLLLoss',
'NLLLoss2d',
'PReLU',
'PairwiseDistance',
'Parameter',
'ParameterDict',
'ParameterList',
'PixelShuffle',
'PixelUnshuffle',
'PoissonNLLLoss',
'RNN',
'RNNBase',
'RNNCell',
'RNNCellBase',
'RReLU',
'ReLU',
'ReLU6',
'ReflectionPad1d',
'ReflectionPad2d',
'ReflectionPad3d',
'ReplicationPad1d',
'ReplicationPad2d',
'ReplicationPad3d',
'SELU',
'Sequential',
'SiLU',
'Sigmoid',
'SmoothL1Loss',
'SoftMarginLoss',
'Softmax',
'Softmax2d',
'Softmin',
'Softplus',
'Softshrink',
'Softsign',
'SyncBatchNorm',
'Tanh',
'Tanhshrink',
'Threshold',
'Transformer',
'TransformerDecoder',
'TransformerDecoderLayer',
'TransformerEncoder',
'TransformerEncoderLayer',
'TripletMarginLoss',
'TripletMarginWithDistanceLoss',
'Unflatten',
'Unfold',
'UninitializedBuffer',
'UninitializedParameter',
'Upsample',
'UpsamplingBilinear2d',
'UpsamplingNearest2d',
'ZeroPad2d',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__path__',
'__spec__',
'_reduction',
'common_types',
'factory_kwargs',
'functional',
'grad',
'init',
'intrinsic',
'modules',
'parallel',
'parameter',
'qat',
'quantizable',
'quantized',
'utils']
loss = nn.SmoothL1Loss()
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X), y)
trainer.zero_grad() # 梯度归零
l.backward() # 反向传播
trainer.step() # 更新模型参数
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
w = net[0].weight.data
print('w 的误差估计:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b 的估计误差:', true_b - b)
epoch 1, loss 0.000057
epoch 2, loss 0.000057
epoch 3, loss 0.000057
w 的误差估计: tensor([0.0005, 0.0001])
b 的估计误差: tensor([-0.0001])
(3)如何访问线性回归的梯度?
net[0].weight.grad, net[0].bias.grad
(tensor([[0.0064, 0.0048]]), tensor([-0.0056]))