关于paddle:随机2D形状周围层流预测基于飞桨实现图形神经网络

36次阅读

共计 6915 个字符,预计需要花费 18 分钟才能阅读完成。

我的项目背景

近年来,疾速流场预测畛域始终由基于像素的卷积神经网络(Convolution Neural Network,CNN)主导。当 CFD 与基于 CNN 的神经网络模型耦合时,来自网格的数据必须在笛卡尔网格上进行插值,而后再投影回网格。然而平均笛卡尔网格的外在几何示意较差,相干的计算成本很大,并不适宜疾速流场预测。与 CNN 不同,图卷积神经网络(Graph Convolution Neural Network,GCNN)能够间接利用于实体拟合的三角网格,从而与 CFD 求解器轻松耦合,解决上述问题。本项目选择复现基于 GCNN 构造的论文《Graph neural networks for laminar flow prediction around random two-dimensional shapes》,验证飞桨框架可能基于 GCNN 模型实现 2D 障碍物四周层流预测的能力。

论文原文

https://hal.archives-ouvertes.fr/hal-03432662/document

原文代码

https://github.com/cfl-minds/gnn_laminar_flow

开发环境与实现过程

开发环境

本文依靠于飞桨框架 2.4 版本实现 2D 障碍物四周层流预测的图卷积神经网络。能够通过拜访飞桨官网的装置文档实现装置。详情见链接:https://www.paddlepaddle.org.cn/install/quick?docurl=/documen…

实现过程

图卷积神经网络的根本组成部分是卷积块。卷积块由一个两步图卷积层和一个两步平滑层组成。在论文中,节点和边上的特色矩阵别离用示意,其中,Nv 和 NE 是节点和边的数量,dV 和 dE 是节点和边上特征向量的维度。卷积层将节点级音讯流传到边缘,而后聚合新的边缘特色并依据以下规定更新节点特色。
其中,v1 和 v2 是由边 e 连贯的两个节点,N(v)是围绕节点 v 的相邻边的汇合。卷积核 fe 和 fv 为单暗藏层的全连贯神经网络,暗藏层中的神经元数量设置为 128。
节点特色更新代码如下:

def update_node_features(node_features, grad_P1, num_filters, initializer, message_fn):
    message_input = paddle.concat([node_features, grad_P1], axis=1)
    updated = message_fn(message_input)
    return updated

边特色更新代码如下:

def update_symmetry_edge_features(node_features, edges, edge_features, edge_feat_dim, initializer, message_fn):
    n_nodes = paddle.to_tensor(node_features.shape[0])
    n_features = paddle.to_tensor(node_features.shape[1])
    reshaped = paddle.reshape(node_features[edges], shape=[-1, 2 * n_features])
    symmetric = 0.5 * paddle.add(reshaped[:, 0:n_features], reshaped[:, n_features:2 * n_features])
    asymmetric = 0.5 * paddle.abs(paddle.subtract(reshaped[:, 0:n_features],
    reshaped[:, n_features:2 * n_features]))
    inputs = paddle.concat([symmetric, asymmetric, edge_features], axis=1)
    messages = message_fn(inputs)  # n_edges, n_output
    n_edges = edges.shape[0]
    updates = paddle.slice(paddle.add(paddle.index_add_(paddle.zeros([n_edges, messages.shape[1]]), edges[:, 0], 0, messages),
        paddle.index_add_(paddle.zeros([n_edges, messages.shape[1]]), edges[:, 1], 0, messages)),
        axes=[0, 1], starts=[0, 0], ends=[n_nodes, messages.shape[1]])
    return messages, updates

在边缘卷积步骤中,对称节点特色
优先于 x1 和 x2,以放弃排列不变性。节点卷积步骤中的求和对于相邻边的排列也是不变的。平滑图层对输入图形执行均匀操作。在三角网格上实现的均匀内核分为两个步骤:

增加此层的动机不是为了音讯流传,而是为了缩小节点因素的空间变异性。它通过相邻节点因素的弥补来压低卷积层的特色图。平滑层代码如下:

class EdgeSmoothing(nn.Layer):
    def __init__(self):
        super(EdgeSmoothing, self).__init__()
    def forward(self, to_concat, node_features, edges, count):
        n_nodes = paddle.to_tensor(node_features.shape[0])
        flow_on_edge = paddle.mean(node_features[edges], axis=1)
        aggre_flow = paddle.add(paddle.index_add_(paddle.zeros([edges.shape[0], flow_on_edge.shape[1]]), edges[:, 0], 0,flow_on_edge[:, :]),
        paddle.index_add_(paddle.zeros([edges.shape[0], flow_on_edge.shape[1]]), edges[:, 1], 0,flow_on_edge[:, :]))
        return paddle.concat([to_concat, paddle.divide(aggre_flow[:n_nodes, :], count)], axis=1)

下图为论文中采纳的网络架构图,该架构图由图卷积层和平滑层组成。输出由三个图像组成,8 个卷积块 / 平滑层重叠造成图卷积神经网络,而后以 1×1 卷积作为输入层。架构中一个重要组成部分是从输出图到卷积块的跳过连贯。在每个平滑图层之后,结点的坐标将连贯到结点因素。这些跳跃连贯为公式中的边缘卷积步骤提供空间信息。

图 1 网络架构图

网络架构代码如下:

