深度學習——TFLite踩坑記

最近模型量化需要使用tensorflow中的lite模塊,着手做這個東西已經好幾天了,但是效果不明顯,在此梳理一下,希望能找到其中的問題。

tf版本:最新的tf版本中(1.13)和nightly中,lite模塊是直接在tf下的,在此之前的版本中,lite包含在tf.contrib中,這部分可以在官方文檔中可以看到,一些類或者函數名也被修改了。

模型量化的目的:https://petewarden.com/2016/05/03/how-to-quantize-neural-networks-with-tensorflow/

這篇文章寫得比較清晰。在此大致做一些摘抄。

1.訓練神經網絡時,浮點運算可以較好的保留精度,且GPU使得浮點運算沒有什麼鴨梨,可是當我們想把這些訓練的模型進行運用時,問題就來了,應用時,我們希望模型在沒有GPU的條件下推斷inference更快。

2.爲何量化可行?深層網絡的特性在於它能夠一定程度上忽略輸入的噪聲,專注於輸入與訓練集的相似性,這種能力意味着它能夠將低精度計算看作是另一種噪聲源,從而依然能在低精度計算中得到正確的推斷結果。

3.爲何量化?神經網絡模型中含有大量的浮點權重,會佔據大量空間,但是可以發現這些權重的範圍符合正態分佈,集中在一個數字範圍內。因此量化最簡單的動機就是存儲每層的最值,並將最值區間的浮點數映射到0~255的8位整形上。另一個量化動機是減少推斷時所需要的計算資源,雖然這很困難,但是會帶來速度和空間上的收益,對於移動設備,還會更省電。

4.爲何不直接訓練低精度?因爲反向傳播需要高精度的計算。

5.如何量化?先使用freeze_graph將checkpoints轉換爲常量,然後使用tf.contrib.quantization中的quantize_graph,這一部分要比博客上的複雜一些。

6.量化是如何工作的?relu,conv,pool等都要進行量化,輸入輸出仍然是浮點,轉換在中間進行。

一旦轉換了各個操作,下一個階段就是刪除與float之間不必要的轉換。如果有連續序列的操作都具有浮點等價物,那麼將會有許多相鄰的去量化/量化操作。這個階段發現了這種模式,認識到它們相互抵消,並將它們移除,就像這樣:

大規模地應用於所有運算都量化等價的模型,這給出了一個圖,其中所有張量計算都在8位元內完成,而不必轉換爲浮點數。

7.如何表示?將權重的範圍線性映射到8位整形範圍上。

8.如何確定範圍。

 

開始轉換:

1.pb文件:保存圖模型的計算流程圖,包含常量,不包含變量。

2.ckpt文件:保存圖模型中的變量。

3.lite文件:包含計算流程圖和變量。

可以根據需要,生成量化或者未量化的lite文件。這裏我們只考慮量化的Lite文件。未量化的lite文件直接使用freeze_graph和toco工具進行轉換就可以。

量化的Lite文件:

首先要在訓練的過程中,計算loss的函數後面要僞量化計算圖。注意在官方文檔中有寫到,不能在訓練一開始就進行僞量化,不然會極大影響模型的精度。tf.contrib.quantize.create_training_graph(quant_delay=2000)正確的方式是加載預訓練模型,或是將quant_delay的值設置在模型開始收斂的次數之後。

然後生成pb文件,在生成pb文件前執行tf.contrib.quantize.create_eval_graph().

接下來使用freeze_graph凍結pb文件和ckpt,生成新的凍結的pb,最後使用toco量化凍結後的pb文件。

python freeze_graph.py --input_graph F:\0316\logs3\1000eval.pb --input_checkpoint F:\0316\logs3\model.ckpt-1000 --output_graph F:\0316\logs3\frozen.pb --output_node MobileNet/pred

可以得到凍結的pb。然後量化。

toco --graph_def_file ~/zhanhuimei/codes/Classification/frozen.pb --output_file ~/zhanhuimei/codes/Classification/cls.tflite --output_arrays MobileNet/pred --input_arrays input_image --allow_custom_ops --output_format TFLITE --inference_type QUANTIZED_UINT8 --std_dev_value 127.5 --mean_value 127.5

執行toco時報錯:Input 0 of node MobileNet/conv4_1/conv4_1/conv2d/weights_quant/AssignMinLast was passed float from MobileNet/conv4_1/conv4_1/conv2d/weights_quant/min:0 incompatible with expected float_ref.

懷疑是tensorflow的bug,用的是1.10,因此重新裝一個1.13.1試一下。發現與tensorflow版本並沒有關係。

在此期間嘗試另一種方法,是通過代碼實現,例程:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/speech_commands/freeze.py

源碼分析:

例程生成pb不是用的freeze_graph,而是

frozen_graph_def = graph_util.convert_variables_to_constants(
    sess, sess.graph_def, ['labels_softmax'])
tf.train.write_graph(
    frozen_graph_def,
    os.path.dirname(FLAGS.output_file),
    os.path.basename(FLAGS.output_file),
    as_text=False)

使用段代碼生成的pb,再用toco進行轉換,沒有報先前那個錯誤,但是又報了另一個錯。

F tensorflow/lite/toco/graph_transformations/quantize.cc:491] Unimplemented: this graph contains an operator of type ReduceProd for which the quantized form is not yet implemented. Sorry, and patches welcome (that's a relatively fun patch to write, mostly providing the actual quantized arithmetic code for this op).
 

之前也遇到過,ReduceProd無法轉換的問題。想到用Netron打開轉化的pb圖,看到裏面有一個Prod的操作,具體的是圖像預處理的時候,使用了圖像標準化 tf.image.per_image_standardization()。把這個刪除後,還是會報錯。【結果發現是路徑設置錯了。。用於轉換的文件沒有更新】我要好好整理下我的路徑。。。

遇到兩個問題:不做標準化得話,訓練的時候不能收斂。此外還遇到一個問題,就是toco轉換的時候,平均值和標準差的設置。即std_dev_value和mean_value,還有兩個參數default_range_max和default_range_min

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章