TensorFlow介绍

TensorFlow是一个基于数据流编程(Data Flow Programming)的符号数学系统,被广泛应用于各类机器学习算法的编程实现。其前身是谷歌的神经网络算法库DistBelief。TensorFlow拥有多层级结构,可部署于各类服务器、PC终端和网页,支持GPU和TPU高性能数值计算,因此也被广泛应用于谷歌内部的产品开发和各领域的科学研究。

TensorFlow可作为一个端到端开源机器学习框架,拥有全面而灵活的生态系统,其中包含各种工具、库和社区资源,可助力机器学习技术的发展,并使开发者能够轻松地构建和部署由机器学习提供支持的应用。TensorFlow的标志如下所示。

TensorFlow与Serverless架构结合_数据集

TensorFlow项目的标志 

TensorFlow是采用数据流图(Data Flow Graph)计算的,所以在使用该框架时首先需要创建一个数据流图,然后再将数据(数据以张量的形式存在)放在数据流图中计算。节点在图中表示数学操作,图中的边表示在节点间相互联系的多维数据数组,即张量(Tensor)。训练模型时,张量会不断地从数据流图中的一个节点流动(Flow)到另一个节点,这就是TensorFlow名字的由来。

TensorFlow有3个重要特点,即张量、计算图(Graph)、会话(Session)。其中,张量有多种,零阶张量为纯量或标量,也就是一个数值,如[1];一阶张量为向量,比如一维的[1, 2, 3];二阶张量为矩阵,比如二维的[[1, 2, 3]、[4, 5, 6]、[7, 8, 9]]。以此类推还有三阶张量等。张量从流图的一端流动到另一端的计算过程,生动形象地描述了复杂数据在人工神经网中的流动、传输、分析和处理模式。

计算图相当于盖房子之前设计的图纸,使得预先定义好的张量根据运算逻辑逐步运行。我们根据定义好的目标函数对计算图进行整体的优化。计算图具有可并发、可分发、可优化、可移植的优点。

有了张量和计算图,就可以构建模型了。但是,这样的模型怎么运行呢?虽然计算图已经有了,但是这个图是静态的,它的数据入口也没有流入数据。TensorFlow中提供了一个叫tf.Session的会话机制,它为任务提供了计算环境,但需要为这个环境提供输入数据。例如下面的代码块:

import tensorflow as tf
# 初始化Session
sess = tf.Session()
# 以float32数据类型创建常量w和b
w = tf.constant(3, dtype=tf.float32)
b = tf.constant(4, dtype=tf.float32)
# 创建x的占位符,因为是一个标量,所以shape为空
x = tf.placeholder(tf.float32, [])
# 前面三个Tensor表示节点,创建它们之间联系来表示边
y = w * x + b
# 运行计算图
print(sess.run(y, {x: 6}))
# 每次运行后需关闭Session sess.close()
目前,TensorFlow拥有支持多种语言的API。以Python语言为例,官方目前在Pypi中提供了多个软件包供选择。
·tensorflow:支持CPU和GPU的最新稳定版(适用于Ubuntu和Windows)。
·tf-nightly:预览build(不稳定)。Ubuntu和Windows均支持GPU。
·tensorflow==1.15:TensorFlow 1.x的最终版本。
TensorFlow可以通过以下指令进行安装:
pip install --user --upgrade tensorflow  # install in $HOME

安装完成之后,验证安装效果:

python-c "import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([1000, 1000])))"

TensorFlow实践:基于人工智能的衣物区分

Fashion MNIST数据集是由Zalando(德国的一家时尚科技公司)旗下研究部门提供的一个替代MNIST手写数字集的图像数据集。Fashion MNIST的大小、格式和训练/测试数据划分与原始的MNIST完全一致,所以开发者通常情况下可以直接用它来测试机器学习和深度学习算法性能,且不需要改动任何代码。

Fashion MNIST数据集包含10个类别的70 000个灰度图像(60 000/10 000的训练/测试数据划分),这些图像以低分辨率(28×28像素)展示了单件衣物,如下所示。

TensorFlow与Serverless架构结合_数据_02

Fashion MNIST数据集展示 

本案例将以该数据集为例,通过TensorFlow框架实现图像分类。

