iOS:使用 Core ML 進(jìn)行機(jī)器學(xué)習(xí)

Core ML 是一個(gè)使各種機(jī)器學(xué)習(xí)和統(tǒng)計(jì)模型在 macOS 和 iOS 上原生支持的令人興奮的新框架。它幫助開發(fā)者將已經(jīng)成型的統(tǒng)計(jì)和機(jī)器學(xué)習(xí)模型整合到應(yīng)用之中。這個(gè)模塊是基于蘋果公司的底層機(jī)器學(xué)習(xí)基本元件,有一些是在 WWDC2016中聲明的。

Core ML在三個(gè)方面幫助開發(fā)者:

  1. Core ML 支持多種機(jī)器學(xué)習(xí)模型,從類神經(jīng)網(wǎng)絡(luò)模型到廣義線性模型,Core ML 中都有。
  2. Core ML 使添加機(jī)器學(xué)習(xí)模型到開發(fā)者應(yīng)用中更容易。這個(gè)目標(biāo)是通過 coremltools實(shí)現(xiàn)的,Core ML Tools 是一個(gè) 專門幫助生成 Xcode 可用的 .mlmodel 文件的 Python 包。
  3. Core ML 可以自動(dòng)為開發(fā)者的模型提供自定義編程接口來提供可供調(diào)用的 API。這項(xiàng)特性幫助開發(fā)者在 Xcode 中直接使用模型,就像模型是一個(gè)本地類型。

在閱讀以上內(nèi)容之后,我們也就可以得到一個(gè)關(guān)于 Core ML 聲明周期的基本認(rèn)識(shí)。開發(fā)者可以用 Python創(chuàng)建一個(gè)模型,生成一個(gè) .mlmodel 文件,添加該模型到 Xcode 中并且通過 Core ML 在一個(gè)設(shè)備上使用該模型?,F(xiàn)在咱們開始吧!

預(yù)測(cè)房?jī)r(jià)

Core ML 支持許多種類的模型。為了支持這些模型,Core ML 希望開發(fā)者在一定的前提條件下創(chuàng)建模型。這可以幫助開發(fā)者劃定 coremltools 包需要生成 .mlmodel 文件的支持范圍。

在這款應(yīng)用中,我們會(huì)使用機(jī)器學(xué)習(xí)來創(chuàng)建一個(gè)線性回歸模型來預(yù)測(cè)房?jī)r(jià)。機(jī)器學(xué)習(xí)是一個(gè)基于專門設(shè)計(jì)為普適的數(shù)據(jù)分析的 NumPy,SciPy 和 matplotlib 的 Python 包。我們預(yù)測(cè)房?jī)r(jià)的回歸模型會(huì)基于兩個(gè)預(yù)測(cè)變量。

  1. 犯罪率
  2. 房屋數(shù)量

順便說一句:我們的目標(biāo)不是創(chuàng)造一個(gè)非常精確的模型。而是通過創(chuàng)建一個(gè)比較合理的模型來展示在應(yīng)用之中。建模過程是比較困難的,并且現(xiàn)在進(jìn)行比較深入的模型選擇和性能調(diào)整并不是一個(gè)特別明智的選擇。

這次的模型我們將要使用以下模式:?? = α + β???? + β???? + ??

這里的??是我們要預(yù)測(cè)的因變量:房?jī)r(jià)。x1是一個(gè)自變量來代表犯罪率。x2是一個(gè)自變量來代表房屋數(shù)目。e 是一個(gè)代表已知的數(shù)據(jù)集合中的記錄中的模型預(yù)測(cè)值和真實(shí)值之間的誤差的誤差項(xiàng)。我們不會(huì)真正的去將這個(gè)參數(shù)建模,但是包含這個(gè)誤差項(xiàng)是為了維護(hù)模型規(guī)格的完整性。

α, β?, 和 β? 是系數(shù)(從技術(shù)上說,α 被稱為攔截?cái)?shù))建模過程會(huì)估計(jì)這些系數(shù)來幫助我們 生成預(yù)測(cè)。順便提一句,就是這些參數(shù)使我們的模型呈現(xiàn)線性。y 是組合了參數(shù)和變量的線性計(jì)算結(jié)果。

關(guān)于回歸的基礎(chǔ)知識(shí)

