每日一學:如何計算cnn模型的計算量

整理自知乎問題: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 次,也就是變成

對於整個公式來說就是分兩步計算:

  1. 括號內是計算得到輸出特徵圖的一個像素的數值;
  2. 括號外則是乘以整個輸出特徵圖的大小,拓展到整個特徵圖。

舉個例子,如下圖所示是一個輸出特徵圖的計算,其中輸入特徵圖是 55 ,卷積核是 3 * 3,輸出的特徵圖大小也是 3 * 3,所以這裡對應公式中的參數,就是 K=3, H=W=3, 假設輸入和輸出通道數量都是 1,那麼下圖得到右邊的特徵圖的一個像素的數值的計算量就是 (33)次乘法+(3 * 3-1)次加法 = 17,然後得到整個輸出特徵圖的計算量就是 17 * 9 = 153.

每日一學:如何計算cnn模型的計算量

此處輸入圖片的描述

全連接層的 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>


分享到:


相關文章: