混合精度

    MindSpore混合精度典型的计算流程如下图所示:

    • 参数以FP32存储;

    • 正向计算过程中,遇到FP16算子,需要把算子输入和参数从FP32 cast成FP16进行计算;

    • 将Loss层设置为FP32进行计算;

    • 反向计算过程中,首先乘以Loss Scale值,避免反向梯度过小而产生下溢;

    • 除以Loss scale值,还原被放大的梯度;

    • 判断梯度是否存在溢出,如果溢出则跳过更新,否则优化器以FP32对原始参数进行更新。

    本文通过自动混合精度和手动混合精度的样例来讲解计算流程。

    使用自动混合精度,需要调用相应的接口,将待训练网络和优化器作为输入传进去;该接口会将整张网络的算子转换成FP16算子(除BatchNorm算子和Loss涉及到的算子外)。另外要注意:使用混合精度后,一般要用上Loss Scale,避免数值计算溢出。

    具体的实现步骤为:

    • 引入MindSpore的混合精度的接口amp;

    • 使用amp.build_train_network()接口封装网络模型和优化器,在该步骤中MindSpore会将有需要的算子自动进行类型转换。

    代码样例如下:

    MindSpore还支持手动混合精度。假定在网络中只有一个Dense Layer要用FP32计算,其他Layer都用FP16计算。混合精度配置以Cell为粒度,Cell默认是FP32类型。

    以下是一个手动混合精度的实现步骤:

    • 定义网络: 该步骤与自动混合精度中的步骤2类似;注意:在LeNet中的fc3算子,需要手动配置成FP32;

    • 配置混合精度: LeNet通过net.add_flags_recursive(fp16=True),把该Cell及其子Cell中所有的算子都配置成FP16;

    代码样例如下:

    1. Copy# Define network
    2. def __init__(self):
    3. super(LeNet5, self).__init__()
    4. self.conv1 = nn.Conv2d(1, 6, 5, pad_mode='valid')
    5. self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid')
    6. self.fc1 = nn.Dense(16 * 5 * 5, 120)
    7. self.fc2 = nn.Dense(120, 84)
    8. self.fc3 = nn.Dense(84, 10).add_flags_recursive(fp32=True)
    9. self.relu = nn.ReLU()
    10. self.max_pool2d = nn.MaxPool2d(kernel_size=2)
    11. self.flatten = P.Flatten()
    12.  
    13. def construct(self, x):
    14. x = self.max_pool2d(self.relu(self.conv1(x)))
    15. x = self.max_pool2d(self.relu(self.conv2(x)))
    16. x = self.flatten(x)
    17. x = self.relu(self.fc1(x))
    18. x = self.relu(self.fc2(x))
    19. x = self.fc3(x)
    20.  
    21. # Initialize network and set mixing precision
    22. net = LeNet5()
    23. net.add_flags_recursive(fp16=True)
    24.  
    25. # Define training data, label and sens
    26. predict = Tensor(np.ones([1, 1, 32, 32]).astype(np.float32) * 0.01)
    27. label = Tensor(np.zeros([1, 10]).astype(np.float32))
    28. scaling_sens = Tensor(np.full((1), 1.0), dtype=mstype.float32)
    29.  
    30. # Define Loss and Optimizer
    31. net.set_train()
    32. loss = MSELoss()
    33. optimizer = Momentum(params=net.trainable_params(), learning_rate=0.1, momentum=0.9)
    34. net_with_loss = WithLossCell(net, loss)
    35. train_network = TrainOneStepWithLossScaleCell(net_with_loss, optimizer)
    36.  
    37. # Run training