如何使用Serverless框架?

  • 回答 (7)
  • 关注 (1)
  • 查看 (1123)

从行业趋势看,Serverless是云计算必经的一场革命,无服务器云函数是实现微服务的最好的方法之一,该如何使用Serverless框架?

汐夜汐夜提问于
萌萌呆想玩游戏却不知道这么开机的吃瓜少年。回答于

我说一个比较新颖的,使用 Serverless 进行 AI 预测推理

模型准备

在这里我们使用 TensorFlow 中的 MNIST 实验作为案例来进行下面的介绍。MNIST 是一个包含了 6 万训练图片和 1 万测试图片的手写数字图片集合,图片为 28x28-pixel 大小的黑白图片。此训练集通常用来训练对数字的识别能力。

关于如何编写代码,使用 MNIST 训练集完成模型训练,可以见 TF层指南:建立卷积神经网络,这篇文章详细介绍了如何通过使用 Tensorflow layer 构建卷积神经网络,并设置如何进行训练和评估。

而在进行训练和评估后,就可以进行模型的导出了。TensorFlow 的模型文件包含了深度学习模型的 Graph 和参数,也就是 checkpoint 文件。在导出模型文件后,我们可以加载模型文件继续训练或者对外提供推理服务。

这里我们可以通过 SavedModelBuilder 模块来进行模型到处保存,更具体的文档和操作方法可见 训练和导出 TF 模型

导出后的文件,为 saved_model.pb 文件, variables 文件夹及包含的若干variables文件,分别是模型的图文件和参数文件。后续在提供推理能力时,就是使用这些图及变量文件,加载到 TF Serving 内。

为了便于后续的操作,我们在这里也直接提供我们导出的模型文件供后续操作,可以点击这里的导出模型文件来下载。

测试文件准备

测试文件我们可以从 MNIST 的测试集中随意抽取若干,用于验证我们最终推理 API 的工作状态。同样,我们也在这里准备了若干图片用于最终验证,可以点击这里的测试图片文件来下载。或者记录以下连接,用于直接测试在线图片的推理情况。

https://main.qcloudimg.com/raw/84783c178cdc6d6b2302bc1b4749b91b.bmp
https://main.qcloudimg.com/raw/0f4630a815c44107a79a224d3263da2c.bmp
https://main.qcloudimg.com/raw/360a7cdd638d22d4145c94e67b3c059f.bmp
https://main.qcloudimg.com/raw/90067917b01d4b4d31f207ac78e70416.bmp
https://main.qcloudimg.com/raw/7828379e768aa5c8a6d09a3d58f64921.bmp
https://main.qcloudimg.com/raw/79e0c07766c739c880dfed8d0433ff83.bmp
https://main.qcloudimg.com/raw/b5f1c6f4ba08c3376c333c5a62f0c1dd.bmp
https://main.qcloudimg.com/raw/40adedc18205428276c3753bac51e740.bmp
https://main.qcloudimg.com/raw/4c750171b4b31772f0b923beef92c9f3.bmp
https://main.qcloudimg.com/raw/8504482825667f97b21209b9570249e6.bmp
https://main.qcloudimg.com/raw/5f22f6f83d26a3f267c823cd5437bdfc.bmp
https://main.qcloudimg.com/raw/fb1f59a3fdbcad0a140045508f28f688.bmp

函数创建及测试

我们使用如下示例来创建函数程序包。

准备函数文件

创建目录 mnist_demo,并在根目录下创建文件 mnist.py,文件内容如下。

#!/usr/bin/env python2.7

import os
import sys
import base64
import urllib
import tensorflow as tf
import numpy as np
import utils
import json

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

# Load model
cur_dir = os.getcwd()
model_dir = cur_dir+"/export/4"
sess = tf.Session(graph=tf.Graph())
meta_graph_def = tf.saved_model.loader.load(sess, [tf.saved_model.tag_constants.SERVING], model_dir)
x = sess.graph.get_tensor_by_name('x:0')
y = sess.graph.get_tensor_by_name('y:0')

def run(event):
    local_path = ""
    if event['image_base64'] != "":
        data=base64.b64decode(event['image_base64'])
        local_path = '/tmp/test_image.xxx'
        file=open(local_path,'wb')
        file.write(data)
        file.close()
    elif event['image_url'] != "":
        img_url = event['image_url']
        filename = urllib.unquote(img_url).decode('utf8').split('/')[-1]
        local_path = os.path.join('/tmp/', filename)
        if not utils.download_image(event['image_url'], local_path):
            return False, -1
    else:
        print('Please specify a image.')

    try:
        x_ = utils.get_image_array(local_path)
        predict = tf.argmax(y, 1)
        res = sess.run(predict, feed_dict={x: x_})[0] 
        return True, sess.run(predict, feed_dict={x: x_})[0]
    except Exception as e:
        print(e)
        return False, -1

def demo(event, context):
    res, num = run(event)
    print num
    return num
        
