TensorFlow Mobile模型壓縮

前言

前文中我們把訓(xùn)練好的模型打包成GraphDef文件(PB文件)了莫杈,可是打包出來的文件還是有點(diǎn)大互例;移動設(shè)備的內(nèi)存容量有限,而且我們需要下載模型到移動端去加載姓迅,所以一個大的模型要經(jīng)過壓縮之后才能放在Android或者IOS上跑的敲霍,不然會給RAM造成極大的負(fù)擔(dān),系統(tǒng)會自行kill掉一些進(jìn)程丁存,甚至你的APP自己crash了肩杈。所以模型壓縮是十分必要的。

正文

在Google的優(yōu)化方案中解寝,主要有那么幾種優(yōu)化方式:刪除未使用節(jié)點(diǎn)扩然,刪除training所需的ops,對權(quán)重進(jìn)行四舍五入運(yùn)算聋伦,對BN進(jìn)行預(yù)處理夫偶,內(nèi)存映射,以及終極傷敵1000自損800的方案—轉(zhuǎn)換權(quán)重數(shù)據(jù)類型觉增。本文將逐一介紹兵拢。

optimize_for_inference

調(diào)用optimize_for_inference腳本會刪除輸入和輸出節(jié)點(diǎn)之間所有不需要的節(jié)點(diǎn)。同時該腳本還做了一些其他優(yōu)化以提高運(yùn)行速度逾礁。例如它把顯式批處理標(biāo)準(zhǔn)化運(yùn)算跟卷積權(quán)重進(jìn)行了合并说铃,從而降低了計算量。

使用方法:

bazel build tensorflow/python/tools:optimize_for_inference

bazel-bin/tensorflow/python/tools/optimize_for_inference \
–input=/tf_files/CTNModel.pb \
–output=/tf_files/optimized_graph.pb \
–input_names=inputs/X \
–output_names=output/predict

首先用bazel build出我們的optimize_for_inference腳本嘹履,再調(diào)用這個腳本腻扇,提供幾個參數(shù):輸入的PB文件路徑,輸出的PB文件路徑砾嫉,輸入節(jié)點(diǎn)名以及輸出節(jié)點(diǎn)名幼苛。

經(jīng)過這一次的優(yōu)化,文件變小了一些焕刮,可是還不足以我們放到手機(jī)端去運(yùn)行舶沿,所以我們要進(jìn)一步的壓縮模型,同時還要保證準(zhǔn)確率配并。

我們可以使用之前的在Python端測試PB文件的代碼測試優(yōu)化后的PB文件是否可行括荡。

quantize_graph

在IOS或者Android項目中,人們通常會把PB文件放在assets文件夾中加載荐绝,不管是一起打包進(jìn)APP還是進(jìn)入APP后再進(jìn)行下載一汽、解壓避消、復(fù)制低滩,在經(jīng)過optimize_for_inference優(yōu)化過后的模型依然是非常的大的召夹。IOS在使用.ipa包發(fā)布APP的時候,所有內(nèi)容都會經(jīng)過zip壓縮恕沫;Android從網(wǎng)絡(luò)端下載文件也要經(jīng)過壓縮后解壓的過程监憎,所以有沒有一種行之有效的方法在不過多的降低精確度的情況下壓縮更大的空間呢?Google提供了這么一個腳本婶溯,經(jīng)過這個腳本的PB文件原本的大小不會改變鲸阔,但會有更多的可利用的重復(fù)性,所以壓縮成zip包會縮小大約3~4倍的大小迄委。使用方式很簡單:

bazel build tensorflow/tools/quantization:quantize_graph

bazel-bin/tensorflow/tools/quantization/quantize_graph \
–input=/tf_files/optimized_graph.pb \
–output=/tf_files/rounded_graph.pb \
–output_node_names=output/predict \
–mode=weights_rounded

輸入的參數(shù)依然是:輸入的PB文件路徑褐筛,輸出的PB文件路徑,輸出節(jié)點(diǎn)名叙身,這里還有個特別的參數(shù)mode渔扎,這個參數(shù)是告訴腳本我們選擇哪種壓縮方式,這里我們選擇了對權(quán)重進(jìn)行四舍五入信轿。

