当前位置:   article > 正文

现在开始:用你的Mac训练和部署一个图片分类模型

model.predict(images)

文末福利:开发者藏经阁

可能有些同学学习机器学习的时候比较迷茫,不知道该怎么上手,看了很多经典书籍介绍的各种算法,但还是不知道怎么用它来解决问题,就算知道了,又发现需要准备环境、准备训练和部署的机器,啊,好麻烦。

今天,我来给大家介绍一种容易上手的方法,给你现成的样本和代码,按照步骤操作,就可以在自己的 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, 需要单独安装。

  1. $ pip install keras
  2. $ pip install tensorflow
  3. $ pip install opencv-python

NO.2

样本准备


这里只准备了 4 个分类:button、keyboard、searchbar、switch, 每个分类 200 个左右的样本。






NO.3

模型训练

开发训练逻辑


新建一个项目 train-project, 文件结构如下:

  1. .
  2. ├── CNN_net.py
  3. ├── dataset
  4. ├── nn_train.py
  5. └── utils_paths.py


入口文件代码如下,这里的逻辑是将准备好的样本输入给图像分类算法 SimpleVGGNet, 并设置一些训练参数,例如学习率、Epoch、Batch Size, 然后执行这段训练逻辑,最终得到一个模型文件。

  1. # nn_train.py
  2. from CNN_net import SimpleVGGNet
  3. from sklearn.preprocessing import LabelBinarizer
  4. from sklearn.model_selection import train_test_split
  5. from sklearn.metrics import classification_report
  6. from keras.optimizers import SGD
  7. from keras.preprocessing.image import ImageDataGenerator
  8. import utils_paths
  9. import matplotlib.pyplot as plt
  10. from cv2 import cv2
  11. import numpy as np
  12. import argparse
  13. import random
  14. import pickle
  15. import os
  16. # 读取数据和标签
  17. print("------开始读取数据------")
  18. data = <a href="https://www.alibabacloud.com/help/zh/doc-detail/126314.htm?#h2-3-python-3">]
  19. labels = []
  20. # 拿到图像数据路径,方便后续读取
  21. imagePaths = sorted(list(utils_paths.list_images('./dataset')))
  22. random.seed(42)
  23. random.shuffle(imagePaths)
  24. image_size = 256
  25. # 遍历读取数据
  26. for imagePath in imagePaths:
  27. # 读取图像数据
  28. image = cv2.imread(imagePath)
  29. image = cv2.resize(image, (image_size, image_size))
  30. data.append(image)
  31. # 读取标签
  32. label = imagePath.split(os.path.sep)[-2]
  33. labels.append(label)
  34. data = np.array(data, dtype="float") / 255.0
  35. labels = np.array(labels)
  36. # 数据集切分
  37. (trainX, testX, trainY, testY) = train_test_split(data,labels, test_size=0.25, random_state=42)
  38. # 转换标签为one-hot encoding格式
  39. lb = LabelBinarizer()
  40. trainY = lb.fit_transform(trainY)
  41. testY = lb.transform(testY)
  42. # 数据增强处理
  43. aug = ImageDataGenerator(
  44. rotation_range=30,
  45. width_shift_range=0.1,
  46. height_shift_range=0.1,
  47. shear_range=0.2,
  48. zoom_range=0.2,
  49. horizontal_flip=True,
  50. fill_mode="nearest")
  51. # 建立卷积神经网络
  52. model = SimpleVGGNet.build(width=256, height=256, depth=3,classes=len(lb.classes_))
  53. # 设置初始化超参数
  54. # 学习率
  55. INIT_LR = 0.01
  56. # Epoch
  57. # 这里设置 5 是为了能尽快训练完毕,可以设置高一点,比如 30
  58. EPOCHS = 5
  59. # Batch Size
  60. BS = 32
  61. # 损失函数,编译模型
  62. print("------开始训练网络------")
  63. opt = SGD(lr=INIT_LR, decay=INIT_LR / EPOCHS)
  64. model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])
  65. # 训练网络模型
  66. H = model.fit_generator(
  67. aug.flow(trainX, trainY, batch_size=BS),
  68. validation_data=(testX, testY),
  69. steps_per_epoch=len(trainX) // BS,
  70. epochs=EPOCHS
  71. )
  72. # 测试
  73. print("------测试网络------")
  74. predictions = model.predict(testX, batch_size=32)
  75. print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=lb.classes_))
  76. # 绘制结果曲线
  77. N = np.arange(0, EPOCHS)
  78. plt.style.use("ggplot")
  79. plt.figure()
  80. plt.plot(N, H.history["loss"], label="train_loss")
  81. plt.plot(N, H.history["val_loss"], label="val_loss")
  82. plt.plot(N, H.history["accuracy"], label="train_acc")
  83. plt.plot(N, H.history["val_accuracy"], label="val_acc")
  84. plt.title("Training Loss and Accuracy")
  85. plt.xlabel("Epoch #")
  86. plt.ylabel("Loss/Accuracy")
  87. plt.legend()
  88. plt.savefig('./output/cnn_plot.png')
  89. # 保存模型
  90. print("------保存模型------")
  91. model.save('./cnn.model.h5')
  92. f = open('./cnn_lb.pickle', "wb")
  93. f.write(pickle.dumps(lb))
  94. 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
  1. # predict.py
  2. import allspark
  3. import io
  4. import numpy as np
  5. import json
  6. from PIL import Image
  7. import requests
  8. import threading
  9. import cv2
  10. import os
  11. import tensorflow as tf
  12. from tensorflow.keras.models import load_model
  13. import time
  14. model = load_model('./train/cnn.model.h5')
  15. # pred的输入应该是一个images的数组,而且图片都已经转为numpy数组的形式
  16. # pred = model.predict(['./validation/button/button-demoplus-20200216-16615.png'])
  17. #这个顺序一定要与label.json顺序相同,模型输出是一个数组,取最大值索引为预测值
  18. Label = [
  19. "button",
  20. "keyboard",
  21. "searchbar",
  22. "switch"
  23. ]
  24. testPath = "./test/button.png"
  25. images = []
  26. image = cv2.imread(testPath)
  27. image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
  28. image = cv2.resize(image,(256,256))
  29. images.append(image)
  30. images = np.asarray(images)
  31. pred = model.predict(images)
  32. print(pred)
  33. max_ = np.argmax(pred)
  34. print('预测结果为:',Label[max_])