1.开发前准备

本案例中使用的tf.keras是TensorFlow用来构建和训练模型的高级API。在进行图像分类案例实现之前,先导入算法相关的库,包括TensorFlow、keras等。

# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras
# Helper libraries
import numpy as np
import matplotlib.pyplot as plt

在本案例中,可以使用60 000个图像训练网络,使用10 000个图像评估网络对图像分类的准确率。开发者可以直接从TensorFlow中导入和加载Fashion MNIST数据:

fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

其中:

·train_images和train_labels数组是训练集,即模型用于学习的数据。

·test_images和test_labels是测试集,被用来对模型进行测试。

数据集加载完成之后,我们还需要对数据进行预处理。这里将这些值缩小至0~1之间,然后将其输入神经网络模型:

train_images=train_images / 255.0

test_images=test_images / 255.0

神经网络的基本组成部分是神经网络层。神经网络层会从向其传送的数据中提取表示形式。大多数网络层(如tf.keras.layers.Dense)拥有在训练期间学习到的参数。这里定义一个简单的神经网络模型:

model = keras.Sequential([keras.layers.Flatten(input_shape=(28, 28)),    keras.layers.Dense(128, activation='relu'),    keras.layers.Dense(10) ])

该网络的第一层tf.keras.layers.Flatten将图像格式从二维数组(28×28像素)转换成一维数组(28×28=784像素)。

该层意味着将图像数据展开成向量形式,将特征空间转换成后面全连接网络层的输入。

该层没有要学习的参数,只会重新格式化数据。

展平图像后,网络包括两个tf.keras.layers.Dense层。

它们是全连接神经层。第一个Dense层以128个神经元作为隐藏层,第二个(也是最后一个)层是输出层,最终输出长度为10的数组。每个节点都包含一个得分,用来表示当前图像属于10个类中的哪一类。

之后对模型进行训练,模型训练前需要定义好损失函数、优化器、评价指标等。

·损失函数:用于测量模型在训练期间的准确率。开发者通常希望最小化此函数,以便将模型“引导”到正确的方向上。

·优化器:决定模型如何根据其看到的数据和自身的损失函数进行更新。

·评价指标:用于监控训练和测试步骤。以下示例使用的准确率,即被正确分类的图像的比例。

model.compile(optimizer='adam',  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),  metrics=['accuracy'])

2.模型训练

作为TensorFlow中更为高级的API,Keras极大地简化了训练代码。通常情况下,训练只需要一行代码:

model.fit(train_images, train_labels, epochs=10)

其中,train_images、train_labels表示训练的图像数据和对应的标签,epochs表示训练迭代的次数。

启动训练后,打印如下日志:

Epoch 1/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.4924 - accuracy: 0.8265 Epoch 2/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.3698 - accuracy: 0.8669 Epoch 3/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.3340 - accuracy: 0.8781 Epoch 4/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.3110 - accuracy: 0.8863 Epoch 5/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.2924 - accuracy: 0.8936 Epoch 6/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.2776 - accuracy: 0.8972 Epoch 7/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.2659 - accuracy: 0.9021 Epoch 8/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.2543 - accuracy: 0.9052 Epoch 9/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.2453 - accuracy: 0.9084 Epoch 10/10 1875/1875 [==========] - 3s 1ms/step - loss: 0.2366 - accuracy: 0.9122

3.模型验证

在模型训练期间,系统会显示损失和准确率指标。此模型在训练集上的准确率在0.91(或91%)左右,但在训练集上的表现并不代表在实际场景中的表现。于是,我们需要对模型进行验证。

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2) print('Test accuracy:', test_acc)

验证结果为:

313/313 - 0s - loss: 0.3726 - accuracy: 0.8635 Test accuracy: 0.8634999990463257

其中,test_images、test_labels为测试集对应的图像数据和标签,最终验证模型的准确率为0.86,相比训练时的准确率降低4%。训练准确率和测试准确率之间的差距代表过拟合。过拟合是指机器学习模型在新的输入数据上的表现不如在训练数据上的表现。

与Serverless架构结合:目标检测系统

