Pytorch中计算自己模型的FLOPs | thop.profile() 方法 | yolov5s 网络模型参数量、计算量统计

1-0

享受学术探讨的欢乐,传递温暖,希望能够帮助到刚刚入门的同学

  • 🍖 Pytorch中计算自己模型的FLOPs | yolov5s 网络模型参数量、计算量统计


📙 FLOPS 基础概念理解


参考链接: 这部分内容 精选于 Z 乎 问答,感谢各位大佬

  • FLOPS:注意全大写,是floating point operations per second的缩写,意指每秒浮点运算次数,理解为计算速度。是一个衡量硬件性能的指标。
  • FLOPs:注意s小写,是floating point operations的缩写(s表复数),意指浮点运算数,理解为计算量。可以用来衡量算法/模型的复杂度。

描述一个深度学习框架/模型,除了精确度,通常用正向推理 计算量和参数个数(#Parameters)来描述复杂度

1-0
1-1

深度学习框架 FLOPs 的组成

  1. Conv1d/2d/3d (including grouping)
  2. ConvTranspose1d/2d/3d (including grouping)
  3. BatchNorm1d/2d/3d, GroupNorm, InstanceNorm1d/2d/3d
  4. Activations (ReLU, PReLU, ELU, ReLU6, LeakyReLU)
  5. Linear
  6. Upsample
  7. Poolings (AvgPool1d/2d/3d, MaxPool1d/2d/3d and adaptive ones)

其中,Conv所占的比重通常最大
和预处理之后网络的输入图像大小有关系
而 #Parameters和图像大小无关

关于网络模型 参数 和 计算量 统计,当前有如下两个大佬的作品可供使用


本博文简单解析,和试用,第二个代码


📔 pytorch-OpCounter GitHub 主页:


https://github.com/Lyken17/pytorch-OpCounter


🟧 How to install
pip install thop
🟨 How to use
  • Basic usage
from torchvision.models import resnet50
from thop import profile
model = resnet50()
input = torch.randn(1, 3, 224, 224) 
macs, params = profile(model, inputs=(input, ))
  • Define the rule for 3rd party module.
class YourModule(nn.Module):
    # your definition
def count_your_model(model, x, y):
    # your rule here

input = torch.randn(1, 3, 224, 224)
macs, params = profile(model, inputs=(input, ), 
                        custom_ops={YourModule: count_your_model})
  • Improve the output readability

设置参数的输出格式

from thop import clever_format
macs, params = clever_format([macs, params], "%.3f")

📕 运行该项目评估代码


python benchmark/evaluate_famous_models.py 

运行输出如下

ModelParams(M)FLOPs(G)
alexnet61.100.71
densenet1217.982.87
densenet16128.687.79
densenet16914.153.40
densenet20120.014.34
googlenet6.621.50
inception_v323.835.73
mobilenet_v23.500.31
resnet10144.557.83
resnet15260.1911.56
resnet1811.691.82
resnet3421.803.67
resnet5025.564.11
resnext101_32x8d88.7916.48
resnext50_32x4d25.034.26
shufflenet_v2_x0_51.370.04
shufflenet_v2_x1_02.280.15
shufflenet_v2_x1_53.500.30
shufflenet_v2_x2_07.390.59
squeezenet1_01.250.82
squeezenet1_11.240.35
vgg11132.867.62
vgg11_bn132.877.63
vgg13133.0511.32
vgg13_bn133.0511.35
vgg16138.3615.48
vgg16_bn138.3715.51
vgg19143.6719.65
vgg19_bn143.6819.68

python benchmark/evaluate_rnn_models.py 

运行输出如下

ModelParams(M)FLOPs(G)
RNNCell0.350.01
GRUCell1.040.03
LSTMCell1.380.04
RNN0.351.11
GRU1.043.32
LSTM1.384.43
stacked-RNN1.926.15
stacked-GRU5.7618.49
stacked-LSTM7.6824.64
BiRNN0.692.21
BiGRU2.076.65
BiLSTM2.768.86
stacked-BiRNN5.4117.34
stacked-BiGRU16.2452.07
stacked-BiLSTM21.6669.42

📗 自己实测的一个代码(该代码无法直接运行)


使用 from thop import profile 方法进行统计

input = torch.randn([1, self.img_ch, self.img_size, self.img_size]).to(self.device)
print('input size:')
print(input.size())

print(input)

macs, params = profile(self.disA, inputs=(input,))
name = 'disA'
print("%s | %s | %s" % ("Model", "Params(M)", "FLOPs(G)"))
print("---|---|---")
print("%s | %.2f | %.2f" % (name, params / (1000 ** 2), macs / (1000 ** 3)))


real_A_ae = self.disA(input)
macs, params = profile(self.gen2B, inputs=(real_A_ae,))

print()
name = 'gen2B'
print("%s | %s | %s" % ("Model", "Params(M)", "FLOPs(G)"))
print("---|---|---")
print("%s | %.2f | %.2f" % (name, params / (1000 ** 2), macs / (1000 ** 3)))

ModelParams(M)FLOPs(G)
disA0.170.73
ModelParams(M)FLOPs(G)
gen2B8.1033.78

这里属个人笔记,和本部分内容无关

  • 这个示例只是说明,对于 精简网络层之后的 NiceGAN测试阶段 判别器的层(共用作为编码层) 参数量极少;
  • 主要还是生成器在运算;

📘 yolov5s 网络模型参数量、计算量统计


🟧 代码修改

对 YOLOv5 有兴趣,可简单查阅我的这篇博文


YOLOv5 环境搭建 | coco128 训练示例 |❤️ 详细记录❤️ |【YOLOv5】


本部分,基础步骤如下

5-0

推理输出如下,可以发现 yolov5 本身yolov5-5.0/utils/torch_utils.py 下 model_info 方法已对 参数和计算量进行统计,对比可以发现

  • parameters 数值一致
  • 计算量 GFLOPS 存在差异 【未深入探究,需要对比 model_info 方法实现】
  • GFLOPS 数值和 送入模型 input 的 shape 正相关
  • 这里的 yolov5s 模型支持动态尺寸【预处理之后,input 宽高为 N 的整数倍数】图像处理
  • 均使用 from thop import profile 下方法进行的统计
python detect.py --source data/images/bus.jpg 

Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', nosave=False, project='runs/detect', save_conf=False, save_txt=False, source='data/images/bus.jpg', update=False, view_img=False, weights='yolov5s.pt')
YOLOv5 🚀 2021-4-12 torch 1.8.1+cu111 CUDA:0 (Quadro RTX 5000, 16125.3125MB)


Fusing layers... 
# 代码本身统计 ,欢迎补充
Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0 GFLOPS



# 自定义方法,参数量、计算量统计输出
 
Model | Params(M) | FLOPs(G)
---|---|---
yolov5s | 7.26697300 | 6.37915530
640x480 4 persons, 1 bus, 1 fire hydrant, Done. (0.040s)
Results saved to runs/detect/exp11
Done. (0.162s)

相关代码如下

import torch
from thop import profile

# ...

"""add moli yolov5s 参数量、计算量统计  start """
print("yolov5s 参数量、计算量统计 \n")
total_ops, total_params = profile(model, inputs=(img,))
name = "yolov5s"

print("%s | %s | %s" % ("Model", "Params(M)", "FLOPs(G)"))
print("---|---|---")
print("%s | %.8f | %.8f" % (name, total_params / (1000 ** 2), total_ops / (1000 ** 3)))
"""add moli yolov5s 参数量、计算量统计  End """

再或者,相关代码添加到 34行 # Load model 之后的位置,效果和上面方式一样

5-1

🟨 相关报错统计
  • 可能遇到的报错一
RuntimeError: Input type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) should be the same

