文/ 阿里淘系 F(x) Team - 蘇川
可能有些同窗學習機器學習的時候比較迷茫,不知道該怎麼上手,看了不少經典書籍介紹的各類算法,但仍是不知道怎麼用它來解決問題,就算知道了,又發現須要準備環境、準備訓練和部署的機器,啊,好麻煩。html
今天,我來給你們介紹一種容易上手的方法,給你現成的樣本和代碼,按照步驟操做,就能夠在本身的 Mac 上體驗運用機器學習的全流程啦~~~python
下面的 Demo, 最終的效果是給定一張圖片,能夠預測圖片的類別。好比咱們訓練模型用的樣本是貓啊狗啊,那模型能學到的認識的就是貓啊狗啊, 若是用的訓練樣本是按鈕啊搜索框啊,那模型能學到的認識的就是這個按鈕啊搜索框啊。git
若是想了解用機器學習是怎麼解決實際問題的,能夠看這篇:如何使用深度學習識別UI界面組件?從問題定義、算法選型、樣本準備、模型訓練、模型評估、模型服務部署、到模型應用都有介紹。github
下載地址: www.anaconda.com/products/in…算法
安裝成功後,在終端命令行執行如下命令,使環境變量當即生效:docker
$ source ~/.bashrc複製代碼
能夠執行如下命令,查看環境變量macos
$ cat ~/.bashrc複製代碼
能夠看到 anaconda 的環境變量已經自動添加到 .bashrc 文件了json
執行如下命令:數組
$ conda list複製代碼
能夠看到 Anaconda 中有不少已經安裝好的包,若是有使用到這些包的就不須要再安裝了,python 環境也裝好了。bash
注意:若是安裝失敗,從新安裝,在提示安裝在哪裏時,選擇「更改安裝位置」,安裝位置選擇其餘地方不是用默認的,安裝在哪裏本身選擇,能夠放在「應用程序」下。
anaconda 中沒有 keras、tensorflow 和 opencv-python, 須要單獨安裝。
$ pip install keras
$ pip install tensorflow
$ pip install opencv-python複製代碼
這裏只准備了 4 個分類: button、keyboard、searchbar、switch, 每一個分類 200 個左右的樣本。
新建一個項目 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 = [] 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
如今,咱們拿到了模型文件 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_]) 複製代碼
若是想要知道這個模型的準確率,也能夠給模型輸入一批帶有已知分類的數據,經過模型預測後,將模型預測的分類與真實的分類比較,計算出準確率和召回率。
但在實際應用中,咱們預測一張圖片的類別, 是經過給定一張圖片,請求一個 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複製代碼
能夠看下阿里雲的模型服務部署文檔:三、Python語言-3.2 構建開發環境-3.2.3 使用預構建的開發鏡像(推薦)
能夠直接查看 Mac Docker 安裝文檔
# 用 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": ["https://img.alicdn.com/tfs/TB1W8K2MeH2gK0jSZJnXXaT1FXa-638-430.png"], "threshold": 0.5 }'複製代碼
獲得預測結果:
{"content": [{"isConfident": true, "label": "keyboard"}]}複製代碼
能夠直接 clone 代碼倉庫:github.com/imgcook/ml-…
在安裝好環境後,直接按如下命令運行。
# 一、訓練模型 $ cd train-project $ python nn_train.py # 生成模型文件:cnn.model.h5 # 二、將模型文件拷貝到 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 # 三、訪問模型服務 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 }'複製代碼
好啦,總結一下這裏使用深度學習的流程。咱們選用了 SimpleVGGNet 做爲圖像分類算法(至關於一個函數),將準備好的數據傳給這個函數,運行這個函數(學習數據集的特徵和標籤)獲得一個輸出,就是模型文件 model.h5。
這個模型文件能夠接收一張圖片做爲輸入,並預測這張圖片是什麼,輸出預測結果。但若是想要讓模型能夠在線上跑,須要寫一個模型服務(API)並部署到線上以獲得一個 HTTP API,咱們能夠在生產環境直接調用。