冰岛大学高性能计算笔记-全-
冰岛大学高性能计算笔记(全)
001:结语与展望

在本节课中,我们将对高性能计算课程进行总结,并从更广阔的视角探讨HPC的未来发展、相关技能、社区资源以及职业机会。
上一节我们完成了课程的技术内容学习,本节中我们将从理论、技术和范式三个层面回顾课程核心,并展望HPC在学术界和工业界的应用前景。
理论、技术与范式:HPC的持久核心
在课程中,我们学习了高性能计算的不同层面。以下是构成HPC基础、并将长期存在的核心要素。
理论层面
- 并行算法理论:将大问题分解为小问题的思想是根本,不会过时。这包括团队协作等理念。
- 可扩展性理论:我们学习了强扩展(增加算力)和弱扩展(增加工作量)。通信开销和阿姆达尔定律(
S = 1 / ( (1-P) + P/N ))揭示了串行部分的瓶颈。古斯塔夫森定律则从不同角度阐述了扩展的局限性。 - 数值近似理论:真实世界的模拟(如天气预报)依赖于偏微分方程和数值方法,存在数值不稳定性。
- 统计学习理论:对于机器学习和深度学习至关重要。它解释了为何只能从数据中进行概率性近似学习,并强调了验证、交叉验证和正则化的重要性。
技术层面
- 通信技术:消息传递接口(MPI) 历史悠久且仍是分布式内存通信的关键。OpenMP 共享内存模型也因其在高带宽内存访问上的优势而持续重要,但受限于内存规模。
- 问题分解技术:通过网格划分或离散化区域(如在计算流体力学中)来分解问题,是并行计算的基础技术。
- 机器学习与深度学习:这是一种从数据中学习而非依赖物理定律的技术。它处理具有相同输入可能产生不同输出的概率性场景。
- 计算流体力学:这是一个专门的物理领域,但课程展示了其与AI/ML结合的可能性,引出了物理信息机器学习等新兴交叉领域。
范式层面
- 高性能计算 vs 高吞吐计算:HPC强调节点间频繁通信(如天气预报),而HTC(在云计算课程中详述)任务间通信较少(如词频统计)。
- 计算建模:这是一个历经数十年的工程范式,从桥梁设计到基于气流和传感器数据的智慧城市建模。
- 从数据中学习与人工智能:这一范式历史悠久,并将持续发展。它涵盖了超参数优化和自动机器学习等关键主题。
延伸学习与社区资源
为了深化理解并跟上领域发展,以下是一些推荐资源和社区。
进一步学习材料
- 欧洲高性能计算中心网络培训:提供关于MPI、OpenMP、AI等主题的深入培训(通常为期数天)。链接:https://training.prace-ri.eu/
- YouTube频道:课程相关讲座和更新材料会发布在YouTube上。
- 参考书籍:对于想深入底层HPC的学习者,推荐社区专家撰写的关于OpenMP和共享内存的书籍。
行业成功案例与社区参与
- UCC网络成功故事:该网站收集了欧洲各地中小企业应用HPC的成功案例,包括冰岛的企业。这有助于了解HPC在工业界的实际应用。链接:https://www.ucc2.eu/success-stories
- 冰岛高性能计算研讨会:定期举办的社区活动,欢迎继续参与。注册网站:https://ehpc.is
- 仿真与数据实验室:提供与工业界合作的专家支持,并可通过欧洲数字创新中心等计划获得帮助。
- 公开项目示例:例如,利用开源CFD软件模拟弗拉泰里地区的雪崩。
- 大型语言模型项目:涉及冰岛语语料库和下游应用(如翻译、聊天机器人)的欧洲合作项目。
学术深造与职业发展
课程所学的技能为学术研究和职业发展奠定了良好基础。
学士/硕士/博士论文课题
潜在研究方向包括:统计方法、机器学习、科学计算、数据挖掘和并行化。这些方向融合了软件工程、统计学和领域知识,旨在培养计算科学家或数据科学家。
博士研究生会定期举办研讨会,以团队形式进行研究。

云计算课程前瞻
秋季的云计算课程将涵盖更多面向超大规模云平台(如AWS、Azure、Google Cloud)的框架(如MapReduce、Spark)。课程将探讨服务化范式、企业应用以及与大数据、机器学习的结合。虽然底层算法相似,但资源供给和使用模式与HPC有所不同。
职业机会与市场需求
- 求职平台:推荐使用LinkedIn Jobs和Glassdoor寻找机会。
- 热门技能:职位描述中常要求人工智能、机器学习、深度学习、Python、TensorFlow/PyTorch、NLP、大型语言模型等技能。物理信息机器学习专家需求也在增长。
- 云计算技能:Docker、AWS/Azure/GCP、Spark等技能在云计算课程中会重点涉及,能显著提升就业竞争力。
未来展望:欧洲AI工厂计划
欧洲委员会正在大力推进“AI大陆”计划,未来将投入约2000亿欧元。其中的核心是建设专门为AI优化的AI工厂(即超级计算机)。这将创造大量就业机会,不仅需要硬件专家,更需要软件、服务和运营人才。HPC是这一战略的关键支柱。
总结与致谢

本节课中我们一起回顾了高性能计算的核心理论、技术与范式,探讨了进一步学习的资源和活跃的社区,并展望了在学术深造与职业发展方面的广阔前景,特别是欧洲AI工厂计划带来的新机遇。
最后,衷心感谢所有参与本课程的同学,以及为课程做出贡献的讲师和助教团队(Razor, Christian, Chris, Hemo, Oliver等),以及保障Atla/Ua系统运行的幕后技术人员(Zgaro, Elva)。也感谢在线观众的支持和反馈。

课程幻灯片将公开提供。如有兴趣参观数据中心(Borealis或气象局新址),请发送邮件,我们可组织活动。


祝大家未来一切顺利,期待在未来的社区活动中再见!
002:绿色能源与HPC应用 🌱

在本节课中,我们将探讨绿色能源与高性能计算之间的关联。我们将了解HPC如何帮助研究和开发绿色能源技术,同时也会审视HPC系统本身对能源的巨大需求及其对环境的影响。
上一节我们介绍了超参数优化与自动化。本节中,我们来看看绿色能源领域与HPC的交叉应用。
课程回顾 📚
在第十四讲中,我们讨论了超参数优化及其自动化。主要内容包括超参数的重要性、它如何提升机器学习模型的性能,以及传统方法与自动化方法在寻找最优超参数上的区别。我们还探讨了在HPC环境中进行超参数调优的实践。

绿色能源简介 🌞
绿色能源,即可再生能源,是应对气候变化和全球变暖的关键。其主要形式包括:
- 太阳能:通过光伏板将太阳辐射转化为电能。
- 风能:利用风力涡轮机将风能转化为电能。
- 地热能:利用地球内部高温高压水或蒸汽驱动涡轮发电。
- 水能:利用水流或水位差发电。
- 海洋能:利用海浪、潮汐等海洋动力发电。
- 生物质能:利用有机物质转化能源。

一方面,这些能源可以为包括HPC在内的各种应用供电。另一方面,优化这些能源设备(如风力涡轮机叶片形状、光伏板材料)的设计以提高效率,也离不开HPC的模拟计算能力。

气候变化、全球变暖与HPC 🌍
理解以下两个概念的区别很重要:
- 天气:短时间、局部地区的大气状况。
- 气候:长时间、大范围天气模式的统计特征。
- 全球变暖:特指工业革命以来,因化石燃料使用导致全球平均气温上升的现象。
- 气候变化:指全球范围内长期气候模式的改变。
研究这些现象涉及海量参数和复杂的物理过程(如计算流体动力学 - CFD),因此必须依赖HPC进行大规模模拟。同时,HPC系统本身消耗大量能源,使用绿色能源为其供电,有助于减少其对环境的负面影响。

绿色能源应用实例 💨
以下是几个绿色能源领域依赖HPC的具体例子:

风能
风力发电场的布局至关重要。上游涡轮机产生的尾流会影响下游涡轮机的效率,这被称为“尾流效应”。使用CFD进行大规模模拟,可以优化涡轮机布局,减少效率损失。此外,垂直轴风力涡轮机因其灵活性和较小的尾流效应受到关注,其设计优化也需HPC支持。

太阳能
太阳能在理论上潜力巨大。研究表明,一小时的太阳辐射能量足以满足全球一年的能源需求。提高光伏板的转换效率是核心挑战,这涉及到材料科学和光电过程的复杂模拟,同样需要HPC。
氢能
氢能是一种清洁的二次能源。通过电解水制氢是主要方法,但其“绿色”与否取决于驱动电解的电力来源。理想的方式是结合太阳能或风能发电来生产“绿氢”。设计和优化这种风光氢一体化系统,需要进行大规模的耦合模拟,对计算资源要求极高。
HPC的能源需求与循环 ⚡
HPC与绿色能源之间存在着紧密的互动循环:
- HPC助力绿色能源:通过CFD、分子模拟、机器学习等计算手段,HPC帮助设计更高效的风力涡轮机、光伏电池和能源系统。
- 绿色能源供给HPC:HPC是耗电大户,一台E级超算的功耗可达25兆瓦。使用太阳能、风能等为其供电,是实现可持续发展的重要途径。
- HPC的冷却挑战:HPC运行产生大量热量,需要高效的冷却系统。冷却系统本身也消耗能源。优化冷却设计(如液冷)同样需要借助CFD模拟,这又回到了第一步。
以下是HPC能源管理的一个策略示例,展示了不同控制策略下CPU使用率与功耗的关系:
策略A:功耗随负载线性增长。
策略B:采用动态调频调压(DVFS),功耗曲线更平缓。
策略C:激进的任务调度与休眠,在低负载时显著降低功耗。
为HPC中心供电需要稳定、安全的电力传输网络,并考虑连接功率、设计功率和合同功率等关键参数,以确保系统稳定运行。
CFD在绿色能源中的大规模应用实例 🖥️

CFD模拟在绿色能源的研发中扮演着核心角色。以下是一些典型的大规模应用场景:
- 风电场:模拟大气边界层流动与涡轮机相互作用。
- 地热井:模拟地下多孔介质中的热-流-固耦合过程。
- 太阳能集热器:模拟复杂的光-热转换与流体传热。
- 氢燃料电池:模拟电化学反应与多相流。
- HPC冷却系统:优化数据中心的气流组织与液冷回路设计。

这些模拟通常涉及数亿甚至更多的网格单元,必须在HPC集群上并行运行才能完成。

总结与展望 📈
本节课我们一起学习了绿色能源与高性能计算之间多层面的关系:

- HPC是绿色能源研发的加速器:在设备设计、效率优化、系统集成和气候研究等方面不可或缺。
- HPC是能源消耗大户:其巨大的算力需求转化为对电力的稳定需求,并产生热管理问题。
- 绿色能源是HPC可持续发展的关键:使用可再生能源为HPC供电,能有效减少其碳足迹,形成良性循环。
- 两者构成协同闭环:HPC帮助改进绿色能源技术;绿色能源则为HPC提供清洁动力;同时,HPC还用于优化自身的能耗与冷却效率。

随着计算迈向E级(百亿亿次)乃至更高规模,如何利用HPC推动能源革命,同时让HPC自身运行得更“绿色”,将是未来重要的研究与发展方向。



课程结束。祝你好运!
003:超参数优化与自动机器学习 🚀

在本节课中,我们将学习超参数优化和自动机器学习。我们将了解什么是超参数,探索不同的优化算法,讨论如何根据情况选择合适的算法,并学习如何在高性能计算系统上高效地进行超参数优化。
什么是超参数?🤔
超参数是影响机器学习模型学习行为的参数,通常由用户或开发者在训练开始前设定。它们包括架构参数,例如使用的机器学习模型类型、网络层数和种类或激活函数。更常见的是,超参数也指优化过程的参数,例如优化器类型、学习率或动量值。此外,有时整个机器学习流程的参数也可以被包含在内。例如,在处理图像时,可以将图像预处理步骤(如归一化或旋转)纳入超参数优化流程。


为了更全面地理解,我们可以从三个层面来调整预定义的参数:
- 神经架构搜索:专门处理架构参数。
- 经典超参数优化:处理优化过程等参数。
- 自动机器学习:在经典超参数优化的基础上,进一步包含流程参数。
超参数对于机器学习模型的成功至关重要。为了直观展示其重要性,下图展示了一个在CIFAR-10数据集上训练的简单ResNet模型的学习曲线。从图中可以清楚地看到,学习率的选择决定了模型是能达到超过80%的分类准确率,还是完全学不到任何东西(准确率仅为10%,相当于随机猜测)。

这也揭示了为什么选择超参数如此困难。观察这些曲线,我们发现无法做出通用的预测。通常不能说“大学习率好,小学习率坏”,这些决策往往非常精细。
术语与数据准备 📚
在深入之前,我们先明确几个术语:
- 搜索空间:指超参数及其采样区间的集合。
- 配置:指从搜索空间中采样得到的一组超参数值。
- 试验:指使用一组特定超参数配置进行一次完整的训练运行。
进行超参数优化时,需要准备三个独立的数据集:
- 训练集:用于训练模型。
- 验证集:用于评估不同超参数配置的性能,并据此选择最佳配置。
- 测试集:一个完全未参与优化过程的数据集,用于最终测试所选最佳超参数配置的性能。
超参数优化的挑战与基础方法 ⚙️
超参数优化之所以困难,主要原因在于搜索空间通常是多种不同类型参数的混合体。例如,网络层数是离散值,而学习率通常是连续值。在这种设定下,我们无法像梯度下降法那样计算梯度来寻找最小值,因此需要借助其他方法。
这也是超参数优化计算成本高昂的主要原因。只有在模型完全训练完成后,我们才能确定某个超参数配置的性能。在缺乏系统指导的现状下,超参数优化通常仍由开发者通过试错法手动完成。
为了找到更好的方法,我们现在来看看能更系统地进行超参数优化的算法。
最基础或朴素的方法是随机搜索或网格搜索。
- 随机搜索:如其名,从超参数搜索空间中随机采样配置。
- 网格搜索:评估搜索空间中所有可能的超参数组合。
有趣的是,随机搜索已经是一个非常好的起点。当对问题一无所知时,至少应该进行随机搜索。从并行计算的角度看,这两种方法都是“令人尴尬的并行”任务,因为不同试验之间几乎不需要通信,可以独立运行。但同时,它们也会浪费大量计算资源在那些性能可能很差、本不该完全训练完的试验上。
如何加速优化?自适应调度与选择 🚀

我们如何加速并提高效率呢?主要有两种途径:
1. 自适应配置选择
使用贝叶斯优化或进化优化等方法,来挑选更有希望的配置进行评估。这里的主要挑战是,如前所述,我们的搜索空间通常维度很高,这可能使某些方法在如此高维的空间中难以工作。
2. 自适应试验调度(本节重点)
这意味着提前终止表现不佳的试验,并将计算资源重新分配给表现更好的试验。这里的主要问题是:在缺乏足够信息时,如何判断一个试验是好是坏?
通过同时观察和比较多个试验的学习曲线,我们可以获得一些信息。下图展示了在CIFAR-10和Tiny ImageNet这两个计算机视觉基准数据集上训练的ResNet模型的学习曲线。因为我们可以同时获得许多学习曲线,所以可以开始比较它们,从而猜测哪条曲线更好。

逐次减半算法 ⏱️
这就引出了逐次减半算法。其基本思想非常简单,同样是基于“提前终止不良试验,将资源分配给更有希望的试验”这一概念。我们可以在多个时间点进行此操作。
在实践中,流程如下:
- 从一定数量的初始配置开始。
- 训练一段时间(例如几个周期)。
- 根据验证损失或验证准确率对所有试验进行相对排名。
- 终止排名靠后(例如最差的50%)的试验,仅用较好的一半继续训练。
- 再训练几个周期,重复此过程。
最终,我们希望只剩下一个损失最低的试验(在下图中是深绿色线)。

虽然这看起来不错,但核心问题是如何平衡计算资源:是应该在开始时运行更多试验以进行更多探索,还是应该让试验运行更长时间以进行更多利用?
Hyperband算法:平衡探索与利用 ⚖️
Hyperband算法通过以不同“激进程度”执行逐次减半,扩展了经典的逐次减半方法。如下图所示,在第一次运行时,可能每20个周期进行一次削减;下一次则每40个周期进行一次削减,依此类推。这样做的希望是能够捕捉到那些起步较慢但后期表现强劲的试验。
在实践中,这意味着同时进行多组不同激进程度的逐次减半:一组试验完全不进行早停;另一组进行一次早停;再一组进行两次早停;最后一组则非常激进地进行早停。Hyperband同时执行所有这些操作。

异步优化:ASHA算法 🔄
然而,当转移到拥有大量并行计算资源(如HPC系统)的环境中时,经典的Hyperband方法可能会遇到性能问题。问题在于,那些需要比较试验性能以做出决策的时间点,同时也是同步屏障。为了决定哪个试验更好,你至少需要两个试验的结果。但如果试验的运行时间不同(例如,神经网络神经元数量不同会导致运行速度差异),那么运行较快的试验就必须等待较慢的试验,这在高并行环境下会严重降低性能。
因此,我们需要转向异步设置,这就是ASHA算法的作用。该算法在每个决策点,会立即根据已有的信息,对到达的试验进行评估并决定是否继续训练。如下图所示,左侧的Hyperband在每个决策点都需要等待所有试验到达后才继续;而右侧的ASHA则会在每个决策点直接继续训练部分试验,即使它们最终可能不是最佳选择。这样做虽然可能继续训练了一些最终表现不佳的试验,但避免了同步等待问题,并且如果继续训练的试验最终表现良好,你就能更快地获得结果。这是ASHA在计算上非常高效的主要原因。

自适应选择方法:贝叶斯优化 🧠
现在,我们简要概述一下超参数的自适应选择方法。其核心思想是构建一个代理模型,该模型计算成本低廉,但能够模拟原始机器学习模型的性能。有许多不同的算法和库可用,例如基于贝叶斯方法的SMAC、Dragonfly、Optuna或Hyperopt。
它们面临的主要挑战是如何处理同时产生的大量并行结果,因为贝叶斯优化通常是一个顺序过程。此外,如前所述,贝叶斯优化有时在非常高维的搜索空间中也会遇到困难。当然,也可以将贝叶斯优化与前面介绍的Hyperband方法结合起来使用。
动态超参数与基于种群的训练 🧬
一个特殊情况是处理非平稳或动态超参数,即在模型训练过程中发生变化的超参数。典型的例子是随着训练周期变化的学习率。虽然已有许多预定义的调度器(如余弦退火或指数退火),但你也可以优化自己的学习率调度。
这就是Google DeepMind最初提出的基于种群的训练的用武之地。其流程如下:
- 从一组初始试验(种群)开始。
- 训练一段时间。
- 测量试验的相对性能并相互比较。
- 终止表现最差的一半试验。
- 关键步骤:表现不佳的试验会复制表现良好模型的状态(包括所有权重),然后对其超参数进行小的突变(例如将学习率改变一个小的因子)。
- 使用这些模型继续训练。
这种方法通常被称为进化优化。同样,也有其他库实现类似功能,例如Nevergrad或ProTune(一个为HPC系统使用而优化的超参数优化工具)。
如何选择算法?决策指南 🗺️
既然我们已经了解了一些超参数优化方法,那么问题来了:何时使用哪种算法?以下是一个决策指南,它结合了他人经验和我在大规模超参数优化方面的实践。
首先,考虑搜索空间的维度。
- 如果维度很低(例如只有两个超参数),那么最好进行随机搜索或网格搜索,采样配置并完整训练。这是一个简单的起点,如果你不想处理复杂的优化,我始终建议至少进行随机搜索。通常,当只有两个超参数时,从计算资源角度看也是可以承受的。
如果搜索空间变大(例如超过三个超参数),那么下一步是看每个试验的运行时间。
- 如果每个试验运行时间非常短(例如少于5分钟),并且有HPC资源可用,那么可能仍然可以承受随机搜索。
- 然而,深度神经网络的训练通常需要数小时,而不仅仅是几分钟。
接下来,判断你训练的模型类型是否属于“后起之秀”,即模型性能的最大提升发生在训练的后半段。
- 如果是这种情况,使用像Hyperband和ASHA这样的方法可能会过早终止非常好的试验。但根据经验和观察典型的学习曲线,性能的最大提升通常发生在前25%的训练周期。因此,通常可以安全地认为机器学习模型不属于“后起之秀”,从而可以安全地使用Hyperband和ASHA等方法。
- 如果确实是特殊情况(模型是后起之秀),则进入下一个决策点。
下一个决策点是看你用计算资源能够负担的试验总数。
- 如果总数小于200,那么使用贝叶斯优化或贝叶斯优化与Hyperband结合的方法是有意义的。
- 如果你有大量的计算预算,可以运行成千上万的试验,那么你又可以回到最初的方法,即随机搜索,或者更高效的ASHA算法。因为试验数量如此之大时,通常不会出太大问题。
此外,对于非平稳动态超参数(如学习率调度)的情况,你应该切换到进化方法,如基于种群的训练。
其他方法与注意事项 💡

当然,还有一些完全不同的方法来解决这个问题。例如,我们可以使用强化学习,让智能体选择参数组合,并使用这些组合的性能作为奖励。当你使用特殊的架构搜索空间表示时,实际上可以计算梯度,这被称为可微分架构搜索,然后你当然也可以在架构搜索空间上进行梯度下降来寻找最小值。
一些非常有趣的工作正在研究所谓的零成本代理指标及其与模型最终性能的关系。例如,仅使用神经网络的参数总数进行回归预测,这种方法效果出人意料地好。
在大型基础模型时代,即使使用HPC资源,可能也只能负担一次训练运行。一种选择是训练一些比大模型小得多的模型,然后研究如何将这些小模型的超参数有效地迁移到大规模模型上。μ-Transfer方法就是一个常见的例子。
另一个需要提及的问题是过拟合。通常,你在一个固定的数据集上评估超参数配置的性能。如果运行大量试验,你也可能对该验证集过拟合。避免这种情况的技术包括构建集成模型,或在训练后对一定数量的模型进行预测平均。你也可以使用交叉验证来代替在固定验证集上测量性能,但这计算成本非常高,因为你需要为每个交叉验证分割重新运行训练或评估。
在我的研究中还发现,使用低于单精度的计算(如半精度)也可以提高超参数优化循环的泛化性能,这是由于低精度方法固有的正则化效应。
在HPC系统上进行超参数优化 🖥️
现在进入最后一部分:在HPC上进行超参数优化。我们已经看到,像ASHA这样的方法在并行计算环境(如HPC系统)中具有很好的可扩展性。但当你在HPC系统上运行机器学习模型的超参数优化时,实际上需要做出一些选择。
其中一个选择是如何分配你的GPU资源。假设你总共有32个GPU。你可以选择在试验层面或分布式学习层面施加更多的并行性。
- 如果将更多GPU分配给分布式学习,每个模型的训练速度会更快。
- 但这意味着并行运行的试验数量会减少(例如,每个试验用8个GPU,则只能并行运行4个试验)。
- 另一方面,你也可以在超参数优化层面施加更多的并行性,即每个试验使用较少的GPU(例如4个),从而可以并行运行更多的试验(例如8个),但每个试验的训练时间会更长。
你可以通过结合逐次减半的概念来缓解这种权衡。文献中通常是在时间域应用逐次减半(如左图所示),即表现更好的模型被训练更多周期。然而,也可以为逐次减半循环增加一个空间分量,在时间上进行逐次减半的同时,在空间上进行逐次倍增(如右图所示)。这意味着我们不仅终止表现不佳的试验,还将这些被终止试验的GPU分配给被允许继续训练的试验。这将加速表现更好试验的运行时间,因为它们现在有更多的GPU并行运行。

如果只采用左侧的时间域逐次减半,迟早会遇到GPU闲置的问题,因为被停止的试验不再运行,而闲置的GPU没有新的试验可以分配。这会导致计算资源使用效率低下。而采用右侧结合了时间减半和空间倍增的资源自适应逐次倍增算法,则可以通过将空闲GPU分配给继续训练的试验来避免闲置。理想情况下,经过多次倍增后,一个试验可能最终在16个GPU上运行,相比最初在4个GPU上运行,可获得4倍的加速,这非常显著。这种方法非常适合在拥有大量GPU的高性能计算系统上应用。
实践要点总结 📝
最后,我想根据在HPC系统上运行超参数优化工作负载的经验,分享一些实践要点:
- 同时优化:将经典的优化器相关参数和架构参数放在一起优化,而不是分开进行神经架构搜索和经典超参数优化,因为它们通常相互影响显著。
- 尝试不同的激进程度:对于高效的逐次减半或倍增算法,可以尝试不同的削减比例(例如保留前50%、30%或25%),而不仅仅是50%。
- 谨慎对待批次大小:通常不将批次大小视为超参数。较小的批次可能提供更好的泛化性能,但计算效率较低。最佳做法是将批次大小增加到GPU内存所能容纳的最大值,然后专注于优化学习率。
- 精心选择搜索空间区间:例如SGD优化器中的权重衰减值非常敏感。如果区间设置过大,可能导致所有配置性能都很差,从而无法从优化运行中获得有效信息。
- 严格使用三个数据集:这是最重要的一点。你需要训练集来训练模型,验证集来评估不同超参数配置的性能并选择最佳配置,以及一个完全独立的测试集,用于最终测试从验证集中选出的最佳配置的性能。测试集分数才是你应该报告的模型最终性能指标,因为训练和验证性能在进行大量超参数优化后可能存在偏差。

本节课中,我们一起学习了超参数优化的核心概念、主要算法(如随机搜索、Hyperband、ASHA、贝叶斯优化、基于种群的训练),了解了如何根据问题特点选择合适算法,并探讨了在HPC系统上高效运行超参数优化的策略和实用技巧。希望这些知识能帮助你更系统、更高效地优化你的机器学习模型。
004:Transformer模型与CFD应用 🚀
概述
在本节课中,我们将学习深度学习领域一个新颖且强大的模型——Transformer,并探讨其如何与高性能计算资源结合,应用于计算流体动力学领域。
回顾:第12讲内容
上一节我们介绍了格子玻尔兹曼方法及其在CFD中的应用。该方法基于介观尺度,介于微观和宏观尺度之间,不追踪单个粒子,而是基于概率分布函数。我们对比了格子玻尔兹曼方程与传统CFD方法,并讨论了其在并行计算方面的优势,特别是在多孔介质等传统CFD难以处理的领域。然而,该方法在处理湍流等复杂流动时仍需发展,例如需要精确指定粘度参数。
本节内容:Transformer模型简介
本节中,我们将介绍注意力机制及其如何催生出Transformer模型。我们将探讨Transformer的核心组成部分,如自注意力和位置编码,并分析其强大的编码器-解码器架构。
注意力机制基础
注意力机制的思想源于生物学中人类处理信息的方式。人类并非处理所有环境信息,而是有选择性地关注感兴趣的部分。这一概念由William James在1890年提出,并分为非自主性提示和自主性提示两类。
以下是两类提示的工作方式示例:
- 自主性提示:例如,在一堆物品(纸、笔记本、咖啡杯)中,若目标是找书阅读,注意力会直接聚焦于书本。
- 非自主性提示:例如,在一堆物品中寻找“非纸质”或“红色”的物品,这需要基于比较进行选择。
注意力机制模型化
简单的注意力汇聚模型设计如下:自主性提示(称为查询,query)与非自主性提示(称为键,key)进行交互,并结合感官输入(称为值,value),从而引导选择偏向。该模型通过为数据分配权重来实现注意力和自注意力。
当模型接收数据输入时,注意力机制(关注所有数据间的关系)和自注意力机制(评估每个数据元素自身的重要性)共同作用,为数据赋予权重,最终由Transformer模型产生输出。
从序列模型到Transformer
传统的序列模型,如循环神经网络,在处理长序列数据时可能面临梯度消失等问题。而Transformer模型通过注意力机制,允许解码器访问整个输入序列,从而能够有选择地聚焦于输入序列的相关部分。这种将输出映射到所有输入的设计,使其在处理长时间序列数据时更为强大和高效。
Transformer架构与类型
让我们看看Transformer的架构及其不同层如何工作。首先,注意力机制主要有两种类型:加性注意力和点积注意力。两者的原理相似,主要区别在于架构和计算方式。
Transformer架构解析
Transformer的基础架构图展示了其编码器和解码器部分。通过位置编码和注意力机制,数据被输入模型。编码器和解码器中的多头注意力层使得模型能够在输出和整个输入之间建立连接和映射。
由于我们关注其在时间序列等序列数据上的应用,因此将具有时间相关性的数据与位置编码一起输入模型,可以很好地训练模型以预测未见过的序列数据。
以下是Transformer模型的一些特性,源自其原始论文:
- 模型包含编码器和解码器层。在原始工作中,两者均有6层。实际应用中,需要试验以找到最优层数。
- 在计算方面,与LSTM或GRU等序列模型相比,Transformer通常使用更少的层数,这意味着在相同数据下需要更少的计算资源,速度可能更快。
- 经验表明,对于波动较大的数据,Transformer能够用更少的训练捕捉到极端事件或波动,而RNN可能需要更多的数据和计算才能达到类似效果。
这些特点使得Transformer成为处理序列数据的强大选择。原始论文中也指出,Transformer通过因式分解调整和条件计算,显著提高了计算效率,同时提升了模型性能。
CFD应用案例:湍流预测 🌊
现在,让我们看一个流体动力学领域的例子,了解如何使用Transformer模型进行预测。本案例基于实验流体动力学数据。
背景与挑战
在CFD领域,我们有以下几种方法:
- 实验方法:存在局限性。
- 解析方法:仅适用于少数湍流问题。
- 数值方法(CFD):也存在限制。例如,要获得高精度解,需要使用直接数值模拟,但这需要巨大的计算资源。此外,对于某些问题(如多相流),CFD可能效果不佳,并且需要边界条件和初始条件,而这有时难以获取。
为了克服这些限制,除了上一讲提到的格子玻尔兹曼方法,另一种新兴方法是基于数据的机器学习和深度学习。它不依赖于理论或解析方程,但其有效范围和局限性仍在探索中。
湍流问题与数据驱动方法
湍流是经典物理学中的未解难题。CFD中常用的LES和DNS方法基于某些假设,例如均匀各向同性假设,但这些假设在大多数实际流动中并不完全成立。数据驱动方法则无需向模型灌输这些理论,仅基于数据工作,因此在此领域具有巨大潜力。
在我们的研究中,我们使用了一个包含4层编码器和解码器的Transformer模型。数据来自粒子图像测速实验,包含湍流速度的X和Y两个分量。我们分别使用这两个分量来训练模型并进行独立预测。
重要说明:湍流本质上是三维现象。本实验数据虽源自三维流动,但测量仅捕获了二维信息。这并不意味着模型完全缺失三维信息,但需注意其局限性。
预测结果
使用80%的数据进行训练,模型在测试集上对X和Y方向速度的预测结果与真实值吻合良好。当训练数据比例降至60%时,模型精度有所下降,但仍能进行合理的预测,不过随着预测步长增加,精度会逐渐损失。显然,80%的训练比例在此案例中表现更佳。
实践:在HPC系统上运行Transformer模型 💻
接下来,我们简要介绍如何在Deep系统上提交和运行Transformer模型的训练任务。
以下是提交任务的基本步骤:
- 准备必要的文件:批处理脚本、Python代码和数据文件。
- 编辑批处理脚本,指定项目账户、分区、作业名、资源需求(节点数、任务数等)、需要加载的软件模块以及执行命令。
- 使用
sbatch命令提交作业。 - 使用
squeue命令查看作业状态。 - 作业完成后,查看输出和日志文件以分析结果。
在Euler系统上的操作流程类似,但需要注意系统特定的配置和分区选择。
超参数优化与性能分析 ⚙️
超参数优化对深度学习模型至关重要。下表展示了不同模型(LSTM, GRU, Transformer)在类似数据集上的性能对比,其中一项是进行了超参数优化的GRU模型。
| 模型 | 平均绝对误差 (MAE) | R² 分数 | 计算时间 | 备注 |
|---|---|---|---|---|
| LSTM | 数值A | 数值A‘ | 时间A | - |
| GRU (无优化) | 数值B | 数值B‘ | 时间B | - |
| GRU (带优化) | 数值C | 数值C‘ | 时间C | 数据量翻倍,但计算时间更短 |
| Transformer | 数值D | 数值D‘ | 时间D | - |
可以看到,经过超参数优化的GRU模型虽然使用了更多数据,但获得了更高的精度和更短的计算时间,这凸显了超参数调优的重要性。