def apigw_interface(event, context):
    if 'requestContext' not in event.keys():
        return {"errMsg":"request not from api gw"}
    body_str = event['body']
    body_info = json.loads(body_str)
    res, num = run(body_info)
    return {"result":num}

从代码中我们可以看到,函数在初始化时就将目录 export 下的文件作为模型加载到了 TensorFlow 中。在实际的事件处理中,既可以从事件中抽取 base64 编码后的图片,也可以识别 url 参数,并均把图片保存至本地 /tmp 目录下。然后在使用 util 工具,对图片进行规整处理后,将处理后的数据送入 TensorFlow,获得推理结果并返回。

在根目录下同时创建 util.py,代码内容如下。此模块提供了图片下载、图片处理等辅助能力。

#!/usr/bin/env python2.7

import urllib2
import numpy as np
from PIL import Image

def download_image(img_url, local_file):
    ret = True
    try: 
      f = urllib2.urlopen(img_url)
      data = f.read()
      with open(local_file, "wb") as img:
          img.write(data)
    except Exception as e:
        print(e)
        ret = False
    return ret

def get_image_array(img_path):
    x_s = 28
    y_s = 28
    n0 = 0
    n255 = 0
    threshold = 100
    im = None
    im = Image.open(img_path)

    img = np.array(im.resize((x_s, y_s), Image.ANTIALIAS).convert('L'))

    for x in range(x_s):
      for y in range(y_s):
        if img[x][y] > threshold:
          n255 = n255 + 1
        else:
          n0 = n0 + 1

    if(n255 > n0) :
      for x in range(x_s):
        for y in range(y_s):
          img[x][y] = 255 - img[x][y]
          if(img[x][y] < threshold) :
            img[x][y] = 0

    arr = img.reshape((1, 784))
    arr = arr.astype(np.float32)
    arr = np.multiply(arr, 1.0 / 255.0)
    return arr

同时由于 util.py 中使用了 PIL 库,需要在代码根目录下提供 PIL 库,可以使用此 PIL库文件 来下载库并解压后放置在代码根目录。

准备函数部署包

最终,我们得到的代码目录结构为如下结构,其中PIL文件夹下由于文件过多就不进行展开了。

mnist_demo
|
|-- mnist.py
|-- utils.py
| export
    | 4
        |-- saved_model.pb
        | variables
            |-- variables.data-00000-of-00001
            |-- variables.index
| PIL
    |-- ...

在 mnist_demo 这个目录下,我们选择所有文件然后打包为 zip 包。注意,这些文件需要在 zip 包的根目录下,而不是 mnist_demo 文件夹在zip包的根目录。

最终我们得到了一个可以上传到云函数的 zip 包。如果对于前面的操作都不想进行,也可以直接在这里下载已经打好的包即可。

创建函数

由于创建的函数部署包稍大,所以我们需要通过对象存储来上传代码包。

我们可以在腾讯云对象存储 COS 中先创建一个 bucket,例如在广州区创建名为 code 的 bucket,并将上一步获取的代码包上传 bucket,作为我们后续创建函数的代码来源。

进入腾讯云无服务器云函数 SCF 的控制台,选择广州区以后,点击新建函数,为函数起一个比较容易记住的名字,例如 testai,选择运行环境为 Python 2.7,然后下一步到代码配置页面。

在代码配置页面,选择代码输入种类为 通过 COS 上传 zip 包,选择刚刚创建的bucket为 cos,并填写对象文件为 /mnist_demo.zip

同时,函数执行方法需要确定为 mnist.apigw_interface,对应代码包中的 mnist 文件和 apigw_interface 函数。

测试函数

点击函数界面右上角的测试按钮,并使用如下测试模版来测试函数。

{
  "requestContext": {
    "serviceName": "testsvc",
    "path": "/test/{path}",
    "httpMethod": "POST",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "identity": {
      "secretId": "abdcdxxxxxxxsdfs"
    },
    "sourceIp": "10.0.2.14",
    "stage": "prod"
  },
  "headers": {
    "Accept-Language": "en-US,en,cn",
    "Accept": "text/html,application/xml,application/json",
    "Host": "service-3ei3tii4-251000691.ap-guangzhou.apigateway.myqloud.com",
    "User-Agent": "User Agent String"
  },
  "body": "{\"image_base64\": \"\", \"image_url\": \"https://main.qcloudimg.com/raw/84783c178cdc6d6b2302bc1b4749b91b.bmp\"}",
  "pathParameters": {
    "path": "value"
  },
  "queryStringParameters": {
    "foo": "bar"
  },
  "headerParameters":{
    "Refer": "10.0.2.14"
  },
  "stageVariables": {
    "stage": "test"
  },
  "path": "/test/value?foo=bar",
  "httpMethod": "POST"
}

这里可以看到,我们主要使用了body字段,并在body字段内填写的数据结构为:

{
  "image_base64": "",
  "image_url": "https://main.qcloudimg.com/raw/84783c178cdc6d6b2302bc1b4749b91b.bmp"
}

