百度推出飛槳(PaddlePaddle)后,不少開發(fā)者開始轉(zhuǎn)向國內(nèi)的深度學(xué)習(xí)框架。但是從代碼的轉(zhuǎn)移談何容易,之前的工作重寫一遍不太現(xiàn)實,成千上萬行代碼的手工轉(zhuǎn)換等于是在做一次二次開發(fā)。
現(xiàn)在,有個好消息:無論Caffe、TensorFlow、ONNX都可以輕松遷移到飛槳平臺上。雖然目前還不直接遷移PyTorch模型,但PyTorch本身支持導(dǎo)出為ONNX模型,等于間接對該平臺提供了支持。
然而,有人還對存在疑惑:不同框架之間的API有沒有差異?整個遷移過程如何操作,步驟復(fù)雜嗎?遷移后如何保證精度的損失在可接受的范圍內(nèi)?
大家會考慮很多問題,而問題再多,歸納一下,無外乎以下幾點:
1. API差異 :模型的實現(xiàn)方式如何遷移,不同框架之間的API有沒有差異?如何避免這些差異帶來的模型效果的差異?
2. 模型文件差異 :訓(xùn)練好的模型文件如何遷移?轉(zhuǎn)換框架后如何保證精度的損失在可接受的范圍內(nèi)?
3. 預(yù)測方式差異 :轉(zhuǎn)換后的模型如何預(yù)測?預(yù)測的效果與轉(zhuǎn)換前的模型差異如何?
飛槳開發(fā)了一個新的功能模塊,叫X2Paddle (Github見參考1),可以支持主流深度學(xué)習(xí)框架模型轉(zhuǎn)換至飛槳,包括Caffe、Tensorflow、onnx等模型直接轉(zhuǎn)換為Paddle Fluid可加載的預(yù)測模型,并且還提供了這三大主流框架間的API差異比較,方便我們在自己直接復(fù)現(xiàn)模型時對比API之間的差異,深入理解API的實現(xiàn)方式從而降低模型遷移帶來的損失。
下面以TensorFlow轉(zhuǎn)換成Paddle Fluid模型為例,詳細(xì)講講如何實現(xiàn)模型的遷移。
TensorFlow-Fluid 的API差異

在深度學(xué)習(xí)入門過程中,大家常見的就是手寫數(shù)字識別這個demo,下面是一份最簡單的實現(xiàn)手寫數(shù)字識別的代碼:
大家看這段代碼里,第一步是導(dǎo)入mnist數(shù)據(jù)集,然后設(shè)置了一個占位符x來表示輸入的圖片數(shù)據(jù),再設(shè)置兩個變量w和b,分別表示權(quán)重和偏置來計算,最后通過softmax計算得到輸出的y值,而我們真實的label則是變量y_ 。
前向傳播完成后,就可以計算預(yù)測值y與label y_之間的交叉熵。
再選擇合適的優(yōu)化函數(shù),此處為梯度下降,最后啟動一個Session,把數(shù)據(jù)按batch灌進(jìn)去,計算acc即可得到準(zhǔn)確率。
這是一段非常簡單的代碼,如果我們想把這段代碼變成飛槳的代碼,有人可能會認(rèn)為非常麻煩,每一個實現(xiàn)的API還要一一去找對應(yīng)的實現(xiàn)方式,但是這里,我可以告訴大家,不!用!這!么!麻!煩!因為在X2Paddle 里有一份常用的Tensorflow對應(yīng)Fluid的API表,(https://github.com/PaddlePaddle/X2Paddle/tree/master/tensorflow2fluid/doc),如下所示:

對于常用的TensorFlow的API,都有相應(yīng)的飛槳接口,如果兩者的功能沒有差異,則會標(biāo)注功能一致,如果實現(xiàn)方式或者支持的功能、參數(shù)等有差異,即會標(biāo)注“差異對比”,并詳細(xì)注明。

譬如,在上文這份非常簡單的代碼里,出現(xiàn)了這些TensorFlow的API:
在出現(xiàn)的這些api里,大部分的功能都是一致的,只有兩個功能不同,分別是tf.placeholder和tf.nn.softmax_cross_entropy_with_logits ,分別對應(yīng) fluid.layers.data 和 fluid.layers.softmax_with_cross_entropy . 我們來看看具體差異:
tf.placeholder V.S fluid.layers.data
常用TensorFlow的同學(xué)對placeholder應(yīng)該不陌生,中文翻譯為占位符,什么意思呢?在TensorFlow 2.0以前,還是靜態(tài)圖的設(shè)計思想,整個設(shè)計理念是計算流圖,在編寫程序時,首先構(gòu)筑整個系統(tǒng)的graph,代碼并不會直接生效,這一點和python的其他數(shù)值計算庫(如Numpy等)不同,graph為靜態(tài)的,在實際的運行時,啟動一個session,程序才會真正的運行。這樣做的好處就是:避免反復(fù)地切換底層程序?qū)嶋H運行的上下文,tensorflow幫你優(yōu)化整個系統(tǒng)的代碼。我們知道,很多python程序的底層為C語言或者其他語言,執(zhí)行一行腳本,就要切換一次,是有成本的,tensorflow通過計算流圖的方式,可以幫你優(yōu)化整個session需要執(zhí)行的代碼。
在代碼層面,每一個tensor值在graph上都是一個op,當(dāng)我們將train數(shù)據(jù)分成一個個minibatch然后傳入網(wǎng)絡(luò)進(jìn)行訓(xùn)練時,每一個minibatch都將是一個op,這樣的話,一副graph上的op未免太多,也會產(chǎn)生巨大的開銷;于是就有了tf.placeholder,我們每次可以將 一個minibatch傳入到x = tf.placeholder(tf.float32,[None,32])上,下一次傳入的x都替換掉上一次傳入的x,這樣就對于所有傳入的minibatch x就只會產(chǎn)生一個op,不會產(chǎn)生其他多余的op,進(jìn)而減少了graph的開銷。
參數(shù)對比
tf.placeholder

