代码
【MindSpore易点通】静态LossScale和动态LossScale的区别

【MindSpore易点通】静态LossScale和动态LossScale的区别

【MindSpore易点通】静态LossScale和动态LossScale的区别

背景信息

在混合精度中,使用float16类型来替代float32类型存储数据,从而达到减少内存和提高计算速度的效果。但是由于float16类型要比float32类型表示的范围小很多,所以当某些参数(比如说梯度)在训练过程中变得很小时,就会发生数据下溢的情况,进而影响网络精度。而loss scale正是为了解决float16类型数据下溢问题的,LossScale的主要思想是在计算loss时,将loss扩大一定的倍数,由于链式法则的存在,梯度也会相应扩大,然后在优化器更新权重时再缩小相应的倍数,从而避免了数据下溢的情况又不影响计算结果;而LossScale又可分为静态LossScale与动态LossScale。

MindSpore中提供了两种Loss Scale的方式,分别是FixedLossScaleManager和DynamicLossScaleManager,需要和Model配合使用。在使用Model构建模型时,可配置混合精度策略amp_level和Loss Scale方式loss_scale_manager。

1、静态LossScale

LossScale在训练过程中使用固定scale的值,且在训练过程中不会改变scale的值,scale的值由入参loss_scale控制,可以由用户指定,不指定则取默认值。

静态LossScale算子的另一个参数是drop_overflow_update,用来控制发生溢出时是否更新参数。

一般情况下LossScale功能不需要和优化器配合使用,但如果drop_overflow_update为False,那么优化器需设置loss_scale的值,且loss_scale的值要与静态LossScale算子相同。

FixedLossScaleManager具体用法如下:

1.import必要的库,并声明使用图模式下执行。

import numpy as npimport mindspore as msimport mindspore.nn as nnfrom mindspore.nn import Accuracyfrom mindspore.common.initializer import Normalfrom mindspore import dataset as ds

#ms.set_seed(0)ms.set_context(mode=ms.GRAPH_MODE, device_target="GPU")

2.定义LeNet5网络模型,任何网络模型都可以使用Loss Scale机制。

class LeNet5(nn.Cell):

    """        Lenet network

        Args:            num_class (int): Number of classes. Default: 10.            num_channel (int): Number of channels. Default: 1.

        Returns:            Tensor, output tensor    """

    def __init__(self, num_class=10, num_channel=1):

        super(LeNet5, self).__init__()

        self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid')

        self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid')

        self.fc1 = nn.Dense(16 * 5 * 5, 120, weight_init=Normal(0.02))

        self.fc2 = nn.Dense(120, 84, weight_init=Normal(0.02))

        self.fc3 = nn.Dense(84, num_class, weight_init=Normal(0.02))

        self.relu = nn.ReLU()

        self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2)

        self.flatten = nn.Flatten()



    def construct(self, x):

        x = self.max_pool2d(self.relu(self.conv1(x)))

        x = self.max_pool2d(self.relu(self.conv2(x)))

        x = self.flatten(x)

        x = self.relu(self.fc1(x))

        x = self.relu(self.fc2(x))

        x = self.fc3(x)

        return x

3.定义数据集和训练流程中常用的接口。

# create datasetdef get_data(num, img_size=(1, 32, 32), num_classes=10, is_onehot=True):

    for _ in range(num):

        img = np.random.randn(*img_size)

        target = np.random.randint(0, num_classes)

        target_ret = np.array([target]).astype(np.float32)

        if is_onehot:

            target_onehot = np.zeros(shape=(num_classes,))

            target_onehot[target] = 1

            target_ret = target_onehot.astype(np.float32)

        yield img.astype(np.float32), target_ret

def create_dataset(num_data=1024, batch_size=32, repeat_size=1):

    input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['data', 'label'])

    input_data = input_data.batch(batch_size, drop_remainder=True)

    input_data = input_data.repeat(repeat_size)

    return input_data

ds_train = create_dataset()

# Initialize networknetwork = LeNet5(10)

# Define Loss and Optimizernet_loss = nn.SoftmaxCrossEntropyWithLogits(reduction="mean")

4.真正使用Loss Scale的API接口,作用于优化器和模型中。

# Define Loss Scale, optimizer and model

#1) Drop the parameter update if there is an overflow

loss_scale_manager = ms.FixedLossScaleManager()

net_opt = nn.Momentum(network.trainable_params(), learning_rate=0.01, momentum=0.9)

