戰(zhàn)指南:從數(shù)據(jù)管道到模型部署的關(guān)鍵經(jīng)驗(yàn))
我經(jīng)常被問(wèn)到同一個(gè)問(wèn)題TensorFlow和另一個(gè)主流框架到底學(xué)哪個(gè)更好我的回答永遠(yuǎn)是先別急著問(wèn)學(xué)哪個(gè)先想清楚你要解決什么問(wèn)題。因?yàn)榻^大多數(shù)問(wèn)這個(gè)問(wèn)題的人剛看完幾篇入門(mén)教程正處于“看懂了但沒(méi)跑通”的階段。真正卡住他們的不是框架選型而是從環(huán)境搭建、數(shù)據(jù)管道、模型訓(xùn)練到最終部署這一整條鏈路里那些教程不會(huì)告訴你的坑。這篇內(nèi)容不打算做成一站式API手冊(cè)而是把我實(shí)際使用TensorFlow 2.x過(guò)程中覺(jué)得最值得反復(fù)琢磨的點(diǎn)包括版本匹配、自定義訓(xùn)練循環(huán)、數(shù)據(jù)管道優(yōu)化、顯存問(wèn)題和跨端部署按一條完整的項(xiàng)目推進(jìn)順序講清楚。無(wú)論你是剛開(kāi)始接觸深度學(xué)習(xí)還是已經(jīng)用TensorFlow寫(xiě)過(guò)幾個(gè)示例但總被生產(chǎn)環(huán)境里的問(wèn)題纏住這篇文章應(yīng)該都能給你一些啟發(fā)。1. TensorFlow 2.x到底解決什么問(wèn)題以及該不該選它1.1 它不是一個(gè)“模型框架”而是一整套生產(chǎn)工具鏈如果只把TensorFlow理解成搭神經(jīng)網(wǎng)絡(luò)的工具那確實(shí)大材小用了。TensorFlow 2.x真正提供的是從數(shù)據(jù)處理、模型構(gòu)建、訓(xùn)練調(diào)優(yōu)、模型導(dǎo)出到多端部署的完整工具鏈tf.data負(fù)責(zé)高效數(shù)據(jù)輸入tf.keras負(fù)責(zé)快速構(gòu)建模型tf.function負(fù)責(zé)把Python代碼編譯成高性能圖執(zhí)行SavedModel統(tǒng)一模型導(dǎo)出格式TensorFlow Lite和TensorFlow.js則把模型帶到移動(dòng)端和瀏覽器。這意味著什么意味著同樣的業(yè)務(wù)你可以在Jupyter Notebook里用幾行代碼做原型驗(yàn)證確認(rèn)可行后不需要推倒重來(lái)可以沿著這條鏈一路平滑地走到線上服務(wù)。我用過(guò)的感受是這種一體化設(shè)計(jì)在跨團(tuán)隊(duì)協(xié)作時(shí)特別省心。模型由算法同學(xué)訓(xùn)練導(dǎo)出成SavedModel后部署同學(xué)不用關(guān)心訓(xùn)練側(cè)用了什么花哨的API直接加載就能服務(wù)。1.2 什么場(chǎng)景下我建議你優(yōu)先選TensorFlow先給個(gè)比較主觀的結(jié)論如果你做的是偏業(yè)務(wù)應(yīng)用、需要把模型部署到多種環(huán)境或者團(tuán)隊(duì)里已經(jīng)積累了大量TensorFlow相關(guān)代碼那選擇它是比較穩(wěn)妥的。舉幾個(gè)真實(shí)場(chǎng)景模型要跑在Android/iOS端或者要在瀏覽器里做推理TensorFlow Lite和TensorFlow.js的生態(tài)明顯更成熟。你需要模型服務(wù)化的標(biāo)準(zhǔn)格式SavedModel配合模型倉(cāng)庫(kù)做版本管理方案非常清晰。公司內(nèi)部已經(jīng)有基于TensorFlow構(gòu)建的特征工程和推理鏈路延續(xù)使用比引入另一套框架的遷移成本低得多。反過(guò)來(lái)如果你的主要訴求是快速驗(yàn)證科研想法、大量嘗試動(dòng)態(tài)結(jié)構(gòu)另一個(gè)框架的學(xué)習(xí)曲線可能更平緩。但這是我的個(gè)人體會(huì)不代表誰(shuí)優(yōu)誰(shuí)劣。工具本來(lái)就分場(chǎng)景硬要比個(gè)高下沒(méi)有意義。1.3 2.x和1.x的差別比你想的大得多以前很多人對(duì)TensorFlow的印象停留在靜態(tài)圖、Session、placeholder這些概念上。到了2.x默認(rèn)就是動(dòng)態(tài)圖優(yōu)先Eager Execution代碼怎么寫(xiě)就怎么執(zhí)行不用先構(gòu)建計(jì)算圖再塞數(shù)據(jù)。這個(gè)改變對(duì)新手極其友好但網(wǎng)上仍然有很多老教程還在教你1.x的寫(xiě)法。我看代碼時(shí)判斷作者經(jīng)驗(yàn)?zāi)挲g基本看兩個(gè)點(diǎn)還在用tf.Session和tf.placeholder那大概率是1.x時(shí)代的老代碼用了tf.keras和tf.function才是2.x的思路。建議看到帶Session的教程直接跳過(guò)不要浪費(fèi)時(shí)間去學(xué)已經(jīng)淘汰的API。2. 環(huán)境搭建這一步版本匹配比想象中更容易翻車(chē)2.1 Python、TensorFlow、GPU運(yùn)行庫(kù)三者必須對(duì)齊很多人在環(huán)境上折騰一兩天問(wèn)題往往不是代碼而是Python版本、TensorFlow版本、GPU驅(qū)動(dòng)和運(yùn)行庫(kù)版本互相不匹配。比如你裝了某個(gè)新版本TensorFlow它可能依賴特定范圍的Python版本Python太老或太新都會(huì)導(dǎo)致安裝后運(yùn)行時(shí)報(bào)錯(cuò)。這里分享我維護(hù)環(huán)境的基本規(guī)則新建一個(gè)干凈的虛擬環(huán)境明確項(xiàng)目使用哪個(gè)Python版本再安裝對(duì)應(yīng)版本的TensorFlow。最好把依賴寫(xiě)進(jìn)requirements.txt或environment.yml固定版本號(hào)避免幾個(gè)月后重新安裝時(shí)依賴漂移。版本漂移是最隱蔽的坑它不會(huì)立刻報(bào)錯(cuò)但會(huì)在某一天觸發(fā)莫名其妙的行為變化。TensorFlow版本Python版本建議備注2.10及更早3.7~3.10老版本在新Python下容易編譯失敗2.11~2.133.8~3.11常用搭配資料最多2.14及以上3.9~3.12留意部分老代碼對(duì)API的兼容性這個(gè)表不用背安裝前花一分鐘查一下當(dāng)前版本的對(duì)關(guān)系能少走很多彎路。2.2 GPU支持裝完不是結(jié)束要驗(yàn)證是否真的調(diào)用成功如果你要訓(xùn)練稍微大一點(diǎn)的模型CPU基本扛不住。GPU版本的TensorFlow裝完之后很多人會(huì)直接跑訓(xùn)練結(jié)果程序慢得離譜或者根本沒(méi)報(bào)錯(cuò)但GPU占用率為零。我一直認(rèn)為環(huán)境裝完第一件事不是訓(xùn)練而是執(zhí)行下面這段驗(yàn)證代碼import tensorflow as tf print(TensorFlow版本:, tf.__version__) print(是否檢測(cè)到GPU:, tf.config.list_physical_devices(GPU))這段代碼輸出列表為空說(shuō)明TensorFlow根本沒(méi)找到GPU。常見(jiàn)原因包括顯卡驅(qū)動(dòng)版本過(guò)低、GPU運(yùn)行庫(kù)沒(méi)有安裝或版本不對(duì)、TensorFlow本身是沒(méi)有GPU支持的純CPU版本。我建議把“能否通過(guò)這段驗(yàn)證”當(dāng)作環(huán)境搭建完成的標(biāo)準(zhǔn)而不是“pip install成功”。操作系統(tǒng)這塊也要多說(shuō)一句。根據(jù)我個(gè)人經(jīng)驗(yàn)在Windows上跑GPU訓(xùn)練相比Linux會(huì)多出不少麻煩尤其是在某些版本之后官方預(yù)編譯包對(duì)Windows原生GPU的支持變得很有限。如果你有條件優(yōu)先在Linux環(huán)境或容器里跑訓(xùn)練可以省下大量排查系統(tǒng)依賴的時(shí)間。2.3 別在一個(gè)環(huán)境里同時(shí)搞多個(gè)項(xiàng)目我見(jiàn)過(guò)不少人圖省事直接在全局環(huán)境里pip install tensorflow然后裝各種包。短期沒(méi)問(wèn)題但時(shí)間一長(zhǎng)不同項(xiàng)目對(duì)同一個(gè)庫(kù)的版本需求打架升級(jí)一個(gè)包可能連帶破壞另一個(gè)項(xiàng)目。虛擬環(huán)境或容器是底線這個(gè)習(xí)慣越早養(yǎng)成越好。3. 從keras的舒適區(qū)走到自定義訓(xùn)練循環(huán)關(guān)鍵的幾步3.1 先學(xué)會(huì)用tf.keras快速跑通一個(gè)模型TensorFlow 2.x里tf.keras是官方主推的高層API。我入門(mén)時(shí)就用它構(gòu)建了一個(gè)手寫(xiě)數(shù)字識(shí)別模型感受確實(shí)像搭積木一樣。import tensorflow as tf from tensorflow.keras import layers model tf.keras.Sequential([ layers.Input(shape(28, 28)), layers.Flatten(), layers.Dense(128, activationrelu), layers.Dense(10, activationsoftmax) ]) model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] )compile、fit、evaluate這幾個(gè)方法足夠應(yīng)付大部分標(biāo)準(zhǔn)訓(xùn)練任務(wù)。再加上幾個(gè)常用的回調(diào)Callback比如EarlyStopping提前停止、ModelCheckpoint保存最優(yōu)權(quán)重基本就是一套完整的訓(xùn)練配置。你可能會(huì)問(wèn)那為什么還需要自定義訓(xùn)練循環(huán)因?yàn)楦邔覣PI處理標(biāo)準(zhǔn)場(chǎng)景很舒服但一旦遇到下面這些情況就有點(diǎn)施展不開(kāi)需要在一個(gè)batch里組合多個(gè)損失、需要?jiǎng)討B(tài)調(diào)整優(yōu)化器參數(shù)、需要做梯度裁剪或者要精細(xì)控制訓(xùn)練過(guò)程中每一步的日志記錄。3.2 用GradientTape接管訓(xùn)練過(guò)程自定義訓(xùn)練循環(huán)的核心是tf.GradientTape。它的工作方式可以這樣理解把前向計(jì)算過(guò)程記錄在“磁帶”上然后自動(dòng)反向計(jì)算梯度。下面是一個(gè)最基礎(chǔ)的自定義訓(xùn)練步驟optimizer tf.keras.optimizers.Adam(learning_rate1e-3) def train_step(images, labels): with tf.GradientTape() as tape: predictions model(images, trainingTrue) loss tf.keras.losses.sparse_categorical_crossentropy(labels, predictions) grads tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(grads, model.trainable_variables)) return loss這里最關(guān)鍵的是必須把前向計(jì)算放在with tf.GradientTape()塊里面否則梯度求不出來(lái)。另外需要注意訓(xùn)練時(shí)要傳trainingTrue這樣Dropout和BatchNormalization這些層才會(huì)進(jìn)入訓(xùn)練模式推理時(shí)傳trainingFalse否則結(jié)果會(huì)飄。3.3 什么時(shí)候需要tf.function加速動(dòng)態(tài)圖模型寫(xiě)起來(lái)方便但執(zhí)行效率有時(shí)不如編譯后的靜態(tài)圖。tf.function就是用來(lái)把一段Python邏輯編譯成高效的TensorFlow圖執(zhí)行的工具。加了它之后代碼邏輯幾乎不用改訓(xùn)練速度經(jīng)常有肉眼可見(jiàn)的提升。不過(guò)有個(gè)點(diǎn)很容易踩tf.function在首次調(diào)用時(shí)會(huì)做一次“追蹤”把Python代碼轉(zhuǎn)成圖。如果你的函數(shù)里用了依賴Python全局狀態(tài)的邏輯比如動(dòng)態(tài)改變list長(zhǎng)度、頻繁創(chuàng)建tf.Variable很可能在追蹤階段報(bào)錯(cuò)或行為異常。我的習(xí)慣是先不用tf.function把訓(xùn)練邏輯調(diào)試正確確認(rèn)無(wú)誤后再在外面套一層加速出了問(wèn)題也容易定位。3.4 自定義指標(biāo)和損失別只等官方模板實(shí)際項(xiàng)目中的損失函數(shù)很少是現(xiàn)成的。比如類(lèi)別不平衡時(shí)我常用Focal Loss代替普通的交叉熵。實(shí)現(xiàn)起來(lái)也就是定義一個(gè)繼承tf.keras.losses.Loss的類(lèi)重寫(xiě)call方法。自定義指標(biāo)類(lèi)似。我認(rèn)為學(xué)會(huì)這個(gè)技能是脫離“只會(huì)跑示例”的分水嶺因?yàn)闃I(yè)務(wù)問(wèn)題的獨(dú)特性就體現(xiàn)在這些定制邏輯里。4. 訓(xùn)練慢的元兇常常不是模型而是數(shù)據(jù)管道4.1 GPU利用率低先別急著怪模型結(jié)構(gòu)不少人發(fā)現(xiàn)自己訓(xùn)練過(guò)程很慢下意識(shí)覺(jué)得是模型太復(fù)雜或者優(yōu)化器參數(shù)沒(méi)調(diào)好。實(shí)際上訓(xùn)練循環(huán)基本是“取數(shù)據(jù)、算損失、更新參數(shù)”的重復(fù)操作如果每一步的前置數(shù)據(jù)加載花了太多時(shí)間GPU就會(huì)一直空轉(zhuǎn)等待。你可以在訓(xùn)練時(shí)通過(guò)GPU狀態(tài)工具觀察利用率如果GPU占用率一直很低并且波動(dòng)很大而CPU某個(gè)核心接近滿載大概率問(wèn)題出在數(shù)據(jù)讀取和預(yù)處理上。4.2 用tf.data而不是Python循環(huán)喂數(shù)據(jù)初學(xué)者常見(jiàn)做法是在Python里用for循環(huán)遍歷數(shù)據(jù)每次調(diào)用model.train_on_batch。雖然也能跑但瓶頸很明顯——數(shù)據(jù)的讀取、解碼、增強(qiáng)全部串行執(zhí)行而且CPU到GPU的數(shù)據(jù)拷貝沒(méi)有被流水線化。tf.data專門(mén)解決這類(lèi)問(wèn)題。它提供的Dataset API用聲明式方式描述數(shù)據(jù)流能充分利用并行能力。以圖片訓(xùn)練為例推薦的數(shù)據(jù)管道大致是這個(gè)形態(tài)def load_and_augment(image_path, label): image tf.io.read_file(image_path) image tf.image.decode_jpeg(image, channels3) image tf.image.resize(image, [224, 224]) image tf.image.random_flip_left_right(image) / 255.0 return image, label train_ds tf.data.Dataset.from_tensor_slices((image_paths, labels)) train_ds train_ds.map(load_and_augment, num_parallel_callstf.data.AUTOTUNE) train_ds train_ds.shuffle(buffer_size1000) train_ds train_ds.batch(32) train_ds train_ds.prefetch(tf.data.AUTOTUNE)這里說(shuō)幾個(gè)容易出錯(cuò)的地方。4.3 數(shù)據(jù)管道里的操作順序直接決定性能shuffle、map、batch、prefetch的順序看著隨意其實(shí)影響很大。我的習(xí)慣是先map做預(yù)處理再shuffle再batch最后prefetch。map里的預(yù)處理是CPU密集操作用num_parallel_callstf.data.AUTOTUNE讓TensorFlow自動(dòng)決定并行度。shuffle的buffer_size要與數(shù)據(jù)集規(guī)模匹配太小會(huì)導(dǎo)致隨機(jī)性不足。batch放在shuffle之后保證每個(gè)batch是從亂序后的數(shù)據(jù)里取的。prefetch放在管道末端作用是讓數(shù)據(jù)準(zhǔn)備和GPU計(jì)算重疊這是提升利用率最直接的一招。還有一個(gè)細(xì)節(jié)如果你的預(yù)處理結(jié)果在多個(gè)epoch之間不變比如普通歸一化可以在map后加.cache()把結(jié)果緩存到內(nèi)存或磁盤(pán)后續(xù)epoch讀取會(huì)明顯變快。但如果做了隨機(jī)增強(qiáng)就不要cache增強(qiáng)之后的數(shù)據(jù)否則每個(gè)epoch看到的都是同一份增強(qiáng)結(jié)果等于白做。4.4 避免在map里寫(xiě)太重的Python代碼tf.data的map回調(diào)雖然可以用tf.py_function包住任意Python邏輯但這樣做會(huì)丟失TensorFlow圖的優(yōu)化機(jī)會(huì)反而變慢。優(yōu)先用TensorFlow自帶的操作例如tf.image里的裁剪、旋轉(zhuǎn)、顏色調(diào)整它們都是編譯好的。只有當(dāng)實(shí)在無(wú)法用現(xiàn)有算子實(shí)現(xiàn)時(shí)才去用tf.py_function并且心里要有預(yù)期性能會(huì)有所下降。5. GPU顯存與訓(xùn)練穩(wěn)定性O(shè)OM、混合精度和踩坑記錄5.1 顯存不足OOM不一定是模型太大訓(xùn)練過(guò)程報(bào)Resource exhausted錯(cuò)誤第一反應(yīng)自然是顯存不夠。但很多時(shí)候是顯存使用策略問(wèn)題。TensorFlow默認(rèn)會(huì)在第一次分配顯存時(shí)搶占幾乎所有可用空間如果同一臺(tái)機(jī)器上還有別的任務(wù)很容易互相干擾。在小規(guī)模調(diào)試階段我更推薦開(kāi)啟顯存按需增長(zhǎng)gpus tf.config.list_physical_devices(GPU) if gpus: try: for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True) except RuntimeError as e: print(e)這樣程序啟動(dòng)時(shí)只占少量顯存隨著真實(shí)需要逐步擴(kuò)容。代價(jià)是訓(xùn)練期間偶爾會(huì)出現(xiàn)顯存碎片所以如果你確定一個(gè)任務(wù)長(zhǎng)期獨(dú)占整張卡反倒不用開(kāi)讓它一次性把顯存管理起來(lái)更高效。5.2 混合精度訓(xùn)練提速效果超出預(yù)期在支持的GPU上用混合精度訓(xùn)練是我強(qiáng)烈推薦的一步。原理很簡(jiǎn)單模型里一部分算子用float16計(jì)算減少顯存占用和計(jì)算時(shí)間同時(shí)用float32保存關(guān)鍵權(quán)重避免精度損失。TensorFlow讓這件事變得很簡(jiǎn)單只需要在訓(xùn)練啟動(dòng)前設(shè)置tf.keras.mixed_precision.set_global_policy(mixed_float16)我自己的實(shí)際項(xiàng)目中混合精度開(kāi)啟后訓(xùn)練速度經(jīng)常有接近兩倍的提升顯存占用也會(huì)下降。需要注意的是有些自定義損失函數(shù)在float16下容易溢出需要留意loss是否出現(xiàn)NaN。如果遇到精度奇怪的問(wèn)題先關(guān)掉混合精度測(cè)試一下往往能定位到是數(shù)值范圍的問(wèn)題。5.3 梯度裁剪和極端數(shù)據(jù)帶來(lái)的NaN訓(xùn)練過(guò)程中最讓人頭疼的就是loss變成NaN。常見(jiàn)原因包括學(xué)習(xí)率過(guò)大、數(shù)據(jù)里存在異常值、損失計(jì)算出現(xiàn)除零。我一般采取兩個(gè)措施一是設(shè)置梯度裁剪限制每次更新的最大梯度范數(shù)二是在數(shù)據(jù)預(yù)處理時(shí)對(duì)異常樣本做好清洗。這兩個(gè)方法成本低卻能在很大程度上讓訓(xùn)練過(guò)程變得更穩(wěn)定。optimizer tf.keras.optimizers.Adam(learning_rate1e-3, clipnorm1.0)5.4 報(bào)錯(cuò)信息看不懂時(shí)先縮小到最近改動(dòng)遇到一條大段報(bào)錯(cuò)很多人第一反應(yīng)是去搜索引擎復(fù)制粘貼。我的經(jīng)驗(yàn)是與其盲查不如先回頭看自己最近改了什么。TensorFlow棧信息比較長(zhǎng)但真正有用的往往是錯(cuò)誤類(lèi)型名稱和最后幾行描述。把報(bào)錯(cuò)當(dāng)作線索而不是天書(shū)你會(huì)發(fā)現(xiàn)問(wèn)題大多出在shape不匹配、數(shù)據(jù)類(lèi)型不一致、或者某個(gè)API參數(shù)用錯(cuò)。6. 訓(xùn)練完不是結(jié)束導(dǎo)出、量化與跨端部署的實(shí)操要點(diǎn)6.1 模型保存格式首選SavedModel訓(xùn)練完成后模型保存是個(gè)看似簡(jiǎn)單卻影響深遠(yuǎn)的環(huán)節(jié)。tf.keras的model.save既可以存成H5格式也可以存成SavedModel目錄格式。我現(xiàn)在的項(xiàng)目里一律優(yōu)先用SavedModel因?yàn)樗Y(jié)構(gòu)清晰包含模型權(quán)重、計(jì)算圖和變量加載時(shí)無(wú)需依賴原始的Python代碼結(jié)構(gòu)。model.save(saved_model/my_model)后續(xù)無(wú)論你要做服務(wù)化推理還是轉(zhuǎn)成移動(dòng)端模型SavedModel都是最順手的起點(diǎn)。6.2 量化壓縮把模型塞進(jìn)手機(jī)和瀏覽器模型在服務(wù)器上是幾百M(fèi)B到了手機(jī)或?yàn)g覽器就是負(fù)擔(dān)。TensorFlow生態(tài)里負(fù)責(zé)這件事的是模型轉(zhuǎn)換和量化工具。最省事的做法是在轉(zhuǎn)換時(shí)打開(kāi)內(nèi)置量化將權(quán)重從float32降到float16甚至int8體積和速度都會(huì)有明顯改善。量化類(lèi)型體積變化速度變化適用場(chǎng)景float16量化約減半提升有限需要保留較高精度int8量化可降到約四分之一明顯提升端側(cè)推理、內(nèi)存受限量化之后一定要用真實(shí)數(shù)據(jù)做精度驗(yàn)證。我曾遇到過(guò)轉(zhuǎn)換后輸出完全不可用的情況問(wèn)題出在部分層對(duì)量化比較敏感。所以不要覺(jué)得轉(zhuǎn)換成功就萬(wàn)事大吉驗(yàn)證集跑一遍才是底線。6.3 瀏覽器和移動(dòng)端的路徑不同如果你想把模型放到瀏覽器里實(shí)時(shí)推理TensorFlow.js可以加載轉(zhuǎn)換后的模型如果目標(biāo)是移動(dòng)AppTensorFlow Lite則是更輕量級(jí)的方案。兩者的共同點(diǎn)是都從SavedModel或直接轉(zhuǎn)換而來(lái)但輸入尺寸、動(dòng)態(tài)shape的處理方式不同。我的經(jīng)驗(yàn)是盡量固定輸入尺寸比如統(tǒng)一resize到固定長(zhǎng)寬減少轉(zhuǎn)換時(shí)的麻煩。動(dòng)態(tài)輸入雖然也能處理但端側(cè)推理的延遲和兼容性往往不如固定尺寸穩(wěn)定。6.4 部署鏈路的“自定義層”陷阱當(dāng)模型里包含自定義層或自定義損失時(shí)導(dǎo)出和加載就要格外小心。SavedModel會(huì)嘗試把自定義邏輯序列化但如果轉(zhuǎn)換器無(wú)法識(shí)別加載時(shí)就會(huì)失敗或丟失行為。一個(gè)保險(xiǎn)的做法是在加載模型時(shí)顯式傳入自定義對(duì)象字典讓模型知道如何重建這些邏輯。這一點(diǎn)在團(tuán)隊(duì)協(xié)作中最容易出問(wèn)題算法側(cè)訓(xùn)練完交付模型部署側(cè)卻不知道這個(gè)自定義層需要特殊處理。建議在交付物里寫(xiě)清楚模型包含哪些自定義組件或者直接把模型定義代碼一并打包省得后面反復(fù)扯皮。最后說(shuō)一個(gè)我自己的使用習(xí)慣?,F(xiàn)在我接到一個(gè)新的深度學(xué)習(xí)任務(wù)第一件事不是立刻開(kāi)模型設(shè)計(jì)而是按“環(huán)境、數(shù)據(jù)管道、訓(xùn)練循環(huán)、部署驗(yàn)證”這四段去拆解項(xiàng)目。每一段都有固定的踩坑點(diǎn)和檢查項(xiàng)按順序過(guò)一遍模型本身反而成了最簡(jiǎn)單的一環(huán)。TensorFlow這款工具功能非常多但真正需要深入掌握的路徑其實(shí)比較固定把tf.keras用熟練把數(shù)據(jù)管道搞明白弄懂一次自定義訓(xùn)練循環(huán)走通一次導(dǎo)出與部署之后大部分項(xiàng)目場(chǎng)景萬(wàn)變不離其宗。真遇到眼花繚亂的新概念往回歸因到這幾條主干上基本不會(huì)迷路。