
這幾年大模型和AIGC火得一塌糊涂找工作也好、搞科研也好十份簡歷里有八份都寫著“熟悉TensorFlow或PyTorch”。但說實話很多人對TensorFlow的理解還停留在“裝個庫、調(diào)個API、跑個demo”的階段一旦遇到真實的項目需求——比如要把模型部署到線上、要處理上千萬條樣本、要跟團隊協(xié)作維護代碼——立刻就抓瞎了。這篇文章我打算從TensorFlow的底層設(shè)計邏輯講起把你可能踩過的坑、沒想明白的概念、裝環(huán)境時的玄學(xué)問題一次性捋清楚。這篇文章適合這幾類人看剛?cè)腴T深度學(xué)習(xí)、準備用TensorFlow做課程設(shè)計或畢業(yè)設(shè)計的學(xué)生已經(jīng)在用PyTorch、但工作需要切到TensorFlow的工程師以及想搞明白“TensorFlow和PyTorch到底該選哪個”的糾結(jié)癥患者。我不會只堆概念會把我自己實際跑項目時的完整流程、報錯記錄、調(diào)參心得都放進來你跟著走一遍基本能避開我當(dāng)年趟過的那些坑。1. TensorFlow到底是什么從一個模型上線的需求說起1.1 它不是你想象的“萬能工具箱”很多新手把TensorFlow理解成“一個能直接讀數(shù)據(jù)、出結(jié)果的軟件”這個理解不算錯但容易走彎路。實際上TensorFlow是一個端到端的深度學(xué)習(xí)平臺它管的不是“你調(diào)一個函數(shù)出結(jié)果”這一件事而是覆蓋了從數(shù)據(jù)處理、模型構(gòu)建、訓(xùn)練調(diào)參、模型導(dǎo)出再到線上服務(wù)部署的完整鏈路。舉個例子你在Kaggle上跑個比賽用PyTorch寫好訓(xùn)練腳本把模型權(quán)重存成.pt文件這件事到“訓(xùn)練完”基本就結(jié)束了。但到了真實業(yè)務(wù)場景你訓(xùn)練好的模型要給別人用可能是Java后端的同事要調(diào)用可能是手機App里要離線跑也可能是幾千臺服務(wù)器上的容器要拉取同一個模型做推理。TensorFlow生態(tài)里對應(yīng)的是TF Serving、TensorFlow Lite、TensorFlow.js這些組件它解決的恰恰是“訓(xùn)練完之后怎么辦”的問題。所以你問我TensorFlow是什么我的答案很簡單它是一個把“訓(xùn)練模型”和“用模型”統(tǒng)一起來的完整生態(tài)。這一點是它跟PyTorch最大的氣質(zhì)差異PyTorch的核心體驗停留在研究和訓(xùn)練階段而TensorFlow從一開始就在為生產(chǎn)環(huán)境鋪路。1.2 TensorFlow的核心組成從工程視角拆解TensorFlow由三層組成理解這三層你就知道平時寫的代碼到底在跟誰打交道。底層運行時負責(zé)張量運算、自動微分、多設(shè)備CPU/GPU/TPU調(diào)度。你不需要直接跟它打交道但它決定了你的模型能不能跑、跑得快不快。中間層API包括tf.keras高層建模接口、tf.data數(shù)據(jù)管道、tf.train訓(xùn)練控制等。日常寫代碼主要在這一層。上層部署工具包括TensorFlow Serving服務(wù)化部署、TensorFlow Lite移動端和嵌入式、TensorFlow.js瀏覽器端、TensorFlow Model Garden官方預(yù)訓(xùn)練模型庫。順便說一句很多人分不清“TensorFlow”和“Keras”。Keras 2.x版本是作為tf.keras模塊內(nèi)置在TensorFlow里的你寫的from tensorflow import keras其實就是Keras。到了Keras 3.0它變成了一個多后端框架可以跑在TensorFlow、PyTorch和JAX之上但TensorFlow 2.16以上的版本里tf.keras仍然是默認的官方推薦入口。不用糾結(jié)直接記結(jié)論用TensorFlow就用tf.keras這是當(dāng)前版本的最佳實踐。2. 環(huán)境搭建安裝細節(jié)與版本選型實操2.1 版本和硬件選型別看教程盲裝TensorFlow的安裝算是老生常談但我?guī)缀趺恐芏寄芸吹接腥嗽谏鐓^(qū)里問“為什么我裝的TensorFlow用不了GPU”。大部分原因是版本跟CUDA、cuDNN對不上或者干脆裝了一個只支持CPU的版本。先給結(jié)論截至2024年TensorFlow 2.x的CPU版和GPU版是同一個安裝包不再像1.x時代那樣分tensorflow和tensorflow-gpu。你執(zhí)行pip install tensorflow裝好之后如果你的機器有NVIDIA顯卡且驅(qū)動版本滿足要求TensorFlow會自動檢測并嘗試使用GPU。但“自動檢測”不等于“自動裝驅(qū)動”CUDA和cuDNN這套東西還是得你自己搞定。這里我給出一個經(jīng)過大量實踐驗證的版本匹配思路先裝好NVIDIA顯卡驅(qū)動用nvidia-smi查看驅(qū)動支持的CUDA版本號。根據(jù)你準備裝的TensorFlow版本反查它要求的CUDA和cuDNN版本。再用conda或pip安裝對應(yīng)版本的CUDA Toolkit和cuDNN注意不是裝最新版就萬事大吉TensorFlow對特定版本才有優(yōu)化適配。我見過太多人直接裝了最新的CUDA 12.x結(jié)果TensorFlow 2.10以下的版本根本不認白白折騰一晚上。TensorFlow官網(wǎng)的“Build from source”頁面里有每個版本對應(yīng)的CUDA/cuDNN/Python版本清單安裝前先花五分鐘查一下能少走一半彎路。2.2 CPU與GPU環(huán)境安裝的完整命令以我目前最常用的TensorFlow 2.15或2.16為例這是我的標準安裝流程。# 1. 創(chuàng)建獨立的Python環(huán)境避免依賴互相污染 conda create -n tf2 python3.11 conda activate tf2 # 2. 安裝CPU版本適合跑小模型或沒有NVIDIA顯卡的機器 pip install tensorflow2.16.1 # 3. 如果要用GPU先裝CUDA和cuDNN以Linux CUDA 12.2為例 conda install -c conda-forge cudatoolkit12.2 cudnn8.9 # 4. 再安裝TensorFlow pip install tensorflow2.16.1裝完之后用一段極簡代碼確認GPU是否真的被識別import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices(GPU))如果你看到類似PhysicalDevice(name/physical_device:GPU:0, device_typeGPU)的輸出就說明TensorFlow已經(jīng)能調(diào)用GPU了。如果你在Windows上遇到“Could not load dynamic library cudnn64_8.dll”之類的報錯別慌這不是你代碼的問題是cudnn的DLL路徑?jīng)]加到系統(tǒng)PATH里把cudnn/bin目錄加進去重啟終端就好。2.3 安裝TensorFlow時最容易翻車的三個細節(jié)第一個坑是Python版本。TensorFlow 2.16對Python 3.12的支持其實已經(jīng)很好了但很多第三方配套庫比如某些數(shù)據(jù)預(yù)處理庫還沒有跟上所以我自己習(xí)慣用Python 3.10或3.11兼容性最穩(wěn)。第二個坑是pip和conda混用。如果你用conda創(chuàng)建了環(huán)境就統(tǒng)一用pip安裝TensorFlow反過來如果你直接用系統(tǒng)Python就小心別再混入conda的包不然經(jīng)常出現(xiàn)“裝了兩遍但import的還是舊版本”的詭異情況。第三個坑是鏡像源。在國內(nèi)環(huán)境直接用pip官方源有時候慢到懷疑人生我一般把pip源和conda源都換到國內(nèi)鏡像安裝速度能快好幾倍。安裝這件事說到底是熟練工踩過一次坑后面就順了。但真正讓新手難受的還不是安裝是裝完之后寫代碼時對TensorFlow的“脾氣”不熟悉下面我重點拆解它的核心概念。3. 核心概念拆解張量、計算圖與Keras建模3.1 張量TensorFlow世界里的“通用貨幣”你可以把張量理解成一個“有形狀的多維數(shù)組”。標量是0維張量向量是1維矩陣是2維而一個視頻數(shù)據(jù)可以看成5維張量——(批次大小, 幀數(shù), 高度, 寬度, 通道數(shù))。TensorFlow里的所有數(shù)據(jù)從輸入到中間特征圖到最后的預(yù)測結(jié)果全部以張量形式存在。但張量和numpy的數(shù)組有一個本質(zhì)區(qū)別TensorFlow的張量支持自動微分。這意味著你在定義模型時寫的那些加減乘除、卷積、池化操作框架會自動記錄每一步的計算過程形成一個“計算圖”。當(dāng)你調(diào)用loss.backward()或者tape.gradient()時框架能沿著這個圖反向算出所有參數(shù)的梯度。我說個生活化的類比你寫做菜步驟的時候每一步都記在紙上。等菜做完了你想知道自己哪一步放鹽放多了、改一下會怎樣就得把步驟從后往前推一遍看看每一步對最終味道的“影響程度”。TensorFlow的自動微分就是這個“反向追溯”過程只不過它算的不是影響程度而是梯度——也就是參數(shù)往哪個方向調(diào)整損失函數(shù)能下降得最快。3.2 動態(tài)圖與靜態(tài)圖為什么TensorFlow 2.x更香TensorFlow 1.x時代用的是靜態(tài)圖你得先把整個計算流程“畫”出來然后提交給會話Session去執(zhí)行。這個模式的好處是圖結(jié)構(gòu)固定、便于分布式優(yōu)化但壞處也很明顯——調(diào)試困難沒法像寫普通Python代碼那樣打斷點看中間結(jié)果。TensorFlow 2.x做了一個關(guān)鍵轉(zhuǎn)變默認采用動態(tài)圖模式Eager Execution也就是你寫的代碼邊定義邊執(zhí)行跟寫PyTorch和普通Python代碼的體驗幾乎一樣。這讓入門門檻大幅降低也是TensorFlow 2.x能被更多人接受的原因之一。但你可能會問靜態(tài)圖優(yōu)化的優(yōu)勢不要了嗎TensorFlow的解法是tf.function你先用動態(tài)圖模式下寫完代碼、調(diào)試好邏輯再用tf.function裝飾器把它轉(zhuǎn)換成靜態(tài)圖執(zhí)行兼顧開發(fā)體驗和運行性能。實際經(jīng)驗是把訓(xùn)練循環(huán)、前向傳播這種反復(fù)調(diào)用的部分用tf.function包起來能明顯提升訓(xùn)練速度尤其是小批量樣本的場景。3.3 Keras官方推薦的建模方式在TensorFlow 2.x里官方主推的建模方式就是tf.keras。它提供了三種建模風(fēng)格適配不同的需求Sequential順序模型適合直筒型網(wǎng)絡(luò)結(jié)構(gòu)一層接一層。Functional函數(shù)式模型適合復(fù)雜結(jié)構(gòu)比如多輸入多輸出、共享層、殘差連接。Subclassing子類化模型通過繼承tf.keras.Model自定義前向傳播靈活性最高同時保留Keras的封裝能力。從工程角度我建議優(yōu)先掌握函數(shù)式模型。它比Sequential靈活能應(yīng)對絕大多數(shù)實際網(wǎng)絡(luò)結(jié)構(gòu)又比子類化模型更適合部署和檢查——因為你通過函數(shù)式API構(gòu)建的模型在導(dǎo)出SavedModel時可以拿到完整的結(jié)構(gòu)圖而自定義子類化的模型在部署時經(jīng)常要多做一步_saved_model_input_spec之類的指定比較麻煩。4. 完整實操從零訓(xùn)練并部署一個圖像分類模型4.1 數(shù)據(jù)準備用tf.data構(gòu)建高效數(shù)據(jù)管道理論看十遍不如動手跑一遍。下面我用一個真實的圖像分類任務(wù)比如貓狗識別走一遍完整流程所有代碼都是可以直接跑的你在自己的環(huán)境里換一下數(shù)據(jù)路徑就能用。數(shù)據(jù)準備這一步我不建議一次性把所有圖片讀進內(nèi)存尤其是真實項目里圖片動輒幾萬張內(nèi)存直接爆炸。正確做法是用tf.data.Dataset構(gòu)建一個數(shù)據(jù)管道它的核心機制是惰性加載和流水線化——只有當(dāng)前這一個batch的數(shù)據(jù)被加載到內(nèi)存而且CPU在讀取和預(yù)處理數(shù)據(jù)的同時GPU可以并行做計算互不等待。# 構(gòu)建高效數(shù)據(jù)管道 train_ds tf.keras.preprocessing.image_dataset_from_directory( data/train, validation_split0.2, subsettraining, seed42, image_size(224, 224), batch_size32 ) # 自動混洗、預(yù)處理、預(yù)取 train_ds train_ds.shuffle(1000)\ .map(preprocess)\ .prefetch(buffer_sizetf.data.AUTOTUNE)這段代碼里有幾個值得說的細節(jié)。prefetch(buffer_sizetf.data.AUTOTUNE)是讓CPU在做當(dāng)前批次的同時提前準備下一批次通常能帶來接近翻倍的訓(xùn)練吞吐提升。map(preprocess)里的預(yù)處理函數(shù)要盡量用TensorFlow原生算子寫不要用Python的for循環(huán)去逐張?zhí)幚韴D像否則速度會慢一個數(shù)量級。4.2 模型構(gòu)建從零手寫不如站在預(yù)訓(xùn)練模型肩膀上做圖像分類從頭訓(xùn)練一個ResNet50這樣的網(wǎng)絡(luò)在普通單卡機器上至少要跑幾天而且數(shù)據(jù)集不夠大的話效果還很差。實踐的常規(guī)操作是遷移學(xué)習(xí)用ImageNet上預(yù)訓(xùn)練好的權(quán)重做初始化只訓(xùn)練末尾新增的分類層。from tensorflow.keras.applications import MobileNetV2 # 加載預(yù)訓(xùn)練模型去掉頂層分類器 base_model MobileNetV2( weightsimagenet, include_topFalse, input_shape(224, 224, 3) ) base_model.trainable False # 先凍結(jié)只訓(xùn)練頂層 # 在頂部添加自己的分類器 model tf.keras.Sequential([ base_model, tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dense(128, activationrelu), tf.keras.layers.Dropout(0.3), tf.keras.layers.Dense(1, activationsigmoid) ]) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), lossbinary_crossentropy, metrics[accuracy] )這段代碼里有幾個值得展開的點。第一include_topFalse表示不要預(yù)訓(xùn)練模型的原始分類層因為我們自己的數(shù)據(jù)集類別數(shù)跟ImageNet1000類不一樣。第二base_model.trainable False表示凍結(jié)基礎(chǔ)模型的權(quán)重訓(xùn)練時只更新我們新加的層。這樣做的原因是預(yù)訓(xùn)練模型已經(jīng)學(xué)到了非常通用的特征邊緣、紋理、物體部件在中小數(shù)據(jù)集上微調(diào)整個網(wǎng)絡(luò)容易過擬合而且訓(xùn)練速度極慢。第三GlobalAveragePooling2D把最后一個卷積層的特征圖壓縮成一個一維向量參數(shù)量很少不容易過擬合比直接Flatten更穩(wěn)。4.3 訓(xùn)練與調(diào)參學(xué)習(xí)率、早停與數(shù)據(jù)增強訓(xùn)練階段我習(xí)慣分兩步走。第一步保持基礎(chǔ)模型凍結(jié)只訓(xùn)練頂層用相對大的學(xué)習(xí)率1e-3訓(xùn)練5到10個epoch等驗證集準確率不再明顯提升。第二步解凍部分基礎(chǔ)模型通常是最后十幾層把學(xué)習(xí)率調(diào)低到1e-5做精細微調(diào)。這種“先粗后細”的策略比從頭到尾一個學(xué)習(xí)率硬跑效果好得多。數(shù)據(jù)增強在圖像任務(wù)里幾乎是必須的。我常用的增強手段包括隨機翻轉(zhuǎn)、隨機旋轉(zhuǎn)、隨機縮放和色彩抖動。TensorFlow里直接用tf.keras.layers.RandomFlip、RandomRotation、RandomZoom這些內(nèi)置層就能實現(xiàn)不用額外引第三方庫。數(shù)據(jù)增強的本質(zhì)是“免費”擴充訓(xùn)練集讓模型對輕微的形變和光線變化變得魯棒對防止過擬合非常有效。history model.fit( train_ds, validation_dataval_ds, epochs30, callbacks[ tf.keras.callbacks.EarlyStopping( monitorval_loss, patience5, restore_best_weightsTrue ), tf.keras.callbacks.ReduceLROnPlateau( monitorval_loss, factor0.2, patience2 ) ] )EarlyStopping的restore_best_weightsTrue很多人會忽略但很重要——它保證訓(xùn)練提前停止時模型恢復(fù)的是驗證集表現(xiàn)最好那一刻的權(quán)重而不是最后幾個epoch可能已經(jīng)過擬合的權(quán)重。ReduceLROnPlateau則是在驗證集loss連續(xù)兩個epoch不降時自動把學(xué)習(xí)率乘0.2省去手動調(diào)學(xué)習(xí)率的麻煩。4.4 部署從SavedModel到線上服務(wù)訓(xùn)練完的模型要給別人用不是把.h5文件扔過去就行。我推薦的標準做法是把模型導(dǎo)出成SavedModel格式這是TensorFlow統(tǒng)一的部署格式能被TensorFlow Serving、TensorFlow Lite、TensorFlow.js等多種工具直接加載。model.export(saved_model/mymodel) # 或者 tf.saved_model.save(model, saved_model/mymodel)導(dǎo)出之后目錄里會包含saved_model.pb、variables/和assets/這幾個部分。做線上推理服務(wù)時可以用TensorFlow Serving把模型包裝成一個HTTP接口Java、Go或者其他后端語言通過標準API調(diào)用即可。這一步在不熟悉TensorFlow生態(tài)的人看來可能很陌生但實際投入產(chǎn)出比很高你訓(xùn)練一萬次模型最終的目標都是有人能用上它部署就是“能用上”的最后一公里。5. TensorFlow與PyTorch2024年的流行趨勢和選型思路5.1 兩邊各自的底盤今年被問到最多的問題就是“TensorFlow和PyTorch學(xué)哪個是不是PyTorch要一統(tǒng)天下了” 2024年的真實情況是PyTorch在學(xué)術(shù)界和科研圈確實勢頭更猛大部分最新論文的官方代碼都是PyTorch寫的。CVPapers、NeurIPS這些頂會的統(tǒng)計里PyTorch的使用率占到了壓倒性多數(shù)這一點沒什么好爭的。但“研究用的多”不等于“生產(chǎn)環(huán)境也用的多”。TensorFlow在以下場景里仍然有很強的存在感大規(guī)模分布式訓(xùn)練TensorFlow的分布式策略MirroredStrategy、MultiWorkerMirroredStrategy、TPUStrategy比PyTorch的原生分布式方案更成熟尤其是TPU訓(xùn)練這塊TensorFlow幾乎是唯一選擇。工業(yè)級部署TensorFlow Serving的成熟度、穩(wěn)定性和吞吐表現(xiàn)在生產(chǎn)環(huán)境里依然是第一梯隊很多互聯(lián)網(wǎng)公司的推薦、搜索、廣告系統(tǒng)里跑的還是TensorFlow模型。移動端和嵌入式TensorFlow Lite在安卓平臺的工具鏈和硬件加速支持做得很好如果你要做端側(cè)推理TensorFlow的生態(tài)更省心。Keras 3.02024年Keras完成了多后端改造你可以在Keras里用PyTorch或JAX后端訓(xùn)練模型再用TensorFlow后端部署。這在一定程度上把兩個生態(tài)打通了也延長了TensorFlow的生命周期。所以我的判斷是TensorFlow并沒有“涼”它的角色從“人人必學(xué)”變成了“特定場景的工業(yè)標準”。PyTorch在研究和快速原型階段優(yōu)勢明顯TensorFlow在重生產(chǎn)、重部署、重分布式的大型系統(tǒng)里地位依然穩(wěn)固。5.2 選型建議你該學(xué)哪個如果你問我個人建議我會這么分你是學(xué)生目標是發(fā)論文、跑實驗、快速復(fù)現(xiàn)優(yōu)先學(xué)PyTorch跟學(xué)術(shù)社區(qū)接軌的路最順。你是工程師工作內(nèi)容涉及模型上線、服務(wù)部署或者公司里老模型就是TensorFlow生態(tài)的那就認真學(xué)TensorFlow學(xué)透了在生產(chǎn)環(huán)境能直接產(chǎn)生價值。你是入門者想先掌握一門作為切入口選哪個都行但TensorFlow的tf.keras高層API對新手相對友好學(xué)起來會順一點。更聰明的做法是先精通一個再用Keras 3或ONNX去打通另一個框架是工具不是信仰。我自己的實際體會是兩個都會的人在職場上明顯更有競爭力。不是說要你平均用力而是理解清楚每個框架的設(shè)計哲學(xué)和最佳適用場景遇到實際問題時自然知道怎么選。6. 常見問題排查與避坑實錄6.1 高頻報錯速查表下面這些報錯是我在答疑和實際項目中遇到過的最頻繁的幾個整理成一個速查表你遇到同類問題直接對照著查就行。報錯信息原因解決方案Could not load dynamic library cudnn64_8.dllcuDNN版本與TensorFlow不匹配或DLL路徑未配置檢查CUDA/cuDNN版本對照表將cuDNN的bin目錄加入系統(tǒng)PATHNot a valid tf.function或Cannot convert a symbolic Tensor在tf.function內(nèi)使用了Python原生對象或動態(tài)shape操作改用TensorFlow原生算子或在外部固定輸入shapeGPU is not availableGPU版驅(qū)動未裝好或裝了CPU版TensorFlow用nvidia-smi驗證驅(qū)動用tf.config.list_physical_devices(GPU)確認識別Model hasnt been compiled調(diào)用model.evaluate前忘了model.compile先編譯再評估注意評估時的loss和metrics可以跟訓(xùn)練時不一致ValueError: Shapes (None, 1) and (None, 2) are incompatible標簽編碼與輸出層神經(jīng)元數(shù)量不一致二分類用1個神經(jīng)元sigmoid多分類用類別數(shù)個神經(jīng)元softmax并確保標簽格式匹配OOM when allocating tensor with shape顯存不足或batch_size過大減小batch_size或使用mixed_precision混合精度訓(xùn)練混合精度訓(xùn)練值得單獨說一句。TensorFlow 2.x里開啟混合精度只需要兩行代碼from tensorflow.keras import mixed_precision mixed_precision.set_global_policy(mixed_float16)這樣可以讓模型的一部分計算用FP16完成顯存占用幾乎減半在支持Tensor Core的NVIDIA顯卡上訓(xùn)練速度還能提升一截。我自己的經(jīng)驗是對于CV類模型混合精度幾乎是無腦開啟的收益但要注意損失計算和輸出層最好保持FP32精度Keras默認會處理這件事你只需關(guān)注數(shù)值穩(wěn)定性異常的情況。6.2 性能調(diào)優(yōu)的幾條實戰(zhàn)經(jīng)驗除了上面這些報錯我再分享幾個能實打?qū)嵦嵘?xùn)練體驗的小技巧這些在官方文檔里不太會專門講但實際效果極其明顯。第一個是tf.function和TF32的搭配。在Ampere架構(gòu)及其后的NVIDIA顯卡上TensorFlow默認可能使用TF32精度來加速矩陣運算這會讓某些任務(wù)的精度有輕微損失但訓(xùn)練速度提升明顯。如果你的任務(wù)對精度極其敏感比如某些回歸任務(wù)可以在環(huán)境變量里關(guān)閉它export TF_FORCE_GPU_ALLOW_GROWTHtrue配合tf.config.experimental.set_memory_growth騰訊的博客也提過。我自己習(xí)慣保留TF32因為大部分任務(wù)的效果差異可以忽略不計但速度賺到了。第二個是數(shù)據(jù)管道的瓶頸排查。訓(xùn)練時如果GPU利用率nvidia-smi里的GPU-Util長期低于80%說明瓶頸很可能在數(shù)據(jù)讀取環(huán)節(jié)。你可以先跑一次model.fit之外的數(shù)據(jù)迭代測量next(iter(train_ds))的時間如果單個batch加載時間超過訓(xùn)練時間的女性部分就需要增加prefetch、增加num_parallel_calls或者把數(shù)據(jù)先轉(zhuǎn)成TFRecord格式再讀取。第三個是權(quán)重保存策略。我強烈建議使用tf.keras.callbacks.ModelCheckpoint保存訓(xùn)練過程中的最佳模型而不是只靠訓(xùn)練結(jié)束后手動保存。因為訓(xùn)練過程可能因為斷電、斷點、顯存溢出等各種原因中斷你不想最后發(fā)現(xiàn)所有進度都白費了。checkpoint_callback tf.keras.callbacks.ModelCheckpoint( filepathbest_model.keras, monitorval_accuracy, save_best_onlyTrue, save_weights_onlyFalse )注意save_weights_onlyFalse很關(guān)鍵表示同時保存完整模型結(jié)構(gòu)和權(quán)重恢復(fù)的時候直接tf.keras.models.load_model就能用不需要重新構(gòu)建網(wǎng)絡(luò)再加載權(quán)重。6.3 兩個容易忽視但影響巨大的模型設(shè)計習(xí)慣最后說兩個我在code review時經(jīng)常發(fā)現(xiàn)的問題。第一不要隨意在整個模型里到處用Dropout。Dropout是正則化手段它的本職任務(wù)是防止過擬合但它會讓訓(xùn)練收斂變慢。正確用法是加在全連接層之間而不是卷積層輸出上。坦率地說很多“加了Dropout效果更差”的案例都是把Dropout當(dāng)成裝飾品到處亂放。第二輸出層的激活函數(shù)和損失函數(shù)必須是匹配的一對。二分類 單神經(jīng)元輸出用sigmoid binary_crossentropy多分類 N個神經(jīng)元輸出用softmax categorical_crossentropy標簽是one-hot編碼如果是整數(shù)標簽用sparse_categorical_crossentropy回歸任務(wù)不設(shè)激活函數(shù)用mse或mae。這個看起來是常識但我在不少開源項目里都見過“用softmax做二分類最后acc卡在50%”的案例問題就出在激活函數(shù)和損失函數(shù)不匹配上。我個人的經(jīng)驗是模型調(diào)優(yōu)的過程本質(zhì)上是一個“用排除法固定問題邊界”的過程。先把數(shù)據(jù)管道、訓(xùn)練循環(huán)、部署流程這些工程問題全部定下來再集中精力去調(diào)模型結(jié)構(gòu)和訓(xùn)練超參數(shù)這樣才能真正在有限時間內(nèi)看到模型效果的提升。如果你一上來就在一個千瘡百孔的環(huán)境里調(diào)參只會浪費大量時間而沒有任何結(jié)論——而這恰恰是我見過的新手最容易犯的錯誤。跑完上面這個流程你應(yīng)該已經(jīng)熟悉了從環(huán)境搭建、數(shù)據(jù)準備、模型構(gòu)建、訓(xùn)練調(diào)參再到部署上線的全過程。TensorFlow的學(xué)習(xí)曲線確實不算平緩但它生態(tài)完整、工程化程度高一旦你把它吃透在真實項目中的戰(zhàn)斗力會非常強。希望我踩過的這些坑能幫你少走一段彎路。