添加 (3)(5) nn.Sequential。怎么运行的?
Add(3)(5) nn.Sequential. How it works?
class Add(nn.Module):
def __init__(self, value):
super().__init__()
self.value = value
def forward(self, x):
return x + self.value
calculator = nn.Sequential(
Add(3),
Add(2),
Add(5),
)
x = torch.tensor([1])
output = calculator(x)
print(output) # tensor([11])
我添加了模型。但我无法理解 'nn.Sequential' 是如何工作的。
第一次这么理解
add = Add(torch.tensor([1]))
add(3) # tensor([4])
add = Add(add(3))
add(2) # tensor([6])
add = Add(add(2))
add(5) # tensor([11])
但 Add(3)(1) 也有效。
我不明白为什么 'Add(3)(1)' 有效。请帮帮我
要记住的核心是,当您 实例化 一个 Module
class 时,您正在创建一个可调用对象,即可以表现得像一个函数。
用通俗易懂的英语一步一步:
- 当你写类似
add5 = Add(5)
的东西时,你所做的是将 PyTorch 模型 Add
的“实例”分配给 add5
- 更具体地说,您将
5
传递给 Add
class 的 __init__
方法,因此其 value
属性设置为 5
.
- PyTorch
Module
s 是“可调用的”,这意味着您可以像调用函数一样调用它们。使用 Module
实例时调用的函数是该实例的 forward
方法。所以具体来说,对于我们的 add5
对象,如果我们传递一个值,x = 10
,通过写类似 add5(10)
的东西,就像我们 运行 x + add5.value
,它等于10 + 5 = 15
.
现在将它们放在一起,我们应该将 Sequential
用于构建没有 b运行ching 结构的神经网络模型的界面视为 sequentially 调用每个实例化的 Module
s' forward
方法。
省略 Add
的定义,只关注 calculator
作为一系列计算,我们有以下内容(我添加了注释以向您展示在每个步骤中应该考虑的内容)
calculator = nn.Sequential(
# given some input tensor x...
Add(3), # run: x = x + self.value with self.value = 3
Add(2), # run: x = x + self.value with self.value = 2
Add(5), # run: x = x + self.value with self.value = 5
)
现在我们可以看到,如果我们传递值 1
(尽管包装为 PyTorch Tensor
),我们只是在做 1 + 3 + 2 + 5
,这当然是合理的等于 11
。 PyTorch return 将值作为 Tensor
对象返回给我们。
x = torch.tensor([1])
output = calculator(x)
print(output) # tensor([11])
最后,Add(3)(5)
* 的工作原理完全相同!使用 Add(3)
我们得到 Add
class 的一个实例,要添加的值为 3。然后我们立即使用它,语法有点不直观 Add(3)(5)
到 return 值 3 + 5 = 8
.
*我认为您想要大写的 class 名称,而不是 class
的实例
你没看错,Sequential
class简单来说就是一个一个调用提供的模块。这是转发方法的代码
def forward(self, input):
for module in self:
input = module(input)
return input
此处,for module in self
只是遍历构造函数中提供的模块(Sequential.__iter__
负责它的方法)。
当您使用 ()
语法调用它时,顺序模块会调用它。
calculator = nn.Sequential(
Add(3),
Add(2),
Add(5),
)
output = calculator(torch.tensor([1]))
但是它是如何工作的呢?在 python
中,如果将 __call__
方法添加到 class 定义中,则可以创建 class callable 的对象。如果 class 没有明确包含这个方法,这意味着它可能是从一个 superclass 继承的。在 Add
和 Sequential
的情况下,Module
class 实现了 __call__
方法。 __call__
方法调用用户定义的'public'forward
方法。
python 对对象实例化和函数或方法调用使用相同的语法,这可能会造成混淆。为了使 reader 可见差异,python
使用命名约定。 类 应该以 CamelCase 命名,首字母大写,对象以 snake_case 命名(这不是强制性的,但最好遵循此规则)。
就像你的例子一样,Add
是一个 class 而 add
是这个 class 的一个 callable 对象:
add = Add(torch.tensor([1]))
因此,您可以调用 add
,就像您在示例中调用计算器一样。
>>> add = Add(torch.tensor([1]))
>>> add(2)
Out: tensor([3])
但这行不通:
>>> add = Add(torch.tensor([1]))
>>> add(2)(1)
Out:
----> 3 add(2)(1)
TypeError: 'Tensor' object is not callable
这意味着 add(2)
returns 一个没有实现 __call__
方法的 Tensor
对象。
将此代码与
进行比较
>>> Add(torch.tensor([1]))(2)
Out:
tensor([3])
此代码与第一个示例相同,但重新排列了一点。
--
为了避免混淆,我通常用不同的方式命名对象:比如 add_obj = Add(1)
。它帮助我突出差异。
如果您不确定自己在使用什么,请使用函数 type
和 isinstance
。他们会帮助找出发生了什么事。
例如,如果您检查 add
对象,您会发现它是一个可调用对象(即它实现了 __call__
)
>>> from typing import Callable
>>> isinstance(add, Callable)
True
对于张量:
>>> from typing import Callable
>>> isinstance(add, torch.tensor(1))
False
因此,如果你调用它,它会上升TypeError: 'Tensor' object is not callable
。
如果您想了解 python 双底方法如 init 或 call 的工作原理,您可以阅读描述 python data model
的页面
(可能有点乏味,所以你可能更愿意阅读 Fluent Python 或其他书籍)
class Add(nn.Module):
def __init__(self, value):
super().__init__()
self.value = value
def forward(self, x):
return x + self.value
calculator = nn.Sequential(
Add(3),
Add(2),
Add(5),
)
x = torch.tensor([1])
output = calculator(x)
print(output) # tensor([11])
我添加了模型。但我无法理解 'nn.Sequential' 是如何工作的。
第一次这么理解
add = Add(torch.tensor([1]))
add(3) # tensor([4])
add = Add(add(3))
add(2) # tensor([6])
add = Add(add(2))
add(5) # tensor([11])
但 Add(3)(1) 也有效。 我不明白为什么 'Add(3)(1)' 有效。请帮帮我
要记住的核心是,当您 实例化 一个 Module
class 时,您正在创建一个可调用对象,即可以表现得像一个函数。
用通俗易懂的英语一步一步:
- 当你写类似
add5 = Add(5)
的东西时,你所做的是将 PyTorch 模型Add
的“实例”分配给add5
- 更具体地说,您将
5
传递给Add
class 的__init__
方法,因此其value
属性设置为5
. - PyTorch
Module
s 是“可调用的”,这意味着您可以像调用函数一样调用它们。使用Module
实例时调用的函数是该实例的forward
方法。所以具体来说,对于我们的add5
对象,如果我们传递一个值,x = 10
,通过写类似add5(10)
的东西,就像我们 运行x + add5.value
,它等于10 + 5 = 15
.
现在将它们放在一起,我们应该将 Sequential
用于构建没有 b运行ching 结构的神经网络模型的界面视为 sequentially 调用每个实例化的 Module
s' forward
方法。
省略 Add
的定义,只关注 calculator
作为一系列计算,我们有以下内容(我添加了注释以向您展示在每个步骤中应该考虑的内容)
calculator = nn.Sequential(
# given some input tensor x...
Add(3), # run: x = x + self.value with self.value = 3
Add(2), # run: x = x + self.value with self.value = 2
Add(5), # run: x = x + self.value with self.value = 5
)
现在我们可以看到,如果我们传递值 1
(尽管包装为 PyTorch Tensor
),我们只是在做 1 + 3 + 2 + 5
,这当然是合理的等于 11
。 PyTorch return 将值作为 Tensor
对象返回给我们。
x = torch.tensor([1])
output = calculator(x)
print(output) # tensor([11])
最后,Add(3)(5)
* 的工作原理完全相同!使用 Add(3)
我们得到 Add
class 的一个实例,要添加的值为 3。然后我们立即使用它,语法有点不直观 Add(3)(5)
到 return 值 3 + 5 = 8
.
*我认为您想要大写的 class 名称,而不是 class
的实例你没看错,Sequential
class简单来说就是一个一个调用提供的模块。这是转发方法的代码
def forward(self, input):
for module in self:
input = module(input)
return input
此处,for module in self
只是遍历构造函数中提供的模块(Sequential.__iter__
负责它的方法)。
当您使用 ()
语法调用它时,顺序模块会调用它。
calculator = nn.Sequential(
Add(3),
Add(2),
Add(5),
)
output = calculator(torch.tensor([1]))
但是它是如何工作的呢?在 python
中,如果将 __call__
方法添加到 class 定义中,则可以创建 class callable 的对象。如果 class 没有明确包含这个方法,这意味着它可能是从一个 superclass 继承的。在 Add
和 Sequential
的情况下,Module
class 实现了 __call__
方法。 __call__
方法调用用户定义的'public'forward
方法。
python 对对象实例化和函数或方法调用使用相同的语法,这可能会造成混淆。为了使 reader 可见差异,python
使用命名约定。 类 应该以 CamelCase 命名,首字母大写,对象以 snake_case 命名(这不是强制性的,但最好遵循此规则)。
就像你的例子一样,Add
是一个 class 而 add
是这个 class 的一个 callable 对象:
add = Add(torch.tensor([1]))
因此,您可以调用 add
,就像您在示例中调用计算器一样。
>>> add = Add(torch.tensor([1]))
>>> add(2)
Out: tensor([3])
但这行不通:
>>> add = Add(torch.tensor([1]))
>>> add(2)(1)
Out:
----> 3 add(2)(1)
TypeError: 'Tensor' object is not callable
这意味着 add(2)
returns 一个没有实现 __call__
方法的 Tensor
对象。
将此代码与
进行比较>>> Add(torch.tensor([1]))(2)
Out:
tensor([3])
此代码与第一个示例相同,但重新排列了一点。
--
为了避免混淆,我通常用不同的方式命名对象:比如 add_obj = Add(1)
。它帮助我突出差异。
如果您不确定自己在使用什么,请使用函数 type
和 isinstance
。他们会帮助找出发生了什么事。
例如,如果您检查 add
对象,您会发现它是一个可调用对象(即它实现了 __call__
)
>>> from typing import Callable
>>> isinstance(add, Callable)
True
对于张量:
>>> from typing import Callable
>>> isinstance(add, torch.tensor(1))
False
因此,如果你调用它,它会上升TypeError: 'Tensor' object is not callable
。
如果您想了解 python 双底方法如 init 或 call 的工作原理,您可以阅读描述 python data model
的页面(可能有点乏味,所以你可能更愿意阅读 Fluent Python 或其他书籍)