# mindscience仓贡献指南 ## **如何成为mindscience仓的贡献者** - 在AtomGit上输入mindscience的主仓网站:https://atomgit.com/mindspore-lab/mindscience - 通过右上角的Star按钮成为mindscience仓的贡献者 - 通过右上角的Fork按钮Fork一个mindscience主仓,为后续贡献代码做准备
star
fork
### **一、提交合并请求,为mindscience仓贡献自己的力量** - 本地修改的代码如果需要合并到主仓,可以通过曾经Fork过mindscience的远端仓,进行新建代码合并请求操作
new pr
- 点击新建合并请求后,需要进行源分支名的选择,目标分支名的选择,标题输入,以及简要说明修改点等操作(**注意:合并标题格式为[SPONG]+内容**) - 在新建合并请求的右下角需进行关联Issue操作,每个合并请求的合入都要有对应的Issue,如果没有相关的Issue,可以自行创建,请记得关联完Issue后将(合并后关闭提到的Issue)前面勾勾取消,然后点击创建合并请求操作
pr_introduction
pr_introduction
- 关联Issue处如果没有可选择的Issue关联,可以在主仓新建一个Issue,如果有则直接忽略此步。在主仓中点击新建Issue,根据合并请求的类型选择对应Issue类型,输入标题后,点击创建即可,这样在新建合并请求的关联Issue操作中就可以选择刚刚创建的Issue(**注意:Issue标题格式为[SPONGE]+内容**)
new_issue
issue_introduction
issue_introduction
- 新用户如果未注册过CLA,新建的合并请求会打上(mindspore-cla/no)标签,需要通过i-robot给出的链接注册,注册完成后在评论区输入(/check-cla)重新校验,已注册用户请忽略此步
cla
- 合并请求创建完成后,需要在评论区输入(/retest)启动门禁任务,进行静态检查以及冒烟用例测试,确保合入的代码不会影响主仓上面已有功能。如果全部通过会出现SUCCESS字样,即可以找相关审核人员合入代码;反之,如果出现FAILURE字样,需要点进失败链接,进行相关报错修复,修复完成后重新执行(/retest)操作,直至静态检查和冒烟用例全部通过才可合入
retest
retest
retest
### **二、新增测试用例,看护代码功能** - 对于贡献者来说,如果需要新增门禁冒烟测试用例来维护自己代码功能,可以在代码目录的mindscience/tests/st下,新增测试用例代码,这样可以保证其他人合入代码时不会影响自己代码功能(**注意:测试用例运行时间必须尽量短,受设备资源限制,太久的用例不适合作为门禁用例看护**) - 系统级测试用例,此阶段的用例是在whl包安装完成后启动,因此可以调用whl包中的任何函数,需要注意,系统级测试用例中需要添加(import pytest),并且在函数入口处新增pytest的标识,该标识可以使门禁任务识别到函数入口
st
- 测试用例代码完成后,需按照第一点进行代码上库操作,上库之后,新增的用例便会在门禁中体现 ## **本地代码操作** - Git 各平台安装包下载地址为:http://git-scm.com/downloads 安装完成后 - windows系统:可以在任意目录下直接右键Git Bush Here进行git相关操作
windows_git_bash_here
Windows_interface
- Linux系统: 通过终端进入所要存放代码的目录下直接进行git操作
linux_git_bash
### **一、信息配置** - 配置用户信息可以使你提交代码的commit下拥有你的账户信息——用于代码贡献量的统计 ```bash git config --global user.name "abc" git config --global user.email abc@tbs.com ```
git_user_config
### **二、拉取目标仓代码** - 代码提交流程为:本地代码---->push到远端自己仓_分支(Fork的仓即可以作为自己的远端仓)---->提起PR请求合入主仓_分支 - 采用git clone + 代码仓链接方式拉取代码,代码仓路径在AtomGot仓代码目录的右上角,有个Clone按钮,点击后可以复制HTTPS或者SSH链接,即为git clone所需的代码仓路径链接 ```bash git clone 代码仓链接 git clone -b 分支名 代码仓链接 例如:git clone https://atomgit.com/mindspore-lab/mindscience.git ``` - 代码拉取完成后,进入代码目录内部便可以使用git操作( **注意:拉取代码时的目录是无法使用类似git pull/push/remote/log等命令,必须进入其下一级目录才可以使用git pull/push等命令** )
git_clone
### **三、查看仓及分支信息** - 一般代码操作都是针对仓中的某个分支进行,因此在git下面主要的操作都有两个参数:仓+分支,如果不加分支名,默认对master分支进行操作 - 仓信息查询以及添加: - a、在代码目录中使用git remote -v就可以看到添加的远端仓地址和名字,后续操作仓便可以直接使用名字代替 - b、如果有多个仓需要操作,可以使用git remote add + 仓名(例如master) + 代码仓路径便可以添加新的仓 ```bash git remote -v git remote add 仓名 仓地址 例如:git remote add master https://atomgit.com/xxxxxxx/mindscience.git ```
remote
分支信息查询 - a、使用git branch -a可以看到clone代码仓的分支名字以及本地分支名 - b、如果还想继续从远端拉分支可以使用git fetch 仓名 远端分支名:本地分支名,这样会将远端对应分支名下面的代码拉到本地命名分支下 - c、既然有了新的分支,那我们如何切换呢?可以使用git checkout + 本地分支名将代码切换为相应分支下面的代码(注意:当所在分支有修改点未保存时切换会报错,因此需要先将修改点保存或者放弃修改点再进行分支切换操作 ```bash git branch -a git fetch 远端仓名 远端分支名:本地分支名 git checkout 本地分支名 ```
branch
### **四、修改代码后提交commit以及多个commit合并** - 当修改代码完成后,可以通过git status查看当前修改的文件数量和路径,如果所有修改点都需要保存,可以直接使用git add .操作,如果仅仅想保存部分修改文件,可以git add 文件名路径逐一保存,如果想回退某个文件的修改点,可以使用git checkout 文件名路径逐一回退,上述操作完成后,可以使用git commit -s生成commit id以及填写相应的commit内容,填写完成后保存,这样通过git log操作就能看到生成的commit信息 ```bash git status git add . git add 文件名路径 git checkout -- 文件名路径 git commit -s git log git rebase -i commit_id ```
add
commit_s
- 合并commit操作:有些时候可能由于修改点和时间原因,保存了多个commit,但是上库只允许有一个commit,怎么办呢?这个时候可以使用git rebase -i + 需要合并的所有commit_id之前的一个id号,然后将多余commit id前面的pick标识修改成f,再进行保存,这样多个commit_id就会自动合并到标识为pick的commit_id上
rebase_commit
rebase_i
commit_result
### **五、更新本地代码,同步仓代码——冲突解决** - 本地代码想同步仓代码时,如果本地没有修改也没有新增commit_id的话,此时直接用git pull 仓名 仓分支名即可 - 如果本地代码有修改,并且保存了commit,此时想要同步主仓并且保留修改点时建议使用git pull --rebase + 仓名 + 仓分支名,这样同步代码的好处是:先将本地未修改前的代码同步仓上代码再自动加入本地的修改点 ```bash 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_id"标识下面的代码为本地修改 5、此时需要识别是保留主仓修改,还是保留本地修改,亦或是合并两者的修改点 6、所有冲突文件以及冲突点都修改完成后,使用git add .保存 7、使用git rebase --continue继续同步下一个commit,如果在同步仓代码前已经把本地修改的commit合并成一个的话,那么这里只需要执行一次命令即可 ```bash git status git add . git rebase --continue git rebase --abort放弃同步 ``` 例如:现在存在A用户和B用户,同时clone了主仓的代码 1、A用户修改了部分文件并且提交到主仓 此时如果B用户只是clone过主仓代码但是没有任何修改点,没有添加任何commit,可直接使用git pull操作
A_push_master
B_pull_master
2、A用户修改了部分文件并且提交主仓 此时B用户本地也修改了同一个文件,并且提交PR时显示有冲突,那么此时需要使用git pull --rebase 仓 分支名进行手动解决冲突后,git add .保存修改,并使用git rebase --continue继续操作
pull_conflict
conflict
### **六、将本地修改代码推到远端仓,提起向主仓合并请求的PR** - 可以通过git push操作将本地代码推到远端仓,远端分支名如果省略则默认提交到master ```bash git push 远端仓名 本地分支名:远端分支名 git push 远端仓名 本地分支名:远端分支名 -f ``` ### **七、附加** - 补丁的使用,修改的代码可以通过format-patch的命令保存为.patch文件,该文件可以任意传输,别的用户只要使用git apply操作便可以把修改点作为补丁打到自己的代码中 ```bash git format-patch -i commit_id git apply --reject xxxxx.patch ``` - Cherry-pick操作:将指定的commit修改应用到其他分支上 ```bash git cherry-pick commit_id ``` - 修改点添加完成后,使用git commit --amend可以不生成新的commit信息,修改点直接合入之前commit_id上 ```bash git commit --amend ``` - 强制回退到某个commit_id节点 ```bash git reset --hard commit_id ``` ## 代码开发合入CheckList 本文档介绍如何向MindFlow合入代码,包括合入前需要准备的文件、数据,合入步骤以及需要注意的事项,帮助贡献者更高效地进行代码合入。 如果缺少调试代码的硬件环境,可以参考[启智社区云脑使用指南](https://download-mindspore.osinfra.cn/mindscience/mindflow/tutorials/%E5%90%AF%E6%99%BA%E6%8C%87%E5%8D%97.pdf), [NPU使用录屏](https://download-mindspore.osinfra.cn/mindscience/mindflow/tutorials/npu%E4%BD%BF%E7%94%A8.MP4), [GPU使用录屏](https://download-mindspore.osinfra.cn/mindscience/mindflow/tutorials/gpu%E4%BD%BF%E7%94%A8.MP4)。 ### 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`](./applications/physics_driven/burgers)为例,代码目录分成以下结构: ```shell . ├── 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`文件介绍具体的案例代码,所有案例具有相同的入口,在命令行里通过指定参数来确定运行的具体案例,文件格式如下: ```shell . ├──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 ``` 外层训练/测试文件调用的方式如下: ```python ... 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为模型训练的入口,格式如下: ```python 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"目录下,配置中的路径参数都是根目录的相对路径。参数命名规范统一格式,格式如下: ```yaml 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文件,一般包含以下部分: ```md # 标题 ## 概述 简单介绍一下案例的背景、方法、数据集、效果等。 ## 快速开始 为用户提供快速运行脚本的方法,一般提供脚本调用和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](https://atomgit.com/mindspore-lab/mindscience/blob/master/MindFlow/applications/cfd/acoustic/acoustic.ipynb)。 将主要代码模块从训练脚本中抽出,有序分块放入Jupyter Notebook文件。Jupyter Notebook一般包含`概述`、`问题背景`、`技术路径`、`依赖导入`、`数据集制作`、`模型搭建`、`模型训练`、`结果展示`等部分。在每个部分,应当对代码重要内容进行说明,保证按照说明执行代码块能正常运行。