解决方法是,input 类型需要指定 device , input = torch.randn(1, 3, 640, 480).to(device)

  • 可能遇到的报错二
RuntimeError: Sizes of tensors must match except in dimension 3. Got 15 and 16 (The offending index is 0)

原因是: input = torch.randn(1, 3, 640, 120).to(device) input 的 宽高无法被 N 【 盲猜 N = 32,欢迎修正 】 整除

9-8


📙 博主 AI 领域八大干货专栏、诚不我欺



📙 预祝各位 前途似锦、可摘星辰


  • 🎉 作为全网 AI 领域 干货最多的博主之一,❤️ 不负光阴不负卿 ❤️
  • ❤️ 过去的每一天、想必你也都有努力、祝你披荆斩棘、未来可期

9-9

  • 45
    点赞
  • 275
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论
可以使用下面的代码计算PyTorch模型FLOPs(浮点操作次数): ```python import torch from torch.autograd import Variable def print_model_parm_flops(model, input_size, custom_layers): multiply_adds = 1 params = 0 flops = 0 input = Variable(torch.rand(1, *input_size)) def register_hook(module): def hook(module, input, output): class_name = str(module.__class__).split(".")[-1].split("'")[0] if class_name == 'Conv2d': out_h, out_w = output.size()[2:] kernel_h, kernel_w = module.kernel_size in_channels = module.in_channels out_channels = module.out_channels if isinstance(module.padding, int): pad_h = pad_w = module.padding else: pad_h, pad_w = module.padding if isinstance(module.stride, int): stride_h = stride_w = module.stride else: stride_h, stride_w = module.stride params += out_channels * (in_channels // module.groups) * kernel_h * kernel_w flops += out_channels * (in_channels // module.groups) * kernel_h * kernel_w * out_h * out_w / (stride_h * stride_w) elif class_name == 'Linear': weight_flops = module.weight.nelement() * input[0].nelement() // module.weight.size(1) bias_flops = module.bias.nelement() flops = weight_flops + bias_flops params = weight_flops + bias_flops elif class_name in custom_layers: custom_flops, custom_params = custom_layers[class_name](module, input, output) flops += custom_flops params += custom_params else: print(f"Warning: module {class_name} not implemented") if not isinstance(module, torch.nn.Sequential) and \ not isinstance(module, torch.nn.ModuleList) and \ not (module == model): hooks.append(module.register_forward_hook(hook)) # loop through the model architecture and register hooks for each layer hooks = [] model.apply(register_hook) # run the input through the model model(input) # remove the hooks for hook in hooks: hook.remove() print(f"Number of parameters: {params}") print(f"Number of FLOPs: {flops}") return flops, params ``` 调用这个函数需要传入模型、输入大小和一个自定义图层字典,其字典的键是自定义层的名称,值是一个函数,该函数接受模块,输入和输出,返回FLOPs数量。例如,如果您的模型包含一个名为MyLayer的自定义层,则可以将以下内容添加到字典: ```python def my_layer_impl(module, input, output): # compute FLOPs and params for MyLayer flops = ... params = ... return flops, params custom_layers = {'MyLayer': my_layer_impl} ``` 使用示例: ```python import torchvision.models as models model = models.resnet18() input_size = (3, 224, 224) custom_layers = {} flops, params = print_model_parm_flops(model, input_size, custom_layers) ``` 该函数将打印出模型数量FLOPs
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨理学AI

不必打赏,关注博主公众号即可

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值