并行加速效果
增加GPU数量可以显著加速训练过程。在Juwels Booster和Deep系统上的测试表明,当GPU数量增加时,训练速度提升了超过2.6倍。
超参数优化实例
以批量大小为例,在4个GPU上进行超参数搜索。结果显示,当每个GPU的批量大小达到512时,平均绝对误差最小。同时,计算时间也大幅减少。找到最优的超参数组合既能降低误差,又能提升训练速度。

总结
本节课我们一起学习了Transformer模型及其在CFD中的应用。
我们首先回顾了注意力机制的生物学起源及其在深度学习中的模型化。接着,深入探讨了Transformer的编码器-解码器架构、自注意力机制和位置编码,并解释了其相对于传统序列模型的优势。
然后,我们通过一个湍流预测的实际案例,展示了如何使用基于实验数据的Transformer模型来克服传统CFD方法的一些限制。该模型无需任何湍流理论假设,仅基于数据就能做出高质量的预测。
最后,我们简要介绍了在HPC系统上运行此类任务的方法,并强调了超参数优化对于提升模型精度和计算效率的关键作用。
这种基于拉格朗日框架将流动局部属性与时间关联,并输入给LSTM、GRU、Transformer或CNN等序列模型的方法,有助于生成更长时间的湍流数据,用于深入研究,并在工业预测应用中发挥价值。

建议:关于Transformer和自注意力的更多细节,可参考课程资料中提供的视频链接。
祝各位期末项目顺利!
005:格子玻尔兹曼方法及其CFD应用 🧮

概述
在本节课中,我们将学习格子玻尔兹曼方法。这是一种用于流体动力学模拟的介观尺度方法,尤其适用于传统计算流体力学难以处理的复杂问题。我们将探讨其基本原理、与HPC的关联以及在实际湍流等应用中的潜力。
回顾上一讲:深度学习与湍流
上一讲我们讨论了深度学习中的序列模型及其在流体动力学和CFD中的应用。这些应用与高性能计算紧密相关,主要原因有两个:处理大规模数据需要强大的计算能力,以及训练大型深度学习模型本身就需要大量计算资源。
我们探讨了预测在流体动力学中的重要性。流体动力学中有一个未解决的难题,即湍流。我们从课程第二部分开始介绍了CFD,并指出当前研究使用有限的分析方法、实验方法和数值方法(CFD)。结合这三种方法,湍流问题仍未完全解决,对其认知仍然有限。
1941年,Kolmogorov提出了一个假设,解释了湍流中不同尺度之间能量如何从大尺度传递到小尺度。然而,该假设有特定前提,例如流动应是各向同性的,而大多数实际应用中的流动并非如此。对于最复杂的大尺度湍流,目前所知仍然甚少。
我们解释了如何利用深度学习研究湍流。关键在于建立流场数据与深度学习模型架构之间的联系。我们介绍了湍流中的拉格朗日框架,即跟踪流场中单个质点的运动。质点速度与位移、时间存在关联,因此这些数据可视为时间序列或序列数据。这样,我们就能建立这些序列数据与深度学习序列模型之间的联系,并利用这些数据训练模型进行预测。
我们还强调了统计学方法在湍流研究中的适用性,这在文献中已有充分论述。该方法通过处理湍流的不同实现样本来利用统计数据进行研究。我们提到,在湍流中,由于高雷诺数下湍流的高度敏感性,即使在相同实验设置下,我们也无法获得完全相同的可视化结果。
此外,关于深度序列模型,我们需要数据来训练模型。在流体动力学和湍流中,最可靠的数据来源是结合了实验和直接数值模拟(DNS)的数据。我们提到DNS是一种计算代价高昂的数值方法。因此,如何获取大规模数据以使用深度序列模型研究湍流,是一个挑战。
本讲中,我们介绍了一些该领域的最新研究。目前,我们研究小组的湍流团队也正在致力于开发此类应用模型。
引入格子玻尔兹曼方法
现在,让我们进入第12讲。在本讲中,我们将探讨格子玻尔兹曼方程。我们将了解这一方法的基本概念、如何在流体动力学建模中应用它、它在湍流研究中的应用,以及它与高性能计算的关系。
我们将看到,格子玻尔兹曼方法在并行计算方面具有巨大潜力。这正是近年来该方法得到发展的原因之一。借助强大的计算能力,它可以作为传统CFD方法的一种替代方案,用于湍流模拟。
流体动力学中的数值方法
CFD属于数值方法。当我们研究湍流时,我们介绍了三种主要方法:RANS、LES和DNS。我们讨论了每种方法的局限,特别是DNS对计算资源的需求极高,以获取精确解。
在流体动力学领域,我们还有多相流和颗粒 laden 湍流(即流场中包含固体颗粒的两相流)。颗粒与流体之间的相互作用非常复杂,再加上湍流本身的不确定性,使得这类模拟极其困难。此外,在多相流或颗粒流中考虑重力效应,会给问题增加更多复杂性,在方程中求解重力项也需要更多的计算资源。
由于这些原因,某些CFD应用仍存在限制。对于CFD,我们需要能够定义问题的边界条件和初始条件,而有些问题目前仍无法做到。我们讨论过边界条件,例如周期性边界条件和非周期性边界条件。
所有这些限制都存在于数值方法中,而实验方法也存在局限和障碍,特别是成本和扫描范围。我们无法为许多研究进行实验,尤其是考虑到成本、设备、尺度以及需要为实验创造的特定环境。
当然,解析方法是最好的,它为方程提供了更通用的解,但它仅适用于湍流中非常有限的情况。
面对这些限制,人们一直在寻找替代或更优的解决方案。正如上一讲提到的,近年来机器学习和深度学习进入了这一领域,为流体动力学研究提供了新的可能性,并有望帮助我们更好地理解这一复杂现象。
此外,我们还有玻尔兹曼方法,这将在本讲中讨论。它是一种额外的方法,在文献中常被视为传统CFD的替代方法,适用于一些无法使用CFD的情况。
更重要的是,由于其特定的工作方式(我们将在下一张幻灯片中解释),它非常适合并行计算,与HPC协同工作效果良好。
尺度范畴:微观、宏观与介观
要开始了解格子玻尔兹曼方法,让我们先简要介绍一下与之相关的尺度范畴。在处理偏微分方程问题时,通常使用三种尺度范畴来定义方法。
- 宏观尺度:基于连续介质假设。例如,在流体流动中,我们将物质视为连续体进行研究。
- 微观尺度:基于离散粒子。在这种尺度下,我们研究问题域中的每一个粒子。
- 介观尺度:基于统计粒子群。简单来说,它基于粒子群,研究粒子在特定状态下的概率分布。
这三种尺度在计算需求和问题研究视角上存在显著差异。例如,在宏观尺度下,我们可以基于连续条件对问题域进行离散化(如第8讲所述),定义空间域大小和时间步长,然后求解并获得信息。而在微观尺度下,我们需要观察域中的每一个粒子或点,这需要巨大的计算量。介观尺度则提供了不同的计算方式和研究视角,用于研究流体等问题的行为。
从微观到介观
当我们谈论宏观尺度时,正如前面提到的,我们研究的是偏微分方程中流动的体积元。因此,网格离散化使得研究这一尺度成为可能。关于连续性,平均自由程远小于最小特征尺度(如网格尺寸)。这里使用一个称为克努森数的数值。当流动满足克努森数的特定范围要求时,我们可以使用纳维-斯托克斯方程。然而,在CFD中求解NS方程或PDE成本高昂,此外,离散化可能导致求解误差,并且为这类复杂问题生成网格的计算成本也很高(有时可占计算的50%)。在某些环境(如多孔介质)中,可能无法使用这种方法。
深入微观方法,正如所述,我们观察粒子运动,因此有粒子运动控制方程,进而可以得到整体流动运动。它基于分子动力学方法,依据牛顿第二定律解释单个粒子,并以高精度求解问题。然而,计算成本非常高,因为我们需要对每一个粒子进行计算。因此,这种方法仅限于短时间的研究,且只能处理数百万量级的粒子。这是该方法的一个局限,无法用于所有问题或大规模问题。微观方法的优势在于易于理解和实现,因为我们对每个粒子进行处理,然后推广到整个流动。但由于这些限制,让我们看看介观方法。
因此,我们可以看到介观平均方法。正如所述,它既不基于流动的体积元,也不基于单个粒子,而是基于统计视角。实际上,微观方法基于单个粒子,而这里基于统计视角,不完全是单个粒子,它处理长时间和大数据集,从而解决了微观方法中的一些问题。
其目标是理解属性的平均值和概率分布,这引出了概率粒子分布函数的概念。这是格子玻尔兹曼方法的基础概念,也是它如何用于流场和流动运动的核心。
格子玻尔兹曼方法的基础
那么,我们可以说介观方法介于微观和宏观方法之间。它不基于粒子,也不基于流动的体积元,基本上是从统计角度看待数据,因此可以用于更广泛的领域,并解决我们讨论的其他尺度范畴中存在的一些限制。
基于上述讨论,我们可以说格子玻尔兹曼方法与早期的格子气自动机(LGA)模型有关。在LGA中,目的是模拟气体中许多单个粒子的相互作用行为。其思想是将气体建模为稀疏粒子,它们沿着规则网格以一组可能的速度(我们称之为速度集 c_i)运动。粒子之间的碰撞通过一组弹性碰撞规则处理,这些规则必须守恒系统的质量 M 和动量 P。
这是解释LGA的基本思想。因此,在这张幻灯片中,我们可以看到如何基于特定的网格定义粒子的运动,并找到它们之间的碰撞,以及碰撞前后发生的情况。我们将基于格子玻尔兹曼方程来解释这一点。
关于LGA方法,有不同的模型被提出来模拟气体粒子。其中之一是HPP模型,由Hardy, de Pazzis和Pomeau于1973年提出。该模型在二维而非三维中工作,因此被称为二维模型。对于每个节点,有四个可能的方向,使得粒子在四个方向上具有完整的速度选择。在每个时间步,当粒子移动到下一个时间步时,如果节点中的粒子数量和所有粒子的总概率在碰撞前后守恒,将会有一个关于质量和动量守恒的决策。这是该思想的基础。
然而,由于一些限制,例如三个或四个粒子之间的碰撞不会导致构型改变,粒子会像没有发生碰撞一样继续运动;或者当三个或四个粒子在同一节点相遇时,唯一满足质量和动量守恒的构型就是碰撞前的相同构型。因此,HPP模型需要一些特定陈述,这也限制了该模型在某些应用中的使用。


LGA模型的演进:FHP模型
因此,正如所述,HPP模型有一些优点和缺点。缺点大多与该模型的微观尺度有关,不能说是完全平衡的,系统具有完全平衡。此外,HPP模型通常面临各向异性条件,这是该模型可用性和弱点中最重要的问题之一。同时,系统的微观状态总是在变化。因此,你可以看到该模型的更多弱点和缺点。然而,在某些应用中,它仍然有效,并且具有此处列出的一些优点。
可以说,LGA的下一代模型是FHP模型,于1986年提出。实际上,我们看到HPP的一些限制,它改变了网格,从方形格子变为六边形格子。这使得它适用于可压缩方程。正如我们在这个模型中看到的,在FHP模型中,速度矢量根据模型中定义的格子获得了严格的方向。这是该模型中呈现的差异。然而,它有一些道德优势并解决了一些限制。例如,FHP模型的一个弱点在于获取宏观量。实际上,因为这个模型基于微观尺度,所以找到宏观尺度的量将是困难的,因为在微观尺度下,系统受到随机波动的影响。这些随机波动在连续极限中会消失。此外,统计噪声的问题在这个模型中仍然存在。然而,它解决了先前模型的一些限制,正如我们所说,并且它比HPP有更多的应用。但是,格子的定义引出了介观思想下格子玻尔兹曼方程如何工作的概念。
格子玻尔兹曼方程的核心
因此,在这里我们可以看到格子玻尔兹曼方法的一个模型。我们可以看到每个粒子具有不同的速度。它基于概率分布函数 f(x, t, p),其中 x 是位置,t 是时间,p 是动量。我们关注这些量的守恒状态。

这里的核心概念是,如果没有碰撞,粒子的量在受到外力前后是相等的;如果发生碰撞,粒子在碰撞前后的量将会不同。这是格子玻尔兹曼方法中最重要的基本原理,用于观察基于此方程的构型如何工作。
那么,正如所述,这里的碰撞是关于碰撞前后发生的情况。如果碰撞发生,会有一个概率分布函数,这是由于碰撞前后的差异造成的。但当没有碰撞时,碰撞前后的函数是相同的。因此,这种条件之间的差异将定义一个函数或方程,从而引出格子玻尔兹曼方程。在这个方程中,实际上忽略了外力;当存在外力时,方程的分类和设置会有所不同,我们将稍作讨论。

玻尔兹曼方程的数学形式
我们可以说,格子玻尔兹曼基于麦克斯韦于1859年提出的思想,即研究单个粒子行为并不重要,如果我们能够研究粒子的概率,那才是关键,它帮助我们理解情况。这就是为什么它使用概率分布函数 f。
需要定义该函数,并且在这类研究中,有三个独立变量:位置、时间、动量(可以是速度或动量)。由于位置和动量在三维中各有三个分量,因此此类方程总共有七个变量。正如我们所提到的,存在碰撞和平衡的基本原理来得到方程。在这张幻灯片中,我们展示了平衡是碰撞前后不同条件以及碰撞后发生的情况。这种差异引出了方程。
在这个绿色框中,你可以看到玻尔兹曼方程的样子。分布函数 f 的梯度如何导致一个函数,该函数表示碰撞前后分布函数 f 的差异。关于定义格子玻尔兹曼方程的函数 Ω,如果它等于0,我们可以在格子玻尔兹曼方程中说右手边为0,这意味着没有碰撞,即粒子条件在碰撞前后状态相同。这种条件可能发生在某些流体中,例如,这里有一些例子:密度非常低时(粒子非常少),以及当克努森数大于10时(意味着粒子没有相互作用)。在非常稀薄的等离子体中也可能发生。但我们应该提到,这个方程没有诸如粘度 ν、热导率 κ 等微观属性,这些属性很重要。因此,我们需要一种方法从介观方程中获取这些信息。
从分布函数到宏观量
因此,观察玻尔兹曼方程,我们可以说右侧的概率分布函数 f 是该方程的关键概念。定义这个函数将为每个问题求解方程。例如,麦克斯韦-玻尔兹曼分布就是为该函数定义的,你可以在这里看到它,它使用了粒子的局部平衡。还有另一个算子,称为BGK算子,于1954年提出,作为定义碰撞发生时该函数的算子。

另外,关键点是我们如何定义流动的属性,即宏观量。我们可以看到,通过对概率分布函数 f 进行积分,我们可以得到这一点。你可以看到我们如何计算质量 ρ、动量 ρu 和能量 E,并可以从这个方程得到流速 u。那么,这就是我们如何获取宏观量的方法。
格子玻尔兹曼方法的特点
让我们回顾一下格子玻尔兹曼方程的一些特点。实际上,格子玻尔兹曼方程基于粒子类别,我们可以说是介观的。它基于粒子,但着眼于它们的概率,因此定义了介观尺度。正如所述,它是传统CFD的替代方法之一,适用于无法用常规CFD解决的问题。它也适用于复杂几何形状,这很好。并且,正如所述,由于该方法包括碰撞步骤及其目标,它在并行计算中运行良好。

对于一些使用CFD的特定情况,传统CFD处理存在一些障碍和困难,例如多孔介质、湍流、多相流和反应流,格子玻尔兹曼是一个很好的选择,并且运行良好。然而,在某些方法中,我们需要使用其他东西,例如,当我们处理高雷诺数的湍流时,正如在这个定义中所说,粘度没有定义,并且湍流中的粘度定义仍然是一个问题。
正如我们之前关于湍流的讨论,当流动从最大尺度到小尺度时,粘度的行为不同,因此在最大尺度定义粘度是一个问题。所以,这是将其用于湍流时需要考虑的一些关键点。然而,格子玻尔兹曼也存在一些限制。例如,模拟具有密度比和高马赫数的气-液两相流具有挑战性。模拟具有曲线边界的几何形状也有一些困难的应用。
然而,研究湍流中粒子的能力使其成为处理湍流复杂性时的一个绝佳选择。
与纳维-斯托克斯方法的比较
因此,我们可以看到纳维-斯托克斯方法与格子玻尔兹曼方法之间的比较,特别是当我们在PDE上工作时,我们可以看到在某些层面上格子玻尔兹曼工作得更好。然而,它在应用中仍然有限,许多研究正在进行中,以发展格子玻尔兹曼方法在流体动力学领域,特别是在湍流中的应用。

例如,在常规CFD的纳维-斯托克斯方程中,有一个称为CFL条件的项,必须计算并满足该条件才能使用CFD,但在格子玻尔兹曼中,它始终为1。关于压力,当处理可压缩/不可压缩流动时,必须从单独的方程获得压力,但在格子玻尔兹曼中,可以通过状态方程计算压力。

格子玻尔兹曼与高性能计算
格子玻尔兹曼计算也在这里展示了一个例子,说明问题域如何离散化,以及我们如何使用格子玻尔兹曼。例如,这里当然可以使用不同的模型,这里是二维的。D2Q9是一个二维模型。正如我们所见,每个粒子在每个时间步的状态取决于相邻粒子。这导致属性在局部更新,从而减少了计算槽和时间。
格子玻尔兹曼非常适合在并行计算中使用,正如我们之前提到的。关于可以使用的模型,对于二维或三维有非常不同的定义,基于问题的维度和几何形状,我们需要选择合适的模型。
正如所述,基于问题的维度选择合适的模型很重要,我们还需要查看这里应该定义多少速度矢量。例如,我们可以在示例中看到格子玻尔兹曼方法的性能评估,称为特定后处理上的流体模拟。
在右侧,我们可以看到弱扩展结果。例如,当我们说D3Q15时,3是维度,所以问题是三维的,15是速度的数量。当速度数量从15增加到27时,我们可以看到性能没有太大变化,并且较低速度数量的性能优于较高的。因此,在弱扩展中变化不大。然而,在左侧的另一个示例中,我们可以看到在单个节点上的计算,使用一个MP后处理,例如处理三维问题,速度数量再次从15增加到17。我们可以看到,当核心数超过14,250时,具有较多速度(19和27)的模型比15的模型工作得更好。因此,这里需要进行一些评估,以确定哪个在性能上更好。此外,我们可以看到并行计算对此类模型的影响。
这里还有格子玻尔兹曼在椭圆形气缸上的另一个应用示例。它在一个封闭的狭窄域中下落,并研究了不同的效应。文献中有一些近似公式,这项研究使用格子玻尔兹曼进行比较。当然,对于这种情况,使用格子玻尔兹曼是处理这种特定气缸情况的绝佳选择,这展示了格子玻尔兹曼在此类研究中的能力。
总结
我们完成了关于格子玻尔兹曼及其CFD应用的讲座。正如在简短讲座中所说,我们尝试介绍了这种方法。
这里有一个展示介观尺度样子的视频,我建议你观看这个视频,链接在讲座的Earthmans中。当然,格子玻尔兹曼是一个需要更详细研究的方法,这里只是一个简短的介绍。如果你感兴趣,有参考文献集,你可以查阅并了解更多。当然,这是一个开放的研究领域,如果你感兴趣,你可以将其用于HPC系统进行特定应用,并观察它在并行计算中的表现。

祝你好运!
006:深度序列模型与CFD应用 🚀

在本节课中,我们将学习深度序列模型在计算流体动力学领域的应用。我们将探讨湍流预测的重要性、可用于预测的方法,以及深度学习中的序列模型如何在此领域发挥作用。同时,我们也会了解高性能计算在这些应用中的角色。
上一节我们介绍了OpenFOAM软件及其在并行计算中的应用。本节中,我们来看看如何将深度学习技术,特别是序列模型,应用于流体动力学中的湍流预测问题。

湍流预测与深度学习 💨

在流体动力学领域,处理问题的方法主要有实验、解析和数值模拟。对于湍流这类复杂问题,数值模拟方法包括雷诺平均法、大涡模拟和直接数值模拟。这些方法,尤其是后两者,需要巨大的计算资源。
与传统的数值方法相比,深度学习在处理湍流这类非线性、时空相关的复杂现象时展现出巨大潜力。流体动力学中的数据通常是空间和时间的函数,具有时空特性。因此,我们需要找到深度学习模型与流场物理特性之间的连接。
循环神经网络与序列模型 🔄
循环神经网络是专门处理序列数据(如时间序列)的深度学习模型。在流体动力学,特别是湍流研究中,我们可以利用RNN来处理湍流数据的时间序列。
以下是RNN在湍流预测中的优势:
- 处理非线性现象:深度学习擅长处理像湍流这样的非线性现象。
- 提取隐藏特征:能够从复杂数据中提取隐藏的模式和特征。
- 模型组合提升性能:通过组合不同的深度学习模型,可以进一步提高预测性能。
LSTM与GRU:解决长期依赖问题 🧠
标准RNN在处理长序列数据时,会遇到梯度消失问题,导致无法有效学习长期依赖关系。
长短期记忆网络是RNN的一种变体,它通过引入门控机制解决了梯度消失问题。LSTM的结构包含输入门、遗忘门和输出门,能够有选择地保留或丢弃信息,从而记住长期的序列模式。
门控循环单元是LSTM的一种简化变体。GRU将LSTM的输入门和遗忘门合并为单一的更新门,减少了模型参数和计算量。文献表明,GRU通常能达到与LSTM相近的性能,但训练速度更快,这在计算资源有限时尤为重要。
连接序列数据与深度学习模型 🔗
在流体动力学中,我们需要建立一个框架,将物理问题中的序列数据与深度学习模型连接起来。这涉及到如何定义数据的时间序列,以及如何找到不同物理量(如速度、位移、时间)之间的相关性。
在流体力学中,主要有两种描述框架:
- 欧拉观点:在空间固定点观察流体的变化。
- 拉格朗日观点:跟随流体质点,观察其属性随时间的变化。
对于序列模型,拉格朗日框架更为合适,因为它天然地提供了质点属性(如速度、位移)随时间演变的时间序列数据。我们可以用这些时间序列数据来训练模型,例如,用过去的位移和速度来预测未来的速度。
数据来源与实验案例 📊
训练深度学习模型需要可靠的数据。在湍流研究中,数据主要来自实验和CFD模拟。其中,直接数值模拟能提供最精确的数据,但计算成本极高。
本节将介绍一个研究案例,该案例使用实验数据来训练LSTM和GRU模型。实验在一个水箱中进行,通过粒子图像测速技术追踪示踪粒子的运动,从而获得位移、速度等时间序列数据。
该数据集包含了大量粒子的运动信息。我们可以将其按比例(如60%或80%)划分为训练集和测试集,用于训练和评估模型。
模型性能与HPC角色 ⚡
实验结果表明,LSTM和GRU模型在湍流粒子轨迹预测上表现良好,预测结果与测试数据吻合度高。
在计算性能方面,使用CPU时,GRU通常比LSTM快5%到12%。然而,当使用GPU进行计算时,两者的速度差异变得不明显。这可能与数据规模有关,当数据量不够大时,GPU的加速优势可能被数据传输等开销抵消。这凸显了在处理大规模数据和复杂模型时,分布式深度学习和高性能计算的重要性。
本节课中我们一起学习了深度序列模型(特别是LSTM和GRU)在计算流体动力学,尤其是湍流预测中的应用。我们探讨了如何将流体物理的序列特性与深度学习模型相连接,并分析了一个使用实验数据成功预测湍流粒子运动的案例。理解模型选择、数据框架以及高性能计算的作用,对于在该领域有效应用深度学习技术至关重要。



在下一个短讲座中,我们将继续探讨CFD与高性能计算的其他应用领域。
007:OpenFOAM软件与CFD应用 🚀
在本节课中,我们将学习开源计算流体动力学软件OpenFOAM。我们将了解其基本概念、如何在HPC系统上进行并行计算设置,特别是域分解方法及其与MPI的关系。
上一节我们介绍了CFD应用中的湍流模拟方法。本节中,我们来看看一个具体的开源工具——OpenFOAM。
OpenFOAM简介 🧮
OpenFOAM是一款用于流体动力学领域的开源软件。由于它是免费和开源的,用户可以针对新的问题设计计算域和边界条件,编写新的求解器,并进行修改以用于高性能计算。
以下是OpenFOAM的一些关键特性:
- 它是开源代码,允许用户进行定制和修改。
- 它适用于层流和湍流等多种流动状态。
- 它支持可压缩和不可压缩流动的模拟。
- 它具备在并行计算环境下运行的能力,这对CFD模拟至关重要。
软件结构与能力 🏗️
OpenFOAM基于C++工具箱构建,用户可以为其定制代码。这意味着研究人员可以为特定的层流或湍流问题从头开始创建代码,使用其工具箱语言指定问题域、几何形状和条件,从而建立新模型。
OpenFOAM还具备以下能力:
- 它与可视化软件连接,可以进行后处理和数据可视化。
- 它能够在并行环境下运行。
- 除了核心的CFD应用,它也可用于解决量子力学等问题。
在工业界,它被广泛应用于汽车制造、工艺工程和环境工程等领域。在基础物理研究中,它也被用于流体动力学模拟,以帮助理解湍流等复杂现象的内部过程。
并行计算与域分解 🔄
在CFD中进行并行计算和高性能计算应用,需要对问题的计算域进行分解。
上一节我们介绍了并行计算在特定问题域上的工作原理。本节中,我们具体看看如何在OpenFOAM中实现。
域分解需要为问题的计算域创建子域。以下是一个MPI域分解的示例,展示了所需存储的数量以及子域之间的重叠区域。边界数据需要在这些重叠区域进行交换,特别是在非稳态问题中,每个时间步都需要从相邻子域更新信息以推进求解。
在OpenFOAM软件中,域分解方法以标准形式提供。用户可以通过命令运行,主要有以下几种方法:
- simple: 简单的几何分解方法。
- hierarchical: 沿指定方向进行分层分解。
- scotch: 基于图论的分解方法,旨在平衡负载并最小化通信。
用户需要根据问题的几何形状和网格类型(如对称几何、复杂形状或结构化/非结构化网格)选择最有效的分解方法,以在HPC上获得高效的求解和准确的结果。
实践示例:在HPC上运行OpenFOAM 💻

