
簡介本資源是一個基于卷積神經網絡CNN實現的貓狗圖像識別Web應用完整工程包面向深度學習初學者與Web部署實踐者解決圖像分類模型訓練、封裝與本地化部署的一體化學習需求。資源共218個文件涵蓋44個Python源碼含模型構建、訓練腳本與Flask/Django后端邏輯、14個JPG與13個PNG測試圖片、18個可執行文件用于快速啟動服務、以及pyd/pyc等編譯模塊和HTML/CSS前端頁面整體壓縮包大小為22.84MB結構清晰兼顧模型開發與工程落地環節。已有122人學習下載適合希望從零復現CNN圖像分類項目、理解Web接口對接圖像推理流程、并掌握模型輕量化部署技巧的學習者。資源包含完整目錄組織、環境激活腳本activate.bat、服務啟動工具launch_instance、bundle_image等及配套配置文件可直接運行調試大幅降低入門門檻。1. 為什么一個貓狗識別 Web 應用必須從卷積神經網絡CNN開始講起你上傳的基于卷積神經網絡的貓狗識別 web 應用.zip不是一個普通的學生作業壓縮包——它是一條完整的技術鏈路切片前端圖像上傳 → 后端模型加載與推理 → 返回結構化分類結果 → 頁面實時渲染。真正決定這個應用能否“認得準、跑得穩、上線快”的不是 HTML 寫得多漂亮而是 CNN 模型在部署側是否適配 Web 場景。很多初學者直接拿 PyTorch 訓練好的.pth文件扔進 Flask結果遇到CUDA out of memory、tensor size mismatch或model.eval() 后仍報 grad_fn等錯誤本質是沒理解 CNN 在 Web 環境下的三重約束輸入尺寸固定性、推理無梯度性、序列化兼容性。本篇不復現訓練過程只聚焦“如何把已訓練好的 CNN 模型安全、低延遲、可維護地嵌入 Web 工程”。適用對象包括剛跑通 Jupyter Notebook 分類實驗、正卡在模型導出環節的 Python 初學者需要快速交付演示系統、但不想重寫前端邏輯的后端開發者以及正在評估 Django/Flask/FastAPI 選型、關注模型服務化路徑的全棧工程師。2. 用 PyTorch 導出 ONNX 模型解決 Web 部署中 CNN 的跨框架兼容問題Web 應用后端通常運行在 CPU 環境且需兼顧不同部署目標Docker 容器、輕量云函數、甚至邊緣設備直接加載.pth文件會引入 PyTorch 運行時依賴增大鏡像體積并增加安全審計負擔。ONNXOpen Neural Network Exchange作為工業級中間表示格式能將 CNN 模型從 PyTorch 解耦交由更輕量的推理引擎如 ONNX Runtime執行。這步不是可選項而是 Web 工程化落地的第一道門檻。2.1 確認原始 CNN 模型結構與輸入規范假設你的cat_dog_cnn.py中定義了標準 CNN 架構含 Conv2d、ReLU、MaxPool2d、AdaptiveAvgPool2d 和 Linear 層且訓練時使用torchvision.transforms.Compose對圖像做如下預處理transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])注意Resize尺寸必須與模型forward()接收的 tensor shape 嚴格一致。若模型最后一層Linear(in_features25088, out_features2)對應224×224輸入則導出時dummy_input必須為torch.randn(1, 3, 224, 224)。尺寸不匹配會導致 ONNX 推理時input shape mismatch錯誤。2.2 使用 torch.onnx.export 導出可部署模型以下代碼需在訓練環境PyTorch ≥1.12中執行生成cat_dog_cnn.onnximport torch import torch.onnx from cat_dog_cnn import CatDogCNN # 替換為你的模型類名 # 1. 加載訓練好的權重 model CatDogCNN(num_classes2) model.load_state_dict(torch.load(best_model.pth, map_locationcpu)) model.eval() # 關鍵必須設為 eval 模式否則 BatchNorm/ Dropout 行為異常 # 2. 構造 dummy input必須與訓練時 transform 輸出 shape 一致 dummy_input torch.randn(1, 3, 224, 224) # batch1, channel3, h224, w224 # 3. 導出 ONNX關鍵參數說明見下文 torch.onnx.export( model, dummy_input, cat_dog_cnn.onnx, export_paramsTrue, # 保存模型權重 opset_version12, # ONNX opset 版本12 兼容性最佳支持 PyTorch 1.10 do_constant_foldingTrue, # 優化常量計算 input_names[input], # 輸入張量名稱供 ONNX Runtime 調用時引用 output_names[output], # 輸出張量名稱 dynamic_axes{ input: {0: batch_size}, output: {0: batch_size} } # 支持動態 batch sizeWeb 請求可變長 )參數說明與常見失敗點map_locationcpu強制加載到 CPU避免導出時因 GPU 設備不可用報錯opset_version12低于 11 則部分 PyTorch 算子如adaptive_avg_pool2d無法映射高于 15 可能導致舊版 ONNX Runtime 不兼容dynamic_axes聲明batch_size為動態維度使 Web 接口可接收單圖或批量圖請求否則默認靜態 batch1多圖請求會 crash若報錯Unsupported ONNX opset version檢查torch.onnx.supported_opset_version并降級opset_version若輸出output維度為[1, 2]但 Web 調用返回softmax值異常需確認模型forward()是否已包含nn.Softmax(dim1)——推薦移除該層由 Web 后端統一做 softmax便于調試和閾值控制。2.3 驗證 ONNX 模型正確性導出后必須驗證前向一致性避免量化或算子轉換引入偏差import onnxruntime as ort import numpy as np # 加載 ONNX 模型 ort_session ort.InferenceSession(cat_dog_cnn.onnx) # 構造與 dummy_input 相同的 numpy 輸入注意 dtype 和 layout test_input np.random.randn(1, 3, 224, 224).astype(np.float32) # 執行推理 outputs ort_session.run(None, {input: test_input}) pred_onnx outputs[0] # shape: (1, 2) # 對比 PyTorch 原始輸出需確保 model.eval() 且無 dropout with torch.no_grad(): pred_torch model(torch.from_numpy(test_input)).numpy() print(ONNX vs PyTorch max diff:, np.max(np.abs(pred_onnx - pred_torch))) # 輸出應 1e-5否則檢查模型是否含非 ONNX 支持算子如 torch.nn.functional.interpolate modebicubic3. 用 FastAPI 構建輕量 Web 接口讓 CNN 模型真正響應 HTTP 請求Flask 因其同步阻塞特性在高并發圖像請求下易成為瓶頸而 FastAPI 基于 Starlette 和 Pydantic原生支持異步 I/O 與類型校驗配合 ONNX Runtime 的線程安全推理能穩定支撐每秒 20 次貓狗識別請求。本節構建最小可行 Web 工程不含前端頁面僅提供/predict接口。3.1 初始化項目結構與依賴管理創建目錄結構catdog_web/ ├── main.py # FastAPI 入口 ├── model/ # 存放 ONNX 模型與標簽文件 │ ├── cat_dog_cnn.onnx │ └── labels.txt # 每行一個類別cat\n dog ├── requirements.txt └── Dockerfilerequirements.txt內容精簡無冗余fastapi0.115.0 uvicorn0.32.0 onnxruntime1.19.2 Pillow10.4.0 python-multipart0.0.19提示onnxruntime優先安裝 CPU 版本onnxruntime而非onnxruntime-gpu。Web 容器通常無 CUDA 環境GPU 版本會因找不到libcudart.so啟動失敗。3.2 編寫 FastAPI 主服務main.pyfrom fastapi import FastAPI, File, UploadFile, HTTPException from fastapi.responses import JSONResponse import numpy as np from PIL import Image import io import onnxruntime as ort # 1. 初始化 ONNX Runtime 推理會話全局單例避免重復加載 ort_session ort.InferenceSession(./model/cat_dog_cnn.onnx, providers[CPUExecutionProvider]) # 2. 加載類別標簽 with open(./model/labels.txt, r) as f: labels [line.strip() for line in f.readlines()] app FastAPI(titleCat-Dog CNN Web API, description基于卷積神經網絡的貓狗二分類服務) app.post(/predict) async def predict_image(file: UploadFile File(...)): try: # 3. 讀取并校驗圖像格式 contents await file.read() image Image.open(io.BytesIO(contents)).convert(RGB) # 強制轉 RGB避免 RGBA 報錯 # 4. 圖像預處理與訓練時 transform 完全一致 # Resize → ToTensor → Normalize手動實現不依賴 torchvision image image.resize((224, 224), Image.BILINEAR) img_array np.array(image).astype(np.float32) / 255.0 # 歸一化到 [0,1] img_array img_array.transpose(2, 0, 1) # HWC → CHW mean np.array([0.485, 0.456, 0.406]).reshape(3, 1, 1) std np.array([0.229, 0.224, 0.225]).reshape(3, 1, 1) img_array (img_array - mean) / std # 標準化 # 5. 添加 batch 維度并轉為 float32 input_tensor np.expand_dims(img_array, axis0) # shape: (1, 3, 224, 224) # 6. 執行 ONNX 推理 outputs ort_session.run(None, {input: input_tensor}) logits outputs[0].flatten() # shape: (2,) # 7. 計算 softmax 概率不在模型內做此處顯式計算 exp_logits np.exp(logits) probs exp_logits / np.sum(exp_logits) # 8. 構建響應 result { prediction: labels[np.argmax(probs)], confidence: float(np.max(probs)), probabilities: { labels[0]: float(probs[0]), labels[1]: float(probs[1]) } } return JSONResponse(contentresult) except Exception as e: raise HTTPException(status_code400, detailfImage processing failed: {str(e)})關鍵設計說明providers[CPUExecutionProvider]顯式指定 CPU 執行器避免 ONNX Runtime 自動嘗試 GPU 導致啟動失敗image.convert(RGB)強制轉換解決 PNG 透明通道RGBA導致np.array()產生 4 通道報錯預處理完全復現訓練流程resize插值方式用BILINEAR與transforms.Resize默認一致/255.0歸一化transpose調整通道順序mean/std標準化 ——任何一步偏差都會導致準確率斷崖式下降np.expand_dims(..., axis0)添加 batch 維度匹配 ONNX 模型input的dynamic_axes定義JSONResponse直接返回結構化數據前端可直接消費無需額外解析。3.3 啟動服務并測試接口終端執行uvicorn main:app --host 0.0.0.0 --port 8000 --reload用 curl 測試替換test.jpg為真實貓或狗圖片curl -X POST http://localhost:8000/predict \ -H accept: application/json \ -F filetest.jpg預期返回{ prediction: dog, confidence: 0.924, probabilities: { cat: 0.076, dog: 0.924 } }4. 構建 Docker 容器實現 CNN Web 應用的一鍵部署與環境隔離本地能跑不等于生產可用。Web 應用需在無 Python 環境的服務器上啟動且依賴版本必須鎖定。Docker 提供標準化打包方案將 ONNX 模型、FastAPI 服務、ONNX Runtime 及所有依賴打包為單一鏡像消除“在我機器上能跑”的陷阱。4.1 編寫生產級 Dockerfile# 使用官方 Python 基礎鏡像精簡版 FROM python:3.9-slim # 設置工作目錄 WORKDIR /app # 復制依賴文件并安裝分層緩存優化 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 復制應用代碼與模型文件 COPY . . # 暴露端口 EXPOSE 8000 # 啟動命令使用 uvicorn 生產配置 CMD [uvicorn, main:app, --host, 0.0.0.0:8000, --port, 8000, --workers, 4, --limit-concurrency, 10]構建與運行命令# 構建鏡像tag 名體現模型版本 docker build -t catdog-cnn-web:v1.0 . # 運行容器映射本地 8000 端口 docker run -p 8000:8000 --name catdog-api catdog-cnn-web:v1.0 # 查看日志確認啟動成功 docker logs catdog-api # 輸出應含 Uvicorn running on http://0.0.0.0:8000注意--workers 4設置 Uvicorn 工作進程數建議設為 CPU 核心數--limit-concurrency 10限制每個 worker 并發請求數防止 ONNX Runtime 線程爭搶導致內存溢出。4.2 驗證容器內模型推理穩定性進入容器執行診斷命令docker exec -it catdog-api bash # 在容器內手動測試 ONNX 推理延遲 python -c import time import numpy as np import onnxruntime as ort sess ort.InferenceSession(./model/cat_dog_cnn.onnx, providers[CPUExecutionProvider]) x np.random.randn(1,3,224,224).astype(np.float32) start time.time() for _ in range(10): sess.run(None, {input: x}) print(fAvg latency: {(time.time()-start)/10*1000:.2f} ms) 健康指標單次推理延遲應穩定在15–35msIntel i5 CPU。若 100ms檢查是否誤裝onnxruntime-gpu或模型未啟用CPUExecutionProvider。4.3 容器化后的 Web 工程優勢對比表維度本地直接運行Python 腳本Docker 容器部署環境一致性依賴宿主機 Python/庫版本鏡像內固化所有依賴100% 一致啟動可靠性ImportError風險高構建階段即驗證依賴完整性資源隔離與其他進程共享內存/CPU可通過--memory512m限容擴展性手動啟多個進程管理復雜docker-compose scale一鍵擴縮安全審計無明確依賴清單docker scan自動生成 SBOM5. 前端集成與性能調優讓貓狗識別在瀏覽器中真正“秒響應”FastAPI 提供了后端能力但用戶需要一個可上傳、可查看結果的界面。本節不寫完整前端工程而是給出可直接嵌入現有網頁的最小 HTML JavaScript 方案并解決 Web 場景下 CNN 推理的兩個核心瓶頸圖像上傳體積過大和HTTP 請求超時。5.1 構建零依賴前端頁面index.html!DOCTYPE html html head titleCat-Dog CNN Classifier/title style .upload-area { border: 2px dashed #ccc; padding: 20px; text-align: center; margin: 10px 0; } .result { margin-top: 20px; font-size: 18px; font-weight: bold; } .spinner { border: 3px solid #f3f3f3; border-top: 3px solid #007bff; border-radius: 50%; width: 20px; height: 20px; animation: spin 1s linear infinite; display: inline-block; margin-right: 10px; } keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /style /head body h1 貓狗識別 Web 應用/h1 div classupload-area p拖拽或點擊選擇圖片JPG/PNG≤5MB/p input typefile idimageInput acceptimage/* styledisplay:none; button onclickdocument.getElementById(imageInput).click()選擇圖片/button /div div idpreview/div div idresult classresult/div script document.getElementById(imageInput).addEventListener(change, async function(e) { const file e.target.files[0]; if (!file) return; // 1. 前端壓縮限制尺寸 質量減小上傳體積 const compressedBlob await compressImage(file, 800, 0.8); // 2. 構建 FormData 并發送 const formData new FormData(); formData.append(file, compressedBlob, file.name); document.getElementById(result).innerHTML div classspinner/div識別中...; try { const res await fetch(http://localhost:8000/predict, { method: POST, body: formData }); const data await res.json(); document.getElementById(result).innerHTML 預測結果span stylecolor:${data.predictiondog?blue:red}${data.prediction}/span置信度 ${data.confidence.toFixed(3)}; } catch (err) { document.getElementById(result).innerHTML 錯誤${err.message}; } }); // 圖像壓縮函數降低分辨率 質量 async function compressImage(file, maxWidth, quality) { return new Promise((resolve) { const reader new FileReader(); reader.onload (e) { const img new Image(); img.src e.target.result; img.onload () { const canvas document.createElement(canvas); let width img.width; let height img.height; if (width maxWidth) { height * maxWidth / width; width maxWidth; } canvas.width width; canvas.height height; const ctx canvas.getContext(2d); ctx.drawImage(img, 0, 0, width, height); canvas.toBlob((blob) resolve(blob), image/jpeg, quality); }; }; reader.readAsDataURL(file); }); } /script /body /html關鍵優化點說明compressImage()函數在上傳前將圖片縮放到最大寬度 800px并以 80% 質量轉為 JPEG ——可將 5MB 原圖壓縮至 300KB 以內減少網絡傳輸時間 80%使用fetch原生 API避免引入 jQuery 等大型庫spinner動畫提升用戶感知流暢度避免“白屏等待”。5.2 后端超時與并發調優main.py 進階配置在 FastAPI 中增加請求超時與并發控制防止大圖阻塞# 在 main.py 開頭添加 from fastapi import Request from starlette.middleware.base import BaseHTTPMiddleware class TimeoutMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): try: # 設置單個請求最大處理時間 10 秒 response await asyncio.wait_for(call_next(request), timeout10.0) return response except asyncio.TimeoutError: return JSONResponse( status_code408, content{detail: Request timeout. Please upload smaller image.} ) # 在 app 實例化后掛載 app.add_middleware(TimeoutMiddleware)同時修改Dockerfile中的 uvicorn 啟動命令增加超時參數CMD [uvicorn, main:app, --host, 0.0.0.0:8000, --port, 8000, --workers, 4, --timeout-keep-alive, 5]生產環境參數建議表參數推薦值說明--workersCPU 核心數避免過多進程競爭 ONNX Runtime 線程--timeout-keep-alive5HTTP keep-alive 超時釋放空閑連接--limit-concurrency10單 worker 最大并發請求數防內存溢出前端壓縮質量 (quality)0.7–0.8平衡畫質與體積0.9 體積增長快但識別精度提升微弱當用戶上傳一張 3MB 的高清圖時前端壓縮后約 250KBFastAPI 接收 → ONNX 推理 → 返回結果全程耗時穩定在300–600ms含網絡延遲符合 Web 應用“秒級響應”體驗標準。本文還有配套的精品資源點擊獲取