转载自:
深度学习模型参数量/计算量和推理速度计算 - 知乎
深度学习模型参数量/计算量和推理速度计算
参考:神经网络层的FLOPs计算_食指上的簸箕的博客-CSDN博客_神经网络flops计算
注意全是大写的,是floating point operations per second的缩写。意思是:每秒浮点运算次数。可以理解为计算速度 。是一个衡量硬件性能的指标。
FLOPS的计算公式:
对卷积层:(K_h * K_w * C_in * C_out) * (H_out * W_out)
对全连接层:C_in * C_out
注:
1)k_h表示卷积核的高,k_w表示卷积核的宽,c_in表示输入通道数目,c_out表示输出通道数目;H_out表示卷积后的输出图的高,W_out表示卷积后的输出图的宽。
注意s是小写的,是floating point operations的缩写(s表示复数),意思是指浮点运算量,理解为计算量。可以用来衡量算法/模型的复杂度。
ps:FLOPs是模型推理时间的一个参考量,但并不能百分之百表示该模型推理时间的长短,因为乘法和加法计算不一样,乘法的时间一般是加法时间的四倍,但现在有很多优化卷积层的计算算法,可以把乘法计算时间缩为加法的两倍不等,所以FLOPs只是个估量的指标,不是决定推理时间长短的指标。即FLOPs越小并不代表着模型推理时间越短。
定义在下面这篇论文中有,但是所描述的计算公式跟博客所搜的不一样,我结合大佬的博客和自己的理解重新整理一下:
自己理解整理:CNN 模型所需的计算力(flops)和参数(parameters)数量是怎么计算的?
卷积层:(2 * C_in * K_h * K_w -1)* H_out * W_out * C_out
-- K_h是卷积核的高度;K_w是卷积核的宽度;C_in是输入的通道数;C_out是输出的通道数,
H_out*W_out是输出特征图大小,不考虑bias时有-1,有bias时没有-1
(2 * C_in * K_h * K_w -1) = (C_in * K_h * K_w)+ (C_in * K_h * K_w -1),
第一项是乘法运算数,第二项是加法运算数,因为n个数相加,要加n-1次,所以不考虑bias,会有一个-1,
如果考虑bias刚好抵消掉,整体公式变为(2 * C_in * K_h * K_w )* H_out * W_out * C_out
全连接层:(2 * I - 1)* O
-- I=输入神经元节点数; O=输出神经元节点数,不考虑bias时有-1,有bias时没有-1
(2 * I - 1)= I + (I - 1), 考虑bais,整体公式为(2 * I )* O
在计算机视觉论文中,常常将一个'乘-加'组合视为一次浮点运算,英文表述为'Multi-Add',运算量正好是上面的算法减半,此时的运算量(不考虑bias)为:
是指模型训练中需要训练的参数总数
模型参数量计算公式为:
对卷积层:(K_h * K_w * C_in + 1)* C_out
--其中,K_h是卷积核的高度;K_w是卷积核的宽度; C_in是输入的通道数; C_out是输出的通道数, +1是考虑bais
比如:10*10*64的输入,卷积核为3*3*64,那么这个卷积操作的参数总量为:(3*3*64)* 64
对全连接层:(C_in + 1) * C_out
--C_in表示输入的节点个数;C_out表示输出的节点个数, +1是考虑bais
另一篇文章也有计算卷积操作的参数量。
所以 :
注意:
1. Params只与你定义的网络结构有关,和forward的任何操作无关。即定义好了网络结构,则参数就已经确定了
2. FLOPs与不同层的运算结构有关。如果forward时在同一层(同一名字命名的层)多次运算,FLOPs不会增加。
3. Model_Size = 4 * params 模型大小约为参数量的4倍
补充:
MAC:内存访问成本
注意:
1)FLOPs与FLOPS的区别。FLOPS 的全称是floating point of per second,它的意思是指每秒浮点运算次数。用来衡量硬件的性能。FLOPs 的全称是floating point of operations,它的意思是指浮点运算次数,可以用来衡量算法/模型复杂度。
2)计算量与参数量不是一回事啊,别搞混了!
(转载)CNN 模型所需的计算力(FLOPs)和参数(parameters)数量计算 - 三年一梦 - 博客园
pytorch计算模型FLOPs和Params - 知乎
方法1:使用thop库
'''
code by zzg-2020-05-19
pip install thop
'''
import torch
from thop import profile
from models.yolo_nano import YOLONano
device = torch.device("cpu")
#input_shape of model,batch_size=1
net = YOLONano(num_classes=20, image_size=416) ##定义好的网络模型
input = torch.randn(1, 3, 416, 416)
flops, params = profile(net, inputs=(input, ))
print("FLOPs=", str(flops/1e9) +'{}'.format("G"))
print("params=", str(params/1e6)+'{}'.format("M")
方法2-使用torchstat库
'''
在PyTorch中,可以使用torchstat这个库来查看网络模型的一些信息,包括总的参数量params、MAdd、显卡内存占用量和FLOPs等
pip install torchstat
'''
from torchstat import stat
from torchvision.models import resnet50
model = resnet50()
stat(model, (3, 224, 224))
方法3-使用ptflops
#pip install ptflops
from ptflops import get_model_complexity_info
from torchvision.models import resnet50
model = resnet50()
flops, params = get_model_complexity_info(model, (3, 224, 224), as_strings=True, print_per_layer_stat=True)
print('Flops: ' + flops)
print('Params: ' + params)
方法4-使用fvcore
使用fvcore计算Pytorch中模型的参数数量以及FLOPs_太阳花的小绿豆的博客-CSDN博客_fvcore库
'''
pip install fvcore
'''
import torch
from torchvision.models import resnet50
from fvcore.nn import FlopCountAnalysis, parameter_count_table
# 创建resnet50网络
model = resnet50(num_classes=1000)
# 创建输入网络的tensor
tensor = (torch.rand(1, 3, 224, 224),)
# 分析FLOPs
flops = FlopCountAnalysis(model, tensor)
print("FLOPs: ", flops.total())
# 分析parameters
print(parameter_count_table(model))
参考:
https://towardsdatascience.com/the-correct-way-to-measure-inference-time-of-deep-neural-networks-304a54e5187f
需要克服GPU异步执行和GPU预热两个问题。下面例子使用Efficient-net-b0,在进行任何时间测量之前,我们通过网络运行一些虚拟示例来进行“预热”。这将自动初始化GPU并防止它在我们测量时间时进入省电模式。接下来,我们使用torch.cuda.event来测量GPU上的时间。在这里使用torch.cuda.synchronize()至关重要。这行代码执行主机和设备(CPU和GPU)之间的同步,因此只有在GPU上运行的进程完成后才会进行时间记录。这克服了不同步执行的问题。
model = EfficientNet.from_pretrained(‘efficientnet-b0’)
device = torch.device(“cuda”)
model.to(device)
dummy_input = torch.randn(1, 3, 224, 224,dtype=torch.float).to(device)
starter, ender = torch.cuda.Event(enable_timing=True), torch.cuda.Event(enable_timing=True)
repetitions = 300
timings=np.zeros((repetitions,1))
#GPU-WARM-UP
for _ in range(10):
_ = model(dummy_input)
# MEASURE PERFORMANCE
with torch.no_grad():
for rep in range(repetitions):
starter.record()
_ = model(dummy_input)
ender.record()
# WAIT FOR GPU SYNC
torch.cuda.synchronize()
curr_time = starter.elapsed_time(ender)
timings[rep] = curr_time
mean_syn = np.sum(timings) / repetitions
std_syn = np.std(timings)
mean_fps = 1000. / mean_syn
print(' * Mean@1 {mean_syn:.3f}ms Std@5 {std_syn:.3f}ms FPS@1 {mean_fps:.2f}'.format(mean_syn=mean_syn, std_syn=std_syn, mean_fps=mean_fps))
print(mean_syn)
神经网络的吞吐量定义为网络在单位时间内(例如,一秒)可以处理的最大输入实例数。与涉及单个实例处理的延迟不同,为了实现最大吞吐量,我们希望并行处理尽可能多的实例。有效的并行性显然依赖于数据、模型和设备。
因此,为了正确测量吞吐量,我们执行以下两个步骤:(1)我们估计允许最大并行度的最佳批量大小;(2)给定这个最佳批量大小,我们测量网络在一秒钟内可以处理的实例数。
要找到最佳批量大小,一个好的经验法则是达到 GPU 对给定数据类型的内存限制。这个大小当然取决于硬件类型和网络的大小。找到这个最大批量大小的最快方法是执行二进制搜索。当时间不重要时,简单的顺序搜索就足够了。为此,我们使用 for 循环将批量大小增加 1,直到达到运行时错误为止,这确定了 GPU 可以处理的最大批量大小,用于我们的神经网络模型及其处理的输入数据。
在找到最佳批量大小后,我们计算实际吞吐量。为此,我们希望处理多个批次(100 个批次就足够了),然后使用以下公式:
(批次数 X 批次大小)/(以秒为单位的总时间)
这个公式给出了我们的网络可以在一秒钟内处理的示例数量。下面的代码提供了一种执行上述计算的简单方法(给定最佳批量大小)
model = EfficientNet.from_pretrained(‘efficientnet-b0’)
device = torch.device(“cuda”)
model.to(device)
dummy_input = torch.randn(optimal_batch_size, 3,224,224, dtype=torch.float).to(device)
repetitions=100
total_time = 0
with torch.no_grad():
for rep in range(repetitions):
starter, ender = torch.cuda.Event(enable_timing=True),torch.cuda.Event(enable_timing=True)
starter.record()
_ = model(dummy_input)
ender.record()
torch.cuda.synchronize()
curr_time = starter.elapsed_time(ender)/1000
total_time += curr_time
Throughput = (repetitions*optimal_batch_size)/total_time
print(‘Final Throughput:’,Throughput)