为了展示OpenFOAM的并行计算示例,我将分享一个用于在HPC系统上提交作业的批处理脚本。
以下是在批处理脚本中进行设置的关键步骤:
- 在第二行指定你的电子邮件地址。
- 使用系统上可用的分区(例如
ua)。 - 设置节点数量(例如
1)和每个节点的任务数(这关系到MPI进程数)。 - 根据需要调整内存和最大作业运行时间。
- 指定标准输出和错误输出的文件路径。
- 在下一部分中,指定要运行的模拟案例目录地址。
关于OpenFOAM示例作业,你需要在脚本中加载必要的软件模块,然后执行一系列命令:
sourceOpenFOAM的环境配置。- 从教程目录复制示例案例文件。
- 生成计算网格(
blockMesh)。 - 进行域分解(
decomposePar)。请注意,分解方法需要在案例目录下的system/decomposeParDict文件中预先设置,你可以尝试不同的方法并比较计算时间和结果。 - 使用
mpirun命令并行运行求解器(如simpleFoam)。 - 并行计算完成后,重建整个域的结果(
reconstructPar)。 - 将结果转换为可视化格式(如
foamToVTK),以便用ParaView等软件查看。


我将把原始的批处理文件分享到课程平台,你可以访问并使用它。
分解方法的选择与影响 ⚖️

选择不同的域分解方法会影响并行计算的性能和负载平衡。
以下是各种分解方法的简要对比:
- simple: 基于x, y, z方向进行划分。优点是指定简单;缺点是仅适用于均匀的结构化网格。
- hierarchical: 沿单一方向进行分解。它比
simple方法更灵活,性能通常也更好。 - scotch: 基于图形拓扑的方法,通过划分单元连通性来平衡负载并最小化子域间通信。在处理复杂的非结构化网格时,使用
scotch方法通常是更好的选择。
边界条件的重要性 🧱
在域分解中,边界条件的处理至关重要。
除了了解邻居子域,每个进程还需要知道其边界是否为物理边界。这有助于避免编程错误并正确应用边界条件。主要有两类边界条件:
- 周期性边界条件: 任何边界块都被视为内部块。这意味着,位于计算域边缘的子域也被假定为内部块。应用这种条件时,只需为每个进程正确定义邻居映射即可。
- 非周期性边界条件: 设置方式不同。对于没有邻居的边界子域(例如计算域的边缘),我们需要明确指出没有来自“不存在块”的信息。在使用MPI时,可以利用
MPI_PROC_NULL通信子来优雅地处理这种情况,避免向不存在的进程发送或接收数据。
总结 📚
本节课中,我们一起学习了OpenFOAM软件及其在CFD中的应用。你看到了OpenFOAM的实际应用,了解了它如何进行域分解,以及如何结合MPI进行修改和优化。我们特别讨论了边界条件的影响,以及如何使用周期性和非周期性边界条件。因此,当使用MPI时,我们可以为此类问题实现高效的并行计算。
建议你观看课程视频中提供的链接,以更直观地了解MPI,特别是针对我们所讨论的CFD应用是如何工作的。

我们将在下一讲继续。祝你好运!
008:CFD与并行计算 🚀

在本节课中,我们将学习计算流体动力学(CFD)与并行计算的结合应用。我们将回顾湍流的基本概念,探讨不同的CFD模拟方法(如RANS、LES、DNS),并深入了解并行计算架构的拓扑结构及其在CFD问题中的效率优化。
回顾与引言
上一节我们介绍了CFD的基础,包括其历史、基本原理以及网格离散化的重要性。本节中,我们将继续探讨CFD,特别是其在湍流模拟中的应用,并分析如何利用并行计算来高效解决这些问题。
湍流流动概述 🌊
湍流是一种高度非线性的复杂流动现象,其特性由雷诺数(Re)决定。雷诺数是一个无量纲参数,定义为惯性力与粘性力的比值。当惯性力占主导时,流动通常呈现湍流状态。
以下是湍流与层流的一些关键区别:
- 层流:流动平滑、有序,具有可预测的流线模式。
- 湍流:流动混乱、无序,充满了不规则的速度脉动和涡旋。

湍流广泛存在于自然界和工程应用中,因此对其进行准确模拟至关重要。

湍流流动示例
湍流可分为内部流动和外部流动。
内部流动示例
内部流动指在封闭管道或通道内的流动。例如,在圆管流动中:
- 当雷诺数 Re < 2300 时,为层流。
- 当 2300 < Re < 4000 时,为过渡流。
- 当 Re > 4000 时,为充分发展的湍流。
在湍流中,时均速度剖面与层流有显著不同,近壁区的速度梯度更为陡峭。
外部流动示例
外部流动指物体外部(如绕流)的流动。这在汽车工业(研究阻力系数 Cd 和升力系数 Cl)、航空航天等领域有重要应用。
CFD在湍流模拟中的应用 🧮
对于湍流模拟,主要有三种数值方法:雷诺平均纳维-斯托克斯模拟(RANS)、大涡模拟(LES)和直接数值模拟(DNS)。
RANS方法
RANS方法求解流动变量的时均值。它在控制方程中引入了雷诺应力项,需要通过湍流模型(如 k-ε模型)来封闭求解。
- 优点:计算成本相对较低。
- 缺点:对于复杂几何或强分离流,精度可能不足。
LES方法
LES方法直接解析大尺度涡,而通过亚格子尺度(SGS)模型模拟小尺度涡的影响。
- 优点:比RANS能捕捉更多流动细节,精度更高。
- 缺点:计算成本高于RANS。
DNS方法
DNS方法直接解析所有尺度的湍流运动,无需任何湍流模型。
- 优点:精度最高,能提供最详细的流动信息。
- 缺点:计算成本极其高昂,通常与雷诺数的 ~Re^3 成正比。
选择准则:需在计算成本与所需精度之间进行权衡。工业应用常采用RANS,而研究和高端设计则会使用LES或DNS。
并行计算架构与拓扑 🔗
为了高效运行CFD模拟,尤其是LES和DNS,必须利用并行计算。并行计算的效率很大程度上取决于处理器之间的互连拓扑结构。
拓扑结构定义了处理器之间的连接方式,主要分为两类:
- 静态拓扑:连接模式固定,如环、网格、超立方体。
- 动态拓扑:连接模式可随时间改变,如交叉开关、多级交换网络。
理想的拓扑应具有:每个节点的连接数少、节点间距离短、节点间可选路径多以及良好的可扩展性。
并行计算在CFD中的实现

在CFD中实现并行计算,通常需要将计算域分解为多个子域,每个处理器负责一个子域的计算。

关键点:子域之间需要有重叠的“幽灵层”或“边界层”,以便在每次迭代时与相邻处理器交换边界信息。对于2D问题,需要交换线数据;对于3D问题,则需要交换面数据。
消息传递接口(MPI)是实现这种分布式内存并行计算的标准工具。一个典型的CFD并行程序结构包括:
MPI_Init(...); // 初始化MPI环境
// ... 确定进程ID和总数 ...
// ... 主计算代码(包括域分解、通信、求解) ...
MPI_Finalize(); // 结束MPI环境

高性能计算效率考量 ⚡

评估HPC效率有两个主要目标:
- 在固定问题规模下,减少计算耗时。
- 在固定计算成本下,求解更大规模的问题。
影响并行效率的主要因素包括:
- 并行内容比例(阿姆达尔定律)
- 负载平衡
- 通信开销
- 冗余计算
例如,在一个使用390万个网格的DNS模拟中,若每个MPI进程分配的网格点过少(如仅216个点),则通信开销将占主导,导致并行效率低下。通常,为了达到50%以上的并行效率,每个MPI进程需要处理至少2000个网格点。
先进计算技术示例
随着计算技术的发展,一些方法被用于加速CFD模拟:
- 高阶格式:可以提高精度,有时在相同精度下减少所需网格量。
- 多重网格求解器:加速偏微分方程求解过程。
- GPU加速:利用图形处理器进行大规模并行计算,特别适用于显式计算(如涉及“半时间步”的龙格-库塔方法),可显著减少计算时间。
总结
本节课中,我们一起学习了:
- 湍流的基本特性及其与层流的区别。
- 三种主要的CFD湍流模拟方法:RANS、LES和DNS,了解了它们各自的优缺点和适用场景。
- 并行计算架构的拓扑结构及其对计算效率的重要性。
- 如何在CFD中实现并行计算,包括域分解、边界通信以及MPI的基本使用框架。
- 评估和优化HPC效率的关键因素,并通过实例看到了负载平衡和通信开销对性能的影响。
- 一些先进的加速计算技术,如GPU加速在高保真模拟中的应用。



CFD与高性能计算的结合是解决复杂工程和科学问题的强大工具。理解这些基本原理和方法,有助于我们更有效地利用计算资源,探索流体世界的奥秘。
009:计算流体力学入门 🚀

在本节课中,我们将要学习计算流体力学的基础知识,了解其应用背景、核心方程、数值方法,并探讨高性能计算在解决复杂CFD问题中的关键作用。
上一节我们介绍了分布式深度学习及其应用。本节中,我们来看看计算流体力学这一传统高性能计算的核心应用领域。
课程背景与讲师介绍
大家好,欢迎来到高性能计算课程。这是第8讲,主题是计算流体力学。在开始之前,请允许我自我介绍。我是Valeza,冰岛大学的一名博士后研究员,隶属于Morris Riedel教授的研究小组。我的研究重点是血液动力学,并在此领域应用高性能计算和人工智能方法。我很荣幸从第8讲到第15讲接手本课程的第二部分。由于时间安排和复活节假期临近,同时考虑到学生需要与业界合作完成期末项目,我们将调整授课形式。从本次讲座开始,我们将采用简短的报告形式来覆盖课程材料,以便大家有更多时间与团队成员合作完成期末项目。

什么是计算流体力学?
计算流体力学是一种用于研究流体动力学的数值方法。流体动力学有着悠久的历史,例如阿基米德在公元前就利用流体力学原理发明了水车等工具。研究流体动力学通常有三种方法:实验方法、理论/解析方法以及数值方法,CFD就属于数值方法。
当处理流体动力学问题时,我们常常面临控制方程、边界条件和初始条件的复杂性。由于问题的非线性以及初始/边界条件的未知性,通常难以获得解析解。CFD通过将问题域离散化为非常小的网格点或单元,并使用数值格式逐点或逐单元求解,从而克服这一难题。

以下是CFD中常用的几种数值方法:
- 有限差分法:简单直接,但通常适用于规则网格。
- 有限体积法:常用于不规则网格。
- 有限元法:功能强大,适用于结构和非结构网格,但数学上更复杂,计算量也更大。
为何使用CFD?
处理流体动力学问题时,我们有三种视角:实验、解析解和数值解(即CFD)。
实验方法可能受限于测量条件(如高温)、特定的初始/边界条件、实验成本以及比例问题。然而,实验对于验证数值解至关重要。

解析解仅适用于有限数量的问题,特别是线性问题。对于非线性问题,解析解非常复杂。大多数问题没有解析解,但解析解能提供问题的通用方程形式。

数值解(CFD)则没有求解上的限制。根据所用方法,有可能降低计算成本。它可以用于复杂几何形状和边界条件的问题,并且在应用前可以进行验证。当然,CFD需要明确的边界条件和初始条件,计算时间也取决于计算机的性能。这正是本课程要讨论的核心:在处理需要模拟湍流等复杂问题时,高性能计算是CFD不可或缺的一部分。近年来,HPC的发展极大地加速了此类复杂问题的求解。

CFD的应用与软件

CFD在工业和科研中有广泛且重要的应用,例如在NASA的一些成就中发挥了关键作用。
CFD代码有不同的类别。我们有商业软件,如ANSYS Fluent、Altair AcuSolve等。也有开源代码,如OpenFOAM、Code_Saturne、SU2等。选择取决于需求和问题性质,无论是研究还是工业应用。开源代码提供了很好的机会,允许用户开发新代码或添加到现有代码库中来解决特定问题。一个完整的CFD代码通常包括网格生成、问题求解和结果可视化模块,以便观察在设定的边界和初始条件下问题的行为。
大多数可用的CFD代码都设计用于处理简单问题,但也具备处理复杂几何形状和工业问题的能力。一些知名的开源CFD代码包括OpenFOAM、Code_Saturne、SU2和Basilisk。它们来自欧洲的不同机构,常用于处理复杂的湍流问题。
CFD:一门多学科交叉领域
CFD通常被认为是一个多学科交叉领域。它需要流体力学知识、数值分析知识和计算机科学知识,才能为需要大量计算的复杂CFD问题提供优秀的解决方案。事实上,CFD在传统应用中一直是计算需求最高的应用之一。正是这种高需求,有效地推动了强大计算机的发展,例如早期的单处理器计算机、后来的向量计算机、90年代的并行计算机,以及我们现在讨论的共享内存和分布式内存系统。
在CFD中,我们有控制方程(主要是Navier-Stokes方程),它们被转化为代数方程组,然后利用计算能力进行求解。
核心挑战:层流与湍流
Navier-Stokes方程描述了粘性流体和不可压缩流动,是二阶非线性偏微分方程,求解颇具挑战性。流动主要有两种类型:层流和湍流。
层流中,流线清晰、有规律,易于追踪和预测,甚至可以求得解析解。湍流则表现出高度的非线性和随机性,流线非常复杂,难以追踪。湍流是Navier-Stokes方程中的一个项,它使得处理这类问题变得困难。湍流是物理学中尚未完全解决的难题之一,相关研究仍在进行。CFD有助于解决这类问题,但对于湍流相干结构及其能量传递过程,仍有许多未知。
为了区分这两种流态,我们使用一个无量纲参数——雷诺数,它定义为惯性力与粘性力之比。在湍流中,惯性力占主导。当惯性力减弱时,流动变为层流。雷诺数的范围取决于问题类型和边界条件。例如,管道内的内部流动,层流和湍流有特定的雷诺数范围。在这两种流态之间,存在一个过渡区,流动既不完全是层流也不是湍流。
此外,在流体动力学中,我们还有可压缩流与不可压缩流的分类,这引出了另一个无量纲数——马赫数。可压缩流主要涉及气体动力学,而马赫数小于1的流动通常被视为不可压缩流(如液体)。这两种流动行为不同,需要不同的求解方法和知识。
CFD求解流程与数值方法
CFD应用于偏微分方程。PDE可分为双曲型、抛物型和椭圆型。求解这些方程需要以不同方式提供边界条件和初始条件。例如,在使用有限差分法时,求解格式的一致性、稳定性和收敛性至关重要。
CFD求解问题的步骤通常如下:首先,理解问题的物理背景并建立物理模型。然后,确定问题的控制方程模型,定义边界条件和初始条件。接着进行离散化(即生成网格),这通常在软件中完成,最终得到代数方程组。随后,根据方程类型选择合适的数值方法进行求解。最后,进行计算后处理和可视化。
在有限差分法中,问题域被离散化为小网格。每个网格单元在二维中有尺寸Δx和Δy。这些单元的尺寸需要仔细检查,以确保解的一致性、稳定性和收敛性。当然,有限差分法有其局限性,在几何处理能力上不如有限体积法或有限元法,特别是它通常需要结构化网格。
在CFD中创建代数方程组时,取决于问题的设置。通常,处理随时间变化的非稳态问题时,我们会有显式格式(只含一个未知量)或隐式格式(含多个未知量)。
如前所述,在有限差分法中,我们需要考虑格式的一致性、稳定性和收敛性。有几种已知的方法,例如在显式格式中,有向前时间中心空间格式(条件稳定),以及Dufort-Frankel格式(无条件稳定)。在隐式格式中,有Laasonen格式和Crank-Nicolson格式。
将这些不同的格式分为显式和隐式时,高性能计算的重要性就凸显出来了。隐式格式通常具有更好的稳定性,因为代数方程中出现多个未知量,但每一步所需的计算时间也更长。因此,需要HPC来加速计算。有时,如果没有足够的计算能力,我们甚至无法求解这类问题。使用隐式格式可以获得更好、更稳定、更一致和收敛的解,但需要更多的计算。网格尺寸和时间步长都直接影响稳定性和一致性。这意味着,当我们使用更小的网格尺寸和时间步长以获得更好的稳定性和一致性时,就需要更强的计算能力。
时间步长和网格尺寸影响计算时间,尤其是在处理三维问题时。我们必须选择网格尺寸和时间步长,以满足稳定性条件并保证合理的计算时间。因此,在求解某些问题时,拥有足够的计算能力非常重要。
对于求解代数方程组的数值格式,主要有两种方法:直接法和迭代法。例如,LU分解、高斯消元法属于直接法。迭代法则包括雅可比法、高斯-赛德尔法和逐次超松弛法。对于三对角矩阵算法,也有托马斯算法可用。当然,需要根据具体问题和格式选择合适的方法。
如前所述,处理非结构网格时,我们需要使用有限体积法或有限元法。在有限体积法中,我们有用于二维和三维的网格和单元。我们需要使用这些形状的网格和体积来离散化问题域,以便能够求解此类问题,这使得复杂性大大增加,因为我们有时没有对称的几何形状,并且单元尺寸也不完全相同,从而导致更多的计算。
湍流模拟与HPC需求
到目前为止,我们已经看到CFD求解如何变得计算密集,以及我们需要HPC来解决这些问题。如果我们想更详细地了解计算需求,例如单元数量、单元尺寸、时间步长等,那么当我们处理湍流时——事实上,在流体动力学中,大多数流动都是湍流,尤其是在工业和大多数自然现象中——情况就更加复杂。
湍流具有宽广的尺度范围、高维度和非线性相互作用。它对初始条件也非常敏感。实际上,初始条件中通常存在扰动。从实验的角度来看,即使我们以相同的设置进行实验,每次运行也永远不会得到完全相同的条件,尽管结果在相似范围内,但初始条件对扰动非常敏感。湍流流动复杂且具有随机性。当我们从平均流的角度观察湍流时,可以看到一些可预测和可重复的特性。
在湍流和CFD应用中,主要有三类方法:雷诺平均纳维-斯托克斯模拟、大涡模拟和直接数值模拟。我们将在下一讲中详细讨论它们,但这里先提一下它们在计算上的差异。
实际上,DNS提供最精确的解。LES在精度和成本之间取得平衡,它比RANS提供更好的解,但比DNS便宜,因为DNS是最精确也是最昂贵的CFD求解方法。而RANS主要提供平均解,是最经济的方法,两者都用于工业和研究中。从计算量来看,RANS通常比LES便宜约10^5倍,这个差距有时不容忽视。然而,RANS有其局限性,例如对于某些特定几何形状或边界条件可能不适用。当使用LES或DNS时,需要了解由于计算成本高,DNS很难用于需要大量计算的问题。LES和RANS在研究和工业中应用最广泛,但工业界倾向于使用RANS,如果RANS无法解决的问题,他们可能会使用LES。
从求解结果来看,RANS为湍流射流提供了平均解预测。LES是大涡模拟,DNS是直接数值模拟。可以看到DNS和RANS在细节上的巨大差异。然而,这取决于应用场景以及对解细节的需求程度。因此,选择使用DNS、LES还是RANS需根据具体情况而定。LES适用于复杂几何形状,且比DNS便宜。RANS则更通用,适用于常规应用。当处理复杂问题且需要细节时,或者RANS无法解决问题时,就需要使用LES或DNS,这时HPC就成为最关键的部分。当然,RANS也需要HPC,但比另外两种方法要经济得多。
HPC在CFD中的实例
让我们看一个CFD中湍流模拟的并行计算实例。通常,我们可以根据雷诺数计算问题的自由度数量。在实际应用中,雷诺数通常超过106。因此,我们预期的自由度数量通常在1013量级,这是一个很高的自由度数量。如果我们查看计算机的浮点运算速度(FLOPS),我们可以估算计算将如何进行。如果我们以纳秒(常规计算单位)来观察,现在我们达到了艾秒级别,那么可以预期计算能力会强得多。但是,以纳秒为单位,对于雷诺数为106的CFD问题,其理论峰值速度需要处理1017个自由度。每个自由度在一个时间步内需要700次浮点运算。这意味着需要每秒10^6百万亿次浮点运算,这大约需要一台每秒千万亿次浮点运算的超级计算机才能计算单个时间步。这只是一个计算示例,说明我们需要超级计算机,需要并行计算,需要将问题域分解为小的子域,并在不同的处理器或GPU之间分配它们。正如我们所知,GPU可以加速计算,使得处理这类问题的计算成为可能。因此,我们可以说,在我们当前使用CFD的领域中,HPC是处理许多应用中复杂问题的关键部分。
关于区域分解和并行计算,这里有一个简单的热传导示例。我们有一个二维域。当我们需要用非常大的网格(例如20000 x 20000)求解这个问题时,网格点数量是4亿个。这里可以看到每个子域的节点数和字节数,以及这类问题需要多少存储空间(例如6.4 GB)。图中展示了如何将问题域分割成6个子域进行并行计算。子域的数量以及这些子域之间的边界处理是一个重要问题,因为每个子域都与其他子域相关,它们需要相互交互和交换信息。这是在设置和求解此类问题时需要考虑的问题。这只是一个简单的例子,展示了在并行计算中如何进行区域分解。
总结与下节预告
本节课中我们一起学习了计算流体力学的基础。我们了解了CFD的定义、应用背景、核心数值方法(如有限差分、有限体积、有限元法),以及层流与湍流的区别。我们重点探讨了CFD求解复杂流体问题(尤其是湍流)时对计算资源的巨大需求,并理解了高性能计算在此领域的核心作用。
最后,有一个视频展示了我们为何需要在CFD中使用HPC,以及近期发展的混合项目模式如何帮助CFD应用。我建议你观看这个视频。



下一讲我们将继续讨论湍流的复杂性,以及HPC如何帮助应对流体力学的挑战。谢谢。
010:分布式深度学习应用(第二部分)
在本节课中,我们将深入学习分布式深度学习的第二部分,重点探讨多节点扩展、不同并行化策略(如数据并行、模型并行)的细节、实际应用中的限制,以及循环神经网络和Transformer等高级模型的应用。
在上一节中,我们介绍了分布式深度学习的基础,包括为何需要离开单GPU环境、通信库(如NCCL)的作用以及硬件(如NVLink)的支持。本节中,我们将进一步探讨如何扩展到多节点设置,并分析其中的挑战与权衡。
分布式深度学习的动机回顾
我们首先简要回顾推动分布式深度学习发展的核心动机,这对于理解后续内容至关重要。
- 模型规模增长:以大型语言模型为例,其可学习参数数量已达数十亿级别,这远远超出了单GPU的内存容量。
- 数据规模增长:科学和工程领域的数据集在体量和质量上都在不断增加,例如卫星图像数据,单GPU内存已无法容纳全部训练数据。
- 计算需求:训练大规模模型和处理海量数据需要巨大的计算能力,这促使我们利用高性能计算集群的强大算力和高速互连。
因此,结合尖端模型与尖端HPC硬件,可以实现更高的模型精度,这在自然语言处理和遥感等领域都有体现。
数据并行技术详解
数据并行是分布式深度学习中最常用的策略之一。其核心思想是将训练数据分割到多个计算节点上。
以下是数据并行的基本工作流程:
- 将完整的训练数据集分割成多个子集。
- 将相同的模型副本复制到每个计算节点(例如,每个GPU)。
- 每个节点使用分配到的数据子集独立进行前向传播和反向传播,计算本地梯度。
- 使用集体通信操作(如
MPI_Allreduce)在所有节点间同步并平均这些梯度。 - 所有节点使用平均后的梯度同步更新其模型参数。
- 重复此过程。
这种方法的优势在于易于并行化,可以显著加速训练过程。它通过使用小批量数据进行随机梯度下降,通常能带来更准确的梯度估计和更平滑的收敛过程。
参数服务器与MPI Allreduce
实现数据并行有两种主要架构:参数服务器模式和基于MPI Allreduce的环状规约模式。
- 参数服务器模式:在这种主-从架构中,一个或多个中心服务器(参数服务器)负责聚合所有工作节点计算出的梯度,计算平均值,然后将更新后的参数广播回工作节点。虽然直观,但手动管理服务器与工作节点的比例和启动过程较为复杂,且服务器可能成为通信瓶颈。
- MPI Allreduce 环状规约模式:这种模式利用MPI运行时环境,无需中心服务器。它使用高效的
MPI_Allreduce集体通信操作,特别是其环状规约实现。每个节点在计算梯度的同时,通过环状通信将梯度片段发送给下一个节点,并接收来自上一个节点的梯度片段,经过N-1步后,所有节点都拥有全局平均梯度。这种方法通信效率高,是当前高性能分布式训练的主流选择。
模型并行与框架支持
当模型本身过于庞大,无法放入单个设备的内存时,就需要采用模型并行策略。


- 模型并行:与数据并行不同,模型并行将单个模型的不同层或部分分割到多个设备上。每个设备持有模型的一部分,但处理相同的小批量数据。这需要在不同设备间传递中间激活值和梯度,特别是在全连接层等部分可能引入复杂的通信模式。
主流的深度学习框架对分布式训练提供了不同层次的支持:
- TensorFlow:内置了参数服务器实现,也支持同步训练策略。
- Keras:作为高级API,构建于TensorFlow之上,简化了模型构建。
- PyTorch:通过
torch.nn.parallel.DistributedDataParallel(DDP) 模块原生支持分布式数据并行训练,易于使用且性能良好。 - Horovod:是一个独立的分布式训练框架,可以轻松与TensorFlow、PyTorch、MXNet等后端集成。它基于MPI实现,特别是高效的Allreduce操作,通常能获得优异的性能。
扩展限制与挑战

尽管分布式训练能加速处理,但盲目扩展会遇到显著的限制。
- 大批量大小问题:当GPU数量急剧增加时,为了保持每个GPU的计算负载,必须增加全局批量大小。研究表明,过大的批量大小会导致模型验证精度下降,即使调整学习率也难以完全弥补。这意味着单纯增加算力可能损害模型最终质量。
- 效率下降:随着节点数增加,通信开销(梯度同步)所占时间比例上升,GPU用于实际计算的时间比例下降,导致整体效率降低。不同模型架构(如ResNet)对此的敏感度不同。
- 准确性与速度的权衡:分布式训练的目标不仅是加速,更重要的是保持或提升模型精度。如果扩展导致精度严重损失,那么加速就失去了意义。
序列模型应用:从RNN到Transformer




分布式深度学习在序列数据处理(如自然语言、时序信号)中有着重要应用,其模型架构也在不断演进。


- 循环神经网络:RNN通过其循环连接结构,能够处理序列数据,将前一时间步的信息传递到当前时间步。这使其非常适合语言建模等任务。
- 长短期记忆网络:LSTM是RNN的改进,引入了门控机制,能更有效地学习长期依赖关系,在诸多序列任务上表现优异。
- Transformer:这是当前驱动大语言模型(如GPT系列)的核心架构。它完全基于自注意力机制,能够并行处理整个序列,并衡量序列中任意两个元素之间的关系权重,从而捕获更广泛的上下文信息。Transformer通常包含编码器和解码器堆栈,在机器翻译、文本生成等任务上取得了革命性成功。


新兴框架与总结
为了应对万亿参数级别模型的训练挑战,出现了许多新的分布式训练框架。

- DeepSpeed:由微软开发,集成了ZeRO内存优化、流水线并行等技术,旨在高效训练超大规模模型。
- 其他框架:如FairScale、Colossal-AI等,也在探索模型并行、流水线并行与数据并行的三维组合优化策略。

本节课中我们一起学习了分布式深度学习在多节点环境下的深入实践。我们详细探讨了数据并行和模型并行的原理与实现,分析了扩展时面临的大批量大小和效率瓶颈等核心挑战。此外,我们还了解了RNN、LSTM和Transformer等序列模型及其应用。理解这些内容,对于利用高性能计算资源解决当今大规模人工智能问题至关重要。
011:分布式深度学习应用(第一部分)