即使你不知道回歸是什么,也沒有問題。只需要知道它是統(tǒng)計(jì)和機(jī)器學(xué)習(xí)的基礎(chǔ)模型?;貧w的主要目標(biāo)是在數(shù)據(jù)中畫一條線。為了這個(gè)目標(biāo),模型要嘗試估計(jì)劃線所必要的參數(shù)。
還記得這個(gè)方程式 y = mx + b? y 是Y 軸,m 是斜率,X 是 X 軸,b 是常量?;貞涍@些概念能夠幫助你更好理解線性回歸是在做什么。

具體來說,設(shè)想以下圖形:

yAndX.png

這個(gè)表格展示了我們通過正態(tài)分布隨機(jī)產(chǎn)生的兩個(gè)變量 X 和 Y。每個(gè)有100個(gè)樣本。X 是一個(gè)分布平均值為0 標(biāo)準(zhǔn)差為1:x ~ N(0, 1)。Y 是用 X 的平均值以1為標(biāo)準(zhǔn)差生成的:y ~ N(x, 1)。這樣,每個(gè) Y 都是由平均值為 X 的正態(tài)分布樣本生成的。舉例來說,第一個(gè) Y 的值是由平均值為第一個(gè) X 的值的正態(tài)分布樣本得來的。

這樣生成 Y 幫助在兩個(gè)變量之間簡(jiǎn)歷聯(lián)系。
接下來,我們使用一個(gè)兩個(gè)變量的線性回歸來估計(jì)常量和斜率來定義 Y 和 X 之間的關(guān)系。上圖中表格上畫出的線表現(xiàn)除了最后模型的結(jié)果。這次,該模型非常契合數(shù)據(jù),就像我們所期望的 Y 是一個(gè) X 加上一些有來自正態(tài)分布樣本的自由因子定義的函數(shù)。

所以,為了避免被某種程度上的減少,線性回歸都是關(guān)于劃線。如果你想要了解更多關(guān)于劃線知識(shí),請(qǐng)看我們之前關(guān)于機(jī)器學(xué)習(xí)的文章。

數(shù)據(jù):波士頓房屋數(shù)據(jù)集

此次的數(shù)據(jù)來自 Harrison 和 Rubinfeld 的一項(xiàng)已經(jīng)公開的研究.該數(shù)據(jù)包含了506份作者收集的關(guān)于波士頓房?jī)r(jià)的各種影響因素的觀察值。這包含了如下的相關(guān)預(yù)測(cè):

     CRIM    ZN  INDUS  CHAS    NOX     RM   AGE     DIS  RAD    TAX \
0  0.00632  18.0   2.31   0.0  0.538  6.575  65.2  4.0900  1.0  296.0
1  0.02731   0.0   7.07   0.0  0.469  6.421  78.9  4.9671  2.0  242.0
2  0.02729   0.0   7.07   0.0  0.469  7.185  61.1  4.9671  2.0  242.0
3  0.03237   0.0   2.18   0.0  0.458  6.998  45.8  6.0622  3.0  222.0
4  0.06905   0.0   2.18   0.0  0.458  7.147  54.2  6.0622  3.0  222.0

   PTRATIO       B  LSTAT
0     15.3  396.90   4.98
1     17.8  396.90   9.14
2     17.8  392.83   4.03
3     18.7  394.63   2.94
4     18.7  396.90   5.33

因變量如下:

   MEDV
0  24.0
1  21.6
2  34.7
3  34.4
4  36.2

這個(gè)因變量展示出 $1,000 這一單元的房?jī)r(jià)的中間值。這些數(shù)據(jù)來自1970年左右,所以在房?jī)r(jià)后面加幾個(gè)零也還是顯得比較低。
充分了解數(shù)據(jù)類型之后,現(xiàn)在該寫回歸模型了。

Python 模型

我們使用 Python 2.7 來定義回歸模型。這是 coremltools 需要的。以下是代碼:

import coremltools
from sklearn import datasets, linear_model
import pandas as pd
from sklearn.externals import joblib

# Load data
boston = datasets.load_boston()
boston_df = pd.DataFrame(boston.data)
boston_df.columns = boston.feature_names

# Define model
X = boston_df.drop(boston_df.columns[[1,2,3,4,6,7,8,9,10,11,12]], axis=1)
Y = boston.target

lm = linear_model.LinearRegression()
lm.fit(X, Y)

# coefficients
lm.intercept_
lm.coef_

# Convert model to Core ML 
coreml_model = coremltools.converters.sklearn.convert(lm, input_features=["crime", "rooms"], output_feature_names="price")

