您可以通过函数计算控制台、SDK或Serverless Devs来体验GPU实例的最佳实践。本文以Python语言为例,说明如何使用Serverless Devs开发工具,将原始图像经过函数代码处理,实现风格合成以及对象检测。

应用场景与优势

传统的面向人工智能应用的GPU基础设施,通常会面临着建设周期长、运维复杂度高、集群利用率低和成本较高等问题。函数计算的GPU实例将这些问题从用户侧转移至云厂商侧,让您无需关心底层GPU基础设施,完全聚焦于业务本身,极大地简化了业务的实现路径。

在不同的应用场景下,函数计算提供的GPU实例与CPU相比所具备的优势如下:

  • 成本优先的AI应用场景
    • 提供弹性预留模式,从而按需为客户保留GPU工作实例,对比自建GPU集群拥有较大成本优势。
    • 提供GPU共享虚拟化,支持以1/2、独占方式使用GPU,允许业务以更精细化的方式配置GPU实例。
  • 效率优先的AI应用场景
    • 屏蔽运维GPU集群的繁重负担(驱动/CUDA版本管理、机器运行管理、GPU坏卡管理),使得开发者专注于代码开发、聚焦业务目标的达成。

GPU实例的更多信息,请参见实例类型及使用模式

神经风格迁移教程

神经风格迁移是一种生成技术,主要用来合成两张图像,即从其中一张图像提取内容,另一张图像提取风格,以合成一张新图像。本示例通过使用TensorFlow Hub预置模型,完成任意图像的风格合成。

合成效果

内容图像 风格图像 合成图像
tensorflow_example_dog tensorflow_example_style_dog tensorflow_example_result_dog

前提条件

  • 诚邀您参与测试GPU实例。请提交工单申请参与测试,并在工单中附上以下信息:
    • 组织名称,例如您所在的公司名称。
    • 您的阿里云账号ID。
    • 您期望使用GPU实例的地域,例如华南1(深圳)。
    • 联系方式,例如您的手机号、邮箱或钉钉账号等。
    • 您的镜像大小。
  • 在GPU实例所在地域,完成以下操作:
  • 安装Serverless Devs和Docker
  • 配置Serverless Devs
  • 将需处理的音视频资源上传至在GPU实例所在地域的OSS Bucket中,且您对该Bucket中的文件有读写权限。具体步骤,请参见上传文件。权限相关说明,请参见修改存储空间读写权限