如果想要知道这个模型的准确率,也可以给模型输入一批带有已知分类的数据,通过模型预测后,将模型预测的分类与真实的分类比较,计算出准确率和召回率。

NO.5

模型服务部署

开发模型服务


但在实际应用中,我们预测一张图片的类别, 是通过给定一张图片,请求一个 API 来拿到返回结果的。我们需要编写一个模型服务,然后部署到远端,拿到一个部署之后的模型服务 API。

现在,我们可以编写一个模型服务,然后在本地部署。

  1. # 模型服务 app.py
  2. import allspark
  3. import io
  4. import numpy as np
  5. import json
  6. from PIL import Image
  7. import requests
  8. import threading
  9. import cv2
  10. import tensorflow as tf
  11. from tensorflow.keras.models import load_model
  12. with open('label.json') as f:
  13. mp = json.load(f)
  14. labels = {value:key for key,value in mp.items()}
  15. def create_opencv_image_from_stringio(img_stream, cv2_img_flag=-1):
  16. img_stream.seek(0)
  17. img_array = np.asarray(bytearray(img_stream.read()), dtype=np.uint8)
  18. image_temp = cv2.imdecode(img_array, cv2_img_flag)
  19. if image_temp.shape[2] == 4:
  20. image_channel3 = cv2.cvtColor(image_temp, cv2.COLOR_BGRA2BGR)
  21. image_mask = image_temp[:,:,3] #.reshape(image_temp.shape[0],image_temp.shape[1], 1)
  22. image_mask = np.stack((image_mask, image_mask, image_mask), axis = 2)
  23. index_mask = np.where(image_mask == 0)
  24. image_channel3[index_mask[0], index_mask[1], index_mask[2]] = 255
  25. return image_channel3
  26. else:
  27. return image_temp
  28. def get_string_io(origin_path):
  29. r = requests.get(origin_path, timeout=2)
  30. stringIo_content = io.BytesIO(r.content)
  31. return stringIo_content
  32. def handleReturn(pred, percent, msg_length):
  33. result = {
  34. "content":[]
  35. }
  36. argm = np.argsort(-pred, axis = 1)
  37. for i in range(msg_length):
  38. label = labels[argm[i, 0]]
  39. index = argm[i, 0]
  40. if(pred[i, index] > percent):
  41. confident = True
  42. else:
  43. confident = False
  44. result['content'].append({'isConfident': confident, 'label': label})
  45. return result
  46. def process(msg, model):
  47. msg_dict = json.loads(msg)
  48. percent = msg_dict['threshold']
  49. msg_dict = msg_dict['images']
  50. msg_length = len(msg_dict)
  51. desire_size = 256
  52. images = []
  53. for i in range(msg_length):
  54. image_temp = create_opencv_image_from_stringio(get_string_io(msg_dict[i]))
  55. image_temp = cv2.cvtColor(image_temp, cv2.COLOR_BGR2RGB)
  56. image = cv2.resize(image_temp, (256, 256))
  57. images.append(image)
  58. images = np.asarray(images)
  59. pred = model.predict(images)
  60. return bytes(json.dumps(handleReturn(pred, percent, msg_length)) ,'utf-8')
  61. def worker(srv, thread_id, model):
  62. while True:
  63. msg = srv.read()
  64. try:
  65. rsp = process(msg, model)
  66. srv.write(rsp)
  67. except Exception as e:
  68. srv.error(500,bytes('invalid data format', 'utf-8'))
  69. if __name__ == '__main__':
  70. desire_size = 256
  71. model = load_model('./cnn.model.h5')
  72. context = allspark.Context(4)
  73. queued = context.queued_service()
  74. workers = []
  75. for i in range(10):
  76. t = threading.Thread(target=worker, args=(queued, i, model))
  77. t.setDaemon(True)
  78. t.start()
  79. workers.append(t)
  80. for t in workers:
  81. t.join()