在本节课中,我们将要学习分布式深度学习的基本概念、动机及其在高性能计算环境中的应用。我们将回顾上一讲的核心内容,并探讨为何以及如何将深度学习训练任务分布到多个计算节点上。
上一讲我们介绍了从简单的线性感知器到多层感知器(人工神经网络),再到更复杂的卷积神经网络(CNN)的演进。本节中,我们来看看当模型参数量急剧增长时,我们为何需要转向分布式训练。
从浅层学习到深度学习
在第六讲中,我们学习了最简单的线性感知器模型。这是一个线性学习模型,因为其输出是权重的线性函数。其公式可表示为:
y = x^T W + b
其中,x 是输入特征向量,W 是可学习的权重矩阵,b 是偏置项。
为了构建今天我们所称的人工神经网络(ANNs),我们采用了监督学习的假设。这意味着对于数据集中的每个样本,我们都有一个已知的标签或输出值 y 作为指导。例如,在花卉分类或手写数字识别(MNIST数据集)中,每个输入图像都对应一个明确的类别标签。
然而,简单的线性模型能力有限。当我们引入多层感知器(MLP)或具有隐藏层的人工神经网络,并使用反向传播算法来更新权重时,模型的参数量会显著增加。例如,一个用于MNIST分类的双隐藏层网络可能拥有约12万个可训练参数。
深度学习不仅仅是增加更多的隐藏层。它的核心在于引入具有特定功能的“智能”层。卷积神经网络(CNN)就是一个典型例子。CNN的关键创新在于:
- 保持输入的二维结构,无需将其展平为长向量。
- 使用卷积操作自动学习图像的特征。
- 通过池化层等减少参数数量并增强特征不变性。
一个用于MNIST的简单CNN可能拥有约120万个参数。虽然对于现代GPU来说,训练这样的“玩具”网络并不困难,但这揭示了模型参数量增长的趋势。


大规模模型与分布式训练的动机


深度学习在图像识别、语音识别等领域取得了超越传统方法的性能。然而,当我们面对更复杂的现实问题时,模型规模会急剧膨胀。


以下是驱动我们采用分布式深度学习的关键因素:
- 大型语言模型(LLMs)的兴起:例如,GPT-3拥有1750亿个参数,而更新的模型参数量更是达到了万亿级别。训练如此庞大的模型,单张GPU的显存和算力都远远不够。
- 数据规模的增长:高性能模型需要海量数据进行训练。例如,为了训练一个优秀的冰岛语翻译模型,需要大规模的冰岛语语料库,而这正是“资源稀缺型语言”所面临的挑战。
- 超参数调优的需求:学习率、批大小、优化器类型、网络结构等超参数的选择极大地影响模型性能。寻找最优超参数组合需要多次训练和验证,这本身就是一个计算密集型任务。

因此,当模型参数量(可学习参数)和数据量都变得非常庞大时,分布式训练成为必然选择。训练过程是计算的核心瓶颈,而推理则相对轻量。

分布式训练的技术基础
要将训练任务分布到多个GPU甚至多个计算节点上,我们需要软硬件两方面的支持。


硬件互联:NVLink与InfiniBand
在单个节点内,多个GPU之间需要通过高速互联进行通信。NVIDIA的NVLink技术提供了远超传统PCIe总线的高带宽GPU间直连通道。NVSwitch则允许节点内的所有GPU实现全互联通信,这对于集合操作(如梯度同步)至关重要。
在节点之间,则需要依赖高速网络,如InfiniBand,来实现跨节点的数据交换。这些高速互联是确保分布式训练效率的物理基础。
软件通信库:NCCL
NVIDIA Collective Communications Library (NCCL) 是一个针对NVIDIA GPU优化的通信库。它实现了高度优化的集合通信原语,如All-Reduce、Broadcast等,这些正是分布式深度学习中进行梯度同步所需要的操作。许多高级分布式训练框架(如PyTorch DDP、Horovod)底层都调用了NCCL。
实际系统示例:Leonardo超算
以欧洲的Leonardo超算系统为例,它拥有约14,000张NVIDIA GPU。要在此类系统上运行分布式深度学习任务,用户通常需要加载相应的软件环境模块,例如特定版本的CUDA、cuDNN以及PyTorch。系统管理软件会帮助用户调度任务到多个节点,而像NCCL这样的库则负责高效利用节点内(通过NVLink)和节点间(通过InfiniBand)的网络。



本节课中我们一起回顾了深度学习模型从浅到深的发展历程,理解了模型参数量和大数据如何成为分布式训练的核心驱动力。我们介绍了支撑分布式深度学习的关键硬件(NVLink, InfiniBand)和软件(NCCL)技术,并了解了实际超算系统的运行环境。在下一部分,我们将深入探讨数据并行与模型并行这两种核心的分布式训练技术,分析它们的优劣与挑战。
012:深度学习导论(第二部分)🎓
在本节课中,我们将继续学习深度学习,重点探讨卷积神经网络(CNN)的基本原理、其与传统机器学习的区别,以及深度学习与高性能计算(HPC)之间的紧密联系。我们将看到,深度学习的巨大成功不仅源于算法创新,更离不开计算能力的飞速发展。
从传统机器学习到深度学习
上一节我们介绍了感知器和人工神经网络等基础机器学习模型。本节中,我们来看看深度学习如何通过特征学习改变游戏规则。
在传统机器学习中,我们需要进行大量的特征工程,即手动设计和提取数据的特征,并将其转换为一维向量输入模型。例如,在识别手写数字时,我们需要手动将28x28像素的图像“压平”成一个784维的向量。
深度学习,特别是卷积神经网络,改变了这一范式。其核心思想是让网络自己从原始数据中学习有用的特征,而不是依赖人工设计。这是通过一种特殊的网络结构实现的。

卷积神经网络(CNN)的核心思想🧠
CNN的设计灵感来源于人类的视觉系统。我们识别物体时,并非一次性处理整个场景的所有像素,而是通过局部感受野来关注图像的特定区域。

以下是CNN实现特征学习的关键组件:


- 卷积层
- 作用:使用一个可学习的滤波器(或卷积核)在输入图像上滑动,提取局部特征。
- 操作:滤波器(例如一个3x3的矩阵)与图像局部区域进行点乘求和,生成特征图。这个过程保留了输入数据的空间结构(即像素间的局部关系)。
- 公式/代码示意:
output_feature_map[x, y] = sum( input_region * filter_weights ) + bias

- 激活函数
- 作用:引入非线性,使网络能够学习复杂的模式。
- 常用函数:修正线性单元。其公式为:
f(x) = max(0, x)


-
池化层
- 作用:对特征图进行下采样,减少数据量和参数数量,同时增强特征的鲁棒性(对微小平移不敏感)。
- 常用方法:最大池化,即取局部区域的最大值。
-
全连接层
- 作用:在网络的末端,将学习到的高级特征进行整合,并输出最终的分类概率。
- 常用输出:Softmax 函数,将输出转换为概率分布。其公式为:
softmax(z_i) = exp(z_i) / sum(exp(z_j))
一个典型的CNN流程是:输入 -> [卷积 -> 激活 -> 池化] x N -> 展平 -> 全连接层 -> Softmax输出。通过堆叠多个这样的模块,网络可以自动学习从边缘、纹理到复杂物体部件的层次化特征。
深度学习与高性能计算的共生关系⚡
深度学习模型的强大能力伴随着巨大的计算需求。让我们通过参数数量来理解这一点:
- 感知器:约8,000个可训练参数。
- 双层人工神经网络:约120,000个可训练参数。
- 一个简单的CNN:可轻松达到120万个可训练参数。
每个参数都需要通过反向传播算法和优化器(如随机梯度下降)在数百万次迭代中不断更新。这涉及大量的矩阵乘法和向量运算,正是GPU等高性能计算硬件所擅长的领域。因此,深度学习的复兴与GPU计算能力的突破密不可分。
模型评估与泛化能力📊

构建一个高性能模型不仅仅是追求训练集上的高准确率。机器学习的核心目标是让模型在从未见过的新数据(即测试集)上表现良好,这被称为泛化能力。
以下是确保模型可靠性的两个关键概念:

-
正则化
- 目的:防止过拟合,即模型过度记忆训练数据中的噪声和细节,导致在新数据上表现不佳。
- 常用技术:权重衰减、Dropout(随机丢弃部分神经元)、早停法等。
-
验证
- 目的:进行模型选择。在训练过程中,使用一个独立的验证集来调整模型的超参数(如网络层数、神经元数量、学习率等),而不是使用测试集。这避免了因根据测试集表现调整模型而导致的乐观偏差。
- 重要原则:测试集只能在最终评估时使用一次,并且绝不能用于训练。
一个严谨的流程是:将数据分为训练集(用于学习)、验证集(用于模型选择和调参)、测试集(用于最终性能报告)。只有这样,报告的性能指标才具有可信度。

总结
本节课中我们一起学习了深度学习第二部分的核心内容。我们了解到,卷积神经网络通过局部连接、权重共享和层次化特征学习,能够自动从图像等结构化数据中提取有效特征。这种能力使得CNN在计算机视觉等领域取得了革命性成功。
同时,我们也深刻认识到,深度学习的强大是以海量的计算为代价的。模型参数数量的爆炸式增长,使得高性能计算(尤其是GPU加速计算)成为深度学习发展的关键驱动力。最后,我们强调了正确的模型评估方法(正则化与验证)对于构建可靠、可泛化模型的重要性。

在下一讲中,我们将探讨分布式深度学习,学习如何将训练任务分布到多个计算节点上,以应对日益增长的超大规模模型训练挑战。
013:深度学习导论(第一部分)

概述
在本节课中,我们将要学习深度学习的基础知识。我们将从回顾上一讲关于GPU加速器的内容开始,然后深入探讨机器学习和深度学习的基本概念。我们将了解它们之间的关系,并特别关注它们如何利用GPU的强大计算能力。课程的核心是理解人工神经网络,从最简单的感知器学习模型开始,逐步过渡到更复杂的多层感知器(即人工神经网络),并探讨其背后的计算原理。
回顾:GPU与高性能计算
上一讲我们介绍了加速器,特别是图形处理单元(GPU)。我们了解到,GPU作为一种特殊形式的加速器,其核心在于拥有一个芯片内的众多核心,这些核心虽然单个性能不如高端CPU,但数量庞大,非常适合大规模并行计算。
如今,GPU已经广泛进入全球超级计算机Top 500榜单。榜单中的许多顶级系统,尤其是排名靠前的,都采用了某种形式的GPU进行通用目的计算(GPGPU)。这是因为许多科学计算任务可以很好地并行化,非常适合在GPU上运行。
目前市场上有不同的GPU供应商。虽然英伟达(NVIDIA)的A100、H100等卡在市场份额上占据主导,但AMD的Instinct卡等也在快速发展。从最新的Top 500榜单可以看出,美国拥有许多顶尖系统,但欧洲的系统,如芬兰的LUMI,也位列前十,显示出激烈的竞争态势。
一个关键的里程碑是,在2022年,名为“Frontier”的系统首次突破了百亿亿次(Exaflop)计算的边界。这标志着超算性能进入了一个新时代。值得注意的是,像Summit这样几年前还是顶尖的系统,如今因其技术相对陈旧已被逐步淘汰,这体现了该领域快速发展的步伐。
尽管Top 500排名具有一定影响力,但也有人认为应用基准测试更能体现系统的真实性能。无论如何,该榜单为我们提供了观察硬件趋势、供应商份额和加速器采用情况的窗口。
最后,我们来总结一下GPU的编程模型。其基本模式是:CPU(主机)将数据和指令传输到GPU(设备)内存中,GPU上的调度器执行一个称为“内核”的程序,利用其众多核心完成大量简单操作,然后将结果写回设备内存,最后传回主机内存。内核通常使用CUDA或ROCm等语言编写。CUDA因其市场地位和持续投资而发展迅速,而像OpenCL这样的开放标准有时可能难以跟上最新硬件的特性。最新的技术,如英伟达的Blackwell架构和NVLink高速互联,进一步提升了GPU集群的性能。关于GPU的话题足以开设一门完整的课程,但由于时间有限,我们将快速转向深度学习的介绍。
机器学习与深度学习基础
现在,让我们进入机器学习和深度学习的领域。请注意,这只是一个非常基础的介绍,要深入掌握需要学习专门的课程。
监督学习
我们将采用一种更偏向应用的方法来入门。监督学习是机器学习的一种主要方法。之所以称为“监督”,是因为我们拥有带有标签的数据集。
以下是其核心思想:
- 输入数据:一个包含多个特征值的向量。
- 输出数据(标签):对于每个输入数据,我们都有一个已知的、期望的输出值。
这个已知的输出就像一个“监督者”,在学习过程中为我们提供指导,告诉我们模型的预测是否正确,从而决定是否需要调整模型。监督学习常用于预测未来观测值或进行分类,以及进行推断以理解变量间的关系。
与监督学习相对的是无监督学习(只有输入数据,没有标签,需自行发现模式,如通过聚类)和强化学习(通过奖励和惩罚机制让智能体学习,类似于游戏)。
一个简单例子:鸢尾花分类

我们用一个简单的例子来说明。假设我们有一个鸢尾花数据集,包含三种鸢尾花(Setosa, Versicolor, Virginica)的150个样本,每个样本有花瓣长度和宽度等特征。
当我们绘制这些数据时,可以发现Setosa(绿色点)与其他两种花能较好地区分开。机器学习的目标是建立一个模型,能够对未来未知的数据进行正确分类。例如,给定一朵新花的花瓣尺寸,模型需要判断它属于哪一类。
这就引出了决策边界的概念。我们希望学习一个模型,能在特征空间中画出一条线(对于更高维度是超平面),将不同类别的数据分开。在这个例子中,决策边界上方可能是Virginica,下方可能是Versicolor。

这便引出了我们能构建的最简单的学习模型:感知器学习模型。
感知器学习模型
感知器提供了一个线性学习模型,意味着它在权重和空间上都是线性的。其目标是在空间中找到一条直线(决策边界)来分隔数据。
其数学模型可以表示为:
y = f( w0 + w1*x1 + w2*x2 + ... + wm*xm )
其中:
x1, x2, ..., xm是输入特征(常量,来自我们的数据集)。w0, w1, ..., wm是可训练的参数(权重),这是模型需要学习的关键。w0是偏置项,允许直线在空间中平移。f是一个非线性激活函数。即使求和部分是线性的,激活函数可以引入非线性,这对于分类至关重要。
为了数学上的便利,我们常使用向量形式表示:y = f( x^T * w ),这本质上是输入向量 x 和权重向量 w 的点积。
应用实例:手写数字识别(MNIST)

我们将使用一个著名的基准数据集:MNIST手写数字数据集。这个数据集包含0到9的手写数字图像。
以下是该数据集的关键信息:
- 每张图像是28x28像素的灰度图。
- 共有60,000张训练样本和10,000张测试样本。
- 每个样本都有一个对应的标签(0-9中的一个数字)。
使用Python库(如Keras)可以方便地加载这个数据集。数据看起来像是一些手写的、灰度显示的字符,并且每个图像都有一个指导标签Y,例如y=5, y=0, y=2等。
在机器学习中,通常将数据分为训练集(用于训练模型)、验证集(用于模型选择和调参,防止过拟合)和测试集(用于最终评估模型性能)。还有像交叉验证这样的技巧来更有效地利用数据。这些是机器学习的基础知识,本课程不会深入展开。
数据预处理与模型构建
为了适应感知器模型,我们需要对数据进行预处理:

- 向量化(重塑):将2D的28x28图像“展平”成一个1x784的长向量。这样做会丢失像素间的空间上下文信息,但为了使用最简单的线性模型,这是必要的。
- 归一化:将像素值(0-255)缩放到0-1之间。这有助于在后续的矩阵乘法等计算中保持数值稳定,避免梯度爆炸或消失等问题。
以下是使用Keras构建感知器模型的代码核心部分:
# 假设 x_train_flat 是已经展平并归一化的训练数据
# 假设 num_classes = 10
model = Sequential()
model.add(Dense(units=num_classes, input_shape=(784,), activation='softmax'))
这段代码创建了一个顺序模型,并添加了一个全连接层(Dense Layer)。该层有10个神经元(对应10个数字类别),输入形状是784(展平后的向量),并使用softmax作为激活函数。softmax函数将神经元的输出转化为概率分布,表示输入图像属于每个数字类别的可能性。
这个简单的模型有多少参数呢?输入有784个特征,连接到10个输出神经元,因此有784 * 10 = 7840个权重,再加上10个偏置项,总共是7850个可训练参数。
模型训练与优化
模型通过优化算法来学习。我们通常使用随机梯度下降(SGD) 或其变体(如Adam)。其核心思想是:
- 定义一个损失函数(例如,对于多分类问题常用分类交叉熵损失)。
- 计算损失函数关于权重的梯度。
- 沿着梯度反方向更新权重,以减小损失。
这个过程会迭代多次(遍历整个训练集多次,每次使用一个批次的数据),逐步调整权重。在Keras中,这通过model.compile(指定优化器、损失函数和评估指标)和model.fit(执行训练)函数完成。
使用这个简单的感知器模型,我们在MNIST测试集上能达到约92%的准确率。这是一个不错的起点,但模型非常简单(没有隐藏层,本质上是一个多输出感知器)。
过拟合与模型选择
在训练过程中,我们需要警惕过拟合。这意味着模型在训练集上表现太好,甚至“记住”了训练样本的噪声和细节,导致在未见过的测试数据上表现下降。这就像学生只死记硬背了旧考题,而无法理解知识应对新考题一样。
为了避免过拟合,可以使用正则化技术,并用验证集来监控训练过程,在模型开始过拟合时停止训练(早停法)。我们的简单模型准确率在92%左右波动,提升空间有限。

人工神经网络
那么,如何改进我们的学习模型呢?关键是从多输出感知器升级到人工神经网络。ANN的核心特征是引入了隐藏层。

从感知器到神经网络
感知器是线性的,其能力有限(例如,它无法解决经典的“异或”问题)。人工神经网络通过组合多个感知器(神经元)并堆叠成层来克服这个限制。
其架构如下:
- 输入层:接收数据。
- 一个或多个隐藏层:每层包含多个神经元。每个神经元仍然执行“加权求和 + 激活函数”的操作。
- 输出层:产生最终预测。
这种结构使得网络能够学习数据中复杂的、非线性的模式。激活函数(如ReLU、Sigmoid、Tanh)在这里至关重要,它们将每层线性的加权和转换为非线性输出,从而使整个网络能够拟合非线性决策边界。
反向传播算法
当网络有了隐藏层后,如何将最终输出层的误差传递回去,以更新前面所有层的权重呢?解决方案是反向传播算法。
该算法基于链式法则,将损失函数计算出的误差从输出层向输入层反向传播,并计算每个权重对总误差的贡献(梯度)。然后,使用优化器(如SGD)根据这些梯度更新所有权重。这是ANN能够成功训练多层网络的关键突破。
学习率
在更新权重时,我们用一个称为学习率的参数乘以梯度。学习率的选择非常重要:
- 学习率太小:收敛速度极慢,可能无法在合理时间内找到好的解。
- 学习率太大:更新步伐过大,可能在最优点附近震荡甚至发散,无法收敛。

因此,找到一个合适的学习率是训练神经网络的重要一环。
构建一个简单的人工神经网络
现在,让我们用Keras构建一个具有两个隐藏层的人工神经网络:

model = Sequential()
model.add(Dense(128, activation='relu', input_shape=(784,))) # 第一隐藏层,128个神经元,使用ReLU激活
model.add(Dense(64, activation='relu')) # 第二隐藏层,64个神经元
model.add(Dense(num_classes, activation='softmax')) # 输出层,10个神经元,使用softmax

这个网络结构更强大。第一隐藏层有128个神经元,这意味着输入层(784)到第一隐藏层(128)就有784 * 128 + 128 ≈ 100,480个参数。加上后续层的参数,总参数量远超之前的7850个,达到约12万个。
计算代价增大了,但性能也提升了。通过训练这个ANN,我们在MNIST上的准确率可以从92%提升到约97%。如果考虑到正则化等因素,一个更现实的评估可能达到95% 左右。这相比简单感知器是一个显著的进步。

总结
在本节课的第一部分中,我们一起学习了以下内容:
- 回顾了GPU在高性能计算中的地位,理解了其并行计算能力为何对后续的深度学习至关重要。
- 介绍了机器学习中的监督学习范式,并通过鸢尾花例子理解了分类任务和决策边界的概念。
- 深入探讨了最简单的学习模型——感知器。我们了解了其线性本质、数学模型,并应用于MNIST手写数字识别任务,实现了约92%的准确率。
- 认识了感知器的局限性,并由此引入了人工神经网络。我们了解了ANN通过添加隐藏层和非线性激活函数来学习更复杂模式的能力。
- 简要了解了ANN训练的核心——反向传播算法,以及学习率这一重要超参数的作用。
- 实践构建了一个简单的两层ANN,观察到其参数量大幅增加至约12万,但准确率也显著提升至95%-97%。

我们看到了性能提升所带来的计算复杂度增加。在下一部分,我们将探索如何通过深度学习(特别是卷积神经网络)来进一步提升性能,同时更有效地处理像图像这样的结构化数据。我们也将更深入地讨论超参数调优,并理解为何深度学习如此依赖GPU的强大算力。
014:加速器与图形处理器(第二部分)
概述

在本节课中,我们将继续学习加速器与图形处理器。我们将深入了解GPU编程的基本流程、其在科学计算与深度学习中的广泛应用,并探讨当前高性能计算领域GPU的市场格局与标准化趋势。
GPU编程核心流程回顾



上一节我们介绍了GPU的基本架构和多核特性。本节中,我们来看看如何实际使用GPU进行计算。其核心流程涉及主机(CPU)与设备(GPU)之间的协作。

一个典型的GPU计算任务遵循以下步骤:
- 数据准备:在主机内存中准备数据。
- 数据传输:将数据从主机内存复制到设备(GPU)内存。
- 内核执行:将包含应用逻辑的程序(称为“内核”)加载到GPU上并执行。内核会利用GPU的数千个核心对数据进行并行处理。
- 结果回传:将计算结果从设备内存复制回主机内存。
这个流程强调了GPU作为“加速器”的角色:它不独立运行,而是由CPU调度,专门处理高度并行化的计算任务。


GPU在深度学习中的应用
GPU因其强大的并行计算能力,已成为深度学习训练不可或缺的工具。深度学习模型,如卷积神经网络或Transformer,其核心运算(如矩阵乘法)天然适合在GPU上并行执行。
以下是深度学习框架利用GPU加速的两个关键点:
- 框架支持:开发者通常无需从零开始编写底层GPU代码。现有框架(如TensorFlow、PyTorch)已集成了对GPU的支持,自动处理数据迁移和内核调度。
- 分布式训练:当模型或数据集过于庞大,单卡GPU无法容纳时,需要进行分布式训练。这通常涉及在多台服务器(节点)的多个GPU上并行处理数据。

在分布式训练中,各个GPU计算出的模型参数更新(梯度)需要同步,以确保模型的一致性。此时,我们之前学到的MPI(消息传递接口)技术再次发挥作用,用于在节点间高效地交换梯度信息。

GPU在传统科学计算中的应用
GPU的应用远不止于人工智能。许多传统的科学计算领域也已广泛采用GPU加速。

以下是一些已支持GPU加速的科学计算库与应用领域:
- 分子动力学:例如AMBER、GROMACS,用于模拟分子运动,助力药物研发。
- 计算流体动力学:例如ANSYS Fluent、OpenFOAM,用于模拟流体运动、预测天气或雪崩。
- 线性代数运算:例如cuBLAS库,为矩阵运算等基础数学例程提供GPU加速版本。
- 气象研究与预报:例如WRF模型,用于高精度气象模拟。
对于科研人员和工程师,在着手解决计算问题时,首先应查询是否存在成熟的、已支持GPU加速的库或应用程序,这可以极大提升开发效率和计算性能。
编程模型示例
为了让您更直观地理解GPU编程,我们来看一个简单的例子:SAXPY运算。其公式为:
y = a * x + y
其中a是标量,x和y是大型向量。这个运算的每个元素都是独立的,非常适合并行化。
在CUDA编程模型中,实现该运算的代码结构大致如下:
// 1. 在设备上分配内存
cudaMalloc(&d_x, size);
cudaMalloc(&d_y, size);
// 2. 将数据从主机复制到设备
cudaMemcpy(d_x, h_x, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_y, h_y, size, cudaMemcpyHostToDevice);
// 3. 调用GPU内核函数执行SAXPY运算
saxpy_kernel<<<...>>>(n, a, d_x, d_y);
// 4. 将结果从设备复制回主机
cudaMemcpy(h_y, d_y, size, cudaMemcpyDeviceToHost);
// 5. 释放设备内存
cudaFree(d_x);
cudaFree(d_y);
另一种更接近OpenMP风格的编程模型是OpenACC。它通过编译指令来指导编译器进行并行化:
#pragma acc data copyin(x[0:n]) copy(y[0:n])
{
#pragma acc parallel loop
for (i = 0; i < n; ++i) {
y[i] = a * x[i] + y[i];
}
}
#pragma acc data指令管理数据在主机与设备间的传输,而#pragma acc parallel loop指令则告诉编译器将接下来的循环并行化到GPU上执行。
市场格局与标准化
目前,NVIDIA凭借其CUDA生态在GPU计算市场占据主导地位。然而,市场也在多元化发展。
需要注意的两个重要趋势是:
- 竞争对手的崛起:例如,目前全球排名前列的Frontier超算系统使用了AMD的Instinct GPU,这促进了基于ROCm等开放平台的发展。
- 标准化努力:为了提升代码在不同硬件(如NVIDIA GPU和AMD GPU)间的可移植性,出现了像HIP这样的编程接口。HIP的语法与CUDA非常相似,旨在让开发者能以较小的代价将代码迁移到不同平台。
总结


本节课中我们一起学习了GPU编程的核心工作流程,了解了GPU在深度学习和传统科学计算中的关键作用。我们看到了通过高级框架或特定库来使用GPU的便捷性,也初步接触了CUDA和OpenACC等编程模型。最后,我们认识到GPU计算领域存在一定的市场集中度,但开放标准与多元竞争正在推动其向前发展。在接下来的课程中,我们将更深入地探讨GPU在分布式深度学习训练中的具体应用。
015:加速器和图形处理器(第一部分)
在本节课中,我们将学习高性能计算中的加速器技术,特别是图形处理器。我们将回顾GPU技术的发展历程、其在科学计算和深度学习中的应用,以及如何在高性能计算系统中大规模部署GPU。
上一节我们介绍了共享内存编程模型OpenMP。本节中,我们将转向一种不同的并行计算范式——利用图形处理器进行加速计算。
GPU概述与演进
GPU最初是为图形处理设计的。然而,人们发现其多核心、高并行的架构非常适合处理可并行化的通用计算任务,因此诞生了GPGPU的概念。与追求单核高性能的CPU不同,GPU通过在单个芯片上集成数千个计算核心来实现高吞吐量。
GPU技术的演进速度极快。从早期的Fermi、Kepler架构,到后来的Pascal、Volta,再到如今的Ampere、Hopper以及最新的Blackwell架构,每一代都在计算核心数量、内存带宽和专用硬件(如张量核心)方面有显著提升。
GPU在高性能计算中的应用
GPU的应用已远不止于图形和深度学习。许多传统的科学模拟代码,例如分子动力学软件Amber,也通过移植到CUDA等平台,成功利用GPU加速,获得了显著的性能提升。这表明GPU正在成为推动物理、气候建模、基因组学等多个科学领域发展的关键计算资源。
高性能计算系统与GPU扩展
单个GPU工作站并非我们讨论的高性能计算系统。真正的高性能计算意味着将成千上万个GPU互连起来,形成大规模计算集群。
以2018年排名世界第一的Summit超级计算机为例:
- 它拥有约4,600个计算节点。
- 每个节点配备6个NVIDIA V100 GPU。
- 系统总计约27,000个GPU。
这样的系统展示了GPU在高性能计算中的核心地位:通过高带宽网络(如NVLink和NVSwitch)将大量GPU紧密耦合,以解决大规模科学问题。然而,技术迭代迅速,Summit已于近期退役,被基于AMD Instinct MI300等新一代加速器的Frontier等系统所取代。
关键硬件概念

以下是理解GPU高性能的几个核心硬件概念:


- 高带宽内存:GPU配备的HBM提供远超传统CPU内存的带宽(例如900 GB/s),这对于需要快速“喂食”数据给数千个计算核心的应用至关重要。
- NVLink/NVSwitch:这是NVIDIA的GPU间高速互连技术。它允许GPU直接、高速地交换数据,对于多GPU协同工作至关重要。其带宽也在快速演进,最新一代已达到1.8 TB/s。
- 张量核心:从Volta架构开始引入的专用计算单元,为混合精度矩阵运算(尤其是FP16)进行了优化,极大地加速了深度学习训练和推理。
编程模型与未来展望

目前,CUDA是主流的GPU编程模型。然而,行业也在推动标准化和开放性,例如OpenCL、HIP以及SYCL。尽管CUDA生态仍占主导地位,但AMD Instinct卡及其ROCm平台、Intel的GPU产品都在推动多元化发展。

未来,我们可能会看到更紧密的CPU-GPU集成架构,甚至可能出现专为通信任务优化的“弱”CPU与强大GPU组合的设计模式,以最大化计算密度和能效。
冰岛的GPU资源
在冰岛本地,我们拥有包含NVIDIA A100等GPU的计算资源,例如配备8个A100的单节点和多个配备双A100的节点。此外,作为EuroHPC联合计划的成员,冰岛的研究人员也可以申请使用欧洲范围内的顶级超算系统。

