Pytorch学习

202206026 -

0. 引言

最近在看开源代码论文的时候,看到很多作者都是采用torch来进行编码,但是我平时都是使用keras进行编码,所以我自己也准备学习一下。

但是因为平时学习的时候,都是有问题就查询一下,毕竟再完整系统的学习太浪费时间了,我也只是想看到作者代码而已。

所以这篇文章就来记录下自己学习的过程。

1. 相关学习记录

1)Understanding PyTorch with an example: a step-by-step tutorial

这篇文章算是我学习过程中,第一篇比较基础的文章了,算是非常全面了,通过这篇文章,差不多就懂了。而且这篇文章我也看了好几遍。

2)分类softmax如何进行编程

torch也算是用了很久了,但是一直没有接触过这种普通的MLP,就是最后的分类过程。此前都是弄自编码器的形式。即使对于是MLP其实也是没什么区别的,大框架没有什么变化,但是对于最终部分的细节,不太清楚。

在进行keras编程的时候,所有一层的softmax就是直接Dense(activation="softmax")就完事了。可能在torch中也可以,但是我还是希望能够看到跟此前使用方法一样的形式,也就是输出logits,然后我利用交叉熵来进行计算。首先,如果是这种形式,那么最后一个输出层,应该是具备与类比数据相同神经元个数的线性层,在之后,他的输出将于one-hot编码的标签数据进行比较,比较的函数是使用交叉熵。例如文章[1]中给出的代码:

# Define the loss function and optimizer
  loss_function = nn.CrossEntropyLoss()
  optimizer = torch.optim.Adam(mlp.parameters(), lr=1e-4)
  
  # Run the training loop
  for epoch in range(0, 5): # 5 epochs at maximum
    
    # Print epoch
    print(f'Starting epoch {epoch+1}')
    
    # Set current loss value
    current_loss = 0.0
    
    # Iterate over the DataLoader for training data
    for i, data in enumerate(trainloader, 0):
      
      # Get inputs
      inputs, targets = data
      
      # Zero the gradients
      optimizer.zero_grad()
      
      # Perform forward pass
      outputs = mlp(inputs)
      
      # Compute loss
      loss = loss_function(outputs, targets)

除了这部分,还需要度量这个准确度,其实这部分你只要利用torch.max这个函数就可以了,但是要注意参数。具体代码可以见文章[2]。但他的acc计算的话,最好还是统计整个epoch来计算,比较清楚。

(20230302)
在前面的内容中,具体阐述了分类模型的损失函数如何构建,我已经得到了最后与类别数量相同的神经元的最后一层输出,但实际上要在计算最后的交叉熵,还需要别的计算,例如softmax。前面的代码中,并不知道target是什么格式,在keras中一般这个一般都是one-hot格式,但是根据torch和文章[7]中指出,这部分并非是,而是直接的label,那么言下之意,是不是说,nn.CrossEntropyLoss是不是帮忙做了一些处理呢?看了一下,文章[7]说法,实际上,他是直接利用这个部分作为索引来进行计算,那这样的话,应该是必须保证label是连续,而且是和类别数量相同的。

而最后,在对这部分的信息进行一下分析,可以查看文章[8],其实nn.CrossEntropyLoss做了多件事情,包括softmax,log还有最后的一个NLLLoss,才得到了交叉熵。

3)打印模型信息

在使用keras编程神经网络的时候,可以直接用model.summary来打印模型的各层信息,包括参数数量等。但是torch中却没有直接的api。按照需求查找到了这部分内容[6],问答中给出的库是pytorch-summary,但实际上他的github已经更名为torchinfo。

from torchinfo import summary

model = ConvNet()
batch_size = 16
summary(model, input_size=(batch_size, 1, 28, 28))

上述命令是他官方给出的用法,但是在我用这个函数来打印我自己模型的时候(利用Sequential构建),会报错,只有把这个input_size参数删掉之后才能输出。暂时就先这么使用吧。后续看看是为什么。

4)batch-normalization