paddle.fluid.layers.data

從圖中可以看到,飛槳的api參數(shù)更多,具體差異如下:
· Batch維度處理
TensorFlow: 對于shape中的batch維度,需要用戶使用None指定;
飛槳: 將第1維設(shè)置為-1表示batch維度;如若第1維為正數(shù),則會默認(rèn)在最前面插入batch維度,如若要避免batch維,可將參數(shù)append_batch_size設(shè)為False。
· 梯度是否回傳
tensorflow和pytorch都支持對輸入求梯度,在飛槳中直接設(shè)置stop_gradient = False即可。如果在某一層使用stop_gradient=True,那么這一層之前的層都會自動的stop_gradient=True,梯度不會參與回傳,可以對某些不需要參與loss計算的信息設(shè)置為stop_gradient=True。對于含有BatchNormalization層的CNN網(wǎng)絡(luò),也可以對輸入求梯度,如

tf.nn.softmax_cross_entropy_with_logits V.S
fluid.layers.softmax_with_cross_entropy
參數(shù)對比

paddle.fluid.layers.softmax_with_cross_entropy

功能差異
標(biāo)簽類型
TensorFlow:labels只能使用軟標(biāo)簽,其shape為[batch, num_classes],表示樣本在各個類別上的概率分布;
飛槳:通過設(shè)置soft_label,可以選擇軟標(biāo)簽或者硬標(biāo)簽。當(dāng)使用硬標(biāo)簽時,label的shape為[batch, 1],dtype為int64;當(dāng)使用軟標(biāo)簽時,其shape為[batch, num_classes],dtype為int64。
返回值
TensorFlow:返回batch中各個樣本的log loss;
飛槳:當(dāng)return_softmax為False時,返回batch中各個樣本的log loss;當(dāng)return_softmax為True時,再額外返回logtis的歸一化值。
疑問點?
硬標(biāo)簽 ,即 one-hot label, 每個樣本僅可分到一個類別
軟標(biāo)簽 ,每個樣本可能被分配至多個類別中
numeric_stable_mode :這個參數(shù)是什么呢?標(biāo)志位,指明是否使用一個具有更佳數(shù)學(xué)穩(wěn)定性的算法。僅在 soft_label 為 False的GPU模式下生效. 若 soft_label 為 True 或者執(zhí)行場所為CPU, 算法一直具有數(shù)學(xué)穩(wěn)定性。注意使用穩(wěn)定算法時速度可能會變慢。默認(rèn)為 True。
return_softmax : 指明是否額外返回一個softmax值, 同時返回交叉熵計算結(jié)果。默認(rèn)為False。
如果 return_softmax 為 False, 則返回交叉熵?fù)p失
如果 return_softmax 為 True,則返回元組 (loss, softmax) ,其中交叉熵?fù)p失為形為[N x 1]的二維張量,softmax為[N x K]的二維張量
代碼示例

所以通過API對應(yīng)表,我們可以直接轉(zhuǎn)換把TensorFlow代碼轉(zhuǎn)換成Paddle Fluid代碼。但是如果現(xiàn)在項目已經(jīng)上線了,代碼幾千行甚至上萬行,或者已經(jīng)訓(xùn)練出可預(yù)測的模型了,如果想要直接轉(zhuǎn)換API是一件非常耗時耗精力的事情,有沒有一種方法可以直接把訓(xùn)練好的可預(yù)測模型直接轉(zhuǎn)換成另一種框架寫的,只要轉(zhuǎn)換后的損失精度在可接受的范圍內(nèi),就可以直接替換。下面就講講訓(xùn)練好的模型如何遷移。
模型遷移
VGG_16是CV領(lǐng)域的一個經(jīng)典模型,我以tensorflow/models下的VGG_16為例,給大家展示如何將TensorFlow訓(xùn)練好的模型轉(zhuǎn)換為飛槳模型。
下載預(yù)訓(xùn)練模型

