PyTorch中“模块”一词的定义是什么?

29
请原谅我这个初学者的问题,Modulemodel是同一个意思吗?
当官方文档说:

如果您需要更复杂的模型而不仅仅是现有的简单序列,则需要定义您的模型(作为自定义 Module 子类)。

时,似乎是这样。或者,当他们提到Module时,是不是指像协议/接口类型这样更正式和计算机科学的东西?

没有单一精确的定义。 - prosti
4个回答

24
  • 这是一个简单的容器。

  • 来自nn.Module文档:

  • 所有神经网络模块的基类,你的模型也应该继承此类。模块还可以包含其他模块,允许将它们嵌套在树形结构中。您可以将子模块分配为常规属性。以这种方式分配的子模块将被注册,并且当您调用.cuda()等方法时,它们的参数也会被转换。

  • 来自教程:

  • 所有网络组件都应该继承自nn.Module并重写forward()方法。就这些,至于样板文件来说就是这么多。从nn.Module继承提供了对组件的功能。例如,它使它跟踪它的可训练参数,使用.to(device)方法可以在CPU和GPU之间交换它,其中device可以是CPU设备torch.device("cpu")或CUDA设备torch.device("cuda:0")。

  • 一个模块是一个容器,层、模型子部分(例如torchvision中的resnet中的BasicBlock)和模型应该继承自它。为什么?因为从nn.Module继承允许您轻松调用方法,如to("cuda:0").eval().parameters()或注册钩子。

  • 为什么不直接称 'module' 为模型(model),层(layers)呢?我猜可能只是语义和微小差别,但...
  • 这是 API 设计的选择,我认为只有一个 Module 类而不是两个分开的 ModelLayers 更加清晰,并允许更大的自由度(更容易将模型的一部分发送到 GPU,仅获取某些层的参数等)。


    6

    在不成为PyTorch专家的情况下,我理解中的模块是指一个容器,它接收张量作为输入并计算输出张量。

    因此,总结起来,您的模型很可能由多个模块组成,例如,您可能有3个模块,每个模块代表神经网络的一层。因此,它们在某种意义上相关,因为您需要模块来实现您的模型,但它们并不是同一件事。

    希望这可以帮助您。


    为什么不直接把“module”称作“模型”,将“layers”叫做“层”呢?我想也许这只是语义上的区别,但还是... - Monica Heddneck
    1
    层是模块。看看例如torch.nn.Linear的源代码,它是一个线性层,它扩展了Module。我认为他们只是决定定义一个将张量作为输入并计算输出张量的模块。这种结构的一些用例可能是神经网络中的一层,而其他用例则不一定。 - JustDanyul

    3

    为什么不直接把“module”称作模型,把“layers”称作层呢?

    这是因为PyTorch最初继承自Torch,而Torch最初是用Lua编写的,在那里他们将其称为“module”。

    PyTorch中“Module”的确切定义是什么?

    一般有不同类型的定义。

    以下是一种实用的定义:

    • 模块是具有结构并通过该结构向前运行以获取输出(返回值)的东西。

    以下是一种结构性的定义:

    • 模块还知道状态,因为您可以要求它提供参数列表:module.parameters()

    以下是一种功能性的定义:

    • 模块可以调用module.zero_grad()将其中所有参数的梯度设置为零。这是我们在每次反向传播步骤后都应该做的事情。这表明模块还必须处理反向传播,反向传播是标记为更新的参数将要更新的步骤。

    被标记为更新的模块参数具有如下requires_grad=True

    Parameter containing:
    tensor([-0.4411, -0.2094, -0.5322, -0.0154, -0.1009], requires_grad=True)
    

    你可以说参数就像张量一样,只不过它们有一个属性requires_grad,你可以决定它们在反向传播期间是否更新。
    最后,回到forward步骤,得到一个重要的提示:
    class ZebraNet(nn.Module):
    
        def __init__(self, num_classes=1000):
            super(self).__init__()
            self.convpart = nn.Sequential(
                nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.Conv2d(64, 192, kernel_size=5, padding=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.Conv2d(192, 384, kernel_size=3, padding=1),
                nn.ReLU(inplace=True),
                nn.Conv2d(384, 256, kernel_size=3, padding=1),
                nn.ReLU(inplace=True),
                nn.Conv2d(256, 256, kernel_size=3, padding=1),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
            )
            self.avgpooling = nn.AdaptiveAvgPool2d((6, 6))
            self.classifier = nn.Sequential(
                nn.Dropout(),
                nn.Linear(256 * 6 * 6, 4096),
                nn.ReLU(inplace=True),
                nn.Dropout(),
                nn.Linear(4096, 4096),
                nn.ReLU(inplace=True),
                nn.Linear(4096, num_classes),
            )
    
        def forward(self, x):
            x = self.convpart(x)
            x = self.avgpooling(x)
            x = x.view(x.size(0), 256 * 6 * 6)
            x = self.classifier(x)
            return x
    

    你可以看到在__init__中设置了结构,而forward()会告诉你输入x会发生什么以及返回什么。这个返回值将具有我们需要的输出维度。根据我们预测输出的精确度,我们的准确性越差或越好,这通常是我们跟踪进展的指标。

    0
    为什么不直接把“module”称作模型,把“layers”称作层呢?
    回想一下数据结构课程中,你是这样定义二叉树的。
    class tree:
        def __init__(self, value, left, right):
            self.value = value
            self.left = left
            self.right = right
    

    你可以向树中添加子树或叶子来形成新的树,就像你可以向模块中添加子模块来形成新的模块(你不想将子树和树作为两种不同的数据结构,你不想将叶子和树作为两种不同的数据结构,因为毕竟它们都是树,你想使用模块来表示模型和层... 将其视为递归,这是一种API设计选择,使事情更加清晰。)

    在PyTorch中,“模块”具体定义是什么?

    我认为模块是一种接收输入并输出结果的东西,就像一个函数... 这就是模块类中的前向方法所做的事情(指定函数是什么),你需要重写默认的前向方法,否则PyTorch将不知道函数是什么...

    def forward(self, x):
            x = F.relu(self.conv1(x))
            x = F.max_pool2d(x, 2, 2)
            x = F.relu(self.conv2(x))
            x = F.max_pool2d(x, 2, 2)
            x = x.view(-1, 4*4*50)
            x = F.relu(self.fc1(x))
            x = self.fc2(x)
            return F.log_softmax(x, dim=1)
    

    另一个例子是nn.sequential,它也是一个模块,但是它是一个特殊的模块,它将这些模块的输入和输出链接在一起,并形成一个列表。
    nn.sequential(a, b c) # a->b->c
    

    这就是为什么您不需要指定前向方法的原因,因为它是隐式指定的(只需将前一个模块的输出作为下一个模块的输入即可)。

    另一个例子是conv2d,它也是一个模块,其前向方法已经定义好了,因此您不需要指定它...

    class _ConvNd(Module):
        # omit 
    class Conv2d(_ConvNd):
        def __init__(self, in_channels, out_channels, kernel_size, stride=1,
    
                     padding=0, dilation=1, groups=1,
    
                     bias=True, padding_mode='zeros'):
    
            kernel_size = _pair(kernel_size)
    
            stride = _pair(stride)
    
            padding = _pair(padding)
    
            dilation = _pair(dilation)
    
            super(Conv2d, self).__init__(
    
                in_channels, out_channels, kernel_size, stride, padding, dilation,
    
                False, _pair(0), groups, bias, padding_mode)
    
    
    
        def conv2d_forward(self, input, weight):
    
            if self.padding_mode == 'circular':
    
                expanded_padding = ((self.padding[1] + 1) // 2, self.padding[1] // 2,
    
                                    (self.padding[0] + 1) // 2, self.padding[0] // 2)
    
                return F.conv2d(F.pad(input, expanded_padding, mode='circular'),
    
                                weight, self.bias, self.stride,
    
                                _pair(0), self.dilation, self.groups)
    
            return F.conv2d(input, weight, self.bias, self.stride,
    
                            self.padding, self.dilation, self.groups)
    
    
    
        def forward(self, input):
    
            return self.conv2d_forward(input, self.weight)
    

    如果有人想知道pytorch如何构建图和进行反向传播...

    看看这个...(请不要认真对待这段代码,因为我不确定这是否是pytorch的实现方式...但是可以带走其中的想法,它可能会帮助你理解pytorch的工作原理)

    一些愚蠢的代码 希望能对您有所帮助 :)

    PS,我是深度学习和pytorch的新手。这里可能会含有一些错误,请仔细阅读...


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接