乐趣区

关于SegmentFault:20-行代码Serverless-架构下用-Python-轻松搞定图像分类和预测

作者 | 江昱

前言

图像分类是人工智能畛域的一个热门话题。艰深解释就是,依据各自在图像信息中所反映的不同特色,把不同类别的指标辨别开来的图像处理办法。

它利用计算机对图像进行定量分析,把图像或图像中的每个像元或区域划归为若干个类别中的某一种,以代替人的视觉判读。

图像分类在理论生产生存中也是常常遇到的,而且针对不同畛域或者需要有着很强的针对性。例如通过拍摄花朵辨认花朵信息、通过人脸比对人物信息等。

通常状况下,这些图像识别或者分类的工具,都是在客户端进行数据采集,在服务端进行运算取得后果,也就是说个别状况下都是有专门的 API 实现图像识别的。例如各大云厂商都会为咱们有偿提供相似的能力:

阿里云图像识别页面:

华为云图像识别页面:

本文将会通过一个乏味的 Python 库,疾速将图像分类的性能搭建在云函数上,并且和 API 网关联合,对外提供 API 性能,实现一个 Serverless 架构的 “图像分类 API”

首先和大家介绍一下须要的依赖库:ImageAI。通过该依赖的官网文档咱们能够看到这样的形容:

ImageAI 是一个 python 库,旨在使开发人员可能应用简略的几行代码构建具备蕴含深度学习和计算机视觉性能的应用程序和零碎。
ImageAI 本着简洁的准则,反对最先进的机器学习算法,用于图像预测、自定义图像预测、物体检测、视频检测、视频对象跟踪和图像预测训练。ImageAI 目前反对应用在 ImageNet-1000 数据集上训练的 4 种不同机器学习算法进行图像预测和训练。ImageAI 还反对应用在 COCO 数据集上训练的 RetinaNet 进行对象检测、视频检测和对象跟踪。最终,ImageAI 将为计算机视觉提供更宽泛和更专业化的反对,包含但不限于非凡环境和非凡畛域的图像识别。

也就是说这个依赖库,能够帮忙咱们实现根本的图像识别和视频的指标提取,尽管他给了一些数据集和模型,然而咱们也能够依据本身须要对其进行额定的训练,进行定制化拓展。通过官网给的代码,咱们能够看到一个简略的 Demo:

# -*- coding: utf-8 -*-
from imageai.Prediction import ImagePrediction

# 模型加载
prediction = ImagePrediction()
prediction.setModelTypeAsResNet()
prediction.setModelPath("resnet50_weights_tf_dim_ordering_tf_kernels.h5")
prediction.loadModel()

predictions, probabilities = prediction.predictImage("./picture.jpg", result_count=5)
for eachPrediction, eachProbability in zip(predictions, probabilities):
    print(str(eachPrediction) + ":" + str(eachProbability))

当咱们指定的 picture.jpg 图片为:

咱们在执行之后的后果是:

laptop : 71.43893241882324
notebook : 16.265612840652466
modem : 4.899394512176514
hard_disc : 4.007557779550552
mouse : 1.2981942854821682

如果在应用过程中感觉模型 resnet50_weights_tf_dim_ordering_tf_kernels.h5 过大,耗时过长,能够按需要抉择模型:

  • SqueezeNet(文件大小:4.82 MB,预测工夫最短,精准度适中)
  • ResNet50 by Microsoft Research(文件大小:98 MB,预测工夫较快,精准度高)
  • InceptionV3 by Google Brain team(文件大小:91.6 MB,预测工夫慢,精度更高)
  • DenseNet121 by Facebook AI Research(文件大小:31.6 MB,预测工夫较慢,精度最高)

模型下载地址可参考 Github 地址:
https://github.com/OlafenwaMoses/ImageAI/releases/tag/1.0

或者参考 ImageAI 官网文档:
https://imageai-cn.readthedocs.io/zh_CN/latest/ImageAI_Image_Prediction.html

我的项目 Serverless 化

将我的项目依照函数计算的需要,编写好入口办法,以及做好我的项目初始化,同时在以后我的项目下创立文件夹 model,并将模型文件拷贝到该文件夹:

我的项目整体流程:

