習(xí)實(shí)戰(zhàn):TensorFlow與Keras環(huán)境搭建及圖像分類(lèi)入門(mén))
開(kāi)頭就直接進(jìn)入正題。這篇文章就是給你準(zhǔn)備的實(shí)戰(zhàn)手冊(cè)——不講虛理論只講怎么把Python深度學(xué)習(xí)這一條線(xiàn)走通。我用的框架是TensorFlow 2.0里面集成的Keras是主力建模工具。先說(shuō)清楚這篇文章不是讓你從零學(xué)Python語(yǔ)法也不是讓你啃透所有深度學(xué)習(xí)數(shù)學(xué)公式而是假設(shè)你已經(jīng)會(huì)寫(xiě)基本Python代碼想快速入門(mén)深度學(xué)習(xí)和TensorFlow。我會(huì)從環(huán)境配置開(kāi)始一路講到完整的圖像分類(lèi)項(xiàng)目實(shí)戰(zhàn)最后分享我踩過(guò)的坑和排查經(jīng)驗(yàn)。讀完你能跑通第一個(gè)模型并且知道下一步該往哪走。1. 開(kāi)工前先搞定環(huán)境Python、虛擬環(huán)境和TensorFlow安裝1.1 為什么勸你別在全局環(huán)境里直接裝TensorFlow我剛接觸深度學(xué)習(xí)那會(huì)兒圖省事直接在系統(tǒng)Python里敲了pip install tensorflow當(dāng)時(shí)是能跑。但后來(lái)項(xiàng)目多了問(wèn)題就來(lái)了——這個(gè)項(xiàng)目要TensorFlow 2.4那個(gè)項(xiàng)目要2.10還有的項(xiàng)目要用PyTorch三個(gè)框架的依賴(lài)在同一個(gè)全局環(huán)境里互相打架。我記得最慘的一次為了裝一個(gè)圖像庫(kù)把NumPy從1.19升級(jí)到了1.21結(jié)果另一個(gè)項(xiàng)目直接崩了一查是TensorFlow底層依賴(lài)不兼容。從那天起我就老老實(shí)實(shí)每個(gè)項(xiàng)目開(kāi)一個(gè)虛擬環(huán)境。Python的虛擬環(huán)境原理其實(shí)很簡(jiǎn)單你可以把它理解成給每個(gè)項(xiàng)目單獨(dú)開(kāi)一個(gè)工具箱。不同項(xiàng)目用自己的工具箱里面裝的庫(kù)版本互不影響。這樣你怎么折騰都不會(huì)波及別的項(xiàng)目。具體到操作我推薦用Anaconda原因有兩條第一它自帶的conda命令能直接裝科學(xué)計(jì)算相關(guān)的二進(jìn)制包省去了Windows上編譯的噩夢(mèng)第二創(chuàng)建環(huán)境后切換環(huán)境只需要一條命令。# 創(chuàng)建名為tf2的虛擬環(huán)境指定Python 3.9 conda create -n tf2 python3.9 -y # 激活環(huán)境 conda activate tf21.2 Python版本和TensorFlow版本的匹配關(guān)系很多人沒(méi)注意TensorFlow跟Python版本有嚴(yán)格的對(duì)應(yīng)關(guān)系裝錯(cuò)了直接報(bào)錯(cuò)。根據(jù)我長(zhǎng)期實(shí)測(cè)以及TensoFlow官方發(fā)布記錄整理一份對(duì)應(yīng)關(guān)系供參考TensorFlow版本推薦Python版本備注2.4 ~ 2.93.7 ~ 3.9老項(xiàng)目常見(jiàn)兼容性處理好2.10 ~ 2.123.8 ~ 3.10目前最穩(wěn)定的區(qū)間2.13以上3.9 ~ 3.11新特性多但部分第三方庫(kù)跟進(jìn)慢我自己現(xiàn)在習(xí)慣固定用Python 3.9 TensorFlow 2.10這個(gè)組合踩坑最少網(wǎng)上資料也最多。你要是從零開(kāi)始直接照這個(gè)來(lái)。別追最新版本深度學(xué)習(xí)的庫(kù)更新快新版本往往意味著新的不兼容。1.3 TensorFlow安裝和第一個(gè)驗(yàn)證命令創(chuàng)建好環(huán)境后安裝TensorFlow很簡(jiǎn)單就一行命令pip install tensorflow這句裝的是CPU版還是GPU版這要看你的操作系統(tǒng)。在Linux上TensorFlow 2.1之后的pip包會(huì)自動(dòng)包含GPU支持有NVIDIA顯卡就會(huì)自動(dòng)調(diào)用。在Windows上默認(rèn)pip裝的是CPU版本GPU版本需要額外配CUDA和cuDNN。再說(shuō)個(gè)掏心窩的建議入門(mén)階段別碰GPU配置。CUDA和cuDNN的版本匹配問(wèn)題能把人折磨到懷疑人生而且入門(mén)跑小模型CPU完全夠用。我在第4章會(huì)專(zhuān)門(mén)講GPU配置的坑但那是你跑通流程以后的事。裝完驗(yàn)證一下在命令行輸入python -c import tensorflow as tf; print(tf.__version__)看到輸出2.10.0之類(lèi)的版本號(hào)環(huán)境就通了。這一步很重要很多人后面代碼寫(xiě)了一大堆結(jié)果才發(fā)現(xiàn)環(huán)境沒(méi)配好。1.4 VS Code配置Python環(huán)境編輯器推薦VS Code免費(fèi)、插件豐富、對(duì)Python支持好。配置步驟就三步安裝Python插件微軟官方出的那個(gè)。CtrlShiftP輸入Python: Select Interpreter選中剛剛創(chuàng)建的tf2虛擬環(huán)境。在項(xiàng)目根目錄新建.vscode/settings.json可以指定默認(rèn)解釋器一勞永逸。有個(gè)小技巧VS Code右下角會(huì)顯示當(dāng)前解釋器路徑你看到路徑里帶envs\tf2就說(shuō)明環(huán)境選對(duì)了。很多人代碼沒(méi)問(wèn)題卻一直報(bào)ModuleNotFoundError八成就是解釋器還指在全局Python上。2. 看懂TensorFlow 2.0和Keras為什么這套組合適合入門(mén)2.1 TensorFlow 1.x到2.0的巨變沒(méi)用過(guò)TensorFlow 1.x的人可能不知道2.0以前寫(xiě)代碼有多反人類(lèi)。在1.x時(shí)代你得先構(gòu)建一張靜態(tài)計(jì)算圖然后放進(jìn)Session里去跑。調(diào)試起來(lái)極其痛苦——你沒(méi)法在中間打印變量看一眼因?yàn)檎麄€(gè)圖是“畫(huà)”好了再執(zhí)行你想看的中間結(jié)果根本不存在。我當(dāng)時(shí)學(xué)的時(shí)候經(jīng)常為了查一個(gè)bug得在代碼里寫(xiě)一堆tf.Print那體驗(yàn)跟盲人摸象差不多。TensorFlow 2.0最核心的改變是引入了默認(rèn)的Eager Execution動(dòng)態(tài)圖機(jī)制。直白點(diǎn)說(shuō)代碼寫(xiě)到哪就執(zhí)行到哪一行一行看得見(jiàn)摸得著你隨時(shí)可以打印中間變量的值。這個(gè)改動(dòng)讓TensorFlow從“先用圖定義計(jì)算再執(zhí)行”變成了“邊定義邊執(zhí)行”調(diào)試體驗(yàn)直接對(duì)標(biāo)Python原生的感覺(jué)。這也是為什么我強(qiáng)烈建議新手從2.0開(kāi)始學(xué)——不用理解計(jì)算圖那一套抽象概念寫(xiě)起來(lái)跟寫(xiě)普通Python代碼一樣自然。2.2 Keras到底是啥和TensorFlow什么關(guān)系Keras最初是一個(gè)獨(dú)立的高層神經(jīng)網(wǎng)絡(luò)API庫(kù)后來(lái)被Google收編整合進(jìn)了TensorFlow的tf.keras模塊?,F(xiàn)在你import tensorflow as tf之后直接寫(xiě)tf.keras就是官方推薦的建模方式。Keras的價(jià)值在于把模型搭建的代碼量壓縮到一個(gè)極其夸張的程度。你用底層API寫(xiě)一個(gè)卷積神經(jīng)網(wǎng)絡(luò)可能要上百行代碼用Keras只需要十幾行。我經(jīng)常跟朋友開(kāi)玩笑Keras像是深度學(xué)習(xí)框架里的“傻瓜相機(jī)”按鍵少、出片快但拍出的照片質(zhì)量一點(diǎn)都不差。Keras在TensorFlow里提供了三種建模方式API類(lèi)型適用場(chǎng)景代碼風(fēng)格Sequential順序模型簡(jiǎn)單線(xiàn)性堆疊網(wǎng)絡(luò)清晰入門(mén)首選Functional函數(shù)式模型多輸入、多輸出、共享層、殘差結(jié)構(gòu)靈活真實(shí)項(xiàng)目主力Subclassing子類(lèi)化模型完全自定義訓(xùn)練邏輯最自由也最復(fù)雜入門(mén)階段先把Sequential玩熟它就像拼積木——一層接一層。之后做復(fù)雜項(xiàng)目再過(guò)渡到Functional它像是搭電路板可以從任意位置分叉、合并。2.3 為什么入門(mén)選TensorFlow而不是PyTorch這是個(gè)好問(wèn)題也是社區(qū)吵了很多年的話(huà)題。我的觀點(diǎn)一直很明確入門(mén)選TensorFlow/Keras進(jìn)階再看PyTorch。原因有三第一Keras的API設(shè)計(jì)真的為新手考慮過(guò)。model.add()、model.compile()、model.fit()這三個(gè)方法讀起來(lái)就是自然語(yǔ)言哪怕你不懂原理也能把模型跑起來(lái)。PyTorch更靈活但靈活的代名詞就是“需要你自己做更多決定”。第二TensorFlow生態(tài)里的配套工具完整。TensorFlow Serving做部署、TensorBoard做可視化、TensorFlow Lite做移動(dòng)端——從訓(xùn)練到上線(xiàn)是一條龍閉環(huán)。你學(xué)完一個(gè)框架后面部署時(shí)不用再換技術(shù)棧。第三中文資料海量。搜索一切TensorFlow報(bào)錯(cuò)基本都能找到前人踩坑記錄。這點(diǎn)實(shí)際用起來(lái)極其重要。但說(shuō)句公道話(huà)如果你是搞科研或者想做學(xué)術(shù)論文復(fù)現(xiàn)PyTorch確實(shí)更方便。這兩者不是死對(duì)頭入門(mén)用Keras建立直覺(jué)后期工作需要再補(bǔ)PyTorch時(shí)間成本完全可控。3. 第一個(gè)實(shí)戰(zhàn)項(xiàng)目CIFAR-10圖像分類(lèi)3.1 項(xiàng)目目標(biāo)與數(shù)據(jù)準(zhǔn)備紙上談兵終覺(jué)淺我?guī)阕鲆粋€(gè)非常經(jīng)典的入門(mén)項(xiàng)目CIFAR-10圖像分類(lèi)。這也是跑通深度學(xué)習(xí)流程的必修課理解了它后面的項(xiàng)目都是同一套邏輯換皮。CIFAR-10數(shù)據(jù)集包含6萬(wàn)張32x32的彩色圖片分為10個(gè)類(lèi)別飛機(jī)、汽車(chē)、鳥(niǎo)、貓、鹿、狗、青蛙、馬、船、卡車(chē)。TensorFlow里直接內(nèi)置了數(shù)據(jù)集不用去網(wǎng)上下載一行代碼的事import tensorflow as tf from tensorflow.keras import datasets, layers, models (train_images, train_labels), (test_images, test_labels) datasets.cifar10.load_data()打印一下數(shù)據(jù)shape你會(huì)看到print(train_images.shape) # (50000, 32, 32, 3) 五萬(wàn)張32x32的三通道彩色圖 print(train_labels.shape) # (50000, 1) 標(biāo)簽是整數(shù)選擇CIFAR-10的原因很現(xiàn)實(shí)① 圖片尺寸小CPU也能快速迭代② 類(lèi)別豐富模型能學(xué)到真正的圖像特征③ 是行業(yè)內(nèi)標(biāo)準(zhǔn)benchmark網(wǎng)上相關(guān)經(jīng)驗(yàn)多報(bào)錯(cuò)了能查到答案。3.2 數(shù)據(jù)預(yù)處理歸一化不能省正式建模前通常要做一步歸一化把像素值從0~255壓縮到0~1之間。train_images, test_images train_images / 255.0, test_images / 255.0為什么必須做這一步因?yàn)樯窠?jīng)網(wǎng)絡(luò)對(duì)輸入數(shù)據(jù)的尺度非常敏感。你想啊像素值范圍0~255如果網(wǎng)絡(luò)某層的權(quán)重初始值在0附近輸入直接乘以255梯度很可能爆炸或消失。類(lèi)比到生活里你給一個(gè)人布置任務(wù)單位是“元”還是“萬(wàn)元”他對(duì)“100”這個(gè)數(shù)字的感知是完全不同的。歸一化就是把所有數(shù)據(jù)都放到同一個(gè)“感知尺度”上讓模型更容易學(xué)習(xí)。這里還有一個(gè)細(xì)節(jié)CIFAR-10的標(biāo)簽是整數(shù)不是one-hot編碼。后面選損失函數(shù)時(shí)必須用SparseCategoricalCrossentropy如果用了CategoricalCrossentropy就會(huì)報(bào)維度不匹配的錯(cuò)。這個(gè)問(wèn)題我見(jiàn)過(guò)太多人踩了。3.3 用Keras搭建卷積神經(jīng)網(wǎng)絡(luò)CNN這是項(xiàng)目的核心模塊。我用Keras Sequential API搭建了一個(gè)經(jīng)典的卷積網(wǎng)絡(luò)結(jié)構(gòu)model models.Sequential([ # 第一層卷積32個(gè)3x3卷積核提取圖像局部特征 layers.Conv2D(32, (3, 3), activationrelu, input_shape(32, 32, 3)), # 池化層壓縮特征圖尺寸減少計(jì)算量防止過(guò)擬合 layers.MaxPooling2D((2, 2)), # 第二層卷積64個(gè)卷積核學(xué)習(xí)更高級(jí)的特征 layers.Conv2D(64, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), # 第三層卷積繼續(xù)加深特征提取 layers.Conv2D(64, (3, 3), activationrelu), # 把二維特征圖拉平接全連接層 layers.Flatten(), # 全連接層做特征整合 layers.Dense(64, activationrelu), # 輸出層10個(gè)神經(jīng)元對(duì)應(yīng)10個(gè)類(lèi)別不帶softmax layers.Dense(10) ])用生活化類(lèi)比解釋這些層Conv2D卷積層像人眼聚焦看圖先識(shí)別邊緣、紋理等小局部特征。32和64是“眼睛”的數(shù)量越多看得越細(xì)但計(jì)算量也越大。MaxPooling2D池化層像圖片壓縮軟件把2x2的區(qū)域取最大值尺寸縮小一半。既減少計(jì)算量又保留主要特征還能防止模型對(duì)位置過(guò)度敏感。Flatten展平層把多維特征圖拉成一維向量因?yàn)楹竺娴娜B接層只接受一維輸入。Dense全連接層把前面提取的特征進(jìn)行綜合打分每個(gè)神經(jīng)元學(xué)到一個(gè)類(lèi)別的“投票權(quán)”。input_shape(32, 32, 3)只在第一層指定后面層會(huì)自動(dòng)推導(dǎo)輸入維度。激活函數(shù)選了relu它是目前圖像識(shí)別領(lǐng)域最常用的激活函數(shù)計(jì)算簡(jiǎn)單、能有效緩解梯度消失。輸出層故意不激活因?yàn)楹竺鎿p失函數(shù)里帶了from_logitsTrue參數(shù)它會(huì)在內(nèi)部自行處理softmax這個(gè)寫(xiě)法數(shù)值穩(wěn)定性更好。3.4 編譯模型優(yōu)化器、損失函數(shù)、評(píng)估指標(biāo)模型結(jié)構(gòu)定義好了還要“編譯”才能訓(xùn)練。編譯是配置學(xué)習(xí)過(guò)程的三個(gè)關(guān)鍵參數(shù)model.compile(optimizeradam, losstf.keras.losses.SparseCategoricalCrossentropy(from_logitsTrue), metrics[accuracy])optimizer優(yōu)化器adam是目前最流行的自適應(yīng)學(xué)習(xí)率優(yōu)化器。簡(jiǎn)單說(shuō)它負(fù)責(zé)回答“怎么調(diào)整權(quán)重讓損失變小”。選adam的原因在于它幾乎不需要調(diào)參默認(rèn)學(xué)習(xí)率就能在大多數(shù)問(wèn)題上表現(xiàn)良好對(duì)新手極其友好。loss損失函數(shù)評(píng)估模型預(yù)測(cè)和真實(shí)標(biāo)簽的差距是模型優(yōu)化的目標(biāo)。因?yàn)槲覀兊臉?biāo)簽是整數(shù)索引不是one-hot向量所以選SparseCategoricalCrossentropy。metrics評(píng)估指標(biāo)在訓(xùn)練過(guò)程中我們觀察什么指標(biāo)。分類(lèi)任務(wù)一般看accuracy就是準(zhǔn)確率。我一直強(qiáng)調(diào)初學(xué)者不用糾結(jié)優(yōu)化器的數(shù)學(xué)原理先把a(bǔ)dam當(dāng)成默認(rèn)選項(xiàng)跑通流程后有興趣再去深入研究。就像開(kāi)車(chē)新手先學(xué)會(huì)踩油門(mén)剎車(chē)發(fā)動(dòng)機(jī)原理可以以后慢慢學(xué)。3.5 訓(xùn)練模型fit函數(shù)和回調(diào)機(jī)制編譯完成后一行代碼就能啟動(dòng)訓(xùn)練history model.fit(train_images, train_labels, epochs15, batch_size64, validation_data(test_images, test_labels))參數(shù)解釋epochs15模型要把整個(gè)訓(xùn)練集看15遍。每看完一遍權(quán)重更新一次理論上看得越多學(xué)得越好但太多會(huì)過(guò)擬合。batch_size64每次取64張圖片計(jì)算一次梯度。如果電腦內(nèi)存不夠調(diào)小這個(gè)值可以緩解內(nèi)存壓力比如32或16。validation_data每訓(xùn)練完一個(gè)epoch在測(cè)試集上驗(yàn)證一次。通過(guò)驗(yàn)證集準(zhǔn)確率判斷模型是“真學(xué)會(huì)了”還是“把訓(xùn)練集背下來(lái)了”。我在實(shí)際訓(xùn)練時(shí)通常會(huì)加兩個(gè)回調(diào)函數(shù)來(lái)控制過(guò)程from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience3, restore_best_weightsTrue), ModelCheckpoint(best_model.h5, monitorval_loss, save_best_onlyTrue) ] history model.fit(train_images, train_labels, epochs15, batch_size64, validation_data(test_images, test_labels), callbackscallbacks)EarlyStopping的作用是當(dāng)驗(yàn)證集損失連續(xù)3個(gè)epoch不再下降就自動(dòng)停止訓(xùn)練防止過(guò)擬合也節(jié)省時(shí)間。ModelCheckpoint會(huì)在每輪結(jié)束后保存驗(yàn)證集表現(xiàn)最好的模型權(quán)重這樣即使后面訓(xùn)練跑偏了我們還能回滾到最優(yōu)版本。訓(xùn)練開(kāi)始后你會(huì)看到類(lèi)似這樣的輸出Epoch 1/15 782/782 [] - 8s 10ms/step - loss: 1.4103 - accuracy: 0.4856 - val_loss: 1.2632 - val_accuracy: 0.5427前幾個(gè)epoch準(zhǔn)確率上升很快這是正常的。隨著訓(xùn)練進(jìn)行你會(huì)看到訓(xùn)練準(zhǔn)確率和驗(yàn)證準(zhǔn)確率的差距逐漸拉大——如果差距過(guò)大說(shuō)明開(kāi)始過(guò)擬合了這時(shí)候就該用EarlyStopping的耐心參數(shù)來(lái)控制。訓(xùn)練結(jié)束后用測(cè)試集評(píng)估一下最終效果test_loss, test_acc model.evaluate(test_images, test_labels, verbose2) print(test_acc)在CPU上訓(xùn)練15個(gè)epoch大概需要幾分鐘到十幾分鐘第一次跑通看到80%以上的準(zhǔn)確率恭喜你深度學(xué)習(xí)第一只腳已經(jīng)踏進(jìn)去了。順便說(shuō)一句能用自己寫(xiě)的代碼在圖像分類(lèi)上達(dá)到這個(gè)水平說(shuō)明你已經(jīng)掌握了數(shù)據(jù)加載、模型搭建、訓(xùn)練評(píng)估這套核心閉環(huán)。3.6 模型保存與加載不能只活在內(nèi)存里訓(xùn)練出來(lái)的模型如果只在當(dāng)前會(huì)話(huà)里能用那價(jià)值大打折扣。保存和加載模型是項(xiàng)目落地的最后一環(huán)兩種常見(jiàn)方式# 方式一保存整個(gè)模型包括結(jié)構(gòu)和權(quán)重 model.save(my_cifar10_model) # 方式二只保存權(quán)重需要配合代碼重新構(gòu)建模型結(jié)構(gòu) model.save_weights(my_cifar10_weights.h5)加載整個(gè)模型from tensorflow.keras.models import load_model restored_model load_model(my_cifar10_model) predictions restored_model.predict(test_images[:10])完整的模型文件除了權(quán)重還包含結(jié)構(gòu)定義、編譯配置和優(yōu)化器狀態(tài)。注意整個(gè)模型保存是TensorFlow 2.x推薦的默認(rèn)方式只保存權(quán)重適合你想改模型結(jié)構(gòu)再加載舊權(quán)重的場(chǎng)景。model.save保存出來(lái)的是一個(gè)文件夾里面包含assets、variables和saved_model.pb等文件。這種格式叫SavedModel是TensorFlow官方推薦的模型交換格式后續(xù)部署到服務(wù)端或移動(dòng)端都用它。4. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄4.1 環(huán)境配置與安裝類(lèi)問(wèn)題問(wèn)題描述大概率原因解決方案ImportError: DLL load failedPython版本不兼容或依賴(lài)庫(kù)沖突重建虛擬環(huán)境嚴(yán)格按版本表安裝ModuleNotFoundError: No module named tensorflow安裝到了別的環(huán)境檢查which python確認(rèn)當(dāng)前解釋器pip安裝速度極慢默認(rèn)下載源在國(guó)外使用國(guó)內(nèi)鏡像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow安裝TensorFlow后NumPy版本沖突TensorFlow對(duì)NumPy有版本范圍要求在虛擬環(huán)境里執(zhí)行pip install numpy1.23.5Pip明明是裝了TensorFlow但import報(bào)錯(cuò)這種問(wèn)題我在帶新人時(shí)見(jiàn)得太多了。十次有八次是環(huán)境沒(méi)對(duì)——你以為是tf2環(huán)境里裝的實(shí)際pip指向的是全局Python。排查方法很簡(jiǎn)單在終端里先執(zhí)行conda activate tf2再輸入which python確認(rèn)路徑是在~/anaconda3/envs/tf2/bin/python。一切檢查完再裝包能省去一半的玄學(xué)問(wèn)題。4.2 訓(xùn)練過(guò)程中的經(jīng)典報(bào)錯(cuò)問(wèn)題1Loss變成了NaN訓(xùn)練剛開(kāi)始還正常跑著跑著loss變成NaN準(zhǔn)確率跟著變沒(méi)。這種情況大概率是學(xué)習(xí)率太大導(dǎo)致梯度爆炸。解決思路# 用更小的學(xué)習(xí)率重新編譯 model.compile(optimizertf.keras.optimizers.Adam(learning_rate0.0001), losstf.keras.losses.SparseCategoricalCrossentropy(from_logitsTrue), metrics[accuracy])還有可能是數(shù)據(jù)問(wèn)題如果輸入數(shù)據(jù)里有NaN值也會(huì)傳導(dǎo)到loss。檢查一下數(shù)據(jù)預(yù)處理環(huán)節(jié)有沒(méi)有除以0或未處理的缺失值。問(wèn)題2訓(xùn)練集準(zhǔn)確率98%測(cè)試集只有75%這是典型的過(guò)擬合。模型把訓(xùn)練集“背”下來(lái)了沒(méi)學(xué)會(huì)泛化。我的處理手段按優(yōu)先級(jí)排序加Dropout層隨機(jī)丟棄部分神經(jīng)元逼迫模型不依賴(lài)特定路徑。早停EarlyStopping在第3.5節(jié)已經(jīng)演示過(guò)。數(shù)據(jù)增強(qiáng)比如隨機(jī)翻轉(zhuǎn)、裁剪圖片增加訓(xùn)練樣本多樣性。數(shù)據(jù)增強(qiáng)是圖像任務(wù)里對(duì)抗過(guò)擬合的神器from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen ImageDataGenerator( rotation_range15, width_shift_range0.1, height_shift_range0.1, horizontal_flipTrue ) datagen.fit(train_images) model.fit(datagen.flow(train_images, train_labels, batch_size64), epochs15, validation_data(test_images, test_labels))問(wèn)題3訓(xùn)練速度慢且CPU占用低如果你用的是CPU訓(xùn)練發(fā)現(xiàn)CPU占用率不到50%甚至10%大概率是數(shù)據(jù)加載成了瓶頸——模型在等數(shù)據(jù)GPU或CPU在摸魚(yú)。解決方案是使用tf.data管道并啟用預(yù)取train_ds tf.data.Dataset.from_tensor_slices((train_images, train_labels)) train_ds train_ds.shuffle(1000).batch(64).prefetch(tf.data.AUTOTUNE)prefetch會(huì)把數(shù)據(jù)提前加載到內(nèi)存里讓計(jì)算和數(shù)據(jù)加載流水線(xiàn)并行工作。加了這一句訓(xùn)練速度能提升很多。問(wèn)題4GPU安裝了但是TensorFlow用不上這個(gè)坑我當(dāng)年跳得最深。nvidia-smi能看到顯卡tf.config.list_physical_devices(GPU)卻是空列表。原因基本就是CUDA和cuDNN版本跟TensorFlow的構(gòu)建要求不匹配。TensorFlow 2.10對(duì)應(yīng)的CUDA版本是11.8cuDNN是8.6。裝的時(shí)候按官方那套來(lái)順序是先裝NVIDIA驅(qū)動(dòng)再裝CUDA Toolkit最后放cuDNN文件。注意核對(duì)CUDA啟動(dòng)環(huán)境變量。再說(shuō)一次我的建議入門(mén)別配這玩意等模型規(guī)模真的需要GPU加速了再按官方文檔從上到下過(guò)一遍成功率會(huì)高很多。4.3 一個(gè)隱藏的“殺手”Keras版本和TensorFlow版本不匹配有些老教程還在教你from keras import ...如果你裝了TensorFlow之后還單獨(dú)裝過(guò)keras包很可能代碼走的是獨(dú)立Keras而不是TensorFlow內(nèi)置的Keras。這兩個(gè)庫(kù)的API存在版本差異輕則警告重則模型跑不起來(lái)。判斷方法import keras print(keras.__version__)如果你用的是TensorFlow集成的Keras應(yīng)該這樣做from tensorflow import keras print(keras.__version__)從第一行代碼就養(yǎng)成from tensorflow.keras import ...的習(xí)慣能避免一大類(lèi)問(wèn)題。5. 學(xué)完這個(gè)項(xiàng)目后往哪走5.1 把項(xiàng)目換湯不換藥地?cái)U(kuò)展CIFAR-10圖像分類(lèi)跑通后你已經(jīng)掌握了深度學(xué)習(xí)的標(biāo)準(zhǔn)流程。接下來(lái)從這個(gè)項(xiàng)目出發(fā)可以做的擴(kuò)展非常多。搜熱詞里看到的幾個(gè)方向其實(shí)都是同一套流程的變體基于深度學(xué)習(xí)的口腔疾病圖像識(shí)別系統(tǒng)本質(zhì)是醫(yī)學(xué)圖像分類(lèi)把CIFAR-10里的貓狗換成各種口腔病變成像一般會(huì)加更嚴(yán)格的數(shù)據(jù)增強(qiáng)處理類(lèi)別不均衡問(wèn)題。難點(diǎn)在數(shù)據(jù)標(biāo)注和質(zhì)量模型結(jié)構(gòu)跟我們的CNN一致主流會(huì)換ResNet這類(lèi)更深的骨架網(wǎng)絡(luò)。CNN識(shí)別惡意軟件把惡意軟件的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成圖像再用CNN判斷是良性還是惡意。小區(qū)分在輸入處理階段但核心思路仍然是“圖像分類(lèi)”。利用Transformer進(jìn)行回歸的案例圖像分類(lèi)學(xué)的是模型輸出類(lèi)別回歸任務(wù)是模型輸出連續(xù)數(shù)值。比如用時(shí)間序列預(yù)測(cè)股價(jià)、銷(xiāo)量。Transformer結(jié)構(gòu)跟CNN完全不同但如果理解了Keras的建模流程你會(huì)發(fā)現(xiàn)在compile時(shí)把損失函數(shù)換成MSE就好網(wǎng)絡(luò)結(jié)構(gòu)單獨(dú)再學(xué)。我從這些熱詞里挑選過(guò)研究選題發(fā)現(xiàn)最有價(jià)值的不是搜到教程而是意識(shí)到搜索引擎里高熱度的問(wèn)題往往都是大眾卡殼最嚴(yán)重的地方把這些痛點(diǎn)解決了你在這個(gè)領(lǐng)域的技術(shù)敏感度就上來(lái)了。5.2 接下來(lái)按什么順序?qū)W我給新手建議的學(xué)習(xí)路徑是這樣的鞏固數(shù)據(jù)預(yù)處理和tf.data管道真實(shí)項(xiàng)目的數(shù)據(jù)是臟的對(duì)這部分能節(jié)省大量時(shí)間。學(xué)Functional API做殘差網(wǎng)絡(luò)、多模態(tài)輸入輸出時(shí)要頻繁用它。掌握TensorBoard可視化用瀏覽器看訓(xùn)練曲線(xiàn)理解模型訓(xùn)練過(guò)程。嘗試遷移學(xué)習(xí)用預(yù)訓(xùn)練的ResNet50、MobileNet直接在自定義數(shù)據(jù)集上微調(diào)。這是目前工業(yè)界最常用也最有效的方法。深入一個(gè)方向文本處理選Transformer圖像分割選U-Net時(shí)間序列選LSTM或Transformer。我說(shuō)得直白一點(diǎn)入門(mén)之后區(qū)分普通人和高手的分水嶺一個(gè)是能不能處理好自己的數(shù)據(jù)另一個(gè)是會(huì)不會(huì)用遷移學(xué)習(xí)快速解決問(wèn)題。5.3 資源推薦現(xiàn)在資料多得泛濫反而不知道該看哪些。我推薦的標(biāo)準(zhǔn)是“少而精配代碼”《動(dòng)手學(xué)深度學(xué)習(xí)》李沐等人免費(fèi)在線(xiàn)閱讀有PyTorch和TensorFlow兩個(gè)版本代碼理論代碼結(jié)合做得很好。TensorFlow官網(wǎng)教程入門(mén)教程質(zhì)量很高而且會(huì)隨著版本更新持續(xù)維護(hù)。Keras官方示例庫(kù)結(jié)構(gòu)清晰覆蓋從圖像到文本幾乎所有任務(wù)。直接把代碼拿下來(lái)改一改就是你的項(xiàng)目。那些所謂的“深度學(xué)習(xí)課本pdf”我勸你別花時(shí)間去搜了。教材的重點(diǎn)是理論推導(dǎo)你現(xiàn)階段最需要的是“跑起來(lái)的感覺(jué)”是那種“我終于把模型訓(xùn)練成功”的正反饋。等代碼跑順了再回頭補(bǔ)理論理解會(huì)深刻得多。照例說(shuō)點(diǎn)實(shí)際體會(huì)。每次帶人入門(mén)我都有一種感覺(jué)讓新手放棄的往往不是算法難度而是環(huán)境的挫敗感和“不知道怎么回事就跑通了”的虛無(wú)感。這篇文章從環(huán)境一步步走到實(shí)戰(zhàn)就是想幫你把這兩座大山都搬走。你照著敲一遍遇到報(bào)錯(cuò)別慌先看錯(cuò)誤信息里有沒(méi)有ModuleNotFoundError、ValueError這些關(guān)鍵詞再對(duì)照第4章的表格查。跑通模型的那一刻你會(huì)發(fā)現(xiàn)深度學(xué)習(xí)沒(méi)那么玄乎——它就是你用Python按照一套固定的流程讓機(jī)器從數(shù)據(jù)里找出規(guī)律而已。這套流程跑熟了后面所有模型對(duì)你來(lái)說(shuō)都只是一個(gè)新結(jié)構(gòu)、新數(shù)據(jù)的組合游戲。