操作步骤

  1. 创建项目。
    s init devsapp/start-fc-custom-container-event-python3.9 -d fc-gpu-prj

    创建的项目目录如下所示。

    fc-gpu-prj
    ├── code
    │   ├── app.py        # 函数代码
    │   └── Dockerfile    # Dockerfile:将代码打包成镜像的Dockerfile
    ├── README.md
    └── s.yaml            # 项目配置:包含了镜像如何部署在函数计算
  2. 进入项目所在目录。
    cd fc-gpu-prj
  3. 按实际情况修改目录文件的参数配置。
    • 编辑s.yaml文件。

      YAML文件的参数详解,请参见YAML规范

      edition: 1.0.0
      name: container-demo
      access: default
      vars:
        region: cn-shenzhen
      services:
        customContainer-demo:
          component: devsapp/fc
          props:
            region: ${vars.region}
            service:
              name: tgpu_tf_service
              internetAccess: true
            function:
              name: tgpu_tf_func
              description: test gpu for tensorflow
              handler: not-used
              timeout: 600
              caPort: 9000
              memorySize: 16384
              gpuMemorySize: 8192
              instanceType: g1
              initializationTimeout: 30
              initializer: not-used
              runtime: custom-container
              customContainerConfig:
                image: registry.cn-shenzhen.aliyuncs.com/demo/gpu-tf-style-transfer_s:v0.1
              codeUri: ./code
            triggers:
              - name: httpTrigger
                type: http
                config:
                  authType: anonymous
                  methods:
                    - GET
    • 编辑app.py文件。

      示例如下:

      # -*- coding: utf-8 -*-
      # python2 and python3
      from __future__ import print_function
      from http.server import HTTPServer, BaseHTTPRequestHandler
      from tensorflow import keras
      from tensorflow.keras import layers
      from tensorflow.keras.models import Sequential
      import json
      import sys
      import logging
      import os
      import matplotlib.pyplot as plt
      import matplotlib as mpl
      import numpy as np
      import os
      import PIL
      import tensorflow as tf
      import pathlib
      import urllib.request
      
      class MyRequest(BaseHTTPRequestHandler):
          def upload(self, url, path):
              print("enter upload:", url)
              headers = {
                  'Content-Type': 'application/octet-stream',
                  'Content-Length': os.stat(path).st_size,
              }
              req = urllib.request.Request(url, open(path, 'rb'), headers=headers, method='PUT')
              urllib.request.urlopen(req)
      
          def tensor_to_image(self, tensor):
              tensor = tensor*255
              tensor = np.array(tensor, dtype=np.uint8)
              if np.ndim(tensor)>3:
                  assert tensor.shape[0] == 1
                  tensor = tensor[0]
              return PIL.Image.fromarray(tensor)
      
          def load_img(self, path_to_img):
              max_dim = 512
              img = tf.io.read_file(path_to_img)
              img = tf.image.decode_image(img, channels=3)
              img = tf.image.convert_image_dtype(img, tf.float32)
      
              shape = tf.cast(tf.shape(img)[:-1], tf.float32)
              long_dim = max(shape)
              scale = max_dim / long_dim
      
              new_shape = tf.cast(shape * scale, tf.int32)
      
              img = tf.image.resize(img, new_shape)
              img = img[tf.newaxis, :]
              return img
      
          def do_style_transfer(self):
              mpl.rcParams['figure.figsize'] = (12,12)
              mpl.rcParams['axes.grid'] = False
      
              # 需替换为您个人账号下的OSS,且您有可读写的权限。
              # 此处是读取您存储在OSS Bucket中的内容和样式图片。
              content_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://your_public_oss/YellowLabradorLooking_new.jpg')
              style_path = tf.keras.utils.get_file('kandinsky5.jpg','https://your_public_oss/popcorn.jpg')
      
              content_image = self.load_img(content_path)
              style_image = self.load_img(style_path)
      
              import tensorflow_hub as hub
              hub_model = hub.load('https://hub.tensorflow.google.cn/google/magenta/arbitrary-image-stylization-v1-256/2')
              stylized_image = hub_model(tf.constant(content_image), tf.constant(style_image))[0]
              path = "/tmp/stylized-image.png"
              self.tensor_to_image(stylized_image).save(path)
      
              # 需替换为您个人账号下的OSS,且您有可读写的权限。
              # 此处是将最后合成的图片存储至您的OSS Bucket。
              self.upload("https://your_public_oss/stylized-image.png", path)
      
              return "transfer ok"
      
          def reply(self, msg):
              data = {"result": msg}
              self.send_response(200)
              self.send_header("Content-type", "application/json")
              self.end_headers()
              self.wfile.write(json.dumps(data).encode())
      
          def do_GET(self):
              msg = self.do_style_transfer()
              self.reply(msg)
      
          def do_POST(self):
              msg = self.do_style_transfer()
              self.reply(msg)
      
      if __name__ == "__main__":
          host = ("0.0.0.0", 9000)
          server = HTTPServer(host, MyRequest)
          print("Starting server, listen at: %s:%s" % host)
          server.serve_forever()
    • 编辑Dockerfile文件。

      示例如下:

      FROM tensorflow/tensorflow:latest-gpu-jupyter
      WORKDIR /usr/src/app
      RUN apt-get update
      RUN apt-get install -y python3
      RUN apt-get install -y python3-pip
      RUN pip3 install matplotlib
      RUN pip install tensorflow_hub
      COPY . .
      CMD [ "python3", "-u", "/usr/src/app/app.py" ]
      EXPOSE 9000
  4. 构建镜像。
    s build --dockerfile ./code/Dockerfile
  5. 部署代码至函数计算
    s deploy
  6. 配置预留模式的实例。
    s provision put --target 1 --qualifier LATEST
  7. 查询预留模式的实例是否就绪。
    s provision get --qualifier LATEST

    如果查询到current参数为1,则说明GPU实例的预留模式已就绪,示例如下。

    [2021-12-06 03:14:28] [INFO] [S-CLI] - Start ...
    [2021-12-06 03:14:29] [INFO] [FC] - Getting provision: tgpu_tf_service.LATEST/tgpu_tf_func
    customContainer-demo: 
      serviceName:            tgpu_tf_service
      functionName:           tgpu_tf_func
      qualifier:              LATEST
      resource:               188077086902****#tgpu_tf_service#LATEST#tgpu_tf_func
      target:                 1
      current:                1
      scheduledActions:       (empty array)
      targetTrackingPolicies: (empty array)
  8. 调用函数。
    s invoke

    调用成功后,预期的返回结果如下。

    ...
    ========= FC invoke Logs end =========
    
    FC Invoke Result[Code: 200]:
    { result: 'transfer ok' }
    
    
    End of method: invoke

