深度神經網絡在諸如圖像分類等任務上非常出色。擁有一半像樣的GPU的任何人現在都可以輕松獲得十年前耗資數百萬美元的結果以及整個研究團隊。但是,深度神經網絡有一個缺點。它們可能非常笨重且緩慢,因此它們并不總是在移動設備上運行良好。幸運的是,Core ML提供了一個解決方案:它使您能夠創建在iOS設備上運行良好的苗條模型。
在本系列文章中,我們將向您展示如何以兩種方式使用Core ML。首先,您將學習如何將預先訓練的圖像分類器模型轉換為Core ML并在iOS應用中使用它。然后,您將訓練自己的機器學習(ML)模型并將其用于制作Not Hotdog應用程序-就像您在HBO的硅谷所見過的那樣。
在上一篇文章中,我們準備了我們的開發環境。在這一部分中,我們將訓練有素的ONNX圖像分類模型轉換為Core ML格式。
Core ML是Apple的框架,允許您將ML模型集成到應用程序中(不僅適用于移動設備和臺式機,還適用于手表和Apple TV)。考慮iOS設備上的ML時,建議始終從Core ML開始。該框架非常易于使用,并且支持Apple設備上可用的自定義CPU,GPU和Neural Engine的充分利用。此外,您幾乎可以將任何神經網絡模型轉換為Core ML的本機格式。
如今,很多ML框架正在大量使用,例如TensorFlow,Keras和PyTorch。每個框架都有其自己的格式來保存模型。有一些工具可以將大多數格式直接轉換為Core ML。我們將重點介紹開放式神經網絡交換(ONNX)格式。ONNX定義了一種通用的文件格式和操作,以使在框架之間切換變得更加容易。
讓我們看一下所謂的模型動物園中可用的ONNX模型:
單擊“ 視覺”部分“ 圖像分類”中的第一個鏈接。這是它顯示的頁面:
如您所見,有很多模型可供選擇。這些模型是使用著名的ImageNet分類數據集進行訓練的,該數據集包含1,000個對象類別(例如“鍵盤”,“圓珠筆”,“蠟燭”,“狼蛛”,“大白鯊”等等……確切地說,是995其他)。
盡管這些模型的體系結構和用于訓練它們的框架有所不同,但在模型動物園中,它們似乎都已轉換為ONNX。
ResNet是此處可用的最佳模型之一(錯誤率低至3.6%)。這是我們將用于轉換為Core ML的代碼。
要下載模型,請單擊上表中的ResNet鏈接,然后向下滾動到所需的模型版本。
為了向您展示,使用Core ML,iOS設備可以處理“真實”模型,我們將選擇可用的最大(也是最好的)模型- 具有152層的ResNet V2。對于較舊的iOS設備,例如iPhone 6或7,您可能需要嘗試使用較小的型號之一,例如ResNet18。
可以使用Conda環境中安裝的coremltools
和onnx
軟件包將幾乎所有模型轉換為Core ML ,只要該模型使用Core ML支持的操作和層(opset版本)(當前為opset 10及更低版本)即可。
兩種類型的模型得到了專門的支持:分類和回歸。分類模型將標簽分配給輸入(例如圖像)。回歸模型計算給定輸入的數值。
我們將重點介紹圖像分類模型。
所選的ResNet模型期望什么作為輸入?有關詳細說明,請參見相應的Zoo模型頁面。
如您所見,ResNet模型期望數組中的圖片具有以下尺寸:批處理,尺寸通道(紅色,綠色和藍色通道始終為3),高度和寬度。應使用分別為每種顏色定義的平均值和標準偏差值,將數組值縮放到〜[0,1]的范圍。
雖然該coremltools
庫非常靈活,但其內置的圖像分類選項無法使我們完全復制原始的預處理步驟。讓我們嘗試足夠接近:
import coremltools as ct
import numpy as np
def resnet_norm_to_scale_bias(mean_rgb, stddev_rgb):
image_scale = 1 / 255. / (sum(stddev_rgb) / 3)
bias_rgb = []
for i in range(3):
bias = -mean_rgb[i] / stddev_rgb[i]
bias_rgb.append(bias)
return image_scale, bias_rgb
# Preprocessing parameters specific for ResNet model
# as defined at: https://github.com/onnx/models/tree/master/vision/
mean_vec = np.array([0.485, 0.456, 0.406])
stddev_vec = np.array([0.229, 0.224, 0.225])
image_scale, (bias_r, bias_g, bias_b) = resnet_norm_to_scale_bias(mean_vec, stddev_vec)
由于標準ResNet過程使用以下公式為圖像中的每個像素計算歸一化值,因此需要上述轉換:
norm_img_data = (img_data/255 - mean) / stddev =
(img_data/255/stddev) - mean/stddev
Core ML期望這樣的事情:
norm_img_data = (img_data * image_scale) + bias
ResNet預處理期望stddev
每個通道具有不同的(值縮放),但是Core ML默認情況下支持對應image_scale
參數的單個值。
由于概括性好的模型不應受到圖像色調微小變化的明顯影響,因此可以安全地使用image_scale
計算出的單個值作為平均值specified stddev_vec
:
image_scale = 1 / 255. / (sum(stddev_rgb) / 3)
接下來,讓我們bias
為每個顏色通道計算。我們結束了一個集預處理參數(image_scale
,bias_r
,bias_g
和bias_b
),我們可以在Core ML轉換使用。
配備了計算的預處理參數后,您可以運行轉換:
model = ct.converters.onnx.convert(
model='./resnet152-v2-7.onnx',
mode='classifier',
class_labels='./labels.txt',
image_input_names=['data'],
preprocessing_args={
'image_scale': image_scale,
'red_bias': bias_r,
'green_bias': bias_g,
'blue_bias': bias_b
},
minimum_ios_deployment_target='13'
)
讓我們簡要看一下其中的一些參數:
mode
='classifier'與class_labels
='。/ labels.txt'一起使用提供的標簽確定分類模式。這將確保模型不僅輸出數值,而且還輸出最可能檢測到的物體的標簽。image_input_names
= ['data']表示輸入數據包含圖像。它將允許您直接使用圖像,而無需事先轉換為Swift中的MultiArray或Python中的NumPy數組。preprocessing_args
指定先前計算的像素值歸一化參數。minimum_ios_deployment_target
設置為13可確保輸入和輸出結構比舊版本的iOS少混亂。運行上面的代碼后,您可以打印模型摘要:
在我們的例子中,模型接受大小為224 x 224像素的RGB圖像作為輸入,并生成兩個輸出:
classLabel
–具有最高模型置信度的對象的標簽。resnetv27_dense0_fwd
–層輸出字典(帶有1,000個“ label”:信心對)。此處返回的置信度是原始神經網絡輸出,而不是概率。可以很容易地將其轉換為概率,如代碼下載中包含的示例筆記本所示。使用轉換后的模型,運行預測是一項簡單的任務。讓我們使用PIL(枕頭)庫處理圖像,并在代碼下載中包含ballpen.jpg圖像。
from PIL import Image
image = Image.open('ballpen.jpg')
image = image.resize((224,224))
pred = model.predict(data={"data": image})
print(pred['classLabel'])
預期結果是:
ballpoint, ballpoint pen, ballpen, Biro
隨意嘗試其他圖片。為避免重復轉換過程,請保存模型:
model.save('ResNet.mlmodel')
您以后可以加載:
model = ct.models.MLModel('ResNet.mlmodel')
檢查代碼下載中提供的筆記本,以查看如何從模型輸出中獲取其他詳細信息,例如“前5名”預測候選的概率和標簽。
您已經以Core ML格式轉換并保存了ResNet模型。
盡管不同的模型將需要不同的預處理和轉換參數,但總體方法將保持不變。
使用模型可以做很多事情,例如添加元數據(模型描述,作者等),添加自定義層(例如最后的softmax來迫使模型返回概率而不是原始網絡輸出)。我們僅介紹了基礎知識,使您可以嘗試自己的模型。
熱門源碼