model = ms.Model(network, net_loss, net_opt, metrics={"Accuracy": Accuracy()}, amp_level="O0", loss_scale_manager=loss_scale_manager)



#2) Execute parameter update even if overflow occurs

loss_scale = 1024.0

loss_scale_manager = ms.FixedLossScaleManager(loss_scale, False)

net_opt = nn.Momentum(network.trainable_params(), learning_rate=0.01, momentum=0.9, loss_scale=loss_scale)

model = ms.Model(network, net_loss, net_opt, metrics={"Accuracy": Accuracy()}, amp_level="O0", loss_scale_manager=loss_scale_manager)



# Run training

model.train(epoch=10, train_dataset=ds_train, callbacks=[ms.LossMonitor()])



TotalTime = 0.639526, [19]

[parse]: 0.00137553

[symbol_resolve]: 0.0630952, [1]

    [Cycle 1]: 0.0626392, [1]

        [resolve]: 0.0626309

[combine_like_graphs]: 0.00123225

[inference_opt_prepare]: 0.000407694

[elininate_unused_parameter]: 0.000182617

[abstract_specialize]: 0.0623443

[auto_monad]: 0.00156646

[inline]: 1.93249e-06

[py_pre_ad]: 9.71369e-07

[pipeline_split]: 2.88989e-06

[optimize]: 0.477379, [14]

    [simplify_data_structures]: 0.00029841

    [opt_a]: 0.473795, [3]

        [Cycle 1]: 0.450451, [25]

            [expand_dump_flag]: 1.45491e-05

            [switch_simplify]: 0.000713205

            [a_1]: 0.00994622

            [recompute_prepare]: 8.40612e-05

            [updatestate_depend_eliminate]: 0.000666355

            [updatestate_assign_eliminate]: 6.59283e-05

            [updatestate_loads_eliminate]: 0.000232871

            [parameter_eliminate]: 2.64868e-06

...

 0.16% :     0.000156s :      1: opt.transforms.opt_after_cconv

 0.04% :     0.000044s :      1: opt.transforms.opt_b

 0.24% :     0.000240s :      1: opt.transforms.opt_trans_graph

 0.02% :     0.000018s :      1: opt.transforms.stop_gradient_special_op

epoch: 1 step: 32, loss is 2.301311731338501

epoch: 2 step: 32, loss is 2.2960867881774902

epoch: 3 step: 32, loss is 2.292977809906006

epoch: 4 step: 32, loss is 2.2935791015625

epoch: 5 step: 32, loss is 2.3079633712768555

epoch: 6 step: 32, loss is 2.3191769123077393

epoch: 7 step: 32, loss is 2.3069581985473633

epoch: 8 step: 32, loss is 2.2944517135620117

epoch: 9 step: 32, loss is 2.3150291442871094

epoch: 10 step: 32, loss is 2.3247132301330566

2、动态LossScale

LossScale在训练过程中可以动态改变scale值的大小,在没有发生溢出(此处为上溢出)的情况下,要尽可能保持较大的scale的值。如果发生了溢出(上溢出),将缩小scale的值。

名词解释:

**上溢出:**数值超出最大表示范围。如表示区间为0~65504,那么当进行50000 + 50000的操作时,则会发生上溢出,输出结果可能是65500。

**下溢出:**数值小于最小表示精度。如最小表示精度为6e-8,那么当进行8e-8 / 10的操作时,则会发生下溢出,输出结果可能是0。

DynamicLossScaleManager会首先将scale设置为一个初始值,该值由入参init_loss_scale控制。

在训练过程中,如果不发生溢出,在更新scale_window次参数后,会尝试扩大scale的值,如果发生了溢出,则跳过参数更新,并缩小scale的值,入参scale_factor是控制扩大或缩小的步数,scale_window控制没有发生溢出时,最大的连续更新步数。

具体用法如下,仅需将FixedLossScaleManager样例中定义LossScale,优化器和模型部分的代码改成如下代码:

# Define Loss Scale, optimizer and model

scale_factor = 4

scale_window = 3000

loss_scale_manager = ms.DynamicLossScaleManager(scale_factor, scale_window)

net_opt = nn.Momentum(network.trainable_params(), learning_rate=0.01, momentum=0.9)

model = ms.Model(network, net_loss, net_opt, metrics={"Accuracy": Accuracy()}, amp_level="O0", loss_scale_manager=loss_scale_manager)