部署模型服务


模型服务编写完成后,在本地部署,需要安装环境。首先创建一个模型服务项目: deploy-project, 将 cnn.model.h5 拷贝到此项目中, 并在此项目下安装环境。

  1. .
  2. ├── app.py
  3. ├── cnn.model.h5
  4. └── label.json
安装环境


可以看下[阿里云的模型服务部署文档(https://www.alibabacloud.com/help/zh/doc-detail/126314.htm?#h2-3-python-3):3、Python语言-3.2 构建开发环境-3.2.3 使用预构建的开发镜像(推荐)

安装 Docker


可以直接查看 Mac Docker 安装文档(https://www.runoob.com/docker/macos-docker-install.html)

  1. # 用 Homebrew 安装 需要先现状 Homebrew: https://brew.sh
  2. $ brew cask install docker

安装完之后,桌面上会出现 Docker 的图标。

创建 anaconda 的虚拟环境
  1. # 使用conda创建python环境,目录需指定固定名字:ENV
  2. $ conda create -p ENV python=3.7
  3. # 安装EAS python sdk
  4. $ ENV/bin/pip install http://eas-data.oss-cn-shanghai.aliyuncs.com/sdk/allspark-0.9-py2.py3-none-any.whl
  5. # 安装其它依赖包
  6. $ ENV/bin/pip install tensorflow keras opencv-python
  7. # 激活虚拟环境
  8. $ conda activate ./ENV
  9. # 退出虚拟环境(不使用时)
  10. $ conda deactivate
运行 Docker 环境


/Users/chang/Desktop/ml-test/deploy-project 换成自己的项目路径

  1. sudo docker run -ti -v /Users/chang/Desktop/ml-test/deploy-project:/home -p 8080:8080
  2. registry.cn-shanghai.aliyuncs.com/eas/eas-python-base-image:py3.6-allspark-0.8
本地部署


现在可以本地部署了,执行以下命令:

  1. cd /home
  2. ./ENV/bin/python app.py

下面的日志可以看到部署成功。

部署成功后,可以通过 localhost:8080/predict 访问模型服务了。

我们用 curl 命令来发一个 post 请求, 预测图片分类:

  1. curl -X POST 'localhost:8080/predict' \
  2. -H 'Content-Type: application/json' \
  3. -d '{
  4. "images": <a href="https://github.com/imgcook/ml-mac-classify">"https://img.alicdn.com/tfs/TB1W8K2MeH2gK0jSZJnXXaT1FXa-638-430.png"],
  5. "threshold": 0.5
  6. }'


得到预测结果:

{"content": [{"isConfident": true, "label": "keyboard"}]}

NO.6

完整代码


可以直接 clone 代码仓库:[https://github.com/imgcook/ml-mac-classify(https://github.com/imgcook/ml-mac-classify)

在安装好环境后,直接按以下命令运行。

  1. # 1、训练模型
  2. $ cd train-project
  3. $ python nn_train.py
  4. # 生成模型文件:cnn.model.h5
  5. # 2、将模型文件拷贝到 deploy-project 中,部署模型服务
  6. # 先安装模型服务运行环境
  7. $ conda activate ./ENV
  8. $ 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
  9. $ cd /home
  10. $ ./ENV/bin/python app.py
  11. # 得到模型服务 API:localhost:8080/predict
  12. # 3、访问模型服务
  13. curl -X POST 'localhost:8080/predict' \
  14. -H 'Content-Type: application/json' \
  15. -d '{
  16. "images": ["https://img.alicdn.com/tfs/TB1W8K2MeH2gK0jSZJnXXaT1FXa-638-430.png"],
  17. "threshold": 0.5
  18. }'

NO.7

最后


好啦,总结一下这里使用深度学习的流程。我们选用了 SimpleVGGNet 作为图像分类算法(相当于一个函数),将准备好的数据传给这个函数,运行这个函数(学习数据集的特征和标签)得到一个输出,就是模型文件 model.h5。


这个模型文件可以接收一张图片作为输入,并预测这张图片是什么,输出预测结果。但如果想要让模型可以在线上跑,需要写一个模型服务(API)并部署到线上以得到一个 HTTP API,我们可以在生产环境直接调用。

推荐阅读

1、你不知道的前端异常处理(万字长文,建议收藏)

2、你不知道的 TypeScript 泛型(万字长文,建议收藏)

3、你不知道的 Web Workers (万字长文,建议收藏)

4、immutablejs 是如何优化我们的代码的?

5、或许是一本可以彻底改变你刷 LeetCode 效率的题解书

6、想去力扣当前端,TypeScript 需要掌握到什么程度?

7、距离弄懂正则的环视,你只差这一篇文章

关注加加,星标加加~

如果觉得文章不错,帮忙点个在看呗

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/513301
推荐阅读
相关标签
  

闽ICP备14008679号