实现代码:

# -*- coding: utf-8 -*-

from imageai.Prediction import ImagePrediction
import json
import uuid
import base64
import random


# Response
class Response:
    def __init__(self, start_response, response, errorCode=None):
        self.start = start_response
        responseBody = {'Error': {"Code": errorCode, "Message": response},
        } if errorCode else {'Response': response}
        # 默认减少 uuid,便于前期定位
        responseBody['ResponseId'] = str(uuid.uuid1())
        print("Response:", json.dumps(responseBody))
        self.response = json.dumps(responseBody)

    def __iter__(self):
        status = '200'
        response_headers = [('Content-type', 'application/json; charset=UTF-8')]
        self.start(status, response_headers)
        yield self.response.encode("utf-8")


# 随机字符串
randomStr = lambda num=5: "".join(random.sample('abcdefghijklmnopqrstuvwxyz', num))

# 模型加载
print("Init model")
prediction = ImagePrediction()
prediction.setModelTypeAsResNet()
print("Load model")
prediction.setModelPath("/mnt/auto/model/resnet50_weights_tf_dim_ordering_tf_kernels.h5")
prediction.loadModel()
print("Load complete")


def handler(environ, start_response):
    try:
        request_body_size = int(environ.get('CONTENT_LENGTH', 0))
    except (ValueError):
        request_body_size = 0
    requestBody = json.loads(environ['wsgi.input'].read(request_body_size).decode("utf-8"))

    # 图片获取
    print("Get pucture")
    imageName = randomStr(10)
    imageData = base64.b64decode(requestBody["image"])
    imagePath = "/tmp/" + imageName
    with open(imagePath, 'wb') as f:
        f.write(imageData)

    # 内容预测
    print("Predicting ...")
    result = {}
    predictions, probabilities = prediction.predictImage(imagePath, result_count=5)
    print(zip(predictions, probabilities))
    for eachPrediction, eachProbability in zip(predictions, probabilities):
        result[str(eachPrediction)] = str(eachProbability)

    return Response(start_response, result)

所须要的依赖:

tensorflow==1.13.1
numpy==1.19.4
scipy==1.5.4
opencv-python==4.4.0.46
pillow==8.0.1
matplotlib==3.3.3
h5py==3.1.0
keras==2.4.3
imageai==2.1.5

编写部署所须要的配置文件:

ServerlessBookImageAIDemo:
  Component: fc
  Provider: alibaba
  Access: release
  Properties:
    Region: cn-beijing
    Service:
      Name: ServerlessBook
      Description: Serverless 图书案例
      Log: Auto
      Nas: Auto
    Function:
      Name: serverless_imageAI
      Description: 图片指标检测
      CodeUri:
        Src: ./src
        Excludes:
          - src/.fun
          - src/model
      Handler: index.handler
      Environment:
        - Key: PYTHONUSERBASE
          Value: /mnt/auto/.fun/python
      MemorySize: 3072
      Runtime: python3
      Timeout: 60
      Triggers:
        - Name: ImageAI
          Type: HTTP
          Parameters:
            AuthType: ANONYMOUS
            Methods:
              - GET
              - POST
              - PUT
            Domains:
              - Domain: Auto

在代码与配置中,能够看到有目录:/mnt/auto/ 的存在,该局部实际上是 nas 挂载之后的地址,只需提前写入到代码中即可,下一个环节会进行 nas 的创立以及挂载点配置的具体操作。

我的项目部署与测试

在实现上述步骤之后,能够通过:

s deploy

进行我的项目部署,部署实现能够看到后果:

实现部署之后,能够通过:

s install docker

进行依赖的装置:

依赖装置实现能够看到在目录下生成了 .fun 的目录,该目录就是通过 docker 打包进去的依赖文件,这些依赖正是咱们在 requirements.txt 文件中申明的依赖内容。

实现之后,咱们通过:

s nas sync ./src/.fun

将依赖目录打包上传到 nas,胜利之后再将 model 目录打包上传:

s nas sync ./src/model

实现之后能够通过:

s nas ls --all

查看目录详情:

实现之后,咱们能够编写脚本进行测试,同样实用方才的测试图片,通过代码:

import json
import urllib.request
import base64
import time