结果验证

您可通过在浏览器中访问以下域名,查看经过风格合成处理后的图片:

https://cri-zbtsehbrr8******-registry.oss-cn-shenzhen.aliyuncs.com/stylized-image.png

本域名仅为示例,需以实际情况为准。

对象检测教程

当多个对象同时出现时,需要使用对象检测技术针对感兴趣的对象构建矩型边框,并持续跟踪。对象检测应用通常用于大量不同类型对象的标记、识别。本示例通过使用OpenCV DNN,完成多对象检测功能。

检测效果

如下表所示,左列为需检测对象的原图,右列为经过OpenCV DNN处理后的对象检测结果图。结果图中会显示检测到的对象名称和准确率。

原始图片 识别对象
tensorflow_example_car_detect tensorflow_example_car_detect_result

前提条件

  • 诚邀您参与测试GPU实例。请提交工单申请参与测试,并在工单中附上以下信息:
    • 组织名称,例如您所在的公司名称。
    • 您的阿里云账号ID。
    • 您期望使用GPU实例的地域,例如华南1(深圳)。
    • 联系方式,例如您的手机号、邮箱或钉钉账号等。
    • 您的镜像大小。
  • 在GPU实例所在地域,完成以下操作:
  • 安装Serverless Devs和Docker
  • 配置Serverless Devs
  • 编译OpenCV。
    OpenCV需要自行编译以使用GPU加速,编译方式如下:
  • 将需处理的音视频资源上传至在GPU实例所在地域的OSS Bucket中,且您对该Bucket中的文件有读写权限。具体步骤,请参见上传文件。权限相关说明,请参见修改存储空间读写权限