# Save Core ML Model
coreml_model.save("BostonPricer.mlmodel")

我們不需要對(duì) Python 非常精通才能理解以上代碼。頂上的部分是導(dǎo)入必須的包。
接下來,我們導(dǎo)入波士頓房?jī)r(jià)數(shù)據(jù),并且進(jìn)行適度處理來使我們的模型更好地獲取自變量和因變量。使用以上變量,我們可以創(chuàng)造出線性規(guī)劃模型:lm = linear_model.LinearRegression().我們創(chuàng)建了模型之后,就可以使用數(shù)據(jù)進(jìn)行適配,適配數(shù)據(jù)可以生產(chǎn)幫助我們預(yù)測(cè)房?jī)r(jià)的系數(shù)。.mlmodel 文件會(huì)允許我滿使用系數(shù)來做出預(yù)測(cè)。
最后兩行的 Python 代碼,是轉(zhuǎn)換線性模型到 .mlmodel 格式并且保存到文件中。
coremltools.converters.sklearn.convert(lm, ["crime", "rooms"], "price")完成裝換操作。coremltools 提供一系列可供使用的轉(zhuǎn)換器來轉(zhuǎn)換模型到 .mlmodel 格式。詳情參考.下載該網(wǎng)頁文檔來了解更多關(guān)于轉(zhuǎn)換的消息。
運(yùn)行python NAME_OF_FILE.py 來生成 BostonPricer.mlmodel。舉例來說,我的文件名是“pricer_example.py”,所以 我們運(yùn)行 python pricer_example.py。確保運(yùn)行程序在 Python 腳本文件在相同目錄中。我們的 .mlmodel 文件會(huì)創(chuàng)建在相同的位置。

一個(gè)小巧智能的 iOS 應(yīng)用程序

我們的應(yīng)用程序是一個(gè)簡(jiǎn)單的單視圖應(yīng)用程序。該應(yīng)用使用了簡(jiǎn)答的用戶圖形接口來根據(jù)兩個(gè)變量生成房?jī)r(jià)預(yù)測(cè)值。這個(gè)程序有一個(gè)有兩個(gè)組件的UIPickerView,一個(gè)是犯罪率另一個(gè)是房屋個(gè)數(shù)。并且有一個(gè)標(biāo)簽來顯示結(jié)果。

顯示如下:

singleView.png

我們不會(huì)使用 storyboard 來構(gòu)建視圖。如果對(duì)該項(xiàng)目好奇可以在這里找到。

添加BostonPricer.mlmodel 到 Xcode 項(xiàng)目中。

將 .mlmodel 文件 拖拽進(jìn)項(xiàng)目中,這樣可以方便查看模型文件。你可以通過點(diǎn)擊BostonPricer.mlmodel 來觀察模型文件。

這樣添加文件和會(huì)自動(dòng)添加生成名為:BostonPricer, BostonPricerInput and BostonPricerOutput的類。

  • BostonPricer 是用來創(chuàng)建模型實(shí)例的。它提供了一套接口來產(chǎn)生預(yù)測(cè)值。
  • BostonPricerInput 是一個(gè)可以創(chuàng)建一個(gè)輸入數(shù)據(jù)源來傳給模型實(shí)例 BostonPricer 的類。模型會(huì)使用這些信息來生成預(yù)測(cè)。我們可以這樣工作,但是卻沒有必要。BostonPricer 提供了一個(gè)方法來從符合你的輸入變量的數(shù)據(jù)類型中生成預(yù)測(cè)值。
  • BostonPricerOutput 是一個(gè)可以規(guī)范基于一定輸入的模型的類。我們可以使用這些通過預(yù)測(cè)模型產(chǎn)品來生成各種類型的結(jié)果。

某些類定義的實(shí)現(xiàn)細(xì)節(jié)對(duì)我們是隱藏的,但是當(dāng)我們對(duì)BostonPricer使用‘command-click’。一個(gè)彈出視圖會(huì)出現(xiàn)并且會(huì)給出“Jump to Definition”的選項(xiàng)。點(diǎn)擊“Jump to Definition”,這樣Xcode 會(huì)跳到BostonPricer 的定義實(shí)現(xiàn)文件中。另一個(gè)定義實(shí)現(xiàn)類也在這個(gè)文件中。如下:

