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ā)者:
- Core ML 支持多種機(jī)器學(xué)習(xí)模型,從類神經(jīng)網(wǎng)絡(luò)模型到廣義線性模型,Core ML 中都有。
- Core ML 使添加機(jī)器學(xué)習(xí)模型到開發(fā)者應(yīng)用之更容易。這個(gè)目標(biāo)是通過(guò) coremltools實(shí)現(xiàn)的,Core ML Tools 是一個(gè) 專門幫助生成 Xcode 可用的 .mlmodel 文件的 Python 包。
- Core ML 可以自動(dòng)為開發(fā)者的模型提供自定義編程接口來(lái)提供可供調(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 中并且通過(guò) Core ML 在一個(gè)設(shè)備上使用該模型。現(xiàn)在咱們開始吧!
預(yù)測(cè)房?jī)r(jià)
Core ML 支持許多種類的模型。為了支持這些模型,Core ML 希望開發(fā)者在一定的前提條件下創(chuàng)建模型。這可以幫助開發(fā)者劃定 coremltools 包需要生成 .mlmodel 文件的支持范圍。
在這款應(yīng)用中,我們會(huì)使用機(jī)器學(xué)習(xí)來(lái)創(chuàng)建一個(gè)線性回歸模型來(lái)預(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è)變量。
- 犯罪率
- 房屋數(shù)量
順便說(shuō)一句:我們的目標(biāo)不是創(chuàng)造一個(gè)非常精確的模型。而是通過(guò)創(chuàng)建一個(gè)比較合理的模型來(lái)展示在應(yīng)用之中。建模過(guò)程是比較困難的,并且現(xiàn)在進(jìn)行比較深入的模型選擇和性能調(diào)整并不是一個(gè)特別明智的選擇。
這次的模型我們將要使用以下模式:?? = α + β???? + β???? + ??
這里的??是我們要預(yù)測(cè)的因變量:房?jī)r(jià)。x1是一個(gè)自變量來(lái)代表犯罪率。x2是一個(gè)自變量來(lái)代表房屋數(shù)目。e 是一個(gè)代表已知的數(shù)據(jù)集合中的記錄中的模型預(yù)測(cè)值和真實(shí)值之間的誤差的誤差項(xiàng)。我們不會(huì)真正的去將這個(gè)參數(shù)建模,但是包含這個(gè)誤差項(xiàng)是為了維護(hù)模型規(guī)格的完整性。
α, β?, 和 β? 是系數(shù)(從技術(shù)上說(shuō),α 被稱為攔截?cái)?shù))建模過(guò)程會(huì)估計(jì)這些系數(shù)來(lái)幫助我們 生成預(yù)測(cè)。順便提一句,就是這些參數(shù)使我們的模型呈現(xiàn)線性。y 是組合了參數(shù)和變量的線性計(jì)算結(jié)果。
關(guān)于回歸的基礎(chǔ)知識(shí)
即使你不知道回歸是什么,也沒(méi)有問(wèn)題。只需要知道它是統(tǒng)計(jì)和機(jī)器學(xué)習(xí)的基礎(chǔ)模型。回歸的主要目標(biāo)是在數(shù)據(jù)中畫一條線。為了這個(gè)目標(biāo),模型要嘗試估計(jì)劃線所必要的參數(shù)。
還記得這個(gè)方程式 y = mx + b? y 是Y 軸,m 是斜率,X 是 X 軸,b 是常量。回憶這些概念能夠幫助你更好理解線性回歸是在做什么。
區(qū)里來(lái)說(shuō),設(shè)想以下圖形:
這個(gè)表格展示了我們通過(guò)正態(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)分布樣本生成的。舉例來(lái)說(shuō),第一個(gè) Y 的值是由平均值為第一個(gè) X 的值的正態(tài)分布樣本得來(lái)的。
這樣生成 Y 幫助在兩個(gè)變量之間簡(jiǎn)歷聯(lián)系。
接下來(lái),我們使用一個(gè)兩個(gè)變量的線性回歸來(lái)估計(jì)常量和斜率來(lái)定義 Y 和 X 之間的關(guān)系。上圖中表格上畫出的線表現(xiàn)除了最后模型的結(jié)果。這次,該模型非常契合數(shù)據(jù),就像我們所期望的 Y 是一個(gè) X 加上一些有來(lái)自正態(tài)分布樣本的自由因子定義的函數(shù)。
所以,為了避免被某種程度上的減少,線性回歸都是關(guān)于劃線。如果你想要了解更多關(guān)于劃線知識(shí),請(qǐng)看我們之前關(guān)于機(jī)器學(xué)習(xí)的文章。
數(shù)據(jù):波士頓房屋數(shù)據(jù)集
此次的數(shù)據(jù)來(lái)自 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ù)來(lái)自1970年左右,所以在房?jī)r(jià)后面加幾個(gè)零也還是顯得比較低。
充分了解數(shù)據(jù)類型之后,現(xiàn)在該寫回歸模型了。
Python 模型
我們使用 Python 2.7 來(lái)定義回歸模型。這是 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)入必須的包。
接下來(lái),我們導(dǎo)入波士頓房?jī)r(jià)數(shù)據(jù),并且進(jìn)行適度處理來(lái)使我們的模型更好地獲取自變量和因變量。使用以上變量,我們可以創(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ù)來(lái)做出預(yù)測(cè)。
最后兩行的 Python 代碼,是轉(zhuǎn)換線性模型到 .mlmodel 格式并且保存到文件中。
coremltools.converters.sklearn.convert(lm, [“crime”, “rooms”], “price”)完成裝換操作。coremltools 提供一系列可供使用的轉(zhuǎn)換器來(lái)轉(zhuǎn)換模型到 .mlmodel 格式。詳情參考.下載該網(wǎng)頁(yè)文檔來(lái)了解更多關(guān)于轉(zhuǎn)換的消息。
運(yùn)行python NAME_OF_FILE.py 來(lái)生成 BostonPricer.mlmodel。舉例來(lái)說(shuō),我的文件名是“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)答的用戶圖形接口來(lái)根據(jù)兩個(gè)變量生成房?jī)r(jià)預(yù)測(cè)值。這個(gè)程序有一個(gè)有兩個(gè)組件的UIPickerView,一個(gè)是犯罪率另一個(gè)是房屋個(gè)數(shù)。并且有一個(gè)標(biāo)簽來(lái)顯示結(jié)果。
顯示如下:
我們不會(huì)使用 storyboard 來(lái)構(gòu)建視圖。如果對(duì)該項(xiàng)目好奇可以在這里找到。
添加BostonPricer.mlmodel 到 Xcode 項(xiàng)目中。
將 .mlmodel 文件 拖拽進(jìn)項(xiàng)目中,這樣可以方便查看模型文件。你可以通過(guò)點(diǎn)擊BostonPricer.mlmodel 來(lái)觀察模型文件。
這樣添加文件和會(huì)自動(dòng)添加生成名為:BostonPricer, BostonPricerInput and BostonPricerOutput的類。
- BostonPricer 是用來(lái)創(chuàng)建模型實(shí)例的。它提供了一套接口來(lái)產(chǎn)生預(yù)測(cè)值。
- BostonPricerInput 是一個(gè)可以創(chuàng)建一個(gè)輸入數(shù)據(jù)源來(lái)傳給模型實(shí)例 BostonPricer 的類。模型會(huì)使用這些信息來(lái)生成預(yù)測(cè)。我們可以這樣工作,但是卻沒(méi)有必要。BostonPricer 提供了一個(gè)方法來(lái)從符合你的輸入變量的數(shù)據(jù)類型中生成預(yù)測(cè)值。
- BostonPricerOutput 是一個(gè)可以規(guī)范基于一定輸入的模型的類。我們可以使用這些通過(guò)預(yù)測(cè)模型產(chǎn)品來(lái)生成各種類型的結(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”,這樣X(jué)code 會(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è)方法。正如上面所說(shuō)的,我們不會(huì)在本文中使用這種類型。
第二種方法使用了兩個(gè)自變量的值:prediction(crime:rooms:)。我們會(huì)使用第二種方法來(lái)生成預(yù)測(cè)值。讓我們看看怎么工作的:
使用 BostonPricer
我們使用 BostonPricer 利用犯罪率和房屋中的房子個(gè)數(shù)來(lái)生成預(yù)測(cè)房?jī)r(jià)的值。我們需要?jiǎng)?chuàng)建一個(gè)實(shí)例來(lái)在工程代碼中使用該模型。我們的例子中添加了模型作為屬性:
UIViewController: let model = BostonPricer()。
(接下來(lái)的代碼片段都在我們的ViewController.swift 中)
當(dāng) Picker 設(shè)置好以后,我們選擇一些隨機(jī)的行來(lái)標(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è)方法來(lái)確定新的值。
第二個(gè)方法是generatePrection(),其中包含了從我們的 .mlmodel 文件生成預(yù)測(cè)值的所有業(yè)務(wù)邏輯。將這些邏輯抽象到新的方法中使得在UIViewController的viewDidLoad()方法調(diào)用是使用預(yù)測(cè)值跟新標(biāo)簽上的值更容易。就像上面看到的,這樣我們可以在viewDidLoad()調(diào)用generatePrediction()。
generatePrediction()根據(jù)選擇器的狀態(tài)來(lái)決定現(xiàn)在組件中現(xiàn)在選擇的值。我們用這些信息來(lái)獲取crimeDataSource和roomsDataSource中相關(guān)行的值。例如,犯罪率關(guān)聯(lián)的行數(shù)3 是0.03。類似的,房間數(shù)在第三行相關(guān)的值是3。
我們傳值到模型中來(lái)生成預(yù)測(cè)值。代碼沒(méi)有執(zhí)行的使用try? model.prediction(crime: crime, rooms: rooms)。
這里簡(jiǎn)單說(shuō)一下使用 try?轉(zhuǎn)換到可選類型來(lái)避免錯(cuò)誤。我們可以對(duì)錯(cuò)誤處理的更好,但是這不是本篇文章的重點(diǎn)。這也不是我們示例程序中的大問(wèn)題。當(dāng)我們給prediction(crime:rooms:)方法傳值時(shí),編譯器會(huì)捕捉到任何不匹配數(shù)據(jù)。這個(gè)方法理論上是可以拋棄的,舉例來(lái)說(shuō),當(dāng)我們需要一張圖片,但是圖片被一種無(wú)法識(shí)別的格式傳遞過(guò)來(lái)。
從現(xiàn)在的目的來(lái)看,從我們的輸入值來(lái)生成預(yù)測(cè)值是十分必要的。
我們生產(chǎn)預(yù)測(cè)值之后,要跟新標(biāo)簽顯示的值。
也許你會(huì)想不通為何預(yù)測(cè)的價(jià)格會(huì)是負(fù)值。問(wèn)題在于超出范圍的模型數(shù)據(jù)匹配。問(wèn)題的關(guān)鍵是我們的模型估計(jì)的常量是負(fù)數(shù)。這表示我們的模型是不足的,并沒(méi)有包含所有自變量參數(shù)來(lái)預(yù)測(cè)房?jī)r(jià)。如果我們想要優(yōu)化模型,我們必須深入挖掘數(shù)據(jù)并且辨別出是怎么發(fā)生的。
有趣的是,犯罪率并沒(méi)有對(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)生了很大的影響,這就像我們所想的。
最后說(shuō)一句
這篇文章說(shuō)明了 iOS 應(yīng)用程序中集成 CoreML 的過(guò)程。使用 CoreML,我們可以使用模型的又是來(lái)在應(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)上充分顯示:很明顯的是我們的模型還有很多缺陷來(lái)預(yù)測(cè)房?jī)r(jià)。
CoreML 可以是一個(gè)包含設(shè)計(jì)和開發(fā)人員的應(yīng)用程序團(tuán)隊(duì)和數(shù)據(jù)科學(xué)界比較最好的合作方式。數(shù)據(jù)學(xué)家會(huì)研究成噸的模型來(lái)確保傳遞出正確的結(jié)果。應(yīng)用程序團(tuán)隊(duì)可以集中精力來(lái)優(yōu)化將模型集成到應(yīng)用程序中的體驗(yàn)。
也許有些問(wèn)題是更容易定義的,并且匹配模型也已經(jīng)是充分測(cè)試的。這樣可以更加容易的集成到應(yīng)用程序中,而我們不用自己對(duì)模型進(jìn)行優(yōu)化。舉例來(lái)說(shuō)就包含了很多分類的問(wèn)題:a)面部識(shí)別, b)圖像識(shí)別 , c)手寫文字識(shí)別 等等。
需要特別注意的地方就是要確定我們所選用的模型要比較契合我們開發(fā)的項(xiàng)目實(shí)例。這樣在測(cè)試時(shí)可以確認(rèn)模型的值是否就是我們所期望的那樣。然而這并不說(shuō)明 CoreML 可以使所有的模型變得即插即用,但是它確實(shí)讓開發(fā)者更容易的進(jìn)行應(yīng)用程序的開發(fā)。
了解更多關(guān)于為何要提前為應(yīng)用程序適配 iOS 11,或者下載圖書來(lái)進(jìn)一步了解這些改變是如何影響你的生意的。