graph_transforms

在2017年的Google I/O大會上晃痴,曾提到過的graph_transforms是一整套優(yōu)化工具,基本上所有的優(yōu)化方案都能在這里找到财忽,包括之前的那兩種優(yōu)化方式倘核。

Optimizing for Deployment

這個方式類似前面的optimize_for_inference,只是被整合到了transforms中即彪,腳本進(jìn)行了刪除前向傳播過程中未調(diào)用到的節(jié)點(diǎn)紧唱,通過預(yù)先乘卷積的權(quán)重來優(yōu)化BN中的一些乘法運(yùn)算。

示例代碼:

bazel build tensorflow/tools/graph_transforms:transform_graph

bazel-bin/tensorflow/tools/graph_transforms/transform_graph \
--in_graph=CTNModel.pb \
--out_graph=optimized_graph.pb \
--inputs='inputs/X' \
--outputs='output/predict' \
--transforms='
  strip_unused_nodes(type=float, shape="256*64")
  remove_nodes(op=Identity, op=CheckNumerics)
  fold_constants(ignore_errors=true)
  fold_batch_norms
  fold_old_batch_norms'

transforms的參數(shù)代表著想要做的操作祖凫,這里主要就是刪除未調(diào)用的節(jié)點(diǎn)琼蚯,以及優(yōu)化BN算法,而且必須先運(yùn)行fold_constants惠况。

Fixing Missing Kernel Errors

由于TensorFlow在Mobile中使用的時候遭庶,默認(rèn)情況下是只能推理預(yù)測,可是在build so文件稠屠、jar文件或者.a文件的時候峦睡,依賴文件是寫入在tensorflow / contrib / makefile / tf_op_files.txt中的,里面還包含了一些training相關(guān)的ops权埠,這些可能會導(dǎo)致我們在加載PB文件的時候報錯—No OpKernel was registered to support Op榨了,這時候我們可以通過這個腳本來修復(fù)

實(shí)際上,經(jīng)過上面的腳本攘蔽,已經(jīng)刪除了這部分節(jié)點(diǎn)龙屉,不需要重復(fù)使用。

示例代碼:

bazel build tensorflow/tools/graph_transforms:transform_graph

bazel-bin/tensorflow/tools/graph_transforms/transform_graph \
--in_graph=CTNModel.pb \
--out_graph=fixed_kernel_graph.pb \
--inputs='inputs/X' \
--outputs='output/predict' \
--transforms='
  strip_unused_nodes(type=float, shape="256*64")
  fold_constants(ignore_errors=true)
  fold_batch_norms
  fold_old_batch_norms'

round_weights

上文中提及的quantize_graph,其實(shí)在graph_transforms也有转捕,在graph_transforms中我們應(yīng)該如何使用呢作岖?

示例代碼:

bazel build tensorflow/tools/graph_transforms:transform_graph

bazel-bin/tensorflow/tools/graph_transforms/transform_graph \
--in_graph=CTNModel.pb \
--out_graph=rounded_grapg.pb \
--inputs='inputs/X' \
--outputs='output/predict' \
--transforms='
  strip_unused_nodes(type=float, shape="256*64")
  fold_constants(ignore_errors=true)
  fold_batch_norms
  fold_old_batch_norms
  round_weights(num_steps=256)'

Eight-bit

在graph_transforms中有一種更加殘暴的文件大小壓縮方式,就是把權(quán)重的數(shù)據(jù)類型由32位的float 32轉(zhuǎn)成8位的int五芝,在數(shù)據(jù)類型層面為模型減少3~4倍的占用大小痘儡。示例圖如下:

image.png

原本80多M的文件經(jīng)過這個轉(zhuǎn)換縮減成20M左右,成效還是挺明顯的枢步,不過缺點(diǎn)就是相比之前的幾種壓縮方式沉删,這種壓縮方式損失精度較大,非必要情況醉途,建議別使用矾瑰。

使用方法:

bazel build tensorflow/tools/graph_transforms:transform_graph

