量化_深度学习
、
一、模型量化的背景需求
Resnet-152神經網絡的提出證明了越寬越深越大的模型往往比越窄越淺越小的模型精度要高,但是越寬越深越大的模型對計算資源要求更高,而現在模型應用越來越傾向于從云端部署到邊緣側,受限于邊緣側設備的計算資源,我們不得不考慮設備存儲空間(storage)、設備內存大小(memory)、設備運行功耗(power)及時延性(latency)等等問題,特別是在移動終端和嵌入式設備等資源受限的邊緣側應用場景中更加需要我們進行優化。因此,為了解決此類問題模型量化應運而生,本篇我們將探討模型量化的概念原理、優缺點及tensorflow模型量化的實現方法。
二、什么是模型量化
模型量化的定義沒有統一的說法,但個人理解為:
模型量化即以較低的推理精度損失將連續取值(或者大量可能的離散取值)的浮點型模型權重或流經模型的張量數據定點近似(通常為int8)為有限多個(或較少的)離散值的過程,它是以更少位數的數據類型用于近似表示32位有限范圍浮點型數據的過程,而模型的輸入輸出依然是浮點型,從而達到減少模型尺寸大小、減少模型內存消耗及加快模型推理速度等目標。
三、模型量化有什么好處
減小模型尺寸,如8位整型量化可減少75%的模型大小
減少存儲空間,在邊緣側存儲空間不足時更具有意義
易于在線升級,模型更小意味著更加容易傳輸
減少內存耗用,更小的模型大小意味著不需要更多的內存
加快推理速度,訪問一次32位浮點型可以訪問四次int8整型,整型運算比浮點型運算更快
減少設備功耗,內存耗用少了推理速度快了自然減少了設備功耗
支持微處理器,有些微處理器屬于8位的,低功耗運行浮點運算速度慢,需要進行8bit量化
總體來看,模型量化優勢明顯,但是其也有一些缺點:
模型量化增加了操作復雜度,在量化時需要做一些特殊的處理,否則精度損失更嚴重
模型量化會損失一定的精度,雖然在微調后可以減少精度損失,但推理精度確實下降
四、模型量化的原理
模型量化橋接了定點與浮點,建立了一種有效的數據映射關系,使得以較小的精度損失代價獲得了較好的收益,要弄懂模型量化的原理就是要弄懂這種數據映射關系。
由浮點到定點的量化公式如下:
這里的S和Z均是量化參數,而Q和R均可由公式進行求值,不管是量化后的Q還是反推求得的浮點值R,如果它們超出各自可表示的最大范圍,那么均需要進行截斷處理。而浮點值0在神經網絡里有著舉足輕重的意義,比如padding就是用的0,因而必須有精確的整型值來對應浮點值0。根據查看的tensorflow相關文檔,以往一般使用uint8進行定點量化,而目前有提供float16和int8等定點量化方法,而int8定點量化針對不同的數據有不同的范圍定義:
我們重點關注的是如下兩項:
每軸(或每通道)或每張量的權重用int8進行定點量化的可表示范圍為[-127,127],且zero-point就是量化值0
每張量的激活值或輸入值用int8進行定點量化的可表示范圍為[-128,127],其zero-point在[-128,127]內依據公式求得
舉例說明模型量化求值過程
模型訓練后權重或激活值往往在一個有限的范圍內分布,如激活值范圍為[-2.0, 6.0],然后我們用int8進行模型量化,則定點量化值范圍為[-128, 127],那么S和Z的求值過程如下:
五、tensorflow訓練后量化(Post-training quantization)介紹及其實現方法
tensorflow訓練后量化是針對已訓練好的模型來說的,針對大部分我們已訓練未做任何處理的模型來說均可用此方法進行模型量化,而tensorflow提供了一整套完整的模型量化工具,如TensorFlow Lite Optimizing COnverter(toco命令工具)以及TensorFlow Lite converter(API源碼調用接口).
tensorflow訓練后量化有幾種選擇,不同選擇的對比如下:
圖四 訓練后量化的幾種方式比較
選擇何種方式,需要結合業務場景及所擁有的硬件資源,目的是以最小損失達到最大化模型量化效果,官方提供了一棵決策樹用于選擇不同的訓練后量化方式,實作中可進行參考。
訓練后量化方式的實現方法如下:
第一種,混合量化–僅量化權重
該方式將浮點型的權重量化為int8整型,可將模型大小直接減少75%、提升推理速度最大3倍。該方式在推理的過程中,需要將int8量化值反量化為浮點型后再進行計算,如果某些Ops不支持int8整型量化,那么其保存的權重依然是浮點型的,即部分支持int8量化的Ops其權重保存為int8整型且存在quantize和dequantize操作,否則依然是浮點型的,因而稱該方式為混合量化。該方式可達到近乎全整型量化的效果,但存在quantize和dequantize操作其速度依然不夠理想,支持該方式的操作如下:
圖六 混合量化Ops
混合量化的實現方式比較簡單,僅需調用tf.lite.TFLiteConverter的API轉化即可:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
tflite_quant_model = converter.convert()
第二種,全整型量化–權重和激活值都進行量化
該方式則試圖將權重、激活值及輸入值均全部做int8量化,并且將所有模型運算操作置于int8下進行執行,以達到最好的量化效果。為了達到此目的,我們需要一個具有代表性的小數據集,用于統計激活值和輸入值等的浮點型范圍,以便進行精準量化,方法如下:
import tensorflow as tfdef representative_dataset_gen():for _ in range(num_calibration_steps):# Get sample input data as a numpy array in a method of your choosing.yield [input]converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset_gen
tflite_quant_model = converter.convert()
全整型量化的輸入輸出依然是浮點型的,但如果某些Ops未實現該方法,則轉化是沒問題的且其依然會自動保存為浮點型,這就要求我們的硬件支持這樣的操作,為了防止這樣的問題出現,我們可以在代碼里添加如下語句強制檢查并在Ops不支持全整型量化時進行報錯!
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
第三種,半精度float16量化–僅量化權重
該方式是將權重量化為半精度float16形式,其可以減少一半的模型大小、相比于int8更小的精度損失,如果硬件支持float16計算的話那么其效果更佳,這種方式是google近段時間提供的,其實現方式也比較簡單,僅需在代碼中調用如下接口即可:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.lite.constants.FLOAT16]
tflite_quant_model = converter.convert()
當在CPU運行時,半精度量化也需要像int8量化一樣進行反量化到float32在進行計算,但在GPU則不需要,因為GPU可以支持float16運算,但官方說float16量化沒有int8量化性價比高,因此,在實作中也需要仁者見仁智者見智了。
六、tensorflow量化感知訓練(Quantization-aware training)介紹及其實現方法
tensorflow量化感知訓練是一種偽量化的過程,它是在可識別的某些操作內嵌入偽量化節點(fake quantization nodes),用以統計訓練時流經該節點數據的最大最小值,便于在使用TOCO轉換tflite格式時量化使用并減少精度損失,其參與模型訓練的前向推理過程令模型獲得量化損失,但梯度更新需要在浮點下進行因而其并不參與反向傳播過程。某些操作無法添加偽量化節點,這時候就需要人為的去統計某些操作的最大最小值,但如果統計不準那么將會帶來較大的精度損失,因而需要較謹慎檢查哪些操作無法添加偽量化節點。值得注意的是,偽量化節點的意義在于統計流經數據的最大最小值并參與前向傳播提升精確度,但其在TOCO工具轉換為量化模型后,其工作原理還是與訓練后量化方式一致的!
量化感知訓練工作過程舉例說明如下圖所示:
可識別的ReLU節點嵌入了對應的偽量化節點QuantizedRelu,統計了其流經數據的max和min值,但存在quantize和dequantize的過程,其輸入輸出依然是float浮點型。而當多個可識別的操作相鄰時其嵌入偽量化節點的形式如圖八左半部分所示,但多個quantize和dequantize連接時是可以相互抵消的,圖八右半部分展示了精簡的偽量化節點連接的過程。
tensorflow量化感知訓練需要對訓練和推理過程添加相應的代碼,其方法如下:
第一步,在訓練圖結構內添加偽量化節點
# Build forward pass of model.
loss = tf.losses.get_total_loss()# Call the training rewrite which rewrites the graph in-place with
# FakeQuantization nodes and folds batchnorm for training. It is
# often needed to fine tune a floating point model for quantization
# with this training tool. When training from scratch, quant_delay
# can be used to activate quantization after training to converge
# with the float graph, effectively fine-tuning the model.
g = tf.get_default_graph()
tf.contrib.quantize.create_training_graph(input_graph=g, quant_delay=2000000)# Call backward pass optimizer as usual.
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
optimizer.minimize(loss)
般是在loss之后optimizer之前添加tf.contrib.quantize.create_training_graph關鍵函數,其將自動的幫我們在可識別的操作上嵌入偽量化節點,訓練并保存模型后,模型圖結構就會自動的存在偽量化節點及其統計的參數。tf.contrib.quantize.create_training_graph的參數input_graph表示訓練的默認圖層,quant_delay是指多少次迭代之后再進行量化,如果是已訓練好進行微調量化的話,那么可以將quant_delay設為0。
第二步,重寫推理圖結構并保存為新的模型
# Build eval model
logits = tf.nn.softmax_cross_entropy_with_logits_v2(...)# Call the eval rewrite which rewrites the graph in-place with
# FakeQuantization nodes and fold batchnorm for eval.
g = tf.get_default_graph()
tf.contrib.quantize.create_eval_graph(input_graph=g)# Save the checkpoint and eval graph proto to disk for freezing
# and providing to TFLite.
with open(eval_graph_file, ‘w’) as f:f.write(str(g.as_graph_def()))
saver = tf.train.Saver()
saver.save(sess, checkpoint_name)
推理和訓練的偽量化圖結構是存在較大差異的,而該操作正是要消除量化操作對batch normalization的影響,這是該步驟的目的所在。
第三步,轉換模型為全量化模型
首先,需要對第二步重寫后的模型進行固化:
freeze_graph \--input_graph=eval_graph_def.pb \--input_checkpoint=checkpoint \--output_graph=frozen_eval_graph.pb --output_node_names=outputs
然后,使用TOCO轉換得到真正的量化模型:
toco \--input_file=frozen_eval_graph.pb \--output_file=tflite_model.tflite \--input_format=TENSORFLOW_GRAPHDEF --output_format=TFLITE \--inference_type=QUANTIZED_UINT8 \--input_shape="1,224, 224,3" \--input_array=input \--output_array=outputs \--std_value=127.5 --mean_value=127.5
至此,我們將得到一個經過Tensorflow量化感知訓練后的全量化模型,我們可以使用Tensorflow lite框架進行模型推理加速!
七、tensorflow兩種量化方式的比較
tensorflow訓練后量化和量化感知訓練是兩種不同的量化方式,前者是一種offline的方式,而后者則是一種online的方式,由于深度學習神經網絡DNN對噪聲和擾動比較魯棒且訓練出來的模型權重往往落入一個有限的區間范圍內,因而這兩者均可達到以較少精度損失達到模型量化的目的,各自的優缺點如下:
兩者均可達到模型量化的作用
兩者的推理工作原理是一樣的
兩者都可工作在Tensorflow lite推理框架下并進行相應加速
訓練后量化工作量稍微簡單些,而量化感知訓練工作量更繁瑣一些
量化感知訓練比訓練后量化損失的精度更少,官方推薦使用量化感知訓練方式
以下是官方給出的一些模型經過訓練后量化(post-training quantization) 和量化感知訓練(quantization-aware training)后的延遲和準確性結果對比表格,該表中所有單幀推理時間都是在使用單個大內核的 Pixel 2 設備上測量的,從中可以看出量化感知訓練是比訓練后量化效果更優的!
圖九 訓練后量化與量化感知訓練性能對比表
總結
- 上一篇: tf.get_variable
- 下一篇: TFRecord tf.train.F