MindTorch贡献指南

1. 环境安装

测试环境请选择PyTorch 1.12.0版本和MindSpore最新版本。

1.1 安装MindSpore

我们选择使用最新版本的MindSpore开发, 安装版本和指令请参考MindSpore安装

1.2 安装PyTorch

安装PyTorch是用于和开发的MindTorch接口进行结果和功能比对,选择PyTorch1.12.0,安装可以参考PyTorch安装

例如需要安装CPU版本PyTorch1.12.0可以用如下指令:

pip install torch==1.12.0+cpu torchvision==0.13.0+cpu torchaudio==0.12.0 --extra-index-url https://download.pytorch.org/whl/cpu

2. 开发指南

2.1 目录结构说明

接口代码开发主要涉及到nn的modules里面文件及nn.functional.py和functional.py;分别对应开发模型构建组件接口代码(对应torch.nn.Conv2d这类),nn函数接口代码(对应类似torch.nn.functional.conv2d接口)以及常用函数接口代码(对应类似torch.abs接口)。

  • ├── nn

  • │ ├── modules 模型构建组件层

  • │ │ ├── __init__.py

  • │ │ ├── activation.py 激活函数层接口

  • │ │ └── batchnorm.py BN层接口

  • │ │ └── …

  • │ └── functionanl.py nn.functional接口

  • ├── optim

  • │ └── init

  • ├── utils

  • └── functional.py 函数接口

代码测试用例编写目录结构如下,在开发完成接口后需要在对应的文件夹内创建测试代码

  • |testing

  • ├── ut

  • │ ├── pytorch

  • │ │ ├── data

  • │ │ ├── function 函数接口测试用例位置

  • │ │ └── nn 模型构建组件类测试用例位置

  • │ │ └── …

2.2 深度学习框架前端层次结构说明

深度学习框架前端层次结构可以描述为如下表示。低阶接口包含Tensor、nn.functional.xx等;中阶接口是对低阶接口的封装,调用低阶接口实现用户友好的API;高阶接口又可以是中低阶接口的封装。因此我们开发中阶接口Layers(nn.Conv2d、nn.MaxPool2d)相关的API时,需要先开发对应的低级接口如nn.functional.xx(nn.functional.conv2d、nn.functional.max_pool2d)。 深度学习框架层次结构

2.3 模型构建组件开发示例

假设我们需要构建一个和PyTorch线性层(Linear)对应的接口。首先应该开发Linear所调用的低阶接口nn.functional.linear 其中低阶接口linear可以使用MindSpore的MatMul和bias_add算子,示例如下:

def linear(input, weight, bias=None):
    input = cast_to_ms_tensor(input)
    output = ms.ops.MatMul(transpose_b=True)(input, weight)
    if bias is not None:
        output = ms.ops.bias_add(output, bias)
    output = cast_to_adapter_tensor(output)
    return output

需要注意的是开发函数接口调用MindSpore算子时需要将tensor调用cast_to_ms_tensor转换成MindSpore所需tensor,输出调用cast_to_adapter_tensor转换成MindTorch所需tensor。

接下来就可以使用nn.functional.linear提供的接口来开发更高阶的神经网络组件接口Linear了,示例如下:

class Linear(Module):
    def __init__(self, in_features, out_features, bias=True, device=None, dtype=None):
        super(Linear, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.has_bias = False
        self.bias = None
        self.weight = Parameter(empty((self.out_features, self.in_features)), requires_grad=True)
        if bias:
            self.bias = Parameter(empty(self.out_features), requires_grad=True)
            self.has_bias = True
        self.reset_parameters()
        unsupported_attr(device)
        unsupported_attr(dtype)


    def reset_parameters(self):
        init.kaiming_uniform_(self.weight, a=math.sqrt(5))
        if self.has_bias:
            fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
            bound = 1 / math.sqrt(fan_in) if fan_in > 0 else 0
            init.uniform_(self.bias, -bound, bound)

    def forward(self, input):
        input = cast_to_ms_tensor(input)
        x = linear(input, self.weight, self.bias)
        return cast_to_adapter_tensor(x)

    def extra_repr(self):
        return 'in_features={}, out_features={}, bias={}'.format(
            self.in_features, self.out_features, self.has_bias is not None
        )

在__init__方法里进行属性的定义和参数初始化,在reset_parameters里进行参数的初始化方法定义,在forward里实现前向计算过程。extra_repr里返回属性信息。

此时Linear Layer和其nn.functioal接口都已经开发完成,需要编写测试用例,在testing/ut/pytorch/nn下新建测试文件test_linear.py。我们先简单测试一下训练参数属性能否和torch一样改变初始化方法。也需要测试相同输入和初始化方法输出是否和torch一样,具体可以参考test_conv.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from mindtorch.torch.nn import Module, Linear, Identity, Bilinear
from mindtorch.torch import tensor
import numpy as np
import mindspore as ms

from ...utils import set_mode_by_env_config
set_mode_by_env_config()

def test_linear_model():
    class LinearModel(Module):
        def __init__(self):
            super(LinearModel, self).__init__()
            self.line1 = Linear(in_features=32, out_features=64)
            self.line2 = Linear(in_features=64, out_features=128, bias=False)
            self.line3 = Linear(in_features=128, out_features=10)

        def forward(self, inputs):
            x = self.line1(inputs)
            x = self.line2(x)
            x = self.line3(x)
            return x

    model = LinearModel()
    model.train()

    def weight_init(m):
        if isinstance(m, Linear):
            m.weight.data.normal_(0, 0.01)
            if m.has_bias:
                m.bias.data.zero_()

    model.apply(weight_init)

    inputs = tensor(np.ones(shape=(5, 32)))
    output = model(inputs)
    assert output.shape == (5, 10)

test_linear_model()

2.4代码提交到仓库

测试用例编写完成并通过CPU/Ascend平台功能测试,此时接口就开发完成了。下一步可以提交代码到代码仓库。 首先从代码仓库clone代码到本地机器:

git clone https://openi.pcl.ac.cn/OpenI/MSAdapter.git

clone代码后,本地开发,我们新建一个分支进行开发:

git checkout -b {新分支名称}

一顿开发完成后,我们正式推送更新分支:

git add .
git status  # 查看更新了什么代码,避免提交错了
git commit -m "你的commit标题,例如开发了Linear起名为:Add Linear Layer"
git push origin {新分支名称}

接下来就在启智平台进行操作 点击: 合并请求–>创建合并请求 继续检查一下开发内容是否正确,没问题后确认创建合并请求,等待审核入库。