class InvariantEdgeModel(nn.Layer):
    def __init__(self, edge_feature_dims, num_filters, initializer):
        super(InvariantEdgeModel, self).__init__()
        self.edge_feat_dims = edge_feature_dims
        self.num_filters = num_filters
        self.initializer = initializer
        self.layer0 = InvariantEdgeConv(self.edge_feat_dims[0], self.num_filters[0], self.initializer)
        self.layer1 = InvariantEdgeConv(self.edge_feat_dims[1], self.num_filters[1], self.initializer)
        self.layer2 = InvariantEdgeConv(self.edge_feat_dims[2], self.num_filters[2], self.initializer)
        self.layer3 = InvariantEdgeConv(self.edge_feat_dims[3], self.num_filters[3], self.initializer)
        self.layer4 = InvariantEdgeConv(self.edge_feat_dims[4], self.num_filters[4], self.initializer)
        self.layer5 = InvariantEdgeConv(self.edge_feat_dims[5], self.num_filters[5], self.initializer)
        self.layer6 = InvariantEdgeConv(self.edge_feat_dims[6], self.num_filters[6], self.initializer)
        self.layer7 = InvariantEdgeConv(self.edge_feat_dims[7], self.num_filters[7], self.initializer)
        self.layer8 = nn.Linear(10, 3)
        self.smoothLayer = EdgeSmoothing()
    def forward(self, node_input, edges, edge_input, smoothing_weights):
        new_node_features_0, new_edge_features_0 = self.layer0(node_input, edge_input, edges)
        smoothed_0 = self.smoothLayer(node_input[:, 0:2], new_node_features_0, edges, smoothing_weights)
        new_node_features_1, new_edge_features_1 = self.layer1(smoothed_0, new_edge_features_0, edges)
        smoothed_1 = self.smoothLayer(node_input[:, 0:2], new_node_features_1, edges, smoothing_weights)
        new_node_features_2, new_edge_features_2 = self.layer2(smoothed_1, new_edge_features_1, edges)
        smoothed_2 = self.smoothLayer(node_input[:, 0:2], new_node_features_2, edges, smoothing_weights)
        new_node_features_3, new_edge_features_3 = self.layer3(smoothed_2, new_edge_features_2, edges)
        smoothed_3 = self.smoothLayer(node_input[:, 0:2], new_node_features_3, edges, smoothing_weights)
        new_node_features_4, new_edge_features_4 = self.layer4(smoothed_3, new_edge_features_3, edges)
        smoothed_4 = self.smoothLayer(node_input[:, 0:2], new_node_features_4, edges, smoothing_weights)
        new_node_features_5, new_edge_features_5 = self.layer5(smoothed_4, new_edge_features_4, edges)
        smoothed_5 = self.smoothLayer(node_input[:, 0:2], new_node_features_5, edges, smoothing_weights)
        new_node_features_6, new_edge_features_6 = self.layer6(smoothed_5, new_edge_features_5, edges)
        smoothed_6 = self.smoothLayer(node_input[:, 0:2], new_node_features_6, edges, smoothing_weights)
        new_node_features_7, new_edge_features_7 = self.layer7(smoothed_6, new_edge_features_6, edges)
        smoothed_7 = self.smoothLayer(node_input[:, 0:2], new_node_features_7, edges, smoothing_weights)
        node_outputs = self.layer8(smoothed_7[:, 0:])
        return node_outputs

我的项目后果


为了展现复现的成果,咱们应用复现模型对圆柱流场进行预测,后果如下:

图 2 预测成果比照图

其中左侧为论文原文中采纳的实在流场,右侧为咱们复现的模型所预测的流场。可见咱们失去的预测值(左边)与实在值(右边)基本一致,模型精度很好。咱们复现的模型在试验后果中的 MAE 为 0.0046,与原论文的后果 0.0043 也十分靠近,验证了飞桨框架可能基于该模型实现 2D 障碍物四周层流预测的能力。

心得体会

百度飞桨的论文复现较量为咱们团队提供了贵重的学习和成长机会。这个较量不仅让咱们深刻理解流场预测这个细分畛域,还锤炼了咱们团队单干和解决问题的能力。当初回顾这次较量,值得称赞的中央有很多。第一,飞桨官网弱小的赛事组织能力,将较量组织的标准和有序。从项目前期宣传、队伍报名、赛前解说、赛中答疑以及后果提交一环扣一环,我的项目安顿有序,每支队伍都分明每个阶段该干什么。第二,较量中,飞桨科学计算团队的技术人员提供粗疏答疑。较量要求咱们仔细阅读论文,并依据论文提供的参考代码应用飞桨进行复现。这个过程不仅须要咱们对深度学习模型有深刻的了解,也须要咱们相熟飞桨框架。作为一个老手,不免遇到各种各样的技术问题,每次找飞桨技术人员,总能失去急躁粗疏的解答。除此之外,官网还会定期跟踪复现的停顿状况,有问题立刻为选手解决问题。第三,加入飞桨的论文复现较量也为咱们关上了更广大的视线。通过此次较量,咱们有机会接触到 AI for Science 这个畛域很多优秀论文。在复现实际的过程中,咱们深入研究了这些论文的办法和技术,加深了咱们对这个畛域的了解,理解到了学术界的最新进展和利用。最初,我要衷心感谢百度飞桨团队所有组织者和工作人员。他们的辛勤付出和业余反对使得这次较量得以顺利进行。也要特别感谢陆林、汪璐、孔德天这些一起加入较量的师兄弟,感激咱们团队中每一位成员的致力和贡献。将来,咱们将持续放弃学习的态度,一直摸索和翻新,争取为推动该畛域的倒退做出奉献。

往期举荐

  • 基于数据驱动 U-Net 模型的大气污染物扩散疾速预测,晋升计算速度近 6000 倍

正文完
 0