本节课中,我们一起学习了GPU作为加速器在高性能计算中的核心作用。我们回顾了其从图形处理器到通用计算设备的演进,探讨了其关键硬件特性(如多核心、HBM、高速互连),并了解了其如何被集成到Summit等大规模超算系统中以解决前沿科学问题。GPU技术的快速迭代及其在从深度学习到传统科学模拟等广泛领域的成功应用,奠定了它作为现代高性能计算基石的牢固地位。
016:使用OpenMP并行编程(第二部分)
概述
在本节课中,我们将深入学习OpenMP并行编程的实践部分。我们将从编写第一个“Hello World”程序开始,逐步探讨如何利用OpenMP对循环、代码段进行并行化,并理解共享内存编程中的核心概念,如共享变量、私有变量、临界区以及数据竞争。最后,我们将简要了解如何将OpenMP与MPI结合,实现混合并行编程。
从串行到并行:第一个OpenMP程序
上一节我们介绍了OpenMP的基本概念及其与共享内存的关系。本节中,我们来看看如何编写一个实际的OpenMP程序。
OpenMP编程的核心思想是指令编译。我们从一个普通的串行C程序开始,通过添加特定的编译器指令(Pragma)来标记并行区域,从而将其转变为并行程序。程序执行时,一个主线程会创建一组工作线程来执行这些并行区域。
最简单的“Hello World”程序
以下是一个最简单的OpenMP “Hello World” 程序示例。它展示了如何通过一条指令让多个线程执行同一段代码。
#include <stdio.h>
int main() {
#pragma omp parallel
{
printf("Hello World\n");
}
return 0;
}
程序解析:
#pragma omp parallel是一个编译器指令,它定义了一个并行区域。- 当程序执行到此处时,会创建一个线程组。
- 花括号
{}内的代码将被线程组中的每一个线程执行。 - 线程的数量不由代码决定,而是由环境变量
OMP_NUM_THREADS控制。例如,在运行程序前设置export OMP_NUM_THREADS=4,则“Hello World”会被打印4次。
关键特性:我们可以在不重新编译程序的情况下,通过环境变量动态控制使用的线程数。
识别线程:共享变量与私有变量
在并行编程中,我们经常需要区分不同的线程,并为它们分配不同的工作。这引出了共享变量和私有变量的概念。
以下程序演示了如何获取线程ID和线程总数,其逻辑与MPI中的rank和size类似。
#include <stdio.h>
#include <omp.h>
int main() {
int num_threads, thread_id;
#pragma omp parallel private(thread_id) shared(num_threads)
{
thread_id = omp_get_thread_num(); // 获取当前线程的ID(类似MPI rank)
if (thread_id == 0) {
num_threads = omp_get_num_threads(); // 获取线程总数(类似MPI size)
printf("Total threads: %d\n", num_threads);
}
printf("Hello World from thread %d\n", thread_id);
}
return 0;
}
概念解析:
- 共享变量:在并行区域内,所有线程都能访问和修改的变量。例如,
num_threads被声明为shared,因此主线程(ID为0)计算出的线程总数可以被所有线程“看到”(尽管此例中仅主线程使用它)。 - 私有变量:每个线程都拥有自己独立副本的变量。例如,
thread_id被声明为private,每个线程的omp_get_thread_num()返回值都存储在自己的thread_id副本中,互不干扰。 - 数据竞争风险:如果将
thread_id也声明为共享变量,所有线程都会向同一个内存地址写入自己的ID,导致结果不可预测。因此,区分变量作用域至关重要。

编译命令:需要使用支持OpenMP的编译器并添加特定标志。
gcc -fopenmp -o hello_omp hello_omp.c

并行化循环:提升计算性能
“Hello World”展示了并行执行,但真正的性能提升来自于对计算密集型任务的并行化,其中最常见的就是循环并行化。
可并行化的循环模式
一个循环能否被高效并行化,关键在于循环迭代之间是否存在数据依赖。
可并行化示例:
do i = 1, N
a(i) = b(i) + c
end do
在这个循环中,计算 a(i) 只依赖于同一次迭代的 b(i),与 i-1 或 i+1 次迭代无关。因此,可以将N次迭代分配给多个线程同时执行。
OpenMP实现:
在C语言中,我们可以使用 #pragma omp parallel for 指令来自动分割循环。
#pragma omp parallel for private(i) shared(a, b, c)
for (i = 0; i < N; i++) {
a[i] = b[i] + c;
}
private(i):确保每个线程有自己的循环计数器i。shared(a, b, c):数组a,b和常量c被所有线程共享访问。
调度策略
OpenMP允许我们控制如何将循环迭代分配给线程,这称为调度。
以下是主要的调度策略:
- 静态调度:循环迭代在并行区域开始前就被预先、均匀地分配给各线程。适用于每次迭代工作量相近的情况。
schedule(static, chunk_size)
- 动态调度:迭代被放入一个任务池中,线程完成当前任务后,动态地从池中获取下一个任务。适用于迭代间工作量不平衡的情况。
schedule(dynamic, chunk_size)
- guided调度:一种自适应的动态调度,开始时分配较大的块,随后块大小逐渐减小。
schedule(guided, chunk_size)
chunk_size 参数定义了每次分配给一个线程的连续迭代次数。
功能并行:并行区域(Sections)
除了数据并行(循环),OpenMP还支持功能并行,即让不同的线程执行完全不同的代码块。这通过 sections 指令实现。
#pragma omp parallel sections private(i)
{
#pragma omp section
{
for (i = 0; i < N; i++) { c[i] = a[i] + b[i]; } // 线程1执行:向量加法
}
#pragma omp section
{
for (i = 0; i < N; i++) { d[i] = a[i] * b[i]; } // 线程2执行:向量乘法
}
}
在这个例子中,两个 section 可能由两个不同的线程执行,实现了加法与乘法运算的并发。这类似于在气候模拟中,一个线程计算大气运动,另一个线程计算海洋环流。
保护共享数据:临界区与数据竞争
当多个线程同时读写同一个共享变量时,就会发生数据竞争,导致结果不确定。例如,多个线程同时执行 x = x + 1。
临界区
为了防止数据竞争,我们可以使用 critical 指令定义一个临界区。在任何时刻,只允许一个线程执行临界区内的代码。
int counter = 0;
#pragma omp parallel
{
#pragma omp critical
{
counter++; // 确保counter的递增操作是原子性的
}
}
Single指令
single 指令指定一块代码仅由线程组中的某一个线程执行一次,其他线程会在此处等待直到该代码块执行完毕。常用于初始化或输出最终结果。
#pragma omp parallel
{
// ... 所有线程并行执行一些工作 ...
#pragma omp single
{
printf("Final result calculated by one thread.\n"); // 仅一个线程执行打印
}
// ... 所有线程继续并行工作 ...
}
规约操作
规约是将一个操作(如求和、求最大值)应用于所有线程的某个私有变量,并将结果合并到单个共享变量中。OpenMP提供了简洁的 reduction 子句。
double sum = 0.0;
#pragma omp parallel for reduction(+:sum)
for (i = 0; i < N; i++) {
sum += a[i]; // 每个线程计算部分和,最后自动合并到总和中
}
reduction(+:sum) 子句自动为每个线程创建一个 sum 的私有副本,并行计算结束后,将所有私有副本的值通过加法(+)操作合并到原始的共享变量 sum 中。

混合编程:OpenMP与MPI结合

正如开篇所述,单一节点的共享内存容量有限。为了在超算集群上运行大规模应用,需要结合使用OpenMP和MPI,即混合并行编程。
- 节点内:使用OpenMP,利用多核CPU的共享内存进行快速数据交换和并行计算。
- 节点间:使用MPI,在不同节点的分布式内存之间进行通信。
这种模式结合了两者的优点:OpenMP减少了节点内进程间通信的开销,而MPI提供了跨节点扩展的能力。例如,在之前提到的HPD-SCAN算法中,混合编程模式相比纯MPI实现了更接近理想的线性加速比。
一个典型的混合编程作业脚本会同时指定MPI进程数和每个进程的OpenMP线程数:
#SBATCH --ntasks=4 # 启动4个MPI进程
#SBATCH --cpus-per-task=8 # 每个MPI进程分配8个CPU核心
export OMP_NUM_THREADS=8 # 每个MPI进程内启用8个OpenMP线程
mpirun -np 4 ./hybrid_program
总结
本节课我们一起学习了OpenMP并行编程的核心实践内容。
我们首先从编写一个简单的“Hello World”程序开始,理解了通过编译器指令创建并行区域的基本方法。接着,我们深入探讨了共享变量与私有变量的区别,这是避免数据错误的基础。
然后,我们学习了如何并行化循环,这是提升程序性能最常用的手段,并了解了不同的循环调度策略以适应不同的计算负载。此外,我们还看到了如何使用 sections 实现功能并行。

为了保护共享数据的一致性,我们引入了临界区和 single 指令来管理线程同步,并学习了使用 reduction 子句简化规约操作。
最后,我们探讨了将OpenMP与MPI结合的混合编程模式,这是在现代高性能计算集群上解决大规模问题的关键策略。



通过本课的学习,你应该已经掌握了使用OpenMP进行共享内存并行编程的基本技能,并能够理解其在更大规模高性能计算应用中的角色。
017:使用OpenMP并行编程(第一部分)
概述

在本节课中,我们将要学习使用OpenMP进行共享内存并行编程。OpenMP是一个用于共享内存系统的实际标准,它通过编译器指令来标记并行区域,从而将串行代码并行化。我们将了解其核心概念、编程模型,并将其与之前学习的MPI进行对比,理解共享内存编程的优势与局限。
回顾:并行计算基础
上一节我们介绍了并行计算的基本概念,如域分解、负载均衡和加速比定律。本节中,我们来看看这些概念如何应用于共享内存环境。
在之前的例子中,我们有一个包含16个元素的数组。如果我们有四个可用的核心,可以将数组分解为四个部分,分别计算每个部分的局部最大值。然而,这引出了两个关键问题:
- 主-从范式:需要有一个核心(主线程)负责收集所有局部结果并计算全局最大值。
- 负载不均衡:主核心的工作时间可能比其他核心长,导致其他核心空闲等待。
在真实的科学计算中,例如海洋波浪模拟,计算域的不同部分(如海洋与陆地)的计算负载差异巨大,这会导致严重的负载不均衡。高性能计算的目标是最大化系统利用率,因此我们需要更高级、自适应的域分解方法。
另一个关键概念是Halo(或Ghost)层。在基于时间步进的模拟中,下一个时间步的计算依赖于邻居在前一个时间步的结果。当计算域被分解并分配到不同处理器时,我们需要在每个子域的边界创建“幽灵”层,用于存储来自邻居子域的数据,以便进行下一时间步的计算。
我们还回顾了强扩展(固定问题规模,增加核心数)和弱扩展(保持每个核心的问题规模不变,同时增加核心数和总问题规模)。两者都会遇到通信开销增加、效率下降的瓶颈,无法通过无限制增加核心数来获得线性加速。
共享内存编程概念
现在,让我们进入共享内存编程的核心。共享内存系统主要有两种类型:
- 统一内存访问(UMA):所有处理器对任何内存地址的访问时间相同。
- 缓存一致性非统一内存访问(CC-NUMA):访问时间取决于数据在内存中的物理位置,但通过缓存一致性协议,对程序员呈现为统一的共享地址空间。
在共享内存编程中,我们操作的基本单位是线程,它们是比MPI进程更轻量级的执行单元。所有线程都可以访问同一个共享地址空间,直接读写其中的变量,而无需显式的消息传递。
以下是共享内存编程的关键特性:
- 线程拥有自己的私有数据栈。
- 线程通过共享内存进行通信和协作。
- 需要机制来协调多个线程对共享数据的访问,避免冲突。

OpenMP简介

OpenMP是高性能计算中用于共享内存并行编程的事实标准。它的核心思想是通过向串行代码中添加特殊的编译器指令(在C/C++中称为#pragma,在Fortran中称为注释)来创建并行区域。
OpenMP程序的基本执行模式是Fork-Join模型:
- 程序从单个主线程开始执行(串行区域)。
- 遇到并行区域时,主线程派生出一组工作线程,形成一个线程团队。
- 团队中的所有线程(包括主线程)并行执行该区域内的代码。
- 并行区域结束时,所有工作线程汇合,只有主线程继续执行后续的串行代码。

一个简单的OpenMP程序结构如下所示:

// 串行区域 (仅主线程执行)
master_work();

// 并行区域开始
#pragma omp parallel
{
// 此代码块被所有线程并行执行
parallel_work();
}
// 并行区域结束,线程汇合
// 串行区域 (仅主线程执行)
more_serial_work();
使用OpenMP的主要优势包括:

- 可移植性:遵循标准编写的代码可以在不同厂商和架构的HPC系统上运行。
- 增量并行化:可以逐步地将串行代码的特定部分(如循环)并行化。
- 编程相对简单:无需像MPI那样显式管理进程间通信。

混合编程动机

尽管共享内存编程很方便,但它存在根本性限制:单个节点的内存容量是有限的。对于大规模模拟问题,数据可能无法全部装入单个节点的共享内存中。

这就是混合编程的动机所在。现代大型HPC系统通常采用层次化结构:
- 节点内:使用多核CPU,内存由所有核心共享。在此层面使用OpenMP,利用其快速的内存访问和缓存优势。
- 节点间:由多个节点通过高速网络互联。节点间的内存不共享。在此层面使用MPI,进行显式的消息传递,以实现跨节点的数据交换和任务协调。
混合编程结合了两种模型的优点:
- OpenMP:在节点内实现细粒度并行,高效利用多核和内存带宽。
- MPI:实现跨节点的粗粒度并行,突破单个节点内存和核心数的限制,获得极致的可扩展性。

一个典型的应用场景是气候模拟:在单个节点内,使用OpenMP并行计算大气或海洋某个区域在单个时间步内的物理过程;在节点间,使用MPI交换不同区域边界(Halo层)的数据,以推进到下一个全局时间步。
研究实例:HPDBSCAN算法(高性能密度聚类)。下图展示了纯MPI版本与MPI+OpenMP混合版本在不同规模数据集上的扩展性对比。可以看到,对于某些数据集,混合版本能更有效地利用大量核心,获得更好的扩展性能。
(此处原讲义包含一张性能扩展对比图,显示混合编程优于纯MPI)

混合程序的作业脚本也需要相应调整,需要指定每个节点的MPI任务数,以及每个MPI任务使用的OpenMP线程数。

扩展视野:任务与数据流

OpenMP标准本身也在演进,从早期的以并行循环为中心,发展到支持更灵活的任务并行模型。在任务模型中,程序员可以定义独立的计算任务及其依赖关系,由运行时系统动态调度执行。这更符合一些不规则或动态负载的应用模式。
这种思想进一步催生了像PyCOMPSs这样的高级编程框架。它允许用户用Python定义任务及其数据依赖,运行时系统可以自动在分布式计算资源(可能跨多个HPC集群甚至云平台)上调度这些任务,处理数据移动和通信。这为构建更大规模、更复杂的科学工作流提供了工具。
总结


本节课中我们一起学习了共享内存并行编程的基础以及OpenMP的入门知识。我们了解到:
- OpenMP通过编译器指令实现共享内存并行,采用Fork-Join执行模型。
- 线程是轻量级执行单元,通过共享地址空间直接通信。
- 共享内存编程简单高效,但受限于单个节点的内存容量。
- 为了突破规模限制,需要采用混合编程(MPI+OpenMP),在节点内用OpenMP,在节点间用MPI。
- OpenMP正朝着更灵活的任务并行方向发展,并与更高级的工作流系统(如PyCOMPSs)结合,以应对更复杂的计算场景。



在接下来的第二部分,我们将深入OpenMP的实践编程细节,学习如何编写第一个OpenMP“Hello Threads”程序,以及如何使用指令来并行化循环、管理变量作用域和处理线程同步。
018:并行化基础(第二部分)🚀
在本节课中,我们将继续探讨并行化的核心概念,重点关注性能衡量指标、可扩展性以及影响并行效率的关键因素。我们将学习强扩展与弱扩展的区别,理解负载均衡的重要性,并介绍一些高性能计算中的基本定律。
概述
上一节我们介绍了域分解、功能并行和模板方法等并行化基本概念。本节中,我们将深入探讨如何衡量并行程序的性能,理解“可扩展性”这一关键术语,并分析影响并行效率的各种因素,例如负载不均衡和通信开销。

性能衡量与可扩展性

在并行计算中,我们不仅追求更快的速度,还希望程序能够有效地利用更多的计算资源。这就是“可扩展性”的概念。在高性能计算中,可扩展性主要分为两种:强扩展和弱扩展。

加速比
加速比是衡量并行化效果的一个基本指标。它表示使用多个处理器相对于使用单个处理器时,程序执行速度的提升。
公式:
S(n) = T(1) / T(n)
其中,S(n) 是使用 n 个处理器时的加速比,T(1) 是串行执行时间,T(n) 是使用 n 个处理器时的并行执行时间。
理想情况下,加速比是线性的,即处理器数量翻倍,执行时间减半。但在现实中,由于各种开销,加速比通常会低于线性。
负载均衡
负载均衡是指将计算任务均匀地分配到所有处理器上,以避免某些处理器空闲等待。以下是负载不均衡的一个示例:


# 假设有4个任务,执行时间不同
task_times = [1, 5, 1, 1] # 单位:秒
# 如果平均分配给4个处理器,总时间取决于最慢的任务(5秒)
total_time = max(task_times) # 结果为5秒
# 理想均衡情况:每个任务耗时相同,例如都是2秒
ideal_times = [2, 2, 2, 2]
ideal_total_time = max(ideal_times) # 结果为2秒

负载不均衡会导致资源浪费,降低整体效率。在MPI编程中,如果主进程(如rank 0)需要汇总所有结果并进行后续串行处理,而其他进程早已完成计算,就会产生大量的空闲时间。
强扩展与弱扩展

理解这两种扩展方式对于评估并行程序至关重要。
强扩展

强扩展研究的是:在问题总规模固定的情况下,增加处理器数量对程序执行时间的影响。

- 目标:最小化解决固定规模问题所需的时间。
- 类比:固定大小的披萨,让越来越多的学生来分着吃。学生越多,每个人分到的就越少,吃完的总时间可能变短,但协调(通信)开销会增大。
- 挑战:随着处理器数量增加,每个处理器处理的数据量减少,但进程间的通信和同步开销可能成为瓶颈,导致加速比无法线性增长,甚至下降。
弱扩展
弱扩展研究的是:在保持每个处理器上工作量(例如,每个核心处理的粒子数)不变的情况下,增加处理器数量对程序执行时间的影响。
- 目标:在增加处理器数量的同时,按比例增大问题规模,看是否能保持相同的效率。
- 类比:学生数量增加时,披萨也按比例做得更大,保证每个学生分到的量不变。目标是看制作和分发更大披萨(更大规模问题)的总时间变化。
- 意义:这反映了我们利用更多计算资源来解决更庞大、更精细的现实问题的能力。例如,在模拟中增加更多的物理细节。
以下是两种扩展的对比示意图:

强扩展:问题规模固定,增加处理器。
[固定大小的网格] -> [更多处理器处理该网格] -> 考察时间如何变化
弱扩展:处理器增加,问题规模同比增加。
[小网格,少量处理器] -> [大网格,大量处理器] -> 考察每个处理器工作量不变时,时间如何变化
影响可扩展性的定律

阿姆达尔定律
阿姆达尔定律指出了并行程序中串行部分对加速比的上限限制。

公式:
S = 1 / ( (1 - P) + P/N )
其中,S 是加速比,P 是程序中可并行部分的比例,N 是处理器数量。

该定律表明,即使可并行部分 P 达到99%,只要存在1%的串行部分,加速比的理论上限也不会超过100倍。串行部分是提升并行性能的根本性障碍。
古斯塔夫森定律
古斯塔夫森定律为大规模并行计算提供了更乐观的视角。它指出,在计算资源充足时,科学家倾向于解决更大规模的问题,而不仅仅是更快地解决原有问题。

- 核心思想:通过增加处理器数量来求解更大规模的问题,此时串行部分的时间可能保持不变或增长缓慢,而并行部分的工作量随问题规模增大而显著增加,从而使得并行效率得以保持。
- 与弱扩展的联系:古斯塔夫森定律的思想与弱扩展的实验目标是一致的。

性能分析工具
要深入理解程序的扩展行为并定位瓶颈(如负载不均衡、通信延迟、I/O等待),需要借助性能分析工具。
以下是HPC中常用的一些性能分析工具:
- Scalasca:用于大规模并行程序的自动性能分析。
- Vampir:提供图形化界面,用于可视化MPI程序的通信和性能概况。
- Score-P:一个集成的测量和剖析工具,为多种分析工具(如Scalasca, Vampir)提供数据。

使用这些工具可以帮助开发者发现代码中的性能问题,例如非必要的阻塞通信、串行I/O瓶颈等,从而进行有针对性的优化。
总结
本节课中我们一起学习了高性能计算中并行性能评估的核心知识。我们明确了加速比和负载均衡的概念,并重点区分了强扩展与弱扩展这两种关键的评估维度。我们了解到,由于阿姆达尔定律所揭示的串行部分限制,并行加速并非无限。然而,通过古斯塔夫森定律所倡导的解决更大规模问题的思路,我们可以更有效地利用大规模并行资源。最后,我们认识到使用专业的性能分析工具对于理解和优化并行程序行为是不可或缺的。


这些基础概念将为我们后续学习共享内存编程(OpenMP)、GPU加速以及分布式深度学习等主题奠定坚实的基础。
019:并行化基础(第一部分)

在本节课中,我们将要学习并行计算的基础概念和常见策略。我们将从回顾上一讲关于MPI的实践内容开始,然后深入探讨并行化的核心思想,包括域分解、不同的并行化范式(如SPMD和MPMD)以及数据与功能并行化。课程内容旨在为初学者提供清晰、直观的理解。
回顾:MPI点对点与集体通信
上一节我们介绍了MPI编程的实践,本节中我们来回顾其中的核心概念。
在分布式内存系统中,进程间需要通过消息传递来交换数据。我们学习了两种主要的通信方式:


-
点对点通信:涉及两个特定进程(发送方和接收方)之间的数据交换。它需要一个系统缓冲区来暂存待发送的数据。其核心模式是阻塞式的
MPI_Send必须与匹配的MPI_Recv配合使用,正如在“乒乓”示例中展示的那样。MPI_Send(&data, count, datatype, destination, tag, communicator); MPI_Recv(&data, count, datatype, source, tag, communicator, &status); -
集体通信:允许一个进程组内的所有进程参与协同操作。例如,
MPI_Bcast(广播)操作允许一个根进程将数据发送给通信域内的所有其他进程,这比使用循环进行多次点对点通信更高效、代码更简洁。MPI_Bcast(&data, count, datatype, root, communicator);
我们还使用了笛卡尔通信器(MPI_Cart_create)来创建具有网格拓扑结构的进程组,这更符合许多科学计算问题的结构。每个进程通过唯一的 rank 来标识自己,并通过 MPI_Comm_size 获知进程总数,这是实现SPMD(单程序多数据)模式的基础。

并行化的常见策略
上一节我们回顾了MPI通信机制,本节中我们来看看实现并行计算的常见策略。

并行计算的核心思想是将一个大问题分解成多个较小的部分,并分配给多个处理器(进程)协同解决,以追求更高的性能(如更快的求解速度)。高性能计算系统正是由许多这样的处理器通过高速网络互连而成。
以下是几个简单的并行计算示例:

- 寻找数组最大值:假设有一个包含16个元素的数组。如果我们有4个处理器,可以将数组平均分成4份,每个处理器负责计算本地片段的最大值。最后,通过一个归约操作(如
MPI_Reduce)收集所有本地最大值并找出全局最大值。与单个处理器遍历全部16个元素相比,这有望获得加速。 - GPU上的矩阵向量乘法:GPU拥有数千个计算核心,非常适合处理可以高度并行化的操作。例如,在矩阵向量乘法中,结果向量的每个元素可以独立计算,几乎不需要核心间的通信。这种数据并行模式是GPU在人工智能领域表现卓越的原因之一。
- 数值天气预报:地球被划分成许多3D网格单元。每个单元内的物理过程(如温度、气压)的计算不仅依赖于自身,还强烈依赖于其相邻单元的状态。因此,在计算每个时间步后,进程需要与邻居交换边界信息。这类问题对通信性能的要求很高。

域分解

从上述例子可以看出,如何将计算域(数据)有效地划分给多个处理器至关重要,这个过程称为域分解。
域分解的方式多种多样,取决于具体应用:
- 对于一维数组,可能是简单的线性划分。
- 对于二维或三维网格(如模拟海洋、大气),划分可以是条带状、块状或更复杂的形状。
- 在计算流体动力学中,甚至可能采用自适应网格加密,在物理变化剧烈的区域使用更精细的网格。

选择分解策略时需要考虑:
- 负载均衡:尽量让每个处理器承担相近的计算量。
- 通信开销:最小化处理器间需要交换的数据量。例如,一个块状分解通常比条带状分解拥有更小的边界(通信面)。
- 内存限制:每个处理器需要有足够的内存存储其分配到的子域数据。



并行化范式:SPMD vs MPMD
根据程序和数据组织方式的不同,并行化主要有两种范式:

- SPMD(单程序多数据):所有处理器执行相同的程序副本,但操作不同的数据。这是MPI编程的典型模式,通过进程的
rank来区分行为(例如,哪个进程负责广播)。 - MPMD(多程序多数据):不同的处理器可能执行不同的程序,处理不同的数据。这常用于功能并行或耦合模拟。例如,一个气候模拟系统可能由独立的大气模型、海洋模型和陆面模型组成,它们通过一个“耦合器”程序来交换数据。


数据并行与功能并行
我们可以从另一个角度理解并行化:

- 数据并行:将同一操作应用于大量数据的不同部分。例如,对一个大型数组的每个元素进行相同的计算。共享内存编程(如OpenMP)非常适合这种模式,因为多个线程可以直接访问同一内存空间中的不同数据部分。
#pragma omp parallel for for (int i = 0; i < N; i++) { c[i] = a[i] + b[i]; // 每个迭代独立 } - 功能并行:将整个计算任务分解成多个不同的功能模块,由不同的处理器同时执行。例如,在图像处理流水线中,一个处理器负责解码,另一个负责滤波,第三个负责编码。
在实际的高性能计算应用中,常常会混合使用这些模式。
时间维度与幽灵层
在许多科学模拟中,问题不仅涉及空间域分解,还涉及时间维度。物理状态是随时间演化的(如热量扩散、流体运动),通常通过时间步进法求解。
在每个时间步内:
- 各处理器基于当前时间步自身子域及邻居子域的数据进行计算。
- 计算完成后,各处理器需要与邻居交换边界数据,为下一个时间步的计算做准备。
在分布式内存系统中,一个处理器的“邻居”数据可能位于另一个处理器的内存中。为了便于计算,我们引入幽灵层或光环层的概念。这是子域边界外的一层虚拟单元格,专门用于存储从邻居进程接收来的数据。在每个时间步的通信阶段,进程间通过MPI交换信息来填充彼此的幽灵层。这样,在下个时间步的计算中,每个进程就拥有了计算所需的完整局部数据视图。

总结



本节课中我们一起学习了并行计算的基础知识。我们回顾了MPI通信的核心机制,包括点对点和集体通信。我们探讨了并行化的动机和常见策略,理解了域分解是将大问题分解为可并行处理的小问题的关键。我们区分了SPMD和MPMD两种并行范式,以及数据并行和功能并行的不同思路。最后,我们引入了时间步进模拟中至关重要的幽灵层概念,它解决了分布式内存系统中子域间的数据依赖问题。这些概念为理解如何设计和实现高效的高性能计算应用奠定了坚实的基础。
020:MPI消息与集合操作(第二部分)
在本节课中,我们将继续学习MPI(消息传递接口)的进阶概念。上一节我们介绍了MPI环境和点对点通信,本节我们将深入探讨非阻塞通信和笛卡尔通信器,并了解如何将它们结合使用来模拟更复杂的应用场景,例如海洋模拟。
非阻塞通信
上一节我们介绍了阻塞通信,其特点是进程在发送或接收操作完成前会一直等待。本节中我们来看看如何避免这种等待,以提高程序的效率。

非阻塞通信允许进程在发起通信操作后,立即继续执行后续代码,而不必等待通信完成。这通过 MPI_Isend(非阻塞发送)和 MPI_Irecv(非阻塞接收)函数实现。通信完成后,需要使用 MPI_Wait 或 MPI_Waitall 等函数进行同步,以确保数据已成功发送或接收。

以下是使用非阻塞通信的一个简单示例的核心代码逻辑:
MPI_Request request;
MPI_Status status;

if (my_rank == 0) {
// 非阻塞发送
MPI_Isend(send_buffer, buffer_size, MPI_CHAR, 1, 0, MPI_COMM_WORLD, &request);
printf("进程0:我已开始发送,现在可以做其他事情。\n");
// ... 执行其他计算 ...
MPI_Wait(&request, &status); // 等待发送完成
printf("进程0:消息已发送。\n");
} else if (my_rank == 1) {
// 非阻塞接收
MPI_Irecv(recv_buffer, buffer_size, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &request);
printf("进程1:我已开始接收,现在可以做其他事情。\n");
// ... 执行其他计算 ...
MPI_Wait(&request, &status); // 等待接收完成
printf("进程1:收到消息,字符数:%d\n", recv_count);
}
在这个例子中,两个进程在通信的同时都打印了信息,展示了非阻塞操作如何允许计算与通信重叠。

笛卡尔通信器
在实际的科学计算中,问题域(如一个海洋区域)通常具有网格结构。MPI提供了笛卡尔通信器,可以方便地为进程创建这种拓扑结构。
与默认的 MPI_COMM_WORLD 不同,笛卡尔通信器将进程排列成一个虚拟的网格。每个进程在网格中都有坐标,并且可以轻松地找到其“邻居”(如上、下、左、右的进程)。这对于模拟在网格上传播的现象(如波浪、鱼群)非常有用。
以下是创建和使用一个3x4二维笛卡尔通信器的关键步骤:



int dims[2] = {3, 4}; // 网格维度
int periods[2] = {1, 1}; // 周期边界条件(1为真,0为假)
MPI_Comm comm_2d;

// 从 MPI_COMM_WORLD 创建笛卡尔通信器
MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, 0, &comm_2d);