解壓下載的壓縮文件

保存模型為checkpoint格式

TensorFlow2fluid目前支持checkpoint格式的模型或者是將網(wǎng)絡(luò)結(jié)構(gòu)和參數(shù)序列化的pb格式模型,上面下載的vgg_16.ckpt僅僅存儲了模型參數(shù),因此我們需要重新加載參數(shù),并將網(wǎng)絡(luò)結(jié)構(gòu)和參數(shù)一起保存為checkpoint模型
將模型轉(zhuǎn)換為飛槳模型

注意:部分OP在轉(zhuǎn)換時,需要將參數(shù)寫入文件;或者是運行tensorflow模型進(jìn)行infer,獲取tensor值。兩種情況下均會消耗一定的時間用于IO或計算,對于后一種情況,
打印輸出log信息(截取部分)

到這一步,我們已經(jīng)把tensorflow/models下的vgg16模型轉(zhuǎn)換成了Paddle Fluid 模型,轉(zhuǎn)換后的模型與原模型的精度有損失嗎?如何預(yù)測呢?來看下面。
預(yù)測結(jié)果差異
加載轉(zhuǎn)換后的飛槳模型,并進(jìn)行預(yù)測
上一步轉(zhuǎn)換后的模型目錄命名為“paddle_model”,在這里我們通過ml.ModelLoader把模型加載進(jìn)來,注意轉(zhuǎn)換后的飛槳模型的輸出格式由NHWC轉(zhuǎn)換為NCHW,所以我們需要對輸入數(shù)據(jù)做一個轉(zhuǎn)置。處理好數(shù)據(jù)后,即可通過model.inference來進(jìn)行預(yù)測了。具體代碼如下:

對比模型損失
轉(zhuǎn)換模型有一個問題始終避免不了,就是損失,從Tesorflow的模型轉(zhuǎn)換為Paddle Fluid模型,如果模型的精度損失過大,那么轉(zhuǎn)換模型實際上是沒有意義的,只有損失的精度在我們可接受的范圍內(nèi),模型轉(zhuǎn)換才能被實際應(yīng)用。在這里可以通過把兩個模型文件加載進(jìn)來后,通過numpy.fabs來求兩個模型結(jié)果的差異。

打印輸出

需要注意的點
1. 轉(zhuǎn)換后的模型需要注意輸入格式,飛槳中輸入格式需為NCHW格式。
此例中不涉及到輸入中間層,如卷積層的輸出,需要了解的是飛槳中的卷積層輸出,卷積核的shape與TensorFlow有差異。
2. 模型轉(zhuǎn)換完后,檢查轉(zhuǎn)換前后模型的diff,需要測試得到的最大diff是否滿足轉(zhuǎn)換需求。
總結(jié)
X2Paddle提供了一個非常方便的轉(zhuǎn)換方式,讓大家可以直接將訓(xùn)練好的模型轉(zhuǎn)換成Paddle Fluid版本。
轉(zhuǎn)換模型原先需要直接通過API對照表來重新實現(xiàn)代碼。但是在實際生產(chǎn)過程中這么操作是很麻煩的,甚至還要進(jìn)行二次開發(fā)。
如果有新的框架能輕松轉(zhuǎn)換模型,迅速運行調(diào)試,迭代出結(jié)果,何樂而不為呢?
雖然飛槳相比其他AI平臺上線較晚,但是憑借X2Paddle小工具,能快速將AI開發(fā)者吸引到自己的平臺上來,后續(xù)的優(yōu)勢將愈加明顯。
除了本文提到的tensoflow2fluid,Paddle Fluid還支持caffe2fluid、onnx2fluid,大家可以根據(jù)自身的需求體驗一下,有問題可以留言交流~
參考資料:
1. X2Paddle Github:https://github.com/PaddlePaddle/X2Paddle
2. tensorflow2fluid: https://github.com/PaddlePaddle/X2Paddle/tree/master/tensorflow2fluid
特別提醒:本網(wǎng)內(nèi)容轉(zhuǎn)載自其他媒體,目的在于傳遞更多信息,并不代表本網(wǎng)贊同其觀點。其原創(chuàng)性以及文中陳述文字和內(nèi)容未經(jīng)本站證實,對本文以及其中全部或者部分內(nèi)容、文字的真實性、完整性、及時性本站不作任何保證或承諾,并請自行核實相關(guān)內(nèi)容。本站不承擔(dān)此類作品侵權(quán)行為的直接責(zé)任及連帶責(zé)任。如若本網(wǎng)有任何內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系我們,本站將會在24小時內(nèi)處理完畢。