剪枝PRUNING TUTORIAL
最新的深度学习技术依赖于难以部署的过度参数化模型。相反,已知生物神经网络使用有效的稀疏连通性。为了减少内存,电池和硬件消耗,同时又不牺牲精度,在设备上部署轻量级模型并通过私有设备上计算来确保私密性,确定通过减少模型中的参数数量来压缩模型的最佳技术很重要。在研究方面,剪枝用于研究参数过度配置和参数不足网络之间学习动态的差异,以研究幸运稀疏子网络和初始化(“lottery tickets”)作为破坏性神经体系结构搜索技术的作用,以及更多。
在本教程中,将学习如何用于torch.nn.utils.prune
稀疏神经网络,以及如何扩展它以实现自己的自定义剪枝技术。
要求Requirements
"torch>=1.4.0a0+8e8a5e0" import torch from torch import nn import torch.nn.utils.prune as prune import torch.nn.functional as F
Create a model
在本教程中,我们使用LeCun等人(1998年)的LeNet体系结构。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") class LeNet(nn.Module):def __init__(self):super(LeNet, self).__init__()# 1 input image channel, 6 output channels, 3x3 square conv kernelself.conv1 = nn.Conv2d(1, 6, 3)self.conv2 = nn.Conv2d(6, 16, 3)self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5x5 image dimensionself.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10) def forward(self, x):x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))x = F.max_pool2d(F.relu(self.conv2(x)), 2)x = x.view(-1, int(x.nelement() / x.shape[0]))x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return x model = LeNet().to(device=device)
检查模块Inspect a Module
让我们检查conv1
LeNet模型中的(未剪枝)层。现在它将包含两个参数weight
和bias
,并且没有缓冲区。
module = model.conv1 print(list(module.named_parameters()))
输出:
[('weight', Parameter containing: tensor([[[[-5.7063e-02, -2.4630e-01, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -1.9451e-01]]], [[[-2.3636e-01, -1.9035e-01, 6.9974e-02],[-7.7690e-02, 4.9759e-02, -6.2006e-02],[-1.7095e-01, 7.3741e-02, 3.1901e-01]]], [[[-1.6287e-02, -3.1315e-01, -2.6263e-01],[-1.1699e-01, -7.4603e-02, 9.0671e-03],[-1.0678e-01, 1.8641e-01, -2.0640e-01]]], [[[-2.2785e-01, 6.2033e-04, -7.2417e-02],[-2.5378e-01, -2.4691e-01, -3.4585e-03],[-5.4172e-02, 2.9494e-01, -1.7844e-01]]], [[[ 1.6865e-01, -2.9616e-01, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 1.6621e-01],[-2.3458e-01, 3.0958e-01, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -1.2893e-01],[-2.1332e-01, 3.0710e-01, 1.8194e-01]]]], device='cuda:0',requires_grad=True)), ('bias', Parameter containing: tensor([ 0.2454, 0.0883, -0.2114, -0.0138, 0.0932, -0.1112], device='cuda:0',requires_grad=True))]
print(list(module.named_buffers()))
[]
模块剪枝Pruning a Module
要剪枝模块(在此示例中,是conv1
LeNet体系结构的层),请首先从中可用的剪枝技术中选择剪枝技术torch.nn.utils.prune
(或 通过子类化implement 自己的剪枝技术 BasePruningMethod
)。然后,指定模块和该模块中要剪枝的参数的名称。最后,使用所选剪枝技术所需的适当关键字参数,指定剪枝参数。
在此示例中,我们将weight
在conv1
层中命名的参数中随机剪枝30%的连接。模块作为第一个参数传递给函数;name
使用其字符串标识符在该模块中标识参数;并 amount
指示与剪枝的连接百分比(如果它是介于0和1之间的浮点数),或者指示与剪枝的连接的绝对数量(如果它是一个非负整数)。
prune.random_unstructured(module, name="weight", amount=0.3)
剪枝通过weight
从参数中删除并将其替换为新参数weight_orig
(即追加"_orig"
到初始参数name
)来进行。weight_orig
存储未剪枝的张量版本。在bias
没有剪枝,因此将保持不变。
print(list(module.named_parameters()))
[('bias', Parameter containing: tensor([ 0.2454, 0.0883, -0.2114, -0.0138, 0.0932, -0.1112], device='cuda:0',requires_grad=True)), ('weight_orig', Parameter containing: tensor([[[[-5.7063e-02, -2.4630e-01, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -1.9451e-01]]], [[[-2.3636e-01, -1.9035e-01, 6.9974e-02],[-7.7690e-02, 4.9759e-02, -6.2006e-02],[-1.7095e-01, 7.3741e-02, 3.1901e-01]]], [[[-1.6287e-02, -3.1315e-01, -2.6263e-01],[-1.1699e-01, -7.4603e-02, 9.0671e-03],[-1.0678e-01, 1.8641e-01, -2.0640e-01]]], [[[-2.2785e-01, 6.2033e-04, -7.2417e-02],[-2.5378e-01, -2.4691e-01, -3.4585e-03],[-5.4172e-02, 2.9494e-01, -1.7844e-01]]], [[[ 1.6865e-01, -2.9616e-01, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 1.6621e-01],[-2.3458e-01, 3.0958e-01, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -1.2893e-01],[-2.1332e-01, 3.0710e-01, 1.8194e-01]]]], device='cuda:0',requires_grad=True))]
通过上面选择的剪枝技术生成的剪枝掩码被保存为名为weight_mask
(即附加"_mask"
到初始参数name
)的模块缓冲区。
print(list(module.named_buffers()))
[('weight_mask', tensor([[[[1., 0., 1.],[1., 1., 1.],[1., 1., 0.]]], [[[0., 1., 0.],[1., 1., 0.],[1., 1., 1.]]], [[[1., 0., 1.],[1., 0., 0.],[0., 1., 1.]]], [[[0., 1., 1.],[1., 1., 1.],[1., 0., 1.]]], [[[1., 0., 1.],[1., 1., 0.],[1., 0., 1.]]], [[[1., 1., 1.],[1., 1., 0.],[1., 1., 0.]]]], device='cuda:0'))]
为了使前向传递不做任何修改就可以使用,该weight
属性必须存在。在torch.nn.utils.prune
计算权重的剪枝版本中实现的剪枝技术 (通过将mask与原始参数组合)并将其存储在attribute中weight
。请注意,这不再是的参数,module
现在只是一个属性
print(module.weight)
输出:
tensor([[[[-5.7063e-02, -0.0000e+00, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -0.0000e+00]]], [[[-0.0000e+00, -1.9035e-01, 0.0000e+00],[-7.7690e-02, 4.9759e-02, -0.0000e+00],[-1.7095e-01, 7.3741e-02, 3.1901e-01]]], [[[-1.6287e-02, -0.0000e+00, -2.6263e-01],[-1.1699e-01, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 1.8641e-01, -2.0640e-01]]], [[[-0.0000e+00, 6.2033e-04, -7.2417e-02],[-2.5378e-01, -2.4691e-01, -3.4585e-03],[-5.4172e-02, 0.0000e+00, -1.7844e-01]]], [[[ 1.6865e-01, -0.0000e+00, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 0.0000e+00],[-2.3458e-01, 0.0000e+00, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -0.0000e+00],[-2.1332e-01, 3.0710e-01, 0.0000e+00]]]], device='cuda:0',grad_fn=<MulBackward0>)
最后,在每次前向传递之前使用PyTorch's进行剪枝 forward_pre_hooks
。具体来说,module
如我们在此处所做的那样,当剪枝时,它将forward_pre_hook
为与之关联的每个要剪枝的参数获取一个。在这种情况下,由于到目前为止我们只剪枝了名为的原始参数weight
,所以只会出现一个钩子。
print(module._forward_pre_hooks)
输出:
OrderedDict([(0, <torch.nn.utils.prune.RandomUnstructured object at 0x7f9cf8062208>)])
为了完整起见,我们现在也可以剪枝一下bias
,以查看module
更改的参数,缓冲区,挂钩和属性。只是为了尝试另一种剪枝技术,这里我们剪枝L1范数中的偏差中的3个最小条目,如在l1_unstructured
实现的那样 。
prune.l1_unstructured(module, name="bias", amount=3)
现在,我们希望命名的参数同时包含weight_orig
(来自之前)和bias_orig
。缓冲区将包括weight_mask
和 bias_mask
。两个张量的剪枝后的版本将作为模块属性存在,并且模块现在将具有两个forward_pre_hooks
。
print(list(module.named_parameters()))
输出:
[('weight_orig', Parameter containing: tensor([[[[-5.7063e-02, -2.4630e-01, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -1.9451e-01]]], [[[-2.3636e-01, -1.9035e-01, 6.9974e-02],[-7.7690e-02, 4.9759e-02, -6.2006e-02],[-1.7095e-01, 7.3741e-02, 3.1901e-01]]], [[[-1.6287e-02, -3.1315e-01, -2.6263e-01],[-1.1699e-01, -7.4603e-02, 9.0671e-03],[-1.0678e-01, 1.8641e-01, -2.0640e-01]]], [[[-2.2785e-01, 6.2033e-04, -7.2417e-02],[-2.5378e-01, -2.4691e-01, -3.4585e-03],[-5.4172e-02, 2.9494e-01, -1.7844e-01]]], [[[ 1.6865e-01, -2.9616e-01, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 1.6621e-01],[-2.3458e-01, 3.0958e-01, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -1.2893e-01],[-2.1332e-01, 3.0710e-01, 1.8194e-01]]]], device='cuda:0',requires_grad=True)), ('bias_orig', Parameter containing: tensor([ 0.2454, 0.0883, -0.2114, -0.0138, 0.0932, -0.1112], device='cuda:0',requires_grad=True))]
print(list(module.named_buffers()))
输出:
[('weight_mask', tensor([[[[1., 0., 1.],[1., 1., 1.],[1., 1., 0.]]], [[[0., 1., 0.],[1., 1., 0.],[1., 1., 1.]]], [[[1., 0., 1.],[1., 0., 0.],[0., 1., 1.]]], [[[0., 1., 1.],[1., 1., 1.],[1., 0., 1.]]], [[[1., 0., 1.],[1., 1., 0.],[1., 0., 1.]]], [[[1., 1., 1.],[1., 1., 0.],[1., 1., 0.]]]], device='cuda:0')), ('bias_mask', tensor([1., 0., 1., 0., 0., 1.], device='cuda:0'))]
print(module.bias)
输出:
tensor([ 0.2454, 0.0000, -0.2114, -0.0000, 0.0000, -0.1112], device='cuda:0',grad_fn=<MulBackward0>)
print(module._forward_pre_hooks)
输出:
OrderedDict([(0, <torch.nn.utils.prune.RandomUnstructured object at 0x7f9cf8062208>), (1, <torch.nn.utils.prune.L1Unstructured object at 0x7f9d5f893f60>)])
迭代剪枝Iterative Pruning
一个模块中的同一参数可以被多次剪枝,各种剪枝调用的效果等于串联应用的各种蒙版的组合。新的蒙版与旧蒙版的组合由PruningContainer
的compute_mask
方法处理 。
举例来说,假设我们现在要进一步剪枝module.weight
,这一次是基于张量的第0轴(第0轴对应于卷积层的输出通道,对于,具有维数6 conv1
)使用结构化剪枝。L2规范。可以使用ln_structured
带有n=2
和的功能来实现dim=0
prune.ln_structured(module, name="weight", amount=0.5, n=2, dim=0) # As we can verify, this will zero out all the connections corresponding to # 50% (3 out of 6) of the channels, while preserving the action of the # previous mask. print(module.weight)
输出:
tensor([[[[-5.7063e-02, -0.0000e+00, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -0.0000e+00]]], [[[-0.0000e+00, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00],[-0.0000e+00, 0.0000e+00, 0.0000e+00]]], [[[-0.0000e+00, -0.0000e+00, -0.0000e+00],[-0.0000e+00, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00]]], [[[-0.0000e+00, 0.0000e+00, -0.0000e+00],[-0.0000e+00, -0.0000e+00, -0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00]]], [[[ 1.6865e-01, -0.0000e+00, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 0.0000e+00],[-2.3458e-01, 0.0000e+00, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -0.0000e+00],[-2.1332e-01, 3.0710e-01, 0.0000e+00]]]], device='cuda:0',grad_fn=<MulBackward0>)
现在,对应的钩子将为类型 torch.nn.utils.prune.PruningContainer
,并将存储应用于该weight
参数的剪枝历史记录。
for hook in module._forward_pre_hooks.values():if hook._tensor_name == "weight": # select out the correct hookbreak print(list(hook)) # pruning history in the container
输出:
[<torch.nn.utils.prune.RandomUnstructured object at 0x7f9cf8062208>, <torch.nn.utils.prune.LnStructured object at 0x7f9cf8059390>]
序列化剪枝的模型Serializing a pruned model
所有相关的张量,包括掩码缓冲区和用于计算剪枝的张量的原始参数,都存储在模型中state_dict
,因此可以根据需要轻松地序列化和保存。
print(model.state_dict().keys())
输出:
odict_keys(['conv1.weight_orig', 'conv1.bias_orig', 'conv1.weight_mask', 'conv1.bias_mask', 'conv2.weight', 'conv2.bias', 'fc1.weight', 'fc1.bias', 'fc2.weight', 'fc2.bias', 'fc3.weight', 'fc3.bias'])
删除剪枝重新参数化Remove pruning re-parametrization
要使剪枝永久化,请根据weight_orig
和删除重新参数化weight_mask
,然后删除forward_pre_hook
,我们可以使用中的remove
功能torch.nn.utils.prune
。请注意,这不会撤消剪枝,好像从未发生过。而是通过将参数重新分配weight
给模型参数(剪枝后的版本)来使其永久化。
删除重新参数化之前:
print(list(module.named_parameters()))
输出:
[('weight_orig', Parameter containing: tensor([[[[-5.7063e-02, -2.4630e-01, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -1.9451e-01]]], [[[-2.3636e-01, -1.9035e-01, 6.9974e-02],[-7.7690e-02, 4.9759e-02, -6.2006e-02],[-1.7095e-01, 7.3741e-02, 3.1901e-01]]], [[[-1.6287e-02, -3.1315e-01, -2.6263e-01],[-1.1699e-01, -7.4603e-02, 9.0671e-03],[-1.0678e-01, 1.8641e-01, -2.0640e-01]]], [[[-2.2785e-01, 6.2033e-04, -7.2417e-02],[-2.5378e-01, -2.4691e-01, -3.4585e-03],[-5.4172e-02, 2.9494e-01, -1.7844e-01]]], [[[ 1.6865e-01, -2.9616e-01, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 1.6621e-01],[-2.3458e-01, 3.0958e-01, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -1.2893e-01],[-2.1332e-01, 3.0710e-01, 1.8194e-01]]]], device='cuda:0',requires_grad=True)), ('bias_orig', Parameter containing: tensor([ 0.2454, 0.0883, -0.2114, -0.0138, 0.0932, -0.1112], device='cuda:0',requires_grad=True))] print(list(module.named_buffers()))
输出:
[('weight_mask', tensor([[[[1., 0., 1.],[1., 1., 1.],[1., 1., 0.]]], [[[0., 0., 0.],[0., 0., 0.],[0., 0., 0.]]], [[[0., 0., 0.],[0., 0., 0.],[0., 0., 0.]]], [[[0., 0., 0.],[0., 0., 0.],[0., 0., 0.]]], [[[1., 0., 1.],[1., 1., 0.],[1., 0., 1.]]], [[[1., 1., 1.],[1., 1., 0.],[1., 1., 0.]]]], device='cuda:0')), ('bias_mask', tensor([1., 0., 1., 0., 0., 1.], device='cuda:0'))]
print(module.weight)
输出:
tensor([[[[-5.7063e-02, -0.0000e+00, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -0.0000e+00]]], [[[-0.0000e+00, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00],[-0.0000e+00, 0.0000e+00, 0.0000e+00]]], [[[-0.0000e+00, -0.0000e+00, -0.0000e+00],[-0.0000e+00, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00]]], [[[-0.0000e+00, 0.0000e+00, -0.0000e+00],[-0.0000e+00, -0.0000e+00, -0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00]]], [[[ 1.6865e-01, -0.0000e+00, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 0.0000e+00],[-2.3458e-01, 0.0000e+00, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -0.0000e+00],[-2.1332e-01, 3.0710e-01, 0.0000e+00]]]], device='cuda:0',grad_fn=<MulBackward0>)
删除重新参数化后:
prune.remove(module, 'weight') print(list(module.named_parameters()))
输出:
[('bias_orig', Parameter containing: tensor([ 0.2454, 0.0883, -0.2114, -0.0138, 0.0932, -0.1112], device='cuda:0',requires_grad=True)), ('weight', Parameter containing: tensor([[[[-5.7063e-02, -0.0000e+00, -3.3144e-01],[-2.9158e-01, -2.4055e-01, -3.3132e-01],[-2.8848e-01, -3.2727e-01, -0.0000e+00]]], [[[-0.0000e+00, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00],[-0.0000e+00, 0.0000e+00, 0.0000e+00]]], [[[-0.0000e+00, -0.0000e+00, -0.0000e+00],[-0.0000e+00, -0.0000e+00, 0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00]]], [[[-0.0000e+00, 0.0000e+00, -0.0000e+00],[-0.0000e+00, -0.0000e+00, -0.0000e+00],[-0.0000e+00, 0.0000e+00, -0.0000e+00]]], [[[ 1.6865e-01, -0.0000e+00, -2.2503e-01],[-8.8503e-02, -2.1696e-01, 0.0000e+00],[-2.3458e-01, 0.0000e+00, 2.4339e-01]]], [[[ 2.7446e-01, -3.2808e-02, 6.3390e-03],[ 1.4047e-04, -2.1429e-01, -0.0000e+00],[-2.1332e-01, 3.0710e-01, 0.0000e+00]]]], device='cuda:0',requires_grad=True))]
print(list(module.named_buffers()))
输出:
[('bias_mask', tensor([1., 0., 1., 0., 0., 1.], device='cuda:0'))]
剪枝模型中的多个参数Pruning multiple parameters in a model
通过指定所需的剪枝技术和参数,我们可以轻松地剪枝网络中的多个张量,也许根据它们的类型,如在本示例中将看到的那样。
new_model = LeNet() for name, module in new_model.named_modules():# prune 20% of connections in all 2D-conv layersif isinstance(module, torch.nn.Conv2d):prune.l1_unstructured(module, name='weight', amount=0.2)# prune 40% of connections in all linear layerselif isinstance(module, torch.nn.Linear):prune.l1_unstructured(module, name='weight', amount=0.4) print(dict(new_model.named_buffers()).keys()) # to verify that all masks exist
输出:
dict_keys(['conv1.weight_mask', 'conv2.weight_mask', 'fc1.weight_mask', 'fc2.weight_mask', 'fc3.weight_mask'])
全局剪枝Global pruning
到目前为止,我们仅查看了通常称为“局部”剪枝的情况,即通过比较每个条目的统计信息(权重,激活度,梯度等)来逐个剪枝模型中的张量的做法。到该张量中的其他条目。但是,一种常见且可能更强大的技术是通过删除(例如)删除整个模型中最低的20%的连接,而不是删除每一层中最低的20%的连接来一次剪枝模型。这很可能导致每个层的剪枝百分比不同。让我们看看如何使用global_unstructured
from 来做到这一点torch.nn.utils.prune
。
model = LeNet() parameters_to_prune = ((model.conv1, 'weight'),(model.conv2, 'weight'),(model.fc1, 'weight'),(model.fc2, 'weight'),(model.fc3, 'weight'), ) prune.global_unstructured(parameters_to_prune,pruning_method=prune.L1Unstructured,amount=0.2, )
现在,我们可以检查每个剪枝参数中引起的稀疏性,该稀疏性将不等于每层中的20%。但是,全局稀疏度将(大约)为20%。
print("Sparsity in conv1.weight: {:.2f}%".format(100. * float(torch.sum(model.conv1.weight == 0))/ float(model.conv1.weight.nelement())) ) print("Sparsity in conv2.weight: {:.2f}%".format(100. * float(torch.sum(model.conv2.weight == 0))/ float(model.conv2.weight.nelement())) ) print("Sparsity in fc1.weight: {:.2f}%".format(100. * float(torch.sum(model.fc1.weight == 0))/ float(model.fc1.weight.nelement())) ) print("Sparsity in fc2.weight: {:.2f}%".format(100. * float(torch.sum(model.fc2.weight == 0))/ float(model.fc2.weight.nelement())) ) print("Sparsity in fc3.weight: {:.2f}%".format(100. * float(torch.sum(model.fc3.weight == 0))/ float(model.fc3.weight.nelement())) ) print("Global sparsity: {:.2f}%".format(100. * float(torch.sum(model.conv1.weight == 0)+ torch.sum(model.conv2.weight == 0)+ torch.sum(model.fc1.weight == 0)+ torch.sum(model.fc2.weight == 0)+ torch.sum(model.fc3.weight == 0))/ float(model.conv1.weight.nelement()+ model.conv2.weight.nelement()+ model.fc1.weight.nelement()+ model.fc2.weight.nelement()+ model.fc3.weight.nelement())) )
输出:
Sparsity in conv1.weight: 5.56% Sparsity in conv2.weight: 7.75% Sparsity in fc1.weight: 22.05% Sparsity in fc2.weight: 12.17% Sparsity in fc3.weight: 10.12% Global sparsity: 20.00%
torch.nn.utils.prune
使用自定义剪枝功能扩展
要实现自己的剪枝功能,可以nn.utils.prune
通过对BasePruningMethod
基类进行子类化来扩展 模块,就像其他所有剪枝方法一样。该基类为您实现下面的方法:__call__
,apply_mask
, apply
,prune
,和remove
。除了一些特殊情况外,您不必为新的剪枝技术重新实现这些方法。但是,您将必须实现__init__
(构造函数)和compute_mask
(有关如何根据剪枝技术的逻辑为给定张量计算掩码的说明)。此外,你将不得不指定剪枝这一技术工具的类型(支持的选项包括global
, structured
,和unstructured
)。需要确定在迭代应用剪枝的情况下如何组合蒙版。换句话说,当剪枝预剪枝的参数时,当前的剪枝技术应作用于参数的未剪枝部分。指定PRUNING_TYPE
将会启用PruningContainer
(处理剪枝掩码的迭代应用程序)正确识别要剪枝的参数的范围。
例如,假设您要实现一种剪枝技术,该技术可以剪枝张量中的所有其他条目(或者-如果以前已剪枝过张量,则可以剪枝张量的其余未剪枝部分)。这是PRUNING_TYPE='unstructured'
因为它作用于层中的各个连接,而不作用于整个单元/通道('structured'
)或不同参数('global'
)。
class FooBarPruningMethod(prune.BasePruningMethod):"""Prune every other entry in a tensor"""PRUNING_TYPE = 'unstructured' def compute_mask(self, t, default_mask):mask = default_mask.clone()mask.view(-1)[::2] = 0return mask
现在,要将其应用到中的参数nn.Module
,还应该提供一个简单的函数来实例化该方法并将其应用。
def foobar_unstructured(module, name):"""Prunes tensor corresponding to parameter called `name` in `module`by removing every other entry in the tensors.Modifies module in place (and also return the modified module)by:1) adding a named buffer called `name+'_mask'` corresponding to thebinary mask applied to the parameter `name` by the pruning method.The parameter `name` is replaced by its pruned version, while theoriginal (unpruned) parameter is stored in a new parameter named`name+'_orig'`. Args:module (nn.Module): module containing the tensor to prunename (string): parameter name within `module` on which pruningwill act. Returns:module (nn.Module): modified (i.e. pruned) version of the inputmodule Examples:>>> m = nn.Linear(3, 4)>>> foobar_unstructured(m, name='bias')"""FooBarPruningMethod.apply(module, name)return module
让我们尝试一下!
model = LeNet() foobar_unstructured(model.fc3, name='bias') print(model.fc3.bias_mask)
输出:
tensor([0., 1., 0., 1., 0., 1., 0., 1., 0., 1.])
接下来,给大家介绍一下租用GPU做实验的方法,我们是在智星云租用的GPU,使用体验很好。具体大家可以参考:智星云官网: http://www.ai-galaxy.cn/,淘宝店:https://shop36573300.taobao.com/公众号: 智星AI
剪枝PRUNING TUTORIAL相关推荐
- 《AI系统周刊》第4期:DNN模型压缩之剪枝(Pruning)
No.04 智源社区 AI系统组 A I 系 统 研究 观点 资源 活动 关于周刊 AI系统是当前人工智能领域极具现实意义与前瞻性的研究热点之一,为了帮助研究与工程人员了解这一领域的进展和资讯,我们 ...
- 减少模型参数---模型剪枝(Pruning Deep Neural Networks)
简介 模型剪枝就是根据神经元的贡献程度对网络中的神经元进行排名,可以从网络中移除排名较低的神经元,从而形成一个更小.更快的网络模型. 基本思想示意图: 模型剪枝根据神经元权重的L1/L2范数来进行排序 ...
- 内存中有两个4字节以压缩的bcd_卷积神经网络的压缩与加速 -- 剪枝(PRUNING)论文(二)...
Learning Efficient Convolutional Networks through Network Slimming Abstract: 讲道理 它这个摘要写得好霸气..太猛了 这个方 ...
- 【6s965-fall2022】剪枝✂pruningⅠ
模型剪枝的介绍 修剪,消除不必要的知识.DNN的知识可以理解为存在于其权重中. 事实证明,许多 DNN 模型可以被分解为权重张量,而权重张量经常包含统计冗余(稀疏性).因此,你可以压缩 DNN 的权重 ...
- pytorch distiller filter channel剪枝
Home - Neural Network DistillerDistiller Documentation by Intel AIhttps://intellabs.github.io/distil ...
- 决策树准确率低原因_机器学习决策树算法--剪枝算法
一.剪枝算法决策树生成算法递归地产生决策树,直到不能继续下去为止.这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没有那么准确,即出现过拟合现象.过拟合的原因在于学习时过多地考虑如何 ...
- 剪枝计算机,α-β剪枝 - 电脑黑白棋 - 黑白棋天地
α-β剪枝算法 前面介绍的基本搜索算法,在实际应用是是十分费时的,因为它需要考虑所有可能的棋步.有研究表明,在黑白棋的中盘阶段,平均每个局面大约有10步棋可供选择[1].如果程序前瞻10步(搜索深度为 ...
- 决策树及决策树生成与剪枝
文章目录 1. 决策树学习 2. 最优划分属性的选择 2.1 信息增益 - ID3 2.1.1 什么是信息增益 2.1.2 ID3 树中最优划分属性计算举例 2.2 信息增益率 - C4.5 2.3 ...
- 机器学习算法 04 —— 决策树(ID3、C4.5、CART,剪枝,特征提取,回归决策树)
文章目录 系列文章 决策树 1 决策树算法简介 2 决策树分类的原理 2.1 信息熵 2.2 决策树划分依据-信息增益(ID3) 2.3 决策树划分依据-信息增益率(C4.5) 2.4 决策树划分依据 ...
最新文章
- php怎么把文字改成黑色,微信如何调成黑色模式?
- java io--内存操作流_JavaIO——内存操作流、打印流
- 判断一个窗口是否有焦点_判断一个项目是否值得加盟的基本方法
- 图解MongoDB的连接与使用,通俗易懂
- 【nyist】6 喷水装置(一) (简单的贪心)
- 把偷快递的贼炸到怀疑人生!不愧是NASA工程师,奇思妙想
- 类名作为方法和形参的返回值
- python软件下载3版本-Python3.9下载
- Linux访问交换机FTP,华为交换机使用FTP查看下载文件
- 高效使用Tigergraph和Docker
- Bom及Bom对象的详细介绍
- 约束最优化方法之最优性条件
- 微波雷达感应模块,智能马桶传感方案,智能化生活
- 恢复Windows10的经典照片查看器
- 我的世界右边显示什么服务器,我的世界MC的服务器是什么意思
- 本地安全管理审核UDF 第2版 - lsasecur.au3
- 第七章 人工智能,7.2 颠覆传统的电商智能助理-阿里小蜜技术揭秘(作者:海青)...
- MySql 笔记(五)InnoDB引擎页分裂与页合并的原理
- 生死看淡,不服就GAN(六)----用DCGAN生成马的彩色图片
- YOYO软件使用指南