int my_cart_rank, my_coords[2];
// 获取在新通信器中的排名和坐标
MPI_Comm_rank(comm_2d, &my_cart_rank);
MPI_Cart_coords(comm_2d, my_cart_rank, 2, my_coords);

// 准备进行“向下”移位通信
int shift_direction = 0; // 0 表示第一个维度(行)
int displacement = 1; // 位移为1
int source, dest;
// 获取源进程和目标进程的排名(不实际通信)
MPI_Cart_shift(comm_2d, shift_direction, displacement, &source, &dest);




// 现在可以在 source 和 dest 之间进行实际的通信(例如 MPI_Sendrecv)
int send_data = my_cart_rank;
int recv_data;
MPI_Sendrecv(&send_data, 1, MPI_INT, dest, 0,
&recv_data, 1, MPI_INT, source, 0,
comm_2d, MPI_STATUS_IGNORE);




重要提示:在笛卡尔通信器中进行的通信操作(如 MPI_Sendrecv),必须指定新创建的通信器(如 comm_2d),而不是 MPI_COMM_WORLD。同时,启动的进程数必须与笛卡尔网格定义的总进程数(本例中为3x4=12)完全匹配,否则创建会失败。
结合非阻塞与笛卡尔通信
将非阻塞通信与笛卡尔拓扑结合,是构建高效并行模拟应用(如海洋中鱼群运动模拟)的常见模式。其核心思想是:每个进程同时与其所有邻居交换信息,并在通信进行时处理本地计算。
以下是一个概念性的代码框架,展示了如何在网格中与四个邻居进行非阻塞通信:

MPI_Comm comm_2d; // 假设已创建的4x4笛卡尔通信器
int neighbors[4]; // 存储上、下、左、右邻居的排名
int my_data, data_from_neighbors[4];
MPI_Request send_reqs[4], recv_reqs[4];


// ... 通过 MPI_Cart_shift 获取四个方向的邻居排名,存入 neighbors 数组 ...

// 第一阶段:发起所有非阻塞发送和接收
for (int i = 0; i < 4; i++) {
MPI_Isend(&my_data, 1, MPI_INT, neighbors[i], 0, comm_2d, &send_reqs[i]);
MPI_Irecv(&data_from_neighbors[i], 1, MPI_INT, neighbors[i], 0, comm_2d, &recv_reqs[i]);
}

// 在等待通信完成的同时,可以进行不依赖于邻居数据的本地计算
// ... 执行本地计算 ...

// 第二阶段:等待所有通信完成
MPI_Waitall(4, send_reqs, MPI_STATUSES_IGNORE);
MPI_Waitall(4, recv_reqs, MPI_STATUSES_IGNORE);
// 现在所有邻居数据都已就绪,可以进行下一步计算
// ... 基于 data_from_neighbors 更新 my_data ...

这种模式可以放入一个时间步循环中,在每个时间步内交换数据并更新状态,从而模拟动态过程。

总结



本节课中我们一起学习了MPI的两个高级特性:非阻塞通信和笛卡尔通信器。非阻塞通信通过允许计算与通信重叠来提升程序效率。笛卡尔通信器则为具有网格结构的应用提供了天然的进程拓扑,简化了邻居查找和通信模式。最后,我们看到了如何将两者结合,为构建复杂的并行模拟应用(如海洋生态或流体动力学模拟)奠定了基础。理解这些概念是编写高效、可扩展并行程序的关键一步。
021:MPI消息与集合操作(第一部分)🚀
在本节课中,我们将学习MPI(消息传递接口)的基础知识,包括如何初始化MPI环境、进行点对点通信以及使用集合操作。我们将通过一系列简单的C语言示例,从“Hello World”开始,逐步深入到消息传递的核心概念。
概述
MPI是用于分布式内存并行编程的标准。它允许运行在不同处理器(或计算节点)上的进程通过发送和接收消息来协同工作。本节课将重点介绍MPI的基本环境设置、点对点通信(如“乒乓”示例)以及集合通信(如广播操作)。我们还将了解如何通过作业调度器(如Slurm)来管理和运行MPI程序。
MPI环境与“Hello World” 🌍
上一节我们概述了MPI的基本概念。本节中,我们来看看如何创建一个最简单的MPI程序来验证并行环境。
任何MPI程序都必须包含MPI头文件,并在MPI_Init和MPI_Finalize之间编写并行代码。两个最关键的函数是MPI_Comm_size(获取通信域中的进程总数)和MPI_Comm_rank(获取当前进程的唯一标识符,即“秩”)。


