整理自知乎問題:CNN 模型所需的計算力(flops)和參數(parameters)數量是怎麼計算的?
鏈接:https://www.zhihu.com/question/65305385
首先是給出兩個定義:
- FLOPS:全大寫,是floating point operations per second的縮寫,意指每秒浮點運算次數,理解為計算速度。是一個衡量硬件性能的指標。
- FLOPs:注意 s 小寫,是floating point operations的縮寫(s表複數),意指浮點運算數,理解為計算量。可以用來衡量算法/模型的複雜度。
這裡說的自然是第二種 FLOPs,計算量,也就是模型的複雜度。
卷積層的 FLOPs
不考慮激活函數,對於單個輸入特徵圖的計算公式為(沒有考慮 batch ):
這裡每個參數的含義: 是輸入通道數量, K 表示卷積核的大小,H 和 W 是輸出特徵圖(feature map)的大小, 是輸出通道。
因為是乘法和加法,所以括號內是 2 ,表示兩次運算操作。另外,不考慮 bias 的時候,有-1,而考慮 bias 的時候是沒有 -1。
對於括號內的理解是這樣的:
,第一項是乘法的運算數量,第二項是加法運算數量,因為 n 個數相加,是執行 n-1 次的加法次數,如果考慮 bias,就剛好是 n 次,也就是變成
對於整個公式來說就是分兩步計算:
- 括號內是計算得到輸出特徵圖的一個像素的數值;
- 括號外則是乘以整個輸出特徵圖的大小,拓展到整個特徵圖。
舉個例子,如下圖所示是一個輸出特徵圖的計算,其中輸入特徵圖是 55 ,卷積核是 3 * 3,輸出的特徵圖大小也是 3 * 3,所以這裡對應公式中的參數,就是 K=3, H=W=3, 假設輸入和輸出通道數量都是 1,那麼下圖得到右邊的特徵圖的一個像素的數值的計算量就是 (33)次乘法+(3 * 3-1)次加法 = 17,然後得到整個輸出特徵圖的計算量就是 17 * 9 = 153.
此處輸入圖片的描述
全連接層的 FLOPs
計算公式為:
(
每個參數的含義:I 是輸入數量,O 是輸出數量。
同樣 2 也是表示乘法和加法,然後不考慮 bias 是 -1,考慮的時候沒有 -1。
對於這個公式也是和卷積層的一樣,括號內考慮一個輸出神經元的計算量,然後擴展到所有的輸出神經元。
相關實現代碼庫
GitHub 上有幾個實現計算模型的 FLOPs 的庫:
- https://github.com/Lyken17/pytorch-OpCounter
- https://github.com/sagartesla/flops-cnn
- https://github.com/sovrasov/flops-counter.pytorch
非常簡單的代碼實現例子,來自 https://github.com/sagartesla/flops-cnn/blob/master/flops_calculation.py
<code>input_shape = (3 ,300 ,300) # Format:(channels, rows,cols)
conv_filter = (64 ,3 ,3 ,3) # Format: (num_filters, channels, rows, cols)
stride = 1
padding = 1
activation = 'relu'
if conv_filter[1] == 0:
n = conv_filter[2] * conv_filter[3] # vector_length
else:
n = conv_filter[1] * conv_filter[2] * conv_filter[3] # vector_length
flops_per_instance = n + ( n -1) # general defination for number of flops (n: multiplications and n-1: additions)
num_instances_per_filter = (( input_shape[1] - conv_filter[2] + 2 * padding) / stride) + 1 # for rows
num_instances_per_filter *= ((input_shape[1] - conv_filter[2] + 2 * padding) / stride) + 1 # multiplying with cols
flops_per_filter = num_instances_per_filter * flops_per_instance
total_flops_per_layer = flops_per_filter * conv_filter[0] # multiply with number of filters
if activation == 'relu':
# Here one can add number of flops required
# Relu takes 1 comparison and 1 multiplication
# Assuming for Relu: number of flops equal to length of input vector
total_flops_per_layer += conv_filter[0] * input_shape[1] * input_shape[2]
if total_flops_per_layer / 1e9 > 1: # for Giga Flops
print(total_flops_per_layer/ 1e9 ,'{}'.format('GFlops'))
else:
print(total_flops_per_layer / 1e6 ,'{}'.format('MFlops'))/<code>
閱讀更多 算法猿的成長 的文章