@objc class BostonPricer:NSObject {
    var model: MLModel
    init(contentsOf url: URL) throws {
        self.model = try MLModel(contentsOf: url)
    }
    convenience override init() {
        let bundle = Bundle(for: BostonPricer.self)
        let assetPath = bundle.url(forResource: "BostonPricer", withExtension:"mlmodelc")
        try! self.init(contentsOf: assetPath!)
    }
    func prediction(input: BostonPricerInput) throws -> BostonPricerOutput {
        let outFeatures = try model.prediction(from: input)
        let result = BostonPricerOutput(price: outFeatures.featureValue(for: "price")!.doubleValue)
        return result
    }
    func prediction(crime: Double, rooms: Double) throws -> BostonPricerOutput {
        let input_ = BostonPricerInput(crime: crime, rooms: rooms)
        return try self.prediction(input: input_)
    }
}

BostonPricer 是一個(gè)提供 MLModel 接口的 NSObject 的子類。它提供了兩個(gè)方法,每個(gè)都是可以從某些輸入值預(yù)測(cè)因變量。第一個(gè)方法是:BostonPricerInput prediction(input:)這是 CoreML 為我們提供的另一個(gè)方法。正如上面所說的,我們不會(huì)在本文中使用這種類型。
第二種方法使用了兩個(gè)自變量的值:prediction(crime:rooms:)。我們會(huì)使用第二種方法來生成預(yù)測(cè)值。讓我們看看怎么工作的:

使用 BostonPricer

我們使用 BostonPricer 利用犯罪率和房屋中的房子個(gè)數(shù)來生成預(yù)測(cè)房?jī)r(jià)的值。我們需要?jiǎng)?chuàng)建一個(gè)實(shí)例來在工程代碼中使用該模型。我們的例子中添加了模型作為屬性:
UIViewController: let model = BostonPricer()。
(接下來的代碼片段都在我們的ViewController.swift 中)
當(dāng) Picker 設(shè)置好以后,我們選擇一些隨機(jī)的行來標(biāo)識(shí)一些輸入值從而計(jì)算預(yù)測(cè)值。

@IBOutlet var picker: UIPickerView! {
    didSet {
        picker.selectRow(4, inComponent: Predictor.crime.rawValue, animated: false)
        picker.selectRow(3, inComponent: Predictor.rooms.rawValue, animated: false)
    }
}

為了在程序運(yùn)行開始時(shí)就顯示預(yù)測(cè)值,我們要調(diào)用generatePrediction()方法在viewDidLoad()。

override func viewDidLoad() {
    super.viewDidLoad()
    generatePrediction()
}

設(shè)置好以后,我們可以在UIPickerView更新選擇值之后生成預(yù)測(cè)值。其中的兩個(gè)中心方法如下:

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    generatePrediction()
}

fileprivate func generatePrediction() {
    let selectedCrimeRow = picker.selectedRow(inComponent: Predictor.crime.rawValue)
    guard let crime = crimeDataSource.value(for: selectedCrimeRow) else {
        return
    }

    let selectedRoomRow = picker.selectedRow(inComponent: Predictor.rooms.rawValue)
    guard let rooms = roomsDataSource.value(for: selectedRoomRow) else {
        return
    }

    guard let modelOutput = try? model.prediction(crime: crime, rooms: rooms) else {
        fatalError("Something went wrong with generating the model output.")
    }

    // Estimated price is in $1k increments (Data is from 1970s...)
    priceLabel.text = priceFormatter.string(for: modelOutput.price)
}

代碼中的第一個(gè)方法是:pickerView(_:didSelectRow:inComponent:),該方法定義在UIPickerViewDelegate 協(xié)議中。我們使用這個(gè)方法來確定新的值。

第二個(gè)方法是generatePrection(),其中包含了從我們的 .mlmodel 文件生成預(yù)測(cè)值的所有業(yè)務(wù)邏輯。將這些邏輯抽象到新的方法中使得在UIViewController的viewDidLoad()方法調(diào)用是使用預(yù)測(cè)值跟新標(biāo)簽上的值更容易。就像上面看到的,這樣我們可以在viewDidLoad()調(diào)用generatePrediction()。
generatePrediction()根據(jù)選擇器的狀態(tài)來決定現(xiàn)在組件中現(xiàn)在選擇的值。我們用這些信息來獲取crimeDataSource和roomsDataSource中相關(guān)行的值。例如,犯罪率關(guān)聯(lián)的行數(shù)3 是0.03。類似的,房間數(shù)在第三行相關(guān)的值是3。