以下是一个“Hello World”MPI程序的代码框架:
#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
// 初始化MPI环境
MPI_Init(&argc, &argv);
// 获取进程总数和当前进程的秩
int world_size, world_rank;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
// 每个进程打印自己的信息
printf("Hello world, I am rank %d out of %d\n", world_rank, world_size);
// 终止MPI环境
MPI_Finalize();
return 0;
}
编译和运行这个程序时,我们需要使用MPI编译器(如mpicc)并指定运行进程的数量。进程数量可以通过作业脚本(例如使用Slurm的#SBATCH --ntasks选项)或mpirun命令的参数来控制。同一个可执行文件会被启动多次,每个进程通过其唯一的“秩”来区分并执行不同的代码分支。

点对点通信:乒乓示例 🏓
理解了如何启动并行进程后,我们来看看进程间如何交换数据。点对点通信要求一个进程发送消息,另一个进程必须准备好接收它。

“乒乓”是一个经典示例,模拟了两个进程(秩0和秩1)之间来回传递消息的过程。其核心逻辑是:
- 秩0首先向秩1发送一个消息(“乒”)。
- 秩1必须调用接收操作来获取这个消息。
- 接着,秩1向秩0发送一个回复消息(“乓”)。
- 秩0也必须调用接收操作来获取这个回复。
以下是实现“乒乓”通信的关键代码逻辑:
if (world_rank == 0) {
// 秩0:发送“乒”给秩1,然后接收“乓”
MPI_Send(&ping, 1, MPI_CHAR, 1, 0, MPI_COMM_WORLD);
MPI_Recv(&pong, 1, MPI_CHAR, 1, 0, MPI_COMM_WORLD, &status);
} else if (world_rank == 1) {
// 秩1:接收“乒”来自秩0,然后发送“乓”回去
MPI_Recv(&ping, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &status);
MPI_Send(&pong, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}


重要概念:每一个MPI_Send都必须有一个配对的MPI_Recv,否则程序可能会挂起等待。在作业调度系统中,我们可以灵活地分配这两个进程:它们可以运行在同一个计算节点的两个CPU核心上,也可以分布在两个不同的计算节点上。后者虽然可能浪费资源,但展示了MPI程序在分布式内存架构上的灵活性。
集合通信:广播操作 📢

点对点通信适用于特定进程间的对话。但当需要将一个进程的数据分发给所有其他进程时,使用集合通信会更高效。广播(MPI_Bcast)是最常用的集合操作之一。
假设我们有一个数组,只在秩0的进程中被初始化,但我们希望所有进程都拥有这个数组的完整副本。如果不使用集合操作,我们需要写一个循环,让秩0依次向其他每个进程发送数据。使用广播,一行代码即可完成。
以下是使用广播的示例:
int data[COUNT];
// 只有秩0初始化数据
if (world_rank == 0) {
for (int i = 0; i < COUNT; i++) {
data[i] = i;
}
}
// 关键的一行:秩0将data数组广播给通信域中的所有进程
MPI_Bcast(data, COUNT, MPI_INT, 0, MPI_COMM_WORLD);
// 现在,所有进程的data数组都包含了{0, 1, 2, ...}
广播操作的美妙之处在于其简洁性和内在的可扩展性。我们无需修改源代码,只需在提交作业时改变进程数量(例如从4个改为8个),广播操作会自动将数据分发给所有新增的进程。这体现了MPI程序设计的目标:编写与进程数量无关的、可扩展的并行代码。
作业调度与运行时间管理 ⏱️

在真实的高性能计算环境中,MPI程序通过作业调度系统(如Slurm)提交到计算集群上运行。除了指定需要的进程数,另一个至关重要的参数是“运行时间”(wall time)。

运行时间是用户向调度器申请的作业最大运行时长。调度器根据这个信息来安排作业,优化整个集群的利用率。如果作业的实际运行时间超过了申请的限制,调度器会强制终止该作业。

这个概念可以通过一个“睡眠”程序来演示。我们在程序中调用sleep(seconds)来模拟长时间计算。
int sleep_seconds = 120; // 程序将睡眠120秒
printf(“I am going to sleep for %d seconds.\n”, sleep_seconds);
sleep(sleep_seconds);
printf(“I am awake!\n”);
关键点:如果我们在作业脚本中申请的时间(例如20秒)短于程序实际需要的时间(120秒),那么程序将在运行20秒后被系统强制终止,无法完成并输出“I am awake!”。因此,在运行大型科学计算应用时,准确预估并设置合理的运行时间上限非常重要。
总结
本节课中我们一起学习了MPI并行编程的基础核心内容。
我们首先从MPI环境初始化开始,学会了如何获取进程的“秩”和“大小”,这是所有MPI程序的起点。接着,我们探索了点对点通信,通过“乒乓”示例理解了MPI_Send和MPI_Recv必须配对使用的原则。然后,我们引入了更高效的集合通信,使用MPI_Bcast演示了如何将数据从根进程一次性分发给所有进程,并体会了MPI程序的可扩展性。最后,我们讨论了在真实HPC环境中通过作业调度器运行MPI程序的关键,特别是合理设置运行时间的重要性。


这些知识构成了使用MPI进行分布式内存编程的基石。在接下来的部分,我们将继续探讨更高级的主题,如非阻塞通信和笛卡尔通信器,以构建更复杂、更高效的并行应用。
022:使用MPI并行编程(第二部分)
在本节课中,我们将继续学习MPI并行编程的核心概念,重点探讨并行环境、非阻塞通信以及并行I/O等高级主题。我们将了解如何编写能够适应不同进程数量的程序,以及如何通过优化通信和I/O来提升大规模并行应用的性能。
并行环境与进程意识
上一节我们介绍了MPI的基本通信模式。本节中,我们来看看MPI程序是如何在并行环境中运行的。

一个MPI程序在启动时,会创建多个独立的进程。尽管它们执行相同的源代码,但每个进程都拥有一个独特的身份标识,称为“秩”。通过MPI_Init和MPI_Finalize,程序在它们之间划定了一个并行执行区域。关键在于,我们通常编写程序时并不知道最终会获得多少个进程,这个数量由外部的作业脚本(如使用Slurm调度器时)决定。这使得同一份源代码可以灵活地运行在不同规模的处理器上。
#include <mpi.h>
#include <stdio.h>
int main(int argc, char** argv) {
MPI_Init(&argc, &argv); // 并行区域开始
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); // 获取当前进程的秩
printf("Hello from process %d\n", world_rank);
MPI_Finalize(); // 并行区域结束
return 0;
}

非阻塞通信

在点对点通信中,发送和接收操作通常是阻塞的,这意味着进程在操作完成前必须等待,可能导致空闲时间。为了更高效地利用计算资源,MPI提供了非阻塞通信。
非阻塞通信允许进程发起一个通信操作(如发送或接收)后,立即返回并继续执行其他计算任务。之后,再通过MPI_Wait等函数来确认通信是否完成。这在计算负载不均衡或需要与多个邻居进程通信时(如模拟海浪传播)尤其有用,可以减少等待时间,缓解负载不均的问题。
以下是使用非阻塞发送和接收的基本模式:
MPI_Request request;
MPI_Isend(&data, count, MPI_INT, dest, tag, MPI_COMM_WORLD, &request); // 非阻塞发送
// ... 此处可以执行其他计算 ...
MPI_Wait(&request, MPI_STATUS_IGNORE); // 等待发送完成

MPI_Irecv(&buffer, count, MPI_INT, source, tag, MPI_COMM_WORLD, &request); // 非阻塞接收
// ... 此处可以执行其他计算 ...
MPI_Wait(&request, MPI_STATUS_IGNORE); // 等待接收完成

并行输入/输出(I/O)的挑战与解决方案

当并行应用扩展到成百上千个进程时,数据输入输出(I/O)成为一个关键瓶颈。与串行程序不同,并行I/O需要处理多个进程同时读写文件的问题。

传统I/O方式的局限

如果让所有进程将数据发送给一个主进程,由它统一写入单个文件(串行I/O),会造成严重的通信拥堵和负载不均。如果让每个进程写入独立的文件(伪并行I/O),则会给文件系统元数据管理(如inode)带来巨大压力,同样无法扩展。

并行文件系统与MPI-IO

解决方案是使用并行文件系统(如Lustre, IBM Spectrum Scale)和MPI-IO接口。并行文件系统将单个逻辑文件在物理上分割成多个“块”,分布存储在不同的I/O节点上。MPI-IO则提供了一套API,允许所有进程以协调、并行的方式共同读写这个逻辑文件。
这种方式结合了集体操作的思想,例如使用MPI_File_write_all,所有进程可以高效地将数据同时写入文件的不同部分,极大地提升了I/O吞吐量和可扩展性。这对于需要定期保存检查点的大型科学模拟至关重要。

MPI_File fh;
MPI_File_open(MPI_COMM_WORLD, "shared_data.bin", MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
MPI_File_write_at_all(fh, my_offset, my_data, my_data_size, MPI_BYTE, MPI_STATUS_IGNORE);
MPI_File_close(&fh);
高层I/O库
在实际应用中,科学家们常使用基于MPI-IO构建的高层I/O库,如HDF5和NetCDF。这些库提供了更友好的数据模型(如层次结构、多维数组)和社区标准,进一步简化了并行I/O的编程。

总结




本节课中我们一起学习了MPI并行编程更深入的内容。我们理解了MPI程序如何在动态的并行环境中运行,并通过进程“秩”来标识自己。我们探讨了非阻塞通信如何通过重叠计算与通信来提升效率。最后,我们认识到在大规模并行计算中,I/O是主要性能瓶颈之一,并学习了通过并行文件系统和MPI-IO来实现高效、可扩展的并行数据读写。这些概念是构建高性能科学与工程应用的基础。在接下来的实践课中,我们将通过具体代码示例来巩固这些知识。
023:使用MPI并行编程(第一部分)🚀

在本节课中,我们将要学习并行编程的核心概念,特别是消息传递接口(MPI)。我们将探讨MPI的基本原理、关键术语以及它如何支持分布式内存编程。课程将涵盖点对点通信、集体通信等核心概念,为后续的实践课程打下基础。
课程回顾与背景
上一节我们介绍了C/C++程序在高性能计算(HPC)中的重要性,并学习了如何在HPC系统上通过调度器提交作业。本节中,我们来看看如何利用MPI标准,编写能够充分利用多个计算节点资源的可扩展并行程序。
HPC系统,如Juwels Booster,拥有数千个GPU,这些资源需要被多个研究者和研究组高效共享。调度算法(如先来先服务、回填)旨在最大化系统利用率。提交作业时,我们通过作业脚本请求资源,而无需修改程序本身的可执行文件。这引出了MPI编程的核心动机:编写一个能够根据分配的进程数进行扩展的应用程序。
什么是MPI?
MPI(Message Passing Interface)是一个用于并行编程的开源标准库。它主要用于分布式内存系统,即每个进程拥有自己独立的地址空间(内存)。当一个进程需要另一个进程的数据时,它必须通过“消息传递”的方式进行显式通信。
核心公式:进程A发送数据给进程B。
MPI_Send(&data, count, datatype, destination, tag, communicator);
MPI_Recv(&buffer, count, datatype, source, tag, communicator, &status);

与共享内存编程(如OpenMP)不同,MPI要求程序员显式管理进程间的数据交换。它的优势在于可移植性高,遵循MPI标准的程序可以相对容易地在不同的HPC系统间迁移。
MPI的核心概念
1. 进程与MPI环境
一个MPI程序由多个进程组成。每个进程运行相同的程序副本(SPMD模型),但可以通过逻辑分支执行不同的任务。MPI环境在程序启动时初始化,为这些进程提供通信的基础设施。
2. 秩(Rank)
每个进程在通信域(Communicator)中被分配一个唯一的标识符,称为秩(Rank)。秩通常是从0开始的整数。进程通过秩来识别彼此,从而指定数据的发送目标和接收来源。

核心概念:MPI_Comm_rank(communicator, &rank) 用于获取当前进程的秩。

3. 通信域(Communicator)
通信域定义了一组可以互相通信的进程及其上下文。最常用的通信域是MPI_COMM_WORLD,它包含了所有初始启动的进程。你可以创建子通信域来定义更小的进程组,以优化特定组内的通信。
4. 点对点通信
点对点通信涉及两个进程:一个发送者,一个接收者。这是最基本的通信模式。

以下是点对点通信的类型:
- 阻塞通信:
MPI_Send和MPI_Recv会一直等待,直到通信操作安全完成才返回。这可能导致死锁,需要谨慎设计通信顺序。 - 非阻塞通信:
MPI_Isend和MPI_Irecv会立即返回,允许进程在通信进行的同时执行其他计算,然后通过MPI_Wait来等待操作完成。这有助于提高程序性能。
5. 集体通信
集体通信涉及一个通信域内的所有进程。MPI提供了高效的集体通信函数,它们通常基于树形算法实现,比手动使用循环进行点对点通信更高效、更简洁。
以下是主要的集体通信操作:
- 广播(Broadcast):一个进程(根进程)将相同的数据发送给通信域内的所有其他进程。
MPI_Bcast(&data, count, datatype, root, communicator)
- 散播(Scatter):根进程将一个数组的不同部分发送给通信域内的每个进程。
MPI_Scatter(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, communicator)
- 聚集(Gather):与散播相反,根进程从所有进程收集数据到一个数组中。
MPI_Gather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, communicator)
- 归约(Reduce):对所有进程提供的数据进行一个操作(如求和、求最大值),并将结果存储在根进程中。
MPI_Reduce(sendbuf, recvbuf, count, datatype, operation, root, communicator)


MPI的应用动机

为了理解MPI的必要性,让我们看一个应用示例:模拟海洋波浪传播。我们将一个大的3D海洋区域网格分解成许多小块(域分解),每个进程负责计算其中一块的波浪高度。随着时间推移,相邻网格块的波浪会相互影响。因此,进程之间必须频繁交换边界处的波浪高度数据,这正是MPI消息传递的用武之地。

类似的应用广泛存在于科学和工程领域,如计算流体动力学(模拟赛车气流、风力涡轮机应力)、气候模拟、冰川崩解模拟以及大规模数据聚类分析等。这些应用都依赖MPI来实现大规模并行计算。
MPI不是什么?
需要明确的是,MPI并非用于互联网通信的网络编程库(如TCP/IP套接字编程)。它是一个为单个HPC集群内部的高性能、低延迟通信而优化的库,其抽象和函数专门为科学计算中常见的数值数据和通信模式设计。

总结

本节课中我们一起学习了MPI并行编程的基础概念。我们了解了MPI作为分布式内存编程标准的核心思想,包括进程、秩、通信域等基本术语。我们重点区分了点对点通信和集体通信,并介绍了广播、散播、聚集和归约等常用集体操作。最后,我们通过波浪模拟等例子了解了MPI在解决大规模科学计算问题中的关键作用。在接下来的实践课中,我们将通过具体的代码示例来巩固这些概念。
024:C语言编程导论与作业调度(第二部分)🚀
概述
在本节课中,我们将继续学习C语言编程,并深入了解高性能计算(HPC)系统中的核心概念——作业调度。我们将探讨为什么不能直接在登录节点上运行计算程序,以及如何通过调度系统将作业提交到计算节点上执行。
上一节我们介绍了如何在HPC系统上编写和编译一个简单的“Hello World”C程序。本节中,我们来看看如何正确地在这个多用户环境中运行它。
HPC系统:多用户共享环境
高性能计算系统与个人笔记本电脑不同,它们是供许多用户同时使用的大型共享资源。不同的用户有不同的计算需求,例如,有些应用需要高性能的单核CPU进行数值计算,而另一些则需要大量GPU进行高度可扩展的并行计算。
为了满足这些多样化的需求,现代超级计算机通常采用模块化超级计算架构。这种架构将系统划分为不同的模块(例如,CPU密集型集群和GPU加速器),每个模块服务于特定类型的应用。
由于许多用户和作业需要同时使用这些昂贵的系统,我们必须有一种机制来管理资源分配,确保公平性和高利用率。这就是作业调度系统的作用。
监控与调度:系统的两大支柱
为了有效管理HPC系统,我们主要依赖两个工具:监控系统和调度系统。
监控系统
监控系统(如Ganglia、LLview)以图形化方式展示系统的实时状态。你可以看到:
- 系统负载:当前有多少CPU、内存、网络正在被使用。
- 运行中的作业:不同颜色代表不同用户或不同类型的作业。
- 系统健康:是否有节点故障或处于维护状态。
这些可视化工具帮助用户和管理员了解系统的整体运行状况。
调度系统
调度系统(如Slurm)是管理用户作业提交和执行的引擎。它的核心任务是:
- 接收用户提交的作业请求。
- 根据作业的资源需求(如CPU数量、内存大小、运行时间)和系统的当前空闲情况,将作业放入队列。
- 在资源可用时,将作业分配到合适的计算节点上启动执行。
常见的调度算法包括“先到先服务”,并结合“回填”策略来填充资源间隙,以提高整体系统利用率。
从登录节点到计算节点
在HPC系统中,节点分为两类:
- 登录节点:用于登录、编辑文件、编译代码和提交作业。严禁在此运行任何实际的计算任务,因为这会严重影响其他用户登录和使用系统。
- 计算节点:专门用于执行计算任务。用户通过调度系统获得计算节点的访问权限。
我们之前编译的hello程序,如果直接在登录节点运行,虽然对于这个小程序无害,但这是一个错误的做法。正确的流程是:在登录节点编译,然后通过调度系统将可执行文件提交到计算节点运行。
实践:编写并提交作业脚本
要将作业提交给调度系统,你需要创建一个作业脚本。这个脚本告诉调度器你的作业需要什么资源以及如何运行。
以下是一个简单的作业脚本示例,用于运行我们的“Hello World”程序:
#!/bin/bash
#SBATCH --job-name=hello_example # 作业名称
#SBATCH --partition=utu # 指定分区(教学分区)
#SBATCH --nodes=1 # 请求1个计算节点
#SBATCH --ntasks-per-node=1 # 每个节点上运行1个任务
#SBATCH --time=00:01:00 # 最大运行时间(1分钟)
#SBATCH --mail-user=your_email@example.com # 作业完成时通知邮箱
#SBATCH --output=hello_%j.out # 标准输出重定向到此文件
#SBATCH --error=hello_%j.err # 标准错误重定向到此文件
# 加载必要的软件模块(例如MPI环境)
module load openmpi/4.1.1

# 执行你的程序
./hello
脚本关键部分说明:
#SBATCH开头的行是给调度器的指令。--partition指定作业在哪个分区运行。你需要根据系统文档选择正确的分区。--nodes和--ntasks-per-node定义了并行规模。对于简单的串行程序,设为1。--time设置了作业的“墙钟时间”限制,超时作业会被终止。--output和--error文件用于保存程序的输出和错误信息,%j会被替换为作业ID。
提交与管理作业
创建好作业脚本(例如 submit_hello.sh)后,就可以使用调度器命令来提交和管理它。
以下是常用的Slurm命令:
# 提交作业
sbatch submit_hello.sh
# 查看队列中所有作业的状态
squeue
# 查看特定用户的作业
squeue -u <用户名>


# 查看已完成的作业详细信息
sacct -j <作业ID> --format=JobID,JobName,Partition,Elapsed,State,NodeList
# 取消一个正在排队或运行的作业
scancel <作业ID>
提交作业后,使用 squeue 命令可以看到作业在队列中的状态(如PENDING等待中,RUNNING运行中)。作业完成后,输出结果会保存在你指定的 --output 文件中。
一个重要提示:作业脚本中指定的可执行文件路径是相对于你提交作业时的目录。如果你修改了源代码(hello.c),必须重新编译生成新的可执行文件,否则调度器运行的仍然是旧版本的程序。
总结

本节课中我们一起学习了HPC系统作业调度的核心概念与实践。我们了解到:
- HPC系统是复杂的多用户共享环境,需要调度系统来高效、公平地管理资源。
- 绝对不能在登录节点上运行计算任务,所有计算都应在计算节点上进行。
- 通过编写作业脚本(包含资源需求和执行指令),并使用
sbatch命令提交,是运行HPC作业的标准方式。 - 使用
squeue、sacct、scancel等命令可以有效地监控和管理自己的作业。

这为我们后续学习消息传递接口(MPI)并行编程打下了坚实基础。在接下来的课程中,我们将把这里的“Hello World”程序扩展为真正的并行程序。
025:C语言编程与调度导论 🚀
在本节课中,我们将学习两个核心主题:如何在HPC环境中编译和运行一个简单的C语言程序,以及如何通过调度器将计算任务提交到专用的计算节点上执行。我们将从回顾上一讲的基础概念开始,逐步深入到具体的实践操作。
回顾:高性能计算基础
上一讲我们介绍了高性能计算的基础概念。我们了解到,HPC系统的核心计算单元是多核处理器,它在一块芯片上集成了多个计算核心,拥有高速缓存,是强大的“数字运算引擎”。
然而,仅有多核处理器还不足以构成我们所说的并行计算。因此,我们进一步学习了如何将许多这样的处理器单元连接起来,形成一个庞大的计算集群。这些节点之间通过高速互联网络(如InfiniBand)进行通信。
为了在这种分布式内存架构上进行编程,我们使用消息传递接口标准。其核心思想是,不同进程(通常运行在不同的处理器上)之间通过发送和接收消息来交换数据,因为一个进程无法直接访问另一个进程的内存空间。其基本通信模式可以抽象为:
进程A 发送(数据) -> 进程B 接收(数据)
另一种重要的编程范式是共享内存编程。在这种模式下,多个轻量级的线程可以访问同一个共享的内存地址空间,一个线程写入的数据可以直接被另一个线程读取。在现代多核处理器上,这通常通过诸如NUMA(非统一内存访问)的架构来实现,它对程序员呈现为单一的共享内存视图。
在实际应用中,我们经常混合使用这两种范式:在节点间使用MPI进行分布式内存编程,在节点内的多核处理器上使用OpenMP进行共享内存编程,从而结合两者的优势。
此外,我们还简要介绍了众核处理器,如图形处理器。GPU拥有数千个计算核心,虽然单个核心的性能不如CPU强劲,但其巨大的核心数量特别适合处理高度并行、计算模式统一的任务,例如矩阵运算和深度学习。
课程概述与目标
基于上一讲的基础,本节课我们将进行首次实践。主要内容分为两部分:
- C语言编程实践:学习在HPC的模块化环境中编译和运行一个基础的C程序。请注意,本课程的重点不是教授C语言本身,而是理解在HPC系统上进行程序编译和执行的流程。
- 调度器介绍:理解HPC系统作为多用户环境,为何需要以及如何使用调度器(如SLURM)来管理计算资源,将任务提交到计算节点执行,而不是在登录节点上直接运行。
现在,让我们开始第一部分:C语言编程实践。
第一部分:编译与运行C程序
在HPC环境中,我们通常不直接运行源代码,而是需要先将其编译成可执行文件。我们将使用系统提供的模块化环境来加载所需的编译器。
连接到HPC系统并准备环境
首先,我们需要通过SSH客户端连接到我们的HPC系统(本例中名为“Elja”)。连接时需要使用之前配置好的SSH密钥对进行认证。
成功登录后,我们会进入自己的家目录。我们可以创建一个专门的工作目录来管理本课程的文件。
以下是创建并进入工作目录的命令:
mkdir -p hpc_course_spring25
cd hpc_course_spring25
编写一个简单的C程序
我们从一个经典的“Hello World”程序开始。使用文本编辑器(如vi或nano)创建一个名为 hello.c 的文件。
以下是 hello.c 文件的内容:
#include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
这个程序非常简单,只包含一个 main 函数,功能是向屏幕输出“Hello World”字符串。它目前是一个纯粹的串行程序,不包含任何并行计算元素。
使用模块环境加载编译器
在HPC系统上,软件通常通过“模块”来管理。我们需要加载一个包含C编译器的模块。这里我们选择加载一个MPI实现(OpenMPI),因为它自带了C编译器 mpicc,方便我们为后续的MPI编程做准备。
以下是查看和加载模块的命令:
# 查看所有可用模块
module avail
# 查看特定模块(如MPI)的详细信息
module spider openmpi
# 加载指定版本的OpenMPI模块
module load openmpi/4.1.2
编译C程序
加载模块后,我们就可以使用 mpicc 编译器来编译我们的C程序了。-o 选项用于指定生成的可执行文件的名称。

以下是编译命令:
mpicc -o hello hello.c
执行该命令后,如果编译成功,当前目录下会生成一个名为 hello 的可执行文件。在Linux系统中,可执行文件通常会被标记为绿色或以“x”权限显示。


在登录节点上运行程序(不推荐)
编译完成后,我们可以直接在终端中运行这个程序:
./hello
此时,程序会在当前的登录节点上执行,并输出“Hello World”。然而,在HPC的最佳实践中,任何实际的计算任务都不应该在登录节点上直接运行。登录节点主要用于文件管理、代码编辑和作业提交等轻量级操作。长时间或高负载的计算会占用登录节点的资源,影响其他用户使用系统。
因此,我们需要一种方法将计算任务转移到专用的计算节点上执行。这就是我们第二部分要介绍的内容:使用作业调度系统。
第二部分:使用调度器提交作业
HPC系统是典型的多用户环境,可能有成百上千的用户同时使用。为了公平、高效地管理所有计算节点上的资源,我们需要使用作业调度器。

调度器的作用


调度器(如SLURM、PBS等)是HPC系统的核心系统软件之一。它的主要功能是:
- 接收用户提交的计算作业(一个脚本或命令)。
- 根据作业的资源需求(如CPU数量、内存大小、运行时间)和当前系统的资源空闲情况,进行排队和调度。
- 在合适的计算节点上分配资源并启动作业。
- 管理作业的运行状态,并将输出结果返回给用户。
编写作业提交脚本
我们需要创建一个作业提交脚本,告诉调度器如何运行我们的 hello 程序。创建一个名为 submit_hello.job 的文件。
以下是 submit_hello.job 脚本的示例内容:
#!/bin/bash
#SBATCH --job-name=hello_test # 作业名称
#SBATCH --output=hello_output.txt # 标准输出重定向到此文件
#SBATCH --error=hello_error.txt # 标准错误重定向到此文件
#SBATCH --ntasks=1 # 启动的任务数(进程数),这里为1
#SBATCH --time=00:01:00 # 作业运行的最大时间(1分钟)
#SBATCH --partition=normal # 提交到的分区(队列)名称

# 加载运行所需的模块
module load openmpi/4.1.2
# 执行编译好的程序
./hello
脚本开头的 #SBATCH 行是给SLURM调度器的指令,用于设置作业参数。脚本主体部分则与我们之前在终端手动执行的命令类似:加载环境,然后运行程序。

提交作业到计算节点
使用 sbatch 命令将作业脚本提交给调度器:
sbatch submit_hello.job
提交后,调度器会返回一个作业ID(例如 Submitted batch job 12345)。作业进入队列等待调度。
查看作业状态与结果
我们可以使用以下命令查看作业状态:
# 查看当前用户的所有作业状态
squeue -u $USER
# 查看指定作业的详细信息
scontrol show job <job_id>
当作业状态从“PENDING”(等待)变为“RUNNING”(运行中),最后消失(表示已完成),说明作业执行完毕。
作业完成后,输出文件(本例中为 hello_output.txt)和错误文件(hello_error.txt)会生成在当前目录。我们可以查看输出文件来获取程序运行结果:
cat hello_output.txt
如果一切正常,文件中应该包含“Hello World”这行输出。至此,我们成功地将一个简单的C程序通过调度器在计算节点上执行。
总结
本节课中我们一起学习了两个关键实践:
- HPC环境下的C程序编译:我们编写了一个基础的C程序,利用HPC系统的模块化环境加载了编译器,并将其成功编译为可执行文件。我们了解到,直接在生产环境的登录节点上运行计算程序是不被允许的。
- 使用调度器管理计算任务:我们认识了作业调度器在HPC多用户环境中的核心作用。通过编写一个包含资源请求的作业脚本,并使用
sbatch命令提交,我们将“Hello World”程序正确地分配到了计算节点上执行,从而符合了HPC系统的使用规范。

这些操作为后续更复杂的并行编程(如使用MPI和OpenMP)以及在HPC上运行大型科学计算应用打下了坚实的基础。在接下来的课程中,我们将深入MPI编程,学习如何让多个进程协同工作,解决真正的并行计算问题。
026:高性能计算概述(第二部分)
在本节课中,我们将继续学习高性能计算(HPC)的概述。上一部分我们介绍了HPC的基本概念、重要性以及其四大支柱(硬件、理论、数据、技术)。本节中,我们将深入探讨HPC的生态系统技术、系统架构,并特别关注当前推动HPC发展的两大驱动力:GPU加速计算与人工智能/深度学习。
HPC生态系统技术
上一节我们介绍了HPC的硬件基础,本节中我们来看看支撑这些硬件的系统软件生态系统。HPC系统的软件环境远比个人电脑复杂,它需要管理多用户并发访问、资源调度和系统健康等。
调度系统
HPC系统需要同时为众多用户服务,因此需要一个调度系统来管理计算资源的分配。这与多人同时想使用一台个人电脑的情况类似,必须有一个系统来协调。
以下是调度系统的核心概念:
- 批处理队列:用户将任务(作业)提交到集群,任务会在一个队列中等待,直到系统有足够的资源来运行它。这意味着你的任务可能需要等待一段时间(例如一小时)才能开始执行。
- 调度算法:存在多种调度算法,例如支持“回填”的算法,它可以在调度空隙中运行较小的作业,以提高资源利用率。
- SLURM:本课程将重点介绍SLURM这一广泛使用的调度系统,并在接下来的实践课中进行演示。
系统监控与管理

将众多不同的技术集成到一个大型机架中会带来许多挑战,不仅涉及系统负载,还包括系统健康状态。
以下是相关的系统软件:
- 健康检查:监控系统是否正常运行,例如CPU是否过热、存储连接是否正常。
- 性能分析:使用如Scalasca等工具分析并行程序的性能,识别瓶颈,帮助优化代码以获得最佳性能。这对于并行编程专家尤为重要。
- 资源监控:跨节点、跨用户监控内存使用情况等,其规模远大于个人电脑上的进程管理。
中间件与简化访问
直接通过SSH密钥和编写复杂的SLURM作业脚本来访问HPC系统对初学者是一大挑战。因此,出现了中间件来简化访问流程。

以下是中间件的作用:
- 简化访问:用户可以选择应用程序并直接运行,无需手动编写底层的作业脚本。例如Rescale或UNICORE系统。
- 交互式计算:像Jupyter Lab这样的工具,通过与UNICORE等中间件结合,允许用户通过用户名密码以交互方式访问HPC资源,直接在计算节点上启动Jupyter Notebook实例。这对于探索性工作、AI建模初期非常方便,尽管在扩展到成千上万个GPU时,可能仍需回归批处理作业方式。


HPC系统架构与术语
理解了系统软件后,我们来看看HPC硬件的物理组织。超级计算机是复杂的系统设计,需要专门的供电、冷却(气体或液体冷却)和洁净环境。

硬件层级
从微观到宏观,HPC系统遵循特定的硬件组织术语。

以下是常见的硬件术语:
- 芯片:包含多个处理核心(多核/众核)。
- 计算卡:多个芯片高度集成在一块卡上。
- 节点卡:多块计算卡进一步集成在一块节点卡上。这与笔记本电脑或台式机的组件密度有巨大差异。
- 机架:多个节点卡安装在一个机架中。我们通常看到的闪烁灯光的大型机柜就是机架。
- 系统互连:所有机架通过高速网络(拓扑结构)连接在一起,构成完整的超级计算机。

网络拓扑

节点之间的连接方式对通信效率至关重要,尤其对于需要频繁交换数据的并行应用(如波传播模拟)。
以下是网络拓扑的关键点:
- 互连模式:例如三维环面网络或网格网络。
- 通信影响:网络结构决定了任务映射和通信性能,高效的拓扑能实现高性能的MPI通信。
- I/O考虑:计算节点不仅相互连接,还连接到文件服务器。大型模拟应用(如地震、熔岩模拟)在运行中需要定期转储数据,因此I/O性能也是系统设计的关键,避免成为瓶颈。

GPU加速计算与深度学习

现在,我们转向近年来为HPC带来巨大动能的两个领域。首先是GPU加速的通用计算,它极大地推动了深度学习的发展。



GPU:从图形处理到通用计算
GPU最初为计算机图形和游戏设计,但现在被广泛用于通用目的计算(GPGPU,但通常简称为GPU)。

以下是GPU的核心特点:
- 众核架构:与追求高单线程性能的CPU不同,GPU集成了数百个性能适中的核心,通过高并行吞吐量来解决问题。
- 能效与适用性:GPU在能效和处理特定计算模式(如矩阵运算)方面具有优势。
- 编程模型:存在多种编程模型,如OpenCL、NVIDIA CUDA、AMD ROCm以及旨在统一标准的SYCL。我们将在专门的GPU讲座中深入探讨。
- 主机-设备模式:当前主流范式是,CPU(主机)负责准备数据并传输到GPU(设备)内存,然后由GPU执行计算。这种数据传输有时会成为瓶颈。

GPU为何适合深度学习
深度学习,特别是其中的矩阵运算,与GPU的架构特性完美契合。
以下是GPU加速深度学习的原理:
- 核心操作:深度学习网络的前向传播和反向传播涉及大量的矩阵-矩阵乘法和矩阵-向量乘法。
- 可并行性:这些矩阵运算通常可以很好地被分解并行处理。例如,矩阵B乘以向量C得到向量A的计算,可以按颜色分区,各部分独立计算,最后合并结果。
- 计算示例:
A = B * C(其中B是矩阵,C是向量,A是结果向量)。通过将B和C分块,可以在多个GPU核心上并行计算部分结果。

深度学习中的HPC需求

深度学习模型,尤其是像Transformer这样的大型语言模型,其成功很大程度上得益于HPC提供的强大计算能力。
以下是HPC对于深度学习的关键作用:
- 训练成本高昂:训练大型网络需要巨大的计算量。例如,卫星图像分析或ImageNet等基准测试,涉及数千万张图片,其数据量和计算需求远超个人电脑的处理能力。
- 超参数优化:深度学习模型有许多需要手动设置的超参数(如学习率、批大小、优化器类型、网络层数等)。找到最佳超参数组合需要多次训练尝试,这本身就是一个计算密集型任务。
- HPC的双重收益:使用HPC不仅能够显著缩短单次模型的训练时间(通过并行化),还能通过并行探索大量超参数配置,从而找到更优模型,提高最终预测的准确性。超参数优化本身已成为一个重要的HPC应用领域。



HPC与大数据的挑战
最后,我们简要探讨HPC与大数据的关系。大规模模拟科学(如高精度地震模拟)会产生PB级甚至EB级的数据输出。
以下是HPC面临的数据挑战:
- 存储层级差距:计算核心的缓存和内存速度很快,但容量有限且昂贵。用于海量数据存储的磁带归档库容量大但速度极慢。磁盘处于中间位置,但仍难以匹配现代GPU的计算吞吐量对数据供给的需求。
- “内存墙”/“DRAM Gap”:内存容量和速度的增长跟不上计算能力(尤其是GPU)的增长速度,导致数据供给成为瓶颈。
- 应用协同设计:这一挑战推动了“应用协同设计”的HPC架构发展,例如模块化超级计算机,以及像NVIDIA Grace Hopper这样的新型芯片,它集成了高速的HBM内存,旨在更好地为GPU喂数据。然而,满足多用户、多应用的海量数据需求仍是持续的研究课题。


总结与展望
本节课中我们一起学习了高性能计算生态系统的关键软件技术(调度、监控、中间件),了解了超级计算机的硬件组织术语(芯片、节点、机架、网络拓扑),并深入探讨了当前HPC发展的两大引擎:GPU加速计算及其在驱动人工智能/深度学习革命中的核心作用。我们还指出了HPC在处理大规模模拟数据时面临的I/O和存储层级挑战。
HPC是一个悠久的领域,并持续演进。展望未来,量子计算等新兴技术可能成为新的加速器。欧洲的EuroHPC联合事业等倡议正在推动下一代百亿亿次(Exascale)及更高级别的超级计算发展。作为学生和未来的从业者,理解这些基础概念和趋势,将有助于你在这个充满活力且至关重要的领域中探索和贡献。
下次实践课,我们将具体学习如何使用SLURM调度系统。



027:高性能计算导论
在本节课中,我们将要学习高性能计算的基本概念。我们将从回顾上一讲的关键内容开始,然后深入探讨高性能计算的定义、核心架构、并行编程模型以及相关的生态系统。课程旨在为初学者提供一个全面而清晰的概览。

回顾:远程访问与Unix基础
上一节我们介绍了课程的基本信息,本节中我们来看看如何为实际操作做好准备。要使用高性能计算系统,首先需要掌握远程访问和基本的Unix操作技能。
以下是远程访问HPC系统的核心步骤:
- 生成SSH密钥对:在本地计算机上生成一个私钥-公钥对。私钥必须始终安全地保存在你的本地主机上,切勿分享。公钥则可以安全地提供给HPC系统的管理员。
- 配置远程系统:管理员会将你的公钥与你在HPC系统上的用户名关联。这样,系统就允许持有对应私钥的用户进行访问。
- 建立连接:使用SSH客户端(如MobaXterm、macOS/Linux终端)并指定主机名、用户名和你的私钥文件,即可连接到远程HPC系统。
成功登录后,你将面对一个Unix/Linux操作系统环境。需要掌握一些基本命令,例如使用 hostname 来确认你连接到了正确的远程系统。
此外,HPC系统通常使用模块系统来管理预安装的软件。你可以使用 module avail 查看可用软件,使用 module load [软件名] 来加载特定环境,例如Python或深度学习框架。
掌握SSH和基础Unix技能是使用全球范围内许多HPC系统(包括冰岛的EHPC、欧洲的EuroHPC联合项目系统)的通用钥匙。
高性能计算基础
理解了如何访问系统后,我们现在正式进入高性能计算的世界。高性能计算并不仅仅是关于强大的硬件,它是一个涵盖理论、多样化架构、复杂软件栈和实际应用的广阔领域。
什么是高性能计算? 🚀
高性能计算通常与超级计算紧密相关。它代表着计算领域的“一级方程式”,总是采用最前沿的处理器(如最新的NVIDIA GPU)来构建世界上最强大的系统。这些系统通过高性能网络互连将成千上万个计算节点连接起来,以支持密集的进程间通信。
一个常见的误解是HPC只关乎硬件。实际上,它包含多个层面:
- 理论与模型:涉及加速比定律、并行计算理论等。
- 硬件架构:包括多核/众核处理器、高速网络、存储系统等,并且架构可以非常多样化。
- 软件生态:包含操作系统、调度器、监控工具、数值库、科学可视化工具以及领域专用的应用程序。
- 实际访问:通过项目申请等方式,研究人员和学生可以获取这些强大系统的计算资源。


并行处理与科学可视化 🎨
高性能计算的核心目标是并行处理:将一个大问题分解,同时在多个处理器(进程)上协作解决,以获得最佳性能。
许多科学和工程问题,如计算流体动力学模拟飞机湍流、气候建模或分子动力学模拟,计算量极其庞大。并行计算使得这些模拟成为可能。
然而,模拟产生的数据往往非常复杂和抽象。这时,科学可视化就变得至关重要。它能够将庞大的数值数据转化为直观的图像、动画或交互式图形,帮助研究人员理解和发现隐藏在数据中的规律。正所谓“一图胜千言”。
性能衡量:TOP500榜单 📊

全球超级计算机的性能通常通过 TOP500 榜单来排名。该榜单每半年更新一次,使用LINPACK基准测试程序来评估系统的浮点计算能力。
观察TOP500榜单可以了解行业趋势,例如不同厂商的市场份额、主流处理器技术(如NVIDIA GPU各代产品的普及情况)等。但需要注意的是,LINPACK测试主要针对密集线性代数运算,并不能完全代表所有实际应用的性能。因此,像HPCG这样的更贴近实际应用的基准测试也变得越来越重要。

核心硬件:多核处理器

现代计算的基础是多核处理器。单个CPU芯片内集成了多个处理核心,每个核心都能独立执行线程。这些核心通常具有较高的单线程性能(高主频),并共享多级缓存,以加速对内存的访问。
多核CPU仍然是许多应用的主力。然而,由于物理限制(功耗和散热),单纯提升单个核心的频率已遇到瓶颈,这推动了众核处理器(如GPU)的发展,我们将在课程第二部分详细讨论。
主流架构与编程模型
了解了基本组件后,我们来看看如何将它们组织成完整的系统。当前主要有两种基础架构,而实际系统往往是两者的混合体。
共享内存架构

在共享内存系统中,多个处理器核心通过总线或交叉开关连接到一个统一的全局内存池。所有核心都能直接访问这片共享内存。
- 编程模型:主要使用 OpenMP 标准。它是一种基于编译器指令的模型,通过在代码中标记并行区域(如循环)来创建多个轻量级线程。
- 通信方式:线程间通过读写共享内存中的变量进行通信,无需显式发送消息。
- 优点:编程相对简单直观。
- 挑战:需要处理同步问题(如竞态条件),且可扩展性受限于单一内存控制器的带宽和延迟。常见的两种模型是:
- UMA:统一内存访问,所有处理器访问任何内存地址的时间相同。
- NUMA:非统一内存访问,处理器访问本地内存更快,访问其他处理器的内存较慢,但通过缓存一致性协议对程序员屏蔽了这种差异。
分布式内存架构
在分布式内存系统中,每个计算节点都有自己的处理器和本地内存。一个节点内的内存不能被其他节点直接访问。

- 编程模型:主要使用 MPI 标准。它是一个消息传递接口库,程序运行时会启动多个独立的进程。
- 通信方式:进程之间必须通过网络互连显式地发送和接收消息来交换数据。
- 优点:具有极强的可扩展性,可以通过增加节点来构建包含成千上万个处理器的超大规模系统。
- 挑战:编程更复杂,需要开发者显式地管理数据分布和通信。

混合架构与未来挑战

现代超级计算机通常是混合架构。在一个计算节点内部,多个CPU核心构成一个共享内存系统,可以使用OpenMP进行并行化。而跨节点之间,则构成一个分布式内存系统,必须使用MPI进行通信。

此外,节点上还可能挂载加速器(如GPU)。这就形成了“三明治”式的复杂编程模型:MPI用于节点间通信,OpenMP用于节点内CPU并行,CUDA或OpenACC等用于GPU加速。



这种混合模式带来了巨大的编程复杂性,但也是充分利用现代超算强大算力的必然途径。在后续课程中,我们将深入学习MPI和OpenMP。
总结


本节课中我们一起学习了高性能计算的入门知识。我们首先回顾了通过SSH密钥对安全访问远程HPC系统以及必要的Unix基础。然后,我们探讨了HPC的广泛内涵,它远不止是硬件,更包含理论、软件和应用生态。

我们了解了并行处理是HPC的核心,科学可视化是理解复杂模拟结果的关键工具,而TOP500榜单则是衡量全球超算性能的标尺。接着,我们剖析了共享内存与分布式内存这两种基础架构及其对应的编程模型(OpenMP和MPI),并指出现代系统实为两者结合的混合架构,且正融入GPU等加速器技术。



掌握这些基础知识,为你后续深入学习并行编程、特定应用领域以及利用EuroHPC等大型计算资源奠定了坚实的基础。
028:SSH与UNIX简短介绍(第二部分)🚀
在本节课中,我们将继续学习SSH和UNIX的基础知识。上一部分我们重点介绍了SSH密钥对的工作原理,并实际登录到了DEEP测试集群。本节我们将更深入地探索UNIX命令行环境,学习一些在HPC系统中进行日常操作所必需的核心命令,并了解如何管理复杂的软件环境。
系统概览与回顾



上一节我们介绍了SSH,本节我们来看看我们将要使用的HPC系统。理解这些系统的规模有助于我们明白为何需要特定的工具和环境。
- DEEP测试集群:这是一个模块化超级计算机的演示系统。其集群模块拥有50个节点,采用高性能CPU;其加速器模块则拥有75个节点,配备了NVIDIA V100 GPU。它采用了Mellanox InfiniBand高速互联网络,这对于大规模并行计算至关重要。
- Jülich超级计算中心的Juwels系统:这是一个真正的生产级超级计算机。仅其集群模块就拥有超过2000个标准计算节点,提供约120,000个CPU核心。其加速器模块拥有超过900个计算节点,配备了更先进的NVIDIA A100 GPU。这展示了HPC系统的实际规模。
- 冰岛大学的Euler系统:这是一个用于教学和研究的大学级集群。规模较小,但也配备了NVIDIA A100 GPU。研究人员可以在此进行初步研究,然后申请欧洲大型超算(如Juwels)的计算资源来扩大计算规模。

所有这些系统,无论规模大小,都通过SSH访问,并在UNIX环境下运行。掌握这些基础技能是使用任何HPC系统的前提。


基础UNIX命令实践



现在,让我们回到命令行,学习一些在HPC系统上导航和操作文件的基本UNIX命令。我们将以登录到DEEP系统为例。

首先,我们使用SSH密钥登录到DEEP系统。命令格式为:ssh -i <私钥路径> <用户名>@<主机名>。
登录后,我们需要确认自己身处远程系统。以下是几个有用的命令:



hostname:显示当前主机名。hostname -a:显示主机的完整域名信息,这能清晰表明我们已连接到远程系统(例如deep.fz-juelich.de)。whoami:显示当前登录的用户名,确认我们使用的是远程系统上的身份,而非本地笔记本电脑的身份。
另一个常用命令是 clear,它可以清空终端屏幕,让界面变得整洁。
接下来,我们学习如何在文件系统中导航和操作。
pwd:打印当前工作目录的完整路径。ls:列出当前目录下的文件和子目录。使用ls -l可以查看详细信息,包括权限、所有者、大小和修改时间。cd <目录名>:更改当前工作目录。例如,cd hpc_course_spring_2025进入该目录。mkdir <目录名>:创建新目录。例如,mkdir test_dir。rmdir <目录名>:删除空目录。rm <文件名>:删除文件。警告:rm -rf *命令会强制删除当前目录下的所有文件和子目录,使用时必须极其小心。touch <文件名>:创建一个新的空文件,或更新现有文件的时间戳。例如,touch test_file.txt。

Tab 键是一个非常有用的功能,它可以自动补全命令、文件名或目录名。输入部分字符后按 Tab 键,系统会尝试补全;如果存在多个可能选项,按两次 Tab 键会列出所有选项。
模块化软件环境
HPC系统不仅拥有强大的硬件,还配备了复杂的软件栈以支持各种科学和工程应用。不同的用户和项目需要不同的编译器、库和工具。为了高效管理这些多样化的软件需求,大多数HPC系统采用了模块环境。
模块环境允许用户动态地加载或卸载特定的软件包及其依赖,而无需手动设置复杂的环境变量。
以下是在DEEP系统上使用模块环境的基本命令:


module avail:列出系统中所有可用的软件模块。你会看到从底层编译器(如GCC、Intel)、并行库(如OpenMPI)到高级应用(如Python、R、PyTorch、TensorFlow)的丰富列表。module spider <软件名>:搜索某个特定软件模块的详细信息,包括其可用版本和依赖关系。例如,module spider pytorch。module load <模块名>:加载一个软件模块到当前环境。例如,module load python/3.9.5。module unload <模块名>:卸载一个已加载的软件模块。module list:显示当前已加载的所有模块。
通过模块环境,系统管理员可以预编译和配置好软件,用户只需简单加载即可使用,这极大地简化了在HPC系统上的软件管理工作。

文本编辑器与文件权限

在HPC系统上工作时,经常需要查看或编辑文本文件(如脚本、配置文件、代码)。vi 是一个几乎在所有UNIX系统上都可用的基础文本编辑器。

vi 有两种基本模式:
- 命令模式:刚打开文件时即处于此模式,可以执行保存、退出、搜索等命令。
- 插入模式:在此模式下可以输入文本。按
i键进入插入模式。



基本操作流程:
- 打开文件:
vi filename.txt - 按
i进入插入模式,开始编辑。 - 编辑完成后,按
Esc键返回命令模式。 - 在命令模式下,输入
:wq并回车,表示保存并退出。
此外,使用 ls -l 命令时,会看到文件权限信息,例如 -rw-r--r--。这串字符表示:
- 第一个字符
-表示这是一个普通文件(d表示目录)。 - 接下来的三组
rwx分别代表文件所有者、所属用户组和其他用户的权限。 r代表读权限,w代表写权限,x代表执行权限。- 例如,
-rw-r--r--表示所有者可读可写,组用户和其他用户只可读。


在Euler系统上实践
为了巩固所学,我们现在退出DEEP系统,并登录到冰岛的Euler系统进行同样的操作。这能验证我们学习的命令在不同HPC系统上的通用性。



我们使用类似的SSH命令登录,但注意用户名和私钥可能不同:ssh -i ~/.ssh/id_rsa_euler morris@euler.hpc.is。



登录后,我们可以重复之前的命令:
hostname -a确认我们已在Euler系统(例如euler.hpc.is)。whoami显示用户为morris。pwd查看当前目录。- 使用
cd,ls,mkdir,touch,rm等命令进行文件操作。 - 运行
module avail查看Euler系统上可用的软件模块。虽然作为一个教学集群,其软件数量可能少于大型生产系统,但依然包含GCC编译器、Python、R、MPI库以及支持其NVIDIA A100 GPU所需的CUDA工具包等核心组件。


通过在不同系统上的实践,我们可以看到,尽管硬件配置和安装的软件包不同,但SSH访问方式、基础UNIX命令以及模块环境的使用逻辑是相通的。

总结与展望

本节课中,我们一起学习了HPC入门所需的核心实践技能。
我们首先回顾了SSH密钥对登录,并实际操作了多个基础UNIX命令,包括系统信息查询、文件导航、目录与文件管理。我们深入了解了模块化软件环境,这是管理HPC上复杂软件依赖的关键工具。我们还简单介绍了 vi 编辑器和文件权限的概念。最后,我们在DEEP和Euler两个不同的HPC系统上进行了实践,验证了这些技能的通用性。


掌握这些SSH和UNIX基础,将使你能够在任何HPC系统上迈出第一步,不会在命令行环境中迷失。在接下来的课程中,我们将更深入地探讨HPC的概念、并行编程模型(如MPI和OpenMP)以及作业调度系统,并利用今天学到的技能来编译和运行我们的第一个并行程序。
029:实践讲座 0.1 - SSH 与 UNIX 简短介绍(第一部分)🚀
在本节课中,我们将学习如何通过 SSH 协议连接到远程高性能计算系统,并初步了解 UNIX 环境。这是支持后续课程作业的实践性讲座。
课程回顾与背景
上一节我们介绍了高性能计算课程的整体框架,探讨了其不断演进的技术背景及其在科学与工程应用中的重要性。本节中,我们来看看支持这些应用的基础访问工具。
高性能计算涉及大规模并行计算,例如模拟汽车碰撞、计算流体动力学或地下水建模。这些任务需要远超个人笔记本电脑能力的计算资源,即由众多 CPU 和 GPU 并行工作的高性能计算机。
课程将涵盖两种主要的并行计算范式:用于跨节点通信的消息传递接口和用于节点内共享内存的OpenMP。此外,我们还将学习分布式深度学习、计算流体动力学以及利用 GPU 等加速器。访问这些超级计算机通常需要通过 SSH 协议,并在 UNIX 环境下操作,这正是本次实践讲座的重点。
连接高性能计算系统
要使用高性能计算集群,首先需要安全地远程访问它们。这不仅仅是硬件连接,还涉及一系列 HPC 环境工具,例如作业调度和本次将重点介绍的 SSH 访问 与 模块环境。SSH 是一种几乎被全球所有 HPC 系统采用的安全访问协议。
我们将以两个系统为例进行演示:
- DEEP 系统:位于德国于利希超算中心,是一个采用模块化超算架构的研究原型系统。
- Elli 系统:位于冰岛,是冰岛国家研究基础设施的一部分。
以下是访问 HPC 系统所需的关键信息:
- 主机名:系统的网络地址(如
deep或elli)。 - 用户名:由系统管理员分配的唯一账户标识。
- SSH 密钥对:用于身份验证的一对加密密钥。
理解 SSH 与密钥对 🔑
SSH 使用非对称加密技术进行安全连接。其核心是公钥-私钥对。
- 私钥:必须严格保密,存储在你的个人电脑上。它用于解密由对应公钥加密的信息。
- 公钥:可以公开分享,需要上传到目标 HPC 系统。
连接过程如下:
- 你在本地生成一对密钥。
- 将公钥上传到 HPC 系统。
- 当你通过 SSH 客户端连接时,客户端会向服务器出示你的私钥。
- 服务器用存储的公钥验证私钥的有效性。匹配成功,则授予访问权限。
公式表示认证过程:
服务器验证(客户端私钥签名) == 使用存储的公钥进行验证
为了使用私钥,你通常需要用一个密码来解锁本地的密钥存储。在建立连接时,SSH 客户端会使用你指定的私钥文件。
实践演示:连接到 DEEP 系统
现在,我们来看一个具体的连接示例。我们将使用 MobaXterm 作为 SSH 客户端连接到德国的 DEEP 系统。
连接命令的基本格式如下:
ssh -i /path/to/your/private_key username@hostname
在图形化客户端中,你需要填写:
- 远程主机:
deep - 用户名:你的专属用户名
- 私钥文件:选择你本地存储的私钥文件路径
首次连接时,系统会提示你输入密码来解锁私钥。成功登录后,你会看到系统的欢迎屏幕,其中包含重要通知、维护信息和基本使用指南。
你可以使用一些简单的命令来确认自己已进入远程系统:
hostname:显示当前主机名。ls -la:列出当前目录下的文件和目录。exit:退出当前 SSH 会话。



实践演示:连接到 Elli 系统
连接不同系统的原理完全相同,只是参数发生了变化。现在,我们退出 DEEP 系统,连接到冰岛的 Elli 系统。

所需的变化信息是:
- 远程主机:
elli - 用户名:在 Elli 系统上分配的用户名
关键点在于:即使你使用同一对 SSH 密钥,只要将公钥上传到了不同的 HPC 系统,你就可以通过更改主机名和用户名来访问它们。私钥始终保留在你的本地机器上。
在 MobaXterm 中新建一个会话,填入 Elli 系统的主机名和用户名,并选择相同的私钥文件进行连接。成功登录后,同样可以使用 hostname 等命令确认环境。


欧洲高性能计算生态系统

通过 SSH 可以访问的不仅是课程中使用的系统。例如,欧洲高性能计算联合事业支持多个大型超级计算机,如芬兰的 LUMI、意大利的 Leonardo 和西班牙的 MareNostrum 5。这些系统通常需要通过项目评审来获得计算资源配额。即将上线的 Jupiter 系统将是欧洲首台百亿亿次级超算,同样采用模块化架构,这标志着相关研究项目(如 DEEP 项目系列)的目标正在成为现实。
本节总结

本节课中,我们一起学习了高性能计算系统访问的基础。
- 我们回顾了 HPC 的应用背景,理解了远程访问的必要性。
- 我们掌握了 SSH 协议 的基本概念,特别是公钥-私钥对的工作原理。
- 我们通过实际操作,演示了如何使用 SSH 客户端连接到位于德国和冰岛的两个不同的 HPC 系统。
- 我们了解到,改变主机名和用户名即可用同一密钥访问不同系统,而私钥必须妥善保管在本地。



这为你后续的课程作业打下了基础。在接下来的部分,我们将在成功连接的 UNIX 环境中,学习一些基本的命令和操作。
030:序言(第二部分)

概述
在本节课中,我们将继续介绍高性能计算课程的完整大纲。我们将逐一讲解从第1讲到第16讲的核心内容,涵盖从并行编程基础、GPU加速计算,到计算流体力学、深度学习应用以及前沿的自动化机器学习等主题。通过本次概述,您将对整个课程的结构和重点有一个清晰的了解。
课程内容详解
上一节我们介绍了高性能计算的基本概念和应用领域,本节中我们来看看课程每一讲的具体安排和核心知识点。
第1讲:高性能计算导论
本讲作为入门课程,旨在解答关于高性能计算的基本问题。我们将探讨高性能计算的基本构成、衡量超级计算机性能的Top 500榜单、并行计算(包括共享内存和分布式内存)的必要性,以及GPU的高吞吐量计算理念。课程还将介绍HPC的生态系统,包括软件环境、SSH访问、作业调度器、Linux操作系统以及低层编程接口。此外,我们会简要提及欧洲的超级计算资源和作为新型加速器的量子计算系统。
第2讲:并行编程实践(MPI入门)
本讲将进入实践环节,通过较小的编程项目帮助理解分布式内存编程。核心是学习消息传递接口(MPI) 的概念。MPI之所以重要,是因为在分布式内存系统中,一个处理器无法直接访问另一个处理器的内存地址空间。我们将学习MPI的基本通信模式,例如点对点的发送(send)与接收(receive),以及集体通信操作如广播(broadcast)。同时,我们会介绍如何在HPC系统上配置编译环境、使用MPI库模块,并区分良好与欠佳的MPI编程实践。
第3讲:并行化理论与策略
在初步实践后,本讲将深入探讨并行化的理论与策略。核心目标是理解如何将一个大问题分解为多个小任务(即域分解)。我们将学习MPI中的笛卡尔通信器(Cartesian Communicator),它在处理网格状数据时非常有用。此外,课程将涵盖负载均衡、加速比(Speedup)等概念,并探讨影响程序可扩展性的理论极限,如阿姆达尔定律(Amdahl‘s Law)。该定律指出,程序的加速受其串行部分的限制。
第4讲:共享内存编程(OpenMP)
本讲将转向共享内存编程范式,使用OpenMP。与MPI的进程间通信不同,OpenMP通过线程实现并行,所有线程共享同一内存空间。这使得编程在某些方面更简单,但可扩展性受限于单台机器的共享内存容量。我们将学习如何使用OpenMP的编译制导语句(#pragma)来标记并行区域,特别是如何并行化循环。课程还将讨论共享变量与私有变量的区别、临界区(critical)的作用以避免数据竞争(race condition),以及必要的同步机制。
第5讲:加速器与图形处理器(GPU)
本讲专注于通用图形处理器(GPGPU)。我们将探讨GPU为何特别适合处理大规模并行计算任务,例如矩阵-矩阵乘法,这正是深度学习等应用的核心。课程会对比多核(Multicore) CPU与众核(Manycore) GPU的架构差异,并介绍不同厂商(如NVIDIA、AMD)的GPU架构演进。我们还将讨论GPU在科学计算库和机器学习中的广泛应用。
第6讲:深度学习导论
本讲是高性能计算课程中的深度学习入门。我们将快速介绍人工神经网络的基础,重点解释为何深度学习模型(如卷积神经网络CNN、长短期记忆网络LSTM)的训练过程计算量巨大,涉及海量的矩阵运算和参数优化,从而凸显了对高性能计算资源的迫切需求。
第7讲:分布式深度学习
本讲深入探讨HPC如何加速深度学习。我们将学习分布式训练的核心策略:
- 数据并行:将训练数据分割到多个节点上,每个节点使用相同的模型进行训练,然后同步梯度。
- 模型并行:将模型本身分割到不同节点上。
课程将介绍实现这些策略的常用工具,如TensorFlow和PyTorch,并结合实际研究案例展示如何利用多节点HPC集群显著缩短模型训练时间。
第8讲:计算流体力学(CFD)导论
从本讲开始,课程进入计算流体力学专题。CFD用于求解现实中无法获得解析解的流体动力学问题,通常需要超级计算机的支持。我们将介绍CFD的应用范围、控制流体运动的纳维-斯托克斯方程(Navier-Stokes Equations)、数值离散方法,以及层流与湍流的区别。课程还会讲解CFD求解流程、网格生成、边界条件设置,并重点介绍用于模拟湍流的三种主要方法:雷诺平均纳维-斯托克斯方程(RANS)、大涡模拟(LES)和直接数值模拟(DNS)。
第9讲:CFD中的并行计算
本讲探讨如何将并行计算应用于CFD领域。核心内容是如何对CFD的计算域进行分解,以便分配到多个处理器上并行计算。我们将介绍适用于CFD的并行拓扑结构、向量计算机与超标量计算机的概念,并讨论显式与隐式求解方法及其对并行计算的影响。课程将通过结构化和非结构化网格的实例进行说明。
第10讲:OpenFOAM软件实践
本讲以开源CFD软件OpenFOAM为例进行实践。我们将学习如何使用OpenFOAM设置几何、生成网格、定义边界条件并选择求解器。课程将演示如何模拟层流和湍流,并展示如何将OpenFOAM从单机运行扩展到并行计算环境,包括域分解和结果收集与可视化。
第11讲:深度学习在CFD中的应用(序列模型)
本讲探索深度学习在CFD,特别是湍流预测中的应用。我们将介绍一项具体研究:利用长短期记忆网络(LSTM) 学习湍流数据,并预测其速度场。课程将涵盖模型设计、训练过程,并展示LSTM与传统方法的对比结果。这是一个结合人工智能、CFD实验数据和高性能计算的典型案例。
第12讲:格子玻尔兹曼方法
本讲介绍一种替代传统CFD方法的数值技术——格子玻尔兹曼方法(LBM)。LBM基于粒子碰撞和流动的微观模型。我们将讨论LBM的历史、玻尔兹曼方程、边界条件处理、其优势与局限性,以及如何将其用于湍流模拟(如基于LBM的大涡模拟)。LBM本身具有良好的并行计算潜力。
第13讲:深度学习在CFD中的应用(Transformer模型)
本讲介绍基于注意力机制和Transformer架构的深度学习模型在CFD中的应用。Transformer采用编码器-解码器结构,在捕捉复杂序列依赖关系方面表现出色。课程将通过一个湍流预测的实际案例,展示Transformer模型的设计、训练,并将其性能与LSTM等其他模型进行对比,验证其在处理无固定模式的湍流数据上的有效性。
第14讲:超参数优化与自动化机器学习
本讲是今年新增的前沿主题,涵盖超参数优化(HPO) 和自动化机器学习(AutoML)。超参数(如学习率、网络层数)深刻影响模型性能,传统上依赖专家经验调整。课程将介绍如Ray等工具集,它们能自动搜索最优超参数组合。AutoML则能进一步自动化整个机器学习流程,包括模型选择与组合,降低非专家用户的使用门槛。


第15讲:绿色能源与HPC应用
本讲从两个视角探讨能源与HPC的关系:
- HPC赋能绿色能源:在智能电网和智慧城市中,利用深度学习预测风电、水电、太阳能等可再生能源的发电量,需要HPC处理海量数据。
- HPC的绿色运行:超级计算机本身是耗能大户。本讲将计算HPC系统的能源需求,并探讨如何利用可再生能源为其供电,实现绿色、清洁的计算。
第16讲:课程总结与展望
本讲是课程的非正式总结环节。我们将回顾整个课程,从思维模式、技能组合、工具掌握三个层面进行梳理。课程将讨论HPC相关的职业机会、所需的核心技能(如MPI、OpenMP、GPU编程),以及如何将所学应用于硕士项目或博士研究。最后,我们会展望未来计算趋势,如量子计算、神经形态计算等,为学员的持续学习指明方向。

总结
本节课中我们一起学习了高性能计算课程(第二部分)的详细大纲。从并行编程基础(MPI, OpenMP)、GPU加速,到与人工智能的深度结合(分布式深度学习、HPO/AutoML),再到计算流体力学(CFD)的专业应用及绿色能源议题,本课程旨在提供HPC领域广泛而实用的知识图谱。我们希望这门课程能激发您的兴趣,并为未来在科学计算与人工智能交叉领域的研究或工作打下坚实基础。
031:课程序言(第一部分)

在本节课中,我们将一起了解高性能计算(HPC)课程的整体概览、动机、内容结构以及它与现代计算科学和人工智能的紧密联系。
课程概述与结构
欢迎来到高性能计算课程。我是Morris,将与我的同事Raza一同讲授这门课程。
今天,我们进行的是第0讲,即课程的序言。这是一个温和的开端,旨在解释课程中出现的诸多元素,例如幻灯片上的各种标识,并介绍课程内容,解答你可能存在的疑问。
在序言之后,课程将正式开始。首先,我们会深入介绍高性能计算,提供比今天更详细的导论。紧接着,我们会进入实践环节。课程设计将实践主题前置,以便与你的作业相结合。绿色的主题通常与你的作业直接相关。
我们将学习使用MPI和OpenMP进行并行编程,并因此需要了解并行化基础,这构成了你的第一项作业。根据过去十年的教学经验与学生反馈,我们没有像往常那样先讲授并行化基础等概念性主题,而是选择从一些实践主题入手,让你尽早开始动手。课程中还会穿插一些实践讲座,由Raza或我进行演示,这些内容也与你的作业直接相关。
从并行编程到加速计算
在理清并行化的基本概念,并理解如何在HPC机器上进行并行编程后,我们将深入探讨过去五到十年间日益重要的领域:加速器和图形处理单元(GPU)。我相信你们中的许多人都已经听说过GPU。
接下来,我们将直接进入课程的另一个实践环节:理解深度学习,特别是分布式深度学习。鉴于深度学习目前在HPC领域获得的巨大发展势头,以及许多应用离不开HPC资源,我们今年特别引入了这个新主题。这是一个非常有趣且实用的方向。
计算流体力学与人工智能的融合
今年,我们还将计算流体力学(CFD)作为一个重点引入,这得益于另一位讲师Raza的专长。他将在后续课程中带领大家深入这个领域。CFD涉及理解流体流动等物理现象,我们将学习为何并行计算对此至关重要,以及如何使用OpenFOAM等软件。例如,我们可以用它来模拟冰岛的雪崩。鉴于OpenFOAM的发展势头,我们将其纳入了课程。
同时,结合当前HPC领域的发展趋势,我们也将探讨CFD与人工智能的融合,即物理信息深度学习或基于物理定律约束的深度学习。这是一个有趣的新领域,我们将学习深度序列模型(处理时间序列数据)及其在CFD应用中的体现。为了更好理解CFD,我们还需要学习一些基础概念,如纳维-斯托克斯方程等。
前沿模型与优化技术
近期,深度学习中的Transformer模型也获得了巨大发展势头。我们在此领域进行了大量研究,因此课程中也会包含相关的实践内容。
我们还集成了超参数优化和自动机器学习(AutoML)作为新主题。这意味着HPC的优势不仅在于加速深度学习模型的训练,还在于优化模型本身。选择合适的超参数通常对模型的准确率有巨大影响,我们将在相关讲座中学习这一点。
最后,绿色能源是当前讨论超大规模HPC应用时越来越受关注的话题,因此在这样的课程中也值得考虑。
课程收尾与职业关联
课程的尾声部分将较为正式地总结,同时也会非正式地探讨职业描述,解答你的问题,将课程内容与未来的工作机会联系起来。今天讲座的第二部分将更详细地介绍所有这些不同的课程内容,帮助你更好地理解。
高性能计算的动机与领域
现在,让我们从课程动机出发,回顾一下高性能计算本身。当然,第一讲会全面介绍它,但你们中的许多人可能已经知道,它关乎那些有趣的、装满计算资源的庞大系统。这是一个不断变化的广阔领域,如今你甚至可以攻读HPC方向的硕士学位。
这门课程所能教授的,只是HPC技术和概念的基础与概览。这主要体现在两个层面:一是物理视角,例如CFD可用于模拟汽车碰撞或管道中的烟雾传播,这些通常基于已知物理定律的数值模拟;另一个是地球观测领域,例如结合大气降雨模拟地下水流动和地表相互作用。正是这些计算密集型的应用,推动了过去几十年高性能计算领域的设计与发展。
课程标识中的红色部分暗示了在计算机内部模拟飞机飞行的概念,我们将在课程后期了解更多。我们不仅会涵盖一些基础知识,还会尝试带你进入一些特定领域的应用,如CFD,以及其他科学与工程领域。如今,这些领域也常与人工智能结合,因此我们也相应调整了课程材料,加入了分布式深度学习等内容。
并行编程基础与核心理念
从编程角度看,共享内存和分布式内存编程仍然是基础。你将在课程中学习这些概念,尽管我们会将其控制在适度范围内。例如,MPI(消息传递接口)用于在HPC机器上进行分布式编程,这本身就可以是一门为期数周的课程。
因此,这门课程是基于学生反馈和社区新动态精心设计的混合体。课程标识或许应该更多地向AI方向更新,但我们并非只关注物理。关键要点在于,你将学习并行处理与分布式计算,即从使用单个GPU转向使用多个GPU。
这样,我们就进入了高级科学计算领域,不再局限于你可能从其他课程(如使用Matlab、Scikit-learn的AI课程)中了解的、在单台笔记本电脑或台式机上进行的计算。这里我们考虑的是大规模计算模拟和深度学习,它们需要更强的计算能力。因此,我们必须停止串行计算,转向并行计算。这带来许多影响,不仅包括之前幻灯片中提到的消息交换,还包括技术层面,如内存大小、多核/众核处理器的使用,以及如何结合物理/数值方法和AI工具来利用所有这些技术。
课程目标与技能培养
本课程还特别包含CFD和一些AI内容,旨在让你为就业做好准备,因为市场对高性能计算和并行处理技能的需求日益增长。课程结束时,你应该对最新发展有深入的了解。当然,我们无法在一门课程中涵盖HPC的所有领域科学应用(从等离子体物理到固体物理等),但你将获得并行处理工作原理以及HPC集群构建方式的基本理解。
网络互连在HPC中扮演重要角色,而当我们讨论深度学习和AI时,数据密集型工作负载也是重要议题。我们将在课程开始时教你一些重要概念,特别是域分解——如何将一个大问题分解成更小的部分。这在并行编程中至关重要,即通过共同解决小问题来联合解决大问题。
在整个课程中,尤其是在实践环节,你将学习HPC环境工具,例如作业调度系统,甚至是进行C编程时所需的编译器。别担心,我们不会做太多C编程,主要是通过一些C代码来理解MPI,然后我们会使用Python进行AI编程等。
我们还将学习并行计算有不同的实现层次,你可以根据目标进行选择。特别是,如果你考虑自己使用MPI或OpenMP进行共享内存编程,就需要考虑变量和并行性等问题;而使用TensorFlow、PyTorch等现代AI工具时,它们本身就在处理并行性。
当然,这不是一门纯粹的CFD课程,也不是一门纯粹的AI课程。如果你对这些领域感兴趣,还需要选修相应的专门课程。但在本课程之后,你将掌握如何进行并行计算的基础知识,并理解其基本原理(例如我们讨论过的域分解)。因此,你将有能力在HPC系统上进行编程,从而比从Scikit-learn或Matlab等小型串行技术中获得的体验更进一步,充分利用并行技术的优势。这些技术在设计上就考虑了处理并行复杂性的思想,我们将在后续课程中学习。
讲师背景与相关资源
简要说明一下由我来讲授HPC的原因:我在这个领域已有超过20年的经验,在卡尔斯鲁厄理工学院获得了HPC和网格应用方向的计算机科学博士学位。我目前仍在于利希超算中心(欧洲最大的HPC中心之一)任职,在来冰岛任教之前曾在那里担任多个职位。冰岛和于利希之间保持着良好的合作,我的研究小组约有20名学生,经常往返于德国和冰岛之间。
我还曾涉足云计算等领域,担任过战略总监等职务,并参与了许多欧盟项目。与本课程更相关的是,你将会听到“EuroHPC联合执行体”这个术语,它是新近成立的,我稍后会谈到。我也是冰岛在该联合执行体理事会的成员,参与EuroHPC的战略规划。
此外,你可以在YouTube上找到许多过去的大学课程视频。本课程材料源自多门课程,如统计数据挖掘以及早期的高性能计算课程。像往年一样,所有内容基本上都会在YouTube上公开。
快速介绍一下于利希超算中心,它会反复出现,因为我们也会使用该中心系统景观中的一些HPC系统。该中心是一个拥有5000多名研究人员的大型研究中心,旗下有多个专注于特定领域的中心。这是我们具体合作的一部分。
我还提到,课程中会有另一位讲师Raza,他是我的博士后,最近于2024年底毕业,是流体力学等物理领域的计算工程专家。他将在课程后期,当我们完成深度学习基础介绍后,接手CFD部分的讲授。他稍后也会自我介绍。
冰岛大学与国家级能力中心
继续介绍冰岛大学。我想你们中的许多人可能已经了解,但对于YouTube上的广大观众来说,冰岛大学在遥感等领域是一所重要的大学,在《泰晤士高等教育》等排名中表现突出。我们拥有许多国际学生和交流项目。
同时,我们也是冰岛国家高性能计算与人工智能能力中心的协调机构,该中心隶属于EuroCC项目。这是一个庞大的体系。我们必须讨论这一点,因为它现在也与HPC直接相关。
因此,我们来谈谈EuroHPC联合执行体。它的使命是与欧盟委员会、欧盟成员国以及冰岛、挪威等欧洲自由贸易联盟国家合作,支持HPC的发展,为研究、工程和工业提供不同的项目和资源。我们参与了其中几个项目。
EuroCC项目非常重要,你可以在EuroHPC网站上看到相关信息。能力中心会组织活动,我们将在课程中称之为研讨会,你可以通过参加这些活动获得加分或用于最终项目。冰岛在所有不同的研究项目中都非常活跃,包括云项目。一些选修过云课程的同学可能已经知道,我们也参与了欧洲开放科学云等项目。
冰岛的HPC能力中心体系在所有成员国中都有类似机构,例如芬兰NCC、挪威NCC、德国NCC等。我们将能力组织成所谓的“模拟与数据实验室”,涵盖自然语言处理(由我的同事Hafsteinn Einarsson驱动)、化学(Hannes Jónsson)、计算流体动力学(Raza)、软件工程(Matthías Book)、遥感(Gabriele Cavallaro教授)等不同主题。任何以某种方式使用HPC的人员,基本上都是这个能力中心的一部分。我们将他们组织到模拟与数据实验室中,以积累特定领域的经验,从而更好地服务于工业界用户。
工业合作与应用实例
以下是两个合作示例:一是与冰岛一家语言公司合作,我们共同开发了名为“Trust the Lem”的欧盟项目,利用HPC资源训练了冰岛语的大型语言模型,这将成为后续课程的一部分;另一个是与工程系声学与触觉工程实验室的合作,他们与Össur公司合作,利用能力中心提供的HPC系统,更好地模拟教室、歌剧院等场所的声学效果。如果你参加我们宣布的EuroHPC研讨会,将有机会见到来自Össur、Mideind等工业合作方的人员。
当然,我们与许多不同伙伴都有合作,不仅是中小企业和大型企业,还包括冰岛统计局等公共管理机构,他们也是我们国家能力中心活动的参与者。
我想向你指出一个例子:遥感模拟与数据实验室。它规模很大,因为正如我之前所说,遥感对冰岛非常重要。这里进行了大量的深度学习和机器学习模型研究。对于你们中的一些人来说,这可能并不明显具体是什么。
通过机器学习,你可以理解地球覆盖物。我们不仅使用卫星每日拍摄的可见光图像,还使用高光谱和多光谱通道进行更深入的探测。例如,遥感实验室正在研究如何区分建筑物、河流、树木等。我们也会在课程中引用一些相关示例。
人工智能、机器学习与深度学习的区别
简要谈谈术语,因为我想说明HPC如今对于深度学习也至关重要。如果你之前没听说过这些,可以看到人工智能(包括机器人学)的广阔世界旨在模仿人类行为,这是一个需要分解的大主题。其中之一是专注于机器学习领域,即从数据中学习,而不是通过显式编程代码。我们当然会有相关讲座介绍如何做到这一点。
而今天我们所称的深度学习,是机器学习的一个特定子集。它指的是相对庞大、具有特定层级结构的神经网络,这些结构使其在学习任务中极为成功。学习任务的例子包括分类(将数据点归入不同组别)或聚类(根据欧几里得距离等将数据点分组),以及回归(发现数据趋势)等。所有这些都属于机器学习或数据挖掘算法。
但所有这些都会在不同的讲座中出现。让我简要地通过一个视频来解释深度学习模型和机器学习的复杂性。在机器学习中,我们手动设计特征,然后使用支持向量机等模型学习。而在深度学习中,特征学习内置于模型中,模型更大、能力更强。深度学习不仅仅是层数更深,它还包括卷积层、池化层、下采样层等用于特征学习的特定逻辑。我们有一整讲来讨论它。
正是通过深度学习,我们在许多领域取得了巨大成功,例如从图像片段学习整个场景,或者Transformer模型等创新方法,这些我们也会在课程中学习。
(视频内容解释:视频展示了从简单的感知机模型到多层感知机,再到卷积神经网络(CNN)的演变。CNN属于深度学习网络,它通过卷积过程构建特征图,逐层提取数字的特征(如小圆形部分),最后仍然通过全连接网络进行分类决策。深度学习模型的许多要素都具有这种特征学习的特性,这正是它们的优势所在。)
为何需要高性能计算
回到为什么我们需要HPC的问题。将机器学习、深度学习以及相关的数据方面和模型性能准确性放在一张幻灯片上考虑。这是我曾在德国联邦议院被要求向政治家们解释的内容:用一张幻灯片说明一切。
数据量是机器学习和深度学习的关键要素。大数据越多,进行深度学习的可能性就越大。但你会遇到串行计算的限制。当你使用R、Scikit-learn、Weka、Matlab/Octave等传统统计计算工具时,随着数据量越来越大,你的小型笔记本电脑将成为主要瓶颈。因此,你需要我们称之为高性能计算和云计算的东西。
即使是小型神经网络,也能受益于当今HPC中大量的GPU。但当你真正转向中型和大型深度学习网络、大型Transformer模型、大型语言模型时,其训练时间将非常可观,这已无法在笔记本电脑上以合理的方式完成。因此,高性能计算变得必不可少。我们将特别说明GPU为何如此成功。
当然,重要的是要理解,当数据集足够大时,深度学习网络在准确性和性能上已经超越了以前的计算机视觉算法和传统学习模型。但如果数据量仍然很小,随机森林和支持向量机可能仍然优于深度学习。因此,幻灯片上所示的排序在数据不足的初期并不成立,因为深度学习和HPC确实需要大量数据。
高性能计算与高吞吐量计算的区别
还需要区分高性能计算和高吞吐量计算,一些同学可能已经从云课程中了解。高吞吐量计算更侧重于任务吞吐量,而这里我们谈论的是大型、紧密互连的机器。这些机器成本高昂,因为互连电缆和GPU是目前最昂贵的部分。
这里还提到了FLOPS(每秒浮点运算次数)的概念,用于衡量计算速度。像Juwels这样的超级计算机是过去十年间出现的尖端系统之一,拥有数百个计算节点、大量CPU和GPU。
课程安排与评估方式
在结束前,简要说明一下Canvas工具、办公时间等。请将办公时间等请求发送给我的助教(邮件地址见幻灯片)。除最终成绩通过UGLA系统发布外,其他所有事项基本上都通过Canvas进行。
课程组织方面,有三项作业,占总成绩的40%。此外,还有一个课程项目,占总成绩的10%到50%(注:原文此处表述似有矛盾,按后续内容,项目占比较大)。作业已经在开始时提到,实践讲座将与之密切相关。
对于课程项目,重要的是组成2到3人的小组,并将信息发送给我和我的助教。课程项目将占总成绩的50%(或主要部分),因此需要在四月份提交报告,并从二月份开始准备。但中期报告是必要的,它将额外贡献10%的分数。尽早开始很有好处,因为许多学生总是到最后才匆忙开始最终项目,我们希望尽量避免这种情况。
课程期间还会有特邀讲座。幻灯片上的黄色区块标明了每个部分的关键要点,用于问答环节。同样,Canvas也可用于提问。再次强调,作业提交和评分都将在Canvas上进行。
额外资源与结语
最后,我想指出,你可以在YouTube上找到以往课程的录像。如果你有兴趣,可以从今天的开始看起。当然,课程内容每年会根据当前重点(例如超参数优化)有所调整,但旧的讲座也在那里。新的高性能计算课程录像也将为你提供。
我们在社交媒体上也非常活跃,这对于获取项目资助很重要。如果你想与我们的社区互动,可以在LinkedIn等平台上找到我们。
最后一个常见问题:相关的参考文献和书目通常会在每讲中提供。例如,这张幻灯片上列出的书目,如果你想了解更多,可以查阅像《Gka》这样的书籍,它可能是一本有趣的参考书。
我们在YouTube上的课程多年来获得了来自冰岛大学以外广大YouTube社区的极好反馈。在每堂课中,我都会提供一个视频供你在家观看。今天作为课程序言,我鼓励你在开始下一部分关于详细课程大纲和内容的讲解(与Raza一起)之前,先观看这个来自Prajwal的YouTube视频。

本节课中,我们一起了解了高性能计算课程的整体框架、动机、涵盖的核心主题(从并行编程基础到CFD与AI的融合),以及HPC在现代科学计算与人工智能中的关键作用。我们还介绍了课程的组织方式、评估方法以及相关的学习资源。希望这为你接下来的学习奠定了清晰的基础。

浙公网安备 33010602011771号