1.Layout Optimizer
Tensorflow有幾種圖優化的方法,其中一種較為重要的是layout optimizer,核心思想是調整tensor的layout(NHWC to NCHW),原因在於在較早的cudnn版本中的API是不支持NHWC格式的輸入的,目前cudnn7.0版本已經能支持NHWC格式輸入了,但經過實測發現以NHWC格式為輸入調用cudnn API的速度是不如NCHW的(在k80上實測,lenet-5的兩個卷積層,使用NHWC計算需要平均約2.5ms,使用NCHW只需要1.8ms),所以Tensorflow layout的作用就是將NHWC轉換為NCHW,從而加速網絡的計算速度。
在使用layout轉換的時候,Tensorflow會監測網絡的結構,如果發現存在調用cudnn計算的節點,就會在節點前加一個transpose,將tensor轉換為NCHW格式,然后經過所有由cudnn計算的節點及計算方式與layout無關的節點(比如relu的計算就與layout無關,但是reduce_mean就與layout有關)之后,再加一個transpose將tensor轉換回來。以resnet_v2為例:https://github.com/tensorpack/tensorpack/blob/master/examples/ResNet/cifar10-resnet.py,resnet的中間網絡都是Conv + BN + Relu的組合,因此像這種網絡Tensorflow就會在第一個Conv節點前加一個transpose節點,,在GlobalAvgPooling前再加一個transpose節點(因為GlobalAvgPooling調用的是reduce_mean(input, [1,2])),用過timeline分析節點運算時間的應該注意到過這兩個transpose節點。這樣就用兩個transpose的時間換取更快的cudnn的調用時間,大幅縮減網絡的執行時間。
但是假設你的網絡中調用cudnn的節點不連續,就會造成加多個transpose的情況。雖然也能起到加速效果,但是肯定不如只加兩個transpose更合理,這樣在設計網路的時候才能設計出運行更快的網絡.
2.Conv Autotune
cudnn的conv forward API:https://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html#cudnnConvolutionForward,其中有一個algo參數,含義是計算卷積所用的算法,這個參數一共可以傳8種不同的算法,具體參考:https://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html#cudnnConvolutionFwdAlgo_t,在真正計算卷積的時候是不能確定哪種算法速度是最快的,因此Tensorflow啟用了一種緩存機制,就是對於每種尺寸的Tensor和conv參數(strides, padding啥的),8個算法依次調用。哪次時間短就將其記錄下來,下次繼續用(其實cudnn有相關的API可以直接調用的,Tensorflow1.13版本,貌似還是自己實現的調用+計時功能),相當於對於每種不同的尺寸的輸入都要算8次卷積,所以如果模型是變尺寸的模型,那么這8次卷積帶來的代價就是嚴重拖慢模型的速度。那么是否可以關閉這個緩存功能呢?Tensorflow的環境變量TF_CUDNN_USE_AUTOTUNE可以控制是否打開卷積的autotune功能。那么如果關閉了這個autotune,是否能找到運算最快的卷積算法呢,答案是不能.關閉autotune功能。就只能調用cudnnGetConvolutionForwardAlgorithm函數以一種啟發式的搜索方式返回一個算法,這個返回值通常都不是最優解,所以如果網絡是變尺寸的情況下,最好的處理方式就是在一定范圍內緩存所有情況的卷積輸入尺寸與使用算法的對應關系。當然最好的情況還是固定卷積的尺寸.
