@深度神经网络推理时间的正确测量方法( 二 )


当我们测量网络的延迟时 , 我们的目标是只测量网络的前馈 。 通常 , 即使是专家 , 在测量时也会犯一些常见的错误 。
1.在主机和设备之间传输数据 。 这篇文章的观点是仅测量神经网络的推理时间 。 从这个角度来看 , 最常见的错误之一是在进行时间测量时 , CPU和GPU之间存在数据传输 。 这通常是在CPU上创建一个张量 , 然后在GPU上执行推断 , 这种内存分配需要相当长的时间 , 从而增加了推理的时间 。 这个错误对测量的均值和方差的影响如下所示:
@深度神经网络推理时间的正确测量方法
本文插图
左:均值和标准差的正确测量值 。 右:在每次调用网络时 , 在CPU和GPU之间传输输入张量时的平均值和标准差 。 X轴是计时方法 , Y轴是时间(以毫秒为单位) 。
2.不使用GPU warm-up 。 如上所述 , 在GPU上首次运行会??提示其初始化 。 GPU初始化可能需要3秒钟的时间 , 如果以毫秒为单位 , 则差异很大 。
3.使用标准CPU计时 。 最常见的错误是测量时间而没有同步 。 即使是经验丰富的程序员也会使用下面这段代码 。
s = time.time() _ = model(dummy_input) curr_time = (time.time()-s )*1000当然 , 这完全忽略了前面提到的异步执行 , 因此输出不正确的时间 。 这个错误对测量的均值和方差的影响如下所示:
@深度神经网络推理时间的正确测量方法
本文插图
左:均值和标准差的正确测量值 。 右:不同步过程时的平均值和标准差 。 X轴是计时方法 , Y轴是时间(以毫秒为单位) 。
4. 一个样本 。 与计算机科学中的许多过程一样 , 神经网络的前馈具有(小的)随机成分 。 运行时间的差异可能很大 , 尤其是在测量低延迟网络时 。 为此 , 必须在几个样本上运行网络 , 然后对结果求平均值(300个样本可能是比较好) 。
测量吞吐量
神经网络的吞吐量被定义为网络在单位时间内(如一秒)所能处理的输入实例的最大数量 。 与延迟(涉及处理单个实例)不同 , 为了实现最大的吞吐量 , 我们希望并行处理尽可能多的实例 。 有效的并行性明显依赖于数据、模型和设备 。 因此 , 为了正确地度量吞吐量 , 我们执行以下两个步骤:
(1)估计允许最大并行度的最优batch size;
(2)给定最优batch size , 我们测量网络一秒钟内可以处理的实例数量 。
为了找到最佳的batch size , 一个好的经验法则是达到给定数据类型的GPU内存限制 。 当然 , 此大小取决于硬件类型和网络的大小 。 找到此最大batch size的最快方法是执行二分查找 。 当时间无关紧要时 , 简单的顺序搜索就足够了 , 使用for循环将batch size增加1 , 直到达到运行时错误为止 , 这表明GPU可以处理的最大batch size 。
找到最佳batch size后 , 然后计算实际吞吐量 。 使用以下公式:
(batches数量X batch size)/(总时间(以秒为单位)) 。
该公式给出了我们的网络在一秒钟内可以处理的实例数 。 下面的Python代码提供了执行上述计算的简单方法(给定了最佳batch size):
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)结论