You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TensorFlow自定义神经网络参数估计误差过高问题排查

问题诱因

你的代码存在几个核心错误,直接导致训练效率低、参数估计不准,和优化器本身实现无关:

  • 最严重的错误:优化器被反复重置初始化。你在train方法的每轮训练循环内部都重新创建了Adam优化器实例,Adam依赖的历史梯度动量、一阶/二阶矩估计值每次迭代都会被清零,完全无法发挥自适应优化器的优势,退化成了单步梯度下降,收敛速度极慢、更新方向不稳定。
  • 高阶自动微分计算逻辑错误。net_f中计算PDE残差所需的二阶导u_xx和一阶导u_t的计算路径没有被梯度带完整追踪:你在with GradientTape上下文块内仅计算了u和u_x,u_t、u_xx的计算放在了上下文块外,且u_x作为梯度运算的输出,其对输入x的依赖没有被梯度带正确记录,算出来的高阶导数值错误,PDE残差损失完全失真,才会出现“损失值很低但参数误差极大”的现象——低损失是网络过拟合了错误的残差信号,没有学到正确的物理方程约束。
  • 训练配置不合理:一是定义了学习率衰减策略但完全没有传入优化器使用;二是训练轮次仅1000轮,对于PINN类任务(同时拟合解、满足PDE约束、反演未知参数)远远不够;三是lambda_1初始值设为0,初始状态下对流项直接失效,前期梯度更新方向偏差大。
  • 冗余代码:predict方法中存在重复赋值的无效写法,不影响运行但不规范。
正确实现方案

核心修改点

  1. 将优化器初始化移到训练循环外,不要每轮迭代新建实例;如果需要对网络权重、物理参数用不同学习率,保留两个优化器的写法即可,优化器的内部状态会在多轮迭代中持续保留。
  2. 修正高阶自动微分的计算逻辑,保证所有一阶、二阶导数的计算都被持久化梯度带正确追踪,避免计算图断开。
  3. 移除无效的学习率调度代码,或者将其正确传入优化器;训练轮次提升到10000轮以上,待Adam训练收敛后可以切换L-BFGS优化器做精调,进一步降低参数误差。
  4. 调整物理参数初始值,避免零初始化导致的梯度失效问题。

修正后的核心代码

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import scipy.io
from scipy.interpolate import griddata
from pyDOE import lhs
import math as ma

