当前位置 博文首页 > DECHIN:MindSpore保存与加载模型

    DECHIN:MindSpore保存与加载模型

    作者:DECHIN 时间:2021-05-17 18:29

    本文主要从工程实现的角度测试了一下MindSpore的机器学习模型保存与加载的功能,通过这个功能,我们可以将自己训练好的机器学习模型发布出去供更多的人使用,我们也可以直接使用别人在更好的硬件体系上训练好的模型,或者应用于迁移学习。

    技术背景

    近几年在机器学习和传统搜索算法的结合中,逐渐发展出了一种Search To Optimization的思维,旨在通过构造一个特定的机器学习模型,来替代传统算法中的搜索过程,进而加速经典图论等问题的求解。那么这里面就涉及到一个非常关键的工程步骤:把机器学习中训练出来的模型保存成一个文件或者数据库,使得其他人可以重复的使用这个已经训练出来的模型。甚至是可以发布在云端,通过API接口进行调用。那么本文的内容就是介绍给予MindSpore的模型保存与加载,官方文档可以参考这个链接。

    保存模型

    这里我们使用的模型来自于这篇博客,是一个非常基础的线性神经网络模型,用于拟合一个给定的函数。因为这里我们是基于线性的模型,因此当我们需要去拟合一个更加高阶的函数的话,需要手动的处理,比如这里我们使用的平方函数。总结起来该模型可以抽象为:

    \[f(x)=ax^2+b \]

    而这里产生训练集时,我们是加了一个随机的噪声,也就是:

    \[D(x)=ax^2+b+noise \]

    完整的代码如下所示:

    # save_model.py
    
    from mindspore import context
    context.set_context(mode=context.GRAPH_MODE, device_target="GPU")
    import numpy as np
    from mindspore import dataset as ds
    from mindspore import nn, Tensor, Model
    import time
    from mindspore.train.callback import Callback, LossMonitor, ModelCheckpoint
    
    def get_data(num, a=2.0, b=3.0):
        for _ in range(num):
            x = np.random.uniform(-1.0, 1.0)
            noise = np.random.normal(0, 0.03)
            z = a * x ** 2 + b + noise
            # 返回数据的时候就返回数据的平方
            yield np.array([x**2]).astype(np.float32), np.array([z]).astype(np.float32)
    
    def create_dataset(num_data, batch_size=16, repeat_size=1):
        input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['x','z'])
        input_data = input_data.batch(batch_size)
        input_data = input_data.repeat(repeat_size)
        return input_data
    
    data_number = 1600 # 一共产生1600组数据
    batch_number = 16 # 分为16组分别进行优化
    repeat_number = 2 # 重复2次,可以取到更低的损失函数值
    
    ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)
    
    class LinearNet(nn.Cell):
        def __init__(self):
            super(LinearNet, self).__init__()
            self.fc = nn.Dense(1, 1, 0.02, 0.02)
    
        def construct(self, x):
            x = self.fc(x)
            return x
    
    net = LinearNet()
    model_params = net.trainable_params()
    print ('Param Shape is: {}'.format(len(model_params)))
    for net_param in net.trainable_params():
        print(net_param, net_param.asnumpy())
    net_loss = nn.loss.MSELoss()
    
    # 设定优化算法,常用的是Momentum和ADAM
    optim = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)
    model = Model(net, net_loss, optim)
    ckpt_cb = ModelCheckpoint()
    
    epoch = 1
    # 设定每8个batch训练完成后就播报一次,这里一共播报25次
    model.train(epoch, ds_train, callbacks=[LossMonitor(16), ckpt_cb], dataset_sink_mode=False)
    
    for net_param in net.trainable_params():
        print(net_param, net_param.asnumpy())
    

    最后是通过ModelCheckpoint这一方法将训练出来的模型保存成.ckpt的checkpoint文件格式。上述代码执行的结果如下:

    dechin@ubuntu2004:~/projects/gitlab/dechin/src/mindspore$ sudo docker run --rm -v /dev/shm:/dev/shm -v /home/dechin/projects/gitlab/dechin/src/mindspore/:/home/ --runtime=nvidia --privileged=true swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-gpu:1.2.0 /bin/bash -c "cd /home && python save_model.py"
    Param Shape is: 2
    Parameter (name=fc.weight, shape=(1, 1), dtype=Float32, requires_grad=True) [[0.02]]
    Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [0.02]
    epoch: 1 step: 16, loss is 0.9980968
    epoch: 1 step: 32, loss is 0.3894167
    epoch: 1 step: 48, loss is 0.090543285
    epoch: 1 step: 64, loss is 0.060300454
    epoch: 1 step: 80, loss is 0.014248277
    epoch: 1 step: 96, loss is 0.015697923
    epoch: 1 step: 112, loss is 0.014582128
    epoch: 1 step: 128, loss is 0.008066677
    epoch: 1 step: 144, loss is 0.007225203
    epoch: 1 step: 160, loss is 0.0046849623
    epoch: 1 step: 176, loss is 0.006007362
    epoch: 1 step: 192, loss is 0.004276552
    Parameter (name=fc.weight, shape=(1, 1), dtype=Float32, requires_grad=True) [[1.8259585]]
    Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [3.0577476]
    

    可能是由于数据集的问题,可能是由于噪声的问题,使得我们训练出来的参数跟我们最开始所定义的参数有些许的区别,但是具体的误差范围还得通过测试集来验证。运行结束后会在当前目录下生成一系列的.ckpt文件和一个.meta的计算图文件:

    dechin@ubuntu2004:~/projects/gitlab/dechin/src/mindspore$ ll
    total 148
    drwxr-xr-x 2 1000 1000  4096 May 16 13:25 ./
    drwxr-xr-x 1 root root  4096 May 15 01:55 ../
    -r-------- 1 root root   211 May 16 13:25 CKP-1_196.ckpt
    -r-------- 1 root root   211 May 16 13:25 CKP-1_197.ckpt
    -r-------- 1 root root   211 May 16 13:25 CKP-1_198.ckpt
    -r-------- 1 root root   211 May 16 13:25 CKP-1_199.ckpt
    -r-------- 1 root root   211 May 16 13:25 CKP-1_200.ckpt
    -r-------- 1 root root  3705 May 16 13:25 CKP-graph.meta
    -rw-r--r-- 1 1000 1000  2087 May 16 13:25 save_model.py
    

    接下来就可以开始加载这些文件中所给出的模型,用于测试集的验证。

    加载模型

    在模型的加载中,我们依然还是需要原始的神经网络对象LinearNet

    # load_model.py
    
    from mindspore import context
    context.set_context(mode=context.GRAPH_MODE, device_target="GPU")
    import mindspore.dataset as ds
    from mindspore import load_checkpoint, load_param_into_net
    from mindspore import nn, Tensor, Model
    import numpy as np
    
    class LinearNet(nn.Cell):
        def __init__(self):
            super(LinearNet, self).__init__()
            self.fc = nn.Dense(1,1,0.02,0.02)
    
        def construct(self, x):
            print ('x:', x)
            x = self.fc(x)
            print ('z:', x)
            return x
    
    net = LinearNet()
    param_dict = load_checkpoint("CKP-1_200.ckpt")
    load_param_into_net(net, param_dict)
    
    net_loss = nn.loss.MSELoss()
    model = Model(net, net_loss, metrics={"accuracy"})
    
    data = {'x':np.array([[0.01],[0.25],[1],[4],[9]]).astype(np.float32),
            'z':np.array([3.02,3.5,5,11,21]).astype(np.float32)}
    dataset = ds.NumpySlicesDataset(data=data)
    dataset = dataset.batch(1)
    
    acc = model.eval(dataset, dataset_sink_mode=False)
    

    这里我们只是定义了一个非常简单的测试集,在实际场景中往往测试集也是非常大量的数据。这里为了简化本地环境,使用的是Docker的编程环境,并且在Docker容器的拉起中使用了--rm选项,在运行结束后会删除这个容器,确保环境的整洁。关于在Docker容器环境下安装和部署MindSpore的方案,可以参考这篇CPU部署博客,以及这篇GPU部署博客。上述代码的执行结果如下:

    dechin@ubuntu2004:~/projects/gitlab/dechin/src/mindspore$ sudo docker run --rm -v /dev/shm:/dev/shm -v /home/dechin/projects/gitlab/dechin/src/mindspore/:/home/ --runtime=nvidia --privileged=true swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-gpu:1.2.0 /bin/bash -c "cd /home && python load_model.py"
    x:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 9.00000000e+00]])
    z:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 1.94898682e+01]])
    x:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 2.50000000e-01]])
    z:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 3.52294850e+00]])
    x:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 9.99999978e-03]])
    z:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 3.08499861e+00]])
    x:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 1.00000000e+00]])
    z:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 4.89154148e+00]])
    x:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 4.00000000e+00]])
    z:
    Tensor(shape=[1, 1], dtype=Float32, value=
    [[ 1.03659143e+01]])
    

    从这个结果中我们可以看到,由于在训练集中我们使用的数据集中在比较小的范围,因此在测试这个范围外的数据时,误差相较于这个区域内的数据点会更大一些,这也是属于正常的现象。

    总结概要

    本文主要从工程实现的角度测试了一下MindSpore的机器学习模型保存与加载的功能,通过这个功能,我们可以将自己训练好的机器学习模型发布出去供更多的人使用,我们也可以直接使用别人在更好的硬件体系上训练好的模型,或者应用于迁移学习。

    版权声明

    本文首发链接为:https://www.cnblogs.com/dechinphy/p/sl.html
    作者ID:DechinPhy
    更多原著文章请参考:https://www.cnblogs.com/dechinphy/

    bk