赞
踩
文末福利:开发者藏经阁
可能有些同学学习机器学习的时候比较迷茫,不知道该怎么上手,看了很多经典书籍介绍的各种算法,但还是不知道怎么用它来解决问题,就算知道了,又发现需要准备环境、准备训练和部署的机器,啊,好麻烦。
今天,我来给大家介绍一种容易上手的方法,给你现成的样本和代码,按照步骤操作,就可以在自己的 Mac 上体验运用机器学习的全流程啦~~~
下面的 Demo, 最终的效果是给定一张图片,可以预测图片的类别。比如我们训练模型用的样本是猫啊狗啊,那模型能学到的认识的就是猫啊狗啊, 如果用的训练样本是按钮啊搜索框啊,那模型能学到的认识的就是这个按钮啊搜索框啊。
如果想了解用机器学习是怎么解决实际问题的,可以看这篇:如何使用深度学习识别UI界面组件?(https://juejin.im/post/5e781a78e51d45271c3016db)从问题定义、算法选型、样本准备、模型训练、模型评估、模型服务部署、到模型应用都有介绍。
NO.1
环境准备
安装 Anaconda
下载地址:https://www.anaconda.com/products/individual
安装成功后,在终端命令行执行以下命令,使环境变量立即生效:
$ source ~/.bashrc
可以执行以下命令,查看环境变量
$ cat ~/.bashrc
可以看到 anaconda 的环境变量已经自动添加到 .bashrc 文件了
执行以下命令:
$ conda list
可以看到 Anaconda 中有很多已经安装好的包,如果有使用到这些包的就不需要再安装了,python 环境也装好了。
注意:如果安装失败,重新安装,在提示安装在哪里时,选择「更改安装位置」,安装位置选择其他地方不是用默认的,安装在哪里自己选择,可以放在「应用程序」下。
安装相关依赖
anaconda 中没有 keras、tensorflow 和 opencv-python, 需要单独安装。
- $ pip install keras
- $ pip install tensorflow
- $ pip install opencv-python
NO.2
样本准备
这里只准备了 4 个分类:button、keyboard、searchbar、switch, 每个分类 200 个左右的样本。
NO.3
模型训练
开发训练逻辑
新建一个项目 train-project, 文件结构如下:
- .
- ├── CNN_net.py
- ├── dataset
- ├── nn_train.py
- └── utils_paths.py
入口文件代码如下,这里的逻辑是将准备好的样本输入给图像分类算法 SimpleVGGNet, 并设置一些训练参数,例如学习率、Epoch、Batch Size, 然后执行这段训练逻辑,最终得到一个模型文件。
- # nn_train.py
- from CNN_net import SimpleVGGNet
- from sklearn.preprocessing import LabelBinarizer
- from sklearn.model_selection import train_test_split
- from sklearn.metrics import classification_report
-
- from keras.optimizers import SGD
- from keras.preprocessing.image import ImageDataGenerator
- import utils_paths
- import matplotlib.pyplot as plt
- from cv2 import cv2
- import numpy as np
- import argparse
- import random
- import pickle
-
- import os
-
- # 读取数据和标签
- print("------开始读取数据------")
- data = <a href="https://www.alibabacloud.com/help/zh/doc-detail/126314.htm?#h2-3-python-3">]
- labels = []
-
- # 拿到图像数据路径,方便后续读取
- imagePaths = sorted(list(utils_paths.list_images('./dataset')))
- random.seed(42)
- random.shuffle(imagePaths)
-
- image_size = 256
- # 遍历读取数据
- for imagePath in imagePaths:
- # 读取图像数据
- image = cv2.imread(imagePath)
- image = cv2.resize(image, (image_size, image_size))
- data.append(image)
- # 读取标签
- label = imagePath.split(os.path.sep)[-2]
- labels.append(label)
-
- data = np.array(data, dtype="float") / 255.0
- labels = np.array(labels)
-
- # 数据集切分
- (trainX, testX, trainY, testY) = train_test_split(data,labels, test_size=0.25, random_state=42)
-
- # 转换标签为one-hot encoding格式
- lb = LabelBinarizer()
- trainY = lb.fit_transform(trainY)
- testY = lb.transform(testY)
-
- # 数据增强处理
- aug = ImageDataGenerator(
- rotation_range=30,
- width_shift_range=0.1,
- height_shift_range=0.1,
- shear_range=0.2,
- zoom_range=0.2,
- horizontal_flip=True,
- fill_mode="nearest")
-
- # 建立卷积神经网络
- model = SimpleVGGNet.build(width=256, height=256, depth=3,classes=len(lb.classes_))
-
- # 设置初始化超参数
-
- # 学习率
- INIT_LR = 0.01
- # Epoch
- # 这里设置 5 是为了能尽快训练完毕,可以设置高一点,比如 30
- EPOCHS = 5
- # Batch Size
- BS = 32
-
- # 损失函数,编译模型
- print("------开始训练网络------")
- opt = SGD(lr=INIT_LR, decay=INIT_LR / EPOCHS)
- model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])
-
- # 训练网络模型
- H = model.fit_generator(
- aug.flow(trainX, trainY, batch_size=BS),
- validation_data=(testX, testY),
- steps_per_epoch=len(trainX) // BS,
- epochs=EPOCHS
- )
-
-
- # 测试
- print("------测试网络------")
- predictions = model.predict(testX, batch_size=32)
- print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=lb.classes_))
-
- # 绘制结果曲线
- N = np.arange(0, EPOCHS)
- plt.style.use("ggplot")
- plt.figure()
- plt.plot(N, H.history["loss"], label="train_loss")
- plt.plot(N, H.history["val_loss"], label="val_loss")
- plt.plot(N, H.history["accuracy"], label="train_acc")
- plt.plot(N, H.history["val_accuracy"], label="val_acc")
- plt.title("Training Loss and Accuracy")
- plt.xlabel("Epoch #")
- plt.ylabel("Loss/Accuracy")
- plt.legend()
- plt.savefig('./output/cnn_plot.png')
-
- # 保存模型
- print("------保存模型------")
- model.save('./cnn.model.h5')
- f = open('./cnn_lb.pickle', "wb")
- f.write(pickle.dumps(lb))
- f.close()
对于实际应用场景下,数据集很大,epoch 也会设置比较大,并在高性能的机器上训练。现在要在本机 Mac 上完成训练任务,我们只给了很少的样本来训练模型,epoch 也很小(为 5),当然这样模型的识别准确率也会很差,但我们此篇文章的目的是为了在本机完成一个机器学习的任务。
开始训练
执行以下命令开始训练:
$ python nn_train.py
训练过程日志如下:
训练结束后,在当前目录下会生成两个文件:模型文件 cnn.model.h5 和 损失函数曲线 output/cnn_plot.png
NO.4
模型评估
现在,我们拿到了模型文件 cnn.model.h5, 可以写一个预测脚本,本地执行脚本预测一张图片的分类。
$ python predict.py
- # predict.py
- import allspark
- import io
- import numpy as np
- import json
- from PIL import Image
- import requests
- import threading
- import cv2
- import os
- import tensorflow as tf
- from tensorflow.keras.models import load_model
- import time
-
- model = load_model('./train/cnn.model.h5')
- # pred的输入应该是一个images的数组,而且图片都已经转为numpy数组的形式
- # pred = model.predict(['./validation/button/button-demoplus-20200216-16615.png'])
-
- #这个顺序一定要与label.json顺序相同,模型输出是一个数组,取最大值索引为预测值
- Label = [
- "button",
- "keyboard",
- "searchbar",
- "switch"
- ]
- testPath = "./test/button.png"
-
- images = []
- image = cv2.imread(testPath)
- image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
-
- image = cv2.resize(image,(256,256))
- images.append(image)
- images = np.asarray(images)
-
- pred = model.predict(images)
-
- print(pred)
-
- max_ = np.argmax(pred)
- print('预测结果为:',Label[max_])
如果想要知道这个模型的准确率,也可以给模型输入一批带有已知分类的数据,通过模型预测后,将模型预测的分类与真实的分类比较,计算出准确率和召回率。
NO.5
模型服务部署
开发模型服务
但在实际应用中,我们预测一张图片的类别, 是通过给定一张图片,请求一个 API 来拿到返回结果的。我们需要编写一个模型服务,然后部署到远端,拿到一个部署之后的模型服务 API。
现在,我们可以编写一个模型服务,然后在本地部署。
- # 模型服务 app.py
- import allspark
- import io
- import numpy as np
- import json
- from PIL import Image
- import requests
- import threading
- import cv2
- import tensorflow as tf
- from tensorflow.keras.models import load_model
-
-
- with open('label.json') as f:
- mp = json.load(f)
- labels = {value:key for key,value in mp.items()}
-
- def create_opencv_image_from_stringio(img_stream, cv2_img_flag=-1):
- img_stream.seek(0)
- img_array = np.asarray(bytearray(img_stream.read()), dtype=np.uint8)
- image_temp = cv2.imdecode(img_array, cv2_img_flag)
- if image_temp.shape[2] == 4:
- image_channel3 = cv2.cvtColor(image_temp, cv2.COLOR_BGRA2BGR)
- image_mask = image_temp[:,:,3] #.reshape(image_temp.shape[0],image_temp.shape[1], 1)
- image_mask = np.stack((image_mask, image_mask, image_mask), axis = 2)
- index_mask = np.where(image_mask == 0)
- image_channel3[index_mask[0], index_mask[1], index_mask[2]] = 255
- return image_channel3
- else:
- return image_temp
-
- def get_string_io(origin_path):
- r = requests.get(origin_path, timeout=2)
- stringIo_content = io.BytesIO(r.content)
- return stringIo_content
-
- def handleReturn(pred, percent, msg_length):
- result = {
- "content":[]
- }
- argm = np.argsort(-pred, axis = 1)
- for i in range(msg_length):
- label = labels[argm[i, 0]]
- index = argm[i, 0]
- if(pred[i, index] > percent):
- confident = True
- else:
- confident = False
- result['content'].append({'isConfident': confident, 'label': label})
- return result
-
-
- def process(msg, model):
- msg_dict = json.loads(msg)
- percent = msg_dict['threshold']
- msg_dict = msg_dict['images']
- msg_length = len(msg_dict)
- desire_size = 256
-
- images = []
- for i in range(msg_length):
- image_temp = create_opencv_image_from_stringio(get_string_io(msg_dict[i]))
- image_temp = cv2.cvtColor(image_temp, cv2.COLOR_BGR2RGB)
- image = cv2.resize(image_temp, (256, 256))
- images.append(image)
- images = np.asarray(images)
- pred = model.predict(images)
- return bytes(json.dumps(handleReturn(pred, percent, msg_length)) ,'utf-8')
-
-
- def worker(srv, thread_id, model):
- while True:
- msg = srv.read()
- try:
- rsp = process(msg, model)
- srv.write(rsp)
- except Exception as e:
- srv.error(500,bytes('invalid data format', 'utf-8'))
-
- if __name__ == '__main__':
- desire_size = 256
- model = load_model('./cnn.model.h5')
-
- context = allspark.Context(4)
- queued = context.queued_service()
-
- workers = []
- for i in range(10):
- t = threading.Thread(target=worker, args=(queued, i, model))
- t.setDaemon(True)
- t.start()
- workers.append(t)
- for t in workers:
- t.join()
部署模型服务
模型服务编写完成后,在本地部署,需要安装环境。首先创建一个模型服务项目: deploy-project, 将 cnn.model.h5 拷贝到此项目中, 并在此项目下安装环境。
- .
- ├── app.py
- ├── cnn.model.h5
- └── label.json
可以看下[阿里云的模型服务部署文档(https://www.alibabacloud.com/help/zh/doc-detail/126314.htm?#h2-3-python-3):3、Python语言-3.2 构建开发环境-3.2.3 使用预构建的开发镜像(推荐)
可以直接查看 Mac Docker 安装文档(https://www.runoob.com/docker/macos-docker-install.html)
- # 用 Homebrew 安装 需要先现状 Homebrew: https://brew.sh
- $ brew cask install docker
安装完之后,桌面上会出现 Docker 的图标。
- # 使用conda创建python环境,目录需指定固定名字:ENV
- $ conda create -p ENV python=3.7
-
- # 安装EAS python sdk
- $ ENV/bin/pip install http://eas-data.oss-cn-shanghai.aliyuncs.com/sdk/allspark-0.9-py2.py3-none-any.whl
-
- # 安装其它依赖包
- $ ENV/bin/pip install tensorflow keras opencv-python
-
- # 激活虚拟环境
- $ conda activate ./ENV
-
- # 退出虚拟环境(不使用时)
- $ conda deactivate
/Users/chang/Desktop/ml-test/deploy-project 换成自己的项目路径
- sudo docker run -ti -v /Users/chang/Desktop/ml-test/deploy-project:/home -p 8080:8080
- registry.cn-shanghai.aliyuncs.com/eas/eas-python-base-image:py3.6-allspark-0.8
现在可以本地部署了,执行以下命令:
- cd /home
- ./ENV/bin/python app.py
下面的日志可以看到部署成功。
部署成功后,可以通过 localhost:8080/predict 访问模型服务了。
我们用 curl 命令来发一个 post 请求, 预测图片分类:
- curl -X POST 'localhost:8080/predict' \
- -H 'Content-Type: application/json' \
- -d '{
- "images": <a href="https://github.com/imgcook/ml-mac-classify">"https://img.alicdn.com/tfs/TB1W8K2MeH2gK0jSZJnXXaT1FXa-638-430.png"],
- "threshold": 0.5
- }'
得到预测结果:
{"content": [{"isConfident": true, "label": "keyboard"}]}
NO.6
完整代码
可以直接 clone 代码仓库:[https://github.com/imgcook/ml-mac-classify(https://github.com/imgcook/ml-mac-classify)
在安装好环境后,直接按以下命令运行。
- # 1、训练模型
- $ cd train-project
- $ python nn_train.py
-
- # 生成模型文件:cnn.model.h5
-
- # 2、将模型文件拷贝到 deploy-project 中,部署模型服务
- # 先安装模型服务运行环境
- $ conda activate ./ENV
- $ sudo docker run -ti -v /Users/chang/Desktop/ml-test/deploy-project:/home -p 8080:8080 registry.cn-shanghai.aliyuncs.com/eas/eas-python-base-image:py3.6-allspark-0.8
- $ cd /home
- $ ./ENV/bin/python app.py
-
- # 得到模型服务 API:localhost:8080/predict
-
- # 3、访问模型服务
- curl -X POST 'localhost:8080/predict' \
- -H 'Content-Type: application/json' \
- -d '{
- "images": ["https://img.alicdn.com/tfs/TB1W8K2MeH2gK0jSZJnXXaT1FXa-638-430.png"],
- "threshold": 0.5
- }'
NO.7
最后
好啦,总结一下这里使用深度学习的流程。我们选用了 SimpleVGGNet 作为图像分类算法(相当于一个函数),将准备好的数据传给这个函数,运行这个函数(学习数据集的特征和标签)得到一个输出,就是模型文件 model.h5。
这个模型文件可以接收一张图片作为输入,并预测这张图片是什么,输出预测结果。但如果想要让模型可以在线上跑,需要写一个模型服务(API)并部署到线上以得到一个 HTTP API,我们可以在生产环境直接调用。
2、你不知道的 TypeScript 泛型(万字长文,建议收藏)
3、你不知道的 Web Workers (万字长文,建议收藏)
5、或许是一本可以彻底改变你刷 LeetCode 效率的题解书
6、想去力扣当前端,TypeScript 需要掌握到什么程度?
❝关注加加,星标加加~
❞
如果觉得文章不错,帮忙点个在看呗
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。