这个数据结构也是我们创建的函数所能接受和处理的结构,如果有 base64 编码的图片文件内容,则使用编码的内容,或者使用url传入的图片地址,将图片下载到本地后交由 TensorFlow 进行预测推理。在这里测试时,我们可以用上我们在一开始准备的图片地址,而在实际推理时,可以替换成其他可访问的图片地址。

点击测试运行后,如果使用的是上面的地址,使用的是第一副图片,那么函数的返回内容将是 {"result": 0},标识其推理出来的图片内是数值 0。

使用 API 网关进行 API 封装

接下来我们通过 API 网关服务,来创建一个 API 对刚刚创建的推理函数进行封装,并对外提供 API 服务。

创建 API 服务及 API

首先在 API 网关的控制台,在广州区创建一个 API 服务。服务名可以起一个容易记住的名字,例如 testai。

接着进入API 管理,创建 API。同样可以起一个容易记住的名字,例如 ai。请求路径可以写为 /ai,请求方法为 POST。为了方便调试,我们这里可以勾选上免鉴权。

接着下一步,后端类型选择为 cloud function,并选择我们在前面创建的函数 testai。

最后一步填入响应内容为 JSON,描述正确示例为 {"result":0},错误示例为 {"errMsg":"error info"} 即可。

调试 API

点击 API 查看界面的 API 调试,进入调试页面。确定 Content-Type 为 application/json,输入框内填入以下内容后点击发送请求。

{
  "image_base64": "",
  "image_url": "https://main.qcloudimg.com/raw/84783c178cdc6d6b2302bc1b4749b91b.bmp"
}

确认响应的 body 为 {"result": 0},符合预期。同理,这里的 url 地址同样可以更改,并且响应内容随图片的不同而不同。

API 发布及外网测试

在 API 调试成功后,我们可以在服务列表页面,将我们刚刚创建的 testai 服务发布到 发布环境

然后根据 testai 的服务域名,我们可以得到刚才的 API 的完整路径为:http://service-kzeyrb6x-1253970226.ap-beijing.apigateway.myqcloud.com/release/ai。我们可以使用 http request 的发起工具,例如 Postman,或 restclient 等,向此 API 地址发起 POST请求,POST内容可以为如下内容。

{
  "image_base64": "",
  "image_url": "https://main.qcloudimg.com/raw/84783c178cdc6d6b2302bc1b4749b91b.bmp"
}

{
  "image_base64": "Qk1mCQAAAAAAADYAAAAoAAAAHAAAABwAAAABABgAAAAAADAJAAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAA////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////",
  "image_url": ""
}

这两个不同的数据结构,分别测试了使用 base64 编码的图片内容,或者使用图片 url 地址传递图片内容的方式。同时可以根据自身需求,修改数据结构内的 image_base64 或 image_url 内容,查看测试结果。

回答过的其他问题

腾讯云SCF无服务器云函数有哪些应用?

萌萌呆想玩游戏却不知道这么开机的吃瓜少年。
推荐
可以用 SCF 无服务器云函数定时备份数据库 mysqldump 准备 常用来导出数据库备份数据的的 mysqldump 工具,在云函数中也同样能使用;但是由于云函数环境并未内置 mysqldump,因此我们要自行打包工具。 通过 mysql 社区版下载地址,选择操作系统为 Li...... 展开详请

如何从零开始搭建一款小程序?

萌萌呆想玩游戏却不知道这么开机的吃瓜少年。
说到小程序,不得不提小游戏,那么如何从零快速搭建小游戏呢?什么是小游戏? 从普通用户的视角看,小游戏是小程序的一个子类目,可在微信内被便捷的获取和传播,即点即玩,具备出色的用户体验。小游戏是小程序,普通用户分不清也无需分清。 同时,从开发者的视角,它可以看作是基于 Canvas/...... 展开详请

如何解决LinkError:org.apache.hadoop.io.nativeio.NativeIO$Windows.createFileWithMode0(Ljava/lang/String;JJJI)Ljava/io/FileDescriptor?

萌萌呆想玩游戏却不知道这么开机的吃瓜少年。

腾讯智能钛机器学习平台是免费的吗?

萌萌呆想玩游戏却不知道这么开机的吃瓜少年。

在自闭标签之前在自闭合标签中添加空格

萌萌呆想玩游戏却不知道这么开机的吃瓜少年。
也许有点混乱,设置是在“HTML”代码样式下,而不是IntelliJ中的JS代码样式。 在首选项 - >编辑器 - >代码样式 - > HTML中启用“空标记”设置。 当您进行显式重新格式化时,将添加空间。要确保在自动完成React组件时添加空间,请在首选项 - >编辑器 -...... 展开详请

如何在JavaFX中的所有表单元格上设置工具提示?

萌萌呆想玩游戏却不知道这么开机的吃瓜少年。
已采纳
设置表后(即创建并添加列,并在所有列上设置单元工厂),可以“装饰”列的单元工厂: private <T> void addTooltipToColumnCells(TableColumn<TableDataType,T> column) { Callback<Tabl...... 展开详请

扫码关注云+社区

领取腾讯云代金券