
物理信息神經網絡PINN入門課程走到第 11 講這一講不寫復雜算例先把框架選型這件決定后面所有代碼風格的事情講清楚。前面的課程已經解釋了 PINN 的核心思路把偏微分方程PDE的殘差、邊界條件、初值條件一起塞進損失函數用神經網絡反向傳播去逼近物理系統的解。到了真正寫代碼這一步新手面臨的第一道坎不是方程而是選 PyTorch 還是 TensorFlow。這個選擇題沒有標準答案但不同選擇會直接影響你后續寫損失函數、做自動微分、跑批量算例、甚至部署到工程環境的效率。這一講會把兩個框架在 PINN 場景下的真實差異拆開對比給出可復制的環境搭建命令分別用 PyTorch 和 TensorFlow 寫一個最小可運行的 PINN 代碼然后告訴你如何驗證訓練是否真正收斂、遇到 loss 不下降和梯度異常時該從哪里排查。讀完這一講你應該能根據自己手里的 GPU、后續應用場景和代碼習慣做出一個不后悔的框架選擇。1. 核心能力速覽PINN 代碼本質上只需要三個能力神經網絡前向計算、自動微分求物理殘差、優化器更新參數。兩個主流框架都具備但使用方式和工程側重點不同。選型維度PyTorch 方案TensorFlow 方案說明自動微分方式torch.autograd.grad動態計算圖tf.GradientTape默認 Eager 執行PINN 需要高階導數兩者都能實現寫法差異明顯網絡構建nn.Module子類化Python 風格keras層 API 或自定義tf.keras.ModelPyTorch 更貼近 Python 原生習慣TensorFlow 更偏工程化組裝自定義損失直接寫函數返回標量 tensor 即可繼承tf.keras.Model重寫train_step或用GradientTape手動更新PINN 的損失函數包含 PDE 殘差兩個框架都支持調試體驗print直接打印中間張量斷點調試友好2.x 后 Eager 模式也支持但部分 1.x 舊代碼風格有遺留新手建議優先考慮調試便利性部署環境TorchScript / ONNX / TorchServeTensorFlow Serving / Lite / ONNX如果后續要把 PINN 模型做成接口服務需要提前考慮學習資源PINN 論文附帶的官方代碼很多基于 PyTorch老牌 PINN 庫和部分工業案例使用 TensorFlow找到參考代碼的速度決定上手效率從 PINN 入門角度說更穩妥的建議是如果你過去沒寫過任何深度學習代碼優先選 PyTorch。它的動態圖和 Python 原生風格能讓你把注意力放在“物理損失怎么寫”上而不是被框架語法絆住。如果你所在團隊已經有 TensorFlow 部署鏈路或者后續要把 PINN 模型接入工業流水線就選 TensorFlow。2. PINN 為什么必須用框架先回答一個很多初學者會問的問題PINN 不就是解方程嗎為什么不能自己寫個普通全連接網絡普通的神經網絡訓練損失函數一般只依賴模型輸出和標簽之間的差值比如交叉熵或均方誤差。但 PINN 的損失函數里包含對模型輸出的導數項。以最簡單的二維熱傳導方程為例損失函數要計算溫度場對空間坐標的二階導數、對時間的導數然后把方程殘差作為一項加到損失里。這個需求直接指向深度學習框架最核心的能力自動微分。你可以手動推導導數表達式再用有限差分近似但那樣精度低、實現復雜而且邊界條件一旦復雜就基本不可維護。自動微分能把“網絡輸出對輸入求導”這件事在每次前向傳播中自動完成無論是二階導、混合偏導還是幾十個輸出分量一起求導都只需要幾行代碼。PINN 對框架的訴求可以總結為三點任意階自動微分物理方程里的殘差項通常是高階導框架必須支持連續求導例如先求一階導再對一階導求導得到二階導。靈活的自定義損失PINN 的損失由 PDE 殘差、邊界條件、初值條件、數據擬合項等組成每項權重不同框架不能限制你只能使用預置損失函數。GPU 加速真實物理問題往往需要成千上萬次迭代純 CPU 訓練會很慢框架必須能很方便地把張量運算切到 CUDA。PyTorch 和 TensorFlow 都能滿足這三個訴求差別在于寫起來順不順手。接下來的章節分別演示。3. PyTorch 還是 TensorFlow按場景選3.1 什么時候選 PyTorch如果你是學術研究或課程學習場景選擇 PyTorch 更合適。原因是近幾年發布的 PINN 相關論文中基于 PyTorch 的參考代碼占比很高。遇到問題你很容易在 GitHub 上找到對應實現把別人的網絡結構、損失函數寫法直接拿過來改。PyTorch 的調試體驗對新手非常友好。你可以在任意一行代碼處設置斷點查看某個中間張量的形狀和數值。PINN 訓練過程中經常需要檢查某個物理量殘差的具體數值這個特點會大幅節省排查時間。另外PyTorch 的生態組件對自定義模型非常友好。torch.utils.data可以做數據加載和 batch 劃分torch.optim提供 Adam、L-BFGS 等優化器后面如果要做批量物理參數掃描組合起來很方便。3.2 什么時候選 TensorFlow如果團隊已經有 TensorFlow 部署基礎設施或者模型最終要放到服務端長期運行TensorFlow 的部署鏈路會更成熟。TensorFlow Serving 對模型的版本管理、線上更新、并發請求處理都有完整方案這在工業場景中比“代碼寫起來是否順手”更重要。另外TensorFlow 的tf.keras高層 API 封裝程度很高網絡層定義、訓練循環結構比較固定。如果你只做標準的前饋網絡擬合物理場用 Keras Sequential API 幾行就能搭完一個網絡代碼量看起來確實更簡潔。需要提醒的是TensorFlow 2.x 雖然默認 Eager 執行自定義 PINN 時會用到tf.GradientTape和手動更新參數這和 PyTorch 的autograd思考方式有些差異但學會之后也能順暢使用。3.3 我的建議這一講給出的結論是入門階段優先選 PyTorch不要在這里過度糾結。核心原因是 PINN 學習過程本身就是不斷修改損失函數和網絡結構來對照物理現象的過程PyTorch 的動態圖和 Python 風格能讓你用最低的認知成本完成這些事情。等真正進入工程項目如果框架滿足不了性能或部署需求再考慮遷移到 TensorFlow 或其他推理框架。學習階段的代碼遷移成本不高因為物理損失的計算邏輯是通用的換框架只需要重寫網絡和求導部分。4. 本地部署環境準備無論選哪個框架先準備好 Python 環境和 GPU 環境。下面給出通用步驟適用于 Windows 和 Linux具體版本號請以官方文檔為準。4.1 創建獨立虛擬環境PINN 學習中會頻繁安裝和更新依賴強烈建議使用虛擬環境隔離不要直接裝到系統 Python 里。# 使用 conda 創建 Python 3.10 環境 conda create -n pinn python3.10 conda activate pinn如果不使用 conda用 venv 也可以python -m venv pinn_env source pinn_env/bin/activate4.2 安裝 PyTorchPyTorch 的安裝命令需要根據你的操作系統、CUDA 版本和是否使用 GPU 來選擇建議直接訪問 PyTorch 官網獲取對應命令。一個通用模板如下# CPU 版本 pip install torch # GPU 版本示例實際安裝命令以官網選擇為準 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121注意在命令行直接運行pip install torch默認安裝的是 PyTorch 官網的預編譯包CPU 版和 GPU 版的差別在安裝后可以通過torch.cuda.is_available()驗證。import torch print(torch.__version__) print(torch.cuda.is_available())如果輸出False說明當前環境沒有可用的 CUDA訓練時只能走 CPU 路徑。4.3 安裝 TensorFlowTensorFlow 同樣分為 CPU 版和 GPU 版。安裝前確認你的顯卡驅動支持 CUDA 版本一個通用模板如下# CPU 版本 pip install tensorflow # GPU 版本通過官方 pip 源安裝 pip install tensorflow安裝完成后同樣需要驗證 GPU 是否可用import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices(GPU))如果 GPU 列表為空說明 TensorFlow 沒有識別到 GPU需要檢查驅動和 CUDA 工具包版本。4.4 硬件與磁盤PINN 訓練對 GPU 顯存的要求與網絡規模和批大小有關。入門階段用全連接網絡、每層 20 到 64 個神經元、批大小 128 左右時顯存占用并不高很多集成顯卡或低端獨立顯卡也能運行。但要注意如果做高維 PDE 或大批量訓練顯存占用會快速上升。這不是框架本身的問題而是自動微分需要保存反向傳播所需的中間張量。降低顯存占用的通用辦法是減小 batch size、減少網絡層數、降低輸入分辨率或使用混合精度訓練。磁盤空間方面Python 環境加兩個框架包大約需要 3 到 5 GB如果包含 CUDA 工具包會更多。建議為訓練實驗單獨準備一個數據目錄把輸入數據、模型權重和輸出結果分目錄管理。5. 用 PyTorch 寫一個最小 PINN下面用一維泊松方程作為示例展示 PyTorch 中 PINN 的完整代碼結構。方程形式為[ -u(x) \pi^2 \sin(\pi x), \quad x \in [0, 1] ]邊界條件為 ( u(0)0, u(1)0 )精確解為 ( u(x)\sin(\pi x) )。PINN 的損失由兩部分組成方程殘差損失和邊界條件損失。import torch import torch.nn as nn # 定義網絡結構 class PINN(nn.Module): def __init__(self): super().__init__() self.net nn.Sequential( nn.Linear(1, 20), nn.Tanh(), nn.Linear(20, 20), nn.Tanh(), nn.Linear(20, 1) ) def forward(self, x): return self.net(x) # 初始化模型和優化器 model PINN() optimizer torch.optim.Adam(model.parameters(), lr1e-3) # 計算方程殘差損失 def pde_loss(x): x.requires_grad_(True) u model(x) # 一階導 u_x torch.autograd.grad(u, x, grad_outputstorch.ones_like(u), create_graphTrue)[0] # 二階導 u_xx torch.autograd.grad(u_x, x, grad_outputstorch.ones_like(u_x), create_graphTrue)[0] # 方程右端項 f torch.pi**2 * torch.sin(torch.pi * x) # 方程殘差 residual -u_xx - f return torch.mean(residual**2) # 邊界條件損失 def boundary_loss(): x_left torch.zeros((100, 1), requires_gradFalse) x_right torch.ones((100, 1), requires_gradFalse) u_left model(x_left) u_right model(x_right) return torch.mean(u_left**2) torch.mean(u_right**2) # 訓練循環 n_iter 5000 for it in range(n_iter): optimizer.zero_grad() # 內部配置點用于計算方程殘差 x_collocation torch.rand((256, 1)) # 在 [0,1] 內采樣 loss_pde pde_loss(x_collocation) loss_bc boundary_loss() # 加權組合 loss loss_pde loss_bc loss.backward() optimizer.step() if it % 500 0: print(fIter {it}, Loss: {loss.item():.6f})這段代碼的關鍵點有三個requires_grad_(True)讓輸入張量參與自動微分這樣神經網絡輸出才能對輸入求梯度。torch.autograd.grad中的create_graphTrue表示允許對梯度再求梯度這是計算二階導的必要條件。邊界條件通過直接輸入x0和x1的采樣點來約束模型輸出。只有一階導的方程create_graph可以設置為 False需要二階或更高階導數時必須保留計算圖。6. 用 TensorFlow 寫一個最小 PINNTensorFlow 版本使用GradientTape完成相同的計算。import tensorflow as tf # 定義網絡 class PINN(tf.keras.Model): def __init__(self): super().__init__() self.dense1 tf.keras.layers.Dense(20, activationtanh) self.dense2 tf.keras.layers.Dense(20, activationtanh) self.dense3 tf.keras.layers.Dense(1) def call(self, x): x self.dense1(x) x self.dense2(x) return self.dense3(x) model PINN() optimizer tf.keras.optimizers.Adam(learning_rate1e-3) # 計算方程殘差損失 def pde_loss(x): with tf.GradientTape(persistentTrue) as tape: tape.watch(x) u model(x) u_x tape.gradient(u, x) u_xx tape.gradient(u_x, x) f tf.constant(tf.math.pi**2, dtypetf.float32) * tf.sin(tf.constant(tf.math.pi, dtypetf.float32) * x) residual -u_xx - f return tf.reduce_mean(tf.square(residual)) # 訓練循環 for it in range(5000): with tf.GradientTape() as tape: x_collocation tf.random.uniform((256, 1), minval0.0, maxval1.0) # 方程殘差損失 loss_pde pde_loss(x_collocation) # 邊界條件損失 x_left tf.zeros((100, 1)) x_right tf.ones((100, 1)) loss_bc tf.reduce_mean(tf.square(model(x_left))) tf.reduce_mean(tf.square(model(x_right))) loss loss_pde loss_bc grads tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(grads, model.trainable_variables)) if it % 500 0: print(fIter {it}, Loss: {loss.numpy():.6f})TensorFlow 版本的差異點tf.GradientTape(persistentTrue)允許重復調用多次梯度操作。求完一階導再求二階導時如果用普通GradientTape需要在同一個上下文內完成全部計算。tf.random.uniform生成采樣點張量默認參與自動微分但通過tape.watch(x)顯式監聽輸入張量。參數更新需要手動apply_gradients這一步在 PyTorch 里對應optimizer.step()。從代碼量看兩個框架差別不大。真正影響體驗的是你對哪種寫法的直覺更接近。PyTorch 的tensor.grad和autograd更透明TensorFlow 的GradientTape更強調作用域。7. PINN 典型問題測試與結果驗證寫完最小代碼后下一步是驗證訓練是否真的收斂到了物理上正確的解。7.1 用精確解對照上面的泊松方程有解析解 ( u(x)\sin(\pi x) )。訓練結束后在 [0,1] 上均勻取 100 個點對比網絡輸出和解析解import numpy as np import torch # PyTorch 對比示例 model.eval() x_test torch.linspace(0, 1, 100).reshape(-1, 1) u_pred model(x_test).detach().numpy().ravel() u_true np.sin(np.pi * x_test.numpy().ravel()) # 計算最大誤差和均方誤差 error np.abs(u_pred - u_true) print(fMax error: {error.max():.2e}) print(fRMSE: {np.sqrt(np.mean(error**2)):.2e})一般來說迭代到后期最大誤差降到 ( 10^{-2} ) 到 ( 10^{-3} ) 量級說明模型已經學習到了方程的主要特征。如果一直停留在 ( 10^{-1} ) 以上需要檢查網絡層數、激活函數、采樣點數和迭代次數。7.2 觀察損失曲線的不同階段PINN 訓練中損失曲線通常不會像圖像分類那樣平滑下降。物理殘差和邊界條件損失在初始階段權重不同可能出現以下幾個現象邊界條件損失快速下降但 PDE 殘差損失下降緩慢。這說明網絡優先擬合了邊界內部物理約束還沒學透。可以適當增大迭代次數或調整兩項損失的權重。PDE 殘差下降到某個值后不再變化。此時不一定是代碼寫錯了而是網絡容量或采樣點密度不足。嘗試加深網絡、增加采樣點或者切換到 L-BFGS 這類二階優化器。訓練后期損失出現周期性波動。這是配置點每次重新采樣導致的屬正常現象。更穩定的做法是把配置點固定并進行較多次迭代或者使用帶重置策略的采樣方法。7.3 判斷收斂的物理準則PINN 訓練不僅看 Loss 數值更要看預測解是否滿足物理規律。以熱傳導方程為例預測的溫度場不應該出現局部突變或違背能量守恒。因此測試時應增加一組物理約束驗證檢查預測值的單調性、極值位置、邊界值誤差。如果預測結果在某些區域明顯偏離物理直覺優先懷疑配置點分布是否覆蓋了這些區域。PINN 以配置點方式離散物理域配置點稀疏的地方網絡學習到的物理約束就弱。8. 資源占用與性能觀察PINN 訓練的資源占用沒有固定數字因為它與網絡大小、批量大小、方程維度和自動微分階數直接相關。不過可以給出通用的觀察和調優思路。8.1 如何觀察顯存和內存占用訓練過程中可以用系統自帶的nvidia-smi命令查看 GPU 顯存占用或者使用watch -n 1 nvidia-smi持續刷新。在 Python 內部也可以用torch.cuda.memory_allocated()獲取當前顯存占用。print(torch.cuda.memory_allocated() / 1024**2, MB)如果顯存不足優先減少 batch size。PINN 使用隨機采樣batch size 從 256 降到 128 幾乎不影響物理規律學習反而可能讓單次迭代的穩定性提升。8.2 CPU 和 GPU 訓練差異PINN 入門代碼在沒有 GPU 的環境中也能跑起來只是訓練速度慢。CPU 訓練 5000 次迭代可能在幾分鐘到十幾分鐘GPU 訓練通常能在幾秒到幾十秒內完成。差異主要來自矩陣運算的并行度和自動微分構建計算圖的開銷。如果你暫時沒有 GPU優先用小網絡小采樣點跑通流程。但要注意最后的效果驗證部分必須基于真實訓練結果不能只用小采樣點數量“硬推”出物理規律。8.3 如何降低顯存占用幾個常見手段減少網絡層數和每層神經元數。降低每個 iteration 的采樣點數量。使用混合精度訓練例如 PyTorch 的torch.autocast。清理不再使用的中間張量例如del u_x, u_xx; torch.cuda.empty_cache()。這些手段在入門階段不一定都需要但它們能幫助你理解 PINN 的資源瓶頸在哪里后續做高維問題時直接用得上。9. 常見問題與排查方法PINN 訓練中的問題很多下面列出最常遇到的幾類。問題現象可能原因排查方式解決方案安裝框架后import torch報錯環境類型不匹配、Python 版本不兼容檢查 Python 版本和 pip 安裝源重新創建虛擬環境按官方安裝命令安裝torch.cuda.is_available()返回 FalseCUDA 驅動版本過低或 PyTorch 裝成 CPU 版運行nvidia-smi查看驅動版本安裝與驅動匹配的 CUDA 版 PyTorchTensorFlow 識別不到 GPUCUDA、cuDNN 版本與 TensorFlow 版本不匹配運行tf.config.list_physical_devices(GPU)參照官方 GPU 支持矩陣安裝對應版本訓練時 loss 不下降學習率過大或過小、網絡結構不合理、損失函數權重失衡打印各分項損失數值調小學習率、調整損失權重、加大采樣點數二階導計算結果異常create_graph參數錯誤或采樣點超出定義域打印中間梯度值比較檢查create_graphTrue是否設置訓練后期 loss 波動大配置點隨機采樣導致梯度噪聲觀察波動幅度固定配置點或增大 batch size預測邊界值偏差大邊界條件權重太低檢查邊界損失數值提高邊界條件損失權重模型在局部區域預測異常配置點稀疏可視化采樣點分布該區域加密采樣點針對“訓練時 loss 不下降”這個最常見問題一個有效的排查思路是先把物理損失去掉只保留邊界條件損失看模型是否能學會邊界值。如果能學會再逐步加回物理殘差定位是哪一項導致數值不穩定。10. 框架學習路徑與工程實踐建議選框架只是起點真正決定 PINN 學習進度的是后面幾個習慣。第一建議把“最小可運行代碼”保存下來。后續所有更復雜的算例都在這份代碼基礎上修改網絡、損失函數和數據加載邏輯。不要每次從零開始寫。第二訓練過程中要有記錄意識。每次修改網絡結構、采樣點數或學習率后記錄 loss 曲線和最終預測誤差并保留模型權重文件。這樣能準確判斷哪個改動帶來了實際效果而不是靠感覺調參。第三批量實驗時要做好任務隊列。PINN 經常需要掃描多個物理參數例如擴散系數、邊界溫度、材料屬性等。可以先寫一個參數列表循環創建不同實驗目錄每個目錄下保存對應配置和模型輸出。接口調用層面如果后面要把訓練好的 PINN 模型封裝成服務優先考慮 ONNX 導出或框架自帶的 serving 方案這樣其他程序可以通過統一接口訪問模型。第四合規問題需要從一開始就重視。如果使用實驗數據、仿真數據或任何第三方數據集務必確認數據來源合法、具備使用權。涉及真實工業生產數據、人體相關數據或受版權保護的材料時必須做匿名化和授權合規處理。PINN 的應用場景越多數據合規的邊界就越重要。第五不要盲目使用最新版本框架。對于 PINN 學習穩定性和生態兼容性比新功能更重要。熱門框架版本升級后自動微分 API 或部署工具鏈可能發生變動優先選擇文檔較為成熟、周圍同學或同事用得多的版本。11. 這一講之后第 12 講方向本講已經完成框架選型和最小代碼驗證。接下來第 12 講會繼續深入 PINN 的損失函數權重配置、激活函數選擇以及邊界條件的標準化處理這些都是實際訓練中直接影響收斂速度和精度的細節。建議你先動手跑一遍本講的代碼把 PyTorch 和 TensorFlow 兩個版本都運行一次對比兩者的 loss 下降速度。通過這個對比你才能真正理解框架選擇對你編程習慣和調試效率的影響。選擇 PyTorch 還是 TensorFlow答案不在別人嘴里在你自己的運行結果里。