我试图用torch.nn.utils.prune
在PyTorch中修剪我的模型,它提供了2个Tensor,
1.一个是原始重量
1.另一个是包含0和1的掩码,用于帮助我们关闭网络中的某些连接。
我尝试了这两种解决方案,但都没有提高推理速度:
1.使用修剪后的网络来推断哪一个将首先关闭与掩码的一些连接,然后运行推断。
1.使用掩码将原始权重清零,然后从state_dict中移除掩码以进行推断。
- 有没有办法提高模型Tensor和掩码的速度?与一个非零的浮点数0相乘是否会比两个浮点数相乘更快?***
下面是我的修剪函数和修剪速度计算过程:
def prune_net(net):
"""Prune 20% net's weights that have abs(value) approx. 0
Function that will be use when an iteration is reach
Args:
Return:
newnet (nn.Module): a newnet contain mask that help prune network's weight
"""
if not isinstance(net,nn.Module):
print('Invalid input. Must be nn.Module')
return
newnet = copy.copy(net)
modules_list = []
for name, module in newnet.named_modules():
if isinstance(module, torch.nn.Conv2d):
modules_list += [(module,'weight'),(module,'bias')]
if isinstance(module, torch.nn.Linear):
modules_list += [(module,'weight'),(module,'bias')]
prune.global_unstructured(
modules_list,
pruning_method=prune.L1Unstructured,
amount=0.2,)
return newnet
字符串
测试推理速度第1种情况:
import torch
from torch import nn
import torch.nn.utils.prune as prune
import torch.nn.functional as F
import time
from torch.autograd import Variable
torch.set_default_tensor_type('torch.cuda.FloatTensor')
old_net = init_your_net()
new_net = prune_net(old_net)
new_net = prune_net(new_net)
old_net.eval()
new_net.eval()
old_net = old_net.cuda()
new_net = new_net.cuda()
dataset = load_your_dataset()
for i in range(100):
x = dataset[i]
x = x.cuda()
y = x.cuda()
#new infer
start_time = time.perf_counter()
detections = new_net(x).data
time_new += time.perf_counter() - start_time
#old infer
start_time = time.perf_counter()
detections = old_net(y).data
time_old += time.perf_counter() - start_time
print('old ',time_old)
print('new ', time_new)
型
测试推理速度第二种情况:
import torch
from torch import nn
import torch.nn.utils.prune as prune
import torch.nn.functional as F
import time
from torch.autograd import Variable
torch.set_default_tensor_type('torch.cuda.FloatTensor')
old_net = init_your_net()
new_net = prune_net(old_net)
new_net = prune_net(new_net)
# Apply mask to model tensor and remove mask from state_dict
for name, module in new_net.named_modules():
if isinstance(module, torch.nn.Conv2d):
prune.remove(module,'weight')
prune.remove(module,'bias')
if isinstance(module, torch.nn.Linear):
prune.remove(module,'weight')
prune.remove(module,'bias')
old_net.eval()
new_net.eval()
old_net = old_net.cuda()
new_net = new_net.cuda()
dataset = load_your_dataset()
for i in range(100):
x = dataset[i]
x = x.cuda()
y = x.cuda()
#new infer
start_time = time.perf_counter()
detections = new_net(x).data
time_new += time.perf_counter() - start_time
#old infer
start_time = time.perf_counter()
detections = old_net(y).data
time_old += time.perf_counter() - start_time
print('old ',time_old)
print('new ', time_new)
型
最新
我发现torch有一个稀疏模块,如果我们修剪足够的参数,可以减少内存使用,但它还不支持nn.Module,只有Tensor对象。以下是一些有用的链接:
https://github.com/pytorch/pytorch/issues/36214#issuecomment-619586452
https://pytorch.org/docs/stable/sparse.html
3条答案
按热度按时间mbyulnm01#
理解 * 非结构化修剪 * 和 * 结构化修剪 * 之间的区别很重要。
***结构化修剪:**通过删除整个Tensor的行/列来减少权重Tensor的维度。这转化为移除所有传入和传出连接(在密集层中)或整个卷积滤波器(在卷积层中)的神经元。
***非结构化修剪:**单个权重可以被“移除”(归零),而不受最终Tensor形状的约束。这转化为移除神经元之间的个体连接(在密集层中)或移除卷积滤波器的个体权重(在卷积层中)。请注意,生成的权重Tensor可以是稀疏的,但保持其原始形状。
目前,
torch.nn.utils.prune
只支持非结构化修剪,这几乎无助于降低推理成本,因为GPU没有针对稀疏矩阵乘法进行优化。虽然您可能希望减少权重Tensor的维度以减少浮点运算的数量,但非结构化修剪会产生具有许多零的权重Tensor,但不会自动减少此类Tensor的大小。非结构化修剪只有在删除 * 大量 * 权重时才有助于提高性能。在这种情况下,您可以依赖PyTorch sparse operations,也可以尝试查找包含全零的行/列,从而可以将其删除。
相反,如果你想研究结构化修剪,你可以看看TorchPruner,这是我自己开发的一个用于研究目的的库,它提供了一些实用程序来找到最不重要的神经元并相应地切片权重Tensor。
vsaztqbk2#
我也在尝试修剪以提高推理速度。但我发现更有用的是使用ONNX和ONNXRuntime。以下是所有步骤的链接:
https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html
它将减少高达85%的时间,而不损失精度。
7eumitmz3#
通过将权重设置为0.0来修剪权重只是故事的一半。另一半是将它们从模型中移除(物理上),以便它们不参与任何计算。这在非结构化修剪中是不可能的,但是在结构化修剪中,如果你能够从你的conv操作中删除特定的输出通道(以及下一个conv的输入通道),那么你可以在你的模型中得到一些加速。
要物理删除通道,一旦确定要删除的通道,您需要调整Conv2d模块上的可学习权重参数。您可能还需要更改它之后的几个层,特别是批范数层和下一个conv或线性层。