mindscience仓贡献指南
如何成为mindscience仓的贡献者
在AtomGit上输入mindscience的主仓网站:https://atomgit.com/mindspore-lab/mindscience
通过右上角的Star按钮成为mindscience仓的贡献者
通过右上角的Fork按钮Fork一个mindscience主仓,为后续贡献代码做准备
一、提交合并请求,为mindscience仓贡献自己的力量
本地修改的代码如果需要合并到主仓,可以通过曾经Fork过mindscience的远端仓,进行新建代码合并请求操作
点击新建合并请求后,需要进行源分支名的选择,目标分支名的选择,标题输入,以及简要说明修改点等操作(注意:合并标题格式为[SPONG]+内容)
在新建合并请求的右下角需进行关联Issue操作,每个合并请求的合入都要有对应的Issue,如果没有相关的Issue,可以自行创建,请记得关联完Issue后将(合并后关闭提到的Issue)前面勾勾取消,然后点击创建合并请求操作
关联Issue处如果没有可选择的Issue关联,可以在主仓新建一个Issue,如果有则直接忽略此步。在主仓中点击新建Issue,根据合并请求的类型选择对应Issue类型,输入标题后,点击创建即可,这样在新建合并请求的关联Issue操作中就可以选择刚刚创建的Issue(注意:Issue标题格式为[SPONGE]+内容)
新用户如果未注册过CLA,新建的合并请求会打上(mindspore-cla/no)标签,需要通过i-robot给出的链接注册,注册完成后在评论区输入(/check-cla)重新校验,已注册用户请忽略此步
合并请求创建完成后,需要在评论区输入(/retest)启动门禁任务,进行静态检查以及冒烟用例测试,确保合入的代码不会影响主仓上面已有功能。如果全部通过会出现SUCCESS字样,即可以找相关审核人员合入代码;反之,如果出现FAILURE字样,需要点进失败链接,进行相关报错修复,修复完成后重新执行(/retest)操作,直至静态检查和冒烟用例全部通过才可合入
二、新增测试用例,看护代码功能
对于贡献者来说,如果需要新增门禁冒烟测试用例来维护自己代码功能,可以在代码目录的mindscience/tests/st下,新增测试用例代码,这样可以保证其他人合入代码时不会影响自己代码功能(注意:测试用例运行时间必须尽量短,受设备资源限制,太久的用例不适合作为门禁用例看护)
系统级测试用例,此阶段的用例是在whl包安装完成后启动,因此可以调用whl包中的任何函数,需要注意,系统级测试用例中需要添加(import pytest),并且在函数入口处新增pytest的标识,该标识可以使门禁任务识别到函数入口
测试用例代码完成后,需按照第一点进行代码上库操作,上库之后,新增的用例便会在门禁中体现
本地代码操作
Git 各平台安装包下载地址为:http://git-scm.com/downloads
安装完成后
windows系统:可以在任意目录下直接右键Git Bush Here进行git相关操作
Linux系统: 通过终端进入所要存放代码的目录下直接进行git操作
一、信息配置
配置用户信息可以使你提交代码的commit下拥有你的账户信息——用于代码贡献量的统计
git config --global user.name "abc"
git config --global user.email abc@tbs.com
二、拉取目标仓代码
代码提交流程为:本地代码—->push到远端自己仓_分支(Fork的仓即可以作为自己的远端仓)—->提起PR请求合入主仓_分支
采用git clone + 代码仓链接方式拉取代码,代码仓路径在AtomGot仓代码目录的右上角,有个Clone按钮,点击后可以复制HTTPS或者SSH链接,即为git clone所需的代码仓路径链接
git clone 代码仓链接
git clone -b 分支名 代码仓链接
例如:git clone https://atomgit.com/mindspore-lab/mindscience.git
代码拉取完成后,进入代码目录内部便可以使用git操作( 注意:拉取代码时的目录是无法使用类似git pull/push/remote/log等命令,必须进入其下一级目录才可以使用git pull/push等命令 )
三、查看仓及分支信息
一般代码操作都是针对仓中的某个分支进行,因此在git下面主要的操作都有两个参数:仓+分支,如果不加分支名,默认对master分支进行操作
仓信息查询以及添加:
a、在代码目录中使用git remote -v就可以看到添加的远端仓地址和名字,后续操作仓便可以直接使用名字代替
b、如果有多个仓需要操作,可以使用git remote add + 仓名(例如master) + 代码仓路径便可以添加新的仓
git remote -v
git remote add 仓名 仓地址
例如:git remote add master https://atomgit.com/xxxxxxx/mindscience.git
分支信息查询
a、使用git branch -a可以看到clone代码仓的分支名字以及本地分支名
b、如果还想继续从远端拉分支可以使用git fetch 仓名 远端分支名:本地分支名,这样会将远端对应分支名下面的代码拉到本地命名分支下
c、既然有了新的分支,那我们如何切换呢?可以使用git checkout + 本地分支名将代码切换为相应分支下面的代码(注意:当所在分支有修改点未保存时切换会报错,因此需要先将修改点保存或者放弃修改点再进行分支切换操作
git branch -a
git fetch 远端仓名 远端分支名:本地分支名
git checkout 本地分支名
四、修改代码后提交commit以及多个commit合并
当修改代码完成后,可以通过git status查看当前修改的文件数量和路径,如果所有修改点都需要保存,可以直接使用git add .操作,如果仅仅想保存部分修改文件,可以git add 文件名路径逐一保存,如果想回退某个文件的修改点,可以使用git checkout 文件名路径逐一回退,上述操作完成后,可以使用git commit -s生成commit id以及填写相应的commit内容,填写完成后保存,这样通过git log操作就能看到生成的commit信息
git status
git add .
git add 文件名路径
git checkout -- 文件名路径
git commit -s
git log
git rebase -i commit_id
合并commit操作:有些时候可能由于修改点和时间原因,保存了多个commit,但是上库只允许有一个commit,怎么办呢?这个时候可以使用git rebase -i + 需要合并的所有commit_id之前的一个id号,然后将多余commit id前面的pick标识修改成f,再进行保存,这样多个commit_id就会自动合并到标识为pick的commit_id上
五、更新本地代码,同步仓代码——冲突解决
本地代码想同步仓代码时,如果本地没有修改也没有新增commit_id的话,此时直接用git pull 仓名 仓分支名即可
如果本地代码有修改,并且保存了commit,此时想要同步主仓并且保留修改点时建议使用git pull –rebase + 仓名 + 仓分支名,这样同步代码的好处是:先将本地未修改前的代码同步仓上代码再自动加入本地的修改点
git pull 仓名 仓分支名
git pull --rebase 仓名 仓分支名
由于主仓不止一个人在提交代码,当出现多人同时修改同一行时,如果前面人代码先合入主仓,此时更新本地代码操作会出现冲突,通过git pull –rebase 更新主仓时可以使用以下命令解决冲突(注意:在git pull –rebase时,如果本地修改存在多个commit,最好通过步骤四将多个commit id合并后再进行,否则会对每个修改的commit做一次rebase同步操作,例如本地有4个修改的commit,同步仓上代码时会首先对第一个修改的commit同步一次仓,如果有冲突需要解决后再git rebase –continue继续对第二个commit同步一次仓,这样相对繁琐,因此建议最好合并完本地commit后再进行同步仓代码)
冲突解决:
1、git status会显示主仓代码和自己代码同时修改的文件
2、进入该文件,可以搜索“<<<<<<<"这样的符号标识
3、"<<<<<<<commit"到”============“标识下面的代码即为主仓上面的代码
4、”============“到”>>>>>>>commit_id"标识下面的代码为本地修改
5、此时需要识别是保留主仓修改,还是保留本地修改,亦或是合并两者的修改点
6、所有冲突文件以及冲突点都修改完成后,使用git add .保存
7、使用git rebase –continue继续同步下一个commit,如果在同步仓代码前已经把本地修改的commit合并成一个的话,那么这里只需要执行一次命令即可
git status
git add .
git rebase --continue
git rebase --abort放弃同步
例如:现在存在A用户和B用户,同时clone了主仓的代码
1、A用户修改了部分文件并且提交到主仓 此时如果B用户只是clone过主仓代码但是没有任何修改点,没有添加任何commit,可直接使用git pull操作
2、A用户修改了部分文件并且提交主仓 此时B用户本地也修改了同一个文件,并且提交PR时显示有冲突,那么此时需要使用git pull –rebase 仓 分支名进行手动解决冲突后,git add .保存修改,并使用git rebase –continue继续操作
六、将本地修改代码推到远端仓,提起向主仓合并请求的PR
可以通过git push操作将本地代码推到远端仓,远端分支名如果省略则默认提交到master
git push 远端仓名 本地分支名:远端分支名
git push 远端仓名 本地分支名:远端分支名 -f
七、附加
补丁的使用,修改的代码可以通过format-patch的命令保存为.patch文件,该文件可以任意传输,别的用户只要使用git apply操作便可以把修改点作为补丁打到自己的代码中
git format-patch -i commit_id
git apply --reject xxxxx.patch
Cherry-pick操作:将指定的commit修改应用到其他分支上
git cherry-pick commit_id
修改点添加完成后,使用git commit –amend可以不生成新的commit信息,修改点直接合入之前commit_id上
git commit --amend
强制回退到某个commit_id节点
git reset --hard commit_id
代码开发合入CheckList
本文档介绍如何向MindFlow合入代码,包括合入前需要准备的文件、数据,合入步骤以及需要注意的事项,帮助贡献者更高效地进行代码合入。
如果缺少调试代码的硬件环境,可以参考启智社区云脑使用指南, NPU使用录屏, GPU使用录屏。
API代码
API代码主要指合入MindFlow/mindflow目录的代码,主要为案例提供高效、易用的公共API接口,因此API代码编写时需要注意以下几点:
1、考虑在多个案例上的可扩展性,避免'Hard Code',在维度、深度等参量上预留足够的入参,以供用户根据实际情况选择,注意非法入参的检查;
2、入参命名上,MindFlow追求尽量统一,因此新的API合入时,需要与原有API的入参尽量对齐,新的入参命名可与Commiter联系;
3、API的存放位置需根据MindFlow的套件架构决定,注意更新__init__.py文件和cmake/package.cmake文件;
4、API文档包含两部分,一个是代码注释部分,一个是mindscience/docs/api_python/mindflow和mindscience/docs/api_python_en/mindflow中的中英文文档;
5、API相关测试用例来进行维护,保证其随时可用,测试用例提交在mindscience/tests中,可根据具体用例修改,但运行时间不宜过长,结果检查合理;
案例目录格式
案例代码主要指合入MindFlow/applications目录的代码,需要根据研究范式,归入physics_driven、data_driven、data_mechanism_fusion、cfd几个目录中。
必须 Jupyter Notebook中英文:为用户提供逐行的代码实现方式,详细讲解案例的实现方式和运行结果。
必须
images:包含了README、notebook等文件里的所有图片。必须
src:为了保证训练代码的整洁性,可以抽取的函数和类可以统一放在src目录中,__init__.py一般为必须,dataset.py中包含数据集相关函数和类,model.py中包含模型相关函数和类,utils.py中包含工具函数和类,外部文件的调用统一从src导入。必须 参数文件:案例中具体参数的配置,一般采用yaml文件,为了方便查看,按照优化器、模型等进行分类。
必须 训练脚本:案例的训练和验证脚本,在训练时除特殊情况,必须有测试集进行验证;训练脚本中的代码应该尽量简洁,复杂的调用封装到后端函数里。
注意:类和函数中需要避免'Hard Code',变量名需要有实际含义;尽量避免使用'Magic Number',必要的需要在注释里说明;超过50行以上的代码可以考虑抽取出函数调用,减少重复代码;函数的功能尽可能单一,遵从'高内聚,低耦合'原则。
单个案例目录格式
单一的案例代码如PINNs求解Burgers为例,代码目录分成以下结构:
.
├── images
│ ├── background.png
│ └── result.png
├── src
│ ├── __init__.py
│ ├── dataset.py
│ ├── model.py
│ └── utils.py
├── configs
│ ├── fno1d.yaml
├── problem.ipynb
├── problem_CN.ipynb
├── burgers_cfg.yaml
├── eval.py
└── train.py
多个案例目录格式
有时,有多个案例会使用相同的模型和方法,使用不同的数据集,为了避免代码和文档的重复,src目录下统一存放所有案例公共的代码和每个案例自定义的代码,images目录统一存放图片文件,README.md文件在总体上介绍模型方法和所有的案例,problem.ipynb文件介绍具体的案例代码,所有案例具有相同的入口,在命令行里通过指定参数来确定运行的具体案例,文件格式如下:
.
├──images
│ ├──background.png
│ ├──result1.png
│ ├──result2.png
│ └──result3.png
├──src
│ ├──__init__.py
│ ├──dataset.py
│ ├──model.py
│ └──utils.py
├──configs
│ ├──fno1d.yaml
├──README.md
├──README_CN.md
├──problem.ipynb
├──problem_CN.ipynb
├──problem_cfg.yaml
├──eval.py
└──train.py
外层训练/测试文件调用的方式如下:
...
parser = argparse.ArgumentParser(description="Cae-Lstm")
parser.add_argument("--case", type=str, default='riemann', choices=['riemann', 'kh', 'sod'],
help="Which case to run")
...
args = parser.parse_args()
...
model = Model()
if args.case == 'riemann':
dataset = create_riemann_dataset()
elif args.case == 'kh':
dataset = create_kh_dataset()
else:
dataset = create_sod_dataset()
model.train(dataset)
...
训练文件格式
训练文件train.py为模型训练的入口,格式如下:
import os
import time
import argparse
import numpy as np
from mindspore import context, nn, Tensor, set_seed, ops, data_sink, jit, save_checkpoint
from mindspore import dtype as mstype
from mindflow import FNO1D, load_yaml_config, get_warmup_cosine_annealing_lr
from mindflow.pde import FlowWithLoss
from src import create_training_dataset, visual, calculate_l2_error
# 相关依赖导入,按照python官方库、第三方库、mindflow、src的顺序导入,导入mindflow时,精确到二级目录
set_seed(123456)
np.random.seed(123456)
# 设置随机数
def parse_args():
'''Parse input args'''
parser = argparse.ArgumentParser(description='Problem description')
parser.add_argument("--config_file_path", type=str, default="./config.yaml")
parser.add_argument("--device_target", type=str, default="GPU", choices=["GPU", "Ascend"],
help="The target device to run, support 'Ascend', 'GPU'")
parser.add_argument("--device_id", type=int, default=3, help="ID of the target device")
parser.add_argument("--mode", type=str, default="GRAPH", choices=["GRAPH", "PYNATIVE"],
help="Context mode, support 'GRAPH', 'PYNATIVE'")
parser.add_argument("--save_graphs", type=bool, default=False, choices=[True, False],
help="Whether to save intermediate compilation graphs")
parser.add_argument("--save_graphs_path", type=str, default="./graphs")
input_args = parser.parse_args()
return input_args
def train(input_args):
use_ascend = context.get_context(attr_key='device_target') == "Ascend"
# 读取训练配置
config = load_yaml_config(input_args.config_file_path)
# 创建训练集和测试集
train_dataset, test_dataset = create_training_dataset(data_params, shuffle=True)
# 初始化模型
model = Model(config)
problem = FlowWithLoss(model)
# 前向函数
def forward_fn(data, label):
...
grad_fn = ops.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=False)
# 训练的前向和反向过程
@jit
def train_step(data, label):
...
# 数据下沉
sink_process = data_sink(train_step, train_dataset, 1)
# 训练流程
for epoch in range(1, config["epochs"] + 1):
model.set_train()
train()
# 训练和验证函数,采用MindSpore函数式编程范式编写,注意打印内容尽量统一
print(f"epoch: {epoch} train loss: {step_train_loss} epoch time: {time.time() - time_beg:.2f}s")
# 验证
if epoch % config['eval_interval'] == 0:
model.set_train(False)
print("================================Start Evaluation================================")
eval()
print(f"epoch: {epoch} eval loss: {step_train_loss} epoch time: {time.time() - time_beg:.2f}s")
print("=================================End Evaluation=================================")
if epoch % config['save_ckpt_interval'] == 0:
save_checkpoint(model, 'my_model.ckpt')
if __name__ == '__main__':
print(f"pid: {os.getpid()}")
print(datetime.datetime.now())
# 读取脚本入参
args = parse_args()
context.set_context(mode=context.GRAPH_MODE if args.mode.upper().startswith("GRAPH") else context.PYNATIVE_MODE,
device_target=args.device_target,
device_id=args.device_id)
print(f"Running in {args.mode.upper()} mode, using device id: {args.device_id}.")
# context设置,由于Ascend和GPU使用的差异,需要使用use_ascend变量进行判断
start_time = time.time()
# 调用训练函数
train(args)
print("End-to-End total time: {}s".format(time.time() - start_time))
配置文件格式
参数按照模型、数据、优化器等类别分类,放在"./configs"目录下,配置中的路径参数都是根目录的相对路径。参数命名规范统一格式,格式如下:
model:
in_channels: 3
out_channels: 3
height: 192
width: 384
encoder_depth: 6
decoder_depth: 6
decoder_num_heads: 16
data:
train_dataset_path: "./dataset/test.npy"
test_dataset_path: "./dataset/train.npy"
grid_path: "./dataset/grid.npy"
batch_size: 32
optimizer:
epochs: 1000
lr: 0.0005
wave_level: 1
README文件格式
其中,总目录中的README对整体背景、技术路线、结果进行讲解,在每个案例中,可以分别在案例的角度描述,注意整体和局部的详略关系,避免重复描述和重复代码。
【必须】README.md和README_CN.md,中英文README文件,一般包含以下部分:
# 标题
## 概述
简单介绍一下案例的背景、方法、数据集、效果等。
## 快速开始
为用户提供快速运行脚本的方法,一般提供脚本调用和Jupyter Notebook两种方式。其中,脚本调用需要展示启动命令的入参含义
### 训练方式一:在命令行中调用`train.py`脚本
python train.py --config_file_path ./configs/burgers.yaml --mode GRAPH --device_target Ascend --device_id 0
其中,
`--config_file_path`表示参数文件的路径,默认值'./burgers_cfg.yaml';
`--mode`表示运行的模式,'GRAPH'表示静态图模式, 'PYNATIVE'表示动态图模式,默认值'GRAPH';
`--device_target`表示使用的计算平台类型,可以选择'Ascend'或'GPU',默认值'Ascend';
`--device_id`表示使用的计算卡编号,可按照实际情况填写,默认值0;
### 训练方式二:运行Jupyter Notebook
您可以使用中英文版本的Jupyter Notebook(附链接)逐行运行训练和验证代码。
## 结果展示
用1-2张图的方式展示模型推理的效果,最好为gif。
## 性能
如果案例涉及到GPU和Ascend双后端,则需要用表格的形式展示训练的主要性能指标进行对比。
| 参数 | NPU | GPU |
|:-------------------:|:------------------------:|:------------:|
| 硬件资源 | Ascend, 显存32G | NVIDIA V100, 显存32G |
| MindSpore版本 | >=2.0.0 | >=2.0.0 |
| 数据集 | [Burgers数据集](https://download.mindspore.cn/mindscience/mindflow/dataset/applications/physics_driven/burgers_pinns/) | [Burgers数据集](https://download.mindspore.cn/mindscience/mindflow/dataset/applications/physics_driven/burgers_pinns/) |
| 参数量 | 6e4 | 6e4 |
| 训练参数 | batch_size=8192, steps_per_epoch=1, epochs=15000 | batch_size=8192, steps_per_epoch=1, epochs=15000 |
| 测试参数 | batch_size=8192, steps=4 | batch_size=8192, steps=4 |
| 优化器 | Adam | Adam |
| 训练损失(MSE) | 0.001 | 0.0001 |
| 验证损失(RMSE) | 0.010 | 0.008 |
| 训练速度(ms/step) | 10 | 130 |
## 贡献者
AtomGit id: [id](开发者AtomGit个人空间的链接)
email: myemail@163.com
Jupyter Notebook文件格式
Jupyter Notebook文件格式可参考2D_steady_CN.ipynb。
将主要代码模块从训练脚本中抽出,有序分块放入Jupyter Notebook文件。Jupyter Notebook一般包含概述、问题背景、技术路径、依赖导入、数据集制作、模型搭建、模型训练、结果展示等部分。在每个部分,应当对代码重要内容进行说明,保证按照说明执行代码块能正常运行。