我們傳值到模型中來生成預(yù)測(cè)值。代碼沒有執(zhí)行的使用try? model.prediction(crime: crime, rooms: rooms)。

這里簡(jiǎn)單說一下使用 try?轉(zhuǎn)換到可選類型來避免錯(cuò)誤。我們可以對(duì)錯(cuò)誤處理的更好,但是這不是本篇文章的重點(diǎn)。這也不是我們示例程序中的大問題。當(dāng)我們給prediction(crime:rooms:)方法傳值時(shí),編譯器會(huì)捕捉到任何不匹配數(shù)據(jù)。這個(gè)方法理論上是可以拋棄的,舉例來說,當(dāng)我們需要一張圖片,但是圖片被一種無法識(shí)別的格式傳遞過來。

從現(xiàn)在的目的來看,從我們的輸入值來生成預(yù)測(cè)值是十分必要的。
我們生產(chǎn)預(yù)測(cè)值之后,要跟新標(biāo)簽顯示的值。


pricer_estimator.gif

也許你會(huì)想不通為何預(yù)測(cè)的價(jià)格會(huì)是負(fù)值。問題在于超出范圍的模型數(shù)據(jù)匹配。問題的關(guān)鍵是我們的模型估計(jì)的常量是負(fù)數(shù)。這表示我們的模型是不足的,并沒有包含所有自變量參數(shù)來預(yù)測(cè)房?jī)r(jià)。如果我們想要優(yōu)化模型,我們必須深入挖掘數(shù)據(jù)并且辨別出是怎么發(fā)生的。

有趣的是,犯罪率并沒有對(duì)房?jī)r(jià)產(chǎn)生很大的影響。這一點(diǎn)可能是真實(shí)的發(fā)現(xiàn),也可能是我們模型中的一些缺陷。如果這是一個(gè)真的應(yīng)用程序,它可能會(huì)需要包含更多的調(diào)查。然而,屋子中的房間數(shù)對(duì)房?jī)r(jià)產(chǎn)生了很大的影響,這就像我們所想的。

最后說一句

這篇文章說明了 iOS 應(yīng)用程序中集成 CoreML 的過程。使用 CoreML,我們可以使用模型的又是來在應(yīng)用程序中直接使用我們創(chuàng)建的模型。CoreML 使創(chuàng)建機(jī)器學(xué)習(xí)和統(tǒng)計(jì)模型更加方便。

盡管如此,還是要做一個(gè)警告。統(tǒng)計(jì)和機(jī)器學(xué)習(xí)并不是簡(jiǎn)單的應(yīng)用程序接口。他們是整塊的領(lǐng)域就像藝術(shù)和科學(xué)。開發(fā)選擇一個(gè)有益的模型需要更多的實(shí)踐和學(xué)習(xí)。我們的應(yīng)用程序在這一點(diǎn)上充分顯示:很明顯的是我們的模型還有很多缺陷來預(yù)測(cè)房?jī)r(jià)。

CoreML 可以是一個(gè)包含設(shè)計(jì)和開發(fā)人員的應(yīng)用程序團(tuán)隊(duì)和數(shù)據(jù)科學(xué)界比較最好的合作方式。數(shù)據(jù)學(xué)家會(huì)研究成噸的模型來確保傳遞出正確的結(jié)果。應(yīng)用程序團(tuán)隊(duì)可以集中精力來優(yōu)化將模型集成到應(yīng)用程序中的體驗(yàn)。

也許有些問題是更容易定義的,并且匹配模型也已經(jīng)是充分測(cè)試的。這樣可以更加容易的集成到應(yīng)用程序中,而我們不用自己對(duì)模型進(jìn)行優(yōu)化。舉例來說就包含了很多分類的問題:a)面部識(shí)別, b)圖像識(shí)別 , c)手寫文字識(shí)別 等等。

需要特別注意的地方就是要確定我們所選用的模型要比較契合我們開發(fā)的項(xiàng)目實(shí)例。這樣在測(cè)試時(shí)可以確認(rèn)模型的值是否就是我們所期望的那樣。然而這并不說明 CoreML 可以使所有的模型變得即插即用,但是它確實(shí)讓開發(fā)者更容易的進(jìn)行應(yīng)用程序的開發(fā)。

了解更多關(guān)于為何要提前為應(yīng)用程序適配 iOS 11,或者下載圖書來進(jìn)一步了解這些改變是如何影響你的生意的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容