课程摘要
上一节通过Stacking组合多个机器学习模型,本节进一步学习能够自动构造非线性特征的神经网络。课程以悬臂梁最大弯曲应力预测为案例,讲解神经元、权重、偏置、激活函数、网络层、前向传播、损失函数和反向传播,并完成数据标准化、MLP训练、验证集早停和独立测试。学习者将理解神经网络“如何预测、怎样学习、如何判断可靠”,为后续CNN、LSTM、Transformer和PINN课程建立基础。
一、承接第十三节:为什么继续学习神经网络?
第十三节使用Ridge、随机森林、梯度提升和RBF-SVR构建了Stacking模型,其基本思想是:
\[ \text{多个基模型的预测} \longrightarrow \text{元学习器} \longrightarrow \text{最终预测} \]
Stacking的优势是综合已有模型,但每个基模型仍需人工选择。面对应力场、位移场、裂纹图像和瞬态响应等高维问题,传统模型会遇到三个困难:
- 输入可能包含成千上万个网格节点或像素;
- 几何、载荷和材料之间存在复杂非线性耦合;
- 很难完全依靠人工设计特征。
神经网络可以在训练过程中自动构造中间特征:
\[ \text{原始输入} \longrightarrow \text{多层特征变换} \longrightarrow \text{力学响应} \]
本节先从最基本的多层感知机开始。后续的CNN、LSTM、Transformer和PINN,本质上都建立在神经元、激活函数、前向传播与反向传播之上。
二、本节学习目标
完成本节后,学习者应能够:
- 解释神经元中权重和偏置的作用;
- 理解激活函数为什么能够引入非线性;
- 区分输入层、隐藏层和输出层;
- 说清楚前向传播、损失计算和反向传播的关系;
- 使用MLP预测悬臂梁最大弯曲应力;
- 正确划分训练集、验证集和测试集;
- 查看训练曲线、预测散点图和残差图;
- 认识神经网络在力学预测中的能力边界。
三、从一个神经元开始
3.1 神经元的计算过程
设神经元接收四个标准化后的力学参数:
\[ \tilde{\boldsymbol{x}} = \begin{bmatrix} \tilde{F}\\ \tilde{L}\\ \tilde{b}\\ \tilde{h} \end{bmatrix} \]
其中:
- \(F\):载荷;
- \(L\):梁长;
- \(b\):截面宽度;
- \(h\):截面高度。
神经元首先进行加权求和:
\[ z= w_1\tilde{F} +w_2\tilde{L} +w_3\tilde{b} +w_4\tilde{h} +c \]
式中:
- \(w_1,w_2,w_3,w_4\) 为权重;
- \(c\) 为偏置;
- \(z\) 为神经元的线性输入。
这里用 \(c\) 表示偏置,避免与截面宽度 \(b\) 混淆。
随后,神经元通过激活函数得到输出:
\[ a=\phi(z) \]
因此,一个神经元包含两个步骤:
\[ \boxed{ \text{加权求和} \longrightarrow \text{激活函数} } \]
3.2 一个可以手算的例子
假设输入为:
\[ \tilde{\boldsymbol{x}} = \begin{bmatrix} 1&0&-1&0.5 \end{bmatrix}^{\mathrm T} \]
权重和偏置为:
\[ \boldsymbol{w} = \begin{bmatrix} 0.4&0.2&-0.3&-0.6 \end{bmatrix}^{\mathrm T} \]\[ c=0.1 \]
则加权求和结果为:
\[ \begin{aligned} z &=0.4\times1 +0.2\times0 +(-0.3)\times(-1)\\ &\quad+(-0.6)\times0.5 +0.1\\ &=0.5 \end{aligned} \]
如果使用Tanh激活函数:
\[ a=\tanh(0.5)\approx0.4621 \]
这里的 \(0.4621\) 是神经元产生的中间特征值,不直接代表应力或位移。
四、为什么必须使用激活函数?
假设一个两层网络没有激活函数:
\[ \boldsymbol{a}^{(1)} = \boldsymbol{W}^{(1)}\boldsymbol{x} +\boldsymbol{c}^{(1)} \]\[ \hat{\boldsymbol{y}} = \boldsymbol{W}^{(2)}\boldsymbol{a}^{(1)} +\boldsymbol{c}^{(2)} \]
将第一式代入第二式:
\[ \hat{\boldsymbol{y}} = \boldsymbol{W}^{(2)} \boldsymbol{W}^{(1)} \boldsymbol{x} + \boldsymbol{W}^{(2)} \boldsymbol{c}^{(1)} + \boldsymbol{c}^{(2)} \]
无论叠加多少层,最终仍然是关于输入的线性函数。
激活函数可以打破这种线性关系,使网络能够学习:
- 应力与梁高之间的平方反比关系;
- 材料进入塑性后的非线性响应;
- 裂纹扩展的突变趋势;
- 接触状态改变产生的分段响应;
- 多种材料参数和边界条件的耦合关系。
五、常用激活函数
5.1 ReLU函数
\[ \operatorname{ReLU}(z)=\max(0,z) \]
当 \(z<0\) 时:
\[ \operatorname{ReLU}(z)=0 \]
当 \(z\geq0\) 时:
\[ \operatorname{ReLU}(z)=z \]
ReLU计算简单,在深层网络和CNN中使用非常广泛。
5.2 Tanh函数
\[ \tanh(z) = \frac{e^z-e^{-z}}{e^z+e^{-z}} \]
输出范围为:
\[ -1<\tanh(z)<1 \]
Tanh连续、平滑,在某些平滑力学响应和物理信息神经网络中较常见。
【正文插图1:ReLU与Tanh激活函数】
需要注意,隐藏层使用Tanh,并不意味着最终预测应力只能在 \(-1\) 到 \(1\) MPa之间。回归网络的输出层通常使用线性函数,标准化后的结果还会被恢复为原来的应力单位。
六、从神经元组成多层感知机
多个神经元可以组成一层,多层神经元连接后形成多层感知机,即MLP。
本节采用的网络结构为:
\[ 4 \longrightarrow 32 \longrightarrow 32 \longrightarrow 1 \]
它表示:
| 网络层 | 神经元数量 | 作用 |
|---|---|---|
| 输入层 | 4 | 接收 \(F,L,b,h\) |
| 第一隐藏层 | 32 | 提取初级非线性特征 |
| 第二隐藏层 | 32 | 组合更复杂的特征 |
| 输出层 | 1 | 预测最大弯曲应力 |
需要强调的是,普通神经网络的隐藏神经元通常没有明确的物理名称。不能直接断言某个神经元代表弯矩,另一个神经元代表惯性矩。
6.1 网络参数量
输入层到第一隐藏层:
\[ 4\times32+32=160 \]
第一隐藏层到第二隐藏层:
\[ 32\times32+32=1056 \]
第二隐藏层到输出层:
\[ 32\times1+1=33 \]
总参数量为:
\[ 160+1056+33=1249 \]
也就是说,这个看起来并不大的网络,已经包含1249个需要从数据中学习的参数。
七、神经网络如何完成预测?
对于一个样本,第一隐藏层计算:
\[ \boldsymbol{a}^{(1)} = \tanh \left( \boldsymbol{W}^{(1)} \tilde{\boldsymbol{x}} + \boldsymbol{c}^{(1)} \right) \]
第二隐藏层计算:
\[ \boldsymbol{a}^{(2)} = \tanh \left( \boldsymbol{W}^{(2)} \boldsymbol{a}^{(1)} + \boldsymbol{c}^{(2)} \right) \]
输出层计算:
\[ \hat{\tilde{\sigma}} = \boldsymbol{W}^{(3)} \boldsymbol{a}^{(2)} +c^{(3)} \]
这一过程称为前向传播:
\[ \boxed{ \text{输入} \longrightarrow \text{隐藏层1} \longrightarrow \text{隐藏层2} \longrightarrow \text{预测结果} } \]
前向传播只负责使用当前参数完成预测,还没有修改网络参数。
八、神经网络如何学习?
8.1 计算损失
对于回归问题,可以使用均方误差:
\[ \mathcal{L}_{\mathrm{data}} = \frac{1}{N} \sum_{i=1}^{N} \left( \hat{\tilde{\sigma}}_i - \tilde{\sigma}_i \right)^2 \]
损失越小,说明预测值整体越接近目标值。
为了抑制过大的权重,还可以加入L2正则化:
\[ \mathcal{L} = \mathcal{L}_{\mathrm{data}} + \alpha \sum_l \left\| \boldsymbol{W}^{(l)} \right\|_2^2 \]
其中 \(\alpha\) 控制正则化强度。
8.2 反向传播
反向传播利用链式法则计算:
\[ \frac{\partial\mathcal{L}} {\partial\boldsymbol{W}^{(l)}} \]
以及:
\[ \frac{\partial\mathcal{L}} {\partial\boldsymbol{c}^{(l)}} \]
它回答的问题是:
某个权重发生微小变化时,预测误差将怎样变化?
8.3 参数更新
得到梯度后,优化器更新参数:
\[ \boldsymbol{W}_{t+1} = \boldsymbol{W}_t - \eta \frac{\partial\mathcal{L}} {\partial\boldsymbol{W}_t} \]
其中 \(\eta\) 是学习率。
完整训练循环为:
\[ \boxed{ \text{前向预测} \rightarrow \text{计算损失} \rightarrow \text{反向传播} \rightarrow \text{更新参数} } \]
反向传播负责计算梯度,Adam等优化器负责根据梯度更新参数,两者不是同一个概念。
九、实战项目:悬臂梁最大应力预测
9.1 力学模型
矩形截面悬臂梁自由端承受集中载荷 \(F\)。
固定端弯矩为:
\[ M=FL \]
截面惯性矩为:
\[ I=\frac{bh^3}{12} \]
截面最外缘距离中性轴:
\[ c=\frac{h}{2} \]
最大弯曲正应力为:
\[ \sigma_{\max} = \frac{Mc}{I} = \frac{6FL}{bh^2} \]
当载荷使用N、尺寸使用mm时:
\[ 1\ \mathrm{N/mm^2}=1\ \mathrm{MPa} \]
9.2 数据范围
生成1000组教学数据:
| 参数 | 范围 |
|---|---|
| 载荷 \(F\) | 10~50 N |
| 梁长 \(L\) | 300~600 mm |
| 梁宽 \(b\) | 15~30 mm |
| 梁高 \(h\) | 10~20 mm |
目标数据为:
\[ \sigma_{\mathrm{target}} = \frac{6FL}{bh^2} +\varepsilon \]
其中:
\[ \varepsilon \sim \mathcal{N}(0,0.40^2) \]
这里加入小幅可重复扰动,用于模拟含噪声数据的训练过程。本节数据来自解析公式,并非真实有限元计算结果。
十、准备数据
from copy import deepcopy import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neural_network import MLPRegressor from sklearn.linear_model import Ridge from sklearn.metrics import ( mean_squared_error, mean_absolute_error, r2_score ) def rmse(y_true, y_pred): return float( np.sqrt(mean_squared_error(y_true, y_pred)) ) rng = np.random.default_rng(42) n_samples = 1000 F = rng.uniform(10.0, 50.0, n_samples) L = rng.uniform(300.0, 600.0, n_samples) b = rng.uniform(15.0, 30.0, n_samples) h = rng.uniform(10.0, 20.0, n_samples) sigma_theory = 6.0 * F * L / (b * h**2) sigma_target = ( sigma_theory + rng.normal(0.0, 0.40, n_samples) ) features = [ "load_N", "length_mm", "width_mm", "height_mm" ] X = pd.DataFrame({ "load_N": F, "length_mm": L, "width_mm": b, "height_mm": h })十一、划分训练集、验证集和测试集
X_dev, X_test, y_dev, y_test = train_test_split( X, sigma_target, test_size=0.20, random_state=42 ) X_train, X_val, y_train, y_val = train_test_split( X_dev, y_dev, test_size=0.20, random_state=42 ) print("训练集:", len(X_train)) print("验证集:", len(X_val)) print("测试集:", len(X_test))实际输出:
训练集: 640 验证集: 160 测试集: 200三类数据的职责不同:
- 训练集用于更新网络参数;
- 验证集用于选择最佳训练轮次;
- 测试集只用于最终评价。
测试集不能参与标准化器拟合、训练轮数选择和模型参数调整。
十二、为什么神经网络需要标准化?
四个输入特征的数值尺度明显不同:
\[ F\approx10\sim50 \]\[ L\approx300\sim600 \]\[ h\approx10\sim20 \]
如果直接输入网络,梁长的数值可能在梯度计算中占据过大影响。
标准化公式为:
\[ \tilde{x}_j = \frac{x_j-\mu_{j,\mathrm{train}}} {s_{j,\mathrm{train}}} \]
代码如下:
input_scaler = StandardScaler() target_scaler = StandardScaler() X_train_scaled = input_scaler.fit_transform(X_train) X_val_scaled = input_scaler.transform(X_val) X_test_scaled = input_scaler.transform(X_test) y_train_scaled = target_scaler.fit_transform( y_train.reshape(-1, 1) ).ravel() print(X_train_scaled.shape) print(y_train_scaled.shape)实际输出:
(640, 4) (640,)标准化器只能在训练集上调用fit。验证集、测试集和后续新工况只能调用transform。
十三、建立MLP网络
model = MLPRegressor( hidden_layer_sizes=(32, 32), activation="tanh", solver="adam", learning_rate_init=0.001, alpha=0.0001, batch_size=64, random_state=42, early_stopping=False )主要参数含义:
| 参数 | 本节设置 | 作用 |
|---|---|---|
hidden_layer_sizes | (32, 32) | 两个隐藏层,每层32个神经元 |
activation | tanh | 隐藏层激活函数 |
solver | adam | 参数优化方法 |
learning_rate_init | 0.001 | 初始学习率 |
alpha | 0.0001 | L2正则化强度 |
batch_size | 64 | 每批训练样本数 |
random_state | 42 | 保证结果可重复 |
定义预测函数,将标准化结果恢复为MPa:
def predict_mpa(network, X_scaled): pred_scaled = network.predict(X_scaled) return target_scaler.inverse_transform( pred_scaled.reshape(-1, 1) ).ravel()十四、逐轮训练并保存最佳模型
history = [] best_val_rmse = np.inf best_epoch = 0 best_model = None max_epochs = 1500 patience = 120 for epoch in range(1, max_epochs + 1): model.partial_fit( X_train_scaled, y_train_scaled ) train_pred = predict_mpa( model, X_train_scaled ) val_pred = predict_mpa( model, X_val_scaled ) train_error = rmse( y_train, train_pred ) val_error = rmse( y_val, val_pred ) history.append([ epoch, train_error, val_error ]) if val_error < best_val_rmse: best_val_rmse = val_error best_epoch = epoch best_model = deepcopy(model) if epoch - best_epoch >= patience: break model = best_model print("实际训练轮数:", len(history)) print("最佳模型轮数:", best_epoch) print( f"最佳验证RMSE:" f"{best_val_rmse:.6f} MPa" )实际运行输出:
实际训练轮数: 1374 最佳模型轮数: 1254 最佳验证RMSE:0.427851 MPadeepcopy用于保存当时的模型参数副本。如果只写:
best_model = model两个变量会指向同一个持续更新的对象,无法真正保留最佳轮次。
十五、分析训练曲线
import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = [ "Microsoft YaHei", "SimHei", "DejaVu Sans" ] plt.rcParams["axes.unicode_minus"] = False history_array = np.asarray(history) plt.figure(figsize=(9, 4.5)) plt.plot( history_array[:, 0], history_array[:, 1], label="训练集" ) plt.plot( history_array[:, 0], history_array[:, 2], label="验证集" ) plt.axvline( best_epoch, linestyle="--", color="gray", label=f"保留第{best_epoch}轮" ) plt.yscale("log") plt.xlabel("训练轮数 Epoch") plt.ylabel("RMSE(MPa,对数刻度)") plt.legend() plt.grid(alpha=0.2) plt.tight_layout() plt.show()【正文插图2:神经网络训练曲线】
从曲线可以看到:
- 训练初期,误差迅速下降;
- 约200轮后,网络已经学到主要非线性趋势;
- 后期误差仍在缓慢下降;
- 第1254轮取得最低验证误差;
- 此后连续120轮没有刷新最佳值,训练停止。
本例后期验证曲线基本趋于平稳,并未出现明显的大幅上升。因此,更准确的判断是模型已经接近收敛,而不是严重过拟合。
十六、独立测试与线性基线比较
为了判断神经网络是否真正学到了非线性关系,使用相同训练数据建立Ridge基线。
mlp_pred = predict_mpa( model, X_test_scaled ) ridge = Ridge(alpha=1.0) ridge.fit( X_train_scaled, y_train ) ridge_pred = ridge.predict( X_test_scaled ) print( f"Ridge测试RMSE:" f"{rmse(y_test, ridge_pred):.6f} MPa" ) print( f"MLP测试RMSE:" f"{rmse(y_test, mlp_pred):.6f} MPa" ) print( f"MLP测试MAE:" f"{mean_absolute_error(y_test, mlp_pred):.6f} MPa" ) print( f"MLP测试R²:" f"{r2_score(y_test, mlp_pred):.6f}" )实际运行输出:
Ridge测试RMSE:4.696376 MPa MLP测试RMSE:0.470548 MPa MLP测试MAE:0.367447 MPa MLP测试R²:0.998709结果说明,在只输入 \(F,L,b,h\) 四个原始特征的条件下,MLP比线性模型更好地学习了:
\[ \sigma_{\max} = \frac{6FL}{bh^2} \]
中的乘法、除法和平方关系。
这个实验不表示神经网络比解析公式更合适。已知准确公式时,直接计算更快速、更透明。本例使用解析问题,是为了核验神经网络是否学到了正确的输入输出关系。
十七、查看预测结果与残差
【正文插图3:测试集预测和残差】
左图中,散点整体接近理想预测线:
\[ \hat{\sigma}=\sigma \]
右图展示残差:
\[ e_i= \hat{\sigma}_i-\sigma_i \]
残差图可以帮助发现:
- 模型是否整体偏高或偏低;
- 高应力区域是否误差更大;
- 是否存在少量异常工况;
- 误差是否随目标值发生系统变化。
虽然测试集 \(R^2\) 达到0.9987,但高应力区域仍出现较大的个别残差。因此,不能只看一个综合指标。
对于工程应用,还应检查:
\[ e_{\max} = \max_i \left| \hat{\sigma}_i-\sigma_i \right| \]
以及相对误差:
\[ e_{\mathrm{relative},i} = \frac{ \left| \hat{\sigma}_i-\sigma_i \right| }{ \left| \sigma_i \right|+\epsilon } \times100\% \]
十八、预测一个新工况
设新工况为:
\[ F=30\ \mathrm{N} \]\[ L=450\ \mathrm{mm} \]\[ b=22\ \mathrm{mm} \]\[ h=15\ \mathrm{mm} \]
理论应力为:
\[ \begin{aligned} \sigma_{\max} &= \frac{6\times30\times450} {22\times15^2}\\ &= 16.363636\ \mathrm{MPa} \end{aligned} \]
网络预测代码:
new_case = pd.DataFrame( [[30.0, 450.0, 22.0, 15.0]], columns=features ) new_case_scaled = input_scaler.transform( new_case ) new_prediction = predict_mpa( model, new_case_scaled )[0] theory_value = ( 6.0 * 30.0 * 450.0 / (22.0 * 15.0**2) ) print( f"理论应力:" f"{theory_value:.6f} MPa" ) print( f"网络预测:" f"{new_prediction:.6f} MPa" ) print( f"绝对误差:" f"{abs(new_prediction-theory_value):.6f} MPa" )实际运行输出:
理论应力:16.363636 MPa 网络预测:16.391931 MPa 绝对误差:0.028295 MPa新工况位于训练参数范围之内,因此属于插值预测。若输入:
\[ F=100\ \mathrm{N} \]
或者:
\[ h=5\ \mathrm{mm} \]
就可能进入训练范围之外。此时即使程序能够给出数值,也不能直接认为预测可靠。
十九、与第十三节结果应该怎样比较?
第十三节中,RBF-SVR和Stacking也取得了很高精度;本节MLP的测试RMSE为:
\[ 0.470548\ \mathrm{MPa} \]
它与上一节的优秀模型处于接近水平。不过两节的训练方案存在差异:
- 第十三节使用800条开发数据进行基模型拟合;
- 第十四节从开发集中划出160条验证数据;
- 本节只有640条样本直接参与网络参数更新;
- 模型选择和训练过程也不完全相同。
因此,这些结果适合帮助理解算法特点,不应作为严格的排行榜。
若要正式比较,应使用相同的数据划分、相同的评价流程和重复交叉验证。
二十、如何迁移到真实有限元项目?
真实项目可以将输入扩展为:
\[ \boldsymbol{x} = \begin{bmatrix} \text{几何参数}\\ \text{材料参数}\\ \text{载荷参数}\\ \text{边界条件编码}\\ \text{网格参数} \end{bmatrix} \]
输出可以是:
\[ \boldsymbol{y} = \begin{bmatrix} U_{\max}\\ \sigma_{\mathrm{Mises,max}}\\ J\\ K_I\\ N_f \end{bmatrix} \]
一个实际工作流可以写成:
Abaqus参数化建模 ↓ 多工况批量计算 ↓ ODB结果提取 ↓ 工况级数据划分 ↓ 输入与目标标准化 ↓ 神经网络训练 ↓ 验证集选择模型 ↓ 独立工况测试 ↓ 物理合理性检查这里有三个必须保持的习惯。
第一,模型与标准化器应同时保存。只有网络参数而没有训练时的均值、标准差和特征顺序,模型不能可靠部署。
第二,输出量的定义必须统一。例如“固定位置的名义应力”和“全模型最大积分点应力”不是同一个目标。
第三,普通MLP只从数据误差中学习。本节损失函数没有加入平衡方程、本构方程或边界条件,因此它属于数据驱动网络,不属于PINN。
二十一、常见问题
1. 隐藏层越多越好吗?
不一定。网络加深会增加参数量和训练难度。对于本节这种四输入、单输出的小问题,两层隐藏层已经具有足够的表达能力。
2. 神经元越多越准确吗?
不一定。神经元过少可能欠拟合,过多则可能增加过拟合风险和计算成本,应根据验证集结果选择。
3. 损失下降就表示模型可靠了吗?
只能说明优化过程取得进展。最终还需要独立测试、残差分析、物理趋势检查和适用范围检查。
4. 为什么同一代码可能得到略有不同的结果?
网络权重初始化和批次训练包含随机过程。固定random_state可以提高可重复性,但不同软件版本或计算环境仍可能导致最后几位存在差异。
5. 神经网络会自动学会力学定律吗?
不保证。它可能在训练范围内很好地拟合数据,却在外推区域违反单调性、对称性、平衡关系或边界条件。
二十二、课后练习
练习一:改变网络规模
将:
hidden_layer_sizes=(32, 32)改为:
hidden_layer_sizes=(16, 16)计算新网络的参数量,并比较验证集与测试集误差。
其参数量应为:
\[ 4\times16+16 + 16\times16+16 + 16\times1+1 = 369 \]
练习二:比较激活函数
分别设置:
activation="tanh"和:
activation="relu"比较:
- 最佳验证RMSE;
- 达到最佳结果所需的训练轮数;
- 高应力区域的最大残差。
练习三:加入物理特征
构造新特征:
\[ q=\frac{FL}{bh^2} \]
根据力学公式:
\[ \sigma_{\max}=6q \]
使用单变量线性回归预测应力,并与MLP比较。
这个练习可以说明:
合理的物理特征能够显著降低模型学习难度。
练习四:检查物理单调性
固定 \(F,L,b\),让梁高 \(h\) 从10 mm增加到20 mm,绘制网络预测曲线。
理论上应满足:
\[ \frac{\partial\sigma_{\max}}{\partial h} = -\frac{12FL}{bh^3}<0 \]
也就是说,梁高增大时,应力应单调下降。检查神经网络是否遵守这一趋势。
二十三、本节小结
本节从第十三节的集成学习过渡到神经网络,建立了后续深度学习课程需要的基本概念:
\[ \boxed{ \text{神经元} = \text{加权求和} + \text{激活函数} } \]\[ \boxed{ \text{网络预测} = \text{多层前向传播} } \]\[ \boxed{ \text{网络训练} = \text{损失计算} + \text{反向传播} + \text{参数更新} } \]
神经网络能够学习复杂的力学映射,但模型的可靠性仍依赖于数据覆盖、验证方法、物理检查和适用范围。
完整课程附件
完整代码建议放在本节末尾的“附录:完整可运行代码”中。正文保留分段代码,便于学习者跟随讲解逐步运行。
- [完整可运行代码]
- [悬臂梁教学数据]
- [逐轮训练记录]
- [实际运行结果]
- [模型与标准化器文件]
附件地址:
【零基础学智能仿真-14】神经网络基础-从集成学习走向深度学习资源-CSDN下载