美文网首页
pytorch实现线性回归,Softmax与分类模型,多层感知机

pytorch实现线性回归,Softmax与分类模型,多层感知机

作者: 多彩海洋 | 来源:发表于2020-02-13 19:46 被阅读0次

    1.线性回归的简洁实现

    实践中,我们通常可以用比上分段更简洁的代码来实现同样的模型。在本节中,我们将介绍如何使用PyTorch更方便地实现线性回归的训练。

    1.1生成数据集

    我们生成与上一级中相同的数据集。其中features是训练数据特征,labels是标签。

    num_inputs = 2
    num_examples = 1000
    true_w = [2, -3.4]
    true_b = 4.2
    features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
    labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
    labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
    

    1.2读取数据

    PyTorch提供了data包来读取数据。由于data经常使用变量名,我们将导入的data模块用代替Data。在每一次重复中,我们将随机读取包含10个数据样本的小批量。

    import torch.utils.data as Data
    
    batch_size = 10
    # 将训练数据的特征和标签组合
    dataset = Data.TensorDataset(features, labels)
    # 随机读取小批量
    data_iter = Data.DataLoader(dataset, batch_size, shuffle=True)
    

    让我们读取并打印第一个小批量数据样本。

    for X, y in data_iter:
        print(X, y)
        break
    

    输出:

    tensor([[-2.7723, -0.6627],
            [-1.1058,  0.7688],
            [ 0.4901, -1.2260],
            [-0.7227, -0.2664],
            [-0.3390,  0.1162],
            [ 1.6705, -2.7930],
            [ 0.2576, -0.2928],
            [ 2.0475, -2.7440],
            [ 1.0685,  1.1920],
            [ 1.0996,  0.5106]]) 
     tensor([ 0.9066, -0.6247,  9.3383,  3.6537,  3.1283, 17.0213,  5.6953, 17.6279,
             2.2809,  4.6661])
    

    1.3定义模型

    首先,引入torch.nn模块。实际上,“ nn”是神经网络(神经网络)的缩写。顾名思义,该模块定义了串联神经网络的层。nn就是利用autograd来定义模型。nn的核心数据结构是Module,它是一个抽象概念,既可以表示神经网络中的某个层(layer),也可以表示一个包含很多层的神经网络。在实际使用中,最常见的做法是继承nn.Module,编写自己的网络/层。一个nn.Module实例应该包含一些层以及返回输出的前向传播(forward)方法。下面先来看看如何用nn.Module实现一个线性回归模型。

    class LinearNet(nn.Module):
        def __init__(self, n_feature):
            super(LinearNet, self).__init__()
            self.linear = nn.Linear(n_feature, 1)
        # forward 定义前向传播
        def forward(self, x):
            y = self.linear(x)
            return y
    
    net = LinearNet(num_inputs)
    print(net) # 使用print可以打印出网络的结构
    

    输出:

    LinearNet(
      (linear): Linear(in_features=2, out_features=1, bias=True)
    )
    

    我们也可以用nn.Sequential来更加网求方便地搭建网络,Sequential是一个有序的容器,层网络将按照在传入Sequential的顺序依次被添加到计算图产品中。

    # 写法一
    net = nn.Sequential(
        nn.Linear(num_inputs, 1)
        # 此处还可以传入其他层
        )
    
    # 写法二
    net = nn.Sequential()
    net.add_module('linear', nn.Linear(num_inputs, 1))
    # net.add_module ......
    
    # 写法三
    from collections import OrderedDict
    net = nn.Sequential(OrderedDict([
              ('linear', nn.Linear(num_inputs, 1))
              # ......
            ]))
    
    print(net)
    print(net[0])
    

    输出:

    Sequential(
      (linear): Linear(in_features=2, out_features=1, bias=True)
    )
    Linear(in_features=2, out_features=1, bias=True)
    

    可以通过net.parameters()来查看模型所有的可学习参数,此函数将返回一个生成器。

    for param in net.parameters():
        print(param)
    

    输出:

    Parameter containing:
    tensor([[-0.0277,  0.2771]], requires_grad=True)
    Parameter containing:
    tensor([0.3395], requires_grad=True)
    

    回顾图1.1中线性回归在神经网络图中的表示。作为一个单层神经网络,线性回归输出层中的神经元和输入层中各个输入完全连接。因此,线性回归的输出层又叫全连接层。

    注意:仅torch.nn支持输入一个批处理的样本不支持样本样本输入,如果只有单个样本,可使用input.unsqueeze(0)来添加一维。

    1..4初始化模型参数

    在使用net前,我们需要初始化模型参数,如线性回归模型中的权重和偏差。PyTorch在init模块中提供了多种参数初始化方法。这里的initinitializer的缩写形式。我们init.normal_将权重参数每个元素初始化为随机采样于均值0,标准差为0.01的正态分布。偏差会初始化为零。

    from torch.nn import init
    
    init.normal_(net[0].weight, mean=0, std=0.01)
    init.constant_(net[0].bias, val=0)  # 也可以直接修改bias的data: net[0].bias.data.fill_(0)
    

    注:如果这里的net是用3.3.3节一开始的代码自定义的,那么上面代码会报错,net[0].weight应替换net.linear.weightbias亦然。因为net[0]这样根据下标访问子模块的写法只有当net是一个ModuleList或者Sequential实例时才可以,详见4.1节。

    1.5定义损失函数

    PyTorch在nn模块中提供了各种损失函数,这些损失函数可称为是一种特殊的层,PyTorch也将这些损失函数实现为nn.Module的子类。函数。

    loss = nn.MSELoss()
    

    1.6 定义优化算法

    同样,我们也无须自己实现小批量随机梯度下降算法。torch.optim模块提供了很多常用的优化算法比如SGD,亚当和RMSProp等。我们下面创建³³一个用于优化net所有参数的优化器实例,并指定学习率为0.03的小批量随机梯度下降(SGD)为优化算法。

    import torch.optim as optim
    
    optimizer = optim.SGD(net.parameters(), lr=0.03)
    print(optimizer)
    

    输出:

    SGD (
    Parameter Group 0
        dampening: 0
        lr: 0.03
        momentum: 0
        nesterov: False
        weight_decay: 0
    )
    

    我们还可以为不同子网络设置不同的学习率,这在finetune时经常用到。

    optimizer =optim.SGD([
                    # 如果对某个参数不指定学习率,就使用最外层的默认学习率
                    {'params': net.subnet1.parameters()}, # lr=0.03
                    {'params': net.subnet2.parameters(), 'lr': 0.01}
                ], lr=0.03)
    

    有时候我们不想让学习率固定成一个常数,那如何调整学习率呢?主要有两种做法。一种是修改optimizer.param_groups中对应的学习率,另一种是更简单也是推荐的做法-新建优化器,由于optimizer极其轻量级,体积很小,故而可以构建新的optimizer。震荡等情况。

    # 调整学习率
    for param_group in optimizer.param_groups:
        param_group['lr'] *= 0.1 # 学习率为之前的0.1倍
    

    1.7训练模型

    在使用Gluon训练模型时,我们通过调用optim实例的step函数来转换模型参数。按照小批量随机递减的定义,我们在step函数中指定尺寸大小,从而对批量中样本梯度求平均。

    num_epochs = 3
    for epoch in range(1, num_epochs + 1):
        for X, y in data_iter:
            output = net(X)
            l = loss(output, y.view(-1, 1))
            optimizer.zero_grad() # 梯度清零,等价于net.zero_grad()
            l.backward()
            optimizer.step()
        print('epoch %d, loss: %f' % (epoch, l.item()))
    

    输出:

    epoch 1, loss: 0.000457
    epoch 2, loss: 0.000081
    epoch 3, loss: 0.000198
    

    下面我们分别比较比较到到模型参数和真实的模型参数。我们从net获得需要的层,并访问其权重(weight)和偏差(bias)。学到的参数和真实的参数很接近。

    dense = net[0]
    print(true_w, dense.weight)
    print(true_b, dense.bias)
    

    输出:

    [2, -3.4] tensor([[ 1.9999, -3.4005]])
    4.2 tensor([4.2011])
    

    小结

    • 使用PyTorch可以更简洁地实现模型。
    • torch.utils.data模块提供了有关数据处理的工具,torch.nn模块定义了神经网络的层,torch.nn.init模块定义了各种初始化方法,torch.optim模块提供了很多常用的优化算法。

    2 softmax回归的简洁实现

    (线性回归的简洁实现)中已经了解了使用Pytorch实现模型的便利。下面,让我们再次使用Pytorch来实现一个softmax回归模型。首先引入所需的包或模块。

    import torch
    from torch import nn
    from torch.nn import init
    import numpy as np
    import sys
    sys.path.append("..") 
    import d2lzh_pytorch as d2l
    

    2.1获取和读取数据

    我们仍然使用Fashion-MNIST数据集和上段中设置的批量大小。

    batch_size = 256
    train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
    

    2.2定义和初始化模型

    softmax回归的输出层是一个全连接层,所以我们用一个线性模块就可以了。因为前面我们数据返回的每个批量样本x的形状为(batch_size,1, 28,28),所以我们要先用view()x的形状转换成(batch_size,784)才送入全连接层。

    num_inputs = 784
    num_outputs = 10
    
    class LinearNet(nn.Module):
        def __init__(self, num_inputs, num_outputs):
            super(LinearNet, self).__init__()
            self.linear = nn.Linear(num_inputs, num_outputs)
        def forward(self, x): # x shape: (batch, 1, 28, 28)
            y = self.linear(x.view(x.shape[0], -1))
            return y
    
    net = LinearNet(num_inputs, num_outputs)
    

    我们将对x的形状转换的这个功能自定义一个FlattenLayer并记录在d2lzh_pytorch中方便后面使用。

    # 本函数已保存在d2lzh_pytorch包中方便以后使用
    class FlattenLayer(nn.Module):
        def __init__(self):
            super(FlattenLayer, self).__init__()
        def forward(self, x): # x shape: (batch, *, *, ...)
            return x.view(x.shape[0], -1)
    

    这样我们就可以更方便地定义我们的模型:

    from collections import OrderedDict
    
    net = nn.Sequential(
        # FlattenLayer(),
        # nn.Linear(num_inputs, num_outputs)
        OrderedDict([
            ('flatten', FlattenLayer()),
            ('linear', nn.Linear(num_inputs, num_outputs))
        ])
    )
    

    然后,我们使用均值0,标准差为0.01的正态分布随机初始化模型的权重参数。

    init.normal_(net.linear.weight, mean=0, std=0.01)
    init.constant_(net.linear.bias, val=0) 
    

    2.3 softmax和交叉熵损失函数

    因此,PyTorch提供了一个包括softmax计算和交叉熵计算的函数。它的数值稳定性更好。

    loss = nn.CrossEntropyLoss()
    

    2.4定义优化算法

    我们使用学习逐步0.1的小批量随机梯度下降作为优化算法。

    optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
    

    2.5训练模型

    接下来,我们使用上一级中定义的训练函数来训练模型。

    num_epochs = 5
    d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
    

    输出:

    epoch 1, loss 0.0031, train acc 0.745, test acc 0.790
    epoch 2, loss 0.0022, train acc 0.812, test acc 0.807
    epoch 3, loss 0.0021, train acc 0.825, test acc 0.806
    epoch 4, loss 0.0020, train acc 0.832, test acc 0.810
    epoch 5, loss 0.0019, train acc 0.838, test acc 0.823
    

    小结

    • PyTorch提供的函数往往具有更好的数值稳定性。
    • 可以使用PyTorch更简洁地实现softmax回归。

    3 多层感知机的简洁实现

    下面我们使用PyTorch来实现上分段中的多层感知机。首先引入所需的包或模块。

    import torch
    from torch import nn
    from torch.nn import init
    import numpy as np
    import sys
    sys.path.append("..") 
    import d2lzh_pytorch as d2l
    

    3.1定义模型

    和softmax回归唯一的不同在于,我们多加了一个全连接层作为隐藏层。它的隐藏单元个数为256,并使用ReLU函数作为激活函数。

    num_inputs, num_outputs, num_hiddens = 784, 10, 256
    
    net = nn.Sequential(
            d2l.FlattenLayer(),
            nn.Linear(num_inputs, num_hiddens),
            nn.ReLU(),
            nn.Linear(num_hiddens, num_outputs), 
            )
    
    for params in net.parameters():
        init.normal_(params, mean=0, std=0.01)
    

    3.2读取数据并训练模型

    我们使用与3.7节中训练softmax回归几乎相同的步骤来读取数据并训练模型。

    注:由于这里使用的是PyTorch的SGD而不是d2lzh_pytorch里面的sgd,所以就不存在3.9节那样学习率看起来很大的问题了。

    batch_size = 256
    train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
    loss = torch.nn.CrossEntropyLoss()
    
    optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
    
    num_epochs = 5
    d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
    

    输出:

    epoch 1, loss 0.0030, train acc 0.712, test acc 0.744
    epoch 2, loss 0.0019, train acc 0.823, test acc 0.821
    epoch 3, loss 0.0017, train acc 0.844, test acc 0.842
    epoch 4, loss 0.0015, train acc 0.856, test acc 0.842
    epoch 5, loss 0.0014, train acc 0.864, test acc 0.818
    

    小结

    • 通过PyTorch可以更简洁地实现多层感知机。

    相关文章

      网友评论

          本文标题:pytorch实现线性回归,Softmax与分类模型,多层感知机

          本文链接:https://www.haomeiwen.com/subject/zlncfhtx.html