class PhysicsInformedNN:
    def __init__(self,X_n,v,layers,lb,ub):
        self.lb = lb
        self.ub = ub
        self.layers = layers
        
        self.dx_n = tf.convert_to_tensor(X_n[:,0:1],dtype = 'float32')
        self.t_n = tf.convert_to_tensor(X_n[:,1:2],dtype = 'float32')
        self.v_r = tf.convert_to_tensor(v,dtype = 'float32')

        # 调整参数初始值,避免零初始化
        self.lambda_1 = tf.Variable(0.5,dtype = 'float32')
        self.lambda_2 = tf.Variable(-6,dtype = 'float32')
        self.para =[self.lambda_1,self.lambda_2]
        
        self.weights, self.biases = self.initialize_NN(layers)
        
        # 优化器移到初始化阶段创建,保留训练状态
        self.optimizer_net = tf.keras.optimizers.Adam(learning_rate=1e-4)
        self.optimizer_para = tf.keras.optimizers.Adam(learning_rate=1e-3)
        
    def initialize_NN(self,layers):
        weights = []
        biases = []
        num_layers = len(layers) 
        for l in range(0,num_layers-1):
            W = self.xavier_init(size=[layers[l], layers[l+1]])
            b = tf.Variable(tf.zeros([1,layers[l+1]], dtype='float32'), dtype='float32')
            weights.append(W)
            biases.append(b)        
        return weights, biases
    
    def xavier_init(self, size):
        in_dim = size[0]
        out_dim = size[1]        
        xavier_stddev = np.sqrt(2/(in_dim + out_dim))
        return tf.Variable(tf.random.truncated_normal([in_dim, out_dim], stddev=xavier_stddev), dtype='float32')
    
    def neural_net(self, X, weights, biases):
        num_layers = len(weights) + 1
        
        H = 2.0*(X - self.lb)/(self.ub - self.lb) - 1.0
        for l in range(0,num_layers-2):
            W = weights[l]
            b = biases[l]
            H = tf.math.tanh(tf.math.add(tf.linalg.matmul(H, W), b))
        W = weights[-1]
        b = biases[-1]
        Y = tf.math.add(tf.linalg.matmul(H, W), b)
        return Y
    
    def net_u(self, x, t):
        v = self.neural_net(tf.concat([x,t],1), self.weights, self.biases)
        return v
    
    def net_f(self, x, t):
        lambda_1 = self.para[0]       
        lambda_2 = tf.exp(self.para[1])
        with tf.GradientTape(persistent=True) as tape :
            tape.watch(t)
            tape.watch(x)
            u = self.net_u(x,t)
            # 一阶梯度全部在梯度带上下文内计算,保证计算图连通
            u_x = tape.gradient(u,x)
            u_t = tape.gradient(u,t)
        # 持久化梯度带在上下文外仍可计算高阶导
        u_xx = tape.gradient(u_x,x)
        del tape
        f = u_t + lambda_1*u*u_x - lambda_2*u_xx
        return f
    
    def callback(self, loss,n):
        if n % 100 == 0: # 每100轮打印一次,避免输出过多
            print('Loss:', loss.numpy(), ' Epoch : ', n, 'lambda1:', self.lambda_1.numpy(), 'lambda2:', tf.exp(self.lambda_2).numpy())
        
    def train(self,epoch):
        for i in range(epoch):
            with tf.GradientTape(persistent=True) as tape :
                # Variable默认自动watch,无需手动添加
                f_pred = self.net_f(self.dx_n, self.t_n)
                v_pred = self.net_u(self.dx_n, self.t_n)
                loss =  tf.reduce_mean(tf.square(self.v_r - v_pred)) + tf.reduce_mean(tf.square(f_pred))
            dw = tape.gradient(loss,self.weights)
            db = tape.gradient(loss,self.biases)
            dp = tape.gradient(loss,self.para)
            
            # 直接调用预初始化的优化器更新参数
            self.optimizer_net.apply_gradients(zip(dw, self.weights))
            self.optimizer_net.apply_gradients(zip(db, self.biases))
            self.optimizer_para.apply_gradients(zip(dp, self.para))
            del tape
            self.callback(loss,i)
            
    def predict(self, X_star):
        v_star = self.net_u(X_star[:,0:1], X_star[:,1:2])
        f_star = self.net_f(X_star[:,0:1], X_star[:,1:2])
        para_last = self.para
        return v_star, f_star, para_last
 

if __name__ == '__main__':
    np.random.seed(123)
    nu =0.01/np.pi
    
    layers = [2, 20, 20, 20, 20, 1]
    N_u = 2000
        
    data = scipy.io.loadmat('burgers_shock.mat')
    
    t = data['t'].flatten()[:,None]
    x = data['x'].flatten()[:,None]
    Exact = np.real(data['usol']).T
    X, T = np.meshgrid(x,t)
    X_star = np.hstack((X.flatten()[:,None], T.flatten()[:,None]))
    u_star = Exact.flatten()[:,None]
    lb = X_star.min(0)
    ub = X_star.max(0)
    idx = np.random.choice(X_star.shape[0], N_u, replace=False)
    X_u_train = X_star[idx,:]
    u_train = u_star[idx,:]
    
    model = PhysicsInformedNN(X_u_train, u_train, layers, lb, ub)
    model.train(10000) # 训练轮次提升到10000轮以上

    X_star = tf.convert_to_tensor(X_star,dtype='float32')
    u_pred, f_pred, param = model.predict(X_star)

    error_lambda_1 = np.abs(param[0].numpy() - 1.0)*100
    error_lambda_2 = np.abs( np.exp(param[1].numpy())- nu)/nu * 100

    print('lambda1误差(%):',error_lambda_1,'lambda2误差(%):',error_lambda_2)

调优建议

如果修改后参数误差仍未降到理想水平,可以尝试:

  • 增加网络层数或神经元数量,比如换成[2,30,30,30,30,30,1]的结构
  • 调整两部分损失的权重,比如给PDE残差损失乘0.1~10的权重系数,平衡数据拟合和物理约束的贡献
  • Adam训练收敛后,换用L-BFGS优化器做二阶精调,PINN反演问题用L-BFGS收尾通常能把参数误差降到1%以内

内容的提问来源于stack exchange,提问作者Eeshwari ash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 12:06:51