Mar*_*oma 8 python flops tensorflow
我想计算每层LeNet-5(纸张)需要多少翻牌.一些论文给触发器其他架构共(1,2,3)但是,这些文件没有就如何计算FLOPS的数量细节,我不知道有多少触发器的非线性激活功能是必要的.例如,计算需要多少FLOP tanh(x)
?
我想这将是实现,也可能是硬件特定的.但是,我主要感兴趣的是获得一个数量级.我们在谈论10个FLOP吗?100 FLOP?1000 FLOP?因此,选择您想要的任何架构/实现来获得答案.(虽然我很欣赏接近"常见"设置的答案,例如Intel i5/nvidia GPU/Tensorflow)
注意:这个答案不是特定于python的,但我认为tanh之类的东西在语言上根本不同.
Tanh通常通过定义上限和下限来实现,其中分别返回1和-1.中间部分用不同的函数近似如下:
Interval 0 x_small x_medium x_large
tanh(x) | x | polynomial approx. | 1-(2/(1+exp(2x))) | 1
Run Code Online (Sandbox Code Playgroud)
存在精确到多个精确浮点的多项式,并且还存在双精度.该算法称为Cody-Waite算法.
引用这个描述(你可以在那里找到关于数学的更多信息,例如如何确定x_medium),Cody和Waite的理性形式需要四次乘法,三次加法,一次单精度除法,七次乘法,六次加法,以及双精度的一个师.
对于负x,您可以计算| x | 并翻转标志.因此,您需要比较x所在的区间,并评估相应的近似值.这总共:
现在,这是1993年的一份报告,但我认为这里没有太大的改变.
如果我们看一下glibc实现tanh(x)
,我们看到:
x
值大于22.0和双精度的值,tanh(x)
可以安全地假设为1.0,因此几乎没有成本.x
(假设x<2^(-55)
)另一种廉价的近似是可能的:tanh(x)=x(1+x)
因此只需要两个浮点运算.tanh(x)=(1-exp(-2x))/(1+exp(-2x))
.然而,一个必须是准确的,因为1-exp(t)
由于重要性的损失而对于小的t值是非常有问题的,因此人们使用expm(x)=exp(x)-1
并计算tanh(x)=-expm1(-2x)/(expm1(-2x)+2)
.所以基本上,最坏的情况大约是所需操作次数的2倍expm1
,这是一个相当复杂的功能.最好的方法可能只是测量计算tanh(x)
所需的时间与两个双精度乘法所需的时间相比较.
我在英特尔处理器上的(草率)实验产生了以下结果,这给出了一个粗略的想法:
因此,对于非常小且数量> 22的几乎没有成本,对于0.1
我们支付6 FLOPS的数字,然后成本上升到大约20 FLOPS每个tanh
计算.
关键点:计算成本tanh(x)
取决于参数x
,最大成本介于10到100 FLOP之间.
有称为Intel的指令F2XM1
,其计算2^x-1
为-1.0<x<1.0
,其可用于计算tanh
,至少对于一些范围.但是,如果要相信agner的表格,这项操作的成本约为60 FLOP.
另一个问题是矢量化 - 就我所见,正常的glibc实现没有矢量化.因此,如果您的程序使用向量化并且必须使用非向量化tanh
实现,则会进一步减慢程序的速度.为此,intel编译器具有mkl库,该库可以在其他库中进行矢量化tanh
.
正如您在表中所看到的,每次操作的最大成本约为10个时钟(浮点运算的成本约为1个时钟).
我猜你有可能通过使用-ffast-math
编译器选项赢得一些FLOP ,这会导致程序更快但更不精确(这是Cuda或c/c ++的一个选项,不确定是否可以为python/numpy完成).
产生图形数据的c ++代码(用g ++ -std = c ++ 11 -O2编译).它的目的不是给出确切的数字,而是关于成本的第一印象:
#include <chrono>
#include <iostream>
#include <vector>
#include <math.h>
int main(){
const std::vector<double> starts={1e-30, 1e-18, 1e-16, 1e-10, 1e-5, 1e-2, 1e-1, 0.5, 0.7, 0.9, 1.0, 2.0, 10, 20, 23, 100,1e3, 1e4};
const double FACTOR=1.0+1e-11;
const size_t ITER=100000000;
//warm-up:
double res=1.0;
for(size_t i=0;i<4*ITER;i++){
res*=FACTOR;
}
//overhead:
auto begin = std::chrono::high_resolution_clock::now();
for(size_t i=0;i<ITER;i++){
res*=FACTOR;
}
auto end = std::chrono::high_resolution_clock::now();
auto overhead=std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count();
//std::cout<<"overhead: "<<overhead<<"\n";
//experiments:
for(auto start : starts){
begin=std::chrono::high_resolution_clock::now();
for(size_t i=0;i<ITER;i++){
res*=tanh(start);
start*=FACTOR;
}
auto end = std::chrono::high_resolution_clock::now();
auto time_needed=std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count();
std::cout<<start<<" "<<time_needed/overhead<<"\n";
}
//overhead check:
begin = std::chrono::high_resolution_clock::now();
for(size_t i=0;i<ITER;i++){
res*=FACTOR;
}
end = std::chrono::high_resolution_clock::now();
auto overhead_new=std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count();
std::cerr<<"overhead check: "<<overhead/overhead_new<<"\n";
std::cerr<<res;//don't optimize anything out...
}
Run Code Online (Sandbox Code Playgroud)