做者 | 江昱html
圖像分類是人工智能領域的一個熱門話題。通俗解釋就是,根據各自在圖像信息中所反映的不一樣特徵,把不一樣類別的目標區分開來的圖像處理方法。python
它利用計算機對圖像進行定量分析,把圖像或圖像中的每一個像元或區域劃歸爲若干個類別中的某一種,以代替人的視覺判讀。git
圖像分類在實際生產生活中也是常常遇到的,並且針對不一樣領域或者需求有着很強的針對性。例如經過拍攝花朵識別花朵信息、經過人臉比對人物信息等。github
一般狀況下,這些圖像識別或者分類的工具,都是在客戶端進行數據採集,在服務端進行運算得到結果,也就是說通常狀況下都是有專門的 API 實現圖像識別的。例如各大雲廠商都會爲咱們有償提供相似的能力:算法
阿里雲圖像識別頁面:docker
華爲雲圖像識別頁面:json
本文將會經過一個有趣的 Python 庫,快速將圖像分類的功能搭建在雲函數上,而且和 API 網關結合,對外提供 API 功能,實現一個 Serverless 架構的「圖像分類 API」。架構
首先和你們介紹一下須要的依賴庫:ImageAI。經過該依賴的官方文檔咱們能夠看到這樣的描述:app
ImageAI 是一個 python 庫,旨在使開發人員可以使用簡單的幾行代碼構建具備包含深度學習和計算機視覺功能的應用程序和系統。less
ImageAI 本着簡潔的原則,支持最早進的機器學習算法,用於圖像預測、自定義圖像預測、物體檢測、視頻檢測、視頻對象跟蹤和圖像預測訓練。ImageAI 目前支持使用在 ImageNet-1000 數據集上訓練的 4 種不一樣機器學習算法進行圖像預測和訓練。ImageAI 還支持使用在 COCO 數據集上訓練的 RetinaNet 進行對象檢測、視頻檢測和對象跟蹤。最終,ImageAI 將爲計算機視覺提供更普遍和更專業化的支持,包括但不限於特殊環境和特殊領域的圖像識別。
也就是說這個依賴庫,能夠幫助咱們完成基本的圖像識別和視頻的目標提取,雖然他給了一些數據集和模型,可是咱們也能夠根據自身須要對其進行額外的訓練,進行定製化拓展。經過官方給的代碼,咱們能夠看到一個簡單的 Demo:
# -*- coding: utf-8 -*- 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))
當咱們指定的 picture.jpg 圖片爲:
咱們在執行以後的結果是:
laptop : 71.43893241882324 notebook : 16.265612840652466 modem : 4.899394512176514 hard_disc : 4.007557779550552 mouse : 1.2981942854821682
若是在使用過程當中以爲模型 resnet50_weights_tf_dim_ordering_tf_kernels.h5 過大,耗時過長,能夠按需求選擇模型:
模型下載地址可參考 Github 地址:
https://github.com/OlafenwaMoses/ImageAI/releases/tag/1.0
或者參考 ImageAI 官方文檔:
https://imageai-cn.readthedocs.io/zh_CN/latest/ImageAI_Image_Prediction.html
將項目按照函數計算的需求,編寫好入口方法,以及作好項目初始化,同時在當前項目下建立文件夾 model,並將模型文件拷貝到該文件夾:
項目總體流程:
實現代碼:
# -*- coding: utf-8 -*- from imageai.Prediction import ImagePrediction import json import uuid import base64 import random # Response class Response: def __init__(self, start_response, response, errorCode=None): self.start = start_response responseBody = { 'Error': {"Code": errorCode, "Message": response}, } if errorCode else { 'Response': response } # 默認增長uuid,便於後期定位 responseBody['ResponseId'] = str(uuid.uuid1()) print("Response: ", json.dumps(responseBody)) self.response = json.dumps(responseBody) def __iter__(self): status = '200' response_headers = [('Content-type', 'application/json; charset=UTF-8')] self.start(status, response_headers) yield self.response.encode("utf-8") # 隨機字符串 randomStr = lambda num=5: "".join(random.sample('abcdefghijklmnopqrstuvwxyz', num)) # 模型加載 print("Init model") prediction = ImagePrediction() prediction.setModelTypeAsResNet() print("Load model") prediction.setModelPath("/mnt/auto/model/resnet50_weights_tf_dim_ordering_tf_kernels.h5") prediction.loadModel() print("Load complete") def handler(environ, start_response): try: request_body_size = int(environ.get('CONTENT_LENGTH', 0)) except (ValueError): request_body_size = 0 requestBody = json.loads(environ['wsgi.input'].read(request_body_size).decode("utf-8")) # 圖片獲取 print("Get pucture") imageName = randomStr(10) imageData = base64.b64decode(requestBody["image"]) imagePath = "/tmp/" + imageName with open(imagePath, 'wb') as f: f.write(imageData) # 內容預測 print("Predicting ... ") result = {} predictions, probabilities = prediction.predictImage(imagePath, result_count=5) print(zip(predictions, probabilities)) for eachPrediction, eachProbability in zip(predictions, probabilities): result[str(eachPrediction)] = str(eachProbability) return Response(start_response, result)
所須要的依賴:
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
編寫部署所須要的配置文件:
ServerlessBookImageAIDemo: Component: fc Provider: alibaba Access: release Properties: Region: cn-beijing Service: Name: ServerlessBook Description: Serverless圖書案例 Log: Auto Nas: Auto Function: Name: serverless_imageAI Description: 圖片目標檢測 CodeUri: Src: ./src Excludes: - src/.fun - src/model Handler: index.handler Environment: - Key: PYTHONUSERBASE Value: /mnt/auto/.fun/python MemorySize: 3072 Runtime: python3 Timeout: 60 Triggers: - Name: ImageAI Type: HTTP Parameters: AuthType: ANONYMOUS Methods: - GET - POST - PUT Domains: - Domain: Auto
在代碼與配置中,能夠看到有目錄:/mnt/auto/ 的存在,該部分其實是 nas 掛載以後的地址,只需提早寫入到代碼中便可,下一個環節會進行 nas 的建立以及掛載點配置的具體操做。
在完成上述步驟以後,能夠經過:
s deploy
進行項目部署,部署完成能夠看到結果:
完成部署以後,能夠經過:
s install docker
進行依賴的安裝:
依賴安裝完成能夠看到在目錄下生成了 .fun 的目錄,該目錄就是經過 docker 打包出來的依賴文件,這些依賴正是咱們在 requirements.txt 文件中聲明的依賴內容。
完成以後,咱們經過:
s nas sync ./src/.fun
將依賴目錄打包上傳到 nas,成功以後再將 model 目錄打包上傳:
s nas sync ./src/model
完成以後能夠經過:
s nas ls --all
查看目錄詳情:
完成以後,咱們能夠編寫腳本進行測試,一樣適用剛纔的測試圖片,經過代碼:
import json import urllib.request import base64 import time with open("picture.jpg", 'rb') as f: data = base64.b64encode(f.read()).decode() url = 'http://35685264-1295939377467795.test.functioncompute.com/' timeStart = time.time() print(urllib.request.urlopen(urllib.request.Request( url=url, data=json.dumps({'image': data}).encode("utf-8") )).read().decode("utf-8")) print("Time: ", time.time() - timeStart)
能夠看到結果:
{"Response": {"laptop": "71.43893837928772", "notebook": "16.265614330768585", "modem": "4.899385944008827", "hard_disc": "4.007565602660179", "mouse": "1.2981869280338287"}, "ResponseId": "1d74ae7e-298a-11eb-8374-024215000701"} Time: 29.16020894050598
能夠看到,函數計算順利地返回了預期結果,可是總體耗時卻超乎想象,有近 30s,此時咱們再次執行一下測試腳本:
{"Response": {"laptop": "71.43893837928772", "notebook": "16.265614330768585", "modem": "4.899385944008827", "hard_disc": "4.007565602660179", "mouse": "1.2981869280338287"}, "ResponseId": "4b8be48a-298a-11eb-ba97-024215000501"} Time: 1.1511380672454834
能夠看到,再次執行的時間僅有 1.15 秒,比上次整整提高了 28 秒之多。
在上一輪的測試中能夠看到,項目首次啓動和二次啓動的耗時差距,其實這個時間差,主要是函數在加載模型的時候浪費了極長的時間。
即便在本地,咱們也能夠簡單測試:
# -*- coding: utf-8 -*- import time timeStart = time.time() # 模型加載 from imageai.Prediction import ImagePrediction prediction = ImagePrediction() prediction.setModelTypeAsResNet() prediction.setModelPath("resnet50_weights_tf_dim_ordering_tf_kernels.h5") prediction.loadModel() print("Load Time: ", time.time() - timeStart) timeStart = time.time() predictions, probabilities = prediction.predictImage("./picture.jpg", result_count=5) for eachPrediction, eachProbability in zip(predictions, probabilities): print(str(eachPrediction) + " : " + str(eachProbability)) print("Predict Time: ", time.time() - timeStart)
執行結果:
Load Time: 5.549695014953613 laptop : 71.43893241882324 notebook : 16.265612840652466 modem : 4.899394512176514 hard_disc : 4.007557779550552 mouse : 1.2981942854821682 Predict Time: 0.8137111663818359
能夠看到,在加載 imageAI 模塊以及加載模型文件的過程當中,一共耗時 5.5 秒,在預測部分僅有不到 1 秒鐘的時間。而在函數計算中,機器性能自己就沒有我本地的性能高,此時爲了不每次裝載模型致使的響應時間過長,在部署的代碼中,能夠看到模型裝載過程其實是被放在了入口方法以外。這樣作的一個好處是,項目每次執行的時候,不必定會有冷啓動,也就是說在某些複用的前提下是能夠複用一些對象的,即無需每次都從新加載模型、導入依賴等。
因此在實際項目中,爲了不頻繁請求,實例重複裝載、建立某些資源,咱們能夠將部分資源放在初始化的時候進行。這樣能夠大幅度提升項目的總體性能,同時配合廠商所提供的預留能力,能夠基本上杜絕函數冷啓動帶來的負面影響。
近年來,人工智能與雲計算的發展日新月異,在 Serverless 架構中,如何運行傳統的人工智能項目已經逐漸成爲不少人所須要瞭解的事情。本文主要介紹了經過一個已有的依賴庫(ImageAI)實現一個圖像分類和預測的接口。藉助這個例子,其實有幾個事情是能夠被明確的:
固然,本文也算是拋磚引玉,但願讀者在本文以後,能夠發揮本身的想象,將更多的 AI 項目與 Serverless 架構進行進一步結合。