Module
和model
是同一个意思吗?当官方文档说:
时,似乎是这样。或者,当他们提到如果您需要更复杂的模型而不仅仅是现有的简单序列,则需要定义您的模型(作为自定义
Module
子类)。
Module
时,是不是指像协议/接口类型这样更正式和计算机科学的东西?这是一个简单的容器。
来自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()
或注册钩子。
这是 API 设计的选择,我认为只有一个 Module
类而不是两个分开的 Model
和 Layers
更加清晰,并允许更大的自由度(更容易将模型的一部分发送到 GPU,仅获取某些层的参数等)。
在不成为PyTorch专家的情况下,我理解中的模块是指一个容器,它接收张量作为输入并计算输出张量。
因此,总结起来,您的模型很可能由多个模块组成,例如,您可能有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
会发生什么以及返回什么。这个返回值将具有我们需要的输出维度。根据我们预测输出的精确度,我们的准确性越差或越好,这通常是我们跟踪进展的指标。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(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的新手。这里可能会含有一些错误,请仔细阅读...