1 前言

在线性回归的从零开始实现中【DeepLearning】1-1线性神经网络——线性回归的从零开始实现,我们手动定义了一些函数,对线性回归模型、损失函数和随机梯度下降算法有了一些直观的了解。在上一节中,我们只运用了张量来存储数据和进行线性运算,并通过自动微分来计算梯度。在本章节,我们将调用框架中现有的API,来实现数据迭代器、损失函数、优化器和神经网络层。

2 导入所需的函数模块

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l

3 线性回归的简洁实现

3.1 生成数据集

这里我们设置的参数和上一节相同,并利用synthetic_data()生成一个人造数据集。

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

3.2 调取数据集

与上一节不同的是,我们构建了一个用于PyTorch模型训练的迭代器。与data_iter()生成器函数相比,load_array()在处理大型数据集的速度更快,能够有效管理内存并提供多线程加载等功能。

def load_array(data_arrays, batch_size, is_train=True): 
#构建一个PyTorch数据迭代器
    dataset = data.TensorDataset(*data_arrays) 
    return data.DataLoader(dataset, batch_size, shuffle=is_train) 
#DataLoader创建一个数据加载器,能够以批次的形式获取数据。

注:
1、load_array()函数接受三个参数:data_arrays 是包含数据集features和labels的张量,batch_size是每批次加载的数据样本数量,is_train指示数据加载器是否用于训练集。
2、data.TensorDataset()可以将两个张量打包成一个数据集。*data_arrays将data_array中的两个张量拆解,分别上传给data.TensorDataset(),并存储在dataset中。
3、data.DataLoader()是 PyTorch 中用于加载数据的迭代器。将创建好的数据集dataset和批量大小输入,如果是用于训练就将数据随机打乱,增加模型训练时的随机性。最后,DataLoader会以指定的批次大小加载数据。

我们将features和labels作为参数传递,指定batch_size = 10,读取数据集。

batch_size = 10
data_iter = load_array((features, labels), batch_size) 

next(iter(data_iter)) #利用iter()函数将data_iter转换成数据迭代器,next() 函数用于获取迭代器的下一个数据批次。
[tensor([[ 0.5600, -0.5638],
         [-1.6015,  1.1657],
         [-0.1813, -0.9633],
         [-0.0611,  1.7921],
         [ 0.5661, -0.1923],
         [-0.9937, -0.4863],
         [-0.6721, -0.3319],
         [ 0.6390, -1.6512],
         [ 0.0119, -0.4017],
         [ 0.6863,  0.3038]]),
 tensor([[ 7.2355],
         [-2.9799],
         [ 7.1042],
         [-2.0026],
         [ 5.9905],
         [ 3.8471],
         [ 3.9819],
         [11.1014],
         [ 5.5861],
         [ 4.5413]])]

3.3 定义模型

对于标准的深度学习模型,我们通常使用框架预定义好的层,并且只关注使用哪些曾来构造模型,而不必关注层的实现细节。

from torch import nn

net = nn.Sequential(nn.Linear(2, 1))

注:
1、nn.Linear(2, 1) 指定接收2维的输入,即每个样本有2个features,和1个输出label。这个层在内部给定两组参数:一组权重(w1, w2)和一个偏置(b),它们的组合可以将输入数据线性变换为输出数据。
2、nn.Sequential()可以将所有层串联在一起,将第一层的输出作为第二层的输入,以此类推。但在本节中只有一层。

3.4 初始化模型参数

通过从均值为0、标准差为0.01的正态分布中抽取随机随机数来初始化 w w w,并将 b b b初始化设置为0。

net[0].weight.data.normal_(0, 0.01) #用正态分布替换掉data中w的值
net[0].bias.data.fill_(0)

注:刚才提到使用nn.Linear()创建层时内部给定了一组权重(w1, w2)和一个偏置(b),因此我们需要使用.data获取内部参数,在利用.normal_(0, 0.01)和.fill_(0)替换掉data中的内容。事实上 _ 会直接修改调用该方法的对象,而不是返回一个新的对象,从而达到替换的目的。

3.5 定义损失函数

计算均方误差使用的是MESLoss类,也称平方 L 2 L_2 L2 泛数。

loss = nn.MSELoss()

3.6 定义优化算法

在这里我们依旧使用上一节讲述的“小批量随机梯度下降”算法,在PyTorch的optim模块中可以直接调用SGD。我们需要指定模型中的优化参数 w w w b b b,以及算法的学习率。

trainer = torch.optim.SGD(net.parameters(), lr=0.03)

3.7 训练过程

1、在每轮(epoch)中,我们使用数据迭代器,用于遍历训练数据集中的每一个批次,将整个数据集遍历三次。
2、在新的模型中,我们只需要上传features(因为 w和b内置在层中)即可得到预测值,再与真实值做损失得到这一批次的损失值。
3、l.sum().backward() 对损失进行反向传播,计算损失对模型参数的梯度。由于PyTorch不会清零梯度,因此每一个循环都需要重复将模型参数梯度清零,再计算梯度更新模型。
4、最后利用最终更新的参数,计算整个数据集上的损失。

num_epochs = 3

for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X), y)
        trainer.zero_grad()
        l.sum().backward()
        trainer.step() #模型更新
    l = loss(net(features), labels)
    print(f'epoch{epoch + 1}, loss {l:f}')
epoch1, loss 0.000214
epoch2, loss 0.000102
epoch3, loss 0.000102

注:事实上,loss值的大小本身没有意义,在人造数据集中我们知道真实的参数 w w w b b b是多少,而在实际实际应用中并不存在真实的 w w w b b b,因此我们是通过每一轮epoch后loss是否是向着下降的趋势进行的,来评判我们的训练是否成功。

4 小结

在这一节中,我们调用框架中高级的API更加简洁地对线性回归模型进行了实现。利用data模块提供的数据处理工具,我们构建了数据迭代器,更加高效地获取数据集中的数据。利用nn模块的Linear()和Sequential(),我们构建了一个单层的网络。在PyTorch的optim模块中,我们可以直接调用SGD,不必手动计算随机梯度下降。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