pytorch Prunning模型并没有提高推理速度或减少模型大小

6xfqseft  于 2023-08-05  发布在  其他
关注(0)|答案(3)|浏览(165)

我试图用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

mbyulnm0

mbyulnm01#

理解 * 非结构化修剪 * 和 * 结构化修剪 * 之间的区别很重要。

***结构化修剪:**通过删除整个Tensor的行/列来减少权重Tensor的维度。这转化为移除所有传入和传出连接(在密集层中)或整个卷积滤波器(在卷积层中)的神经元。
***非结构化修剪:**单个权重可以被“移除”(归零),而不受最终Tensor形状的约束。这转化为移除神经元之间的个体连接(在密集层中)或移除卷积滤波器的个体权重(在卷积层中)。请注意,生成的权重Tensor可以是稀疏的,但保持其原始形状。

目前,torch.nn.utils.prune只支持非结构化修剪,这几乎无助于降低推理成本,因为GPU没有针对稀疏矩阵乘法进行优化。虽然您可能希望减少权重Tensor的维度以减少浮点运算的数量,但非结构化修剪会产生具有许多零的权重Tensor,但不会自动减少此类Tensor的大小。
非结构化修剪只有在删除 * 大量 * 权重时才有助于提高性能。在这种情况下,您可以依赖PyTorch sparse operations,也可以尝试查找包含全零的行/列,从而可以将其删除。
相反,如果你想研究结构化修剪,你可以看看TorchPruner,这是我自己开发的一个用于研究目的的库,它提供了一些实用程序来找到最不重要的神经元并相应地切片权重Tensor。

vsaztqbk

vsaztqbk2#

我也在尝试修剪以提高推理速度。但我发现更有用的是使用ONNX和ONNXRuntime。以下是所有步骤的链接:
https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html
它将减少高达85%的时间,而不损失精度。

7eumitmz

7eumitmz3#

通过将权重设置为0.0来修剪权重只是故事的一半。另一半是将它们从模型中移除(物理上),以便它们不参与任何计算。这在非结构化修剪中是不可能的,但是在结构化修剪中,如果你能够从你的conv操作中删除特定的输出通道(以及下一个conv的输入通道),那么你可以在你的模型中得到一些加速。
要物理删除通道,一旦确定要删除的通道,您需要调整Conv2d模块上的可学习权重参数。您可能还需要更改它之后的几个层,特别是批范数层和下一个conv或线性层。

相关问题