machine-learning-articles/batch-normalization-with-pytorch.md

2. 遇到的问题

1)同样的模型,keras和torch结果相差非常大

一般来说,两种不同的模型,你可能有一定差距,都是可以理解的,但是在我的数据上,居然相差了10个点,这就很尴尬了,这就没办法理解了。

首先,网络结构什么的,我都是固定好的,这个肯定是要一样的,同时里面的激活函数什么的,肯定也不能有所变化,这些简答的内容肯定都完全没什么问题。

但是效果就是不一样。然后我突然意识到,很有可能是这个神经网络权值初始化的问题。然后搜索了搜索这部分内容,的确是这部分问题。然后修改了之后,就能差不多的效果了,虽然并没有完全一致,毕竟还有一些随机性的问题。

2)随意层数的torch网络

在之前的使用中,因为都是比较简单的网络,五层,十层的样子,没有使用太深层次的网络,所以在编程中,都是直接把这个部分给写死到模型中。但其实我之前的时候,使用keras进行编程的时候,采用的形式都是变长的,利用一个字符串的形式,然后在定义网络的时候,注意增加到网络中。所以如何定义torch框架下的变长网络呢?!

在文章[3]的方法中,就是定义了了self.hidden = nn.ModuleList()这个变量,在增加这个神经元的时候,形式是

        self.hidden = nn.ModuleList()
        for k in range(len(h_sizes)-1):
            self.hidden.append(nn.Linear(h_sizes[k], h_sizes[k+1]))

同时另外的一个人提出了另外的方案,

import torch
from torch import nn, optim
from torch.nn.modules import Module
from implem.settings import settings


class MLP(nn.Module):
    def __init__(self, input_size, layers_data: list, learning_rate=0.01, optimizer=optim.Adam):
        super().__init__()

        self.layers = nn.ModuleList()
        self.input_size = input_size  # Can be useful later ...
        for size, activation in layers_data:
            self.layers.append(nn.Linear(input_size, size))
            input_size = size  # For the next layer
            if activation is not None:
                assert isinstance(activation, Module), \
                    "Each tuples should contain a size (int) and a torch.nn.modules.Module."
                self.layers.append(activation)

        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        self.to(self.device)
        self.learning_rate = learning_rate
        self.optimizer = optimizer(params=self.parameters(), lr=learning_rate)

    def forward(self, input_data):
        for layer in self.layers:
            input_data = layer(input_data)
        return input_data

下面这种形式,更符合我现在的一个需求。但是我觉得还是应该看看更多的代码,来看看更精简,更清晰的代码形式是什么样子。这种,应该是属于比较普遍的需求了。之前的时候,我也是看了一篇顶会上的论文,采用了那种从字符串逐步读取的形式来定义Keras模型,这里后面多看看代码应该也很好找到。

另外,torch还有另外一种形式,就是nn.Sequantial这种,跟我之前Keras下使用的一种形式应该是一样的,在问答[4]中说明了两种形式的不同。

(20230223 - 增加)

今天需要编写变长的自编码器,就专门搜索了这部分内容,重点在于怎么使用sequanntial或者nn.ModuleList这种,然后在这篇文章[5]中找到了答案:

在这里插入图片描述
不过对我来说,重点是为了得到某个部分的输出,我最后是选择了利用ModuleList的形式,但实际上,我按照Sequential先定义多个块也是可以的。这样看来结果更加方便。

参考

[1]creating-a-multilayer-perceptron-with-pytorch-and-lightning
[2]1 - Multilayer Perceptron.ipynb
[3]How to create MLP model with arbitrary number of hidden layers
[4]When should I use nn.ModuleList and when should I use nn.Sequential?
[5]FrancescoSaverioZuppichini/Pytorch-how-and-when-to-use-Module-Sequential-ModuleList-and-ModuleDict
[6]How do I print the model summary in PyTorch?
[7]Is One-Hot Encoding required for using PyTorch’s Cross Entropy Loss Function?
[8]Pytorch详解NLLLoss和CrossEntropyLoss

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值