ImageAI是一个Python库,旨在使开发人员使用简单的几行代码构建具有包含深度学习和计算机视觉功能的应用程序和系统。ImageAI本着编程简单的原则,支持最先进的机器学习算法,用于图像预测、自定义图像预测、物体检测、视频检测、视频对象跟踪和图像预测训练。目前,该库除支持使用在ImageNet-1000数据集上训练的4种机器学习算法进行图像预测和训练,还支持使用在COCO数据集上训练的RetinaNet进行对象检测、视频检测和对象跟踪等。

ImageAI依赖TensorFlow 1.4.0及以上版本。通过对TensorFlow的进一步封装,ImageAI提供用于图像预测的4种算法,包括SqueezeNet、ResNet、InceptionV3和DenseNet。我们可以通过非常简单的方法实现图像预测、目标检测等任务。

1.本地开发

首先明确要进行目标检测的图像,如下所示。

TensorFlow与Serverless架构结合_数据_03

目标检测图像 

然后根据ImageAI提供的开发文档,实现以下代码:

# index.py 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))

完成代码开发之后,可以通过执行该文件进行效果测试:

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,预测时间较慢,准确度最高)。

2.部署到Serverless架构

在本地完成ImageAI的基本测试之后,我们可以将目标检测模型部署到Serverless架构。在Serverless架构上,该模型的基本运行流程如下所示。

TensorFlow与Serverless架构结合_数据集_04

目标检测模型的基本运行流程 

按照以上流程,以阿里云函数计算和轻量级Python Web框架Bottle为例,可以分别实现页面代码以及逻辑代码。其中,对于逻辑代码,这里主要是将在本地开发案例代码稍做修改,与Bottle框架进行结合:

# -*- coding: utf-8 -*- 
from imageai.Prediction import ImagePrediction 
import base64 
import bottle 
import random 
import json 
# 随机字符串 
randomStr = lambda num=5: "".join(random.sample('abcdefghijklmnopqrstuvwxyz', num)) 
# 模型加载 
prediction = ImagePrediction() 
prediction.setModelTypeAsResNet() 
prediction.setModelPath("/mnt/auto/model/resnet50_weights_tf_dim_ordering_tf_kernels.h5") 
prediction.loadModel() 
@bottle.route('/image_prediction', method='POST') 
def getNextLine():    
    postData = json.loads(bottle.request.body.read().decode("utf-8"))    
    image = postData.get("image", None)    
    image = image.split("base64,")[1]    
    # 图片获取    
    imagePath = "/tmp/%s" % randomStr(10)    
    with open(imagePath, 'wb') as f:        
        f.write(base64.b64decode(image))    
        # 内容预测    
        result = {}    
        predictions, probabilities = prediction.predictImage(imagePath, result_count=5)    
        for eachPrediction, eachProbability in zip(predictions, probabilities):        
            result[str(eachPrediction)] = str(eachProbability)    
            return result @bottle.route('/', method='GET') 

def getNextLine():    
    return bottle.template('./html/index.html') 

app = bottle.default_app() 

if __name__ == "__main__":    
    bottle.run(host='localhost', port=8080)

对于页面代码,这里主要是通过HTML与JavaScript实现一个便于测试的前端页面,如下所示。

TensorFlow与Serverless架构结合_数据_05

前端预测页面 

为了快速进行项目的构建、依赖的安装、最终的项目部署,以及对项目进行观测,我们可以通过Serverless Devs开发者工具进行项目的托管。此时,我们需要根据Serverless Devs开发者工具相关规范,进行相关的资源描述以及行为描述:

edition: 1.0.0     
name: imageAi                                                     
# 项目名称 
access: 'default'                                                 
# 密钥别名 
services:    
imageAi:                                                     
# 服务名称        
component: devsapp/fc        
actions:                                                  
# 自定义执行逻辑            
pre-deploy:                                           
# 在部署之前运行                
- run: s build --use-docker                       
# 要运行的命令行                    
path: ./                                      
# 命令行运行的路径            
post-deploy:                                          
# 在部署之后运行                
- run: s nas command mkdir /mnt/auto/.s                    
path: ./                                      
# 命令行运行的路径                
- run: s nas upload -r -n ./.s/build/artifacts/ai-cv-image-pred-iction/server/.s/python /mnt/auto/.s/python    
# 要运行的命令行                     
path: ./                                    
# 命令行运行的路径                
- run: s nas upload -r -n ./src/model /mnt/auto/model 
# 要运行的命令行                     
path: ./                                     
# 命令行运行的路径        
props:                                                    
# 组件的属性值            
region: cn-hangzhou            
service:                
  name: ai-cv-image-prediction                
  description: 图片目标检测服务                
  nasConfig: auto                
  vpcConfig: auto                
  logConfig: auto            
  function:                
    name: server                
    description: 图片目标检测                
    runtime: python3                
    codeUri: ./src                
    handler: index.app                
    memorySize: 3072                
    timeout: 60                
    environmentVariables:                    
      PYTHONUSERBASE: /mnt/auto/.s/python            
      triggers:                
        - name: httpTrigger                    
        type: http                    
        config:                        
          authType: anonymous                       
          methods:                            
            - GET                            
            - POST                            
            - PUT            
            customDomains:                
              - domainName: auto                    
              protocol: HTTP                    
              routeConfigs:                        
                - path: /*

从上面描述的Yaml文件中不难发现,actions中的定义包括在项目部署开始前,通过Docker进行项目的构建,在项目部署完成后,在NAS中创建文件夹,上传对应的文件等。

首先是通过Serverless Devs开发者工具拉取Docker镜像,进行依赖安装。值得一提的是,在进行依赖安装之前,我们需要明确所需要的具体依赖内容:

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

bottle==0.12.19

完成之后,通过s build--use-docker命令实现项目的构建,如下所示。

TensorFlow与Serverless架构结合_目标检测_06

项目构建日志 

依赖安装完成之后,执行项目部署操作。部署完成之后,将相关文件上传到NAS,待一切都成功后,系统会进行如下所示的结果提示。

此时,根据工具返回的测试地址,可以在浏览器中打开网页,并上传之前准备好的图像进行预测,可以看到所示的结果。

可以看到,预测结果和前面在本地执行的结果是一致的。至此,已经成功将本地的TensorFlow项目(确切来说是基于TensorFlow开发的ImageAI项目)部署到Serverless架构,并通过Serverless架构对外暴露可视化的操作页面,以便于测试和白屏化操作。

TensorFlow与Serverless架构结合_数据_07

项目部署完成的日志 

TensorFlow与Serverless架构结合_目标检测_08

 目标检测模型的测试 

3.项目优化

本项目采用的是阿里云函数计算Python运行时。相对来说,该运行时与人工智能项目相结合存在一定的复杂度。

·TensorFlow等依赖比较大,而通常情况下,函数计算可以上传的代码包大小在100MB以下,这就导致模型与依赖很难同步部署到Serverless架构。虽然本项目采用了NAS作为挂载盘,完成了大依赖包以及模型文件的上传和加载,但是在开发和部署环节相对比较复杂,比较难上手。针对这一问题,可以通过容器镜像等运行时降低复杂度。

·由于FaaS平台的环境在很多情况下和开发者的本地环境有一定差异,因此一些依赖无法跨平台运行,需要准备和FaaS平台一致的环境,进行依赖的安装和项目的打包。类似Serverless Devs的开发者工具虽然能协助依赖安装和项目打包,但是在一定程度上还是相对复杂的。此时,我们可以通过容器镜像等运行时降低这一部分的复杂度。

·深度学习项目如果有GPU实例的支持会让训练和推理的速度大幅度提升,而本实例使用的是CPU实例进行的业务逻辑处理(包括预测部分),如果这一部分可以替换成GPU实例,将会大大提高训练和推理速度,获得更好的客户端体验。

·在部署完成该项目,首次使用该项目时,不难发现网页打开的速度以及训练和推理的速度相对较慢,这充分说明冷启动的存在,并对项目产生了影响。为了在生产过程中降低冷启动带来的危害,可以适当进行实例的预留,以最大限度地保证项目性能。

·在该项目中,网页等静态资源和预测等业务逻辑全部在函数中处理的。在复杂场景下,这种做法极有可能加剧Serverless架构的冷启动,所以相对科学的做法是将页面等静态资源部署到对象存储等云产品中,函数计算仅作为计算平台,并对外暴露API以提供相对应的能力。