bazel-bin/tensorflow/tools/graph_transforms/transform_graph \
--in_graph=CTNModel.pb \
--out_graph=eight_bit_graph.pb \
--inputs='inputs/X' \
--outputs='output/predict' \
--transforms='
  strip_unused_nodes(type=float, shape="256*64")
  fold_constants(ignore_errors=true)
  fold_batch_norms
  fold_old_batch_norms
  quantize_weights'

再次提醒,非極端情況請勿使用隘擎!

由上面幾種grapg轉(zhuǎn)換功能來講脯倚,graph_transforms確實(shí)是一個非常強(qiáng)大的優(yōu)化腳本,他能夠一步到位的優(yōu)化我們的PB文件嵌屎,不像之前需要使用多種腳本之后才能得到想要的結(jié)果推正。上文提供了幾種常用的腳本代碼示例,每次使用只需要選其一即可宝惰。

memmapped_format

在前面幾種壓縮之后植榕,如果文件還是較大,那該怎么辦呢尼夺,這時候可以采用內(nèi)存映射的方式尊残,這一種方式是在運(yùn)行時控制內(nèi)存占用的一種有效方式,只是使用起來與原本的PB文件調(diào)用方式有些不同(暫時找不到Android的資料淤堵,只有IOS的寝衫,代碼詳見example)

示例圖如下:

image.png

從整個文件讀到內(nèi)存中變成內(nèi)存的映射,這能大量的節(jié)省內(nèi)存帶寬以及占用量拐邪。

使用方法:

bazel build tensorflow/contrib/util:convert_graphdef_memmapped_format

bazel-bin/tensorflow/contrib/util/convert_graphdef_memmapped_format \
–in_graph=/tf_files/rounded_graph.pb \
–out_graph=/tf_files/mmapped_graph.pb

經(jīng)過這個腳本輸出的PB文件不能再Python中直接調(diào)用慰毅,所以我們暫時也無法檢測其可行性,需要在IOS端調(diào)用扎阶。

后記

以上是PB文件常見的壓縮方法汹胃,在TensorFlow移植Mobile的過程中還有其他的優(yōu)化方案,比如優(yōu)化TensorFlow依賴庫的大小东臀,對ops進(jìn)行高級定制着饥,以刪除不需要的ops,此方法需要針對不同的model進(jìn)行不同的定制惰赋,較為高端宰掉。還有就是對模型進(jìn)行優(yōu)化比如嘗試減少層數(shù)、減少節(jié)點(diǎn)數(shù)、對卷積運(yùn)行做優(yōu)化(點(diǎn)卷積降維(inception)轨奄、卷積拆分(MobileNets))這些都是較為高級的Mobile端壓縮方式仇穗,本文暫時不做介紹了。

歡迎各位指錯討論戚绕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市枝冀,隨后出現(xiàn)的幾起案子舞丛,更是在濱河造成了極大的恐慌,老刑警劉巖果漾,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件球切,死亡現(xiàn)場離奇詭異,居然都是意外死亡绒障,警方通過查閱死者的電腦和手機(jī)吨凑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來户辱,“玉大人鸵钝,你說我怎么就攤上這事÷洌” “怎么了恩商?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長必逆。 經(jīng)常有香客問我怠堪,道長,這世上最難降的妖魔是什么名眉? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任粟矿,我火速辦了婚禮,結(jié)果婚禮上损拢,老公的妹妹穿的比我還像新娘陌粹。我一直安慰自己,他們只是感情好福压,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布申屹。 她就那樣靜靜地躺著,像睡著了一般隧膏。 火紅的嫁衣襯著肌膚如雪哗讥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天胞枕,我揣著相機(jī)與錄音杆煞,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛决乎,可吹牛的內(nèi)容都是我干的队询。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼构诚,長吁一口氣:“原來是場噩夢啊……” “哼蚌斩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起范嘱,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤送膳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丑蛤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叠聋,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年受裹,在試婚紗的時候發(fā)現(xiàn)自己被綠了碌补。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡棉饶,死狀恐怖厦章,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情照藻,我是刑警寧澤闷袒,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站岩梳,受9級特大地震影響囊骤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冀值,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一也物、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧列疗,春花似錦滑蚯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至古劲,卻和暖如春斥赋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背产艾。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工疤剑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留滑绒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓隘膘,卻偏偏與公主長得像疑故,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子弯菊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容