with open("picture.jpg", 'rb') as f:
    data = base64.b64encode(f.read()).decode()

url = 'http://35685264-1295939377467795.test.functioncompute.com/'

timeStart = time.time()
print(urllib.request.urlopen(urllib.request.Request(
    url=url,
    data=json.dumps({'image': data}).encode("utf-8")
)).read().decode("utf-8"))
print("Time:", time.time() - timeStart)

能够看到后果:

{"Response": {"laptop": "71.43893837928772", "notebook": "16.265614330768585", "modem": "4.899385944008827", "hard_disc": "4.007565602660179", "mouse": "1.2981869280338287"}, "ResponseId": "1d74ae7e-298a-11eb-8374-024215000701"}
Time:  29.16020894050598

能够看到,函数计算顺利地返回了预期后果,然而整体耗时却超乎设想,有近 30s,此时咱们再次执行一下测试脚本:

{"Response": {"laptop": "71.43893837928772", "notebook": "16.265614330768585", "modem": "4.899385944008827", "hard_disc": "4.007565602660179", "mouse": "1.2981869280338287"}, "ResponseId": "4b8be48a-298a-11eb-ba97-024215000501"}
Time:  1.1511380672454834

能够看到,再次执行的工夫仅有 1.15 秒,比上次整整晋升了 28 秒之多。

我的项目优化

在上一轮的测试中能够看到,我的项目首次启动和二次启动的耗时差距,其实这个时间差,次要是函数在加载模型的时候节约了极长的工夫。

即便在本地,咱们也能够简略测试:

# -*- coding: utf-8 -*-

import time

timeStart = time.time()

# 模型加载
from imageai.Prediction import ImagePrediction

prediction = ImagePrediction()
prediction.setModelTypeAsResNet()
prediction.setModelPath("resnet50_weights_tf_dim_ordering_tf_kernels.h5")
prediction.loadModel()
print("Load Time:", time.time() - timeStart)
timeStart = time.time()

predictions, probabilities = prediction.predictImage("./picture.jpg", result_count=5)
for eachPrediction, eachProbability in zip(predictions, probabilities):
    print(str(eachPrediction) + ":" + str(eachProbability))
print("Predict Time:", time.time() - timeStart)

执行后果:

Load Time:  5.549695014953613
laptop : 71.43893241882324
notebook : 16.265612840652466
modem : 4.899394512176514
hard_disc : 4.007557779550552
mouse : 1.2981942854821682
Predict Time:  0.8137111663818359

能够看到,在加载 imageAI 模块以及加载模型文件的过程中,一共耗时 5.5 秒,在预测局部仅有不到 1 秒钟的工夫。而在函数计算中,机器性能自身就没有我本地的性能高,此时为了防止每次装载模型导致的响应工夫过长,在部署的代码中,能够看到模型装载过程实际上是被放在了入口办法之外。这样做的一个益处是,我的项目每次执行的时候,不肯定会有冷启动,也就是说在某些复用的前提下是能够复用一些对象的,即无需每次都从新加载模型、导入依赖等。

所以在理论我的项目中,为了防止频繁申请,实例反复装载、创立某些资源,咱们能够将局部资源放在初始化的时候进行。这样能够大幅度提高我的项目的整体性能,同时配合厂商所提供的预留能力,能够基本上杜绝函数冷启动带来的负面影响。

总结

近年来,人工智能与云计算的倒退突飞猛进,在 Serverless 架构中,如何运行传统的人工智能我的项目曾经逐步成为很多人所须要理解的事件。本文次要介绍了通过一个已有的依赖库(ImageAI)实现一个图像分类和预测的接口。借助这个例子,其实有几个事件是能够被明确的:

  • Serverless 架构能够运行人工智能相干我的项目;
  • Serverless 能够很好地兼容 Tensorflow 等机器学习 / 深度学习的工具;
  • 尽管说函数计算自身有空间限度,然而实际上减少了硬盘挂载能力之后,函数计算自身的能力将会失去大幅度的拓展。

当然,本文也算是抛砖引玉,心愿读者在本文之后,能够施展本人的设想,将更多的 AI 我的项目与 Serverless 架构进行进一步联合。

退出移动版