美文网首页AI模型部署
自动部署深度神经网络模型TensorFlow(Keras)到生产

自动部署深度神经网络模型TensorFlow(Keras)到生产

作者: aipredict | 来源:发表于2020-02-16 14:03 被阅读0次

    目录

    Keras简介

    Keras是一套由Python实现的高级神经网络API,可以基于不同的后台(backend)实现,包括TensoreFlowCNTKMXNet(基于Keras-MXNet,处于孵化阶段)和Theano(已停止开发)。Keras可以让用户能快速的开发出原型,支持流行的卷积网络(Convolutional Networks)、循环网络(Recurrent Networks)或者两者的组合,并且可以无缝的在CPU和GPU上运行。

    Keras模型分类

    Keras提供了两种类型的模型,顺序(Sequential)模型和函数式(Functional)模型:

    • 顺序模型,由一组线性的层堆栈组成(a linear stack of layers),是一种最简单的模型类型,API简单明了,详情查看官方文档:Guide to the Sequential model
    • 函数式模型,可以定义更复杂的模型网络结构,比如多输出模型,有向无环图,具有共享层的多模型,详情查看官方文档:Guide to the Functional API

    以上两套API,生成的模型类型是keras.models.Sequential(继承自Model)和keras.models.Model类,除了以上两种类型模型,从Keras 2.2.0开始,支持用户继承keras.models.Model来创建完全自定义化模型类型,详情查看官方文档模型自类化(Model subclassing)。本文中我们主要讨论非子类化(non-subclassing)模型的部署,关于子类化模型(subclassing)的部署会在后面介 绍。

    Keras模型部署准备

    在上一篇文章《自动部署开源AI模型到生产环境:Sklearn、XGBoost、LightGBM、和PySpark》中我们介绍了如何通过AutoDeployAI的AI模型自动部署和管理系统DaaS(Deployment-as-a-Service)来自动部署传统开源机器学习模型(包括Scikit-learn、XGBoost、LightGBM、和PySpark等),这里我们详细介绍如果通过DaaS来自动部署Keras深度神经网络模型,同样我们需要:

    • 安装Python DaaS-Client
    • 初始化DaasClient
    • 创建项目

    详细准备工作,请参考以上文章中的部署准备部分。完整的代码,请参考Github上的Notebook:deploy-keras.ipynb

    默认部署Keras模型

    1. 基于Keras Sequence API训练一个简单的多分类模型,使用Scikit-learn中的Iris数据:
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.optimizers import Adam
    
    model = Sequential()
    model.add(Dense(10, input_shape=(4,), activation='relu'))
    model.add(Dense(8, activation='relu'))
    model.add(Dense(6, activation='relu'))
    model.add(Dense(3, activation='softmax'))
    model.compile(Adam(lr=0.04), 'categorical_crossentropy', ['accuracy'])
    model.fit(X_train, pd.get_dummies(y_train).values, epochs=100)
    
    1. 发布Keras模型:
    publish_resp = client.publish(model,
                                  name='keras-iris',
                                  mining_function='classification',
                                  x_test=X_test,
                                  y_test=y_test,
                                  description='A Keras classification model')
    pprint(publish_resp)
    

    publish_resp是一个字典类型的结果,记录了模型名称,和发布的模型版本。

    {'model_name': 'keras-iris', 'model_version': '1'}
    
    1. 测试Keras模型:
    test_resp = client.test(publish_resp['model_name'], 
                            model_version=publish_resp['model_version'])
    pprint(test_resp)
    

    test_resp是一个字典类型的结果,记录了测试REST API信息,如下:

    {'access_token': 'A-LONG-STRING-OF-BEARER-TOKEN-USED-IN-HTTP-HEADER-AUTHORIZATION',
     'endpoint_url': 'https://daas.autodeploy.ai/api/v1/test/deployment-test/daas-python37-faas/test',
     'payload': {'args': {'X': [{'dense_1_input': [5.7, 4.4, 1.5, 0.4]}],
                          'model_name': 'keras-iris',
                          'model_version': '1'}}}
    

    使用requests库调用测试API:

    response = requests.post(test_resp['endpoint_url'],
                             headers={'Authorization': 'Bearer {token}'.format(token=test_resp['access_token'])},
                             json=test_resp['payload'],
                             verify=False)
    pprint(response.json())
    

    返回结果:

    {'result': [{'dense_4': [[0.996542751789093,
                              0.0034567660186439753,
                              4.955750227964018e-07]]}],
     'stderr': [],
     'stdout': []}
    
    1. 正式部署Keras模型:
    deploy_resp = client.deploy(model_name='keras-iris', 
                                deployment_name='keras-iris-svc',
                                model_version=publish_resp['model_version'],
                                replicas=1)
    pprint(deploy_resp)
    

    返回结果:

    {'access_token': 'A-LONG-STRING-OF-BEARER-TOKEN-USED-IN-HTTP-HEADER-AUTHORIZATION',
     'endpoint_url': 'https://daas.autodeploy.ai/api/v1/svc/deployment-test/keras-iris-svc/predict',
     'payload': {'args': {'X': [{'dense_1_input': [5.7, 4.4, 1.5, 0.4]}]}}}
    

    使用requests库调用正式API:

    response = requests.post(deploy_resp['endpoint_url'],
                             headers={'Authorization': 'Bearer {token}'.format(token=deploy_resp['access_token'])},
                             json=deploy_resp['payload'],
                             verify=False)
    pprint(response.json())
    

    结果如下:

    {'result': [{'dense_4': [[0.996542751789093,
                              0.0034567660186439753,
                              4.955750227964018e-07]]}]}
    

    除了使用Keras API,对于TensorFlow,DaaS也同样支持使用tf.keras API训练的模型。

    自定义部署Keras模型

    1. 基于tf.Keras Functional API训练模型,使用Keras中的MNist数据来识别用户输入的数字,以下代码参考Functional API on MNIST
    # load MNIST dataset
    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    
    # from sparse label to categorical
    num_labels = len(np.unique(y_train))
    y_train = to_categorical(y_train)
    y_test = to_categorical(y_test)
    
    # reshape and normalize input images
    image_size = x_train.shape[1]
    x_train = np.reshape(x_train,[-1, image_size, image_size, 1])
    x_test = np.reshape(x_test,[-1, image_size, image_size, 1])
    x_train = x_train.astype('float32') / 255
    x_test = x_test.astype('float32') / 255
    
    # network parameters
    input_shape = (image_size, image_size, 1)
    batch_size = 128
    kernel_size = 3
    filters = 64
    dropout = 0.3
    
    # use functional API to build cnn layers
    inputs = Input(shape=input_shape)
    y = Conv2D(filters=filters,
               kernel_size=kernel_size,
               activation='relu')(inputs)
    y = MaxPooling2D()(y)
    y = Conv2D(filters=filters,
               kernel_size=kernel_size,
               activation='relu')(y)
    y = MaxPooling2D()(y)
    y = Conv2D(filters=filters,
               kernel_size=kernel_size,
               activation='relu')(y)
    # image to vector before connecting to dense layer
    y = Flatten()(y)
    # dropout regularization
    y = Dropout(dropout)(y)
    outputs = Dense(num_labels, activation='softmax')(y)
    
    # build the model by supplying inputs/outputs
    model = Model(inputs=inputs, outputs=outputs)
    # network model in text
    model.summary()
    
    # classifier loss, Adam optimizer, classifier accuracy
    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
    
    # train the model with input images and labels
    model.fit(x_train,
              y_train,
              validation_data=(x_test, y_test),
              epochs=3,
              batch_size=batch_size)
    

    为了可以快速训练出模型,修改原程序中epochs=20为3。

    1. 发布Keras模型:
    publish_resp = client.publish(model,
                                  name='keras-mnist',
                                  mining_function='classification',
                                  x_test=x_test,
                                  y_test=y_test,
                                  description='A tf.Keras classification model')
    pprint(publish_resp)
    

    结果如下:

    {'model_name': 'keras-mnist', 'model_version': '1'}
    
    1. 测试Keras模型。登陆DaaS Web客户端,查看keras-mnist模型信息:
    daas-model-overview.png

    模型输入字段input_1,维数为(,28,28,1),输出字段dense,维数为(,10)。切换到测试标签页,我们看到DaaS自动存储了一条测试数据,点击提交命令,测试该条数据,如图:

    daas-model-test.png
    1. 自定义部署Keras模型。keras-mnist模型输入数据是由一张28*28的黑底白字灰度图像生成的numpy数组,在REST API使用JSON传输数据时,因为JSON作为一种文本格式,在存储传输大的列表时有性能劣势,并且需要调用端做图像预处理工作,增加了客户端使用的负担。我们希望这些都可以在服务器端完成:接收二进制图像文件,预处理图像,并且转成模型需要的numpy数组。在DaaS中,我们可以通过创建自定义部署脚本来完成该该任务,它允许用户在模型部署中添加任意的数据预处理和后处理操作。切换到实时预测标签页,点击命令生成自定义实时预测脚本,生成预定义脚本:
    daas-model-realtime.png

    点击作为API测试命令,页面切换到测试页面,修改preprocess_files函数,并且添加处理图像的函数:

    def rgb2gray(rgb):
        """Convert the input image into grayscale"""
        import numpy as np
        return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])
    
    
    def resize_img(img_to_resize):
        """Resize image to MNIST model input dimensions"""
        import cv2
        r_img = cv2.resize(img_to_resize, dsize=(28, 28), interpolation=cv2.INTER_AREA)
        r_img.resize((1, 28, 28, 1))
        r_img = 1 - r_img
        return r_img
    
    
    def preprocess_image(img_to_preprocess):
        """Resize input images and convert them to grayscale."""
        if img_to_preprocess.shape == (28, 28):
            img_to_preprocess.resize((1, 28, 28, 1))
            img_to_preprocess = 1 - img_to_preprocess / 255
            return img_to_preprocess
    
        grayscale = rgb2gray(img_to_preprocess)
        processed_img = resize_img(grayscale)
        return processed_img
    
    
    def preprocess_files(args):
        """preprocess the uploaded files"""
        files = args.get('files')
        if files is not None:
            # get the first record object in X if it's present
            if 'X' in args:
                record = args['X'][0]
            else:
                record = {}
                args['X'] = [record]
    
            # TODO add your own custom opeartions, e.g. loading images, make transformation, then write back into X
            import matplotlib.image as mpimg
            for key, file in files.items():
                img = mpimg.imread(file)
                record[key] = preprocess_image(img)
    
        return args
    

    输入函数名predict,选择请求正文基于表单,输入名称input_1,选择文件,点击上传测试图像2.png,点击提交,右侧响应页面显示结果为:

    {
      "result": [
        {
          "dense": [
            [
              4.0412282942270394e-7,
              5.612335129967505e-8,
              0.9999896287918091,
              0.0000014349453749673557,
              2.1778572326623669e-13,
              3.3688251840913175e-12,
              1.8931906042851665e-10,
              1.7151558395767097e-8,
              0.000008489014362567104,
              6.33769703384246e-10
            ]
          ]
        }
      ],
      "stderr": [
        "Some warning messages here"
      ],
      "stdout": []
    }
    

    继续修改postprocess函数为:

    def postprocess(result):
        """postprocess the predicted results"""
        import numpy as np
        return [int(np.argmax(np.array(result).squeeze(), axis=0))]
    

    重新提交,右侧响应页面显示结果为:

    daas-model-script-test.png

    测试完成后,可以创建正式的部署,切换到部署标签页,点击命令添加网络服务,输入服务名称mnist-svc,其他使用默认选项,点击创建。进入到部署页面后,点击测试标签页,该界面类似之前的脚本测试界面,输入函数名predict,请求正文选择基于表单,输入名称input_1,类型选择文件,点击上传测试的图片后,点击提交:

    daas-deployment-test.png

    到此,正式部署已经测试和创建完成,用户可以使用任意的客户端程序调用该部署服务。点击以上界面中的生成代码命令,显示如何通过curl命令调用该服务,测试如下:

    daas-deployment-curl-test.png
    1. 通过ONNX部署Keras模型:

      • 转换模型到ONNX:
      import onnxmltools
      
      onnx_model = onnxmltools.convert_keras(model, model.name)
      
      • 发布ONNX模型:
      publish_resp = client.publish(onnx_model,
                                    name='keras-mnist-onnx',
                                    mining_function='classification',
                                    x_test=x_test,
                                    y_test=y_test,
                                    description='A tf.Keras classification model in ONNX')
      pprint(publish_resp)
      

      结果如下:

      {'model_name': 'keras-mnist-onnx', 'model_version': '1'}
      
      • 测试ONNX模型:登陆DaaS Web客户端,查看keras-onnx-mnist模型信息:
    daas-onnx-model-overview.png

    模型输入字段input_1,维数为(N,28,28,1),输出字段dense,维数为(N,10)。切换到测试标签页,我们看到DaaS自动存储了一条测试数据,点击提交命令,测试该条数据,如图:

    daas-onnx-model-test.png

    我们看到,该ONNX模型和原生Keras模型测试结果是一致的。

    总结

    通过以上的演示,我们可以看到,DaaS在部署深度学习模型时的优势:既可以一键式创建默认部署,又可以灵活的自定义部署,满足用户多样的部署需求。关于其他深度学习框架的部署,比如Pytorch,MXNet等,会在后续的文章中介绍。

    参考

    相关文章

      网友评论

        本文标题:自动部署深度神经网络模型TensorFlow(Keras)到生产

        本文链接:https://www.haomeiwen.com/subject/qmvpfhtx.html