Python 人工智能实战:智能边缘计算
1.背景介绍
智能边缘计算简介
随着互联网、物联网、人工智能、云计算等新型技术的发展,人们对计算能力提出了更高的要求。在这种背景下,智能边缘计算(IEC)技术应运而生。相对于云端服务器和传统IT平台,智能边缘计算采用较低成本、资源有限、可靠性高的特点,能够满足用户日益增长的计算需求。在实际应用中,智能边缘计算可以实现对视频流的分析、监控、识别、预测等任务,极大地促进了生产力的提升。然而,面临着各种各样的问题和挑战。例如,如何保障计算环境的安全性、可靠性以及性能,如何有效利用计算资源、减少功耗,如何合理配置计算资源,如何进行分布式并行计算等等。本文将以图像分类任务为例,结合计算机视觉、机器学习、分布式计算等相关知识点,对智能边缘计算的原理、方案、流程、案例等方面进行深入剖析,并通过实例介绍如何开发基于Docker的智能边缘计算框架。
图像分类任务介绍
图像分类任务属于计算机视觉领域的基本任务之一。其主要目标是根据给定的图像或视频序列,自动识别其中的内容并将其分组到不同的类别中。图像分类任务具有广泛的应用场景,如广告图像的分类、图像搜索引擎的功能模块、对象检测、人脸识别等。在实际的智能边缘计算场景中,图像分类任务被用来对摄像头获取的视频进行分析,从而实现对人类的行为、物体及环境的感知与分析。
假设有一个场景,其中有两个人站在一个圆桌前,左侧观察者在远处拍摄视频,右侧观察者在近处看着自己。由于主角的身份,左侧观察者只需要了解到发生了什么,而右侧观察者则需要对自己在场景中所看到的内容进行分类。比如,左侧观察者可能只想知道自己的身体状态是否正常,而右侧观察者则需要知道看到的是不是他人。
如下图所示,左侧观察者的摄像头拍摄到了左手的动作,右侧观察者看到了一个人,这个人的背景颜色为白色、服饰颜色为黄色,手上戴着耳机、手机等物品。右侧观察者可以用图片、视频、语音等多种方式了解到这些信息,但最方便的方法还是通过计算机的帮助进行分类。因此,智能边缘计算技术的出现意味着图像分类任务可以被应用到更加复杂、敏感的场景中。
Docker简介
Docker是一个开源的应用容器引擎,让开发人员可以打包他们的应用以及依赖包到一个轻量级、可移植的镜像文件中,然后发布到任何流行的 Linux 或 Windows 机器上运行。开发人员可以通过Dockerfile来指定创建镜像时的参数。通过管理工具(docker compose、swarm mode等)可以很容易的实现集群部署。本文涉及到的智能边缘计算框架都将基于Docker技术,因此需要对Docker有一定了解。
使用Docker部署图像分类Web服务
为了展示智能边缘计算的图像分类Web服务的开发过程,下面将通过案例展示如何使用Python、Flask、Tensorflow、OpenCV以及Docker完成图像分类Web服务的部署。
准备工作
-
安装Docker环境。首先需要安装Docker环境,这里推荐安装最新版本的Docker Desktop,并设置好Docker Hub帐号进行免费的Docker镜像仓库服务。
-
安装Flask、OpenCV、Tensorflow。这里需要注意,目前有几个地方需要配置相应的环境变量才能顺利安装以上三项依赖库,具体操作如下:
- Flask:
pip install flask - OpenCV: 需要先下载安装OpenCV库,再通过环境变量配置OpenCV路径。
- Tensorflow: 在Anaconda prompt命令行下输入
conda install tensorflow即可安装Tensorflow环境。
- Flask:
-
创建文件夹结构。首先创建一个名为“ImageClassification”的文件夹作为项目根目录,然后创建以下子目录:
- app.py : 用于编写图像分类服务的代码
- templates : 用于存放前端页面的html模板
- static : 用于存放前端页面的css、js等静态资源
最终的目录结构应该如下图所示:
编写图像分类服务代码
接下来,我们将编写app.py 文件,该文件用于编写图像分类服务的代码,包括:
-
初始化模型:首先,我们需要初始化一个Tensorflow神经网络模型,在本案例中,我们使用VGG16模型。然后加载训练好的权重文件,该文件可以从Google Drive下载。
-
定义前端路由:前端发送POST请求到后端的**/predict** 接口,后端接收到请求后返回预测结果。
-
定义后端API接口:通过Flask提供的@app.route()装饰器来定义前端向后端发送POST请求的路由。
-
图像处理:读取前端发送的Base64编码后的图像数据,解码为OpenCV的Mat格式,然后通过模型进行预测。
-
返回结果:把预测结果转换为JSON格式的数据,并发送回前端。
import base64
from flask import Flask, request, jsonify
import cv2
import numpy as np
import tensorflow as tf
import os
# 初始化模型
model = tf.keras.applications.vgg16.VGG16(weights='imagenet', include_top=True)
graph = tf.get_default_graph()
# Flask初始化
app = Flask(__name__)
# 设置静态文件访问路由
@app.route('/static/<path:filename>')
def static_file(filename):
return app.send_static_file(filename)
# 设置后端API接口
@app.route('/predict', methods=['POST'])
def predict():
# 获取图像base64编码字符串
image_string = request.form['image']
# 解码图像数据
encoded_data = str.encode(image_string)
nparr = np.frombuffer(encoded_data, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# 模型预测
with graph.as_default():
pred = model.predict(np.expand_dims(cv2.resize(img, (224, 224)), axis=0))
results = decode_predictions(pred)[0]
result = {}
for i in range(len(results)):
label = '{}: {:.2f}%'.format(results[i][1], results[i][2]*100)
if i == 0:
result['class'] = label
else:
result[label] = ''
# 返回预测结果
response = {'success': True,'result': [result]}
return jsonify(response), 200
if __name__ == '__main__':
port = int(os.environ.get("PORT", 5000))
app.run(debug=False, host='0.0.0.0', port=port)
代码解读
编写前端HTML页面
接下来,我们将编写前端页面,包括:
-
HTML结构:定义前端页面的HTML结构,包括表单字段、按钮控件、显示预测结果的表格、图片上传控件等。
-
JavaScript脚本:编写JavaScript脚本,监听前端页面的输入事件,并调用后端的**/predict** 接口进行图像分类。
-
CSS样式:添加CSS样式,美化前端页面的外观。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Image Classification</title>
<!-- 添加CSS样式 -->
<style>
body{
text-align: center;
}
input[type=file]{
display: inline-block;
margin: auto;
padding: 10px;
font-size: 1em;
width: 20%;
border: none;
background-color: lightblue;
color: white;
border-radius: 5px;
cursor: pointer;
}
button{
display: inline-block;
margin: auto;
padding: 10px;
font-size: 1em;
width: 20%;
border: none;
background-color: orange;
color: white;
border-radius: 5px;
cursor: pointer;
}
table{
margin: auto;
width: 50%;
padding: 10px;
border-collapse: collapse;
border-spacing: 0;
}
td{
padding: 10px;
vertical-align: top;
}
th{
background-color: lightgray;
font-weight: bold;
text-align: left;
padding: 10px;
}
/*响应式布局*/
@media screen and (max-width: 768px){
input[type=file]{
width: 40%;
}
button{
width: 40%;
}
table{
width: 90%;
}
h2{
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<h1>Image Classification Service</h1>
<p><strong>Note:</strong> The uploaded images will be automatically classified after being selected.</p>
<div id="upload">
<input type="file" name="files[]" accept="image/*" multiple />
<button οnclick="classifyImages()">Predict</button>
</div>
<table id="result"></table>
<!-- jQuery引用 -->
<script src="//code.jquery.com/jquery-3.5.1.min.js"></script>
<!-- 添加JavaScript脚本 -->
<script>
// 定义全局变量
var fileInput = $('input[type=file]'); // 文件选择输入框
var submitBtn = $('button'); // 提交按钮
var filesArr = []; // 选中的文件数组
var formData = new FormData(); // form表单数据
// 监听文件选择变化
fileInput.on('change', function(){
$.each(this.files, function(index, item){
filesArr.push(item);
});
});
// 将选中的文件追加到FormData对象中
function classifyImages(){
for(var i=0; i<filesArr.length; i++){
formData.append('images[]', filesArr[i]);
}
// 发起POST请求
$.ajax({
url:'http://localhost:5000/predict',
method:"POST",
data:formData,
contentType:false,
cache:false,
processData:false,
success:function(res){
console.log(res);
// 清空原有的表格数据
$('#result tbody').empty();
// 填充新的表格数据
for(var key in res.result[0]){
var value = res.result[0][key];
if(key === "class"){
$('<tr>').append($('<th>', {text: key})).appendTo('#result tbody')
$('<td>').append($('<span>', {
text: value,
style: 'font-size: 20px; font-weight: bold;'
})).appendTo('#result tbody')
}else{
$('<tr>').append($('<th>', {text: key})).appendTo('#result tbody')
$('<td>').append($('<span>', {text: value})).appendTo('#result tbody')
}
}
},
error:function(err){
console.error(err);
}
})
};
</script>
</body>
</html>
代码解读
Dockerfile描述
最后,我们需要构建Docker镜像,将我们的Python Flask Web服务和相关依赖打包成一个镜像,供其他设备(如边缘计算设备)进行部署。
Dockerfile的语法比较简单,有如下几步:
- FROM 指定基础镜像,这里使用的是Python的Alpine版本。
- RUN 更新系统软件包并安装编译环境。
- COPY 将本地代码复制到镜像。
- WORKDIR 指定工作目录。
- ENV 设置环境变量。
- EXPOSE 指定容器对外开放的端口。
- CMD 设置启动命令。
FROM python:3.7-alpine
RUN apk update && apk add build-base && \
apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev
COPY. /app
WORKDIR /app
ENV FLASK_APP=app.py
ENV FLASK_ENV=development
EXPOSE 5000
CMD ["flask", "run"]
代码解读
配置Docker Compose
虽然我们已经成功创建了Docker镜像,但是还没有部署到服务器或者边缘计算设备上。接下来,我们将通过配置文件(.yml)的方式来描述Docker的部署环境,配置文件的内容包含:
- version: 版本号。
- services: 服务列表。
- volumes: 数据卷列表。
- networks: 网络列表。
services部分描述了要部署的容器及其配置,包括:
- build: 从Dockerfile生成镜像,如果不存在会自动构建镜像。
- ports: 映射端口。
- environment: 设置环境变量。
- depends_on: 指定依赖关系,确保服务启动顺序正确。
version: '3'
services:
classification:
container_name: classification
restart: always
working_dir: /app
ports:
- "5000:5000"
expose:
- "5000"
environment:
FLASK_ENV: development
command: sh -c "flask run --host=0.0.0.0"
volumes:
-./:/app
networks:
- classification_network
volumes:
app-volume:
networks:
classification_network:
代码解读
至此,整个部署环境就完成了,可以使用如下命令来启动容器:
docker-compose up -d
代码解读