操作步骤

  1. 创建项目。
    s init devsapp/start-fc-custom-container-event-python3.9 -d fc-gpu-prj

    创建的项目目录如下所示。

    fc-gpu-prj
    ├── code
    │   ├── app.py        # 函数代码
    │   └── Dockerfile    # Dockerfile:将代码打包成镜像的Dockerfile
    ├── README.md
    └── s.yaml            # 项目配置:包含了镜像如何部署在函数计算
  2. 进入项目所在目录。
    cd fc-gpu-prj
  3. 按实际情况修改目录文件的参数配置。
    • 编辑s.yaml文件。

      YAML文件的参数详解,请参见YAML规范

      edition: 1.0.0
      name: container-demo
      access: default
      vars:
        region: cn-shenzhen
      services:
        customContainer-demo:
          component: devsapp/fc
          props:
            region: ${vars.region}
            service:
              name: tgpu_object_detect_service
              internetAccess: true
            function:
              name: tgpu_object_detect_func
              description: test gpu for opencv
              handler: not-used
              timeout: 600
              caPort: 9000
              memorySize: 16384
              gpuMemorySize: 8192
              instanceType: g1
              initializationTimeout: 30
              initializer: not-used
              runtime: custom-container
              customContainerConfig:
                image: registry.cn-shenzhen.aliyuncs.com/demo/gpu-object-detect_s:v0.1
              codeUri: ./code
            triggers:
              - name: httpTrigger
                type: http
                config:
                  authType: anonymous
                  methods:
                    - GET
    • 编辑app.py文件。

      示例如下:

      # -*- coding: utf-8 -*-
      # python2 and python3
      
      from __future__ import print_function
      from http.server import HTTPServer, BaseHTTPRequestHandler
      import json
      import sys
      import logging
      import os
      import numpy as np
      import cv2
      import urllib.request
      
      class MyRequest(BaseHTTPRequestHandler):
          def download(self, url, path):
              print("enter download:", url)
              f = urllib.request.urlopen(url)
              with open(path, "wb") as local_file:
                  local_file.write(f.read())
      
          def upload(self, url, path):
              print("enter upload:", url)
              headers = {
                  'Content-Type': 'application/octet-stream',
                  'Content-Length': os.stat(path).st_size,
              }
              req = urllib.request.Request(url, open(path, 'rb'), headers=headers, method='PUT')
              urllib.request.urlopen(req)
      
          def core(self):
              CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
                     "bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
                     "dog", "horse", "motorbike", "person", "pottedplant", "sheep",
                     "sofa", "train", "tvmonitor"]
              COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))
      
              print("[INFO] loading model...")
              prototxt = "m.prototxt.txt"
              model = "m.caffemodel"
              net = cv2.dnn.readNetFromCaffe(prototxt, model)
      
              msg = ""
              mode = ""
              if not cv2.cuda.getCudaEnabledDeviceCount():
                  msg = "No CUDA-capable device is detected |"
              else:
                  msg = "CUDA-capable device supported |"
                  net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
                  net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
      
              path = "/tmp/target.jpg"
              self.download("https://your_public_oss/target.jpg", path)  # 需替换为您个人账号下的OSS,且您有可读写的权限。此处是读取您存储在OSS Bucket中的图片。
              image = cv2.imread(path)
              (h, w) = image.shape[:2]
              blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 0.007843, (300, 300), 127.5)
      
              print("[INFO] computing object detections...")
              net.setInput(blob)
              detections = net.forward()
      
              # loop over the detections
              for i in np.arange(0, detections.shape[2]):
                  confidence = detections[0, 0, i, 2]
                  if confidence > 0.2:
                      idx = int(detections[0, 0, i, 1])
                      box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
                      (startX, startY, endX, endY) = box.astype("int")
                      cv2.rectangle(image, (startX, startY), (endX, endY), COLORS[idx], 2)
                      x = startX + 10 if startY - 15 < 15 else startX
                      y = startY - 15 if startY - 15 > 15 else startY + 20
                      label = "{}: {:.2f}%".format(CLASSES[idx], confidence * 100)
                      cv2.putText(image, label, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[idx], 2)
                      print("[INFO] {}".format(label))
              cv2.imwrite(path, image)
      
              self.upload("https://your_public_oss/target2.jpg", path)    # 需替换为您个人账号下的OSS,且您有可读写的权限。此处是向您的OSS Bucket中上传图片。
      
              msg = msg + " process image ok!"
      
              data = {'result': msg}
              self.send_response(200)
              self.send_header('Content-type', 'application/json')
              self.end_headers()
              self.wfile.write(json.dumps(data).encode())
      
          def do_GET(self):
              self.core()
      
          def do_POST(self):
              self.core()
      
      if __name__ == '__main__':
          host = ('0.0.0.0', 9000)
          server = HTTPServer(host, MyRequest)
          print("Starting server, listen at: %s:%s" % host)
          server.serve_forever()
    • 编辑Dockerfile文件。

      示例如下:

      FROM julianassmann/opencv-cuda:cuda-10.2-opencv-4.2
      WORKDIR /usr/src/app
      RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
      RUN apt-get clean
      RUN apt-get update --fix-missing
      RUN apt-get install -y build-essential
      RUN apt-get install -y python3
      COPY . .
      CMD [ "python3", "-u", "/usr/src/app/app.py" ]
      EXPOSE 9000
  4. 下载以下文件,并存放至/code目录下。
  5. 构建镜像。
    s build --dockerfile ./code/Dockerfile
  6. 部署代码至函数计算
    s deploy
  7. 配置预留模式的实例。
    s provision put --target 1 --qualifier LATEST
  8. 查询预留模式的实例是否就绪。
    s provision put --target 1 --qualifier LATEST

    如果查询到current参数为1,则说明GPU实例的预留模式已就绪,示例如下。

    [2021-12-07 02:20:55] [INFO] [S-CLI] - Start ...
    [2021-12-07 02:20:55] [INFO] [FC] - Getting provision: tgpu_object_detect_service.LATEST/tgpu_object_detect_func
    customContainer-demo: 
      serviceName:            tgpu_object_detect_service
      functionName:           tgpu_object_detect_func
      qualifier:              LATEST
      resource:               188077086902****#tgpu_object_detect_service#LATEST#tgpu_object_detect_func
      target:                 1
      current:                1
      scheduledActions:       (empty array)
      targetTrackingPolicies: (empty array)
  9. 调用函数。
    s invoke

    调用成功后,预期的返回结果如下。

    ...
    ========= FC invoke Logs end =========
    
    FC Invoke Result[Code: 200]:
    { result: 'CUDA-capable device supported | process image ok!' }
    
    
    End of method: invoke

结果验证

您可通过在浏览器中访问以下域名,查看目标识别处理后的图片:

https://cri-zbtsehbrr8******-registry.oss-cn-shenzhen.aliyuncs.com/target2.jpg

本域名仅为示例,需以实际情况为准。