1. 我的项目简介
本我的项目来源于飞桨 AI for Science 共创打算的论文复现赛题,复现论文为《AMGNET: multi-scale graph neural networks for flow field prediction》。该论文次要采纳图神经网络,因为在计算流体力学中计算域被网格离散化,这与图构造人造符合。论文中通过训练 CFD 仿真数据,构建一种数据驱动模型进行流场预测。本文将与大家分享基于飞桨实现该论文的复现过程,欢送大家一起沟通学习。
- 论文链接
https://doi.org/10.1080/09540091.2022.2131737
- 原文代码
https://github.com/baoshiaijhin/amgnet
AI for Science 共创打算 AMGNET 论文复现
2. 环境依赖
硬件与框架
本我的项目须要的硬件环境与框架要求如下所示:
- GPU Memory >= 8GB
- 飞桨 == 2.4.0
- Python ==3.7.4
- PGL == 2.2.4
- Matplotlib == 3.5.3
- pyamg == 4.2.3
- scipy
本地装置
通过以下指令实现飞桨以及各个库的装置。
1.conda create -n paddle_env python=3.7
2.conda install paddlepaddle-gpu==2.4.0 cudatoolkit=11.6 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/Paddle/ -c conda-forge
3.pip install pgl==2.2.4
4.pip install matplotlib==3.5.3
5.pip install pyamg==4.2.3
6.pip install scipy
3. 模型介绍
模型框架
AMGNET 是一种基于图神经网络的 CFD 计算模型,该模型能够预测在不同物理参数下的流场。图 3.1 为模型的网络结构图,该模型的基本原理就是将网格构造转化为图构造,而后通过网格中节点的物理信息、地位信息以及节点类型对图中的节点和边进行编码。接着对失去的图神经网络应用基于代数多重网格算法 (Olson and Schroder, 2018 ) 的粗化层进行粗化,将所有节点分类为粗节点集和细节点集,其中粗节点集是细节点集的子集。粗图的节点汇合就是粗节点集,于是实现了图的粗化,放大了图的规模。粗化实现后通过设计的图神经网络信息传递块 (GN) 来总结和提取图的特色。之后图复原层采纳反向操作,应用空间插值法 (Qi et al.,2017 ) 对图进行上采样。例如要对节点 i 插值,则在粗图中找到间隔节点 i 最近的 k 个节点,而后通过公式计算失去节点 i 的特色。最初,通过解码器失去每个节点的速度与压力信息。
图 3.1 网络结构图
论文源码
原文的代码总体上能够分为 4 个局部,别离是数据预处理、图粗化层、图卷积块和图复原层。如图 3.2 所示。
图 3.2 原文代码构造原文代码的关键技术要点如下:
- 编码节点的特色和边的特色:
进而将原始网格数据变换为图构造的数据。
- 应用代数多重网格算法进行图粗化,这个过程中存在大量的稠密矩阵乘法,例如:
其中,A 示意图的邻接矩阵。
- 在图复原层中,源代码采纳空间插值法进行上采样:
其中,ri 示意要复原的节点 i 的特色,wij 示意节点 i 和节点 j 之间间隔的倒数。
飞桨复现的技术要点
在模型中咱们须要应用图神经网络模仿网格,基于图这种数据结构模仿物理场的状态,于是咱们调用了飞桨的 PGL 库。因为模型进行图数据的粗化时进行了大量的稠密矩阵运算,为了进步运算效率,缩小运算工夫,咱们须要对图进行合并。然而 PGL 是以列表的模式存储图,所以在图数据进入模型之前须要预处理,如下:
1.def forward(self, graphs):
2. batch = MyCopy(graphs[0])
3. for index, graph in enumerate(graphs):
4. if index > 0:
5. batch = Myadd(batch, graph)
6.
7. latent_graph = self.encoder(batch)
8. x,p= self.processor(latent_graph)
9. node_features=self.spa_compute(x,p)
10. pred_field = self.decoder(node_features)
11.
12. return pred_field
在基于代数多重网格的图粗化层中,咱们将图粗化到不同的尺度,而后应用 GN 块通过消息传递来总结和提取图的特色。与基于代数多重网格的图粗化层相比,图复原层采纳反向操作。在图复原层中,咱们应用空间插值办法对图进行上采样,空间插值法的实现如下:
1.def knn_interpolate(features, coarse_nodes, fine_nodes):
2. coarse_nodes_input = paddle.repeat_interleave(coarse_nodes.unsqueeze(0), fine_nodes.shape[0], 0)
3. fine_nodes_input = paddle.repeat_interleave(fine_nodes.unsqueeze(1), coarse_nodes.shape[0], 1)
4. dist_w = 1.0 / (paddle.norm(x=coarse_nodes_input - fine_nodes_input, p=2, axis=-1) + 1e-9)
5. knn_value, knn_index = paddle.topk(dist_w, k=3, largest=True)
6. weight = knn_value.unsqueeze(-2)
7. features_input = features[knn_index]
8. output = paddle.bmm(weight, features_input).squeeze(-2) / paddle.sum(knn_value, axis=-1, keepdim=True)
9. return output
应用代数多重网格算法对图进行粗化时,波及大量的稠密矩阵乘法,于是咱们调用了 scipy.sparse 库。然而 scipy.sparse 库中的.dot 办法和现在大部分深度学习框架中的稠密矩阵乘法不同,后者会保留一些 0,而前者只存非零值。这种差别会导致数据维度不统一,进而影响之后的操作。针对这个问题,咱们提出了两种解决方案,第一种是采纳填充的办法保持数据维度,第二种是采纳编码器与解码器将特色维度进行压缩。其中第一种办法与代数多重网格算法保持一致,精度较高。第二种办法进步了约 20 倍的训练速度,然而均方根误差会略高。两种计划的代码如下:
计划 1:
1.if (index_E.shape[1] != standard_index.shape[1]):
2. index_E, value_E = FillZeros(index_E, value_E, standard_index, kN)
3.
4.def FillZeros(index_E, value_E, standard_index, kN):
5. shape = [kN, kN]
6. row_E = index_E[0]
7. col_E = index_E[1]
8. # coo_E = paddle.sparse.sparse_coo_tensor(index_E, value_E, shape)
9. DenseMatrix_E = sp.coo_matrix((paddle.ones_like(value_E), (row_E, col_E)), shape).toarray()
10.
11. row_S = standard_index[0]
12. col_S = standard_index[1]
13. DenseMatrix_S = sp.coo_matrix((paddle.ones([row_S.shape[0]]), (row_S, col_S)), shape).toarray()
14.
15. diff = DenseMatrix_S - DenseMatrix_E
16. rows, cols = np.nonzero(diff)
17. rows = paddle.to_tensor(rows, dtype = 'int32')
18. cols = paddle.to_tensor(cols, dtype = 'int32')
19. index = paddle.stack([rows, cols], axis=0)
20. value = paddle.zeros([index.shape[1]])
21. index_E = paddle.concat([index_E, index], axis=1)
22. value_E = paddle.concat([value_E, value], axis=-1)
23.
24. sp_x = paddle.sparse.sparse_coo_tensor(index_E, value_E)
25. sp_x = paddle.sparse.coalesce(sp_x)
26. index_E = sp_x.indices()
27. value_E = sp_x.values()
28.
29. return index_E, value_E
计划 2:
1.model_1 = paddle.nn.Sequential(2.('l1', paddle.nn.Linear(128, 256)), ('act1', paddle.nn.ReLU()),
3.('l2', paddle.nn.Linear(256, 256)), ('act2', paddle.nn.ReLU()),
4.# ('l3', paddle.nn.Linear(256, 256)), ('act3', paddle.nn.ReLU()),
5.('l4', paddle.nn.Linear(256, 128)), ('act4', paddle.nn.ReLU()),
6.('l5', paddle.nn.Linear(128, 1))
7.)
8.model_2 = paddle.nn.Sequential(9.('l1', paddle.nn.Linear(1, 64)), ('act1', paddle.nn.ReLU()),
10.('l2', paddle.nn.Linear(64, 128)),('act2', paddle.nn.ReLU()),
11.# ('l3', paddle.nn.Linear(128, 128)),('act3', paddle.nn.ReLU()),
12.('l4', paddle.nn.Linear(128, 128))
13.)
14.
15.val_A = model_1(value_A)
16.val_A = paddle.squeeze(val_A)
17.index_E, value_E = StAS(index_A, val_A, index_S, value_S, N, kN, nor)
18.value_E = paddle.reshape(value_E, shape=[-1, 1])
19.edge_weight = model_2(value_E)
4. 预测后果
咱们的模型依照论文给的案例,别离在机翼和圆柱这两种不同的物理场景中进行预测,其中机翼采纳 NACA0012 翼型,示意翼型的网格蕴含 6648 个节点。圆柱体网格蕴含 3887 个节点。所有网格均由三角形和四边形网格组成。翼型在稳态、可压缩和无粘性条件下由 Navier-Stokes 方程管制。圆柱在稳态、不可压缩和无粘性条件下由 Navier-Stokes 方程管制。预测结果表明,咱们的模型预测的流场与实在流场基本相同,可能达到原文的精度,证实复现胜利。咱们模型对圆柱和机翼的预测后果如图 4.1 和图 4.2 所示。原文的预测后果如图 4.3 所示,其中原文的预测后果图采纳了业余的流体力学软件绘制。
图 4.1 雷诺数 =78 的圆柱速度场的预测值与实在值比照图
图 4.2 攻角 =8.0, 马赫数 =0.65 下的机翼压力场的预测值与实在值比照图
图 4.3 原文中雷诺数 =78 的圆柱速度场的预测(左图);攻角 =8.0, 马赫数 =0.65 的机翼压力场的预测(右图)
5. 结语
这是咱们团队第一次加入百度飞桨的论文复现赛,这次较量的经验让咱们受害良多。首先,飞桨的复现赛赛事流程安顿十分正当,咱们被要求浏览论文,而后跑通论文的原始参考代码,这使得咱们对论文的整体思路和原始代码有了清晰的理解。在复现的过程中,咱们疾速理解两种框架之间的差别,应用 API 映射表顺利切换和应用不同的框架。只管有一些 API 无奈齐全对应,但咱们能够进行适当地改变或重写来解决这个问题。第二,飞桨团队为这个较量提供了优良的平台、短缺的资源以及充沛的领导,这使咱们可能更好地了解论文和实现代码。他们的反馈对咱们复现的后果和进一步改良十分有帮忙。最初,特别感谢论文作者杨志双学长以及实验室的李天宇师兄对本次复现工作提供的技术领导和专业知识解说。参加飞桨的论文复现赛是一次贵重的学习和成长机会。我学到了很多对于论文复现和深度学习框架转换的常识,也结识了许多气味相投的同学和专业人士。将来,咱们期待看到更多来自企业、高校、科研院所以及超算的小伙伴,退出飞桨 AI for Science 共创打算,参加建设基于飞桨的 AI for Science 畛域顶尖开源我的项目和开源社区。
论文复现我的项目的代码链接为:
https://aistudio.baidu.com/aistudio/projectdetail/5592458
往期举荐
- 随机 2D 形态四周层流预测!基于飞桨实现图形神经网络
- 基于数据驱动 U-Net 模型的大气污染物扩散疾速预测,晋升计算速度近 6000 倍