在PyTorch中,.module属性通常用于访问DataParallel包装的模型的原始模型。当你使用DataParallel包装模型时,原始模型会被包装在module属性下。这是为了处理多GPU训练时的并行计算。
如果你没有使用DataParallel包装模型,你应该可以直接通过模型对象访问模块,而不需要使用.module属性。只有在使用DataParallel包装模型时,原始模型会被放在.module下。
所以,如果你在模型上使用了DataParallel,那么你应该使用.module来访问原始模型的属性和方法。如果没有使用DataParallel,你可以直接通过模型对象访问属性和方法,而不需要.module
1、module:
_modules
可以直接获取特定名称的子模块对象,但无法直接获取名称以及遍历所有子模块,所以需要递归,是一个字典,所以其好处就是可以直接用键值索引,坏处是无法直接迭代需要用items()
,其中键是子模块的名称,值是对应的子模块对象。
_moudules.items()
是迭代字典的基本用法,返回可迭代对象,基操如下:
for key, value in my_dict.items():
print(key, value)
如果是:
for key in my_dict:
print(key)
则迭代一个字典的键。
所以看下面这段代码:
for name, module in model._modules.items():
if hasattr(module, "_modules"):
.....
name: layer1 (key)
module: Sequential() (value)
2、children
children()
方法返回一个迭代器,用于遍历模型的直接子模块。它只返回子模块的对象,不包含子模块的名称。
model.named_children()
返回一个迭代器,该迭代器依次生成由子模块名称和子模块对象组成的元组。
每个元组的结构是 (name, child),其中 name 是子模块的名称,child 是子模块对象本身。
for name, child in model.named_children():
这和上面的效果是一样的。
而且:model._modules[name]
和child
是一样的
3、modules
def modules(self) -> Iterator[‘Module’]:
Returns an iterator over all modules in the network.
modules(): 返回一个迭代器,提供模型及其所有子模块(包括子模块的子模块,这样就不用递归了)
named_modules(): 返回一个迭代器,提供模型及其所有子模块的名称和对象
官方实例:
>>> l = nn.Linear(2, 2)
>>> net = nn.Sequential(l, l)
>>> for idx, m in enumerate(net.modules()):
... print(idx, '->', m)
0 -> Sequential(
(0): Linear(in_features=2, out_features=2, bias=True)
(1): Linear(in_features=2, out_features=2, bias=True)
)
1 -> Linear(in_features=2, out_features=2, bias=True)
几个点:
1、当调用net.modules()时,它会返回模型中的所有模块,包括子模块
2、net.modules()返回的是模型及其子模块的生成器,而不是按照层级结构的生成器。由于这两个nn.Linear层是同一个对象,所以它们在模型中共享同一个索引编号。
4、practice
懂了上面之后我们可以写一个hook
函数
Version1:用直接子模块迭代器
def add_IF_hook(model):
for name, child in model.named_children():
if isinstance(child, ScaledNeuron):
child.register_forward_hook(save_spikes_number)
else:
add_IF_hook(child) # 需要递归children对应的是直接子模块
Version2:用属性_modules,items()转成迭代器
def add_IF_hook(model):
for name, module in model._modules.items():
if isinstance(module, ScaledNeuron):
model._modules[name],register_forward_hook(save_spikes_number)
#或者 module.register_forward_hook(save_spikes_number)
else:
add_IF_hook(module) #需要递归
上面两个输出的形式是这样的:pipeline是先从layer1—sequential下去,然后到sequential里面找到ScaledNeuron:layer1.2
===> name: 2 child: ScaledNeuron(
(neuron): IFNode(
v_threshold=1.0, v_reset=None, detach_reset=False
(surrogate_function): Sigmoid(alpha=1.0, spiking=True)
)
)
Version3:用全部子模块,最简形式:
def add_IF_hook(model):
for name, module in model.named_modules():
if isinstance(module, ScaledNeuron):
print(f"===> name: {name} child: {module}")
module.register_forward_hook(save_spikes_number)
最后输出的形式是这样的:
===> name: layer1.2 child: ScaledNeuron(
(neuron): IFNode(
v_threshold=1.0, v_reset=None, detach_reset=False
(surrogate_function): Sigmoid(alpha=1.0, spiking=True)
)
)
===> name: layer1.6 child: ScaledNeuron(
(neuron): IFNode(
v_threshold=1.0, v_reset=None, detach_reset=False
(surrogate_function): Sigmoid(alpha=1.0, spiking=True)
)
)
5、
spikes = {'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}
# 使用enumerate(spikes.keys())遍历字典的键
for i, key in enumerate(spikes.keys()):
print(f"Index: {i}, Key: {key}")
# 使用spikes.items()遍历字典的键和值
for key, value in spikes.items():
print(f"Key: {key}, Value: {value}")
6、
.module 属性来访问原始的模型
如果没有parallel你访问module就会
'SNNVGG' object has no attribute 'module'
没有parallel直接访问即可啊
snn_model = torch.nn.DataParallel(snn_model).cuda()
layers_fin_fout = snn_model.module.calculate_fin_fout()